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 Novell, Inc.
19   	 * Copyright (C) 2008 - 2012 Red Hat, Inc.
20   	 */
21   	
22   	#include <config.h>
23   	#include <stdlib.h>
24   	#include <sys/stat.h>
25   	#include <unistd.h>
26   	#include <stdio.h>
27   	#include <errno.h>
28   	
29   	#include <dbus/dbus-glib.h>
30   	#include <nm-setting.h>
31   	#include <nm-setting-connection.h>
32   	#include <nm-setting-ip4-config.h>
33   	#include <nm-setting-ip6-config.h>
34   	#include <nm-setting-vpn.h>
35   	#include <nm-setting-wired.h>
36   	#include <nm-setting-wireless.h>
37   	#include <nm-setting-ip4-config.h>
38   	#include <nm-setting-bluetooth.h>
39   	#include <nm-setting-8021x.h>
40   	#include <nm-utils.h>
41   	#include <string.h>
42   	#include <arpa/inet.h>
43   	#include <netinet/ether.h>
44   	
45   	#include "nm-dbus-glib-types.h"
46   	#include "nm-glib-compat.h"
47   	#include "writer.h"
48   	#include "common.h"
49   	#include "utils.h"
50   	
51   	/* Some setting properties also contain setting names, such as
52   	 * NMSettingConnection's 'type' property (which specifies the base type of the
53   	 * connection, eg ethernet or wifi) or the 802-11-wireless setting's
54   	 * 'security' property which specifies whether or not the AP requires
55   	 * encrpytion.  This function handles translating those properties' values
56   	 * from the real setting name to the more-readable alias.
57   	 */
58   	static void
59   	setting_alias_writer (GKeyFile *file,
60   	                      const char *keyfile_dir,
61   	                      const char *uuid,
62   	                      NMSetting *setting,
63   	                      const char *key,
64   	                      const GValue *value)
65   	{
66   		const char *str, *alias;
67   	
68   		str = g_value_get_string (value);
69   		alias = nm_keyfile_plugin_get_alias_for_setting_name (str);
70   		nm_keyfile_plugin_kf_set_string (file,
71   		                                 nm_setting_get_name (setting),
72   		                                 key,
73   		                                 alias ? alias : str);
74   	}
75   	
76   	static gboolean
77   	write_array_of_uint (GKeyFile *file,
78   	                     NMSetting *setting,
79   	                     const char *key,
80   	                     const GValue *value)
81   	{
82   		GArray *array;
83   		int i;
84   		int *tmp_array;
85   	
86   		array = (GArray *) g_value_get_boxed (value);
87   		if (!array || !array->len)
88   			return TRUE;
89   	
90   		tmp_array = g_new (gint, array->len);
91   		for (i = 0; i < array->len; i++)
92   			tmp_array[i] = g_array_index (array, int, i);
93   	
94   		nm_keyfile_plugin_kf_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len);
95   		g_free (tmp_array);
96   		return TRUE;
97   	}
98   	
99   	static void
100  	ip4_dns_writer (GKeyFile *file,
101  	                const char *keyfile_dir,
102  	                const char *uuid,
103  	                NMSetting *setting,
104  	                const char *key,
105  	                const GValue *value)
106  	{
107  		GArray *array;
108  		char **list;
109  		int i, num = 0;
110  	
111  		g_return_if_fail (G_VALUE_HOLDS (value, DBUS_TYPE_G_UINT_ARRAY));
112  	
113  		array = (GArray *) g_value_get_boxed (value);
114  		if (!array || !array->len)
115  			return;
116  	
117  		list = g_new0 (char *, array->len + 1);
118  	
119  		for (i = 0; i < array->len; i++) {
120  			char buf[INET_ADDRSTRLEN + 1];
121  			guint32 addr;
122  	
123  			addr = g_array_index (array, guint32, i);
124  			if (!inet_ntop (AF_INET, &addr, buf, sizeof (buf))) {
125  				g_warning ("%s: error converting IP4 address 0x%X",
126  				           __func__, ntohl (addr));
127  			} else
128  				list[num++] = g_strdup (buf);
129  		}
130  	
131  		nm_keyfile_plugin_kf_set_string_list (file, nm_setting_get_name (setting), key, (const char **) list, num);
132  		g_strfreev (list);
133  	}
134  	
135  	static void
136  	write_ip4_values (GKeyFile *file,
137  	                  const char *setting_name,
138  	                  const char *key,
139  	                  GPtrArray *array,
140  	                  guint32 tuple_len,
141  	                  guint32 addr1_pos,
142  	                  guint32 addr2_pos)
143  	{
144  		GString *output;
145  		int i, j;
146  	
147  		for (i = 0, j = 0; i < array->len; i++, j++) {
148  			GArray *tuple = g_ptr_array_index (array, i);
149  			gboolean success = TRUE;
150  			char *key_name;
151  			int k;
152  	
153  			output = g_string_new ("");
154  	
155  			for (k = 0; k < tuple_len; k++) {
156  				if (k == addr1_pos || k == addr2_pos) {
157  					char buf[INET_ADDRSTRLEN + 1];
158  					guint32 addr;
159  	
160  					/* IP addresses */
161  					addr = g_array_index (tuple, guint32, k);
162  					if (!inet_ntop (AF_INET, &addr, buf, sizeof (buf))) {
163  						g_warning ("%s: error converting IP4 address 0x%X",
164  						           __func__, ntohl (addr));
165  						success = FALSE;
166  						break;
167  					} else {
168  						g_string_append_printf (output, "%s%s", k == 0 ? "" : ",", buf);
169  					}
170  				} else {
171  					/* prefix, metric */
172  					g_string_append_printf (output, "%c%d", k == 1 ? '/' : ',', g_array_index (tuple, guint32, k));
173  				}
174  			}
175  	
176  			if (success) {
177  				key_name = g_strdup_printf ("%s%d", key, j + 1);
178  				nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str);
179  				g_free (key_name);
180  			}
181  	
182  			g_string_free (output, TRUE);
183  	
184  		}
185  	}
186  	
187  	static void
188  	ip4_addr_writer (GKeyFile *file,
189  	                 const char *keyfile_dir,
190  	                 const char *uuid,
191  	                 NMSetting *setting,
192  	                 const char *key,
193  	                 const GValue *value)
194  	{
195  		GPtrArray *array;
196  		const char *setting_name = nm_setting_get_name (setting);
197  	
198  		g_return_if_fail (G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT));
199  	
200  		array = (GPtrArray *) g_value_get_boxed (value);
201  		if (array && array->len)
202  			write_ip4_values (file, setting_name, "address", array, 3, 0, 2);
203  	}
204  	
205  	static void
206  	ip4_route_writer (GKeyFile *file,
207  	                  const char *keyfile_dir,
208  	                  const char *uuid,
209  	                  NMSetting *setting,
210  	                  const char *key,
211  	                  const GValue *value)
212  	{
213  		GPtrArray *array;
214  		const char *setting_name = nm_setting_get_name (setting);
215  	
216  		g_return_if_fail (G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT));
217  	
218  		array = (GPtrArray *) g_value_get_boxed (value);
219  		if (array && array->len)
220  			write_ip4_values (file, setting_name, "route", array, 4, 0, 2);
221  	}
222  	
223  	static void
224  	ip6_dns_writer (GKeyFile *file,
225  	                const char *keyfile_dir,
226  	                const char *uuid,
227  	                NMSetting *setting,
228  	                const char *key,
229  	                const GValue *value)
230  	{
231  		GPtrArray *array;
232  		GByteArray *byte_array;
233  		char **list;
234  		int i, num = 0;
235  	
236  		g_return_if_fail (G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR));
237  	
238  		array = (GPtrArray *) g_value_get_boxed (value);
239  		if (!array || !array->len)
240  			return;
241  	
242  		list = g_new0 (char *, array->len + 1);
243  	
244  		for (i = 0; i < array->len; i++) {
245  			char buf[INET6_ADDRSTRLEN];
246  	
247  			byte_array = g_ptr_array_index (array, i);
248  			if (!inet_ntop (AF_INET6, (struct in6_addr *) byte_array->data, buf, sizeof (buf))) {
249  				int j;
250  				GString *ip6_str = g_string_new (NULL);
251  				g_string_append_printf (ip6_str, "%02X", byte_array->data[0]);
252  				for (j = 1; j < 16; j++)
253  					g_string_append_printf (ip6_str, " %02X", byte_array->data[j]);
254  				g_warning ("%s: error converting IP6 address %s",
255  				           __func__, ip6_str->str);
256  				g_string_free (ip6_str, TRUE);
257  			} else
258  				list[num++] = g_strdup (buf);
259  		}
260  	
261  		nm_keyfile_plugin_kf_set_string_list (file, nm_setting_get_name (setting), key, (const char **) list, num);
262  		g_strfreev (list);
263  	}
264  	
265  	static gboolean
266  	ip6_array_to_addr (GValueArray *values,
267  	                   guint32 idx,
268  	                   char *buf,
269  	                   size_t buflen,
270  	                   gboolean *out_is_unspec)
271  	{
272  		GByteArray *byte_array;
273  		GValue *addr_val;
274  		struct in6_addr *addr;
275  	
276  		g_return_val_if_fail (buflen >= INET6_ADDRSTRLEN, FALSE);
277  	
278  		addr_val = g_value_array_get_nth (values, idx);
279  		byte_array = g_value_get_boxed (addr_val);
280  		addr = (struct in6_addr *) byte_array->data;
281  	
282  		if (out_is_unspec && IN6_IS_ADDR_UNSPECIFIED (addr))
283  			*out_is_unspec = TRUE;
284  	
285  		errno = 0;
286  		if (!inet_ntop (AF_INET6, addr, buf, buflen)) {
287  			GString *ip6_str = g_string_sized_new (INET6_ADDRSTRLEN + 10);
288  	
289  			/* error converting the address */
290  			g_string_append_printf (ip6_str, "%02X", byte_array->data[0]);
291  			for (idx = 1; idx < 16; idx++)
292  				g_string_append_printf (ip6_str, " %02X", byte_array->data[idx]);
293  			g_warning ("%s: error %d converting IP6 address %s",
294  			           __func__, errno, ip6_str->str);
295  			g_string_free (ip6_str, TRUE);
296  			return FALSE;
297  		}
298  	
299  		return TRUE;
300  	}
301  	
302  	static char *
303  	ip6_array_to_addr_prefix (GValueArray *values)
304  	{
305  		GValue *prefix_val;
306  		char *ret = NULL;
307  		GString *ip6_str;
308  		char buf[INET6_ADDRSTRLEN + 1];
309  		gboolean is_unspec = FALSE;
310  	
311  		/* address */
312  		if (ip6_array_to_addr (values, 0, buf, sizeof (buf), NULL)) {
313  			/* Enough space for the address, '/', and the prefix */
314  			ip6_str = g_string_sized_new ((INET6_ADDRSTRLEN * 2) + 5);
315  	
316  			/* prefix */
317  			g_string_append (ip6_str, buf);
318  			prefix_val = g_value_array_get_nth (values, 1);
319  			g_string_append_printf (ip6_str, "/%u", g_value_get_uint (prefix_val));
320  	
321  			if (ip6_array_to_addr (values, 2, buf, sizeof (buf), &is_unspec)) {
322  				if (!is_unspec)
323  					g_string_append_printf (ip6_str, ",%s", buf);
324  			}
325  	
326  			ret = ip6_str->str;
327  			g_string_free (ip6_str, FALSE);
328  		}
329  	
330  		return ret;
331  	}
332  	
333  	static void
334  	ip6_addr_writer (GKeyFile *file,
335  	                 const char *keyfile_dir,
336  	                 const char *uuid,
337  	                 NMSetting *setting,
338  	                 const char *key,
339  	                 const GValue *value)
340  	{
341  		GPtrArray *array;
342  		const char *setting_name = nm_setting_get_name (setting);
343  		int i, j;
344  	
345  		g_return_if_fail (G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS));
346  	
347  		array = (GPtrArray *) g_value_get_boxed (value);
348  		if (!array || !array->len)
349  			return;
350  	
351  		for (i = 0, j = 1; i < array->len; i++) {
352  			GValueArray *values = g_ptr_array_index (array, i);
353  			char *key_name, *ip6_addr;
354  	
355  			if (values->n_values != 3) {
356  				g_warning ("%s: error writing IP6 address %d (address array length "
357  				           "%d is not 3)",
358  				           __func__, i, values->n_values);
359  				continue;
360  			}
361  	
362  			ip6_addr = ip6_array_to_addr_prefix (values);
363  			if (ip6_addr) {
364  				/* Write it out */
365  				key_name = g_strdup_printf ("address%d", j++);
366  				nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, ip6_addr);
367  				g_free (key_name);
368  				g_free (ip6_addr);
369  			}
370  		}
371  	}
372  	
373  	static void
374  	ip6_route_writer (GKeyFile *file,
375  	                  const char *keyfile_dir,
376  	                  const char *uuid,
377  	                  NMSetting *setting,
378  	                  const char *key,
379  	                  const GValue *value)
380  	{
381  		GPtrArray *array;
382  		const char *setting_name = nm_setting_get_name (setting);
383  		GString *output;
384  		int i, j;
385  	
386  		g_return_if_fail (G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE));
387  	
388  		array = (GPtrArray *) g_value_get_boxed (value);
389  		if (!array || !array->len)
390  			return;
391  	
392  		for (i = 0, j = 1; i < array->len; i++) {
393  			GValueArray *values = g_ptr_array_index (array, i);
394  			char *key_name;
395  			guint32 int_val;
396  	
397  			output = g_string_new ("");
398  	
399  			/* Address, prefix and next hop*/
400  			g_string_append (output, ip6_array_to_addr_prefix (values));
401  	
402  			/* Metric */
403  			value = g_value_array_get_nth (values, 3);
404  			int_val = g_value_get_uint (value);
405  			g_string_append_printf (output, ",%d", int_val);
406  	
407  			/* Write it out */
408  			key_name = g_strdup_printf ("route%d", j++);
409  			nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str);
410  			g_free (key_name);
411  	
412  			g_string_free (output, TRUE);
413  		}
414  	}
415  	
416  	
417  	static void
418  	mac_address_writer (GKeyFile *file,
419  	                    const char *keyfile_dir,
420  	                    const char *uuid,
421  	                    NMSetting *setting,
422  	                    const char *key,
423  	                    const GValue *value)
424  	{
425  		GByteArray *array;
426  		const char *setting_name = nm_setting_get_name (setting);
427  		char *mac;
428  		int type;
429  	
430  		g_return_if_fail (G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY));
431  	
432  		array = (GByteArray *) g_value_get_boxed (value);
433  		if (!array)
434  			return;
435  	
436  		type = nm_utils_hwaddr_type (array->len);
437  		if (type < 0) {
438  			g_warning ("%s: invalid %s / %s MAC address length %d",
439  			           __func__, setting_name, key, array->len);
440  			return;
441  		}
442  	
443  		mac = nm_utils_hwaddr_ntoa (array->data, type);
444  		nm_keyfile_plugin_kf_set_string (file, setting_name, key, mac);
445  		g_free (mac);
446  	}
447  	
448  	static void
449  	write_hash_of_string (GKeyFile *file,
450  	                      NMSetting *setting,
451  	                      const char *key,
452  	                      const GValue *value)
453  	{
454  		GHashTableIter iter;
455  		const char *property = NULL, *data = NULL;
456  		const char *group_name = nm_setting_get_name (setting);
457  		gboolean vpn_secrets = FALSE;
458  	
459  		/* Write VPN secrets out to a different group to keep them separate */
460  		if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) {
461  			group_name = VPN_SECRETS_GROUP;
462  			vpn_secrets = TRUE;
463  		}
464  	
465  		g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value));
466  		while (g_hash_table_iter_next (&iter, (gpointer *) &property, (gpointer *) &data)) {
467  			gboolean write_item = TRUE;
468  	
469  			/* Handle VPN secrets specially; they are nested in the property's hash;
470  			 * we don't want to write them if the secret is not saved, not required,
471  			 * or owned by a user's secret agent.
472  			 */
473  			if (vpn_secrets) {
474  				NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
475  	
476  				nm_setting_get_secret_flags (setting, property, &secret_flags, NULL);
477  				if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
478  					write_item = FALSE;
479  			}
480  	
481  			if (write_item)
482  				nm_keyfile_plugin_kf_set_string (file, group_name, property, data);
483  		}
484  	}
485  	
486  	static void
487  	ssid_writer (GKeyFile *file,
488  	             const char *keyfile_dir,
489  	             const char *uuid,
490  	             NMSetting *setting,
491  	             const char *key,
492  	             const GValue *value)
493  	{
494  		GByteArray *array;
495  		const char *setting_name = nm_setting_get_name (setting);
496  		gboolean new_format = TRUE;
497  		unsigned int semicolons = 0;
498  		int i, *tmp_array;
499  		char *ssid;
500  	
501  		g_return_if_fail (G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY));
502  	
503  		array = (GByteArray *) g_value_get_boxed (value);
504  		if (!array || !array->len)
505  			return;
506  	
507  		/* Check whether each byte is printable.  If not, we have to use an
508  		 * integer list, otherwise we can just use a string.
509  		 */
510  		for (i = 0; i < array->len; i++) {
511  			char c = array->data[i] & 0xFF;
512  			if (!g_ascii_isprint (c)) {
513  				new_format = FALSE;
514  				break;
515  			}
516  			if (c == ';')
517  				semicolons++;
518  		}
519  	
520  		if (new_format) {
521  			ssid = g_malloc0 (array->len + semicolons + 1);
522  			if (semicolons == 0)
523  				memcpy (ssid, array->data, array->len);
524  			else {
525  				/* Escape semicolons with backslashes to make strings
526  				 * containing ';', such as '16;17;' unambiguous */
527  				int j = 0;
528  				for (i = 0; i < array->len; i++) {
529  					if (array->data[i] == ';')
530  						ssid[j++] = '\\';
531  					ssid[j++] = array->data[i];
532  				}
533  			}
534  			nm_keyfile_plugin_kf_set_string (file, setting_name, key, ssid);
535  			g_free (ssid);
536  		} else {
537  			tmp_array = g_new (gint, array->len);
538  			for (i = 0; i < array->len; i++)
539  				tmp_array[i] = (int) array->data[i];
540  			nm_keyfile_plugin_kf_set_integer_list (file, setting_name, key, tmp_array, array->len);
541  			g_free (tmp_array);
542  		}
543  	}
544  	
545  	static void
546  	password_raw_writer (GKeyFile *file,
547  	                     const char *keyfile_dir,
548  	                     const char *uuid,
549  	                     NMSetting *setting,
550  	                     const char *key,
551  	                     const GValue *value)
552  	{
553  		const char *setting_name = nm_setting_get_name (setting);
554  		GByteArray *array;
555  		int i, *tmp_array;
556  	
557  		g_return_if_fail (G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY));
558  	
559  		array = (GByteArray *) g_value_get_boxed (value);
560  		if (!array || !array->len)
561  			return;
562  	
563  		tmp_array = g_new (gint, array->len);
564  		for (i = 0; i < array->len; i++)
565  			tmp_array[i] = (int) array->data[i];
566  		nm_keyfile_plugin_kf_set_integer_list (file, setting_name, key, tmp_array, array->len);
567  		g_free (tmp_array);
568  	}
569  	
570  	typedef struct ObjectType {
571  		const char *key;
572  		const char *suffix;
573  		const char *privkey_pw_prop;
574  		NMSetting8021xCKScheme (*scheme_func) (NMSetting8021x *setting);
575  		NMSetting8021xCKFormat (*format_func) (NMSetting8021x *setting);
576  		const char *           (*path_func)   (NMSetting8021x *setting);
577  		const GByteArray *     (*blob_func)   (NMSetting8021x *setting);
578  	} ObjectType;
579  	
580  	static const ObjectType objtypes[10] = {
581  		{ NM_SETTING_802_1X_CA_CERT,
582  		  "ca-cert",
583  		  NULL,
584  		  nm_setting_802_1x_get_ca_cert_scheme,
585  		  NULL,
586  		  nm_setting_802_1x_get_ca_cert_path,
587  		  nm_setting_802_1x_get_ca_cert_blob },
588  	
589  		{ NM_SETTING_802_1X_PHASE2_CA_CERT,
590  		  "inner-ca-cert",
591  		  NULL,
592  		  nm_setting_802_1x_get_phase2_ca_cert_scheme,
593  		  NULL,
594  		  nm_setting_802_1x_get_phase2_ca_cert_path,
595  		  nm_setting_802_1x_get_phase2_ca_cert_blob },
596  	
597  		{ NM_SETTING_802_1X_CLIENT_CERT,
598  		  "client-cert",
599  		  NULL,
600  		  nm_setting_802_1x_get_client_cert_scheme,
601  		  NULL,
602  		  nm_setting_802_1x_get_client_cert_path,
603  		  nm_setting_802_1x_get_client_cert_blob },
604  	
605  		{ NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
606  		  "inner-client-cert",
607  		  NULL,
608  		  nm_setting_802_1x_get_phase2_client_cert_scheme,
609  		  NULL,
610  		  nm_setting_802_1x_get_phase2_client_cert_path,
611  		  nm_setting_802_1x_get_phase2_client_cert_blob },
612  	
613  		{ NM_SETTING_802_1X_PRIVATE_KEY,
614  		  "private-key",
615  		  NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
616  		  nm_setting_802_1x_get_private_key_scheme,
617  		  nm_setting_802_1x_get_private_key_format,
618  		  nm_setting_802_1x_get_private_key_path,
619  		  nm_setting_802_1x_get_private_key_blob },
620  	
621  		{ NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
622  		  "inner-private-key",
623  		  NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD,
624  		  nm_setting_802_1x_get_phase2_private_key_scheme,
625  		  nm_setting_802_1x_get_phase2_private_key_format,
626  		  nm_setting_802_1x_get_phase2_private_key_path,
627  		  nm_setting_802_1x_get_phase2_private_key_blob },
628  	
629  		{ NULL },
630  	};
631  	
632  	static gboolean
633  	write_cert_key_file (const char *path,
634  	                     const GByteArray *data,
635  	                     GError **error)
636  	{
637  		char *tmppath;
638  		int fd = -1, written;
639  		gboolean success = FALSE;
640  	
641  		tmppath = g_malloc0 (strlen (path) + 10);
642  		g_assert (tmppath);
643  		memcpy (tmppath, path, strlen (path));
644  		strcat (tmppath, ".XXXXXX");
645  	
646  		errno = 0;
647  		fd = mkstemp (tmppath);
648  		if (fd < 0) {
649  			g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
650  			             "Could not create temporary file for '%s': %d",
651  			             path, errno);
652  			goto out;
653  		}
654  	
655  		/* Only readable by root */
656  		errno = 0;
657  		if (fchmod (fd, S_IRUSR | S_IWUSR) != 0) {
658  			close (fd);
659  			unlink (tmppath);
660  			g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
661  			             "Could not set permissions for temporary file '%s': %d",
662  			             path, errno);
663  			goto out;
664  		}
665  	
666  		errno = 0;
667  		written = write (fd, data->data, data->len);
668  		if (written != data->len) {
669  			close (fd);
670  			unlink (tmppath);
671  			g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
672  			             "Could not write temporary file for '%s': %d",
673  			             path, errno);
674  			goto out;
675  		}
676  		close (fd);
677  	
678  		/* Try to rename */
679  		errno = 0;
680  		if (rename (tmppath, path) == 0)
681  			success = TRUE;
682  		else {
683  			unlink (tmppath);
684  			g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
685  			             "Could not rename temporary file to '%s': %d",
686  			             path, errno);
687  		}
688  	
689  	out:
690  		g_free (tmppath);
691  		return success;
692  	}
693  	
694  	static void
695  	cert_writer (GKeyFile *file,
696  	             const char *keyfile_dir,
697  	             const char *uuid,
698  	             NMSetting *setting,
699  	             const char *key,
700  	             const GValue *value)
701  	{
702  		const char *setting_name = nm_setting_get_name (setting);
703  		NMSetting8021xCKScheme scheme;
704  		NMSetting8021xCKFormat format;
705  		const char *path = NULL, *ext = "pem";
706  		const ObjectType *objtype = NULL;
707  		int i;
708  	
709  		for (i = 0; i < G_N_ELEMENTS (objtypes) && objtypes[i].key; i++) {
710  			if (g_strcmp0 (objtypes[i].key, key) == 0) {
711  				objtype = &objtypes[i];
712  				break;
713  			}
714  		}
715  		g_return_if_fail (objtype != NULL);
716  	
717  		scheme = objtype->scheme_func (NM_SETTING_802_1X (setting));
718  		if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
719  			path = objtype->path_func (NM_SETTING_802_1X (setting));
720  			g_assert (path);
721  	
722  			/* If the path is rooted in the keyfile directory, just use a
723  			 * relative path instead of an absolute one.
724  			 */
725  			if (g_str_has_prefix (path, keyfile_dir)) {
726  				path += strlen (keyfile_dir);
727  				while (*path == '/')
728  					path++;
729  			}
730  	
731  			nm_keyfile_plugin_kf_set_string (file, setting_name, key, path);
732  		} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
733  			const GByteArray *blob;
734  			gboolean success;
735  			GError *error = NULL;
736  			char *new_path;
737  	
738  			blob = objtype->blob_func (NM_SETTING_802_1X (setting));
739  			g_assert (blob);
740  	
741  			if (objtype->format_func) {
742  				/* Get the extension for a private key */
743  				format = objtype->format_func (NM_SETTING_802_1X (setting));
744  				if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
745  					ext = "p12";
746  			} else {
747  				/* DER or PEM format certificate? */
748  				if (blob->len > 2 && blob->data[0] == 0x30 && blob->data[1] == 0x82)
749  					ext = "der";
750  			}
751  	
752  			/* Write the raw data out to the standard file so that we can use paths
753  			 * from now on instead of pushing around the certificate data.
754  			 */
755  			new_path = g_strdup_printf ("%s/%s-%s.%s", keyfile_dir, uuid, objtype->suffix, ext);
756  			g_assert (new_path);
757  	
758  			success = write_cert_key_file (new_path, blob, &error);
759  			if (success) {
760  				/* Write the path value to the keyfile */
761  				nm_keyfile_plugin_kf_set_string (file, setting_name, key, new_path);
762  			} else {
763  				g_warning ("Failed to write certificate/key %s: %s", new_path, error->message);
764  				g_error_free (error);
765  			}
766  			g_free (new_path);
767  		} else
768  			g_assert_not_reached ();
769  	}
770  	
771  	typedef struct {
772  		const char *setting_name;
773  		const char *key;
774  		void (*writer) (GKeyFile *keyfile,
775  		                const char *keyfile_dir,
776  		                const char *uuid,
777  		                NMSetting *setting,
778  		                const char *key,
779  		                const GValue *value);
780  	} KeyWriter;
781  	
782  	/* A table of keys that require further parsing/conversion because they are
783  	 * stored in a format that can't be automatically read using the key's type.
784  	 * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are
785  	 * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored 
786  	 * in struct in6_addr internally, but as string in keyfiles.
787  	 */
788  	static KeyWriter key_writers[] = {
789  		{ NM_SETTING_CONNECTION_SETTING_NAME,
790  		  NM_SETTING_CONNECTION_TYPE,
791  		  setting_alias_writer },
792  		{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
793  		  NM_SETTING_IP4_CONFIG_ADDRESSES,
794  		  ip4_addr_writer },
795  		{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
796  		  NM_SETTING_IP6_CONFIG_ADDRESSES,
797  		  ip6_addr_writer },
798  		{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
799  		  NM_SETTING_IP4_CONFIG_ROUTES,
800  		  ip4_route_writer },
801  		{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
802  		  NM_SETTING_IP6_CONFIG_ROUTES,
803  		  ip6_route_writer },
804  		{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
805  		  NM_SETTING_IP4_CONFIG_DNS,
806  		  ip4_dns_writer },
807  		{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
808  		  NM_SETTING_IP6_CONFIG_DNS,
809  		  ip6_dns_writer },
810  		{ NM_SETTING_WIRED_SETTING_NAME,
811  		  NM_SETTING_WIRED_MAC_ADDRESS,
812  		  mac_address_writer },
813  		{ NM_SETTING_WIRED_SETTING_NAME,
814  		  NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
815  		  mac_address_writer },
816  		{ NM_SETTING_WIRELESS_SETTING_NAME,
817  		  NM_SETTING_WIRELESS_MAC_ADDRESS,
818  		  mac_address_writer },
819  		{ NM_SETTING_WIRELESS_SETTING_NAME,
820  		  NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS,
821  		  mac_address_writer },
822  		{ NM_SETTING_WIRELESS_SETTING_NAME,
823  		  NM_SETTING_WIRELESS_BSSID,
824  		  mac_address_writer },
825  		{ NM_SETTING_BLUETOOTH_SETTING_NAME,
826  		  NM_SETTING_BLUETOOTH_BDADDR,
827  		  mac_address_writer },
828  		{ NM_SETTING_INFINIBAND_SETTING_NAME,
829  		  NM_SETTING_INFINIBAND_MAC_ADDRESS,
830  		  mac_address_writer },
831  		{ NM_SETTING_WIMAX_SETTING_NAME,
832  		  NM_SETTING_WIMAX_MAC_ADDRESS,
833  		  mac_address_writer },
834  		{ NM_SETTING_WIRELESS_SETTING_NAME,
835  		  NM_SETTING_WIRELESS_SSID,
836  		  ssid_writer },
837  		{ NM_SETTING_802_1X_SETTING_NAME,
838  		  NM_SETTING_802_1X_PASSWORD_RAW,
839  		  password_raw_writer },
840  		{ NM_SETTING_802_1X_SETTING_NAME,
841  		  NM_SETTING_802_1X_CA_CERT,
842  		  cert_writer },
843  		{ NM_SETTING_802_1X_SETTING_NAME,
844  		  NM_SETTING_802_1X_CLIENT_CERT,
845  		  cert_writer },
846  		{ NM_SETTING_802_1X_SETTING_NAME,
847  		  NM_SETTING_802_1X_PRIVATE_KEY,
848  		  cert_writer },
849  		{ NM_SETTING_802_1X_SETTING_NAME,
850  		  NM_SETTING_802_1X_PHASE2_CA_CERT,
851  		  cert_writer },
852  		{ NM_SETTING_802_1X_SETTING_NAME,
853  		  NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
854  		  cert_writer },
855  		{ NM_SETTING_802_1X_SETTING_NAME,
856  		  NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
857  		  cert_writer },
858  		{ NULL, NULL, NULL }
859  	};
860  	
861  	typedef struct {
862  		GKeyFile *keyfile;
863  		const char *keyfile_dir;
864  		const char *uuid;
865  	} WriteInfo;
866  	
867  	static void
868  	write_setting_value (NMSetting *setting,
869  	                     const char *key,
870  	                     const GValue *value,
871  	                     GParamFlags flag,
872  	                     gpointer user_data)
873  	{
874  		WriteInfo *info = user_data;
875  		const char *setting_name;
876  		GType type = G_VALUE_TYPE (value);
877  		KeyWriter *writer = &key_writers[0];
878  		GParamSpec *pspec;
879  	
880  		/* Setting name gets picked up from the keyfile's section name instead */
(1) Event cond_false: Condition "!__coverity_strcmp(key, "name")", taking false branch
881  		if (!strcmp (key, NM_SETTING_NAME))
(2) Event if_end: End of if statement
882  			return;
883  	
884  		/* Don't write the NMSettingConnection object's 'read-only' property */
(3) Event cond_false: Condition "!__inst", taking false branch
(4) Event else_branch: Reached else branch
(5) Event cond_true: Condition "__inst->g_class", taking true branch
(6) Event cond_true: Condition "__inst->g_class->g_type == __t", taking true branch
(7) Event if_fallthrough: Falling through to end of if statement
(8) Event if_end: End of if statement
(9) Event cond_true: Condition "({...})", taking true branch
(10) Event cond_false: Condition "!__coverity_strcmp(key, "read-only")", taking false branch
885  		if (   NM_IS_SETTING_CONNECTION (setting)
886  		    && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY))
(11) Event if_end: End of if statement
887  			return;
888  	
889  		setting_name = nm_setting_get_name (setting);
890  	
891  		/* If the value is the default value, remove the item from the keyfile */
892  		pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
(12) Event cond_false: Condition "pspec", taking false branch
(14) Event var_compare_op: Comparing "pspec" to null implies that "pspec" might be null.
Also see events: [var_deref_op]
893  		if (pspec) {
894  			if (g_param_value_defaults (pspec, (GValue *) value)) {
895  				g_key_file_remove_key (info->keyfile, setting_name, key, NULL);
896  				return;
897  			}
(13) Event if_end: End of if statement
898  		}
899  	
900  		/* Don't write secrets that are owned by user secret agents or aren't
901  		 * supposed to be saved.  VPN secrets are handled specially though since
902  		 * the secret flags there are in a third-level hash in the 'secrets'
903  		 * property.
904  		 */
(15) Event var_deref_op: Dereferencing null pointer "pspec".
Also see events: [var_compare_op]
905  		if (pspec->flags & NM_SETTING_PARAM_SECRET && !NM_IS_SETTING_VPN (setting)) {
906  			NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
907  	
908  			nm_setting_get_secret_flags (setting, key, &secret_flags, NULL);
909  			if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
910  				return;
911  		}
912  	
913  		/* Look through the list of handlers for non-standard format key values */
914  		while (writer->setting_name) {
915  			if (!strcmp (writer->setting_name, setting_name) && !strcmp (writer->key, key)) {
916  				(*writer->writer) (info->keyfile, info->keyfile_dir, info->uuid, setting, key, value);
917  				return;
918  			}
919  			writer++;
920  		}
921  	
922  		if (type == G_TYPE_STRING) {
923  			const char *str;
924  	
925  			str = g_value_get_string (value);
926  			if (str)
927  				nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, str);
928  		} else if (type == G_TYPE_UINT)
929  			nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_uint (value));
930  		else if (type == G_TYPE_INT)
931  			nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, g_value_get_int (value));
932  		else if (type == G_TYPE_UINT64) {
933  			char *numstr;
934  	
935  			numstr = g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (value));
936  			nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
937  			g_free (numstr);
938  		} else if (type == G_TYPE_BOOLEAN) {
939  			nm_keyfile_plugin_kf_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value));
940  		} else if (type == G_TYPE_CHAR) {
941  			nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value));
942  		} else if (type == DBUS_TYPE_G_UCHAR_ARRAY) {
943  			GByteArray *array;
944  	
945  			array = (GByteArray *) g_value_get_boxed (value);
946  			if (array && array->len > 0) {
947  				int *tmp_array;
948  				int i;
949  	
950  				tmp_array = g_new (gint, array->len);
951  				for (i = 0; i < array->len; i++)
952  					tmp_array[i] = (int) array->data[i];
953  	
954  				nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, array->len);
955  				g_free (tmp_array);
956  			}
957  		} else if (type == DBUS_TYPE_G_LIST_OF_STRING) {
958  			GSList *list;
959  			GSList *iter;
960  	
961  			list = (GSList *) g_value_get_boxed (value);
962  			if (list) {
963  				char **array;
964  				int i = 0;
965  	
966  				array = g_new (char *, g_slist_length (list));
967  				for (iter = list; iter; iter = iter->next)
968  					array[i++] = iter->data;
969  	
970  				nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, i);
971  				g_free (array);
972  			}
973  		} else if (type == DBUS_TYPE_G_MAP_OF_STRING) {
974  			write_hash_of_string (info->keyfile, setting, key, value);
975  		} else if (type == DBUS_TYPE_G_UINT_ARRAY) {
976  			if (!write_array_of_uint (info->keyfile, setting, key, value)) {
977  				g_warning ("Unhandled setting property type (write) '%s/%s' : '%s'", 
978  						 setting_name, key, g_type_name (type));
979  			}
980  		} else {
981  			g_warning ("Unhandled setting property type (write) '%s/%s' : '%s'", 
982  					 setting_name, key, g_type_name (type));
983  		}
984  	}
985  	
986  	static char *
987  	_writer_id_to_filename (const char *id)
988  	{
989  		char *filename, *f;
990  		const char *i = id;
991  	
992  		f = filename = g_malloc0 (strlen (id) + 1);
993  	
994  		/* Convert '/' to '*' */
995  		while (*i) {
996  			if (*i == '/')
997  				*f++ = '*';
998  			else
999  				*f++ = *i;
1000 			i++;
1001 		}
1002 	
1003 		return filename;
1004 	}
1005 	
1006 	static gboolean
1007 	_internal_write_connection (NMConnection *connection,
1008 	                            const char *keyfile_dir,
1009 	                            uid_t owner_uid,
1010 	                            pid_t owner_grp,
1011 	                            const char *existing_path,
1012 	                            char **out_path,
1013 	                            GError **error)
1014 	{
1015 		GKeyFile *key_file;
1016 		char *data;
1017 		gsize len;
1018 		gboolean success = FALSE;
1019 		char *filename = NULL, *path;
1020 		const char *id;
1021 		WriteInfo info;
1022 		GError *local_err = NULL;
1023 	
1024 		if (out_path)
1025 			g_return_val_if_fail (*out_path == NULL, FALSE);
1026 	
1027 		id = nm_connection_get_id (connection);
1028 		if (!id) {
1029 			g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
1030 			             "%s.%d: connection had no ID", __FILE__, __LINE__);
1031 			return FALSE;
1032 		}
1033 	
1034 		info.keyfile = key_file = g_key_file_new ();
1035 		info.keyfile_dir = keyfile_dir;
1036 		info.uuid = nm_connection_get_uuid (connection);
1037 		g_assert (info.uuid);
1038 		nm_connection_for_each_setting_value (connection, write_setting_value, &info);
1039 		data = g_key_file_to_data (key_file, &len, error);
1040 		if (!data)
1041 			goto out;
1042 	
1043 		/* If we have existing file path, use it. Else generate one from
1044 		 * connection's ID.
1045 		 */
1046 		if (existing_path != NULL) {
1047 			path = g_strdup (existing_path);
1048 		} else {
1049 			filename = _writer_id_to_filename (id);
1050 			path = g_build_filename (keyfile_dir, filename, NULL);
1051 		}
1052 	
1053 		/* If a file with this path already exists (but isn't the existing path
1054 		 * of the connection) then we need another name.  Multiple connections
1055 		 * can have the same ID (ie if two connections with the same ID are visible
1056 		 * to different users) but of course can't have the same path.  Yeah,
1057 		 * there's a race here, but there's not a lot we can do about it, and
1058 		 * we shouldn't get more than one connection with the same UUID either.
1059 		 */
1060 		if (g_file_test (path, G_FILE_TEST_EXISTS) && (g_strcmp0 (path, existing_path) != 0)) {
1061 			/* A keyfile with this connection's ID already exists. Pick another name. */
1062 			g_free (path);
1063 	
1064 			path = g_strdup_printf ("%s/%s-%s", keyfile_dir, filename, nm_connection_get_uuid (connection));
1065 			if (g_file_test (path, G_FILE_TEST_EXISTS)) {
1066 				if (existing_path == NULL || g_strcmp0 (path, existing_path) != 0) {
1067 					/* This should not happen. But, it actually occurs when
1068 					 * two connections have the same UUID, and one of the connections
1069 					 * is edited to contain the same ID as the other one.
1070 					 * Give up.
1071 					 */
1072 					g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
1073 					                    "%s.%d: could not find suitable keyfile file name (%s already used)",
1074 					                    __FILE__, __LINE__, path);
1075 					g_free (path);
1076 					goto out;
1077 				}
1078 			}
1079 		}
1080 	
1081 		/* In case of updating the connection and changing the file path,
1082 		 * we need to remove the old one, not to end up with two connections.
1083 		 */
1084 		if (existing_path != NULL && strcmp (path, existing_path) != 0)
1085 			unlink (existing_path);
1086 	
1087 		g_file_set_contents (path, data, len, &local_err);
1088 		if (local_err) {
1089 			g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
1090 			             "%s.%d: error writing to file '%s': %s", __FILE__, __LINE__,
1091 			             path, local_err->message);
1092 			g_error_free (local_err);
1093 			g_free (path);
1094 			goto out;
1095 		}
1096 	
1097 		if (chown (path, owner_uid, owner_grp) < 0) {
1098 			g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
1099 			             "%s.%d: error chowning '%s': %d", __FILE__, __LINE__,
1100 			             path, errno);
1101 			unlink (path);
1102 		} else {
1103 			if (chmod (path, S_IRUSR | S_IWUSR) < 0) {
1104 				g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
1105 				             "%s.%d: error setting permissions on '%s': %d", __FILE__,
1106 				             __LINE__, path, errno);
1107 				unlink (path);
1108 			} else {
1109 				if (out_path && g_strcmp0 (existing_path, path)) {
1110 					*out_path = path;  /* pass path out to caller */
1111 					path = NULL;
1112 				}
1113 				success = TRUE;
1114 			}
1115 		}
1116 		g_free (path);
1117 	
1118 	out:
1119 		g_free (filename);
1120 		g_free (data);
1121 		g_key_file_free (key_file);
1122 		return success;
1123 	}
1124 	
1125 	gboolean
1126 	nm_keyfile_plugin_write_connection (NMConnection *connection,
1127 	                                    const char *existing_path,
1128 	                                    char **out_path,
1129 	                                    GError **error)
1130 	{
1131 		return _internal_write_connection (connection,
1132 		                                   KEYFILE_DIR,
1133 		                                   0, 0,
1134 		                                   existing_path,
1135 		                                   out_path,
1136 		                                   error);
1137 	}
1138 	
1139 	gboolean
1140 	nm_keyfile_plugin_write_test_connection (NMConnection *connection,
1141 	                                         const char *keyfile_dir,
1142 	                                         uid_t owner_uid,
1143 	                                         pid_t owner_grp,
1144 	                                         char **out_path,
1145 	                                         GError **error)
1146 	{
1147 		return _internal_write_connection (connection,
1148 		                                   keyfile_dir,
1149 		                                   owner_uid, owner_grp,
1150 		                                   NULL,
1151 		                                   out_path,
1152 		                                   error);
1153 	}
1154 	
1155