diff options
Diffstat (limited to 'libnm-core/nm-utils.c')
| -rw-r--r-- | libnm-core/nm-utils.c | 470 |
1 files changed, 441 insertions, 29 deletions
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 987cf731..930f528e 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -29,14 +29,13 @@ #include <uuid/uuid.h> #include <libintl.h> #include <gmodule.h> -#include <glib/gi18n-lib.h> +#include <sys/stat.h> +#include "nm-default.h" #include "nm-utils.h" #include "nm-utils-private.h" -#include "nm-glib-compat.h" #include "nm-setting-private.h" #include "crypto.h" -#include "gsystem-local-alloc.h" #include "nm-macros-internal.h" #include "nm-setting-bond.h" @@ -240,7 +239,7 @@ _nm_utils_init (void) bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - g_type_init (); + nm_g_type_init (); _nm_dbus_errors_init (); } @@ -845,6 +844,30 @@ _nm_utils_ptrarray_to_strv (GPtrArray *ptrarray) } /** + * _nm_utils_strv_equal: + * @strv1: a string array + * @strv2: a string array + * + * Compare NULL-terminated string arrays for equality. + * + * Returns: %TRUE if the arrays are equal, %FALSE otherwise. + **/ +gboolean +_nm_utils_strv_equal (char **strv1, char **strv2) +{ + if (strv1 == strv2) + return TRUE; + + if (!strv1 || !strv2) + return FALSE; + + for ( ; *strv1 && *strv2 && !strcmp (*strv1, *strv2); strv1++, strv2++) + ; + + return !*strv1 && !*strv2; +} + +/** * _nm_utils_strsplit_set: * @str: string to split * @delimiters: string of delimiter characters @@ -1730,7 +1753,7 @@ nm_utils_ip6_addresses_from_variant (GVariant *value, char **out_gateway) *out_gateway = g_strdup (nm_utils_inet6_ntop (gateway_bytes, NULL)); } } else { - g_warning ("Ignoring invalid IP4 address: %s", error->message); + g_warning ("Ignoring invalid IP6 address: %s", error->message); g_clear_error (&error); } @@ -2409,6 +2432,132 @@ nm_utils_file_is_pkcs12 (const char *filename) /**********************************************************************************************/ +gboolean +_nm_utils_check_file (const char *filename, + gint64 check_owner, + NMUtilsCheckFilePredicate check_file, + gpointer user_data, + struct stat *out_st, + GError **error) +{ + struct stat st_backup; + + if (!out_st) + out_st = &st_backup; + + if (stat (filename, out_st) != 0) { + int errsv = errno; + + g_set_error (error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("failed stat file %s: %s"), filename, strerror (errsv)); + return FALSE; + } + + /* ignore non-files. */ + if (!S_ISREG (out_st->st_mode)) { + g_set_error (error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("not a file (%s)"), filename); + return FALSE; + } + + /* with check_owner enabled, check that the file belongs to the + * owner or root. */ + if ( check_owner >= 0 + && (out_st->st_uid != 0 && (gint64) out_st->st_uid != check_owner)) { + g_set_error (error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("invalid file owner %d for %s"), out_st->st_uid, filename); + return FALSE; + } + + /* with check_owner enabled, check that the file cannot be modified + * by other users (except root). */ + if ( check_owner >= 0 + && NM_FLAGS_ANY (out_st->st_mode, S_IWGRP | S_IWOTH | S_ISUID)) { + g_set_error (error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("file permissions for %s"), filename); + return FALSE; + } + + if ( check_file + && !check_file (filename, out_st, user_data, error)) { + if (error && !*error) { + g_set_error (error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("reject %s"), filename); + } + return FALSE; + } + + return TRUE; +} + + +gboolean +_nm_utils_check_module_file (const char *name, + int check_owner, + NMUtilsCheckFilePredicate check_file, + gpointer user_data, + GError **error) +{ + if (!g_path_is_absolute (name)) { + g_set_error (error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("path is not absolute (%s)"), name); + return FALSE; + } + + /* Set special error code if the file doesn't exist. + * The VPN package might be split into separate packages, + * so it could be correct that the plugin file is missing. + * + * Note that nm-applet checks for this error code to fail + * gracefully. */ + if (!g_file_test (name, G_FILE_TEST_EXISTS)) { + g_set_error (error, + G_FILE_ERROR, + G_FILE_ERROR_NOENT, + _("Plugin file does not exist (%s)"), name); + return FALSE; + } + + if (!g_file_test (name, G_FILE_TEST_IS_REGULAR)) { + g_set_error (error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("Plugin is not a valid file (%s)"), name); + return FALSE; + } + + if (g_str_has_suffix (name, ".la")) { + /* g_module_open() treats files that end with .la special. + * We don't want to parse the libtool archive. Just error out. */ + g_set_error (error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("libtool archives are not supported (%s)"), name); + return FALSE; + } + + return _nm_utils_check_file (name, + check_owner, + check_file, + user_data, + NULL, + error); +} + +/**********************************************************************************************/ + /** * nm_utils_file_search_in_paths: * @progname: the helper program name, like "iptables" @@ -2724,13 +2873,14 @@ _wifi_freqs (gboolean bg_band) * * Returns: zero-terminated array of frequencies numbers (in MHz) * - * Since: 1.0.6 + * Since: 1.2 **/ const guint * nm_utils_wifi_2ghz_freqs (void) { return _wifi_freqs (TRUE); } +NM_BACKPORT_SYMBOL (libnm_1_0_6, const guint *, nm_utils_wifi_2ghz_freqs, (void), ()); /** * nm_utils_wifi_5ghz_freqs: @@ -2739,13 +2889,14 @@ nm_utils_wifi_2ghz_freqs (void) * * Returns: zero-terminated array of frequencies numbers (in MHz) * - * Since: 1.0.6 + * Since: 1.2 **/ const guint * nm_utils_wifi_5ghz_freqs (void) { return _wifi_freqs (FALSE); } +NM_BACKPORT_SYMBOL (libnm_1_0_6, const guint *, nm_utils_wifi_5ghz_freqs, (void), ()); /** * nm_utils_wifi_strength_bars: @@ -3501,8 +3652,9 @@ static BondMode bond_mode_table[] = { * available modes. * * Returns: bonding mode string, or NULL on error -*/ - + * + * Since: 1.2 + */ const char * nm_utils_bond_mode_int_to_string (int mode) { @@ -3521,7 +3673,9 @@ nm_utils_bond_mode_int_to_string (int mode) * The @mode string can be either a descriptive name or a number (as string). * * Returns: numeric bond mode, or -1 on error -*/ + * + * Since: 1.2 + */ int nm_utils_bond_mode_string_to_int (const char *mode) { @@ -3540,6 +3694,105 @@ nm_utils_bond_mode_string_to_int (const char *mode) /**********************************************************************************************/ +#define STRSTRDICTKEY_V1_SET 0x01 +#define STRSTRDICTKEY_V2_SET 0x02 +#define STRSTRDICTKEY_ALL_SET 0x03 + +struct _NMUtilsStrStrDictKey { + char type; + char data[1]; +}; + +guint +_nm_utils_strstrdictkey_hash (gconstpointer a) +{ + const NMUtilsStrStrDictKey *k = a; + const signed char *p; + guint32 h = 5381; + + if (k) { + if (((int) k->type) & ~STRSTRDICTKEY_ALL_SET) + g_return_val_if_reached (0); + + h = (h << 5) + h + k->type; + if (k->type & STRSTRDICTKEY_ALL_SET) { + p = (void *) k->data; + for (; *p != '\0'; p++) + h = (h << 5) + h + *p; + if (k->type == STRSTRDICTKEY_ALL_SET) { + /* the key contains two strings. Continue... */ + h = (h << 5) + h + '\0'; + for (p++; *p != '\0'; p++) + h = (h << 5) + h + *p; + } + } + } + + return h; +} + +gboolean +_nm_utils_strstrdictkey_equal (gconstpointer a, gconstpointer b) +{ + const NMUtilsStrStrDictKey *k1 = a; + const NMUtilsStrStrDictKey *k2 = b; + + if (k1 == k2) + return TRUE; + if (!k1 || !k2) + return FALSE; + + if (k1->type != k2->type) + return FALSE; + + if (k1->type & STRSTRDICTKEY_ALL_SET) { + if (strcmp (k1->data, k2->data) != 0) + return FALSE; + + if (k1->type == STRSTRDICTKEY_ALL_SET) { + gsize l = strlen (k1->data) + 1; + + return strcmp (&k1->data[l], &k2->data[l]) == 0; + } + } + + return TRUE; +} + +NMUtilsStrStrDictKey * +_nm_utils_strstrdictkey_create (const char *v1, const char *v2) +{ + char type = 0; + gsize l1 = 0, l2 = 0; + NMUtilsStrStrDictKey *k; + + if (!v1 && !v2) + return g_malloc0 (1); + + /* we need to distinguish between ("",NULL) and (NULL,""). + * Thus, in @type we encode which strings we have present + * as not-NULL. */ + if (v1) { + type |= STRSTRDICTKEY_V1_SET; + l1 = strlen (v1) + 1; + } + if (v2) { + type |= STRSTRDICTKEY_V2_SET; + l2 = strlen (v2) + 1; + } + + k = g_malloc (G_STRUCT_OFFSET (NMUtilsStrStrDictKey, data) + l1 + l2); + k->type = type; + if (v1) + memcpy (&k->data[0], v1, l1); + if (v2) + memcpy (&k->data[l1], v2, l2); + + return k; +} + +/**********************************************************************************************/ + /* _nm_utils_ascii_str_to_int64: * * A wrapper for g_ascii_strtoll, that checks whether the whole string @@ -3613,30 +3866,131 @@ _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 ma return v; } +static gboolean +validate_dns_option (const char *name, gboolean numeric, gboolean ipv6, + const NMUtilsDNSOptionDesc *option_descs) +{ + const NMUtilsDNSOptionDesc *desc; + + if (!option_descs) + return !!*name; + + for (desc = option_descs; desc->name; desc++) { + if (!strcmp (name, desc->name) && + numeric == desc->numeric && + (!desc->ipv6_only || ipv6)) + return TRUE; + } + + return FALSE; +} + /** - * _nm_dbus_error_has_name: - * @error: (allow-none): a #GError, or %NULL - * @dbus_error_name: a D-Bus error name + * _nm_utils_dns_option_validate: + * @option: option string + * @out_name: (out) (allow-none): the option name + * @out_value: (out) (allow-none): the option value + * @ipv6: whether the option refers to a IPv6 configuration + * @option_descs: (allow-none): an array of NMUtilsDNSOptionDesc which describes the + * valid options + * + * Parses a DNS option in the form "name" or "name:number" and, if + * @option_descs is not NULL, checks that the option conforms to one + * of the provided descriptors. If @option_descs is NULL @ipv6 is + * not considered. + * + * Returns: %TRUE when the parsing was successful and the option is valid, + * %FALSE otherwise + */ +gboolean +_nm_utils_dns_option_validate (const char *option, char **out_name, + long *out_value, gboolean ipv6, + const NMUtilsDNSOptionDesc *option_descs) +{ + char **tokens, *ptr; + gboolean ret = FALSE; + + g_return_val_if_fail (option != NULL, FALSE); + + if (out_name) + *out_name = NULL; + if (out_value) + *out_value = -1; + + if (!option[0]) + return FALSE; + + tokens = g_strsplit (option, ":", 2); + + if (g_strv_length (tokens) == 1) { + ret = validate_dns_option (tokens[0], FALSE, ipv6, option_descs); + if (ret && out_name) + *out_name = g_strdup (tokens[0]); + goto out; + } + + if (!tokens[1][0]) { + ret = FALSE; + goto out; + } + + for (ptr = tokens[1]; *ptr; ptr++) { + if (!g_ascii_isdigit (*ptr)) { + ret = FALSE; + goto out; + } + } + + ret = FALSE; + if (validate_dns_option (tokens[0], TRUE, ipv6, option_descs)) { + int value = _nm_utils_ascii_str_to_int64 (tokens[1], 10, 0, G_MAXINT32, -1); + if (value >= 0) { + if (out_name) + *out_name = g_strdup (tokens[0]); + if (out_value) + *out_value = value; + ret = TRUE; + } + } +out: + g_strfreev (tokens); + return ret; +} + +/** + * _nm_utils_dns_option_find_idx: + * @array: an array of strings + * @option: a dns option string * - * Checks if @error is set and corresponds to the D-Bus error @dbus_error_name. + * Searches for an option in an array of strings. The match is + * performed only the option name; the option value is ignored. * - * Returns: %TRUE or %FALSE + * Returns: the index of the option in the array or -1 if was not + * found. */ -gboolean -_nm_dbus_error_has_name (GError *error, - const char *dbus_error_name) +int _nm_utils_dns_option_find_idx (GPtrArray *array, const char *option) { - gboolean has_name = FALSE; + gboolean ret; + char *option_name, *tmp_name; + int i; + + if (!_nm_utils_dns_option_validate (option, &option_name, NULL, FALSE, NULL)) + return -1; - if (error && g_dbus_error_is_remote_error (error)) { - char *error_name; + for (i = 0; i < array->len; i++) { + if (_nm_utils_dns_option_validate (array->pdata[i], &tmp_name, NULL, FALSE, NULL)) { + ret = strcmp (tmp_name, option_name); + g_free (tmp_name); + if (!ret) { + g_free (option_name); + return i; + } + } - error_name = g_dbus_error_get_remote_error (error); - has_name = !g_strcmp0 (error_name, dbus_error_name); - g_free (error_name); } - return has_name; + g_free (option_name); + return -1; } /** @@ -3651,7 +4005,7 @@ _nm_dbus_error_has_name (GError *error, * * Returns: a newly allocated string or %NULL * - * Since: 1.0.6 + * Since: 1.2 */ char *nm_utils_enum_to_str (GType type, int value) { @@ -3689,13 +4043,15 @@ char *nm_utils_enum_to_str (GType type, int value) g_type_class_unref (class); return ret; } +NM_BACKPORT_SYMBOL (libnm_1_0_6, char *, nm_utils_enum_to_str, + (GType type, int value), (type, value)); /** * nm_utils_enum_from_str: * @type: the %GType of the enum * @str: the input string - * @out_value: (out) (allow-none) the output value - * @err_token: (out) (allow-none) location to store the first unrecognized token + * @out_value: (out) (allow-none): the output value + * @err_token: (out) (allow-none): location to store the first unrecognized token * * Converts a string to the matching enum value. * @@ -3706,7 +4062,7 @@ char *nm_utils_enum_to_str (GType type, int value) * * Returns: %TRUE if the conversion was successful, %FALSE otherwise * - * Since: 1.0.6 + * Since: 1.2 */ gboolean nm_utils_enum_from_str (GType type, const char *str, int *out_value, char **err_token) @@ -3760,3 +4116,59 @@ gboolean nm_utils_enum_from_str (GType type, const char *str, g_type_class_unref (class); return ret; } +NM_BACKPORT_SYMBOL (libnm_1_0_6, gboolean, nm_utils_enum_from_str, + (GType type, const char *str, int *out_value, char **err_token), + (type, str, out_value, err_token)); + +/** + * nm_utils_enum_get_values: + * @type: the %GType of the enum + * @from: the first element to be returned + * @to: the last element to be returned + * + * Returns the list of possible values for a given enum. + * + * Returns: (transfer full): a NULL-terminated dynamically-allocated array of static strings + * or %NULL on error + * + * Since: 1.2 + */ +const char **nm_utils_enum_get_values (GType type, gint from, gint to) +{ + GTypeClass *class; + GPtrArray *array; + gint i; + + class = g_type_class_ref (type); + array = g_ptr_array_new (); + + if (G_IS_ENUM_CLASS (class)) { + GEnumClass *enum_class = G_ENUM_CLASS (class); + GEnumValue *enum_value; + + for (i = 0; i < enum_class->n_values; i++) { + enum_value = &enum_class->values[i]; + if (enum_value->value >= from && enum_value->value <= to) + g_ptr_array_add (array, (gpointer) enum_value->value_nick); + } + } else if (G_IS_FLAGS_CLASS (class)) { + GFlagsClass *flags_class = G_FLAGS_CLASS (class); + GFlagsValue *flags_value; + + for (i = 0; i < flags_class->n_values; i++) { + flags_value = &flags_class->values[i]; + if (flags_value->value >= from && flags_value->value <= to) + g_ptr_array_add (array, (gpointer) flags_value->value_nick); + } + } else { + g_type_class_unref (class); + g_ptr_array_free (array, TRUE); + g_return_val_if_reached (NULL); + } + + g_type_class_unref (class); + g_ptr_array_add (array, NULL); + + return (const char **) g_ptr_array_free (array, FALSE); +} + |