1    	/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2    	/* NetworkManager system settings service - keyfile plugin
3    	 *
4    	 * This program is free software; you can redistribute it and/or modify
5    	 * it under the terms of the GNU General Public License as published by
6    	 * the Free Software Foundation; either version 2 of the License, or
7    	 * (at your option) any later version.
8    	 *
9    	 * This program is distributed in the hope that it will be useful,
10   	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   	 * GNU General Public License for more details.
13   	 *
14   	 * You should have received a copy of the GNU General Public License along
15   	 * with this program; if not, write to the Free Software Foundation, Inc.,
16   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17   	 *
18   	 * Copyright (C) 2008 - 2009 Novell, Inc.
19   	 * Copyright (C) 2008 - 2011 Red Hat, Inc.
20   	 */
21   	
22   	#include <errno.h>
23   	#include <stdlib.h>
24   	#include <sys/stat.h>
25   	#include <unistd.h>
26   	#include <sys/types.h>
27   	#include <dbus/dbus-glib.h>
28   	#include <nm-setting.h>
29   	#include <nm-setting-ip4-config.h>
30   	#include <nm-setting-ip6-config.h>
31   	#include <nm-setting-vpn.h>
32   	#include <nm-setting-connection.h>
33   	#include <nm-setting-wired.h>
34   	#include <nm-setting-wireless.h>
35   	#include <nm-setting-bluetooth.h>
36   	#include <nm-setting-8021x.h>
37   	#include <nm-utils.h>
38   	#include <arpa/inet.h>
39   	#include <netinet/ether.h>
40   	#include <linux/if_infiniband.h>
41   	#include <string.h>
42   	
43   	#include "nm-dbus-glib-types.h"
44   	#include "nm-glib-compat.h"
45   	#include "nm-system-config-interface.h"
46   	#include "reader.h"
47   	#include "common.h"
48   	#include "utils.h"
49   	
50   	/* Some setting properties also contain setting names, such as
51   	 * NMSettingConnection's 'type' property (which specifies the base type of the
52   	 * connection, eg ethernet or wifi) or the 802-11-wireless setting's
53   	 * 'security' property which specifies whether or not the AP requires
54   	 * encrpytion.  This function handles translating those properties' values
55   	 * to the real setting name if they are an alias.
56   	 */
57   	static void
58   	setting_alias_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
59   	{
60   		const char *setting_name = nm_setting_get_name (setting);
61   		char *s;
62   		const char *key_setting_name;
63   	
64   		s = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
65   		if (s) {
66   			key_setting_name = nm_keyfile_plugin_get_setting_name_for_alias (s);
67   			g_object_set (G_OBJECT (setting),
68   			              key, key_setting_name ? key_setting_name : s,
69   			              NULL);
70   			g_free (s);
71   		}
72   	}
73   	
74   	static gboolean
75   	read_array_of_uint (GKeyFile *file,
76   	                    NMSetting *setting,
77   	                    const char *key)
78   	{
79   		GArray *array = NULL;
80   		gsize length;
81   		int i;
82   		gint *tmp;
83   	
84   		tmp = nm_keyfile_plugin_kf_get_integer_list (file, nm_setting_get_name (setting), key, &length, NULL);
85   		array = g_array_sized_new (FALSE, FALSE, sizeof (guint32), length);
86   		g_return_val_if_fail (array != NULL, FALSE);
87   	
88   		for (i = 0; i < length; i++)
89   			g_array_append_val (array, tmp[i]);
90   	
91   		g_object_set (setting, key, array, NULL);
92   		g_array_unref (array);
93   	
94   		return TRUE;
95   	}
96   	
97   	static gboolean
98   	get_one_int (const char *str, guint32 max_val, const char *key_name, guint32 *out)
99   	{
100  		long tmp;
101  	
102  		errno = 0;
103  		tmp = strtol (str, NULL, 10);
104  		if (errno || (tmp < 0) || (tmp > max_val)) {
105  			g_warning ("%s: ignoring invalid IP %s item '%s'", __func__, key_name, str);
106  			return FALSE;
107  		}
108  	
109  		*out = (guint32) tmp;
110  		return TRUE;
111  	}
112  	
113  	static gpointer
114  	build_ip4_address_or_route (const char *address_str, guint32 plen, const char *gateway_str, guint32 metric, gboolean route)
115  	{
116  		GArray *result;
117  		guint32 addr;
118  		guint32 address = 0;
119  		guint32 gateway = 0;
120  		int err;
121  	
122  		g_return_val_if_fail (address_str, NULL);
123  	
124  		/* Address */
125  		err = inet_pton (AF_INET, address_str, &addr);
126  		if (err <= 0) {
127  			g_warning ("%s: ignoring invalid IPv4 address '%s'", __func__, address_str);
128  			return NULL;
129  		}
130  		address = addr;
131  		/* Gateway */
132  		if (gateway_str) {
133  			err = inet_pton (AF_INET, gateway_str, &addr);
134  			if (err <= 0) {
135  				g_warning ("%s: ignoring invalid IPv4 gateway '%s'", __func__, gateway_str);
136  				return NULL;
137  			}
138  			gateway = addr;
139  		}
140  		else
141  			gateway = 0;
142  	
143  		result = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
144  		g_array_append_val (result, address);
145  		g_array_append_val (result, plen);
146  		g_array_append_val (result, gateway);
147  		if (route)
148  			g_array_append_val (result, metric);
149  	
150  		return result;
151  	}
152  	
153  	static gpointer
154  	build_ip6_address_or_route (const char *address_str, guint32 plen, const char *gateway_str, guint32 metric, gboolean route)
155  	{
156  		GValueArray *result;
157  		struct in6_addr addr;
158  		GByteArray *address;
159  		GByteArray *gateway;
160  		GValue value = G_VALUE_INIT;
161  		int err;
162  	
163  		g_return_val_if_fail (address_str, NULL);
164  	
165  		result = g_value_array_new (3);
166  	
167  		/* add address */
168  		err = inet_pton (AF_INET6, address_str, &addr);
169  		if (err <= 0) {
170  			g_warning ("%s: ignoring invalid IPv6 address '%s'", __func__, address_str);
171  			g_value_array_free (result);
172  			return NULL;
173  		}
174  		address = g_byte_array_new ();
175  		g_byte_array_append (address, (guint8 *) addr.s6_addr, 16);
176  		g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
177  		g_value_take_boxed (&value, address);
178  		g_value_array_append (result, &value);
179  		g_value_unset (&value);
180  	
181  		/* add prefix length */
182  		g_value_init (&value, G_TYPE_UINT);
183  		g_value_set_uint (&value, plen);
184  		g_value_array_append (result, &value);
185  		g_value_unset (&value);
186  	
187  		/* add gateway */
188  		if (gateway_str) {
189  			err = inet_pton (AF_INET6, gateway_str, &addr);
190  			if (err <= 0) {
191  				g_warning ("%s: ignoring invalid IPv6 gateway '%s'", __func__, gateway_str);
192  				g_value_array_free (result);
193  				return NULL;
194  			}
195  		} else
196  			memset (&addr, 0, 16);
197  		gateway = g_byte_array_new ();
198  		g_byte_array_append (gateway, (guint8 *) addr.s6_addr, 16);
199  		g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
200  		g_value_take_boxed (&value, gateway);
201  		g_value_array_append (result, &value);
202  		g_value_unset (&value);
203  	
204  		/* add metric (for routing) */
205  		if (route) {
206  			g_value_init (&value, G_TYPE_UINT);
207  			g_value_set_uint (&value, metric);
208  			g_value_array_append (result, &value);
209  			g_value_unset (&value);
210  		}
211  	
212  		return result;
213  	}
214  	
215  	/* On success, returns pointer to the zero-terminated field (original @current).
216  	 * The @current * pointer target is set to point to the rest of the input
217  	 * or %NULL if there is no more input. Sets error to %NULL for convenience.
218  	 *
219  	 * On failure, returns %NULL (unspecified). The @current pointer target is
220  	 * resets to its original value to allow skipping fields. The @error target
221  	 * is set to the character that breaks the parsing or %NULL if @current was %NULL.
222  	 *
223  	 * When @current target is %NULL, gracefully fail returning %NULL while
224  	 * leaving the @current target %NULL end setting @error to %NULL;
225  	 */
226  	static char *
227  	read_field (char **current, char **error, const char *characters, const char *delimiters)
228  	{
229  		char *start;
230  	
231  		g_return_val_if_fail (current, NULL);
232  		g_return_val_if_fail (error, NULL);
233  		g_return_val_if_fail (characters, NULL);
234  		g_return_val_if_fail (delimiters, NULL);
235  	
236  		if (!*current) {
237  			/* graceful failure, leave '*current' NULL */
238  			*error = NULL;
239  			return NULL;
240  		}
241  	
242  		/* fail on empty input */
243  		g_return_val_if_fail (**current, NULL);
244  	
245  		/* remember beginning of input */
246  		start = *current;
247  	
248  		while (**current && strchr (characters, **current))
249  			(*current)++;
250  		if (**current)
251  			if (strchr (delimiters, **current)) {
252  				/* success, more data available */
253  				*error = NULL;
254  				*(*current)++ = '\0';
255  				return start;
256  			} else {
257  				/* error, bad character */
258  				*error = *current;
259  				*current = start;
260  				return NULL;
261  			}
262  		else {
263  			/* success, end of input */
264  			*error = NULL;
265  			*current = NULL;
266  			return start;
267  		}
268  	}
269  	
270  	#define IP_ADDRESS_CHARS "0123456789abcdefABCDEF:.%"
271  	#define DIGITS "0123456789"
272  	#define DELIMITERS "/;,"
273  	
274  	
275  	/* The following IPv4 and IPv6 address formats are supported:
276  	 *
277  	 * address (DEPRECATED)
278  	 * address/plen
279  	 * address/gateway (DEPRECATED)
280  	 * address/plen/gateway
281  	 *
282  	 * The following IPv4 and IPv6 route formats are supported:
283  	 *
284  	 * address/plen (NETWORK dev DEVICE)
285  	 * address/plen/gateway (NETWORK via GATEWAY dev DEVICE)
286  	 * address/plen//gateway (NETWORK dev DEVICE metric METRIC)
287  	 * address/plen/gateway/metric (NETWORK via GATEWAY dev DEVICE metric METRIC)
288  	 *
289  	 * For backward, forward and sideward compatibility, slash (/),
290  	 * semicolon (;) and comma (,) are interchangable. The use of
291  	 * slash in the above examples is therefore not significant.
292  	 *
293  	 * Leaving out the prefix length is discouraged and DEPRECATED. The
294  	 * default value of IPv6 prefix length was 64 and has not been
295  	 * changed. The default for IPv4 is now 24, which is the closest
296  	 * IPv4 equivalent. These defaults may just as well be changed to
297  	 * match the iproute2 defaults (32 for IPv4 and 128 for IPv6).
298  	 *
299  	 * The returned result is GArray for IPv4 and GValueArray for IPv6.
300  	 */
301  	static gpointer
302  	read_one_ip_address_or_route (GKeyFile *file,
303  		const char *setting_name,
304  		const char *key_name,
305  		gboolean ipv6,
306  		gboolean route)
307  	{
308  		guint32 plen, metric;
309  		gpointer result;
310  		char *address_str, *plen_str, *gateway_str, *metric_str, *value, *current, *error;
311  	
312  		current = value = nm_keyfile_plugin_kf_get_string (file, setting_name, key_name, NULL);
313  		if (!value)
314  			return NULL;
315  	
316  		/* get address field */
317  		address_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
318  		if (error) {
319  			g_warning ("keyfile: Unexpected character '%c' in '%s.%s' address (position %td of '%s').",
320  				*error, setting_name, key_name, error - current, current);
321  			goto error;
322  		}
323  		/* get prefix length field (skippable) */
324  		plen_str = read_field (&current, &error, DIGITS, DELIMITERS);
325  		/* get gateway field */
326  		gateway_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
327  		if (error) {
328  			g_warning ("keyfile: Unexpected character '%c' in '%s.%s' %s (position %td of '%s').",
329  				*error, setting_name, key_name,
330  				plen_str ? "gateway" : "gateway or prefix length",
331  				error - current, current);
332  			goto error;
333  		}
334  		/* for routes, get metric */
335  		if (route) {
336  			metric_str = read_field (&current, &error, DIGITS, DELIMITERS);
337  			if (error) {
338  				g_warning ("keyfile: Unexpected character '%c' in '%s.%s' prefix length (position %td of '%s').",
339  					*error, setting_name, key_name, error - current, current);
340  				goto error;
341  			}
342  		} else
343  			metric_str = NULL;
344  		if (current) {
345  			/* there is still some data */
346  			if (*current) {
347  				/* another field follows */
348  				g_warning ("keyfile: %s.%s: Garbage at the and of the line: %s",
349  					setting_name, key_name, current);
350  				goto error;
351  			} else {
352  				/* semicolon at the end of input */
353  				g_message ("keyfile: %s.%s: Deprecated semicolon at the end of value.",
354  					setting_name, key_name);
355  			}
356  		}
357  	
358  		/* parse plen, fallback to defaults */
359  		if (plen_str)
360  			g_return_val_if_fail (get_one_int (plen_str, ipv6 ? 128 : 32,
361  				key_name, &plen), NULL);
362  		else {
363  			if (route)
364  				plen = ipv6 ? 128 : 24;
365  			else
366  				plen = ipv6 ? 64 : 24;
367  			g_warning ("keyfile: Missing prefix length in '%s.%s', defaulting to %d",
368  				setting_name, key_name, plen);
369  		}
370  	
371  		/* parse metric, default to 0 */
372  		metric = 0;
373  		if (metric_str)
374  			g_return_val_if_fail (get_one_int (metric_str, G_MAXUINT32,
375  				key_name, &metric), NULL);
376  	
377  		/* build the appropriate data structure for NetworkManager settings */
378  		result = (ipv6 ? build_ip6_address_or_route : build_ip4_address_or_route) (
379  			address_str, plen, gateway_str, metric, route);
380  	
381  		g_free (value);
382  		return result;
383  	error:
384  		g_free (value);
385  		return NULL;
386  	}
387  	
388  	static void
389  	ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
390  	{
391  		const char *setting_name = nm_setting_get_name (setting);
392  		gboolean ipv6 = !strcmp (setting_name, "ipv6");
393  		gboolean routes = !strcmp (key, "routes");
394  		static const char *key_names_routes[] = { "route", "routes", NULL };
395  		static const char *key_names_addresses[] = { "address", "addresses", NULL };
396  		const char **key_names = routes ? key_names_routes : key_names_addresses;
397  		GPtrArray *list;
398  		int i;
399  	
400  		G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
401  		list = g_ptr_array_new_with_free_func (
402  			ipv6 ? (GDestroyNotify) g_value_array_free : (GDestroyNotify) g_array_unref);
403  		G_GNUC_END_IGNORE_DEPRECATIONS;
404  	
405  		for (i = -1; i < 1000; i++) {
406  			const char **key_basename;
407  			
408  			for (key_basename = key_names; *key_basename; key_basename++) {
409  				char *key_name;
410  				gpointer item;
411  	
412  				/* -1 means no suffix */
413  				if (i >= 0)
414  					key_name = g_strdup_printf ("%s%d", *key_basename, i);
415  				else
416  					key_name = g_strdup (*key_basename);
417  	
418  				item = read_one_ip_address_or_route (keyfile, setting_name, key_name, ipv6, routes);
419  	
420  				if (item)
421  					g_ptr_array_add (list, item);
422  	
423  				g_free (key_name);
424  			}
425  		}
426  	
427  		if (list->len >= 1)
428  			g_object_set (setting, key, list, NULL);
429  	
430  		g_ptr_array_unref (list);
431  	}
432  	
433  	static void
434  	ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
435  	{
436  		const char *setting_name = nm_setting_get_name (setting);
437  		GArray *array = NULL;
438  		gsize length;
439  		char **list, **iter;
440  		int ret;
441  	
442  		list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
443  		if (!list || !g_strv_length (list))
444  			return;
445  	
446  		array = g_array_sized_new (FALSE, FALSE, sizeof (guint32), length);
447  		for (iter = list; *iter; iter++) {
448  			guint32 addr;
449  	
450  			ret = inet_pton (AF_INET, *iter, &addr);
451  			if (ret <= 0) {
452  				g_warning ("%s: ignoring invalid DNS server address '%s'", __func__, *iter);
453  				continue;
454  			}
455  	
456  			g_array_append_val (array, addr);
457  		}
458  		g_strfreev (list);
459  	
460  		if (array) {
461  			g_object_set (setting, key, array, NULL);
462  			g_array_unref (array);
463  		}
464  	}
465  	
466  	static void
467  	ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
468  	{
469  		const char *setting_name = nm_setting_get_name (setting);
470  		GPtrArray *array = NULL;
471  		gsize length;
472  		char **list, **iter;
473  		int ret;
474  	
475  		list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
476  		if (!list || !g_strv_length (list))
477  			return;
478  	
479  		array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_byte_array_unref);
480  	
481  		for (iter = list; *iter; iter++) {
482  			GByteArray *byte_array;
483  			struct in6_addr addr;
484  	
485  			ret = inet_pton (AF_INET6, *iter, &addr);
486  			if (ret <= 0) {
487  				g_warning ("%s: ignoring invalid DNS server IPv6 address '%s'", __func__, *iter);
488  				continue;
489  			}
490  			byte_array = g_byte_array_new ();
491  			g_byte_array_append (byte_array, (guint8 *) addr.s6_addr, 16);
492  	
493  			g_ptr_array_add (array, byte_array);
494  		}
495  		g_strfreev (list);
496  	
497  		if (array) {
498  			g_object_set (setting, key, array, NULL);
499  			g_ptr_array_unref (array);
500  		}
501  	}
502  	
503  	static void
504  	mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
505  	{
506  		const char *setting_name = nm_setting_get_name (setting);
507  		char *tmp_string = NULL, *p;
508  		gint *tmp_list;
509  		GByteArray *array = NULL;
510  		gsize length;
511  		int i, type;
512  	
513  		p = tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
514  		if (tmp_string) {
515  			/* Look for enough ':' characters to signify a MAC address */
516  			i = 0;
517  			while (*p) {
518  				if (*p == ':')
519  					i++;
520  				p++;
521  			}
522  	
523  			/* If we found enough it's probably a string-format MAC address */
524  			type = nm_utils_hwaddr_type (i + 1);
525  			if (type > 0)
526  				array = nm_utils_hwaddr_atoba (tmp_string, type);
527  		}
528  		g_free (tmp_string);
529  	
530  		if (array == NULL) {
531  			/* Old format; list of ints */
532  			tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL);
533  			type = nm_utils_hwaddr_type (length);
534  			if (type < 0) {
535  				array = g_byte_array_sized_new (length);
536  				for (i = 0; i < length; i++) {
537  					int val = tmp_list[i];
538  					const guint8 v = (guint8) (val & 0xFF);
539  	
540  					if (val < 0 || val > 255) {
541  						g_warning ("%s: %s / %s ignoring invalid byte element '%d' (not "
542  								   " between 0 and 255 inclusive)", __func__, setting_name,
543  								   key, val);
544  						g_byte_array_free (array, TRUE);
545  						array = NULL;
546  						break;
547  					}
548  					g_byte_array_append (array, &v, 1);
549  				}
550  			}
551  			g_free (tmp_list);
552  		}
553  	
554  		if (array) {
555  			g_object_set (setting, key, array, NULL);
556  			g_byte_array_free (array, TRUE);
557  		} else {
558  			g_warning ("%s: ignoring invalid MAC address for %s / %s",
559  			           __func__, setting_name, key);
560  		}
561  	}
562  	
563  	static void
564  	read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key)
565  	{
566  		char **keys, **iter;
567  		char *value;
568  		const char *setting_name = nm_setting_get_name (setting);
569  	
570  		keys = nm_keyfile_plugin_kf_get_keys (file, setting_name, NULL, NULL);
571  		if (!keys || !*keys)
572  			return;
573  	
574  		for (iter = keys; *iter; iter++) {
575  			value = nm_keyfile_plugin_kf_get_string (file, setting_name, *iter, NULL);
576  			if (!value)
577  				continue;
578  	
579  			if (NM_IS_SETTING_VPN (setting)) {
580  				if (strcmp (*iter, NM_SETTING_VPN_SERVICE_TYPE))
581  					nm_setting_vpn_add_data_item (NM_SETTING_VPN (setting), *iter, value);
582  			}
583  			if (NM_IS_SETTING_BOND (setting)) {
584  				if (strcmp (*iter, NM_SETTING_BOND_INTERFACE_NAME))
585  					nm_setting_bond_add_option (NM_SETTING_BOND (setting), *iter, value);
586  			}
587  			g_free (value);
588  		}
589  		g_strfreev (keys);
590  	}
591  	
592  	static void
593  	unescape_semicolons (char *str)
594  	{
595  		int i;
596  		gsize len = strlen (str);
597  	
598  		for (i = 0; i < len; i++) {
599  			if (str[i] == '\\' && str[i+1] == ';') {
600  				memmove(str + i, str + i + 1, len - (i + 1));
601  				len--;
602  			}
603  			str[len] = '\0';
604  		}
605  	}
606  	
607  	static GByteArray *
608  	get_uchar_array (GKeyFile *keyfile,
609  	                 const char *setting_name,
610  	                 const char *key,
611  	                 gboolean zero_terminate,
612  	                 gboolean unescape_semicolon)
613  	{
614  		GByteArray *array = NULL;
615  		char *tmp_string;
616  		gint *tmp_list;
617  		gsize length;
618  		int i;
619  	
620  		/* New format: just a string
621  		 * Old format: integer list; e.g. 11;25;38;
622  		 */
623  		tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
624  		if (tmp_string) {
625  			GRegex *regex;
626  			GMatchInfo *match_info;
627  			const char *pattern = "^[[:space:]]*[[:digit:]]{1,3}[[:space:]]*;([[:space:]]*[[:digit:]]{1,3}[[:space:]]*;)*([[:space:]]*)?$";
628  	
629  			regex = g_regex_new (pattern, 0, 0, NULL);
630  			g_regex_match (regex, tmp_string, 0, &match_info);
631  			if (!g_match_info_matches (match_info)) {
632  				/* Handle as a simple string (ie, new format) */
633  				if (unescape_semicolon)
634  					unescape_semicolons (tmp_string);
635  				length = strlen (tmp_string);
636  				if (zero_terminate)
637  					length++;
638  				array = g_byte_array_sized_new (length);
639  				g_byte_array_append (array, (guint8 *) tmp_string, length);
640  			}
641  			g_match_info_free (match_info);
642  			g_regex_unref (regex);
643  			g_free (tmp_string);
644  		}
645  	
646  		if (!array) {
647  			/* Old format; list of ints */
648  			tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL);
649  			array = g_byte_array_sized_new (length);
650  			for (i = 0; i < length; i++) {
651  				int val = tmp_list[i];
652  				unsigned char v = (unsigned char) (val & 0xFF);
653  	
654  				if (val < 0 || val > 255) {
655  					g_warning ("%s: %s / %s ignoring invalid byte element '%d' (not "
656  						       " between 0 and 255 inclusive)", __func__, setting_name,
657  						       key, val);
658  				} else
659  					g_byte_array_append (array, (const unsigned char *) &v, sizeof (v));
660  			}
661  			g_free (tmp_list);
662  		}
663  	
664  		if (array->len == 0) {
665  			g_byte_array_free (array, TRUE);
666  			array = NULL;
667  		}
668  		return array;
669  	}
670  	
671  	static void
672  	ssid_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
673  	{
674  		const char *setting_name = nm_setting_get_name (setting);
675  		GByteArray *array;
676  	
677  		array = get_uchar_array (keyfile, setting_name, key, FALSE, TRUE);
678  		if (array) {
679  			g_object_set (setting, key, array, NULL);
680  			g_byte_array_free (array, TRUE);
681  		} else {
682  			g_warning ("%s: ignoring invalid SSID for %s / %s",
683  			           __func__, setting_name, key);
684  		}
685  	}
686  	
687  	static void
688  	password_raw_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
689  	{
690  		const char *setting_name = nm_setting_get_name (setting);
691  		GByteArray *array;
692  	
693  		array = get_uchar_array (keyfile, setting_name, key, FALSE, TRUE);
694  		if (array) {
695  			g_object_set (setting, key, array, NULL);
696  			g_byte_array_free (array, TRUE);
697  		} else {
698  			g_warning ("%s: ignoring invalid raw password for %s / %s",
699  			           __func__, setting_name, key);
700  		}
701  	}
702  	
703  	static char *
704  	get_cert_path (const char *keyfile_path, GByteArray *cert_path)
705  	{
706  		const char *base;
707  		char *p = NULL, *path, *dirname, *tmp;
708  	
709  		g_return_val_if_fail (keyfile_path != NULL, NULL);
710  		g_return_val_if_fail (cert_path != NULL, NULL);
711  	
712  		base = path = g_malloc0 (cert_path->len + 1);
713  		memcpy (path, cert_path->data, cert_path->len);
714  	
715  		if (path[0] == '/')
716  			return path;
717  	
718  		p = strrchr (path, '/');
719  		if (p)
720  			base = p + 1;
721  	
722  		dirname = g_path_get_dirname (keyfile_path);
723  		tmp = g_build_path ("/", dirname, base, NULL);
724  		g_free (dirname);
725  		g_free (path);
726  		return tmp;
727  	}
728  	
729  	#define SCHEME_PATH "file://"
730  	
731  	static const char *certext[] = { ".pem", ".cert", ".crt", ".cer", ".p12", ".der", ".key" };
732  	
733  	static gboolean
734  	has_cert_ext (const char *path)
735  	{
736  		int i;
737  	
738  		for (i = 0; i < G_N_ELEMENTS (certext); i++) {
739  			if (g_str_has_suffix (path, certext[i]))
740  				return TRUE;
741  		}
742  		return FALSE;
743  	}
744  	
745  	static gboolean
746  	handle_as_scheme (GByteArray *array, NMSetting *setting, const char *key)
747  	{
748  		/* It's the PATH scheme, can just set plain data */
749  		if (   (array->len > strlen (SCHEME_PATH))
750  		    && g_str_has_prefix ((const char *) array->data, SCHEME_PATH)
751  		    && (array->data[array->len - 1] == '\0')) {
752  			g_object_set (setting, key, array, NULL);
753  			return TRUE;
754  		}
755  		return FALSE;
756  	}
757  	
758  	static gboolean
759  	handle_as_path (GByteArray *array,
760  	                NMSetting *setting,
761  	                const char *key,
762  	                const char *keyfile_path)
763  	{
764  		gsize validate_len = array->len;
765  		GByteArray *val;
766  		char *path;
767  		gboolean exists, success = FALSE;
768  	
769  		if (array->len > 500 || array->len < 1)
770  			return FALSE;
771  	
772  		/* If there's a trailing NULL tell g_utf8_validate() to to until the NULL */
773  		if (array->data[array->len - 1] == '\0')
774  			validate_len = -1;
775  	
776  		if (g_utf8_validate ((const char *) array->data, validate_len, NULL) == FALSE)
777  			return FALSE;
778  	
779  		/* Might be a bare path without the file:// prefix; in that case
780  		 * if it's an absolute path, use that, otherwise treat it as a
781  		 * relative path to the current directory.
782  		 */
783  	
784  		path = get_cert_path (keyfile_path, array);
785  		exists = g_file_test (path, G_FILE_TEST_EXISTS);
786  		if (   exists
787  		    || memchr (array->data, '/', array->len)
788  		    || has_cert_ext (path)) {
789  			/* Construct the proper value as required for the PATH scheme */
790  			val = g_byte_array_sized_new (strlen (SCHEME_PATH) + strlen (path) + 1);
791  			g_byte_array_append (val, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
792  			g_byte_array_append (val, (const guint8 *) path, strlen (path));
793  			g_byte_array_append (val, (const guint8 *) "\0", 1);
794  			g_object_set (setting, key, val, NULL);
795  			g_byte_array_free (val, TRUE);
796  			success = TRUE;
797  	
798  			/* Warn if the certificate didn't exist */
799  			if (exists == FALSE)
800  				PLUGIN_WARN (KEYFILE_PLUGIN_NAME, "   certificate or key %s does not exist", path);
801  		}
802  		g_free (path);
803  	
804  		return success;
805  	}
806  	
807  	static void
808  	cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
809  	{
810  		const char *setting_name = nm_setting_get_name (setting);
811  		GByteArray *array;
812  		gboolean success = FALSE;
813  	
814  		array = get_uchar_array (keyfile, setting_name, key, TRUE, FALSE);
815  		if (array && array->len > 0) {
816  			/* Try as a path + scheme (ie, starts with "file://") */
817  			success = handle_as_scheme (array, setting, key);
818  	
819  			/* If not, it might be a plain path */
820  			if (success == FALSE)
821  				success = handle_as_path (array, setting, key, keyfile_path);
822  	
823  			/* If neither of those two, assume blob with certificate data */
824  			if (success == FALSE)
825  				g_object_set (setting, key, array, NULL);
826  		} else {
827  			g_warning ("%s: ignoring invalid key/cert value for %s / %s",
828  			           __func__, setting_name, key);
829  		}
830  	
831  		if (array)
832  			g_byte_array_free (array, TRUE);
833  	}
834  	
835  	typedef struct {
836  		const char *setting_name;
837  		const char *key;
838  		gboolean check_for_key;
839  		void (*parser) (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path);
840  	} KeyParser;
841  	
842  	/* A table of keys that require further parsing/conversion because they are
843  	 * stored in a format that can't be automatically read using the key's type.
844  	 * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are
845  	 * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored
846  	 * in struct in6_addr internally, but as string in keyfiles.
847  	 */
848  	static KeyParser key_parsers[] = {
849  		{ NM_SETTING_CONNECTION_SETTING_NAME,
850  		  NM_SETTING_CONNECTION_TYPE,
851  		  TRUE,
852  		  setting_alias_parser },
853  		{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
854  		  NM_SETTING_IP4_CONFIG_ADDRESSES,
855  		  FALSE,
856  		  ip_address_or_route_parser },
857  		{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
858  		  NM_SETTING_IP6_CONFIG_ADDRESSES,
859  		  FALSE,
860  		  ip_address_or_route_parser },
861  		{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
862  		  NM_SETTING_IP4_CONFIG_ROUTES,
863  		  FALSE,
864  		  ip_address_or_route_parser },
865  		{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
866  		  NM_SETTING_IP6_CONFIG_ROUTES,
867  		  FALSE,
868  		  ip_address_or_route_parser },
869  		{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
870  		  NM_SETTING_IP4_CONFIG_DNS,
871  		  FALSE,
872  		  ip4_dns_parser },
873  		{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
874  		  NM_SETTING_IP6_CONFIG_DNS,
875  		  FALSE,
876  		  ip6_dns_parser },
877  		{ NM_SETTING_WIRED_SETTING_NAME,
878  		  NM_SETTING_WIRED_MAC_ADDRESS,
879  		  TRUE,
880  		  mac_address_parser },
881  		{ NM_SETTING_WIRED_SETTING_NAME,
882  		  NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
883  		  TRUE,
884  		  mac_address_parser },
885  		{ NM_SETTING_WIRELESS_SETTING_NAME,
886  		  NM_SETTING_WIRELESS_MAC_ADDRESS,
887  		  TRUE,
888  		  mac_address_parser },
889  		{ NM_SETTING_WIRELESS_SETTING_NAME,
890  		  NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS,
891  		  TRUE,
892  		  mac_address_parser },
893  		{ NM_SETTING_WIRELESS_SETTING_NAME,
894  		  NM_SETTING_WIRELESS_BSSID,
895  		  TRUE,
896  		  mac_address_parser },
897  		{ NM_SETTING_BLUETOOTH_SETTING_NAME,
898  		  NM_SETTING_BLUETOOTH_BDADDR,
899  		  TRUE,
900  		  mac_address_parser },
901  		{ NM_SETTING_INFINIBAND_SETTING_NAME,
902  		  NM_SETTING_INFINIBAND_MAC_ADDRESS,
903  		  TRUE,
904  		  mac_address_parser },
905  		{ NM_SETTING_WIMAX_SETTING_NAME,
906  		  NM_SETTING_WIMAX_MAC_ADDRESS,
907  		  TRUE,
908  		  mac_address_parser },
909  		{ NM_SETTING_WIRELESS_SETTING_NAME,
910  		  NM_SETTING_WIRELESS_SSID,
911  		  TRUE,
912  		  ssid_parser },
913  		{ NM_SETTING_802_1X_SETTING_NAME,
914  		  NM_SETTING_802_1X_PASSWORD_RAW,
915  		  TRUE,
916  		  password_raw_parser },
917  		{ NM_SETTING_802_1X_SETTING_NAME,
918  		  NM_SETTING_802_1X_CA_CERT,
919  		  TRUE,
920  		  cert_parser },
921  		{ NM_SETTING_802_1X_SETTING_NAME,
922  		  NM_SETTING_802_1X_CLIENT_CERT,
923  		  TRUE,
924  		  cert_parser },
925  		{ NM_SETTING_802_1X_SETTING_NAME,
926  		  NM_SETTING_802_1X_PRIVATE_KEY,
927  		  TRUE,
928  		  cert_parser },
929  		{ NM_SETTING_802_1X_SETTING_NAME,
930  		  NM_SETTING_802_1X_PHASE2_CA_CERT,
931  		  TRUE,
932  		  cert_parser },
933  		{ NM_SETTING_802_1X_SETTING_NAME,
934  		  NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
935  		  TRUE,
936  		  cert_parser },
937  		{ NM_SETTING_802_1X_SETTING_NAME,
938  		  NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
939  		  TRUE,
940  		  cert_parser },
941  		{ NULL, NULL, FALSE }
942  	};
943  	
944  	typedef struct {
945  		GKeyFile *keyfile;
946  		const char *keyfile_path;
947  	} ReadInfo;
948  	
949  	static void
950  	read_one_setting_value (NMSetting *setting,
951  	                        const char *key,
952  	                        const GValue *value,
953  	                        GParamFlags flags,
954  	                        gpointer user_data)
955  	{
956  		ReadInfo *info = user_data;
957  		const char *setting_name;
958  		GType type;
959  		GError *err = NULL;
960  		gboolean check_for_key = TRUE;
961  		KeyParser *parser = &key_parsers[0];
962  	
963  		/* Property is not writable */
964  		if (!(flags & G_PARAM_WRITABLE))
965  			return;
966  	
967  		/* Setting name gets picked up from the keyfile's section name instead */
968  		if (!strcmp (key, NM_SETTING_NAME))
969  			return;
970  	
971  		/* Don't read the NMSettingConnection object's 'read-only' property */
972  		if (   NM_IS_SETTING_CONNECTION (setting)
973  		    && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY))
974  			return;
975  	
976  		setting_name = nm_setting_get_name (setting);
977  	
978  		/* Look through the list of handlers for non-standard format key values */
(2) Event deref_ptr: Directly dereferencing pointer "parser".
Also see events: [check_after_deref]
979  		while (parser->setting_name) {
980  			if (!strcmp (parser->setting_name, setting_name) && !strcmp (parser->key, key)) {
981  				check_for_key = parser->check_for_key;
982  				break;
983  			}
984  			parser++;
985  		}
986  	
987  		/* VPN properties don't have the exact key name */
988  		if (NM_IS_SETTING_VPN (setting))
989  			check_for_key = FALSE;
990  	
991  		/* Bonding 'options' don't have the exact key name. The options are right under [bond] group. */
992  		if (NM_IS_SETTING_BOND (setting))
993  			check_for_key = FALSE;
994  	
995  		/* Check for the exact key in the GKeyFile if required.  Most setting
996  		 * properties map 1:1 to a key in the GKeyFile, but for those properties
997  		 * like IP addresses and routes where more than one value is actually
998  		 * encoded by the setting property, this won't be true.
999  		 */
1000 		if (check_for_key && !nm_keyfile_plugin_kf_has_key (info->keyfile, setting_name, key, &err)) {
1001 			/* Key doesn't exist or an error ocurred, thus nothing to do. */
1002 			if (err) {
1003 				g_warning ("Error loading setting '%s' value: %s", setting_name, err->message);
1004 				g_error_free (err);
1005 			}
1006 			return;
1007 		}
1008 	
1009 		/* If there's a custom parser for this key, handle that before the generic
1010 		 * parsers below.
1011 		 */
(1) Event check_after_deref: Null-checking "parser" suggests that it may be null, but it has already been dereferenced on all paths leading to the check.
Also see events: [deref_ptr]
1012 		if (parser && parser->setting_name) {
1013 			(*parser->parser) (setting, key, info->keyfile, info->keyfile_path);
1014 			return;
1015 		}
1016 	
1017 		type = G_VALUE_TYPE (value);
1018 	
1019 		if (type == G_TYPE_STRING) {
1020 			char *str_val;
1021 	
1022 			str_val = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
1023 			g_object_set (setting, key, str_val, NULL);
1024 			g_free (str_val);
1025 		} else if (type == G_TYPE_UINT) {
1026 			int int_val;
1027 	
1028 			int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
1029 			if (int_val < 0)
1030 				g_warning ("Casting negative value (%i) to uint", int_val);
1031 			g_object_set (setting, key, int_val, NULL);
1032 		} else if (type == G_TYPE_INT) {
1033 			int int_val;
1034 	
1035 			int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
1036 			g_object_set (setting, key, int_val, NULL);
1037 		} else if (type == G_TYPE_BOOLEAN) {
1038 			gboolean bool_val;
1039 	
1040 			bool_val = nm_keyfile_plugin_kf_get_boolean (info->keyfile, setting_name, key, NULL);
1041 			g_object_set (setting, key, bool_val, NULL);
1042 		} else if (type == G_TYPE_CHAR) {
1043 			int int_val;
1044 	
1045 			int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
1046 			if (int_val < G_MININT8 || int_val > G_MAXINT8)
1047 				g_warning ("Casting value (%i) to char", int_val);
1048 	
1049 			g_object_set (setting, key, int_val, NULL);
1050 		} else if (type == G_TYPE_UINT64) {
1051 			char *tmp_str;
1052 			guint64 uint_val;
1053 	
1054 			tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, NULL);
1055 			uint_val = g_ascii_strtoull (tmp_str, NULL, 10);
1056 			g_free (tmp_str);
1057 			g_object_set (setting, key, uint_val, NULL);
1058 	 	} else if (type == DBUS_TYPE_G_UCHAR_ARRAY) {
1059 			gint *tmp;
1060 			GByteArray *array;
1061 			gsize length;
1062 			int i;
1063 	
1064 			tmp = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL);
1065 	
1066 			array = g_byte_array_sized_new (length);
1067 			for (i = 0; i < length; i++) {
1068 				int val = tmp[i];
1069 				unsigned char v = (unsigned char) (val & 0xFF);
1070 	
1071 				if (val < 0 || val > 255) {
1072 					g_warning ("%s: %s / %s ignoring invalid byte element '%d' (not "
1073 					           " between 0 and 255 inclusive)", __func__, setting_name,
1074 					           key, val);
1075 				} else
1076 					g_byte_array_append (array, (const unsigned char *) &v, sizeof (v));
1077 			}
1078 	
1079 			g_object_set (setting, key, array, NULL);
1080 			g_byte_array_free (array, TRUE);
1081 			g_free (tmp);
1082 	 	} else if (type == DBUS_TYPE_G_LIST_OF_STRING) {
1083 			gchar **sa;
1084 			gsize length;
1085 			int i;
1086 			GSList *list = NULL;
1087 	
1088 			sa = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL);
1089 			for (i = 0; i < length; i++)
1090 				list = g_slist_prepend (list, sa[i]);
1091 	
1092 			list = g_slist_reverse (list);
1093 			g_object_set (setting, key, list, NULL);
1094 	
1095 			g_slist_free (list);
1096 			g_strfreev (sa);
1097 		} else if (type == DBUS_TYPE_G_MAP_OF_STRING) {
1098 			read_hash_of_string (info->keyfile, setting, key);
1099 		} else if (type == DBUS_TYPE_G_UINT_ARRAY) {
1100 			if (!read_array_of_uint (info->keyfile, setting, key)) {
1101 				g_warning ("Unhandled setting property type (read): '%s/%s' : '%s'",
1102 						 setting_name, key, G_VALUE_TYPE_NAME (value));
1103 			}
1104 		} else {
1105 			g_warning ("Unhandled setting property type (read): '%s/%s' : '%s'",
1106 					 setting_name, key, G_VALUE_TYPE_NAME (value));
1107 		}
1108 	}
1109 	
1110 	static NMSetting *
1111 	read_setting (GKeyFile *file, const char *keyfile_path, const char *group)
1112 	{
1113 		NMSetting *setting;
1114 		ReadInfo info = { file, keyfile_path };
1115 		const char *alias;
1116 	
1117 		alias = nm_keyfile_plugin_get_setting_name_for_alias (group);
1118 		setting = nm_connection_create_setting (alias ? alias : group);
1119 		if (setting)
1120 			nm_setting_enumerate_values (setting, read_one_setting_value, &info);
1121 		else
1122 			g_warning ("Invalid setting name '%s'", group);
1123 	
1124 		return setting;
1125 	}
1126 	
1127 	static void
1128 	read_vpn_secrets (GKeyFile *file, NMSettingVPN *s_vpn)
1129 	{
1130 		char **keys, **iter;
1131 	
1132 		keys = nm_keyfile_plugin_kf_get_keys (file, VPN_SECRETS_GROUP, NULL, NULL);
1133 		for (iter = keys; *iter; iter++) {
1134 			char *secret;
1135 	
1136 			secret = nm_keyfile_plugin_kf_get_string (file, VPN_SECRETS_GROUP, *iter, NULL);
1137 			if (secret) {
1138 				nm_setting_vpn_add_secret (s_vpn, *iter, secret);
1139 				g_free (secret);
1140 			}
1141 		}
1142 		g_strfreev (keys);
1143 	}
1144 	
1145 	NMConnection *
1146 	nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
1147 	{
1148 		GKeyFile *key_file;
1149 		struct stat statbuf;
1150 		gboolean bad_permissions;
1151 		NMConnection *connection = NULL;
1152 		NMSettingConnection *s_con;
1153 		NMSetting *setting;
1154 		gchar **groups;
1155 		gsize length;
1156 		int i;
1157 		gboolean vpn_secrets = FALSE;
1158 		const char *ctype;
1159 		GError *verify_error = NULL;
1160 	
1161 		if (stat (filename, &statbuf) != 0 || !S_ISREG (statbuf.st_mode)) {
1162 			g_set_error_literal (error, KEYFILE_PLUGIN_ERROR, 0,
1163 			                     "File did not exist or was not a regular file");
1164 			return NULL;
1165 		}
1166 	
1167 		bad_permissions = statbuf.st_mode & 0077;
1168 	
1169 		if (bad_permissions) {
1170 			g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
1171 			             "File permissions (%o) were insecure",
1172 			             statbuf.st_mode);
1173 			return NULL;
1174 		}
1175 	
1176 		key_file = g_key_file_new ();
1177 		if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error))
1178 			goto out;
1179 	
1180 		connection = nm_connection_new ();
1181 	
1182 		groups = g_key_file_get_groups (key_file, &length);
1183 		for (i = 0; i < length; i++) {
1184 			/* Only read out secrets when needed */
1185 			if (!strcmp (groups[i], VPN_SECRETS_GROUP)) {
1186 				vpn_secrets = TRUE;
1187 				continue;
1188 			}
1189 	
1190 			setting = read_setting (key_file, filename, groups[i]);
1191 			if (setting)
1192 				nm_connection_add_setting (connection, setting);
1193 		}
1194 	
1195 		/* Make sure that we have the base device type setting even if
1196 		 * the keyfile didn't include it, which can happen when the base
1197 		 * device type setting is all default values (like ethernet where
1198 		 * the MAC address isn't given, or VLAN when the VLAN ID is zero).
1199 		 */
1200 		s_con = nm_connection_get_setting_connection (connection);
1201 		if (s_con) {
1202 			ctype = nm_setting_connection_get_connection_type (s_con);
1203 			setting = nm_connection_get_setting_by_name (connection, ctype);
1204 			if (ctype && !setting) {
1205 				NMSetting *base_setting;
1206 				GType base_setting_type;
1207 	
1208 				base_setting_type = nm_connection_lookup_setting_type (ctype);
1209 				if (base_setting_type != G_TYPE_INVALID) {
1210 					base_setting = (NMSetting *) g_object_new (base_setting_type, NULL);
1211 					g_assert (base_setting);
1212 					nm_connection_add_setting (connection, base_setting);
1213 				}
1214 			}
1215 		}
1216 	
1217 		/* Handle vpn secrets after the 'vpn' setting was read */
1218 		if (vpn_secrets) {
1219 			NMSettingVPN *s_vpn;
1220 	
1221 			s_vpn = nm_connection_get_setting_vpn (connection);
1222 			if (s_vpn)
1223 				read_vpn_secrets (key_file, s_vpn);
1224 		}
1225 	
1226 		g_strfreev (groups);
1227 	
1228 		/* Verify the connection */
1229 		if (!nm_connection_verify (connection, &verify_error)) {
1230 			g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
1231 				         "invalid or missing connection property '%s/%s'",
1232 				         verify_error ? g_type_name (nm_connection_lookup_setting_type_by_quark (verify_error->domain)) : "(unknown)",
1233 				         (verify_error && verify_error->message) ? verify_error->message : "(unknown)");
1234 			g_clear_error (&verify_error);
1235 			g_object_unref (connection);
1236 			connection = NULL;
1237 			g_warning ("Connection failed to verify: %s",
1238 				verify_error ? g_type_name (nm_connection_lookup_setting_type_by_quark (verify_error->domain)) : "(unknown)");
1239 		}
1240 	
1241 	out:
1242 		g_key_file_free (key_file);
1243 		return connection;
1244 	}
1245