diff options
Diffstat (limited to 'libnm-core/nm-setting-connection.c')
-rw-r--r-- | libnm-core/nm-setting-connection.c | 352 |
1 files changed, 195 insertions, 157 deletions
diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c index 9a2ab4988e..5fa6cb3506 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -31,13 +31,14 @@ /*****************************************************************************/ -typedef enum { - PERM_TYPE_USER = 0, +typedef enum _nm_packed { + PERM_TYPE_INVALID, + PERM_TYPE_USER, } PermType; typedef struct { - guint8 ptype; - char * item; + PermType ptype; + char * item; } Permission; NM_GOBJECT_PROPERTIES_DEFINE(NMSettingConnection, @@ -68,7 +69,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingConnection, PROP_MUD_URL, ); typedef struct { - GSList *permissions; /* list of Permission structs */ + GArray *permissions; GSList *secondaries; /* secondary connections to activate with the base connection */ char * id; char * uuid; @@ -102,83 +103,85 @@ G_DEFINE_TYPE(NMSettingConnection, nm_setting_connection, NM_TYPE_SETTING) /*****************************************************************************/ -#define PERM_USER_PREFIX "user:" +static void +_permission_set_stale(Permission *permission, PermType ptype, char *item_take) +{ + nm_assert(permission); + nm_assert(NM_IN_SET(ptype, PERM_TYPE_INVALID, PERM_TYPE_USER)); + nm_assert(ptype != PERM_TYPE_USER + || nm_settings_connection_validate_permission_user(item_take, -1)); + + /* we don't inspect (clear) permission before setting. It takes a + * stale instance. */ + *permission = (Permission){ + .ptype = ptype, + .item = item_take, + }; +} + +static void +_permission_clear_stale(Permission *permission) +{ + g_free(permission->item); + /* We leave the permission instance with dangling pointers. + * It is "stale". */ +} -static Permission * -permission_new_from_str(const char *str) +static gboolean +_permission_set_stale_parse(Permission *permission, const char *str) { - Permission *p; + const char *str0 = str; const char *last_colon; - size_t ulen = 0, i; + gsize ulen; + + nm_assert(str); - g_return_val_if_fail(strncmp(str, PERM_USER_PREFIX, strlen(PERM_USER_PREFIX)) == 0, NULL); - str += strlen(PERM_USER_PREFIX); + if (!str) + goto invalid; + + if (!NM_STR_HAS_PREFIX(str, NM_SETTINGS_CONNECTION_PERMISSION_USER_PREFIX)) + goto invalid; + + str += NM_STRLEN(NM_SETTINGS_CONNECTION_PERMISSION_USER_PREFIX); last_colon = strrchr(str, ':'); if (last_colon) { - /* Ensure that somebody didn't pass "user::" */ - g_return_val_if_fail(last_colon > str, NULL); - /* Reject :[detail] for now */ - g_return_val_if_fail(*(last_colon + 1) == '\0', NULL); - - /* Make sure we don't include detail in the username */ + if (last_colon[1] != '\0') + goto invalid; ulen = last_colon - str; } else ulen = strlen(str); - /* Sanity check the length of the username */ - g_return_val_if_fail(ulen < 100, NULL); - - /* Make sure there's no ':' in the username */ - for (i = 0; i < ulen; i++) - g_return_val_if_fail(str[i] != ':', NULL); - - /* And the username must be valid UTF-8 */ - g_return_val_if_fail(g_utf8_validate(str, -1, NULL) == TRUE, NULL); + if (!nm_settings_connection_validate_permission_user(str, ulen)) + goto invalid; /* Yay, valid... create the new permission */ - p = g_slice_new0(Permission); - p->ptype = PERM_TYPE_USER; - if (last_colon) { - p->item = g_malloc(ulen + 1); - memcpy(p->item, str, ulen); - p->item[ulen] = '\0'; - } else - p->item = g_strdup(str); - - return p; -} - -static Permission * -permission_new(const char *uname) -{ - Permission *p; - - g_return_val_if_fail(uname, NULL); - g_return_val_if_fail(uname[0] != '\0', NULL); - g_return_val_if_fail(strchr(uname, ':') == NULL, NULL); - g_return_val_if_fail(g_utf8_validate(uname, -1, NULL) == TRUE, NULL); + if (permission) + _permission_set_stale(permission, PERM_TYPE_USER, g_strndup(str, ulen)); + return TRUE; - /* Yay, valid... create the new permission */ - p = g_slice_new0(Permission); - p->ptype = PERM_TYPE_USER; - p->item = g_strdup(uname); - return p; +invalid: + if (permission) + _permission_set_stale(permission, PERM_TYPE_INVALID, g_strdup(str0)); + return FALSE; } static char * -permission_to_string(Permission *p) -{ - return g_strdup_printf(PERM_USER_PREFIX "%s:", p->item); -} - -static void -permission_free(Permission *p) +_permission_to_string(Permission *permission) { - g_free(p->item); - memset(p, 0, sizeof(*p)); - g_slice_free(Permission, p); + nm_assert(permission); + + switch (permission->ptype) { + case PERM_TYPE_USER: + return g_strdup_printf(NM_SETTINGS_CONNECTION_PERMISSION_USER_PREFIX "%s:", + permission->item); + case PERM_TYPE_INVALID: + nm_assert(permission->item); + return g_strdup(permission->item); + } + nm_assert_not_reached(); + return g_strdup(permission->item ?: ""); } /*****************************************************************************/ @@ -279,14 +282,15 @@ nm_setting_connection_get_num_permissions(NMSettingConnection *setting) { g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), 0); - return g_slist_length(NM_SETTING_CONNECTION_GET_PRIVATE(setting)->permissions); + return nm_g_array_len(NM_SETTING_CONNECTION_GET_PRIVATE(setting)->permissions); } /** * nm_setting_connection_get_permission: * @setting: the #NMSettingConnection * @idx: the zero-based index of the permissions entry - * @out_ptype: on return, the permission type (at this time, always "user") + * @out_ptype: on return, the permission type. This is currently always "user", + * unless the entry is invalid, in which case it returns "invalid". * @out_pitem: on return, the permission item (formatted according to @ptype, see * #NMSettingConnection:permissions for more detail * @out_detail: on return, the permission detail (at this time, always %NULL) @@ -304,22 +308,29 @@ nm_setting_connection_get_permission(NMSettingConnection *setting, const char ** out_detail) { NMSettingConnectionPrivate *priv; - Permission * p; + Permission * permission; g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), FALSE); priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - g_return_val_if_fail(idx < g_slist_length(priv->permissions), FALSE); - - p = g_slist_nth_data(priv->permissions, idx); - if (out_ptype) - *out_ptype = "user"; - if (out_pitem) - *out_pitem = p->item; - if (out_detail) - *out_detail = NULL; + g_return_val_if_fail(idx < nm_g_array_len(priv->permissions), FALSE); + permission = &g_array_index(priv->permissions, Permission, idx); + switch (permission->ptype) { + case PERM_TYPE_USER: + NM_SET_OUT(out_ptype, NM_SETTINGS_CONNECTION_PERMISSION_USER); + NM_SET_OUT(out_pitem, permission->item); + NM_SET_OUT(out_detail, NULL); + return TRUE; + case PERM_TYPE_INVALID: + goto invalid; + } + nm_assert_not_reached(); +invalid: + NM_SET_OUT(out_ptype, "invalid"); + NM_SET_OUT(out_pitem, permission->item); + NM_SET_OUT(out_detail, NULL); return TRUE; } @@ -337,23 +348,22 @@ gboolean nm_setting_connection_permissions_user_allowed(NMSettingConnection *setting, const char *uname) { NMSettingConnectionPrivate *priv; - GSList * iter; + guint i; g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), FALSE); g_return_val_if_fail(uname != NULL, FALSE); - g_return_val_if_fail(*uname != '\0', FALSE); priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - /* If no permissions, visible to all */ - if (priv->permissions == NULL) + if (nm_g_array_len(priv->permissions) == 0) { + /* If no permissions, visible to all */ return TRUE; + } - /* Find the username in the permissions list */ - for (iter = priv->permissions; iter; iter = g_slist_next(iter)) { - Permission *p = iter->data; + for (i = 0; i < priv->permissions->len; i++) { + const Permission *permission = &g_array_index(priv->permissions, Permission, i); - if (strcmp(uname, p->item) == 0) + if (permission->ptype == PERM_TYPE_USER && nm_streq(permission->item, uname)) return TRUE; } @@ -372,8 +382,10 @@ nm_setting_connection_permissions_user_allowed(NMSettingConnection *setting, con * #NMSettingConnection:permissions: for more details. * * Returns: %TRUE if the permission was unique and was successfully added to the - * list, %FALSE if @ptype or @pitem was invalid or it the permission was already - * present in the list + * list, %FALSE if @ptype or @pitem was invalid. + * If the permission was already present in the list, it will not be added + * a second time but %TRUE will be returned. Note that before 1.28, in this + * case %FALSE would be returned. */ gboolean nm_setting_connection_add_permission(NMSettingConnection *setting, @@ -382,30 +394,39 @@ nm_setting_connection_add_permission(NMSettingConnection *setting, const char * detail) { NMSettingConnectionPrivate *priv; - Permission * p; - GSList * iter; + guint i; g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), FALSE); - g_return_val_if_fail(ptype && ptype[0], FALSE); - g_return_val_if_fail(detail == NULL, FALSE); + g_return_val_if_fail(ptype, FALSE); + g_return_val_if_fail(pitem, FALSE); - /* Only "user" for now... */ - g_return_val_if_fail(strcmp(ptype, "user") == 0, FALSE); + if (!nm_streq0(ptype, NM_SETTINGS_CONNECTION_PERMISSION_USER)) + return FALSE; + + if (!nm_settings_connection_validate_permission_user(pitem, -1)) + return FALSE; + + if (detail) + return FALSE; priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - /* No dupes */ - for (iter = priv->permissions; iter; iter = g_slist_next(iter)) { - p = iter->data; - if (strcmp(pitem, p->item) == 0) - return FALSE; + if (!priv->permissions) { + priv->permissions = g_array_sized_new(FALSE, FALSE, sizeof(Permission), 1); + g_array_set_clear_func(priv->permissions, (GDestroyNotify) _permission_clear_stale); } - p = permission_new(pitem); - g_return_val_if_fail(p != NULL, FALSE); - priv->permissions = g_slist_append(priv->permissions, p); - _notify(setting, PROP_PERMISSIONS); + for (i = 0; i < priv->permissions->len; i++) { + const Permission *permission = &g_array_index(priv->permissions, Permission, i); + if (permission->ptype == PERM_TYPE_USER && nm_streq(permission->item, pitem)) + return TRUE; + } + + _permission_set_stale(nm_g_array_append_new(priv->permissions, Permission), + PERM_TYPE_USER, + g_strdup(pitem)); + _notify(setting, PROP_PERMISSIONS); return TRUE; } @@ -420,16 +441,15 @@ void nm_setting_connection_remove_permission(NMSettingConnection *setting, guint32 idx) { NMSettingConnectionPrivate *priv; - GSList * iter; g_return_if_fail(NM_IS_SETTING_CONNECTION(setting)); priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - iter = g_slist_nth(priv->permissions, idx); - g_return_if_fail(iter != NULL); - permission_free((Permission *) iter->data); - priv->permissions = g_slist_delete_link(priv->permissions, iter); + g_return_if_fail(idx < nm_g_array_len(priv->permissions)); + + g_array_remove_index(priv->permissions, idx); + _notify(setting, PROP_PERMISSIONS); } @@ -453,25 +473,28 @@ nm_setting_connection_remove_permission_by_value(NMSettingConnection *setting, const char * detail) { NMSettingConnectionPrivate *priv; - Permission * p; - GSList * iter; + guint i; g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), FALSE); - g_return_val_if_fail(ptype && ptype[0], FALSE); - g_return_val_if_fail(detail == NULL, FALSE); - g_return_val_if_fail(pitem != NULL, FALSE); + g_return_val_if_fail(ptype, FALSE); + g_return_val_if_fail(pitem, FALSE); + + if (!nm_streq0(ptype, NM_SETTINGS_CONNECTION_PERMISSION_USER)) + return FALSE; - /* Only "user" for now... */ - g_return_val_if_fail(strcmp(ptype, "user") == 0, FALSE); + if (detail) + return FALSE; priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - for (iter = priv->permissions; iter; iter = g_slist_next(iter)) { - p = iter->data; - if (strcmp(pitem, p->item) == 0) { - permission_free((Permission *) iter->data); - priv->permissions = g_slist_delete_link(priv->permissions, iter); - _notify(setting, PROP_PERMISSIONS); - return TRUE; + if (priv->permissions) { + for (i = 0; i < priv->permissions->len; i++) { + const Permission *permission = &g_array_index(priv->permissions, Permission, i); + + if (permission->ptype == PERM_TYPE_USER && nm_streq(permission->item, pitem)) { + g_array_remove_index(priv->permissions, i); + _notify(setting, PROP_PERMISSIONS); + return TRUE; + } } } return FALSE; @@ -1298,6 +1321,27 @@ after_interface_name: } } + if (priv->permissions) { + guint i; + + for (i = 0; i < priv->permissions->len; i++) { + const Permission *permissions = &g_array_index(priv->permissions, Permission, i); + + if (permissions->ptype != PERM_TYPE_USER) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("invalid permissions not in format \"user:$UNAME[:]\"")); + g_prefix_error(error, + "%s.%s: ", + nm_setting_get_name(setting), + NM_SETTING_CONNECTION_PERMISSIONS); + return FALSE; + } + nm_assert(nm_settings_connection_validate_permission_user(permissions->item, -1)); + } + } + /* *** errors above here should be always fatal, below NORMALIZABLE_ERROR *** */ if (!priv->uuid) { @@ -1462,40 +1506,6 @@ compare_property(const NMSettInfoSetting *sett_info, ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags); } -static GSList * -perm_strv_to_permlist(char **strv) -{ - GSList *list = NULL; - int i; - - if (!strv) - return NULL; - - for (i = 0; strv[i]; i++) { - Permission *p; - - p = permission_new_from_str(strv[i]); - if (p) - list = g_slist_append(list, p); - } - - return list; -} - -static char ** -perm_permlist_to_strv(GSList *permlist) -{ - GPtrArray *strings; - GSList * iter; - - strings = g_ptr_array_new(); - for (iter = permlist; iter; iter = g_slist_next(iter)) - g_ptr_array_add(strings, permission_to_string((Permission *) iter->data)); - g_ptr_array_add(strings, NULL); - - return (char **) g_ptr_array_free(strings, FALSE); -} - /*****************************************************************************/ static void @@ -1521,8 +1531,20 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) g_value_set_string(value, nm_setting_connection_get_connection_type(setting)); break; case PROP_PERMISSIONS: - g_value_take_boxed(value, perm_permlist_to_strv(priv->permissions)); + { + char **strv; + gsize i, l; + + l = nm_g_array_len(priv->permissions); + strv = g_new(char *, l + 1u); + + for (i = 0; i < l; i++) + strv[i] = _permission_to_string(&g_array_index(priv->permissions, Permission, i)); + strv[i] = NULL; + + g_value_take_boxed(value, strv); break; + } case PROP_AUTOCONNECT: g_value_set_boolean(value, nm_setting_connection_get_autoconnect(setting)); break; @@ -1613,9 +1635,25 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps priv->type = g_value_dup_string(value); break; case PROP_PERMISSIONS: - g_slist_free_full(priv->permissions, (GDestroyNotify) permission_free); - priv->permissions = perm_strv_to_permlist(g_value_get_boxed(value)); + { + const char *const *strv; + guint i; + + nm_clear_pointer(&priv->permissions, g_array_unref); + strv = g_value_get_boxed(value); + if (strv && strv[0]) { + priv->permissions = + g_array_sized_new(FALSE, FALSE, sizeof(Permission), NM_PTRARRAY_LEN(strv)); + g_array_set_clear_func(priv->permissions, (GDestroyNotify) _permission_clear_stale); + + for (i = 0; strv[i]; i++) { + Permission *permission = nm_g_array_append_new(priv->permissions, Permission); + + _permission_set_stale_parse(permission, strv[i]); + } + } break; + } case PROP_AUTOCONNECT: priv->autoconnect = g_value_get_boolean(value); break; @@ -1729,7 +1767,7 @@ finalize(GObject *object) g_free(priv->master); g_free(priv->slave_type); g_free(priv->mud_url); - g_slist_free_full(priv->permissions, (GDestroyNotify) permission_free); + nm_clear_pointer(&priv->permissions, g_array_unref); g_slist_free_full(priv->secondaries, g_free); G_OBJECT_CLASS(nm_setting_connection_parent_class)->finalize(object); |