diff options
Diffstat (limited to 'libnm-core/nm-setting-wireguard.c')
| -rw-r--r-- | libnm-core/nm-setting-wireguard.c | 2591 |
1 files changed, 0 insertions, 2591 deletions
diff --git a/libnm-core/nm-setting-wireguard.c b/libnm-core/nm-setting-wireguard.c deleted file mode 100644 index 3a36daba..00000000 --- a/libnm-core/nm-setting-wireguard.c +++ /dev/null @@ -1,2591 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 - 2019 Red Hat, Inc. - */ - -#include "libnm-core/nm-default-libnm-core.h" - -#include "nm-setting-wireguard.h" - -#include "nm-setting-private.h" -#include "nm-utils-private.h" -#include "nm-connection-private.h" -#include "nm-glib-aux/nm-secret-utils.h" - -/*****************************************************************************/ - -/** - * SECTION:nm-setting-wireguard - * @short_description: Describes connection properties for wireguard related options - * - * The #NMSettingWireGuard object is a #NMSetting subclass that contains settings - * for configuring WireGuard. - **/ - -/*****************************************************************************/ - -static NMWireGuardPeer *_wireguard_peer_dup(const NMWireGuardPeer *self); - -G_DEFINE_BOXED_TYPE(NMWireGuardPeer, - nm_wireguard_peer, - _wireguard_peer_dup, - nm_wireguard_peer_unref) - -/* NMWireGuardPeer can also track invalid allowed-ip settings, and only reject - * them later during is_valid(). Such values are marked by a leading 'X' character - * in the @allowed_ips. It is expected, that such values are the exception, and - * commonly not present. */ -#define ALLOWED_IP_INVALID_X 'X' -#define ALLOWED_IP_INVALID_X_STR "X" - -/** - * NMWireGuardPeer: - * - * The settings of one WireGuard peer. - * - * Since: 1.16 - */ -struct _NMWireGuardPeer { - NMSockAddrEndpoint * endpoint; - char * public_key; - char * preshared_key; - GPtrArray * allowed_ips; - guint refcount; - NMSettingSecretFlags preshared_key_flags; - guint16 persistent_keepalive; - bool public_key_valid : 1; - bool preshared_key_valid : 1; - bool sealed : 1; -}; - -static gboolean -NM_IS_WIREGUARD_PEER(const NMWireGuardPeer *self, gboolean also_sealed) -{ - return self && self->refcount > 0 && (also_sealed || !self->sealed); -} - -/** - * nm_wireguard_peer_new: - * - * Returns: (transfer full): a new, default, unsealed #NMWireGuardPeer instance. - * - * Since: 1.16 - */ -NMWireGuardPeer * -nm_wireguard_peer_new(void) -{ - NMWireGuardPeer *self; - - self = g_slice_new(NMWireGuardPeer); - *self = (NMWireGuardPeer){ - .refcount = 1, - .preshared_key_flags = NM_SETTING_SECRET_FLAG_NOT_REQUIRED, - }; - return self; -} - -/** - * nm_wireguard_peer_new_clone: - * @self: the #NMWireGuardPeer instance to copy. - * @with_secrets: if %TRUE, the preshared-key secrets are copied - * as well. Otherwise, they will be removed. - * - * Returns: (transfer full): a clone of @self. This instance - * is always unsealed. - * - * Since: 1.16 - */ -NMWireGuardPeer * -nm_wireguard_peer_new_clone(const NMWireGuardPeer *self, gboolean with_secrets) -{ - NMWireGuardPeer *new; - guint i; - - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), NULL); - - new = g_slice_new(NMWireGuardPeer); - *new = (NMWireGuardPeer){ - .refcount = 1, - .public_key = g_strdup(self->public_key), - .public_key_valid = self->public_key_valid, - .preshared_key = with_secrets ? g_strdup(self->preshared_key) : NULL, - .preshared_key_valid = self->preshared_key_valid, - .preshared_key_flags = self->preshared_key_flags, - .endpoint = nm_sock_addr_endpoint_ref(self->endpoint), - .persistent_keepalive = self->persistent_keepalive, - }; - if (self->allowed_ips && self->allowed_ips->len > 0) { - new->allowed_ips = g_ptr_array_new_full(self->allowed_ips->len, g_free); - for (i = 0; i < self->allowed_ips->len; i++) { - g_ptr_array_add(new->allowed_ips, g_strdup(self->allowed_ips->pdata[i])); - } - } - return new; -} - -/** - * nm_wireguard_peer_ref: - * @self: (allow-none): the #NMWireGuardPeer instance - * - * This is not thread-safe. - * - * Returns: returns the input argument @self after incrementing - * the reference count. - * - * Since: 1.16 - */ -NMWireGuardPeer * -nm_wireguard_peer_ref(NMWireGuardPeer *self) -{ - if (!self) - return NULL; - - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), NULL); - - nm_assert(self->refcount < G_MAXUINT); - - self->refcount++; - return self; -} - -/** - * nm_wireguard_peer_unref: - * @self: (allow-none): the #NMWireGuardPeer instance - * - * Drop a reference to @self. If the last reference is dropped, - * the instance is freed and all associate data released. - * - * This is not thread-safe. - * - * Since: 1.16 - */ -void -nm_wireguard_peer_unref(NMWireGuardPeer *self) -{ - if (!self) - return; - - g_return_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE)); - - if (--self->refcount > 0) - return; - - nm_sock_addr_endpoint_unref(self->endpoint); - if (self->allowed_ips) - g_ptr_array_unref(self->allowed_ips); - g_free(self->public_key); - nm_free_secret(self->preshared_key); - g_slice_free(NMWireGuardPeer, self); -} - -/** - * _wireguard_peer_dup: - * @self: the #NMWireGuardPeer instance - * - * Duplicates the #NMWireGuardPeer instance. Note that if @self - * is already sealed, this increments the reference count and - * returns it. If the instance is still unsealed, it is copied. - * - * Returns: (transfer full): a duplicate of @self, or (if the - * instance is sealed and thus immutable) a reference to @self. - * As such, the instance will be sealed if and only if @self is - * sealed. - */ -static NMWireGuardPeer * -_wireguard_peer_dup(const NMWireGuardPeer *self) -{ - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), NULL); - - if (self->sealed) - return nm_wireguard_peer_ref((NMWireGuardPeer *) self); - return nm_wireguard_peer_new_clone(self, TRUE); -} - -/** - * nm_wireguard_peer_seal: - * @self: the #NMWireGuardPeer instance - * - * Seal the #NMWireGuardPeer instance. Afterwards, it is a bug - * to call all functions that modify the instance (except ref/unref). - * A sealed instance cannot be unsealed again, but you can create - * an unsealed copy with nm_wireguard_peer_new_clone(). - * - * Since: 1.16 - */ -void -nm_wireguard_peer_seal(NMWireGuardPeer *self) -{ - g_return_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE)); - - self->sealed = TRUE; - - if (self->allowed_ips) { - if (self->allowed_ips->len == 0) - nm_clear_pointer(&self->allowed_ips, g_ptr_array_unref); - } -} - -/** - * nm_wireguard_peer_is_sealed: - * @self: the #NMWireGuardPeer instance - * - * Returns: whether @self is sealed or not. - * - * Since: 1.16 - */ -gboolean -nm_wireguard_peer_is_sealed(const NMWireGuardPeer *self) -{ - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), FALSE); - - return self->sealed; -} - -/** - * nm_wireguard_peer_get_public_key: - * @self: the #NMWireGuardPeer instance - * - * Returns: (transfer none): the public key or %NULL if unset. - * - * Since: 1.16 - */ -const char * -nm_wireguard_peer_get_public_key(const NMWireGuardPeer *self) -{ - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), NULL); - - return self->public_key; -} - -/** - * nm_wireguard_peer_set_public_key: - * @self: the unsealed #NMWireGuardPeer instance - * @public_key: (allow-none) (transfer none): the new public - * key or %NULL to clear the public key. - * @accept_invalid: if %TRUE and @public_key is not %NULL and - * invalid, then do not modify the instance. - * - * Reset the public key. Note that if the public key is valid, it - * will be normalized (which may or may not modify the set value). - * - * It is a bug trying to modify a sealed #NMWireGuardPeer instance. - * - * Returns: %TRUE if the key was valid or %NULL. Returns - * %FALSE for invalid keys. Depending on @accept_invalid - * will an invalid key be set or not. - * - * Since: 1.16 - */ -gboolean -nm_wireguard_peer_set_public_key(NMWireGuardPeer *self, - const char * public_key, - gboolean accept_invalid) -{ - char * public_key_normalized = NULL; - gboolean is_valid; - - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, FALSE), FALSE); - - if (!public_key) { - nm_clear_g_free(&self->public_key); - return TRUE; - } - - is_valid = nm_utils_base64secret_normalize(public_key, - NM_WIREGUARD_PUBLIC_KEY_LEN, - &public_key_normalized); - nm_assert(is_valid == (public_key_normalized != NULL)); - - if (!is_valid && !accept_invalid) - return FALSE; - - self->public_key_valid = is_valid; - g_free(self->public_key); - self->public_key = public_key_normalized ?: g_strdup(public_key); - return is_valid; -} - -void -_nm_wireguard_peer_set_public_key_bin(NMWireGuardPeer *self, - const guint8 public_key[static NM_WIREGUARD_PUBLIC_KEY_LEN]) -{ - g_return_if_fail(NM_IS_WIREGUARD_PEER(self, FALSE)); - - nm_clear_g_free(&self->public_key); - - if (!public_key) - return; - - self->public_key = g_base64_encode(public_key, NM_WIREGUARD_PUBLIC_KEY_LEN); - self->public_key_valid = TRUE; -} - -/** - * nm_wireguard_peer_get_preshared_key: - * @self: the #NMWireGuardPeer instance - * - * Returns: (transfer none): the preshared key or %NULL if unset. - * - * Since: 1.16 - */ -const char * -nm_wireguard_peer_get_preshared_key(const NMWireGuardPeer *self) -{ - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), NULL); - - return self->preshared_key; -} - -/** - * nm_wireguard_peer_set_preshared_key: - * @self: the unsealed #NMWireGuardPeer instance - * @preshared_key: (allow-none) (transfer none): the new preshared - * key or %NULL to clear the preshared key. - * @accept_invalid: whether to allow setting the key to an invalid - * value. If %FALSE, @self is unchanged if the key is invalid - * and if %FALSE is returned. - * - * Reset the preshared key. Note that if the preshared key is valid, it - * will be normalized (which may or may not modify the set value). - * - * Note that the preshared-key is a secret and consequently has corresponding - * preshared-key-flags property. This is so that secrets can be optional - * and requested on demand from a secret-agent. Also, an invalid preshared-key - * may optionally cause nm_wireguard_peer_is_valid() to fail or it may - * be accepted. - * - * It is a bug trying to modify a sealed #NMWireGuardPeer instance. - * - * Returns: %TRUE if the preshared-key is valid, otherwise %FALSE. - * %NULL is considered a valid value. - * If the key is invalid, it depends on @accept_invalid whether the - * previous value was reset. - * - * Since: 1.16 - */ -gboolean -nm_wireguard_peer_set_preshared_key(NMWireGuardPeer *self, - const char * preshared_key, - gboolean accept_invalid) -{ - char * preshared_key_normalized = NULL; - gboolean is_valid; - - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, FALSE), FALSE); - - if (!preshared_key) { - nm_clear_pointer(&self->preshared_key, nm_free_secret); - return TRUE; - } - - is_valid = nm_utils_base64secret_normalize(preshared_key, - NM_WIREGUARD_SYMMETRIC_KEY_LEN, - &preshared_key_normalized); - nm_assert(is_valid == (preshared_key_normalized != NULL)); - - if (!is_valid && !accept_invalid) - return FALSE; - - self->preshared_key_valid = is_valid; - nm_free_secret(self->preshared_key); - self->preshared_key = preshared_key_normalized ?: g_strdup(preshared_key); - return is_valid; -} - -/** - * nm_wireguard_peer_get_preshared_key_flags: - * @self: the #NMWireGuardPeer instance - * - * Returns: get the secret flags for the preshared-key. - * - * Since: 1.16 - */ -NMSettingSecretFlags -nm_wireguard_peer_get_preshared_key_flags(const NMWireGuardPeer *self) -{ - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), 0); - - return self->preshared_key_flags; -} - -/** - * nm_wireguard_peer_set_preshared_key_flags: - * @self: the unsealed #NMWireGuardPeer instance - * @preshared_key_flags: the secret flags to set. - * - * It is a bug trying to modify a sealed #NMWireGuardPeer instance. - * - * Since: 1.16 - */ -void -nm_wireguard_peer_set_preshared_key_flags(NMWireGuardPeer * self, - NMSettingSecretFlags preshared_key_flags) -{ - g_return_if_fail(NM_IS_WIREGUARD_PEER(self, FALSE)); - - self->preshared_key_flags = preshared_key_flags; -} - -/** - * nm_wireguard_peer_get_persistent_keepalive: - * @self: the #NMWireGuardPeer instance - * - * Returns: get the persistent-keepalive setting in seconds. Set to zero to disable - * keep-alive. - * - * Since: 1.16 - */ -guint16 -nm_wireguard_peer_get_persistent_keepalive(const NMWireGuardPeer *self) -{ - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), 0); - - return self->persistent_keepalive; -} - -/** - * nm_wireguard_peer_set_persistent_keepalive: - * @self: the unsealed #NMWireGuardPeer instance - * @persistent_keepalive: the keep-alive value to set. - * - * It is a bug trying to modify a sealed #NMWireGuardPeer instance. - * - * Since: 1.16 - */ -void -nm_wireguard_peer_set_persistent_keepalive(NMWireGuardPeer *self, guint16 persistent_keepalive) -{ - g_return_if_fail(NM_IS_WIREGUARD_PEER(self, FALSE)); - - self->persistent_keepalive = persistent_keepalive; -} - -NMSockAddrEndpoint * -_nm_wireguard_peer_get_endpoint(const NMWireGuardPeer *self) -{ - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), NULL); - - return self->endpoint; -} - -/** - * nm_wireguard_peer_get_endpoint: - * @self: the #NMWireGuardPeer instance - * - * Returns: (transfer none): the endpoint or %NULL if none was set. - * - * Since: 1.16 - */ -const char * -nm_wireguard_peer_get_endpoint(const NMWireGuardPeer *self) -{ - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), NULL); - - return self->endpoint ? nm_sock_addr_endpoint_get_endpoint(self->endpoint) : NULL; -} - -void -_nm_wireguard_peer_set_endpoint(NMWireGuardPeer *self, NMSockAddrEndpoint *endpoint) -{ - NMSockAddrEndpoint *old; - - nm_assert(NM_IS_WIREGUARD_PEER(self, FALSE)); - - old = self->endpoint; - self->endpoint = nm_sock_addr_endpoint_ref(endpoint); - nm_sock_addr_endpoint_unref(old); -} - -/** - * nm_wireguard_peer_set_endpoint: - * @self: the unsealed #NMWireGuardPeer instance - * @endpoint: the socket address endpoint to set or %NULL. - * @allow_invalid: if %TRUE, also invalid values are set. - * If %FALSE, the function does nothing for invalid @endpoint - * arguments. - * - * Sets or clears the endpoint of @self. - * - * It is a bug trying to modify a sealed #NMWireGuardPeer instance. - * - * Returns: %TRUE if the endpoint is %NULL or valid. For an - * invalid @endpoint argument, %FALSE is returned. Depending - * on @allow_invalid, the instance will be modified. - * - * Since: 1.16 - */ -gboolean -nm_wireguard_peer_set_endpoint(NMWireGuardPeer *self, const char *endpoint, gboolean allow_invalid) -{ - NMSockAddrEndpoint *old; - NMSockAddrEndpoint *new; - gboolean is_valid; - - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, FALSE), FALSE); - - if (!endpoint) { - nm_clear_pointer(&self->endpoint, nm_sock_addr_endpoint_unref); - return TRUE; - } - - new = nm_sock_addr_endpoint_new(endpoint); - - is_valid = (nm_sock_addr_endpoint_get_host(new) != NULL); - - if (!allow_invalid && !is_valid) { - nm_sock_addr_endpoint_unref(new); - return FALSE; - } - - old = self->endpoint; - self->endpoint = new; - nm_sock_addr_endpoint_unref(old); - return is_valid; -} - -/** - * nm_wireguard_peer_get_allowed_ips_len: - * @self: the #NMWireGuardPeer instance - * - * Returns: the number of allowed-ips entries. - * - * Since: 1.16 - */ -guint -nm_wireguard_peer_get_allowed_ips_len(const NMWireGuardPeer *self) -{ - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), 0); - - return self->allowed_ips ? self->allowed_ips->len : 0u; -} - -/** - * nm_wireguard_peer_get_allowed_ip: - * @self: the #NMWireGuardPeer instance - * @idx: the index from zero to (allowed-ips-len - 1) to - * retrieve. - * @out_is_valid: (allow-none): %TRUE if the returned value is a valid allowed-ip - * setting. - * - * Returns: (transfer none): the allowed-ip setting at index @idx. - * If @idx is out of range, %NULL will be returned. - * - * Since: 1.16 - */ -const char * -nm_wireguard_peer_get_allowed_ip(const NMWireGuardPeer *self, guint idx, gboolean *out_is_valid) -{ - const char *s; - - /* With LTO, the compiler might warn about the g_return_val_if_fail() - * code path not initializing the output argument. Workaround that by - * always setting the out argument. */ - NM_SET_OUT(out_is_valid, FALSE); - - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), NULL); - - if (!self->allowed_ips || idx >= self->allowed_ips->len) - return NULL; - - s = self->allowed_ips->pdata[idx]; - NM_SET_OUT(out_is_valid, s[0] != ALLOWED_IP_INVALID_X); - return s[0] == ALLOWED_IP_INVALID_X ? &s[1] : s; -} - -/** - * nm_wireguard_peer_clear_allowed_ips: - * @self: the unsealed #NMWireGuardPeer instance - * - * Removes all allowed-ip entries. - * - * It is a bug trying to modify a sealed #NMWireGuardPeer instance. - * - * Since: 1.16 - */ -void -nm_wireguard_peer_clear_allowed_ips(NMWireGuardPeer *self) -{ - g_return_if_fail(NM_IS_WIREGUARD_PEER(self, FALSE)); - - if (self->allowed_ips) - g_ptr_array_set_size(self->allowed_ips, 0); -} - -static gboolean -_peer_append_allowed_ip(NMWireGuardPeer *self, const char *allowed_ip, gboolean accept_invalid) -{ - int addr_family; - int prefix; - NMIPAddr addrbin; - char * str; - gboolean is_valid = TRUE; - - nm_assert(NM_IS_WIREGUARD_PEER(self, FALSE)); - nm_assert(allowed_ip); - - /* normalize the address (if it is valid. Otherwise, take it - * as-is (it will render the instance invalid). */ - if (!nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC, allowed_ip, &addr_family, &addrbin, &prefix)) { - if (!accept_invalid) - return FALSE; - /* mark the entry as invalid by having a "X" prefix. */ - str = g_strconcat(ALLOWED_IP_INVALID_X_STR, allowed_ip, NULL); - is_valid = FALSE; - } else { - char addrstr[NM_UTILS_INET_ADDRSTRLEN]; - - nm_assert_addr_family(addr_family); - - nm_utils_inet_ntop(addr_family, &addrbin, addrstr); - if (prefix >= 0) - str = g_strdup_printf("%s/%d", addrstr, prefix); - else - str = g_strdup(addrstr); - nm_assert(str[0] != ALLOWED_IP_INVALID_X); - } - - if (!self->allowed_ips) - self->allowed_ips = g_ptr_array_new_with_free_func(g_free); - - g_ptr_array_add(self->allowed_ips, str); - return is_valid; -} - -/** - * nm_wireguard_peer_append_allowed_ip: - * @self: the unsealed #NMWireGuardPeer instance - * @allowed_ip: the allowed-ip entry to set. - * @accept_invalid: if %TRUE, also invalid @allowed_ip value - * will be appended. Otherwise, the function does nothing - * in face of invalid values and returns %FALSE. - * - * Appends @allowed_ip setting to the list. This does not check - * for duplicates and always appends @allowed_ip to the end of the - * list. If @allowed_ip is valid, it will be normalized and a modified - * for might be appended. If @allowed_ip is invalid, it will still be - * appended, but later verification will fail. - * - * It is a bug trying to modify a sealed #NMWireGuardPeer instance. - * - * Returns: %TRUE if the value is a valid allowed-ips value, %FALSE otherwise. - * Depending on @accept_invalid, also invalid values are added. - * - * Since: 1.16 - */ -gboolean -nm_wireguard_peer_append_allowed_ip(NMWireGuardPeer *self, - const char * allowed_ip, - gboolean accept_invalid) -{ - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, FALSE), FALSE); - g_return_val_if_fail(allowed_ip, FALSE); - - return _peer_append_allowed_ip(self, allowed_ip, accept_invalid); -} - -/** - * nm_wireguard_peer_remove_allowed_ip: - * @self: the unsealed #NMWireGuardPeer instance - * @idx: the index from zero to (allowed-ips-len - 1) to - * retrieve. If the index is out of range, %FALSE is returned - * and nothing is done. - * - * Removes the allowed-ip at the given @idx. This shifts all - * following entries one index down. - * - * It is a bug trying to modify a sealed #NMWireGuardPeer instance. - * - * Returns: %TRUE if @idx was valid and the allowed-ip was removed. - * %FALSE otherwise, and the peer will not be changed. - * - * Since: 1.16 - */ -gboolean -nm_wireguard_peer_remove_allowed_ip(NMWireGuardPeer *self, guint idx) -{ - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, FALSE), FALSE); - - if (!self->allowed_ips || idx >= self->allowed_ips->len) - return FALSE; - - g_ptr_array_remove_index(self->allowed_ips, idx); - return TRUE; -} - -/** - * nm_wireguard_peer_is_valid: - * @self: the #NMWireGuardPeer instance - * @check_secrets: if %TRUE, non-secret properties are validated. - * Otherwise, they are ignored for this purpose. - * @check_non_secrets: if %TRUE, secret properties are validated. - * Otherwise, they are ignored for this purpose. - * @error: the #GError location for returning the failure reason. - * - * Returns: %TRUE if the peer is valid or fails with an error - * reason. - * - * Since: 1.16 - */ -gboolean -nm_wireguard_peer_is_valid(const NMWireGuardPeer *self, - gboolean check_non_secrets, - gboolean check_secrets, - GError ** error) -{ - guint i; - - g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), FALSE); - g_return_val_if_fail(!error || !*error, FALSE); - - if (check_non_secrets) { - if (!self->public_key) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("missing public-key for peer")); - return FALSE; - } else if (!self->public_key_valid) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid public-key for peer")); - return FALSE; - } - } - - if (check_secrets) { - if (self->preshared_key && !self->preshared_key_valid) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid preshared-key for peer")); - return FALSE; - } - } - - if (check_non_secrets) { - if (!_nm_utils_secret_flags_validate(self->preshared_key_flags, - NULL, - NULL, - NM_SETTING_SECRET_FLAG_NONE, - error)) - return FALSE; - } - - if (check_non_secrets) { - if (self->endpoint && !nm_sock_addr_endpoint_get_host(self->endpoint)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid endpoint for peer")); - return FALSE; - } - - if (self->allowed_ips) { - for (i = 0; i < self->allowed_ips->len; i++) { - const char *s = self->allowed_ips->pdata[i]; - - if (s[0] == ALLOWED_IP_INVALID_X) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid IP address \"%s\" for allowed-ip of peer"), - &s[1]); - return FALSE; - } - } - } - - if (!_nm_setting_secret_flags_valid(self->preshared_key_flags)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid preshared-key-flags for peer")); - return FALSE; - } - } - - return TRUE; -} - -/** - * nm_wireguard_peer_cmp: - * @a: (allow-none): the #NMWireGuardPeer to compare. - * @b: (allow-none): the other #NMWireGuardPeer to compare. - * @compare_flags: #NMSettingCompareFlags to affect the comparison. - * - * Returns: zero of the two instances are equivalent or - * a non-zero integer otherwise. This defines a total ordering - * over the peers. Whether a peer is sealed or not, does not - * affect the comparison. - * - * Since: 1.16 - */ -int -nm_wireguard_peer_cmp(const NMWireGuardPeer *a, - const NMWireGuardPeer *b, - NMSettingCompareFlags compare_flags) -{ - guint i, n; - - NM_CMP_SELF(a, b); - - /* regardless of the @compare_flags, the public-key is the ID of the peer. It must - * always be compared. */ - NM_CMP_FIELD_BOOL(a, b, public_key_valid); - NM_CMP_FIELD_STR0(a, b, public_key); - - if (NM_FLAGS_ANY(compare_flags, - NM_SETTING_COMPARE_FLAG_INFERRABLE | NM_SETTING_COMPARE_FLAG_FUZZY)) - return 0; - - NM_CMP_FIELD_BOOL(a, b, endpoint); - if (a->endpoint) { - NM_CMP_DIRECT_STRCMP0(nm_sock_addr_endpoint_get_endpoint(a->endpoint), - nm_sock_addr_endpoint_get_endpoint(b->endpoint)); - } - - NM_CMP_FIELD(a, b, persistent_keepalive); - - NM_CMP_DIRECT((n = (a->allowed_ips ? a->allowed_ips->len : 0u)), - (b->allowed_ips ? b->allowed_ips->len : 0u)); - for (i = 0; i < n; i++) - NM_CMP_DIRECT_STRCMP0(a->allowed_ips->pdata[i], b->allowed_ips->pdata[i]); - - NM_CMP_FIELD(a, b, preshared_key_flags); - - if (!NM_FLAGS_HAS(compare_flags, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)) { - if (NM_FLAGS_HAS(compare_flags, NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS) - && NM_FLAGS_HAS(a->preshared_key_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED)) { - /* pass */ - } else if (NM_FLAGS_HAS(compare_flags, NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS) - && NM_FLAGS_HAS(a->preshared_key_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED)) { - /* pass */ - } else { - NM_CMP_FIELD_BOOL(a, b, preshared_key_valid); - NM_CMP_FIELD_STR0(a, b, preshared_key); - } - } - - return 0; -} - -/*****************************************************************************/ - -typedef struct { - const char * public_key; - NMWireGuardPeer *peer; - guint idx; -} PeerData; - -/*****************************************************************************/ - -NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_FWMARK, - PROP_IP4_AUTO_DEFAULT_ROUTE, - PROP_IP6_AUTO_DEFAULT_ROUTE, - PROP_LISTEN_PORT, - PROP_MTU, - PROP_PEER_ROUTES, - PROP_PRIVATE_KEY, - PROP_PRIVATE_KEY_FLAGS, ); - -typedef struct { - char * private_key; - GPtrArray * peers_arr; - GHashTable * peers_hash; - NMSettingSecretFlags private_key_flags; - NMTernary ip4_auto_default_route; - NMTernary ip6_auto_default_route; - guint32 fwmark; - guint32 mtu; - guint16 listen_port; - bool private_key_valid : 1; - bool peer_routes : 1; -} NMSettingWireGuardPrivate; - -/** - * NMSettingWireGuard: - * - * WireGuard Settings - * - * Since: 1.16 - */ -struct _NMSettingWireGuard { - NMSetting parent; - NMSettingWireGuardPrivate _priv; -}; - -struct _NMSettingWireGuardClass { - NMSettingClass parent; -}; - -G_DEFINE_TYPE(NMSettingWireGuard, nm_setting_wireguard, NM_TYPE_SETTING) - -#define NM_SETTING_WIREGUARD_GET_PRIVATE(self) \ - _NM_GET_PRIVATE(self, NMSettingWireGuard, NM_IS_SETTING_WIREGUARD, NMSetting) - -/*****************************************************************************/ - -#define peers_psk_get_secret_name_a(public_key, to_free) \ - nm_construct_name_a(NM_SETTING_WIREGUARD_PEERS ".%s." NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, \ - (public_key), \ - (to_free)) - -#define peers_psk_get_secret_name_dup(public_key) \ - g_strdup_printf(NM_SETTING_WIREGUARD_PEERS ".%s." NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, \ - (public_key)) - -#define peers_psk_get_secret_parse_a(secret_public_key, public_key_free) \ - ({ \ - const char *_secret_public_key = (secret_public_key); \ - char ** _public_key_free = (public_key_free); \ - const char *_public_key = NULL; \ - \ - nm_assert(_public_key_free && !*_public_key_free); \ - \ - if (NM_STR_HAS_PREFIX(_secret_public_key, NM_SETTING_WIREGUARD_PEERS ".")) { \ - _secret_public_key += NM_STRLEN(NM_SETTING_WIREGUARD_PEERS "."); \ - if (NM_STR_HAS_SUFFIX(_secret_public_key, "." NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)) { \ - _public_key = \ - nm_strndup_a(300, \ - _secret_public_key, \ - strlen(_secret_public_key) \ - - NM_STRLEN("." NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY), \ - _public_key_free); \ - } \ - } \ - \ - _public_key; \ - }) - -/*****************************************************************************/ - -/** - * nm_setting_wireguard_get_private_key: - * @self: the #NMSettingWireGuard instance - * - * Returns: (transfer none): the set private-key or %NULL. - * - * Since: 1.16 - */ -const char * -nm_setting_wireguard_get_private_key(NMSettingWireGuard *self) -{ - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), NULL); - - return NM_SETTING_WIREGUARD_GET_PRIVATE(self)->private_key; -} - -/** - * nm_setting_wireguard_get_private_key_flags: - * @self: the #NMSettingWireGuard instance - * - * Returns: the secret-flags for #NMSettingWireGuard:private-key. - * - * Since: 1.16 - */ -NMSettingSecretFlags -nm_setting_wireguard_get_private_key_flags(NMSettingWireGuard *self) -{ - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), 0); - - return NM_SETTING_WIREGUARD_GET_PRIVATE(self)->private_key_flags; -} - -/** - * nm_setting_wireguard_get_fwmark: - * @self: the #NMSettingWireGuard instance - * - * Returns: the set firewall mark. - * - * Since: 1.16 - */ -guint32 -nm_setting_wireguard_get_fwmark(NMSettingWireGuard *self) -{ - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), 0); - - return NM_SETTING_WIREGUARD_GET_PRIVATE(self)->fwmark; -} - -/** - * nm_setting_wireguard_get_listen_port: - * @self: the #NMSettingWireGuard instance - * - * Returns: the set UDP listen port. - * - * Since: 1.16 - */ -guint16 -nm_setting_wireguard_get_listen_port(NMSettingWireGuard *self) -{ - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), 0); - - return NM_SETTING_WIREGUARD_GET_PRIVATE(self)->listen_port; -} - -/** - * nm_setting_wireguard_get_peer_routes: - * @self: the #NMSettingWireGuard instance - * - * Returns: whether automatically add peer routes. - * - * Since: 1.16 - */ -gboolean -nm_setting_wireguard_get_peer_routes(NMSettingWireGuard *self) -{ - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), TRUE); - - return NM_SETTING_WIREGUARD_GET_PRIVATE(self)->peer_routes; -} - -/** - * nm_setting_wireguard_get_mtu: - * @self: the #NMSettingWireGuard instance - * - * Returns: the MTU of the setting. - * - * Since: 1.16 - */ -guint32 -nm_setting_wireguard_get_mtu(NMSettingWireGuard *self) -{ - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), 0); - - return NM_SETTING_WIREGUARD_GET_PRIVATE(self)->mtu; -} - -/** - * nm_setting_wireguard_get_ip4_auto_default_route: - * @self: the #NMSettingWireGuard setting. - * - * Returns: the "ip4-auto-default-route" property of the setting. - * - * Since: 1.20 - */ -NMTernary -nm_setting_wireguard_get_ip4_auto_default_route(NMSettingWireGuard *self) -{ - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), NM_TERNARY_DEFAULT); - - return NM_SETTING_WIREGUARD_GET_PRIVATE(self)->ip4_auto_default_route; -} - -/** - * nm_setting_wireguard_get_ip6_auto_default_route: - * @self: the #NMSettingWireGuard setting. - * - * Returns: the "ip6-auto-default-route" property of the setting. - * - * Since: 1.20 - */ -NMTernary -nm_setting_wireguard_get_ip6_auto_default_route(NMSettingWireGuard *self) -{ - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), NM_TERNARY_DEFAULT); - - return NM_SETTING_WIREGUARD_GET_PRIVATE(self)->ip6_auto_default_route; -} - -/*****************************************************************************/ - -static void -_peer_free(PeerData *pd) -{ - nm_assert(pd); - - nm_wireguard_peer_unref(pd->peer); - g_slice_free(PeerData, pd); -} - -/*****************************************************************************/ - -static void -_peers_notify(gpointer self) -{ - _nm_setting_emit_property_changed(self); -} - -static PeerData * -_peers_get(NMSettingWireGuardPrivate *priv, guint idx) -{ - PeerData *pd; - - nm_assert(priv); - nm_assert(idx < priv->peers_arr->len); - - pd = priv->peers_arr->pdata[idx]; - - nm_assert(pd); - nm_assert(pd->idx == idx); - nm_assert(NM_IS_WIREGUARD_PEER(pd->peer, TRUE)); - nm_assert(nm_wireguard_peer_is_sealed(pd->peer)); - nm_assert(pd->public_key == nm_wireguard_peer_get_public_key(pd->peer)); - nm_assert(g_hash_table_lookup(priv->peers_hash, pd) == pd); - - return pd; -} - -static PeerData * -_peers_get_by_public_key(NMSettingWireGuardPrivate *priv, - const char * public_key, - gboolean try_with_normalized_key) -{ - gs_free char *public_key_normalized = NULL; - PeerData * pd; - -again: - nm_assert(priv); - nm_assert(public_key); - - pd = g_hash_table_lookup(priv->peers_hash, &public_key); - if (pd) { - nm_assert(_peers_get(priv, pd->idx) == pd); - return pd; - } - if (try_with_normalized_key - && nm_utils_base64secret_normalize(public_key, - NM_WIREGUARD_PUBLIC_KEY_LEN, - &public_key_normalized)) { - public_key = public_key_normalized; - try_with_normalized_key = FALSE; - goto again; - } - return NULL; -} - -static void -_peers_remove(NMSettingWireGuardPrivate *priv, PeerData *pd, gboolean do_free) -{ - guint i; - - nm_assert(pd); - nm_assert(_peers_get(priv, pd->idx) == pd); - - for (i = pd->idx + 1; i < priv->peers_arr->len; i++) - _peers_get(priv, i)->idx--; - - g_ptr_array_remove_index(priv->peers_arr, pd->idx); - if (!g_hash_table_remove(priv->peers_hash, pd)) - nm_assert_not_reached(); - if (do_free) - _peer_free(pd); -} - -/** - * nm_setting_wireguard_get_peers_len: - * @self: the #NMSettingWireGuard instance - * - * Returns: the number of registered peers. - * - * Since: 1.16 - */ -guint -nm_setting_wireguard_get_peers_len(NMSettingWireGuard *self) -{ - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), 0); - - return NM_SETTING_WIREGUARD_GET_PRIVATE(self)->peers_arr->len; -} - -/** - * nm_setting_wireguard_get_peer: - * @self: the #NMSettingWireGuard instance - * @idx: the index to lookup. - * - * Returns: (transfer none): the #NMWireGuardPeer entry at - * index @idx. If the index is out of range, %NULL is returned. - * - * Since: 1.16 - */ -NMWireGuardPeer * -nm_setting_wireguard_get_peer(NMSettingWireGuard *self, guint idx) -{ - NMSettingWireGuardPrivate *priv; - - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), NULL); - - priv = NM_SETTING_WIREGUARD_GET_PRIVATE(self); - - if (idx >= priv->peers_arr->len) - return NULL; - - return _peers_get(priv, idx)->peer; -} - -/** - * nm_setting_wireguard_get_peer_by_public_key: - * @self: the #NMSettingWireGuard instance - * @public_key: the public key for looking up the - * peer. - * @out_idx: (out) (allow-none): optional output argument - * for the index of the found peer. If no index is found, - * this is set to the nm_setting_wireguard_get_peers_len(). - * - * Returns: (transfer none): the #NMWireGuardPeer instance with a - * matching public key. If no such peer exists, %NULL is returned. - * - * Since: 1.16 - */ -NMWireGuardPeer * -nm_setting_wireguard_get_peer_by_public_key(NMSettingWireGuard *self, - const char * public_key, - guint * out_idx) -{ - NMSettingWireGuardPrivate *priv; - PeerData * pd; - - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), NULL); - g_return_val_if_fail(public_key, NULL); - - priv = NM_SETTING_WIREGUARD_GET_PRIVATE(self); - - pd = _peers_get_by_public_key(priv, public_key, TRUE); - if (!pd) { - NM_SET_OUT(out_idx, priv->peers_arr->len); - return NULL; - } - NM_SET_OUT(out_idx, pd->idx); - return pd->peer; -} - -static gboolean -_peers_set(NMSettingWireGuardPrivate *priv, - NMWireGuardPeer * peer, - guint idx, - gboolean check_same_key) -{ - PeerData * pd_same_key = NULL; - PeerData * pd_idx = NULL; - const char *public_key; - - nm_assert(idx <= priv->peers_arr->len); - - public_key = nm_wireguard_peer_get_public_key(peer); - - if (idx < priv->peers_arr->len) { - pd_idx = _peers_get(priv, idx); - - if (pd_idx->peer == peer) - return FALSE; - - if (check_same_key && nm_streq(public_key, nm_wireguard_peer_get_public_key(pd_idx->peer))) - check_same_key = FALSE; - } - - nm_wireguard_peer_seal(peer); - nm_wireguard_peer_ref(peer); - - if (check_same_key) { - pd_same_key = _peers_get_by_public_key(priv, public_key, FALSE); - if (pd_same_key) { - if (pd_idx) { - nm_assert(pd_same_key != pd_idx); - _peers_remove(priv, pd_same_key, TRUE); - pd_same_key = NULL; - } else { - if (pd_same_key->peer == peer && pd_same_key->idx == priv->peers_arr->len - 1) { - nm_wireguard_peer_unref(peer); - return FALSE; - } - _peers_remove(priv, pd_same_key, FALSE); - nm_wireguard_peer_unref(pd_same_key->peer); - } - } - } else - nm_assert(_peers_get_by_public_key(priv, public_key, FALSE) == pd_idx); - - if (pd_idx) { - g_hash_table_remove(priv->peers_hash, pd_idx); - nm_wireguard_peer_unref(pd_idx->peer); - pd_idx->public_key = public_key; - pd_idx->peer = peer; - g_hash_table_add(priv->peers_hash, pd_idx); - return TRUE; - } - - if (!pd_same_key) - pd_same_key = g_slice_new(PeerData); - - *pd_same_key = (PeerData){ - .peer = peer, - .public_key = public_key, - .idx = priv->peers_arr->len, - }; - - g_ptr_array_add(priv->peers_arr, pd_same_key); - if (!nm_g_hash_table_add(priv->peers_hash, pd_same_key)) - nm_assert_not_reached(); - - nm_assert(_peers_get(priv, pd_same_key->idx) == pd_same_key); - - return TRUE; -} - -static gboolean -_peers_append(NMSettingWireGuardPrivate *priv, NMWireGuardPeer *peer, gboolean check_same_key) -{ - return _peers_set(priv, peer, priv->peers_arr->len, check_same_key); -} - -/** - * nm_setting_wireguard_set_peer: - * @self: the #NMSettingWireGuard instance - * @peer: the #NMWireGuardPeer instance to set. - * This seals @peer and keeps a reference on the - * instance. - * @idx: the index, in the range of 0 to the number of - * peers (including). That means, if @idx is one past - * the end of the number of peers, this is the same as - * nm_setting_wireguard_append_peer(). Otherwise, the - * peer at this index is replaced. - * - * If @idx is one past the last peer, the behavior is the same - * as nm_setting_wireguard_append_peer(). - * Otherwise, the peer will be at @idx and replace the peer - * instance at that index. Note that if a peer with the same - * public-key exists on another index, then that peer will also - * be replaced. In that case, the number of peers will shrink - * by one (because the one at @idx got replace and then one - * with the same public-key got removed). This also means, - * that the resulting index afterwards may be one less than - * @idx (if another peer with a lower index was dropped). - * - * Since: 1.16 - */ -void -nm_setting_wireguard_set_peer(NMSettingWireGuard *self, NMWireGuardPeer *peer, guint idx) -{ - NMSettingWireGuardPrivate *priv; - - g_return_if_fail(NM_IS_SETTING_WIREGUARD(self)); - g_return_if_fail(NM_IS_WIREGUARD_PEER(peer, TRUE)); - - priv = NM_SETTING_WIREGUARD_GET_PRIVATE(self); - - g_return_if_fail(idx <= priv->peers_arr->len); - - if (_peers_set(priv, peer, idx, TRUE)) - _peers_notify(self); -} - -/** - * nm_setting_wireguard_append_peer: - * @self: the #NMSettingWireGuard instance - * @peer: the #NMWireGuardPeer instance to append. - * This seals @peer and keeps a reference on the - * instance. - * - * If a peer with the same public-key already exists, that - * one is replaced by @peer. The new @peer is always appended - * (or moved to) the end, so in case a peer is replaced, the - * indexes are shifted and the number of peers stays unchanged. - * - * Since: 1.16 - */ -void -nm_setting_wireguard_append_peer(NMSettingWireGuard *self, NMWireGuardPeer *peer) -{ - g_return_if_fail(NM_IS_SETTING_WIREGUARD(self)); - g_return_if_fail(NM_IS_WIREGUARD_PEER(peer, TRUE)); - - if (_peers_append(NM_SETTING_WIREGUARD_GET_PRIVATE(self), peer, TRUE)) - _peers_notify(self); -} - -/** - * nm_setting_wireguard_remove_peer - * @self: the #NMSettingWireGuard instance - * @idx: the index to remove. - * - * Returns: %TRUE if @idx was in range and a peer - * was removed. Otherwise, @self is unchanged. - * - * Since: 1.16 - */ -gboolean -nm_setting_wireguard_remove_peer(NMSettingWireGuard *self, guint idx) -{ - NMSettingWireGuardPrivate *priv; - - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), FALSE); - - priv = NM_SETTING_WIREGUARD_GET_PRIVATE(self); - - if (idx >= priv->peers_arr->len) - return FALSE; - - _peers_remove(priv, _peers_get(priv, idx), TRUE); - _peers_notify(self); - return TRUE; -} - -static guint -_peers_clear(NMSettingWireGuardPrivate *priv) -{ - guint l; - - l = priv->peers_arr->len; - while (priv->peers_arr->len > 0) { - _peers_remove(priv, _peers_get(priv, priv->peers_arr->len - 1), TRUE); - } - return l; -} - -/** - * nm_setting_wireguard_: - * @self: the #NMSettingWireGuard instance - * - * Returns: the number of cleared peers. - * - * Since: 1.16 - */ -guint -nm_setting_wireguard_clear_peers(NMSettingWireGuard *self) -{ - guint l; - - g_return_val_if_fail(NM_IS_SETTING_WIREGUARD(self), 0); - - l = _peers_clear(NM_SETTING_WIREGUARD_GET_PRIVATE(self)); - if (l > 0) - _peers_notify(self); - return l; -} - -/*****************************************************************************/ - -static GVariant * -_peers_dbus_only_synth(const NMSettInfoSetting * sett_info, - guint property_idx, - NMConnection * connection, - NMSetting * setting, - NMConnectionSerializationFlags flags, - const NMConnectionSerializationOptions *options) -{ - NMSettingWireGuard * self = NM_SETTING_WIREGUARD(setting); - NMSettingWireGuardPrivate *priv; - gboolean any_peers = FALSE; - GVariantBuilder peers_builder; - guint i_peer, n_peers; - guint i; - - n_peers = nm_setting_wireguard_get_peers_len(self); - if (n_peers == 0) - return NULL; - - priv = NM_SETTING_WIREGUARD_GET_PRIVATE(self); - - for (i_peer = 0; i_peer < n_peers; i_peer++) { - const NMWireGuardPeer *peer = _peers_get(priv, i_peer)->peer; - GVariantBuilder builder; - - if (!peer->public_key) - continue; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); - - g_variant_builder_add(&builder, - "{sv}", - NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY, - g_variant_new_string(peer->public_key)); - - if (!NM_FLAGS_HAS(flags, NM_CONNECTION_SERIALIZE_ONLY_SECRETS) && peer->endpoint) - g_variant_builder_add( - &builder, - "{sv}", - NM_WIREGUARD_PEER_ATTR_ENDPOINT, - g_variant_new_string(nm_sock_addr_endpoint_get_endpoint(peer->endpoint))); - - if (_nm_connection_serialize_secrets(flags, peer->preshared_key_flags) - && peer->preshared_key) - g_variant_builder_add(&builder, - "{sv}", - NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, - g_variant_new_string(peer->preshared_key)); - - if (!NM_FLAGS_HAS(flags, NM_CONNECTION_SERIALIZE_ONLY_SECRETS) - && peer->preshared_key_flags != NM_SETTING_SECRET_FLAG_NOT_REQUIRED) - g_variant_builder_add(&builder, - "{sv}", - NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS, - g_variant_new_uint32(peer->preshared_key_flags)); - - if (!NM_FLAGS_HAS(flags, NM_CONNECTION_SERIALIZE_ONLY_SECRETS) - && peer->persistent_keepalive != 0) - g_variant_builder_add(&builder, - "{sv}", - NM_WIREGUARD_PEER_ATTR_PERSISTENT_KEEPALIVE, - g_variant_new_uint32(peer->persistent_keepalive)); - - if (!NM_FLAGS_HAS(flags, NM_CONNECTION_SERIALIZE_ONLY_SECRETS) && peer->allowed_ips - && peer->allowed_ips->len > 0) { - const char *const * strv = (const char *const *) peer->allowed_ips->pdata; - gs_free const char **strv_fixed = NULL; - - for (i = 0; i < peer->allowed_ips->len; i++) { - if (strv[i][0] != ALLOWED_IP_INVALID_X) - continue; - if (!strv_fixed) { - strv_fixed = nm_memdup(strv, sizeof(strv[0]) * peer->allowed_ips->len); - strv = strv_fixed; - } - ((const char **) strv)[i]++; - } - g_variant_builder_add(&builder, - "{sv}", - NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS, - g_variant_new_strv(strv, peer->allowed_ips->len)); - } - - if (!any_peers) { - g_variant_builder_init(&peers_builder, G_VARIANT_TYPE("aa{sv}")); - any_peers = TRUE; - } - g_variant_builder_add(&peers_builder, "a{sv}", &builder); - } - - return any_peers ? g_variant_builder_end(&peers_builder) : NULL; -} - -static gboolean -_peers_dbus_only_set(NMSetting * setting, - GVariant * connection_dict, - const char * property, - GVariant * value, - NMSettingParseFlags parse_flags, - GError ** error) -{ - GVariantIter iter_peers; - GVariant * peer_var; - guint i_peer; - gboolean success = FALSE; - gboolean peers_changed = FALSE; - - nm_assert(g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}"))); - - g_variant_iter_init(&iter_peers, value); - - i_peer = 0; - while (g_variant_iter_next(&iter_peers, "@a{sv}", &peer_var)) { - _nm_unused gs_unref_variant GVariant *peer_var_unref = peer_var; - nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL; - const char * cstr; - guint32 u32; - GVariant * var; - - i_peer++; - - if (!g_variant_lookup(peer_var, NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY, "&s", &cstr)) { - if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("peer #%u has no public-key"), - i_peer); - goto out; - } - continue; - } - - peer = nm_wireguard_peer_new(); - if (!nm_wireguard_peer_set_public_key(peer, cstr, TRUE)) { - if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("peer #%u has invalid public-key"), - i_peer); - goto out; - } - continue; - } - - if (g_variant_lookup(peer_var, NM_WIREGUARD_PEER_ATTR_ENDPOINT, "&s", &cstr)) { - nm_auto_unref_sockaddrendpoint NMSockAddrEndpoint *ep = NULL; - - ep = nm_sock_addr_endpoint_new(cstr); - if (!nm_sock_addr_endpoint_get_host(ep)) { - if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("peer #%u has invalid endpoint"), - i_peer); - goto out; - } - } else - _nm_wireguard_peer_set_endpoint(peer, ep); - } - - if (g_variant_lookup(peer_var, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, "&s", &cstr)) - nm_wireguard_peer_set_preshared_key(peer, cstr, TRUE); - - if (g_variant_lookup(peer_var, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS, "u", &u32)) - nm_wireguard_peer_set_preshared_key_flags(peer, u32); - - if (g_variant_lookup(peer_var, NM_WIREGUARD_PEER_ATTR_PERSISTENT_KEEPALIVE, "u", &u32)) - nm_wireguard_peer_set_persistent_keepalive(peer, u32); - - if (g_variant_lookup(peer_var, NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS, "@as", &var)) { - _nm_unused gs_unref_variant GVariant *var_free = var; - gs_free const char ** allowed_ips = NULL; - gsize i, l; - - allowed_ips = g_variant_get_strv(var, &l); - if (allowed_ips) { - for (i = 0; i < l; i++) { - if (_peer_append_allowed_ip(peer, allowed_ips[i], FALSE)) - continue; - if (!NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) - continue; - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("peer #%u has invalid allowed-ips setting"), - i_peer); - goto out; - } - } - } - - if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) { - gs_free_error GError *local = NULL; - - if (!nm_wireguard_peer_is_valid(peer, TRUE, FALSE, &local)) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("peer #%u is invalid: %s"), - i_peer, - local->message); - goto out; - } - } - - /* we could easily reject duplicate peers (by public-key) or duplicate GVariant attributes. - * However, don't do that. In case of duplicate values, the latter peer overwrite the earlier - * and GVariant attributes are ignored by g_variant_lookup() above. */ - if (_peers_append(NM_SETTING_WIREGUARD_GET_PRIVATE(setting), peer, TRUE)) - peers_changed = TRUE; - } - - success = TRUE; - -out: - if (peers_changed) - _peers_notify(setting); - return success; -} - -/*****************************************************************************/ - -static gboolean -verify(NMSetting *setting, NMConnection *connection, GError **error) -{ - NMSettingWireGuard * s_wg = NM_SETTING_WIREGUARD(setting); - NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE(setting); - guint i; - - if (!_nm_connection_verify_required_interface_name(connection, error)) - return FALSE; - - if (!_nm_utils_secret_flags_validate(nm_setting_wireguard_get_private_key_flags(s_wg), - NM_SETTING_WIREGUARD_SETTING_NAME, - NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS, - NM_SETTING_SECRET_FLAG_NOT_REQUIRED, - error)) - return FALSE; - - for (i = 0; i < priv->peers_arr->len; i++) { - NMWireGuardPeer *peer = _peers_get(priv, i)->peer; - - if (!nm_wireguard_peer_is_valid(peer, TRUE, FALSE, error)) { - g_prefix_error(error, - "%s.%s[%u]: ", - NM_SETTING_WIREGUARD_SETTING_NAME, - NM_SETTING_WIREGUARD_PEERS, - i); - return FALSE; - } - } - - if (connection) { - NMSettingIPConfig *s_ip4; - NMSettingIPConfig *s_ip6; - const char * method; - - /* WireGuard is Layer 3 only. For the moment, we only support a restricted set of - * IP methods. We may relax that later, once we fix the implementations so they - * actually work. */ - - if ((s_ip4 = nm_connection_get_setting_ip4_config(connection)) - && (method = nm_setting_ip_config_get_method(s_ip4)) - && !NM_IN_STRSET(method, - NM_SETTING_IP4_CONFIG_METHOD_DISABLED, - NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("method \"%s\" is not supported for WireGuard"), - method); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_IP4_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_METHOD); - return FALSE; - } - - if ((s_ip6 = nm_connection_get_setting_ip6_config(connection)) - && (method = nm_setting_ip_config_get_method(s_ip6)) - && !NM_IN_STRSET(method, - NM_SETTING_IP6_CONFIG_METHOD_IGNORE, - NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, - NM_SETTING_IP6_CONFIG_METHOD_MANUAL, - NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("method \"%s\" is not supported for WireGuard"), - method); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_IP6_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_METHOD); - return FALSE; - } - } - - /* private-key is a secret, hence we cannot verify it like a regular property. */ - return TRUE; -} - -static gboolean -verify_secrets(NMSetting *setting, NMConnection *connection, GError **error) -{ - NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE(setting); - guint i; - - if (priv->private_key && !priv->private_key_valid) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("key must be 32 bytes base64 encoded")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_WIREGUARD_SETTING_NAME, - NM_SETTING_WIREGUARD_PRIVATE_KEY); - return FALSE; - } - - for (i = 0; i < priv->peers_arr->len; i++) { - NMWireGuardPeer *peer = _peers_get(priv, i)->peer; - - if (!nm_wireguard_peer_is_valid(peer, FALSE, TRUE, error)) { - g_prefix_error(error, - "%s.%s[%u]: ", - NM_SETTING_WIREGUARD_SETTING_NAME, - NM_SETTING_WIREGUARD_PEERS, - i); - return FALSE; - } - } - - return TRUE; -} - -static GPtrArray * -need_secrets(NMSetting *setting) -{ - NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE(setting); - GPtrArray * secrets = NULL; - guint i; - - if (!priv->private_key || !priv->private_key_valid) { - secrets = g_ptr_array_new_full(1, g_free); - g_ptr_array_add(secrets, g_strdup(NM_SETTING_WIREGUARD_PRIVATE_KEY)); - } - - for (i = 0; i < priv->peers_arr->len; i++) { - NMWireGuardPeer *peer = _peers_get(priv, i)->peer; - - if (NM_FLAGS_HAS(peer->preshared_key_flags, NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) - continue; - - if (peer->preshared_key_valid) - continue; - - if (!peer->public_key_valid) - continue; - - if (!secrets) - secrets = g_ptr_array_new_full(1, g_free); - g_ptr_array_add(secrets, peers_psk_get_secret_name_dup(peer->public_key)); - } - - return secrets; -} - -static gboolean -clear_secrets(const NMSettInfoSetting * sett_info, - guint property_idx, - NMSetting * setting, - NMSettingClearSecretsWithFlagsFn func, - gpointer user_data) -{ - if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_WIREGUARD_PEERS)) { - NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE(setting); - gboolean peers_changed = FALSE; - guint i, j; - - j = 0; - for (i = 0; i < priv->peers_arr->len; i++) { - NMWireGuardPeer *peer = _peers_get(priv, i)->peer; - - if (!peer->preshared_key) - continue; - - if (func) { - gs_free char *name_free = NULL; - const char * name; - - /* only stack-allocate (alloca) a few times. */ - if (j++ < 5) - name = peers_psk_get_secret_name_a(peer->public_key, &name_free); - else { - name_free = peers_psk_get_secret_name_dup(peer->public_key); - name = name_free; - } - - if (!func(setting, name, peer->preshared_key_flags, user_data)) - continue; - } - - { - nm_auto_unref_wgpeer NMWireGuardPeer *peer2 = NULL; - - peer2 = nm_wireguard_peer_new_clone(peer, FALSE); - - if (_peers_set(priv, peer2, i, FALSE)) - peers_changed = TRUE; - } - } - - if (peers_changed) - _peers_notify(setting); - return peers_changed; - } - - return NM_SETTING_CLASS(nm_setting_wireguard_parent_class) - ->clear_secrets(sett_info, property_idx, setting, func, user_data); -} - -static int -update_one_secret(NMSetting *setting, const char *key, GVariant *value, GError **error) -{ - NMSettingWireGuard * self = NM_SETTING_WIREGUARD(setting); - NMSettingWireGuardPrivate *priv; - gboolean has_changes = FALSE; - gboolean has_error = FALSE; - GVariantIter iter_peers; - GVariant * peer_var; - guint i_peer; - - if (!nm_streq(key, NM_SETTING_WIREGUARD_PEERS)) { - return NM_SETTING_CLASS(nm_setting_wireguard_parent_class) - ->update_one_secret(setting, key, value, error); - } - - if (!g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}"))) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_PROPERTY_NOT_SECRET, - _("invalid peer secrets")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_WIREGUARD_SETTING_NAME, - NM_SETTING_WIREGUARD_PEERS); - return NM_SETTING_UPDATE_SECRET_ERROR; - } - - priv = NM_SETTING_WIREGUARD_GET_PRIVATE(self); - - g_variant_iter_init(&iter_peers, value); - - i_peer = 0; - while (g_variant_iter_next(&iter_peers, "@a{sv}", &peer_var)) { - _nm_unused gs_unref_variant GVariant *peer_var_unref = peer_var; - nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL; - PeerData * pd; - const char * cstr; - - i_peer++; - - if (!g_variant_lookup(peer_var, NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY, "&s", &cstr)) { - if (!has_error) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_PROPERTY_NOT_SECRET, - _("peer #%u lacks public-key"), - i_peer - 1); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_WIREGUARD_SETTING_NAME, - NM_SETTING_WIREGUARD_PEERS); - has_error = TRUE; - } - continue; - } - - pd = _peers_get_by_public_key(priv, cstr, TRUE); - if (!pd) { - if (!has_error) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_PROPERTY_NOT_SECRET, - _("non-existing peer '%s'"), - cstr); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_WIREGUARD_SETTING_NAME, - NM_SETTING_WIREGUARD_PEERS); - has_error = TRUE; - } - continue; - } - - if (!g_variant_lookup(peer_var, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, "&s", &cstr)) { - /* no preshared-key. Ignore the rest. - * - * In particular, we don't reject all unknown fields. */ - continue; - } - - if (nm_streq0(cstr, nm_wireguard_peer_get_preshared_key(pd->peer))) - continue; - - peer = nm_wireguard_peer_new_clone(pd->peer, FALSE); - nm_wireguard_peer_set_preshared_key(peer, cstr, TRUE); - - if (!_peers_set(priv, peer, pd->idx, FALSE)) - nm_assert_not_reached(); - has_changes = TRUE; - } - - if (has_error) - return NM_SETTING_UPDATE_SECRET_ERROR; - if (has_changes) - return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED; - return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED; -} - -static NMTernary -compare_property(const NMSettInfoSetting *sett_info, - guint property_idx, - NMConnection * con_a, - NMSetting * set_a, - NMConnection * con_b, - NMSetting * set_b, - NMSettingCompareFlags flags) -{ - NMSettingWireGuardPrivate *a_priv; - NMSettingWireGuardPrivate *b_priv; - guint i; - - if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_WIREGUARD_PEERS)) { - if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)) - return NM_TERNARY_DEFAULT; - - if (!set_b) - return TRUE; - - a_priv = NM_SETTING_WIREGUARD_GET_PRIVATE(set_a); - b_priv = NM_SETTING_WIREGUARD_GET_PRIVATE(set_b); - - if (a_priv->peers_arr->len != b_priv->peers_arr->len) - return FALSE; - for (i = 0; i < a_priv->peers_arr->len; i++) { - NMWireGuardPeer *a_peer = _peers_get(a_priv, i)->peer; - NMWireGuardPeer *b_peer = _peers_get(b_priv, i)->peer; - - if (nm_wireguard_peer_cmp(a_peer, b_peer, flags) != 0) - return FALSE; - } - - return TRUE; - } - - return NM_SETTING_CLASS(nm_setting_wireguard_parent_class) - ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags); -} - -static void -duplicate_copy_properties(const NMSettInfoSetting *sett_info, NMSetting *src, NMSetting *dst) -{ - NMSettingWireGuardPrivate *priv_src = NM_SETTING_WIREGUARD_GET_PRIVATE(src); - NMSettingWireGuardPrivate *priv_dst = NM_SETTING_WIREGUARD_GET_PRIVATE(dst); - guint i; - gboolean peers_changed = FALSE; - - NM_SETTING_CLASS(nm_setting_wireguard_parent_class) - ->duplicate_copy_properties(sett_info, src, dst); - - /* We don't bother comparing the existing peers with what we are about to set. - * Always reset all. */ - if (_peers_clear(priv_dst) > 0) - peers_changed = TRUE; - for (i = 0; i < priv_src->peers_arr->len; i++) { - if (_peers_append(priv_dst, _peers_get(priv_src, i)->peer, FALSE)) - peers_changed = TRUE; - } - if (peers_changed) - _peers_notify(dst); -} - -static void -enumerate_values(const NMSettInfoProperty *property_info, - NMSetting * setting, - NMSettingValueIterFn func, - gpointer user_data) -{ - if (nm_streq(property_info->name, NM_SETTING_WIREGUARD_PEERS)) { - NMSettingWireGuardPrivate * priv = NM_SETTING_WIREGUARD_GET_PRIVATE(setting); - nm_auto_unset_gvalue GValue value = G_VALUE_INIT; - GPtrArray * ptr = NULL; - guint i; - - if (priv->peers_arr && priv->peers_arr->len > 0) { - ptr = g_ptr_array_new_with_free_func((GDestroyNotify) nm_wireguard_peer_unref); - for (i = 0; i < priv->peers_arr->len; i++) - g_ptr_array_add(ptr, nm_wireguard_peer_ref(_peers_get(priv, i)->peer)); - } - g_value_init(&value, G_TYPE_PTR_ARRAY); - g_value_take_boxed(&value, ptr); - func(setting, property_info->name, &value, 0, user_data); - return; - } - - NM_SETTING_CLASS(nm_setting_wireguard_parent_class) - ->enumerate_values(property_info, setting, func, user_data); -} - -static gboolean -aggregate(NMSetting *setting, int type_i, gpointer arg) -{ - NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE(setting); - NMConnectionAggregateType type = type_i; - NMSettingSecretFlags secret_flags; - guint i; - - nm_assert(NM_IN_SET(type, - NM_CONNECTION_AGGREGATE_ANY_SECRETS, - NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS)); - - switch (type) { - case NM_CONNECTION_AGGREGATE_ANY_SECRETS: - if (priv->private_key) - goto out_done; - for (i = 0; i < priv->peers_arr->len; i++) { - if (nm_wireguard_peer_get_preshared_key(_peers_get(priv, i)->peer)) - goto out_done; - } - break; - - case NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS: -#if NM_MORE_ASSERTS - if (!nm_setting_get_secret_flags(setting, - NM_SETTING_WIREGUARD_PRIVATE_KEY, - &secret_flags, - NULL)) - nm_assert_not_reached(); - nm_assert(secret_flags == priv->private_key_flags); -#endif - if (priv->private_key_flags == NM_SETTING_SECRET_FLAG_NONE) - goto out_done; - for (i = 0; i < priv->peers_arr->len; i++) { - secret_flags = nm_wireguard_peer_get_preshared_key_flags(_peers_get(priv, i)->peer); - if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) - goto out_done; - } - break; - } - - return FALSE; - -out_done: - *((gboolean *) arg) = TRUE; - return TRUE; -} - -static gboolean -get_secret_flags(NMSetting * setting, - const char * secret_name, - NMSettingSecretFlags *out_flags, - GError ** error) -{ - if (NM_STR_HAS_PREFIX(secret_name, NM_SETTING_WIREGUARD_PEERS ".")) { - NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE(setting); - gs_free char * public_key_free = NULL; - const char * public_key; - PeerData * pd; - - public_key = peers_psk_get_secret_parse_a(secret_name, &public_key_free); - if (public_key && (pd = _peers_get_by_public_key(priv, public_key, FALSE))) { - NM_SET_OUT(out_flags, nm_wireguard_peer_get_preshared_key_flags(pd->peer)); - return TRUE; - } - } - - return NM_SETTING_CLASS(nm_setting_wireguard_parent_class) - ->get_secret_flags(setting, secret_name, out_flags, error); -} - -static gboolean -set_secret_flags(NMSetting * setting, - const char * secret_name, - NMSettingSecretFlags flags, - GError ** error) -{ - if (NM_STR_HAS_PREFIX(secret_name, NM_SETTING_WIREGUARD_PEERS ".")) { - NMSettingWireGuard * self = NM_SETTING_WIREGUARD(setting); - NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE(self); - gs_free char * public_key_free = NULL; - const char * public_key; - PeerData * pd; - - public_key = peers_psk_get_secret_parse_a(secret_name, &public_key_free); - if (public_key && (pd = _peers_get_by_public_key(priv, public_key, FALSE))) { - if (nm_wireguard_peer_get_preshared_key_flags(pd->peer) != flags) { - nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL; - - peer = nm_wireguard_peer_new_clone(pd->peer, TRUE); - peer->preshared_key_flags = flags; - if (_peers_set(priv, peer, pd->idx, FALSE)) - _peers_notify(self); - } - - return TRUE; - } - } - - return NM_SETTING_CLASS(nm_setting_wireguard_parent_class) - ->set_secret_flags(setting, secret_name, flags, error); -} - -static void -for_each_secret(NMSetting * setting, - const char * data_key, - GVariant * data_val, - gboolean remove_non_secrets, - _NMConnectionForEachSecretFunc callback, - gpointer callback_data, - GVariantBuilder * setting_builder) -{ - NMSettingWireGuard * s_wg; - NMSettingWireGuardPrivate *priv; - GVariantBuilder peers_builder; - GVariantIter * peer_iter; - GVariantIter data_iter; - const char * key; - - if (!nm_streq(data_key, NM_SETTING_WIREGUARD_PEERS)) { - NM_SETTING_CLASS(nm_setting_wireguard_parent_class) - ->for_each_secret(setting, - data_key, - data_val, - remove_non_secrets, - callback, - callback_data, - setting_builder); - return; - } - - if (!g_variant_is_of_type(data_val, G_VARIANT_TYPE("aa{sv}"))) { - /* invalid type. Silently ignore content as we cannot find secret-keys - * here. */ - return; - } - - s_wg = NM_SETTING_WIREGUARD(setting); - priv = NM_SETTING_WIREGUARD_GET_PRIVATE(s_wg); - - g_variant_builder_init(&peers_builder, G_VARIANT_TYPE("aa{sv}")); - g_variant_iter_init(&data_iter, data_val); - while (g_variant_iter_next(&data_iter, "a{sv}", &peer_iter)) { - _nm_unused nm_auto_free_variant_iter GVariantIter *peer_iter_free = peer_iter; - gs_unref_variant GVariant *preshared_key = NULL; - PeerData * pd = NULL; - NMSettingSecretFlags secret_flags; - GVariant * val; - GVariantBuilder peer_builder; - - g_variant_builder_init(&peer_builder, G_VARIANT_TYPE("a{sv}")); - - while (g_variant_iter_next(peer_iter, "{&sv}", &key, &val)) { - _nm_unused gs_unref_variant GVariant *val_free = val; - - if (nm_streq(key, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)) { - if (!preshared_key && g_variant_is_of_type(val, G_VARIANT_TYPE_STRING)) - preshared_key = g_variant_ref(val); - continue; - } - - if (nm_streq(key, NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY)) { - if (!pd && g_variant_is_of_type(val, G_VARIANT_TYPE_STRING)) - pd = _peers_get_by_public_key(priv, g_variant_get_string(val, NULL), TRUE); - } else if (remove_non_secrets) - continue; - - g_variant_builder_add(&peer_builder, "{sv}", key, val); - } - - if (pd && preshared_key) { - /* without specifying a public-key of an existing peer, the secret is - * ignored. */ - secret_flags = nm_wireguard_peer_get_preshared_key_flags(pd->peer); - if (callback(secret_flags, callback_data)) - g_variant_builder_add(&peer_builder, - "{sv}", - NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, - preshared_key); - } - - g_variant_builder_add(&peers_builder, "a{sv}", &peer_builder); - } - - g_variant_builder_add(setting_builder, - "{sv}", - NM_SETTING_WIREGUARD_PEERS, - g_variant_builder_end(&peers_builder)); -} - -/*****************************************************************************/ - -static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - NMSettingWireGuard * setting = NM_SETTING_WIREGUARD(object); - NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE(setting); - - switch (prop_id) { - case PROP_FWMARK: - g_value_set_uint(value, priv->fwmark); - break; - case PROP_IP4_AUTO_DEFAULT_ROUTE: - g_value_set_enum(value, priv->ip4_auto_default_route); - break; - case PROP_IP6_AUTO_DEFAULT_ROUTE: - g_value_set_enum(value, priv->ip6_auto_default_route); - break; - case PROP_LISTEN_PORT: - g_value_set_uint(value, priv->listen_port); - break; - case PROP_MTU: - g_value_set_uint(value, priv->mtu); - break; - case PROP_PEER_ROUTES: - g_value_set_boolean(value, priv->peer_routes); - break; - case PROP_PRIVATE_KEY: - g_value_set_string(value, priv->private_key); - break; - case PROP_PRIVATE_KEY_FLAGS: - g_value_set_flags(value, priv->private_key_flags); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE(object); - const char * str; - - switch (prop_id) { - case PROP_FWMARK: - priv->fwmark = g_value_get_uint(value); - break; - case PROP_IP4_AUTO_DEFAULT_ROUTE: - priv->ip4_auto_default_route = g_value_get_enum(value); - break; - case PROP_IP6_AUTO_DEFAULT_ROUTE: - priv->ip6_auto_default_route = g_value_get_enum(value); - break; - case PROP_LISTEN_PORT: - priv->listen_port = g_value_get_uint(value); - break; - case PROP_MTU: - priv->mtu = g_value_get_uint(value); - break; - case PROP_PEER_ROUTES: - priv->peer_routes = g_value_get_boolean(value); - break; - case PROP_PRIVATE_KEY: - nm_clear_pointer(&priv->private_key, nm_free_secret); - str = g_value_get_string(value); - if (str) { - if (nm_utils_base64secret_normalize(str, - NM_WIREGUARD_PUBLIC_KEY_LEN, - &priv->private_key)) - priv->private_key_valid = TRUE; - else { - priv->private_key = g_strdup(str); - priv->private_key_valid = FALSE; - } - } - break; - case PROP_PRIVATE_KEY_FLAGS: - priv->private_key_flags = g_value_get_flags(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - -static void -nm_setting_wireguard_init(NMSettingWireGuard *setting) -{ - NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE(setting); - - priv->peers_arr = g_ptr_array_new(); - priv->peers_hash = g_hash_table_new(nm_pstr_hash, nm_pstr_equal); - priv->peer_routes = TRUE; - priv->ip4_auto_default_route = NM_TERNARY_DEFAULT; - priv->ip6_auto_default_route = NM_TERNARY_DEFAULT; -} - -/** - * nm_setting_wireguard_new: - * - * Creates a new #NMSettingWireGuard object with default values. - * - * Returns: (transfer full): the new empty #NMSettingWireGuard object - * - * Since: 1.16 - **/ -NMSetting * -nm_setting_wireguard_new(void) -{ - return g_object_new(NM_TYPE_SETTING_WIREGUARD, NULL); -} - -static void -finalize(GObject *object) -{ - NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE(object); - - nm_free_secret(priv->private_key); - - _peers_clear(priv); - g_ptr_array_unref(priv->peers_arr); - g_hash_table_unref(priv->peers_hash); - - G_OBJECT_CLASS(nm_setting_wireguard_parent_class)->finalize(object); -} - -static void -nm_setting_wireguard_class_init(NMSettingWireGuardClass *klass) -{ - GObjectClass * object_class = G_OBJECT_CLASS(klass); - NMSettingClass *setting_class = NM_SETTING_CLASS(klass); - GArray * properties_override = _nm_sett_info_property_override_create_array(); - - object_class->get_property = get_property; - object_class->set_property = set_property; - object_class->finalize = finalize; - - setting_class->verify = verify; - setting_class->verify_secrets = verify_secrets; - setting_class->need_secrets = need_secrets; - setting_class->clear_secrets = clear_secrets; - setting_class->update_one_secret = update_one_secret; - setting_class->compare_property = compare_property; - setting_class->duplicate_copy_properties = duplicate_copy_properties; - setting_class->enumerate_values = enumerate_values; - setting_class->aggregate = aggregate; - setting_class->get_secret_flags = get_secret_flags; - setting_class->set_secret_flags = set_secret_flags; - setting_class->for_each_secret = for_each_secret; - - /** - * NMSettingWireGuard:private-key: - * - * The 256 bit private-key in base64 encoding. - * - * Since: 1.16 - **/ - obj_properties[PROP_PRIVATE_KEY] = - g_param_spec_string(NM_SETTING_WIREGUARD_PRIVATE_KEY, - "", - "", - NULL, - G_PARAM_READWRITE | NM_SETTING_PARAM_SECRET | G_PARAM_STATIC_STRINGS); - - /** - * NMSettingWireGuard:private-key-flags: - * - * Flags indicating how to handle the #NMSettingWirelessSecurity:private-key - * property. - * - * Since: 1.16 - **/ - obj_properties[PROP_PRIVATE_KEY_FLAGS] = - g_param_spec_flags(NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS, - "", - "", - NM_TYPE_SETTING_SECRET_FLAGS, - NM_SETTING_SECRET_FLAG_NONE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - /** - * NMSettingWireGuard:fwmark: - * - * The use of fwmark is optional and is by default off. Setting it to 0 - * disables it. Otherwise, it is a 32-bit fwmark for outgoing packets. - * - * Note that "ip4-auto-default-route" or "ip6-auto-default-route" enabled, - * implies to automatically choose a fwmark. - * - * Since: 1.16 - **/ - obj_properties[PROP_FWMARK] = - g_param_spec_uint(NM_SETTING_WIREGUARD_FWMARK, - "", - "", - 0, - G_MAXUINT32, - 0, - G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS); - - /** - * NMSettingWireGuard:listen-port: - * - * The listen-port. If listen-port is not specified, the port will be chosen - * randomly when the interface comes up. - * - * Since: 1.16 - **/ - obj_properties[PROP_LISTEN_PORT] = - g_param_spec_uint(NM_SETTING_WIREGUARD_LISTEN_PORT, - "", - "", - 0, - 65535, - 0, - G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS); - - /** - * NMSettingWireGuard:peer-routes: - * - * Whether to automatically add routes for the AllowedIPs ranges - * of the peers. If %TRUE (the default), NetworkManager will automatically - * add routes in the routing tables according to ipv4.route-table and - * ipv6.route-table. Usually you want this automatism enabled. - * If %FALSE, no such routes are added automatically. In this case, the - * user may want to configure static routes in ipv4.routes and ipv6.routes, - * respectively. - * - * Note that if the peer's AllowedIPs is "0.0.0.0/0" or "::/0" and the profile's - * ipv4.never-default or ipv6.never-default setting is enabled, the peer route for - * this peer won't be added automatically. - * - * Since: 1.16 - **/ - obj_properties[PROP_PEER_ROUTES] = g_param_spec_boolean( - NM_SETTING_WIREGUARD_PEER_ROUTES, - "", - "", - TRUE, - G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS); - - /** - * NMSettingWireGuard:mtu: - * - * If non-zero, only transmit packets of the specified size or smaller, - * breaking larger packets up into multiple fragments. - * - * If zero a default MTU is used. Note that contrary to wg-quick's MTU - * setting, this does not take into account the current routes at the - * time of activation. - * - * Since: 1.16 - **/ - obj_properties[PROP_MTU] = - g_param_spec_uint(NM_SETTING_WIREGUARD_MTU, - "", - "", - 0, - G_MAXUINT32, - 0, - G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS); - - /** - * NMSettingWireGuard:ip4-auto-default-route: - * - * Whether to enable special handling of the IPv4 default route. - * If enabled, the IPv4 default route from wireguard.peer-routes - * will be placed to a dedicated routing-table and two policy routing rules - * will be added. The fwmark number is also used as routing-table for the default-route, - * and if fwmark is zero, an unused fwmark/table is chosen automatically. - * This corresponds to what wg-quick does with Table=auto and what WireGuard - * calls "Improved Rule-based Routing". - * - * Note that for this automatism to work, you usually don't want to set - * ipv4.gateway, because that will result in a conflicting default route. - * - * Leaving this at the default will enable this option automatically - * if ipv4.never-default is not set and there are any peers that use - * a default-route as allowed-ips. - * - * Since: 1.20 - **/ - obj_properties[PROP_IP4_AUTO_DEFAULT_ROUTE] = g_param_spec_enum( - NM_SETTING_WIREGUARD_IP4_AUTO_DEFAULT_ROUTE, - "", - "", - NM_TYPE_TERNARY, - NM_TERNARY_DEFAULT, - NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - /** - * NMSettingWireGuard:ip6-auto-default-route: - * - * Like ip4-auto-default-route, but for the IPv6 default route. - * - * Since: 1.20 - **/ - obj_properties[PROP_IP6_AUTO_DEFAULT_ROUTE] = g_param_spec_enum( - NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE, - "", - "", - NM_TYPE_TERNARY, - NM_TERNARY_DEFAULT, - NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - /* ---dbus--- - * property: peers - * format: array of 'a{sv}' - * description: Array of dictionaries for the WireGuard peers. - * ---end--- - */ - _nm_properties_override_dbus( - properties_override, - NM_SETTING_WIREGUARD_PEERS, - NM_SETT_INFO_PROPERT_TYPE(.dbus_type = NM_G_VARIANT_TYPE("aa{sv}"), - .to_dbus_fcn = _peers_dbus_only_synth, - .from_dbus_fcn = _peers_dbus_only_set, )); - - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - _nm_setting_class_commit_full(setting_class, - NM_META_SETTING_TYPE_WIREGUARD, - NULL, - properties_override); -} |