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 (¤t, &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 (¤t, &error, DIGITS, DELIMITERS);
325 /* get gateway field */
326 gateway_str = read_field (¤t, &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 (¤t, &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