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";
(1) Event assign_zero: |
Assigning: "objtype" = "NULL". |
Also see events: |
[var_deref_op] |
706 const ObjectType *objtype = NULL;
707 int i;
708
(2) Event cond_true: |
Condition "i < 10UL /* sizeof (objtypes) / sizeof (objtypes[0]) */", taking true branch |
(3) Event cond_true: |
Condition "objtypes[i].key", taking true branch |
(7) Event loop_begin: |
Jumped back to beginning of loop |
(8) Event cond_true: |
Condition "i < 10UL /* sizeof (objtypes) / sizeof (objtypes[0]) */", taking true branch |
(9) Event cond_true: |
Condition "objtypes[i].key", taking true branch |
(13) Event loop_begin: |
Jumped back to beginning of loop |
(14) Event cond_true: |
Condition "i < 10UL /* sizeof (objtypes) / sizeof (objtypes[0]) */", taking true branch |
(15) Event cond_false: |
Condition "objtypes[i].key", taking false branch |
709 for (i = 0; i < G_N_ELEMENTS (objtypes) && objtypes[i].key; i++) {
(4) Event cond_false: |
Condition "g_strcmp0(objtypes[i].key, key) == 0", taking false branch |
(10) Event cond_false: |
Condition "g_strcmp0(objtypes[i].key, key) == 0", taking false branch |
710 if (g_strcmp0 (objtypes[i].key, key) == 0) {
711 objtype = &objtypes[i];
712 break;
(5) Event if_end: |
End of if statement |
(11) Event if_end: |
End of if statement |
713 }
(6) Event loop: |
Jumping back to the beginning of the loop |
(12) Event loop: |
Jumping back to the beginning of the loop |
(16) Event loop_end: |
Reached end of loop |
714 }
(17) Event cond_false: |
Condition "objtype != NULL", taking false branch |
(18) Event else_branch: |
Reached else branch |
(19) Event cond_true: |
Condition "({...})", taking true branch |
(20) Event if_fallthrough: |
Falling through to end of if statement |
(21) Event if_end: |
End of if statement |
715 g_return_if_fail (objtype != NULL);
716
(22) Event var_deref_op: |
Dereferencing null pointer "objtype". |
Also see events: |
[assign_zero] |
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 */
881 if (!strcmp (key, NM_SETTING_NAME))
882 return;
883
884 /* Don't write the NMSettingConnection object's 'read-only' property */
885 if ( NM_IS_SETTING_CONNECTION (setting)
886 && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY))
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);
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 }
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 */
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