Annotation of embedaddon/strongswan/src/starter/confread.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2014 Tobias Brunner
3: * HSR Hochschule fuer Technik Rapperswil
4: *
5: * Copyright (C) 2001-2002 Mathieu Lafon - Arkoon Network Security
6: *
7: * This program is free software; you can redistribute it and/or modify it
8: * under the terms of the GNU General Public License as published by the
9: * Free Software Foundation; either version 2 of the License, or (at your
10: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11: *
12: * This program is distributed in the hope that it will be useful, but
13: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15: * for more details.
16: */
17:
18: #include <sys/types.h>
19: #include <sys/stat.h>
20: #include <unistd.h>
21: #include <stddef.h>
22: #include <stdlib.h>
23: #include <string.h>
24: #include <assert.h>
25: #include <netdb.h>
26:
27: #include <library.h>
28: #include <utils/debug.h>
29:
30: #include "keywords.h"
31: #include "confread.h"
32: #include "args.h"
33: #include "files.h"
34: #include "parser/conf_parser.h"
35:
36: #define IKE_LIFETIME_DEFAULT 10800 /* 3 hours */
37: #define IPSEC_LIFETIME_DEFAULT 3600 /* 1 hour */
38: #define SA_REPLACEMENT_MARGIN_DEFAULT 540 /* 9 minutes */
39: #define SA_REPLACEMENT_FUZZ_DEFAULT 100 /* 100% of margin */
40: #define SA_REPLACEMENT_RETRIES_DEFAULT 3
41: #define SA_REPLAY_WINDOW_DEFAULT -1 /* use charon.replay_window */
42:
43: static const char firewall_defaults[] = IPSEC_SCRIPT " _updown iptables";
44:
45: /**
46: * Process deprecated keywords
47: */
48: static bool is_deprecated(kw_token_t token, char *name, char *conn)
49: {
50: switch (token)
51: {
52: case KW_SETUP_DEPRECATED:
53: case KW_PKCS11_DEPRECATED:
54: DBG1(DBG_APP, "# deprecated keyword '%s' in config setup", name);
55: break;
56: case KW_CONN_DEPRECATED:
57: case KW_END_DEPRECATED:
58: case KW_PFS_DEPRECATED:
59: DBG1(DBG_APP, "# deprecated keyword '%s' in conn '%s'", name, conn);
60: break;
61: case KW_CA_DEPRECATED:
62: DBG1(DBG_APP, "# deprecated keyword '%s' in ca '%s'", name, conn);
63: break;
64: default:
65: return FALSE;
66: }
67: /* additional messages for some */
68: switch (token)
69: {
70: case KW_PKCS11_DEPRECATED:
71: DBG1(DBG_APP, " use the 'pkcs11' plugin instead");
72: break;
73: case KW_PFS_DEPRECATED:
74: DBG1(DBG_APP, " PFS is enabled by specifying a DH group in the "
75: "'esp' cipher suite");
76: break;
77: default:
78: break;
79: }
80: return TRUE;
81: }
82:
83: /*
84: * parse config setup section
85: */
86: static void load_setup(starter_config_t *cfg, conf_parser_t *parser)
87: {
88: enumerator_t *enumerator;
89: dictionary_t *dict;
90: const kw_entry_t *entry;
91: char *key, *value;
92:
93: DBG2(DBG_APP, "Loading config setup");
94: dict = parser->get_section(parser, CONF_PARSER_CONFIG_SETUP, NULL);
95: if (!dict)
96: {
97: return;
98: }
99: enumerator = dict->create_enumerator(dict);
100: while (enumerator->enumerate(enumerator, &key, &value))
101: {
102: bool assigned = FALSE;
103:
104: entry = in_word_set(key, strlen(key));
105: if (!entry)
106: {
107: DBG1(DBG_APP, "# unknown keyword '%s'", key);
108: cfg->non_fatal_err++;
109: continue;
110: }
111: if ((int)entry->token < KW_SETUP_FIRST || entry->token > KW_SETUP_LAST)
112: {
113: DBG1(DBG_APP, "# unsupported keyword '%s' in config setup", key);
114: cfg->err++;
115: continue;
116: }
117: if (is_deprecated(entry->token, key, ""))
118: {
119: cfg->non_fatal_err++;
120: continue;
121: }
122: if (!assign_arg(entry->token, KW_SETUP_FIRST, key, value, cfg,
123: &assigned))
124: {
125: DBG1(DBG_APP, " bad argument value in config setup");
126: cfg->err++;
127: }
128: }
129: enumerator->destroy(enumerator);
130: dict->destroy(dict);
131: }
132:
133: /*
134: * parse a ca section
135: */
136: static void load_ca(starter_ca_t *ca, starter_config_t *cfg,
137: conf_parser_t *parser)
138: {
139: enumerator_t *enumerator;
140: dictionary_t *dict;
141: const kw_entry_t *entry;
142: kw_token_t token;
143: char *key, *value;
144:
145: DBG2(DBG_APP, "Loading ca '%s'", ca->name);
146: dict = parser->get_section(parser, CONF_PARSER_CA, ca->name);
147: if (!dict)
148: {
149: return;
150: }
151: enumerator = dict->create_enumerator(dict);
152: while (enumerator->enumerate(enumerator, &key, &value))
153: {
154: bool assigned = FALSE;
155:
156: entry = in_word_set(key, strlen(key));
157: if (!entry)
158: {
159: DBG1(DBG_APP, "# unknown keyword '%s'", key);
160: cfg->non_fatal_err++;
161: continue;
162: }
163: token = entry->token;
164: if (token == KW_AUTO)
165: {
166: token = KW_CA_SETUP;
167: }
168: if (token < KW_CA_FIRST || token > KW_CA_LAST)
169: {
170: DBG1(DBG_APP, "# unsupported keyword '%s' in ca '%s'",
171: key, ca->name);
172: cfg->err++;
173: continue;
174: }
175: if (is_deprecated(token, key, ca->name))
176: {
177: cfg->non_fatal_err++;
178: continue;
179: }
180: if (!assign_arg(token, KW_CA_FIRST, key, value, ca, &assigned))
181: {
182: DBG1(DBG_APP, " bad argument value in ca '%s'", ca->name);
183: cfg->err++;
184: }
185: }
186: enumerator->destroy(enumerator);
187: dict->destroy(dict);
188:
189: /* treat 'route' and 'start' as 'add' */
190: if (ca->startup != STARTUP_NO)
191: {
192: ca->startup = STARTUP_ADD;
193: }
194: }
195:
196: /*
197: * set some default values
198: */
199: static void conn_defaults(starter_conn_t *conn)
200: {
201: conn->startup = STARTUP_NO;
202: conn->state = STATE_IGNORE;
203: conn->mode = MODE_TUNNEL;
204: conn->options = SA_OPTION_MOBIKE;
205:
206: /* esp defaults are set after parsing the conn section */
207: conn->sa_ike_life_seconds = IKE_LIFETIME_DEFAULT;
208: conn->sa_ipsec_life_seconds = IPSEC_LIFETIME_DEFAULT;
209: conn->sa_rekey_margin = SA_REPLACEMENT_MARGIN_DEFAULT;
210: conn->sa_rekey_fuzz = SA_REPLACEMENT_FUZZ_DEFAULT;
211: conn->sa_keying_tries = SA_REPLACEMENT_RETRIES_DEFAULT;
212: conn->install_policy = TRUE;
213: conn->dpd_delay = 30; /* seconds */
214: conn->dpd_timeout = 150; /* seconds */
215: conn->replay_window = SA_REPLAY_WINDOW_DEFAULT;
216: conn->fragmentation = FRAGMENTATION_YES;
217:
218: conn->left.sendcert = CERT_SEND_IF_ASKED;
219: conn->right.sendcert = CERT_SEND_IF_ASKED;
220:
221: conn->left.ikeport = 500;
222: conn->right.ikeport = 500;
223:
224: conn->left.to_port = 0xffff;
225: conn->right.to_port = 0xffff;
226: }
227:
228: /*
229: * parse left|right specific options
230: */
231: static void kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token,
232: char *key, char *value, starter_config_t *cfg)
233: {
234: bool assigned = FALSE;
235:
236: if (is_deprecated(token, key, conn->name))
237: {
238: cfg->non_fatal_err++;
239: return;
240: }
241:
242: if (!assign_arg(token, KW_END_FIRST, key, value, end, &assigned))
243: {
244: goto err;
245: }
246:
247: /* post processing of some keywords that were assigned automatically */
248: switch (token)
249: {
250: case KW_HOST:
251: if (value && strlen(value) > 0 && value[0] == '%')
252: {
253: if (streq(value, "%defaultroute"))
254: {
255: value = "%any";
256: }
257: if (!streq(value, "%any") && !streq(value, "%any4") &&
258: !streq(value, "%any6"))
259: { /* allow_any prefix */
260: end->allow_any = TRUE;
261: value++;
262: }
263: }
264: free(end->host);
265: end->host = strdupnull(value);
266: break;
267: case KW_SOURCEIP:
268: conn->mode = MODE_TUNNEL;
269: conn->proxy_mode = FALSE;
270: break;
271: case KW_SENDCERT:
272: if (end->sendcert == CERT_YES_SEND)
273: {
274: end->sendcert = CERT_ALWAYS_SEND;
275: }
276: else if (end->sendcert == CERT_NO_SEND)
277: {
278: end->sendcert = CERT_NEVER_SEND;
279: }
280: break;
281: default:
282: break;
283: }
284:
285: if (assigned)
286: {
287: return;
288: }
289:
290: /* individual processing of keywords that were not assigned automatically */
291: switch (token)
292: {
293: case KW_PROTOPORT:
294: {
295: struct protoent *proto;
296: struct servent *svc;
297: char *sep, *port = "", *endptr;
298: long int p;
299:
300: sep = strchr(value, '/');
301: if (sep)
302: { /* protocol/port */
303: *sep = '\0';
304: port = sep + 1;
305: }
306:
307: if (streq(value, "%any"))
308: {
309: end->protocol = 0;
310: }
311: else
312: {
313: proto = getprotobyname(value);
314: if (proto)
315: {
316: end->protocol = proto->p_proto;
317: }
318: else
319: {
320: p = strtol(value, &endptr, 0);
321: if ((*value && *endptr) || p < 0 || p > 0xff)
322: {
323: DBG1(DBG_APP, "# bad protocol: %s=%s", key, value);
324: goto err;
325: }
326: end->protocol = (uint8_t)p;
327: }
328: }
329: if (streq(port, "%any"))
330: {
331: end->from_port = 0;
332: end->to_port = 0xffff;
333: }
334: else if (streq(port, "%opaque"))
335: {
336: end->from_port = 0xffff;
337: end->to_port = 0;
338: }
339: else if (*port)
340: {
341: svc = getservbyname(port, NULL);
342: if (svc)
343: {
344: end->from_port = end->to_port = ntohs(svc->s_port);
345: }
346: else
347: {
348: p = strtol(port, &endptr, 0);
349: if (p < 0 || p > 0xffff)
350: {
351: DBG1(DBG_APP, "# bad port: %s=%s", key, port);
352: goto err;
353: }
354: end->from_port = p;
355: if (*endptr == '-')
356: {
357: port = endptr + 1;
358: p = strtol(port, &endptr, 0);
359: if (p < 0 || p > 0xffff)
360: {
361: DBG1(DBG_APP, "# bad port: %s=%s", key, port);
362: goto err;
363: }
364: }
365: end->to_port = p;
366: if (*endptr)
367: {
368: DBG1(DBG_APP, "# bad port: %s=%s", key, port);
369: goto err;
370: }
371: }
372: }
373: if (sep)
374: { /* restore the original text in case also= is used */
375: *sep = '/';
376: }
377: break;
378: }
379: default:
380: break;
381: }
382: return;
383:
384: err:
385: DBG1(DBG_APP, " bad argument value in conn '%s'", conn->name);
386: cfg->err++;
387: }
388:
389: /*
390: * macro to handle simple flags
391: */
392: #define KW_SA_OPTION_FLAG(sy, sn, fl) \
393: if (streq(value, sy)) { conn->options |= fl; } \
394: else if (streq(value, sn)) { conn->options &= ~fl; } \
395: else { DBG1(DBG_APP, "# bad option value: %s=%s", key, value); cfg->err++; }
396:
397: /*
398: * parse settings not handled by the simple argument parser
399: */
400: static void handle_keyword(kw_token_t token, starter_conn_t *conn, char *key,
401: char *value, starter_config_t *cfg)
402: {
403: if ((token == KW_ESP && conn->ah) || (token == KW_AH && conn->esp))
404: {
405: DBG1(DBG_APP, "# can't have both 'ah' and 'esp' options");
406: cfg->err++;
407: return;
408: }
409: switch (token)
410: {
411: case KW_TYPE:
412: {
413: conn->mode = MODE_TRANSPORT;
414: conn->proxy_mode = FALSE;
415: if (streq(value, "tunnel"))
416: {
417: conn->mode = MODE_TUNNEL;
418: }
419: else if (streq(value, "beet"))
420: {
421: conn->mode = MODE_BEET;
422: }
423: else if (streq(value, "transport_proxy"))
424: {
425: conn->mode = MODE_TRANSPORT;
426: conn->proxy_mode = TRUE;
427: }
428: else if (streq(value, "passthrough") || streq(value, "pass"))
429: {
430: conn->mode = MODE_PASS;
431: }
432: else if (streq(value, "drop") || streq(value, "reject"))
433: {
434: conn->mode = MODE_DROP;
435: }
436: else if (!streq(value, "transport"))
437: {
438: DBG1(DBG_APP, "# bad policy value: %s=%s", key, value);
439: cfg->err++;
440: }
441: break;
442: }
443: case KW_COMPRESS:
444: KW_SA_OPTION_FLAG("yes", "no", SA_OPTION_COMPRESS)
445: break;
446: case KW_MARK:
447: if (!mark_from_string(value, MARK_OP_UNIQUE, &conn->mark_in))
448: {
449: cfg->err++;
450: break;
451: }
452: conn->mark_out = conn->mark_in;
453: break;
454: case KW_MARK_IN:
455: if (!mark_from_string(value, MARK_OP_UNIQUE, &conn->mark_in))
456: {
457: cfg->err++;
458: }
459: break;
460: case KW_MARK_OUT:
461: if (!mark_from_string(value, MARK_OP_UNIQUE, &conn->mark_out))
462: {
463: cfg->err++;
464: }
465: break;
466: case KW_TFC:
467: if (streq(value, "%mtu"))
468: {
469: conn->tfc = -1;
470: }
471: else
472: {
473: char *endptr;
474:
475: conn->tfc = strtoul(value, &endptr, 10);
476: if (*endptr != '\0')
477: {
478: DBG1(DBG_APP, "# bad integer value: %s=%s", key, value);
479: cfg->err++;
480: }
481: }
482: break;
483: case KW_KEYINGTRIES:
484: if (streq(value, "%forever"))
485: {
486: conn->sa_keying_tries = 0;
487: }
488: else
489: {
490: char *endptr;
491:
492: conn->sa_keying_tries = strtoul(value, &endptr, 10);
493: if (*endptr != '\0')
494: {
495: DBG1(DBG_APP, "# bad integer value: %s=%s", key, value);
496: cfg->err++;
497: }
498: }
499: break;
500: case KW_REKEY:
501: KW_SA_OPTION_FLAG("no", "yes", SA_OPTION_DONT_REKEY)
502: break;
503: case KW_REAUTH:
504: KW_SA_OPTION_FLAG("no", "yes", SA_OPTION_DONT_REAUTH)
505: break;
506: case KW_MOBIKE:
507: KW_SA_OPTION_FLAG("yes", "no", SA_OPTION_MOBIKE)
508: break;
509: case KW_FORCEENCAPS:
510: KW_SA_OPTION_FLAG("yes", "no", SA_OPTION_FORCE_ENCAP)
511: break;
512: case KW_MODECONFIG:
513: KW_SA_OPTION_FLAG("push", "pull", SA_OPTION_MODECFG_PUSH)
514: break;
515: case KW_XAUTH:
516: KW_SA_OPTION_FLAG("server", "client", SA_OPTION_XAUTH_SERVER)
517: break;
518: default:
519: break;
520: }
521: }
522:
523: /*
524: * handles left|rightfirewall and left|rightupdown parameters
525: */
526: static void handle_firewall(const char *label, starter_end_t *end,
527: starter_config_t *cfg)
528: {
529: if (end->firewall)
530: {
531: if (end->updown != NULL)
532: {
533: DBG1(DBG_APP, "# cannot have both %sfirewall and %supdown", label,
534: label);
535: cfg->err++;
536: }
537: else
538: {
539: end->updown = strdupnull(firewall_defaults);
540: end->firewall = FALSE;
541: }
542: }
543: }
544:
545: /*
546: * parse a conn section
547: */
548: static void load_conn(starter_conn_t *conn, starter_config_t *cfg,
549: conf_parser_t *parser)
550: {
551: enumerator_t *enumerator;
552: dictionary_t *dict;
553: const kw_entry_t *entry;
554: kw_token_t token;
555: char *key, *value;
556:
557: DBG2(DBG_APP, "Loading conn '%s'", conn->name);
558: dict = parser->get_section(parser, CONF_PARSER_CONN, conn->name);
559: if (!dict)
560: {
561: return;
562: }
563: enumerator = dict->create_enumerator(dict);
564: while (enumerator->enumerate(enumerator, &key, &value))
565: {
566: bool assigned = FALSE;
567:
568: entry = in_word_set(key, strlen(key));
569: if (!entry)
570: {
571: DBG1(DBG_APP, "# unknown keyword '%s'", key);
572: cfg->non_fatal_err++;
573: continue;
574: }
575: token = entry->token;
576: if (token >= KW_LEFT_FIRST && token <= KW_LEFT_LAST)
577: {
578: kw_end(conn, &conn->left, token - KW_LEFT_FIRST + KW_END_FIRST,
579: key, value, cfg);
580: continue;
581: }
582: else if (token >= KW_RIGHT_FIRST && token <= KW_RIGHT_LAST)
583: {
584: kw_end(conn, &conn->right, token - KW_RIGHT_FIRST + KW_END_FIRST,
585: key, value, cfg);
586: continue;
587: }
588: if (token == KW_AUTO)
589: {
590: token = KW_CONN_SETUP;
591: }
592: if (token < KW_CONN_FIRST || token > KW_CONN_LAST)
593: {
594: DBG1(DBG_APP, "# unsupported keyword '%s' in conn '%s'",
595: key, conn->name);
596: cfg->err++;
597: continue;
598: }
599: if (is_deprecated(token, key, conn->name))
600: {
601: cfg->non_fatal_err++;
602: continue;
603: }
604: if (!assign_arg(token, KW_CONN_FIRST, key, value, conn,
605: &assigned))
606: {
607: DBG1(DBG_APP, " bad argument value in conn '%s'", conn->name);
608: cfg->err++;
609: continue;
610: }
611: if (!assigned)
612: {
613: handle_keyword(token, conn, key, value, cfg);
614: }
615: }
616: enumerator->destroy(enumerator);
617: dict->destroy(dict);
618:
619: handle_firewall("left", &conn->left, cfg);
620: handle_firewall("right", &conn->right, cfg);
621: }
622:
623: /*
624: * free the memory used by a starter_ca_t object
625: */
626: static void confread_free_ca(starter_ca_t *ca)
627: {
628: free_args(KW_CA_NAME, KW_CA_LAST, (char *)ca);
629: free(ca);
630: }
631:
632: /*
633: * free the memory used by a starter_conn_t object
634: */
635: static void confread_free_conn(starter_conn_t *conn)
636: {
637: free_args(KW_END_FIRST, KW_END_LAST, (char *)&conn->left);
638: free_args(KW_END_FIRST, KW_END_LAST, (char *)&conn->right);
639: free_args(KW_CONN_NAME, KW_CONN_LAST, (char *)conn);
640: free(conn);
641: }
642:
643: /*
644: * free the memory used by a starter_config_t object
645: */
646: void confread_free(starter_config_t *cfg)
647: {
648: starter_conn_t *conn = cfg->conn_first;
649: starter_ca_t *ca = cfg->ca_first;
650:
651: free_args(KW_SETUP_FIRST, KW_SETUP_LAST, (char *)cfg);
652:
653: while (conn != NULL)
654: {
655: starter_conn_t *conn_aux = conn;
656:
657: conn = conn->next;
658: confread_free_conn(conn_aux);
659: }
660:
661: while (ca != NULL)
662: {
663: starter_ca_t *ca_aux = ca;
664:
665: ca = ca->next;
666: confread_free_ca(ca_aux);
667: }
668:
669: free(cfg);
670: }
671:
672: /*
673: * load and parse an IPsec configuration file
674: */
675: starter_config_t* confread_load(const char *file)
676: {
677: conf_parser_t *parser;
678: starter_config_t *cfg = NULL;
679: enumerator_t *enumerator;
680: u_int total_err;
681: char *name;
682:
683: parser = conf_parser_create(file);
684: if (!parser->parse(parser))
685: {
686: parser->destroy(parser);
687: return NULL;
688: }
689:
690: INIT(cfg,
691: .setup = {
692: .uniqueids = TRUE,
693: }
694: );
695:
696: /* load config setup section */
697: load_setup(cfg, parser);
698:
699: /* load ca sections */
700: enumerator = parser->get_sections(parser, CONF_PARSER_CA);
701: while (enumerator->enumerate(enumerator, &name))
702: {
703: u_int previous_err = cfg->err;
704: starter_ca_t *ca;
705:
706: INIT(ca,
707: .name = strdup(name),
708: );
709: load_ca(ca, cfg, parser);
710:
711: if (cfg->err > previous_err)
712: {
713: total_err = cfg->err - previous_err;
714: DBG1(DBG_APP, "# ignored ca '%s' due to %d parsing error%s", name,
715: total_err, (total_err > 1) ? "s" : "");
716: confread_free_ca(ca);
717: cfg->non_fatal_err += cfg->err - previous_err;
718: cfg->err = previous_err;
719: }
720: else
721: {
722: if (cfg->ca_last)
723: {
724: cfg->ca_last->next = ca;
725: }
726: cfg->ca_last = ca;
727: if (!cfg->ca_first)
728: {
729: cfg->ca_first = ca;
730: }
731: if (ca->startup != STARTUP_NO)
732: {
733: ca->state = STATE_TO_ADD;
734: }
735: }
736: }
737: enumerator->destroy(enumerator);
738:
739: /* load conn sections */
740: enumerator = parser->get_sections(parser, CONF_PARSER_CONN);
741: while (enumerator->enumerate(enumerator, &name))
742: {
743: u_int previous_err = cfg->err;
744: starter_conn_t *conn;
745:
746: INIT(conn,
747: .name = strdup(name),
748: );
749: conn_defaults(conn);
750: load_conn(conn, cfg, parser);
751:
752: if (cfg->err > previous_err)
753: {
754: total_err = cfg->err - previous_err;
755: DBG1(DBG_APP, "# ignored conn '%s' due to %d parsing error%s", name,
756: total_err, (total_err > 1) ? "s" : "");
757: confread_free_conn(conn);
758: cfg->non_fatal_err += cfg->err - previous_err;
759: cfg->err = previous_err;
760: }
761: else
762: {
763: if (cfg->conn_last)
764: {
765: cfg->conn_last->next = conn;
766: }
767: cfg->conn_last = conn;
768: if (!cfg->conn_first)
769: {
770: cfg->conn_first = conn;
771: }
772: if (conn->startup != STARTUP_NO)
773: {
774: conn->state = STATE_TO_ADD;
775: }
776: }
777: }
778: enumerator->destroy(enumerator);
779:
780: parser->destroy(parser);
781:
782: total_err = cfg->err + cfg->non_fatal_err;
783: if (total_err > 0)
784: {
785: DBG1(DBG_APP, "### %d parsing error%s (%d fatal) ###",
786: total_err, (total_err > 1)?"s":"", cfg->err);
787: }
788: return cfg;
789: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>