summary refs log tree commit diff
path: root/libnm-core/nm-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-core/nm-utils.c')
-rw-r--r--libnm-core/nm-utils.c470
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);
+}
+