NetworkManager/libnm-core/nm-setting-wireguard.c
Thomas Haller 3b69f02164 all: unify format of our Copyright source code comments
```bash

readarray -d '' FILES < <(
  git ls-files -z \
    ':(exclude)po' \
    ':(exclude)shared/c-rbtree' \
    ':(exclude)shared/c-list' \
    ':(exclude)shared/c-siphash' \
    ':(exclude)shared/c-stdaux' \
    ':(exclude)shared/n-acd' \
    ':(exclude)shared/n-dhcp4' \
    ':(exclude)src/systemd/src' \
    ':(exclude)shared/systemd/src' \
    ':(exclude)m4' \
    ':(exclude)COPYING*'
  )

sed \
  -e 's/^\(--\|#\| \*\) *\(([cC]) *\)\?Copyright \+\(\(([cC])\) \+\)\?\(\(20\|19\)[0-9][0-9]\) *[-–] *\(\(20\|19\)[0-9][0-9]\) \+\([^ ].*\)$/\1 C1pyright#\5 - \7#\9/' \
  -e 's/^\(--\|#\| \*\) *\(([cC]) *\)\?Copyright \+\(\(([cC])\) \+\)\?\(\(20\|19\)[0-9][0-9]\) *[,] *\(\(20\|19\)[0-9][0-9]\) \+\([^ ].*\)$/\1 C2pyright#\5, \7#\9/' \
  -e 's/^\(--\|#\| \*\) *\(([cC]) *\)\?Copyright \+\(\(([cC])\) \+\)\?\(\(20\|19\)[0-9][0-9]\) \+\([^ ].*\)$/\1 C3pyright#\5#\7/' \
  -e 's/^Copyright \(\(20\|19\)[0-9][0-9]\) \+\([^ ].*\)$/C4pyright#\1#\3/' \
  -i \
  "${FILES[@]}"

echo ">>> untouched Copyright lines"
git grep Copyright "${FILES[@]}"

echo ">>> Copyright lines with unusual extra"
git grep '\<C[0-9]pyright#' "${FILES[@]}" | grep -i reserved

sed \
  -e 's/\<C[0-9]pyright#\([^#]*\)#\(.*\)$/Copyright (C) \1 \2/' \
  -i \
  "${FILES[@]}"

```

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/298
2019-10-02 17:03:52 +02:00

2586 lines
77 KiB
C

// SPDX-License-Identifier: LGPL-2.1+
/*
* Copyright (C) 2018 - 2019 Red Hat, Inc.
*/
#include "nm-default.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 expception, 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 accociate 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;
g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), NULL);
if ( !self->allowed_ips
|| idx >= self->allowed_ips->len) {
NM_SET_OUT (out_is_valid, FALSE);
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.
* 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.
*
* 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 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, a unused fwmark/table is chosen automatically.
* This corresponds to what wg-quick does with Table=auto.
*
* 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);
}