core,libnm-core: introduce property flag for certificate and keys

If we add a new property in the future and it references a certificate
or key stored on disk, we need to also implement the logic to verify
the access to the file for private connections.

Add a new property flag NM_SETTING_PARAM_CERT_KEY_FILE to existing
certificate and key properties, so that it's easier to see that they
need special treatment. Also add some assertions to verify that the
properties with the flag are handled properly.

While at it, move the enumeration of private-files to the settings.
This commit is contained in:
Beniamino Galvani 2025-10-27 17:40:14 +01:00 committed by Íñigo Huguet
parent e85cc46d0b
commit 8d8edda3f4
5 changed files with 143 additions and 48 deletions

View file

@ -5667,47 +5667,19 @@ nm_utils_get_connection_first_permissions_user(NMConnection *connection)
/*****************************************************************************/
static void
get_8021x_private_files(NMConnection *connection, GPtrArray *files)
{
const struct {
NMSetting8021xCKScheme (*get_scheme_func)(NMSetting8021x *);
const char *(*get_path_func)(NMSetting8021x *);
} funcs[] = {
{nm_setting_802_1x_get_ca_cert_scheme, nm_setting_802_1x_get_ca_cert_path},
{nm_setting_802_1x_get_client_cert_scheme, nm_setting_802_1x_get_client_cert_path},
{nm_setting_802_1x_get_private_key_scheme, nm_setting_802_1x_get_private_key_path},
{nm_setting_802_1x_get_phase2_ca_cert_scheme, nm_setting_802_1x_get_phase2_ca_cert_path},
{nm_setting_802_1x_get_phase2_client_cert_scheme,
nm_setting_802_1x_get_phase2_client_cert_path},
{nm_setting_802_1x_get_phase2_private_key_scheme,
nm_setting_802_1x_get_phase2_private_key_path},
};
NMSetting8021x *s_8021x;
const char *path;
guint i;
s_8021x = nm_connection_get_setting_802_1x(connection);
if (!s_8021x)
return;
for (i = 0; i < G_N_ELEMENTS(funcs); i++) {
if (funcs[i].get_scheme_func(s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH) {
path = funcs[i].get_path_func(s_8021x);
if (path) {
g_ptr_array_add(files, (gpointer) path);
}
}
}
}
const char **
nm_utils_get_connection_private_files_paths(NMConnection *connection)
{
GPtrArray *files;
gs_free NMSetting **settings = NULL;
guint num_settings;
guint i;
files = g_ptr_array_new();
get_8021x_private_files(connection, files);
settings = nm_connection_get_settings(connection, &num_settings);
for (i = 0; i < num_settings; i++) {
_nm_setting_get_private_files(settings[i], files);
}
g_ptr_array_add(files, NULL);
return (const char **) g_ptr_array_free(files, files->len == 1);

View file

@ -3133,6 +3133,86 @@ need_secrets(NMSetting *setting, gboolean check_rerequest)
/*****************************************************************************/
static void
get_private_files(NMSetting *setting, GPtrArray *files)
{
const struct {
const char *property;
NMSetting8021xCKScheme (*get_scheme_func)(NMSetting8021x *);
const char *(*get_path_func)(NMSetting8021x *);
} cert_props[] = {
{NM_SETTING_802_1X_CA_CERT,
nm_setting_802_1x_get_ca_cert_scheme,
nm_setting_802_1x_get_ca_cert_path},
{NM_SETTING_802_1X_CLIENT_CERT,
nm_setting_802_1x_get_client_cert_scheme,
nm_setting_802_1x_get_client_cert_path},
{NM_SETTING_802_1X_PRIVATE_KEY,
nm_setting_802_1x_get_private_key_scheme,
nm_setting_802_1x_get_private_key_path},
{NM_SETTING_802_1X_PHASE2_CA_CERT,
nm_setting_802_1x_get_phase2_ca_cert_scheme,
nm_setting_802_1x_get_phase2_ca_cert_path},
{NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
nm_setting_802_1x_get_phase2_client_cert_scheme,
nm_setting_802_1x_get_phase2_client_cert_path},
{NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
nm_setting_802_1x_get_phase2_private_key_scheme,
nm_setting_802_1x_get_phase2_private_key_path},
};
NMSetting8021x *s_8021x = NM_SETTING_802_1X(setting);
const char *path;
guint i;
if (NM_MORE_ASSERT_ONCE(5)) {
GObjectClass *klass;
gs_free GParamSpec **properties = NULL;
guint n_properties;
gboolean found;
guint j;
/* Check that all the properties in the setting with flag CERT_KEY_FILE
* are listed in the table, and vice versa. */
klass = G_OBJECT_GET_CLASS(setting);
properties = g_object_class_list_properties(klass, &n_properties);
for (i = 0; i < n_properties; i++) {
if (!(properties[i]->flags & NM_SETTING_PARAM_CERT_KEY_FILE))
continue;
found = FALSE;
for (j = 0; j < G_N_ELEMENTS(cert_props); j++) {
if (nm_streq0(properties[i]->name, cert_props[j].property)) {
found = TRUE;
break;
}
}
nm_assert(found);
}
for (i = 0; i < G_N_ELEMENTS(cert_props); i++) {
GParamSpec *prop;
prop = g_object_class_find_property(klass, cert_props[i].property);
nm_assert(prop);
nm_assert(prop->flags & NM_SETTING_PARAM_CERT_KEY_FILE);
}
}
for (i = 0; i < G_N_ELEMENTS(cert_props); i++) {
if (cert_props[i].get_scheme_func(s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH) {
path = cert_props[i].get_path_func(s_8021x);
if (path) {
g_ptr_array_add(files, (gpointer) path);
}
}
}
}
/*****************************************************************************/
static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
@ -3225,6 +3305,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
setting_class->verify = verify;
setting_class->need_secrets = need_secrets;
setting_class->get_private_files = get_private_files;
/**
* NMSetting8021x:eap:
@ -3359,7 +3440,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
obj_properties,
NM_SETTING_802_1X_CA_CERT,
PROP_CA_CERT,
NM_SETTING_PARAM_NONE,
NM_SETTING_PARAM_CERT_KEY_FILE,
NMSetting8021xPrivate,
ca_cert);
@ -3556,7 +3637,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
obj_properties,
NM_SETTING_802_1X_CLIENT_CERT,
PROP_CLIENT_CERT,
NM_SETTING_PARAM_NONE,
NM_SETTING_PARAM_CERT_KEY_FILE,
NMSetting8021xPrivate,
client_cert);
@ -3803,7 +3884,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
obj_properties,
NM_SETTING_802_1X_PHASE2_CA_CERT,
PROP_PHASE2_CA_CERT,
NM_SETTING_PARAM_NONE,
NM_SETTING_PARAM_CERT_KEY_FILE,
NMSetting8021xPrivate,
phase2_ca_cert);
@ -4006,7 +4087,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
obj_properties,
NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
PROP_PHASE2_CLIENT_CERT,
NM_SETTING_PARAM_NONE,
NM_SETTING_PARAM_CERT_KEY_FILE,
NMSetting8021xPrivate,
phase2_client_cert);
@ -4175,7 +4256,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
obj_properties,
NM_SETTING_802_1X_PRIVATE_KEY,
PROP_PRIVATE_KEY,
NM_SETTING_PARAM_NONE,
NM_SETTING_PARAM_CERT_KEY_FILE,
NMSetting8021xPrivate,
private_key);
@ -4276,7 +4357,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
obj_properties,
NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
PROP_PHASE2_PRIVATE_KEY,
NM_SETTING_PARAM_NONE,
NM_SETTING_PARAM_CERT_KEY_FILE,
NMSetting8021xPrivate,
phase2_private_key);

View file

@ -154,6 +154,11 @@ struct _NMSettingClass {
guint /* NMSettingParseFlags */ parse_flags,
GError **error);
/* returns a list of certificate/key files referenced in the connection.
* When the connection is private, we need to verify that the owner of
* the connection has access to them. */
void (*get_private_files)(NMSetting *setting, GPtrArray *files);
const struct _NMMetaSettingInfo *setting_info;
};
@ -334,6 +339,11 @@ struct _NMRange {
*/
#define NM_SETTING_PARAM_TO_DBUS_IGNORE_FLAGS (1 << (7 + G_PARAM_USER_SHIFT))
/* The property can refer to a certificate or key stored on disk. As such,
* special care is needed when accessing the file for private connections.
*/
#define NM_SETTING_PARAM_CERT_KEY_FILE (1 << (8 + G_PARAM_USER_SHIFT))
extern const NMSettInfoPropertType nm_sett_info_propert_type_setting_name;
extern const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_interface_name;
extern const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_ignore_i;
@ -859,9 +869,10 @@ _nm_properties_override(GArray *properties_override, const NMSettInfoProperty *p
{ \
GParamSpec *_param_spec; \
\
G_STATIC_ASSERT(!NM_FLAGS_ANY((param_flags), \
G_STATIC_ASSERT( \
!NM_FLAGS_ANY((param_flags), \
~(NM_SETTING_PARAM_SECRET | NM_SETTING_PARAM_INFERRABLE \
| NM_SETTING_PARAM_FUZZY_IGNORE))); \
| NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_CERT_KEY_FILE))); \
\
_param_spec = g_param_spec_boxed("" prop_name "", \
"", \

View file

@ -2262,6 +2262,34 @@ init_from_dbus(NMSetting *setting,
return TRUE;
}
static void
get_private_files(NMSetting *setting, GPtrArray *files)
{
if (NM_MORE_ASSERTS) {
GParamSpec **properties;
guint n_properties;
int i;
properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(setting), &n_properties);
for (i = 0; i < n_properties; i++) {
if (properties[i]->flags & NM_SETTING_PARAM_CERT_KEY_FILE) {
/* Certificates and keys needs special handling, see setting 802.1X */
nm_assert_not_reached();
}
}
g_free(properties);
}
}
void
_nm_setting_get_private_files(NMSetting *setting, GPtrArray *files)
{
g_return_if_fail(NM_IS_SETTING(setting));
g_return_if_fail(files);
NM_SETTING_GET_CLASS(setting)->get_private_files(setting, files);
}
/**
* nm_setting_get_dbus_property_type:
* @setting: an #NMSetting
@ -4672,6 +4700,7 @@ nm_setting_class_init(NMSettingClass *setting_class)
setting_class->enumerate_values = enumerate_values;
setting_class->aggregate = aggregate;
setting_class->init_from_dbus = init_from_dbus;
setting_class->get_private_files = get_private_files;
/**
* NMSetting:name:

View file

@ -1192,4 +1192,6 @@ const GPtrArray *_nm_setting_ovs_port_get_trunks_arr(NMSettingOvsPort *self);
guint _nm_setting_connection_get_num_permissions_users(NMSettingConnection *setting);
const char *_nm_setting_connection_get_first_permissions_user(NMSettingConnection *setting);
void _nm_setting_get_private_files(NMSetting *setting, GPtrArray *files);
#endif