summary refs log tree commit diff
path: root/clients/common/nm-client-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'clients/common/nm-client-utils.c')
-rw-r--r--clients/common/nm-client-utils.c223
1 files changed, 222 insertions, 1 deletions
diff --git a/clients/common/nm-client-utils.c b/clients/common/nm-client-utils.c
index cf8b4785..d3a02161 100644
--- a/clients/common/nm-client-utils.c
+++ b/clients/common/nm-client-utils.c
@@ -6,8 +6,10 @@
 #include "nm-default.h"
 
 #include "nm-client-utils.h"
-#include "nm-utils.h"
 
+#include "nm-glib-aux/nm-secret-utils.h"
+#include "nm-glib-aux/nm-io-utils.h"
+#include "nm-utils.h"
 #include "nm-device-bond.h"
 #include "nm-device-bridge.h"
 #include "nm-device-team.h"
@@ -263,6 +265,38 @@ NM_UTILS_LOOKUP_STR_DEFINE (nmc_device_state_to_string, NMDeviceState,
 	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_UNKNOWN,      N_("unknown")),
 )
 
+static
+NM_UTILS_LOOKUP_STR_DEFINE (_device_state_to_string, NMDeviceState,
+	NM_UTILS_LOOKUP_DEFAULT (NULL),
+	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_PREPARE,      N_("connecting (externally)")),
+	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_CONFIG,       N_("connecting (externally)")),
+	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_NEED_AUTH,    N_("connecting (externally)")),
+	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_IP_CONFIG,    N_("connecting (externally)")),
+	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_IP_CHECK,     N_("connecting (externally)")),
+	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_SECONDARIES,  N_("connecting (externally)")),
+	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_ACTIVATED,    N_("connected (externally)")),
+	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_DEACTIVATING, N_("deactivating (externally)")),
+	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_FAILED,       N_("deactivating (externally)")),
+	NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (),
+)
+
+const char *
+nmc_device_state_to_string_with_external (NMDevice *device)
+{
+	NMActiveConnection *ac;
+	NMDeviceState state;
+	const char *s;
+
+	state = nm_device_get_state (device);
+
+	if (   (ac = nm_device_get_active_connection (device))
+	    && NM_FLAGS_HAS (nm_active_connection_get_state_flags (ac), NM_ACTIVATION_STATE_FLAG_EXTERNAL)
+	    && (s = _device_state_to_string (state)))
+		return s;
+
+	return nmc_device_state_to_string (state);
+}
+
 NM_UTILS_LOOKUP_STR_DEFINE (nmc_device_metered_to_string, NMMetered,
 	NM_UTILS_LOOKUP_DEFAULT (N_("unknown")),
 	NM_UTILS_LOOKUP_ITEM (NM_METERED_YES,       N_("yes")),
@@ -587,3 +621,190 @@ nmc_print_qrcode (const char *str)
 		}
 	}
 }
+
+/**
+ * nmc_utils_read_passwd_file:
+ * @passwd_file: file with passwords to parse
+ * @out_error_line: returns in case of a syntax error in the file, the line
+ *   on which it occurred.
+ * @error: location to store error, or %NULL
+ *
+ * Parse passwords given in @passwd_file and insert them into a hash table.
+ * Example of @passwd_file contents:
+ *   wifi.psk:tajne heslo
+ *   802-1x.password:krakonos
+ *   802-11-wireless-security:leap-password:my leap password
+ *
+ * Returns: (transfer full): hash table with parsed passwords, or %NULL on an error
+ */
+GHashTable *
+nmc_utils_read_passwd_file (const char *passwd_file,
+                            gssize *out_error_line,
+                            GError **error)
+{
+	nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 };
+
+	NM_SET_OUT (out_error_line, -1);
+
+	if (!passwd_file)
+		return g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) nm_free_secret);
+
+	if (!nm_utils_file_get_contents (-1,
+	                                 passwd_file,
+	                                 1024*1024,
+	                                 NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET,
+	                                 &contents.str,
+	                                 &contents.len,
+	                                 NULL,
+	                                 error))
+		return NULL;
+
+	return nmc_utils_parse_passwd_file (contents.str, out_error_line, error);
+}
+
+GHashTable *
+nmc_utils_parse_passwd_file (char *contents /* will be modified */,
+                             gssize *out_error_line,
+                             GError **error)
+{
+	gs_unref_hashtable GHashTable *pwds_hash = NULL;
+	const char *contents_str;
+	gsize contents_line;
+
+	pwds_hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) nm_free_secret);
+
+	NM_SET_OUT (out_error_line, -1);
+
+	contents_str = contents;
+	contents_line = 0;
+	while (contents_str[0]) {
+		nm_auto_free_secret char *l_hash_key = NULL;
+		nm_auto_free_secret char *l_hash_val = NULL;
+		const char *l_content_line;
+		const char *l_setting;
+		const char *l_prop;
+		const char *l_val;
+		const char *s;
+		gsize l_hash_val_len;
+
+		/* consume first line. As line delimiters we accept "\r\n", "\n", and "\r". */
+		l_content_line = contents_str;
+		s = l_content_line;
+		while (!NM_IN_SET (s[0], '\0', '\r', '\n'))
+			s++;
+		if (s[0] != '\0') {
+			if (   s[0] == '\r'
+			    && s[1] == '\n') {
+				((char *) s)[0] = '\0';
+				s += 2;
+			} else {
+				((char *) s)[0] = '\0';
+				s += 1;
+			}
+		}
+		contents_str = s;
+		contents_line++;
+
+		l_content_line = nm_str_skip_leading_spaces (l_content_line);
+		if (NM_IN_SET (l_content_line[0], '\0', '#')) {
+			/* a comment or empty line. Ignore. */
+			continue;
+		}
+
+		l_setting = l_content_line;
+
+		s = l_setting;
+		while (!NM_IN_SET (s[0], '\0', ':', '='))
+			s++;
+		if (s[0] == '\0') {
+			NM_SET_OUT (out_error_line, contents_line);
+			nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+			                    _("missing colon for \"<setting>.<property>:<secret>\" format"));
+			return NULL;
+		}
+		((char *) s)[0] = '\0';
+		s++;
+
+		l_val = s;
+
+		g_strchomp ((char *) l_setting);
+
+		nm_assert (nm_str_is_stripped (l_setting));
+
+		s = strchr (l_setting, '.');
+		if (!s) {
+			NM_SET_OUT (out_error_line, contents_line);
+			nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+			                    _("missing dot for \"<setting>.<property>:<secret>\" format"));
+			return NULL;
+		} else if (s == l_setting) {
+			NM_SET_OUT (out_error_line, contents_line);
+			nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+			                    _("missing setting for \"<setting>.<property>:<secret>\" format"));
+			return NULL;
+		}
+		((char *) s)[0] = '\0';
+		s++;
+
+		l_prop = s;
+		if (l_prop[0] == '\0') {
+			NM_SET_OUT (out_error_line, contents_line);
+			nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+			                    _("missing property for \"<setting>.<property>:<secret>\" format"));
+			return NULL;
+		}
+
+		/* Accept wifi-sec or wifi instead of cumbersome '802-11-wireless-security' */
+		if (NM_IN_STRSET (l_setting, "wifi-sec", "wifi"))
+			l_setting = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
+
+		if (nm_setting_lookup_type (l_setting) == G_TYPE_INVALID) {
+			NM_SET_OUT (out_error_line, contents_line);
+			nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+			                    _("invalid setting name"));
+			return NULL;
+		}
+
+		if (   nm_streq (l_setting, "vpn")
+		    && NM_STR_HAS_PREFIX (l_prop, "secret.")) {
+			/* in 1.12.0, we wrongly required the VPN secrets to be named
+			 * "vpn.secret". It should be "vpn.secrets". Work around it
+			 * (rh#1628833). */
+			l_hash_key = g_strdup_printf ("vpn.secrets.%s", &l_prop[NM_STRLEN ("secret.")]);
+		} else
+			l_hash_key = g_strdup_printf ("%s.%s", l_setting, l_prop);
+
+		if (!g_utf8_validate (l_hash_key, -1, NULL)) {
+			NM_SET_OUT (out_error_line, contents_line);
+			nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+			                    _("property name is not UTF-8"));
+			return NULL;
+		}
+
+		/* Support backslash escaping in the secret value. We strip non-escaped leading/trailing whitespaces. */
+		s = nm_utils_buf_utf8safe_unescape (l_val, NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES, &l_hash_val_len, (gpointer *) &l_hash_val);
+		if (!l_hash_val)
+			l_hash_val = g_strdup (s);
+
+		if (!g_utf8_validate (l_hash_val, -1, NULL)) {
+			/* In some cases it might make sense to support binary secrets (like the WPA-PSK which has no
+			 * defined encoding. However, all API that follows can only handle UTF-8, and no mechanism
+			 * to escape the secrets. Reject non-UTF-8 early. */
+			NM_SET_OUT (out_error_line, contents_line);
+			nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+			                    _("secret is not UTF-8"));
+			return NULL;
+		}
+
+		if (strlen (l_hash_val) != l_hash_val_len) {
+			NM_SET_OUT (out_error_line, contents_line);
+			nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+			                    _("secret is not UTF-8"));
+			return NULL;
+		}
+
+		g_hash_table_insert (pwds_hash, g_steal_pointer (&l_hash_key), g_steal_pointer (&l_hash_val));
+	}
+
+	return g_steal_pointer (&pwds_hash);
+}