kernel-netlink: Check return value of both halfs when installing default route in...
[strongswan.git] / src / pool / pool_attributes.c
1 /*
2 * Copyright (C) 2009-2010 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #define _GNU_SOURCE
17 #include <string.h>
18
19 #include <library.h>
20 #include <networking/host.h>
21
22 #include "pool_attributes.h"
23 #include "pool_usage.h"
24
25 /**
26 * global database handle
27 */
28 extern database_t *db;
29
30 #define UNITY_NETWORK_LEN 14
31
32 ENUM(value_type_names, VALUE_HEX, VALUE_SUBNET,
33 "hex",
34 "string",
35 "addr",
36 "subnet"
37 );
38
39 typedef struct attr_info_t attr_info_t;
40
41 struct attr_info_t {
42 char* keyword;
43 value_type_t value_type;
44 configuration_attribute_type_t type;
45 configuration_attribute_type_t type_ip6;
46 };
47
48 static const attr_info_t attr_info[] = {
49 { "internal_ip4_netmask", VALUE_ADDR, INTERNAL_IP4_NETMASK, 0 },
50 { "internal_ip6_netmask", VALUE_ADDR, INTERNAL_IP6_NETMASK, 0 },
51 { "netmask", VALUE_ADDR, INTERNAL_IP4_NETMASK,
52 INTERNAL_IP6_NETMASK },
53 { "internal_ip4_dns", VALUE_ADDR, INTERNAL_IP4_DNS, 0 },
54 { "internal_ip6_dns", VALUE_ADDR, INTERNAL_IP6_DNS, 0 },
55 { "dns", VALUE_ADDR, INTERNAL_IP4_DNS,
56 INTERNAL_IP6_DNS },
57 { "internal_ip4_nbns", VALUE_ADDR, INTERNAL_IP4_NBNS, 0 },
58 { "internal_ip6_nbns", VALUE_ADDR, INTERNAL_IP6_NBNS, 0 },
59 { "nbns", VALUE_ADDR, INTERNAL_IP4_NBNS,
60 INTERNAL_IP6_NBNS },
61 { "wins", VALUE_ADDR, INTERNAL_IP4_NBNS,
62 INTERNAL_IP6_NBNS },
63 { "internal_ip4_dhcp", VALUE_ADDR, INTERNAL_IP4_DHCP, 0 },
64 { "internal_ip6_dhcp", VALUE_ADDR, INTERNAL_IP6_DHCP, 0 },
65 { "dhcp", VALUE_ADDR, INTERNAL_IP4_DHCP,
66 INTERNAL_IP6_DHCP },
67 { "internal_ip4_server", VALUE_ADDR, INTERNAL_IP4_SERVER, 0 },
68 { "internal_ip6_server", VALUE_ADDR, INTERNAL_IP6_SERVER, 0 },
69 { "server", VALUE_ADDR, INTERNAL_IP4_SERVER,
70 INTERNAL_IP6_SERVER },
71 { "application_version", VALUE_STRING, APPLICATION_VERSION, 0 },
72 { "version", VALUE_STRING, APPLICATION_VERSION, 0 },
73 { "unity_banner", VALUE_STRING, UNITY_BANNER, 0 },
74 { "banner", VALUE_STRING, UNITY_BANNER, 0 },
75 { "unity_def_domain", VALUE_STRING, UNITY_DEF_DOMAIN, 0 },
76 { "unity_splitdns_name", VALUE_STRING, UNITY_SPLITDNS_NAME, 0 },
77 { "unity_split_include", VALUE_SUBNET, UNITY_SPLIT_INCLUDE, 0 },
78 { "unity_split_exclude", VALUE_SUBNET, UNITY_LOCAL_LAN, 0 },
79 { "unity_local_lan", VALUE_SUBNET, UNITY_LOCAL_LAN, 0 },
80 };
81
82 /**
83 * Determine the type of the attribute and its value
84 */
85 static bool parse_attributes(char *name, char *value, value_type_t *value_type,
86 configuration_attribute_type_t *type,
87 configuration_attribute_type_t *type_ip6,
88 chunk_t *blob)
89 {
90 host_t *addr = NULL, *mask = NULL;
91 chunk_t addr_chunk, mask_chunk, blob_next;
92 char *text = "", *pos_addr, *pos_mask, *pos_next, *endptr;
93 int i;
94
95 switch (*value_type)
96 {
97 case VALUE_STRING:
98 *blob = chunk_create(value, strlen(value));
99 *blob = chunk_clone(*blob);
100 break;
101 case VALUE_HEX:
102 *blob = chunk_from_hex(chunk_create(value, strlen(value)), NULL);
103 break;
104 case VALUE_ADDR:
105 addr = host_create_from_string(value, 0);
106 if (addr == NULL)
107 {
108 fprintf(stderr, "invalid IP address: '%s'.\n", value);
109 return FALSE;
110 }
111 addr_chunk = addr->get_address(addr);
112 *blob = chunk_clone(addr_chunk);
113 break;
114 case VALUE_SUBNET:
115 *blob = chunk_empty;
116 pos_next = value;
117
118 do
119 {
120 pos_addr = pos_next;
121 pos_next = strchr(pos_next, ',');
122 if (pos_next)
123 {
124 *pos_next = '\0';
125 pos_next += 1;
126 }
127 pos_mask = strchr(pos_addr, '/');
128 if (pos_mask == NULL)
129 {
130 fprintf(stderr, "invalid IPv4 subnet: '%s'.\n", pos_addr);
131 free(blob->ptr);
132 return FALSE;
133 }
134 *pos_mask = '\0';
135 pos_mask += 1;
136 addr = host_create_from_string(pos_addr, 0);
137 mask = host_create_from_string(pos_mask, 0);
138 if (addr == NULL || addr->get_family(addr) != AF_INET ||
139 mask == NULL || mask->get_family(addr) != AF_INET)
140 {
141 fprintf(stderr, "invalid IPv4 subnet: '%s/%s'.\n",
142 pos_addr, pos_mask);
143 DESTROY_IF(addr);
144 DESTROY_IF(mask);
145 free(blob->ptr);
146 return FALSE;
147 }
148 addr_chunk = addr->get_address(addr);
149 mask_chunk = mask->get_address(mask);
150 blob_next = chunk_alloc(blob->len + UNITY_NETWORK_LEN);
151 memcpy(blob_next.ptr, blob->ptr, blob->len);
152 pos_addr = blob_next.ptr + blob->len;
153 memset(pos_addr, 0x00, UNITY_NETWORK_LEN);
154 memcpy(pos_addr, addr_chunk.ptr, 4);
155 memcpy(pos_addr + 4, mask_chunk.ptr, 4);
156 addr->destroy(addr);
157 addr = NULL;
158 mask->destroy(mask);
159 chunk_free(blob);
160 *blob = blob_next;
161 }
162 while (pos_next);
163 break;
164 case VALUE_NONE:
165 *blob = chunk_empty;
166 break;
167 }
168
169 /* init the attribute type */
170 *type = 0;
171 *type_ip6 = 0;
172
173 for (i = 0; i < countof(attr_info); i++)
174 {
175 if (strcaseeq(name, attr_info[i].keyword))
176 {
177 *type = attr_info[i].type;
178 *type_ip6 = attr_info[i].type_ip6;
179
180 if (*value_type == VALUE_NONE)
181 {
182 *value_type = attr_info[i].value_type;
183 return TRUE;
184 }
185
186 if (*value_type != attr_info[i].value_type &&
187 *value_type != VALUE_HEX)
188 {
189 switch (attr_info[i].value_type)
190 {
191 case VALUE_STRING:
192 text = "a string";
193 break;
194 case VALUE_HEX:
195 text = "a hex";
196 break;
197 case VALUE_ADDR:
198 text = "an IP address";
199 break;
200 case VALUE_SUBNET:
201 text = "a subnet";
202 break;
203 case VALUE_NONE:
204 text = "no";
205 break;
206 }
207 fprintf(stderr, "the %s attribute requires %s value.\n",
208 name, text);
209 DESTROY_IF(addr);
210 free(blob->ptr);
211 return FALSE;
212 }
213
214 if (*value_type == VALUE_ADDR)
215 {
216 *type = (addr->get_family(addr) == AF_INET) ?
217 attr_info[i].type : attr_info[i].type_ip6;
218 addr->destroy(addr);
219 }
220 else if (*value_type == VALUE_HEX)
221 {
222 *value_type = attr_info[i].value_type;
223
224 if (*value_type == VALUE_ADDR)
225 {
226 if (blob->len == 16)
227 {
228 *type = attr_info[i].type_ip6;
229 }
230 else if (blob->len != 4)
231 {
232 fprintf(stderr, "the %s attribute requires "
233 "a valid IP address.\n", name);
234 free(blob->ptr);
235 return FALSE;
236 }
237 }
238 }
239 return TRUE;
240 }
241 }
242
243 /* clean up */
244 DESTROY_IF(addr);
245
246 /* is the attribute type numeric? */
247 *type = strtol(name, &endptr, 10);
248
249 if (*endptr != '\0')
250 {
251 fprintf(stderr, "the %s attribute is not recognized.\n", name);
252 free(blob->ptr);
253 return FALSE;
254 }
255 if (*type < 1 || *type > 32767)
256 {
257 fprintf(stderr, "the attribute type must lie in the range 1..32767.\n");
258 free(blob->ptr);
259 return FALSE;
260 }
261 if (*value_type == VALUE_NONE)
262 {
263 *value_type = VALUE_HEX;
264 }
265 return TRUE;
266 }
267
268 /**
269 * Lookup/insert an attribute pool by name
270 */
271 static u_int get_attr_pool(char *name)
272 {
273 enumerator_t *e;
274 u_int row = 0;
275
276 /* look for an existing attribute pool in the table */
277 e = db->query(db, "SELECT id FROM attribute_pools WHERE name = ?",
278 DB_TEXT, name, DB_UINT);
279 if (e && e->enumerate(e, &row))
280 {
281 e->destroy(e);
282 return row;
283 }
284 DESTROY_IF(e);
285 /* not found, insert new one */
286 if (db->execute(db, &row, "INSERT INTO attribute_pools (name) VALUES (?)",
287 DB_TEXT, name) != 1)
288 {
289 fprintf(stderr, "creating attribute pool '%s' failed.\n", name);
290 return 0;
291 }
292 return row;
293 }
294
295 /**
296 * Lookup/insert an identity
297 */
298 u_int get_identity(identification_t *id)
299 {
300 enumerator_t *e;
301 u_int row;
302
303 /* look for peer identity in the identities table */
304 e = db->query(db, "SELECT id FROM identities WHERE type = ? AND data = ?",
305 DB_INT, id->get_type(id), DB_BLOB, id->get_encoding(id), DB_UINT);
306 if (e && e->enumerate(e, &row))
307 {
308 e->destroy(e);
309 return row;
310 }
311 DESTROY_IF(e);
312 /* not found, insert new one */
313 if (db->execute(db, &row, "INSERT INTO identities (type,data) VALUES (?,?)",
314 DB_INT, id->get_type(id), DB_BLOB, id->get_encoding(id)) != 1)
315 {
316 fprintf(stderr, "creating id '%Y' failed.\n", id);
317 return 0;
318 }
319 return row;
320 }
321
322 /**
323 * ipsec pool --addattr <type> - add attribute entry
324 */
325 void add_attr(char *name, char *pool, char *identity,
326 char *value, value_type_t value_type)
327 {
328 configuration_attribute_type_t type, type_ip6;
329 u_int pool_id = 0, identity_id = 0;
330 char id_pool_str[128] = "";
331 chunk_t blob;
332 bool success;
333
334 if (pool)
335 {
336 pool_id = get_attr_pool(pool);
337 if (pool_id == 0)
338 {
339 exit(EXIT_FAILURE);
340 }
341
342 if (identity)
343 {
344 identification_t *id;
345
346 id = identification_create_from_string(identity);
347 identity_id = get_identity(id);
348 id->destroy(id);
349 if (identity_id == 0)
350 {
351 exit(EXIT_FAILURE);
352 }
353 snprintf(id_pool_str, sizeof(id_pool_str),
354 " for '%s' in pool '%s'", identity, pool);
355 }
356 else
357 {
358 snprintf(id_pool_str, sizeof(id_pool_str), " in pool '%s'", pool);
359 }
360 }
361
362 if (value_type == VALUE_NONE)
363 {
364 fprintf(stderr, "the value of the %s attribute is missing.\n", name);
365 usage();
366 }
367 if (!parse_attributes(name, value, &value_type, &type, &type_ip6, &blob))
368 {
369 exit(EXIT_FAILURE);
370 }
371
372 success = db->execute(db, NULL,
373 "INSERT INTO attributes (identity, pool, type, value) "
374 "VALUES (?, ?, ?, ?)", DB_UINT, identity_id, DB_UINT, pool_id,
375 DB_INT, type, DB_BLOB, blob) == 1;
376 free(blob.ptr);
377
378 if (success)
379 {
380 printf("added %s attribute (%N)%s.\n", name,
381 configuration_attribute_type_names, type, id_pool_str);
382 }
383 else
384 {
385 fprintf(stderr, "adding %s attribute (%N)%s failed.\n", name,
386 configuration_attribute_type_names, type, id_pool_str);
387 }
388 }
389
390 /**
391 * ipsec pool --delattr <type> - delete attribute entry
392 */
393 void del_attr(char *name, char *pool, char *identity,
394 char *value, value_type_t value_type)
395 {
396 configuration_attribute_type_t type, type_ip6, type_db;
397 u_int pool_id = 0, identity_id = 0;
398 char id_pool_str[128] = "";
399 chunk_t blob, blob_db;
400 u_int id;
401 enumerator_t *query;
402 bool found = FALSE;
403
404 if (pool)
405 {
406 pool_id = get_attr_pool(pool);
407 if (pool_id == 0)
408 {
409 exit(EXIT_FAILURE);
410 }
411
412 if (identity)
413 {
414 identification_t *id;
415
416 id = identification_create_from_string(identity);
417 identity_id = get_identity(id);
418 id->destroy(id);
419 if (identity_id == 0)
420 {
421 exit(EXIT_FAILURE);
422 }
423 snprintf(id_pool_str, sizeof(id_pool_str),
424 " for '%s' in pool '%s'", identity, pool);
425 }
426 else
427 {
428 snprintf(id_pool_str, sizeof(id_pool_str), " in pool '%s'", pool);
429 }
430 }
431
432 if (!parse_attributes(name, value, &value_type, &type, &type_ip6, &blob))
433 {
434 exit(EXIT_FAILURE);
435 }
436
437 if (blob.len > 0)
438 {
439 query = db->query(db,
440 "SELECT id, type, value FROM attributes "
441 "WHERE identity = ? AND pool = ? AND type = ? AND value = ?",
442 DB_UINT, identity_id, DB_UINT, pool_id, DB_INT, type,
443 DB_BLOB, blob, DB_UINT, DB_INT, DB_BLOB);
444 }
445 else if (type_ip6 == 0)
446 {
447 query = db->query(db,
448 "SELECT id, type, value FROM attributes "
449 "WHERE identity = ? AND pool = ? AND type = ?",
450 DB_UINT, identity_id, DB_UINT, pool_id, DB_INT, type,
451 DB_UINT, DB_INT, DB_BLOB);
452 }
453 else
454 {
455 query = db->query(db,
456 "SELECT id, type, value FROM attributes "
457 "WHERE identity = ? AND pool = ? AND (type = ? OR type = ?)",
458 DB_UINT, identity_id, DB_UINT, pool_id, DB_INT, type,
459 DB_INT, type_ip6, DB_UINT, DB_INT, DB_BLOB);
460 }
461
462 if (!query)
463 {
464 fprintf(stderr, "deleting '%s' attribute (%N)%s failed.\n",
465 name, configuration_attribute_type_names, type, id_pool_str);
466 free(blob.ptr);
467 exit(EXIT_FAILURE);
468 }
469
470 while (query->enumerate(query, &id, &type_db, &blob_db))
471 {
472 host_t *server = NULL;
473
474 found = TRUE;
475
476 if (value_type == VALUE_ADDR)
477 {
478 int family = (type_db == type_ip6) ? AF_INET6 : AF_INET;
479
480 server = host_create_from_chunk(family, blob_db, 0);
481 }
482
483 if (db->execute(db, NULL,
484 "DELETE FROM attributes WHERE id = ?",
485 DB_UINT, id) != 1)
486 {
487 if (server)
488 {
489 fprintf(stderr, "deleting %s server %H%s failed\n",
490 name, server, id_pool_str);
491 server->destroy(server);
492 }
493 else if (value_type == VALUE_STRING)
494 {
495 fprintf(stderr, "deleting %s attribute (%N) with value '%.*s'%s failed.\n",
496 name, configuration_attribute_type_names, type,
497 (int)blob_db.len, blob_db.ptr, id_pool_str);
498 }
499
500 else
501 {
502 fprintf(stderr, "deleting %s attribute (%N) with value %#B%s failed.\n",
503 name, configuration_attribute_type_names, type,
504 &blob_db, id_pool_str);
505 }
506 query->destroy(query);
507 free(blob.ptr);
508 exit(EXIT_FAILURE);
509 }
510 if (server)
511 {
512 printf("deleted %s server %H%s\n", name, server, id_pool_str);
513 server->destroy(server);
514 }
515 else if (value_type == VALUE_STRING)
516 {
517 printf("deleted %s attribute (%N) with value '%.*s'%s.\n",
518 name, configuration_attribute_type_names, type,
519 (int)blob_db.len, blob_db.ptr, id_pool_str);
520 }
521 else
522 {
523 printf("deleted %s attribute (%N) with value %#B%s.\n",
524 name, configuration_attribute_type_names, type,
525 &blob_db, id_pool_str);
526 }
527 }
528 query->destroy(query);
529
530 if (!found)
531 {
532 if (blob.len == 0)
533 {
534 if (type_ip6 == 0)
535 {
536 fprintf(stderr, "no %s attribute (%N) was found%s.\n", name,
537 configuration_attribute_type_names, type, id_pool_str);
538 }
539 else
540 {
541 fprintf(stderr, "no %s attribute%s was found.\n",
542 name, id_pool_str);
543 }
544 }
545 else
546 {
547 if (value_type == VALUE_ADDR)
548 {
549 host_t *server = host_create_from_chunk(AF_UNSPEC, blob, 0);
550
551 fprintf(stderr, "the %s server %H%s was not found.\n", name,
552 server, id_pool_str);
553 server->destroy(server);
554 }
555 else
556 {
557 fprintf(stderr, "the %s attribute (%N) with value '%.*s'%s "
558 "was not found.\n", name,
559 configuration_attribute_type_names, type,
560 (int)blob.len, blob.ptr, id_pool_str);
561 }
562 }
563 }
564 free(blob.ptr);
565 }
566
567 /**
568 * ipsec pool --statusattr - show all attribute entries
569 */
570 void status_attr(bool hexout)
571 {
572 configuration_attribute_type_t type;
573 value_type_t value_type;
574 chunk_t value, addr_chunk, mask_chunk, identity_chunk;
575 identification_t *identity;
576 enumerator_t *enumerator;
577 host_t *addr, *mask;
578 char type_name[30];
579 bool first = TRUE;
580 int i, identity_type;
581 char *pool_name;
582
583 /* enumerate over all attributes */
584 enumerator = db->query(db,
585 "SELECT attributes.type, attribute_pools.name, "
586 "identities.type, identities.data, attributes.value "
587 "FROM attributes "
588 "LEFT OUTER JOIN identities "
589 "ON attributes.identity = identities.id "
590 "LEFT OUTER JOIN attribute_pools "
591 "ON attributes.pool = attribute_pools.id "
592 "ORDER BY attributes.type, attribute_pools.name, "
593 "identities.type, identities.data, attributes.value",
594 DB_INT, DB_TEXT, DB_INT, DB_BLOB, DB_BLOB);
595 if (enumerator)
596 {
597 while (enumerator->enumerate(enumerator, &type,&pool_name,
598 &identity_type, &identity_chunk, &value))
599 {
600 if (first)
601 {
602 printf(" type description pool "
603 " identity value\n");
604 first = FALSE;
605 }
606 snprintf(type_name, sizeof(type_name), "%N",
607 configuration_attribute_type_names, type);
608 if (type_name[0] == '(')
609 {
610 type_name[0] = '\0';
611 }
612 printf("%5d %-20s ",type, type_name);
613
614 printf(" %-10s ", (pool_name ? pool_name : ""));
615
616 if (identity_type)
617 {
618 identity = identification_create_from_encoding(identity_type, identity_chunk);
619 printf(" %-20.20Y ", identity);
620 identity->destroy(identity);
621 }
622 else
623 {
624 printf(" ");
625 }
626
627 value_type = VALUE_HEX;
628 if (!hexout)
629 {
630 for (i = 0; i < countof(attr_info); i++)
631 {
632 if (type == attr_info[i].type)
633 {
634 value_type = attr_info[i].value_type;
635 break;
636 }
637 }
638 }
639 switch (value_type)
640 {
641 case VALUE_ADDR:
642 addr = host_create_from_chunk(AF_UNSPEC, value, 0);
643 if (addr)
644 {
645 printf(" %H\n", addr);
646 addr->destroy(addr);
647 }
648 else
649 {
650 /* value cannot be represented as an IP address */
651 printf(" %#B\n", &value);
652 }
653 break;
654 case VALUE_SUBNET:
655 if (value.len % UNITY_NETWORK_LEN == 0)
656 {
657 for (i = 0; i < value.len / UNITY_NETWORK_LEN; i++)
658 {
659 addr_chunk = chunk_create(value.ptr + i*UNITY_NETWORK_LEN, 4);
660 addr = host_create_from_chunk(AF_INET, addr_chunk, 0);
661 mask_chunk = chunk_create(addr_chunk.ptr + 4, 4);
662 mask = host_create_from_chunk(AF_INET, mask_chunk, 0);
663 printf("%s%H/%H", (i > 0) ? "," : " ", addr, mask);
664 addr->destroy(addr);
665 mask->destroy(mask);
666 }
667 printf("\n");
668 }
669 else
670 {
671 /* value cannot be represented as a list of subnets */
672 printf(" %#B\n", &value);
673 }
674 break;
675 case VALUE_STRING:
676 printf("\"%.*s\"\n", (int)value.len, value.ptr);
677 break;
678 case VALUE_HEX:
679 default:
680 printf(" %#B\n", &value);
681 }
682 }
683 enumerator->destroy(enumerator);
684 }
685 }
686
687 /**
688 * ipsec pool --showattr - show all supported attribute keywords
689 */
690 void show_attr(void)
691 {
692 int i;
693
694 for (i = 0; i < countof(attr_info); i++)
695 {
696 char value_name[10];
697
698
699 snprintf(value_name, sizeof(value_name), "%N",
700 value_type_names, attr_info[i].value_type);
701
702 printf("%-20s --%-6s (%N",
703 attr_info[i].keyword, value_name,
704 configuration_attribute_type_names, attr_info[i].type);
705
706 if (attr_info[i].type_ip6)
707 {
708 printf(", %N)\n",
709 configuration_attribute_type_names, attr_info[i].type_ip6);
710 }
711 else
712 {
713 printf(")\n");
714 }
715 }
716 }