1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright (c) 2017, Joyent, Inc.
14 */
15 #include <sys/time.h>
16 #include <sys/debug.h>
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <sys/sysmacros.h>
20 #include <sys/stat.h>
21 #include <arpa/inet.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <umem.h>
27 #include <netinet/in.h>
28 #include <errno.h>
29 #include <bunyan.h>
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <err.h>
33 #include "defs.h"
34 #include "config.h"
35 #include "ikev2.h"
36 #include "ikev2_enum.h"
37
38 #ifndef ARRAY_SIZE
39 #define ARRAY_SIZE(x) (sizeof (x) / sizeof (x[0]))
40 #endif
41
42 #define CONFIG_MAX ((size_t)1024*1024)
43 #define CONFIG_CHUNK ((size_t)1024)
44
45 typedef enum keyword_e {
46 KW_NONE = 0,
47 KW_CERT_ROOT,
48 KW_CERT_TRUST,
49 KW_EXPIRE_TIMER,
50 KW_IGNORE_CRLS,
51 KW_LDAP_SERVER,
52 KW_PKCS11_PATH,
53 KW_RETRY_LIMIT,
54 KW_RETRY_TIMER_INIT,
55 KW_RETRY_TIMER_MAX,
56 KW_PROXY,
57 KW_SOCKS,
58 KW_USE_HTTP,
59 KW_P1_LIFETIME_SECS,
60 KW_P1_NONCE_LEN,
61 KW_P2_LIFETIME_SECS,
62 KW_P2_SOFTLIFE_SECS,
63 KW_P2_IDLETIME_SECS,
64 KW_P2_LIFETIME_KB,
65 KW_P2_SOFTLIFE_KB,
66 KW_P2_NONCE_LEN,
67 KW_LOCAL_ID_TYPE,
68 KW_P1_XFORM,
69 KW_AUTH_METHOD,
70 KW_OAKLEY_GROUP,
71 KW_AUTH_ALG,
72 KW_ENCR_ALG,
73 KW_LABEL,
74 KW_LOCAL_ADDR,
75 KW_REMOTE_ADDR,
76 KW_P2_PFS,
77 KW_LOCAL_ID,
78 KW_REMOTE_ID,
79 KW_MAX
80 } keyword_t;
81
82 static struct {
83 const char *kw_str;
84 boolean_t kw_has_arg;
85 boolean_t kw_minus;
86 } keyword_tab[] = {
87 { "", B_FALSE },
88 { "cert_root", B_TRUE, B_FALSE },
89 { "cert_trust", B_TRUE, B_FALSE },
90 { "expire_timer", B_TRUE, B_FALSE },
91 { "ignore_crls", B_FALSE },
92 { "ldap_server", B_TRUE, B_FALSE },
93 { "pkcs11_path", B_TRUE, B_FALSE },
94 { "retry_limit", B_TRUE, B_FALSE },
95 { "retry_timer_init", B_TRUE, B_FALSE },
96 { "retry_timer_max", B_TRUE, B_FALSE },
97 { "proxy", B_TRUE, B_FALSE },
98 { "socks", B_TRUE, B_FALSE },
99 { "use_http", B_FALSE },
100 { "p1_lifetime_secs", B_TRUE, B_FALSE },
101 { "p1_nonce_len", B_TRUE, B_FALSE },
102 { "p2_lifetime_secs", B_TRUE, B_FALSE },
103 { "p2_softlife_secs", B_TRUE, B_FALSE },
104 { "p2_idletime_secs", B_TRUE, B_FALSE },
105 { "p2_lifetime_kb", B_TRUE, B_FALSE },
106 { "p2_softlife_kb", B_TRUE, B_FALSE },
107 { "p2_nonce_len", B_TRUE, B_FALSE },
108 { "local_id_type", B_TRUE, B_FALSE },
109 { "p1_xform", B_FALSE },
110 { "auth_method", B_TRUE, B_FALSE },
111 { "oakley_group", B_TRUE, B_FALSE },
112 { "auth_alg", B_TRUE, B_FALSE },
113 { "encr_alg", B_TRUE, B_FALSE },
114 { "label", B_TRUE, B_FALSE },
115 { "local_addr", B_TRUE, B_TRUE },
116 { "remote_addr", B_TRUE, B_TRUE },
117 { "p2_pfs", B_TRUE, B_FALSE },
118 { "local_id", B_TRUE, B_FALSE },
119 { "remote_id", B_TRUE, B_FALSE },
120 };
121
122 static struct {
123 ikev2_auth_type_t a_id;
124 const char *a_str;
125 } auth_tab[] = {
126 { IKEV2_AUTH_NONE, "" },
127 { IKEV2_AUTH_RSA_SIG, "rsa_sig" },
128 { IKEV2_AUTH_SHARED_KEY_MIC, "preshared" },
129 { IKEV2_AUTH_DSS_SIG, "dss_sig" }
130 };
131
132 static struct {
133 config_auth_id_t p1_id;
134 const char *p1_str;
135 } p1_id_tab[] = {
136 { CFG_AUTH_ID_DN, "dn" },
137 { CFG_AUTH_ID_DN, "DN" },
138 { CFG_AUTH_ID_DNS, "dns" },
139 { CFG_AUTH_ID_DNS, "DNS" },
140 { CFG_AUTH_ID_DNS, "fqdn" },
141 { CFG_AUTH_ID_DNS, "FQDN" },
142 { CFG_AUTH_ID_GN, "gn" },
143 { CFG_AUTH_ID_GN, "GN" },
144 { CFG_AUTH_ID_IPV4, "ip" },
145 { CFG_AUTH_ID_IPV4, "IP" },
146 { CFG_AUTH_ID_IPV4, "ipv4" },
147 { CFG_AUTH_ID_IPV4_PREFIX, "ipv4_prefix" },
148 { CFG_AUTH_ID_IPV4_RANGE, "ipv4_range" },
149 { CFG_AUTH_ID_IPV6, "ipv6" },
150 { CFG_AUTH_ID_IPV6_PREFIX, "ipv6_prefix" },
151 { CFG_AUTH_ID_IPV6_RANGE, "ipv6_range" },
152 { CFG_AUTH_ID_EMAIL, "mbox" },
153 { CFG_AUTH_ID_EMAIL, "MBOX" },
154 { CFG_AUTH_ID_EMAIL, "user_fqdn" }
155 };
156
157 static struct {
158 ikev2_xf_auth_t xfa_id;
159 const char *xfa_str;
160 } xf_auth_tab[] = {
161 { IKEV2_XF_AUTH_HMAC_MD5_128, "md5" }, /* XXX: verify this */
162 { IKEV2_XF_AUTH_HMAC_SHA1_160, "sha" },
163 { IKEV2_XF_AUTH_HMAC_SHA1_160, "sha1" },
164 { IKEV2_XF_AUTH_HMAC_SHA2_256_128, "sha256" },
165 { IKEV2_XF_AUTH_HMAC_SHA2_384_192, "sha384" },
166 { IKEV2_XF_AUTH_HMAC_SHA2_512_256, "sha512" }
167 };
168
169 static struct {
170 ikev2_xf_encr_t xfe_id;
171 const char *xfe_str;
172 } xf_encr_tab[] = {
173 { IKEV2_ENCR_DES, "des" },
174 { IKEV2_ENCR_DES, "des-cbc" },
175 { IKEV2_ENCR_3DES, "3des" },
176 { IKEV2_ENCR_3DES, "3des-cbc" },
177 { IKEV2_ENCR_BLOWFISH, "blowfish" },
178 { IKEV2_ENCR_BLOWFISH, "blowfish-cbc" },
179 { IKEV2_ENCR_AES_CBC, "aes" },
180 { IKEV2_ENCR_AES_CBC, "aes-cbc" },
181 { IKEV2_ENCR_AES_CCM_16, "aes-ccm" },
182 { IKEV2_ENCR_AES_GCM_16, "aes-gcm" }
183 };
184
185 /*
186 * size_t would be a more appropriate type for t_{line,col}, but using
187 * uint32_t makes it cleaner for logging with bunyan
188 */
189 typedef struct token {
190 char *t_str;
191 const char *t_linep;
192 uint32_t t_line;
193 uint32_t t_col;
194 } token_t;
195
196 typedef struct input {
197 char *in_buf;
198 size_t in_buflen;
199 char **in_lines;
200 } input_t;
201
202 typedef struct input_cursor {
203 input_t *ic_input;
204 char *ic_p;
205 token_t *ic_peek;
206 bunyan_logger_t *ic_log;
207 } input_cursor_t;
208
209 static void add_str(char ***restrict, size_t *restrict, const char *restrict);
210 static void add_xf(void *restrict, config_xf_t *restrict, boolean_t);
211 static void add_rule(config_t *restrict, config_rule_t *restrict);
212
213 static token_t *tok_new(const char *, const char *, const char *, size_t,
214 size_t);
215 static void tok_free(token_t *);
216 static void tok_log(token_t *restrict, bunyan_logger_t *restrict,
217 bunyan_level_t, const char *restrict, const char *restrict);
218 static void tok_error(token_t *restrict, bunyan_logger_t *restrict,
219 const char *restrict, const char *restrict);
220 static void tok_invalid(token_t *restrict, bunyan_logger_t *restrict,
221 keyword_t);
222
223 static boolean_t parse_rule(input_cursor_t *restrict, const token_t *restrict,
224 config_rule_t **restrict);
225 static boolean_t parse_address(input_cursor_t *restrict,
226 config_addr_t *restrict);
227
228 static boolean_t parse_xform(input_cursor_t *restrict, config_xf_t **restrict);
229 static boolean_t parse_encrbits(input_cursor_t *restrict,
230 config_xf_t *restrict);
231
232 static boolean_t parse_kw(const char *restrict, keyword_t *restrict);
233 static boolean_t parse_auth(const char *restrict, ikev2_auth_type_t *restrict);
234 static boolean_t parse_authalg(const char *restrict, ikev2_xf_auth_t *restrict);
235 static boolean_t parse_encralg(const char *restrict, ikev2_xf_encr_t *restrict);
236 static boolean_t parse_p1_id(const char *restrict, config_auth_id_t *restrict);
237 static boolean_t parse_p2_pfs(const char *restrict, ikev2_dh_t *restrict);
238 static boolean_t parse_ip(const char *restrict, in_addr_t *restrict);
239 static boolean_t parse_ip6(const char *restrict, in6_addr_t *restrict);
240 static boolean_t parse_int(const char *restrict, uint64_t *restrict);
241 static boolean_t parse_fp(const char *restrict, double *restrict);
242
243 static input_t *input_new(FILE *restrict, bunyan_logger_t *restrict);
244 static void input_free(input_t *);
245
246 static void input_cursor_init(input_cursor_t *, input_t *, bunyan_logger_t *);
247 static void input_cursor_fini(input_cursor_t *);
248 static token_t *input_token(input_cursor_t *, boolean_t);
249 static const token_t *input_peek(input_cursor_t *, boolean_t);
250 static token_t *input_next_token(input_cursor_t *, boolean_t);
251 static void input_cursor_getpos(input_cursor_t *restrict, const char *restrict,
252 const char **restrict, uint32_t *restrict, uint32_t *restrict);
253
254 static boolean_t issep(char c, boolean_t);
255
256 /*
257 * When processing a configuration file, we first load the entire contents
258 * into memory before doing any parsing. This is to hopefully allow more
259 * contextual error messages (such as being able to output the full line of
260 * text where an error occurs, as well as the location where the error occurs).
261 * Once successfully parsed, the contents are discarded.
262 *
263 * The general approach is to then generate a stream of string tokens. We
264 * defer interpretation of the tokens (e.g. 'IP address') since there are
265 * some instances where it'd be complicated to do so due to potential
266 * ambiguities. Instead it's simpler to wait until there's more context.
267 *
268 * For example, once the 'local_addr' keyword has been seen, we know the next
269 * token should be either an IPV4 or IPV6 address, an IPV[46] address prefix
270 * (address/masklen), or an IPV[46] range (start address-end address). We can
271 * attempt to convert the string accordingly without ambiguity.
272 *
273 * To assist in that, there is a (currently) limited ability to peek (view
274 * without advancing the stream) at the next token. This has (so far)
275 * proven sufficient.
276 *
277 * To check the configuration, we build a new copy of config_t, and if it
278 * succeeds to completion, we know the configuration does not have any
279 * errors, and then discard it (instead of replacing the current configuration).
280 *
281 * TODO: We should probably support the ability to add and remove individual
282 * rules.
283 */
284 void
285 process_config(FILE *f, boolean_t check_only, bunyan_logger_t *blog)
286 {
287 input_t *in = input_new(f, blog);
288 token_t *t = NULL, *targ = NULL;
289 config_t *cfg = NULL;
290 config_xf_t *xf = NULL;
291 input_cursor_t ic = { 0 };
292 union {
293 uint64_t ui;
294 double d;
295 } val;
296
297 if (in == NULL) {
298 STDERR(error, blog, "failure reading input");
299 return;
300 }
301
302 cfg = calloc(1, sizeof (*cfg));
303 VERIFY3P(cfg, !=, NULL);
304
305 input_cursor_init(&ic, in, blog);
306 while ((t = input_token(&ic, B_TRUE)) != NULL) {
307 keyword_t kw;
308
309 if (strcmp(t->t_str, "{") == 0) {
310 config_rule_t *rule = NULL;
311
312 if (!parse_rule(&ic, t, &rule))
313 goto fail;
314
315 add_rule(cfg, rule);
316 tok_free(t);
317 continue;
318 }
319
320 if (!parse_kw(t->t_str, &kw)) {
321 tok_error(t, blog,
322 "Unrecognized configuration parameter",
323 "parameter");
324 goto fail;
325 }
326
327 VERIFY3S(kw, !=, KW_NONE);
328 VERIFY3S(kw, !=, KW_MAX);
329
330 if (keyword_tab[kw].kw_has_arg) {
331 targ = input_token(&ic, keyword_tab[kw].kw_minus);
332 if (targ == NULL) {
333 tok_error(t, blog,
334 "Parameter is missing argument",
335 "parameter");
336 goto fail;
337 }
338 }
339
340 switch (kw) {
341 case KW_NONE:
342 case KW_MAX:
343 INVALID("t->t_val.t_kw");
344 break;
345 case KW_CERT_ROOT:
346 add_str(&cfg->cfg_cert_root, &cfg->cfg_cert_root_alloc,
347 targ->t_str);
348 break;
349 case KW_CERT_TRUST:
350 add_str(&cfg->cfg_cert_trust,
351 &cfg->cfg_cert_trust_alloc, targ->t_str);
352 break;
353 case KW_EXPIRE_TIMER:
354 if (!parse_int(targ->t_str, &val.ui)) {
355 tok_invalid(t, blog, KW_EXPIRE_TIMER);
356 goto fail;
357 }
358 cfg->cfg_expire_timer = val.ui * NANOSEC;
359 break;
360 case KW_IGNORE_CRLS:
361 cfg->cfg_ignore_crls = B_TRUE;
362 break;
363 case KW_LDAP_SERVER:
364 case KW_PKCS11_PATH:
365 tok_log(t, blog, BUNYAN_L_INFO,
366 "Ignoring deprecated configuration parameter",
367 "parameter");
368 break;
369 case KW_RETRY_LIMIT:
370 if (!parse_int(targ->t_str, &val.ui)) {
371 tok_invalid(t, blog, KW_RETRY_LIMIT);
372 goto fail;
373 }
374 cfg->cfg_retry_limit = val.ui;
375 break;
376 case KW_PROXY:
377 cfg->cfg_proxy = strdup(targ->t_str);
378 VERIFY3P(cfg->cfg_proxy, !=, NULL);
379 break;
380 case KW_SOCKS:
381 cfg->cfg_socks = strdup(targ->t_str);
382 VERIFY3P(cfg->cfg_socks, !=, NULL);
383 break;
384 case KW_RETRY_TIMER_INIT:
385 if (parse_int(targ->t_str, &val.ui)) {
386 cfg->cfg_retry_init = val.ui * NANOSEC;
387 } else if (parse_fp(targ->t_str, &val.d)) {
388 cfg->cfg_retry_init =
389 (hrtime_t)(val.d * NANOSEC);
390 } else {
391 tok_invalid(targ, blog, kw);
392 goto fail;
393 }
394 break;
395 case KW_RETRY_TIMER_MAX:
396 if (parse_int(targ->t_str, &val.ui)) {
397 cfg->cfg_retry_max = val.ui * NANOSEC;
398 } else if (parse_fp(targ->t_str, &val.d)) {
399 cfg->cfg_retry_max =
400 (hrtime_t)(val.d * NANOSEC);
401 } else {
402 tok_invalid(targ, blog, kw);
403 goto fail;
404 }
405 break;
406 case KW_P1_LIFETIME_SECS:
407 if (!parse_int(targ->t_str, &val.ui)) {
408 tok_invalid(targ, blog, kw);
409 goto fail;
410 }
411 cfg->cfg_p1_lifetime_secs = val.ui;
412 break;
413 case KW_P1_NONCE_LEN:
414 if (!parse_int(targ->t_str, &val.ui)) {
415 tok_invalid(targ, blog, kw);
416 goto fail;
417 }
418 /* XXX: check size */
419 cfg->cfg_p1_nonce_len = val.ui;
420 break;
421 case KW_P2_LIFETIME_SECS:
422 if (!parse_int(targ->t_str, &val.ui)) {
423 tok_invalid(targ, blog, kw);
424 goto fail;
425 }
426 /* XXX: check size */
427 cfg->cfg_p2_lifetime_secs = val.ui;
428 break;
429 case KW_P2_SOFTLIFE_SECS:
430 if (!parse_int(targ->t_str, &val.ui)) {
431 tok_invalid(targ, blog, kw);
432 goto fail;
433 }
434 /* XXX: check size */
435 cfg->cfg_p2_softlife_secs = val.ui;
436 break;
437 case KW_P2_IDLETIME_SECS:
438 if (!parse_int(targ->t_str, &val.ui)) {
439 tok_invalid(targ, blog, kw);
440 goto fail;
441 }
442 /* XXX: check size */
443 cfg->cfg_p2_idletime_secs = val.ui;
444 break;
445 case KW_P2_LIFETIME_KB:
446 if (!parse_int(targ->t_str, &val.ui)) {
447 tok_invalid(targ, blog, kw);
448 goto fail;
449 }
450 /* XXX: check size */
451 cfg->cfg_p2_lifetime_kb = val.ui;
452 break;
453 case KW_P2_SOFTLIFE_KB:
454 if (!parse_int(targ->t_str, &val.ui)) {
455 tok_invalid(targ, blog, kw);
456 goto fail;
457 }
458 /* XXX: check size */
459 cfg->cfg_p2_softlife_kb = val.ui;
460 break;
461 case KW_P2_NONCE_LEN:
462 if (!parse_int(targ->t_str, &val.ui)) {
463 tok_invalid(targ, blog, kw);
464 goto fail;
465 }
466 /* XXX: check size */
467 cfg->cfg_p2_nonce_len = val.ui;
468 break;
469 case KW_LOCAL_ID_TYPE:
470 tok_log(t, blog, BUNYAN_L_INFO, "Unimplemented "
471 "configuration parameter", "keyword");
472 break;
473 case KW_USE_HTTP:
474 cfg->cfg_use_http = B_TRUE;
475 break;
476 case KW_P2_PFS:
477 if (!parse_p2_pfs(targ->t_str, &cfg->cfg_p2_pfs)) {
478 tok_error(targ, blog, "Invalid p2_pfs value",
479 "value");
480 goto fail;
481 }
482 break;
483 case KW_P1_XFORM:
484 if (!parse_xform(&ic, &xf))
485 goto fail;
486 add_xf(cfg, xf, B_FALSE);
487 xf = NULL;
488 break;
489 case KW_AUTH_METHOD:
490 case KW_OAKLEY_GROUP:
491 case KW_AUTH_ALG:
492 case KW_ENCR_ALG:
493 tok_error(t, blog, "Configuration parameter cannot be "
494 "used outside of a transform definition",
495 "parameter");
496 goto fail;
497 case KW_LABEL:
498 case KW_LOCAL_ADDR:
499 case KW_REMOTE_ADDR:
500 case KW_LOCAL_ID:
501 case KW_REMOTE_ID:
502 tok_error(t, blog, "Configuration parameter cannot be "
503 "used outside of a rule definition", "parameter");
504 goto fail;
505 }
506
507 tok_free(t);
508 tok_free(targ);
509 t = NULL;
510 targ = NULL;
511 }
512
513 tok_free(t);
514 tok_free(targ);
515 input_cursor_fini(&ic);
516 input_free(in);
517
518 if (check_only) {
519 cfg_free(cfg);
520 } else {
521 config_t *old = NULL;
522
523 cfg->cfg_refcnt = 1;
524
525 PTH(pthread_rwlock_wrlock(&cfg_lock));
526 old = config;
527 config = cfg;
528 PTH(pthread_rwlock_unlock(&cfg_lock));
529 CONFIG_REFRELE(old);
530 }
531 return;
532
533 fail:
534 tok_free(t);
535 tok_free(targ);
536 input_cursor_fini(&ic);
537 input_free(in);
538 cfg_free(cfg);
539 }
540
541 static boolean_t
542 parse_xform(input_cursor_t *restrict ic, config_xf_t **restrict xfp)
543 {
544 config_xf_t *xf = NULL;
545 token_t *t = NULL, *targ = NULL;
546 boolean_t seen_authalg = B_FALSE;
547 boolean_t seen_encralg = B_FALSE;
548 boolean_t seen_dh = B_FALSE;
549 boolean_t seen_authmethod = B_FALSE;
550 boolean_t seen_lifetime_secs = B_FALSE;
551 boolean_t seen_nonce_len = B_FALSE;
552 uint64_t val = 0;
553
554 xf = calloc(1, sizeof (*xf));
555 VERIFY3P(xf, !=, NULL);
556
557 if ((t = input_token(ic, B_FALSE)) == NULL) {
558 bunyan_error(ic->ic_log, "Unexpected end of input processing "
559 "transform", BUNYAN_T_END);
560 goto fail;
561 }
562
563 if (strcmp(t->t_str, "{") != 0) {
564 bunyan_error(ic->ic_log, "Expected '{' after p1_xform",
565 BUNYAN_T_STRING, "string", t->t_str,
566 BUNYAN_T_END);
567 goto fail;
568 }
569
570 /*CONSTCOND*/
571 while (1) {
572 if ((t = input_token(ic, B_FALSE)) == NULL) {
573 bunyan_error(ic->ic_log,
574 "Unexpected end of input processing transform",
575 BUNYAN_T_END);
576 goto fail;
577 }
578 if (strcmp(t->t_str, "}") == 0)
579 break;
580
581 /* All of the keywords require an argument */
582 if ((targ = input_token(ic, B_FALSE)) == NULL) {
583 tok_error(t, ic->ic_log,
584 "Missing argument to parameter", "parameter");
585 goto fail;
586 }
587
588 keyword_t kw = KW_NONE;
589
590 if (!parse_kw(t->t_str, &kw)) {
591 tok_error(t, ic->ic_log,
592 "Unknown configuration parameter", "parameter");
593 goto fail;
594 }
595
596 switch (kw) {
597 case KW_AUTH_METHOD:
598 if (seen_authmethod)
599 goto duplicate;
600 if (!parse_auth(targ->t_str, &xf->xf_authtype)) {
601 tok_error(targ, ic->ic_log,
602 "Unknown authentication method",
603 "authmethod");
604 goto fail;
605 }
606 seen_authmethod = B_TRUE;
607 break;
608 case KW_OAKLEY_GROUP:
609 if (seen_dh)
610 goto duplicate;
611 if (!parse_int(targ->t_str, &val)) {
612 tok_error(targ, ic->ic_log,
613 "Unknown oakley (DH) group",
614 "group");
615 goto fail;
616 }
617 /* XXX: Should have a way to validate the value */
618 seen_dh = B_TRUE;
619 xf->xf_dh = (ikev2_dh_t)val;
620 break;
621 case KW_AUTH_ALG:
622 if (seen_authalg)
623 goto duplicate;
624 if (!parse_authalg(targ->t_str, &xf->xf_auth)) {
625 tok_error(targ, ic->ic_log,
626 "Unknown authentication algorithm",
627 "algorithm");
628 goto fail;
629 }
630 seen_authalg = B_TRUE;
631 break;
632 case KW_ENCR_ALG:
633 if (seen_encralg)
634 goto duplicate;
635 if (!parse_encralg(targ->t_str, &xf->xf_encr)) {
636 tok_error(targ, ic->ic_log,
637 "Unknown encryption algorithm",
638 "algorithm");
639 goto fail;
640 }
641 seen_encralg = B_TRUE;
642 if (!parse_encrbits(ic, xf))
643 goto fail;
644 break;
645 case KW_P1_LIFETIME_SECS:
646 if (seen_lifetime_secs)
647 goto duplicate;
648 if (!parse_int(targ->t_str, &val)) {
649 tok_error(targ, ic->ic_log, "Invalid value",
650 "value");
651 goto fail;
652 }
653 xf->xf_lifetime_secs = (uint32_t)val;
654 seen_lifetime_secs = B_TRUE;
655 break;
656 case KW_P1_NONCE_LEN: /*xf_nonce_len*/
657 if (seen_nonce_len)
658 goto duplicate;
659 if (!parse_int(targ->t_str, &val)) {
660 tok_error(targ, ic->ic_log, "Invalid value",
661 "value");
662 goto fail;
663 }
664 /* XXX: validate length */
665 xf->xf_nonce_len = (uint32_t)val;
666 seen_nonce_len = B_TRUE;
667 break;
668 default:
669 bunyan_error(ic->ic_log, "Parameter keyword not "
670 "allowed in transform definition",
671 BUNYAN_T_STRING, "keyword", t->t_str,
672 BUNYAN_T_END);
673 goto fail;
674 }
675
676 tok_free(t);
677 tok_free(targ);
678 t = NULL;
679 targ = NULL;
680 }
681 *xfp = xf;
682 return (B_TRUE);
683
684 duplicate:
685 tok_error(t, ic->ic_log, "Duplicate configuration parameter",
686 "parameter");
687
688 fail:
689 tok_free(t);
690 tok_free(targ);
691 free(xf);
692 *xfp = NULL;
693 return (B_FALSE);
694 }
695
696 static boolean_t
697 parse_encrbits(input_cursor_t *restrict ic, config_xf_t *restrict xf)
698 {
699 const token_t *tpeek = NULL;
700 token_t *t = NULL;
701 uint64_t val = 0;
702
703 if ((tpeek = input_peek(ic, B_FALSE)) == NULL)
704 goto truncated;
705
706 /* No key length given, that's ok */
707 if (strcmp(tpeek->t_str, "(") != 0)
708 return (B_TRUE);
709
710 /* consume '(' */
711 tok_free(input_token(ic, B_FALSE));
712
713 if ((t = input_token(ic, B_FALSE)) == NULL)
714 goto truncated;
715
716 if (!parse_int(t->t_str, &val))
717 goto invalid;
718 if (val > SIZE_MAX)
719 goto toobig;
720 xf->xf_minbits = (size_t)val;
721 tok_free(t);
722
723 if ((t = input_token(ic, B_FALSE)) == NULL)
724 goto truncated;
725 if (strcmp(t->t_str, ")") == 0) {
726 xf->xf_maxbits = xf->xf_minbits;
727 goto done;
728 }
729
730 if (strcmp(t->t_str, "..") != 0)
731 goto unexpected;
732 tok_free(t);
733
734 if ((t = input_token(ic, B_TRUE)) == NULL)
735 goto truncated;
736 if (!parse_int(t->t_str, &val))
737 goto invalid;
738 if (val > SIZE_MAX)
739 goto toobig;
740 xf->xf_maxbits = val;
741
742 if (xf->xf_maxbits < xf->xf_minbits) {
743 bunyan_error(ic->ic_log,
744 "Maximum keysize is smaller than minimum keysize",
745 BUNYAN_T_STRING, "value", t->t_str,
746 BUNYAN_T_UINT32, "line", t->t_line,
747 BUNYAN_T_UINT32, "col", t->t_col,
748 BUNYAN_T_END);
749 tok_free(t);
750 return (B_FALSE);
751 }
752
753 tok_free(t);
754 if ((t = input_token(ic, B_TRUE)) == NULL)
755 goto truncated;
756 if (strcmp(t->t_str, ")") != 0)
757 goto unexpected;
758
759 done:
760 tok_free(t);
761 return (B_TRUE);
762
763 unexpected:
764 bunyan_error(ic->ic_log, "Unexpected value after key length",
765 BUNYAN_T_STRING, "value", t->t_str,
766 BUNYAN_T_UINT32, "line", t->t_line,
767 BUNYAN_T_UINT32, "col", t->t_col,
768 BUNYAN_T_END);
769 tok_free(t);
770 return (B_FALSE);
771
772 invalid:
773 bunyan_error(ic->ic_log, "Invalid key bitlength",
774 BUNYAN_T_STRING, "bitlength", t->t_str,
775 BUNYAN_T_UINT32, "line", t->t_line,
776 BUNYAN_T_UINT32, "col", t->t_col,
777 BUNYAN_T_END);
778 tok_free(t);
779 return (B_FALSE);
780
781 toobig:
782 bunyan_error(ic->ic_log, "Keysize is too large",
783 BUNYAN_T_UINT64, "keysize", val,
784 BUNYAN_T_UINT32, "line", t->t_line,
785 BUNYAN_T_UINT32, "col", t->t_col,
786 BUNYAN_T_END);
787 tok_free(t);
788 return (B_FALSE);
789
790 truncated:
791 tok_free(t);
792 bunyan_error(ic->ic_log, "Truncated input while reading transform",
793 BUNYAN_T_END);
794 return (B_FALSE);
795 }
796
797 static boolean_t
798 parse_rule(input_cursor_t *restrict ic, const token_t *start,
799 config_rule_t **restrict rulep)
800 {
801 token_t *t = NULL, *targ = NULL;
802 config_rule_t *rule = NULL;
803 config_xf_t *xf = NULL;
804 config_addr_t addr = { 0 };
805 boolean_t seen_label = B_FALSE;
806 boolean_t seen_local_addr = B_FALSE;
807 boolean_t seen_remote_addr = B_FALSE;
808 boolean_t seen_local_id_type = B_FALSE;
809 boolean_t seen_local_id = B_FALSE;
810 boolean_t seen_remote_id = B_FALSE;
811 boolean_t seen_p2_lifetime_secs = B_FALSE;
812 boolean_t seen_p2_pfs = B_FALSE;
813 boolean_t seen_p1_xform = B_FALSE;
814 boolean_t has_non_preshared = B_FALSE;
815
816 *rulep = NULL;
817
818 rule = calloc(1, sizeof (*rule));
819 VERIFY3P(rule, !=, NULL);
820
821 while ((t = input_token(ic, B_FALSE)) != NULL) {
822 keyword_t kw = KW_NONE;
823
824 if (strcmp(t->t_str, "}") == 0)
825 break;
826
827 if (!parse_kw(t->t_str, &kw)) {
828 tok_log(t, ic->ic_log, BUNYAN_L_ERROR,
829 "Unrecognized configuration parameter",
830 "parameter");
831 goto fail;
832 }
833
834 switch (kw) {
835 case KW_LOCAL_ADDR:
836 case KW_REMOTE_ADDR:
837 case KW_P1_XFORM:
838 break;
839 default:
840 targ = input_token(ic, B_FALSE);
841 if (targ == NULL) {
842 bunyan_error(ic->ic_log, "Input truncated "
843 "while reading rule", BUNYAN_T_END);
844 goto fail;
845 }
846 }
847
848 switch (kw) {
849 case KW_LABEL:
850 if (seen_label)
851 goto duplicate;
852 rule->rule_label = strdup(targ->t_str);
853 if (rule->rule_label == NULL)
854 goto fail;
855 seen_label = B_TRUE;
856 break;
857 case KW_P2_PFS:
858 if (seen_p2_pfs)
859 goto duplicate;
860 if (!parse_p2_pfs(targ->t_str, &rule->rule_p2_dh)) {
861 tok_invalid(targ, ic->ic_log, KW_P2_PFS);
862 goto fail;
863 }
864 break;
865 case KW_P1_XFORM:
866 if (!parse_xform(ic, &xf))
867 goto fail;
868
869 add_xf(rule, xf, B_TRUE);
870 if (xf->xf_authtype != IKEV2_AUTH_SHARED_KEY_MIC)
871 has_non_preshared = B_TRUE;
872 seen_p1_xform = B_TRUE;
873 xf = NULL;
874 break;
875 case KW_LOCAL_ADDR:
876 (void) memset(&addr, 0, sizeof (addr));
877 if (!parse_address(ic, &addr))
878 goto fail;
879 seen_local_addr = B_TRUE;
880 break;
881 case KW_REMOTE_ADDR:
882 (void) memset(&addr, 0, sizeof (addr));
883 if (!parse_address(ic, &addr))
884 goto fail;
885 seen_remote_addr = B_TRUE;
886 break;
887 case KW_LOCAL_ID:
888 /*
889 * According to the man page, only one ID is used
890 * per rule, but instead of erroring, it just uses
891 * the first one.
892 */
893 if (seen_local_id)
894 break;
895 rule->rule_local_id = strdup(targ->t_str);
896 if (rule->rule_local_id == NULL)
897 goto fail;
898 seen_local_id = B_TRUE;
899 break;
900 case KW_REMOTE_ID:
901 /* XXX: allow multiple remote ids */
902 /* See KW_LOCAL_ID above */
903 if (seen_remote_id)
904 break;
905 rule->rule_remote_id = strdup(targ->t_str);
906 if (rule->rule_remote_id == NULL)
907 goto fail;
908 seen_remote_id = B_TRUE;
909 break;
910 case KW_LOCAL_ID_TYPE:
911 if (seen_local_id_type)
912 goto duplicate;
913 if (!parse_p1_id(targ->t_str,
914 &rule->rule_local_id_type)) {
915 tok_log(t, ic->ic_log, BUNYAN_L_ERROR,
916 "Unable to parse local_id_type", "value");
917 goto fail;
918 }
919 seen_local_id_type = B_TRUE;
920 break;
921 default:
922 tok_log(t, ic->ic_log, BUNYAN_L_ERROR, "Configuration "
923 "parameter is invalid inside a rule definition",
924 "parameter");
925 goto fail;
926 }
927
928 tok_free(t);
929 tok_free(targ);
930 t = NULL;
931 targ = NULL;
932 }
933
934 if (t == NULL) {
935 bunyan_error(ic->ic_log, "Input truncated while reading rule",
936 BUNYAN_T_END);
937 goto fail;
938 }
939
940 /* Try to show as many errors as we can */
941 if (!seen_label)
942 tok_error(t, ic->ic_log, "Rule is missing a required label",
943 NULL);
944 if (!seen_local_addr)
945 tok_error(t, ic->ic_log,
946 "Rule is missing a required local address", NULL);
947 if (!seen_remote_addr)
948 tok_error(t, ic->ic_log,
949 "Rule is missing a required remote address", NULL);
950
951 if (!seen_label || !seen_local_addr || !seen_remote_addr)
952 goto fail;
953
954 tok_free(t);
955 tok_free(targ);
956 *rulep = rule;
957 return (B_TRUE);
958
959 duplicate:
960 tok_log(t, ic->ic_log, BUNYAN_L_ERROR,
961 "Configuration parameter can only appear once in a transform "
962 "definition", "parameter");
963
964 fail:
965 tok_free(t);
966 tok_free(targ);
967 free(rule);
968 return (B_FALSE);
969 }
970
971 static boolean_t
972 parse_address(input_cursor_t *restrict ic, config_addr_t *restrict addrp)
973 {
974 const token_t *tpeek = NULL;
975 token_t *t = NULL;
976 boolean_t ip6 = B_FALSE;
977 boolean_t ok = B_FALSE;
978
979 t = input_token(ic, B_TRUE);
980 if (t == NULL)
981 goto truncated;
982
983 if (!parse_ip(t->t_str, &addrp->cfa_startu.cfa_ip4)) {
984 if (!parse_ip6(t->t_str, &addrp->cfa_startu.cfa_ip6)) {
985 tok_log(t, ic->ic_log, BUNYAN_L_ERROR,
986 "Unable to parse address", "address");
987 return (B_FALSE);
988 }
989 ip6 = B_TRUE;
990 }
991 tok_free(t);
992
993 tpeek = input_peek(ic, B_TRUE);
994 if (strcmp(tpeek->t_str, "-") == 0) {
995 /* consume - */
996 tok_free(input_token(ic, B_TRUE));
997
998 addrp->cfa_type =
999 ip6 ? CFG_ADDR_IPV6_RANGE : CFG_ADDR_IPV4_RANGE;
1000
1001 t = input_token(ic, B_FALSE);
1002 if (t == NULL)
1003 goto truncated;
1004
1005 ok = ip6 ? parse_ip6(t->t_str, &addrp->cfa_endu.cfa_ip6) :
1006 parse_ip(t->t_str, &addrp->cfa_endu.cfa_ip4);
1007 if (!ok) {
1008 tok_log(t, ic->ic_log, BUNYAN_L_ERROR,
1009 "Unable to parse address", "address");
1010 }
1011 tok_free(t);
1012 return (ok);
1013 } else if (strcmp(tpeek->t_str, "/") == 0) {
1014 uint64_t val = 0;
1015
1016 addrp->cfa_type =
1017 ip6 ? CFG_ADDR_IPV6_PREFIX : CFG_ADDR_IPV4_PREFIX;
1018
1019 /* consume "/" */
1020 tok_free(input_token(ic, B_TRUE));
1021
1022 t = input_token(ic, B_FALSE);
1023 if (t == NULL)
1024 goto truncated;
1025
1026 if (!parse_int(t->t_str, &val)) {
1027 tok_log(t, ic->ic_log, BUNYAN_L_ERROR,
1028 "Cannot parse mask length", "mask_len");
1029 return (B_FALSE);
1030 }
1031 tok_free(t);
1032 t = NULL;
1033 if ((ip6 && val > 128) || (!ip6 && val > 32)) {
1034 bunyan_error(ic->ic_log, "Mask length too long",
1035 BUNYAN_T_UINT64, "mask", val,
1036 BUNYAN_T_END);
1037 return (B_FALSE);
1038 }
1039 addrp->cfa_endu.cfa_num = (uint8_t)val;
1040 return (B_TRUE);
1041 } else {
1042 addrp->cfa_type =
1043 ip6 ? CFG_ADDR_IPV6 : CFG_ADDR_IPV4;
1044 }
1045
1046 return (B_TRUE);
1047
1048 truncated:
1049 bunyan_error(ic->ic_log, "Input truncated while parsing address",
1050 BUNYAN_T_END);
1051 return (B_FALSE);
1052 }
1053
1054 static boolean_t
1055 parse_p2_pfs(const char *restrict str, ikev2_dh_t *dhp)
1056 {
1057 uint64_t val = 0;
1058
1059 if (!parse_int(str, &val))
1060 return (B_FALSE);
1061
1062 /* XXX: validate value */
1063
1064 if (dhp != NULL)
1065 *dhp = (int)val;
1066 return (B_TRUE);
1067 }
1068
1069 static boolean_t
1070 parse_ip(const char *restrict str, in_addr_t *restrict addrp)
1071 {
1072 if (inet_pton(AF_INET, str, addrp) != 1)
1073 return (B_FALSE);
1074 return (B_TRUE);
1075 }
1076
1077 static boolean_t
1078 parse_ip6(const char *restrict str, in6_addr_t *restrict addrp)
1079 {
1080 if (inet_pton(AF_INET6, str, addrp) != 1)
1081 return (B_FALSE);
1082 return (B_TRUE);
1083 }
1084
1085 static boolean_t
1086 parse_int(const char *restrict str, uint64_t *restrict intp)
1087 {
1088 errno = 0;
1089 *intp = strtoull(str, NULL, 0);
1090 return ((errno == 0) ? B_TRUE : B_FALSE);
1091 }
1092
1093 static boolean_t
1094 parse_fp(const char *restrict str, double *restrict dp)
1095 {
1096 errno = 0;
1097 *dp = strtod(str, NULL);
1098 return ((errno == 0) ? B_TRUE : B_FALSE);
1099 }
1100
1101 static boolean_t
1102 parse_kw(const char *restrict str, keyword_t *restrict kwp)
1103 {
1104 for (keyword_t kw = KW_NONE; kw < KW_MAX; kw++) {
1105 if (strcmp(keyword_tab[kw].kw_str, str) == 0) {
1106 *kwp = kw;
1107 return (B_TRUE);
1108 }
1109 }
1110 return (B_FALSE);
1111 }
1112
1113 static boolean_t
1114 parse_auth(const char *restrict str, ikev2_auth_type_t *restrict authp)
1115 {
1116 for (size_t i = 0; i < ARRAY_SIZE(auth_tab); i++) {
1117 if (strcmp(auth_tab[i].a_str, str) == 0) {
1118 *authp = auth_tab[i].a_id;
1119 return (B_TRUE);
1120 }
1121 }
1122 return (B_FALSE);
1123 }
1124
1125 static boolean_t
1126 parse_authalg(const char *restrict str, ikev2_xf_auth_t *authp)
1127 {
1128 for (size_t i = 0; i < ARRAY_SIZE(xf_auth_tab); i++) {
1129 if (strcmp(xf_auth_tab[i].xfa_str, str) == 0) {
1130 *authp = xf_auth_tab[i].xfa_id;
1131 return (B_TRUE);
1132 }
1133 }
1134 return (B_FALSE);
1135 }
1136
1137 static boolean_t
1138 parse_encralg(const char *restrict str, ikev2_xf_encr_t *restrict encp)
1139 {
1140 for (size_t i = 0; i < ARRAY_SIZE(xf_encr_tab); i++) {
1141 if (strcmp(xf_encr_tab[i].xfe_str, str) == 0) {
1142 *encp = xf_encr_tab[i].xfe_id;
1143 return (B_TRUE);
1144 }
1145 }
1146 return (B_FALSE);
1147 }
1148
1149 static boolean_t
1150 parse_p1_id(const char *restrict str, config_auth_id_t *restrict p1p)
1151 {
1152 for (size_t i = 0; i < ARRAY_SIZE(p1_id_tab); i++) {
1153 if (strcmp(p1_id_tab[i].p1_str, str) == 0) {
1154 *p1p = p1_id_tab[i].p1_id;
1155 return (B_TRUE);
1156 }
1157 }
1158 return (B_FALSE);
1159 }
1160
1161 static token_t *
1162 tok_new(const char *startp, const char *endp, const char *linep, size_t line,
1163 size_t col)
1164 {
1165 VERIFY3P(endp, >=, startp);
1166
1167 token_t *t = NULL;
1168 size_t len = (size_t)(endp - startp) + 1;
1169
1170 t = calloc(1, sizeof (*t));
1171 VERIFY3P(t, !=, NULL);
1172
1173 t->t_str = calloc(1, len);
1174 VERIFY3P(t, !=, NULL);
1175
1176 (void) strlcpy(t->t_str, startp, len);
1177 t->t_linep = linep;
1178 t->t_line = line;
1179 t->t_col = col;
1180 return (t);
1181 }
1182
1183 static void
1184 tok_free(token_t *t)
1185 {
1186 if (t == NULL)
1187 return;
1188 free(t->t_str);
1189 free(t);
1190 }
1191
1192 #define STR(x) case x: return (#x)
1193 static const char *
1194 cfg_auth_id_str(config_auth_id_t id)
1195 {
1196 switch (id) {
1197 STR(CFG_AUTH_ID_DN);
1198 STR(CFG_AUTH_ID_DNS);
1199 STR(CFG_AUTH_ID_GN);
1200 STR(CFG_AUTH_ID_IPV4);
1201 STR(CFG_AUTH_ID_IPV4_PREFIX);
1202 STR(CFG_AUTH_ID_IPV4_RANGE);
1203 STR(CFG_AUTH_ID_IPV6);
1204 STR(CFG_AUTH_ID_IPV6_PREFIX);
1205 STR(CFG_AUTH_ID_IPV6_RANGE);
1206 STR(CFG_AUTH_ID_EMAIL);
1207 }
1208 return ("UNKNOWN");
1209 }
1210 #undef STR
1211
1212 static void
1213 tok_log(token_t *restrict t, bunyan_logger_t *restrict blog,
1214 bunyan_level_t level, const char *msg, const char *strname)
1215 {
1216 char *linecpy = NULL;
1217 const char *endp = strchr(t->t_linep, '\n');
1218 size_t len = 0;
1219
1220 if (endp != NULL)
1221 len = endp - t->t_linep + 1;
1222 else
1223 len = strlen(t->t_linep) + 1;
1224
1225 linecpy = malloc(len);
1226 VERIFY3P(linecpy, !=, NULL);
1227 (void) strlcpy(linecpy, t->t_linep, len);
1228
1229 if (strname != NULL) {
1230 getlog(level)(blog, msg, BUNYAN_T_STRING, strname, t->t_str,
1231 BUNYAN_T_STRING, "line", linecpy,
1232 BUNYAN_T_UINT32, "lineno", t->t_line,
1233 BUNYAN_T_UINT32, "col", t->t_col,
1234 BUNYAN_T_END);
1235 } else {
1236 getlog(level)(blog, msg,
1237 BUNYAN_T_STRING, "line", linecpy,
1238 BUNYAN_T_UINT32, "lineno", t->t_line,
1239 BUNYAN_T_UINT32, "col", t->t_col,
1240 BUNYAN_T_END);
1241 }
1242 free(linecpy);
1243 }
1244
1245 static void
1246 tok_error(token_t *restrict t, bunyan_logger_t *restrict b,
1247 const char *restrict msg, const char *restrict tname)
1248 {
1249 tok_log(t, b, BUNYAN_L_ERROR, msg, tname);
1250 }
1251
1252 static void
1253 tok_invalid(token_t *restrict t, bunyan_logger_t *restrict b, keyword_t kw)
1254 {
1255 char buf[128] = { 0 };
1256 (void) snprintf(buf, sizeof (buf), "Invalid %s parameter",
1257 keyword_tab[kw]);
1258 tok_error(t, b, buf, "parameter");
1259 }
1260
1261 static input_t *
1262 input_new(FILE *restrict f, bunyan_logger_t *restrict blog)
1263 {
1264 input_t *in = NULL;
1265 char *p = NULL;
1266 ssize_t n = 0;
1267 size_t cnt = 0;
1268 size_t nlines = 0;
1269 struct stat sb = { 0 };
1270 int fd = -1;
1271
1272 in = calloc(1, sizeof (*in));
1273 VERIFY3P(in, !=, NULL);
1274
1275 fd = fileno(f);
1276 if (fstat(fd, &sb) == -1) {
1277 STDERR(error, blog, "stat failed");
1278 goto fail;
1279 }
1280
1281 /*
1282 * Try to read in one go, however the input could be a pipe instead
1283 * of a file, in which case we have to keep growing the buffer
1284 * (up to the limit)
1285 */
1286 if (S_ISREG(sb.st_mode)) {
1287 in->in_buflen = sb.st_size + 2;
1288 } else {
1289 in->in_buflen = CONFIG_CHUNK;
1290 }
1291 in->in_buf = calloc(1, in->in_buflen);
1292 VERIFY3P(in->in_buf, !=, NULL);
1293
1294 do {
1295 n = fread(in->in_buf + cnt, 1, in->in_buflen - cnt - 1, f);
1296 if (n < 0) {
1297 STDERR(error, blog, "read failed");
1298 goto fail;
1299 }
1300 cnt += n;
1301
1302 if (cnt + 1 >= in->in_buflen) {
1303 if (in->in_buflen >= CONFIG_MAX) {
1304 bunyan_error(blog, "Input size exceeds limits",
1305 BUNYAN_T_UINT32, "size",
1306 (uint32_t)in->in_buflen,
1307 BUNYAN_T_UINT32, "limit",
1308 (uint32_t)CONFIG_MAX,
1309 BUNYAN_T_END);
1310 goto fail;
1311 }
1312
1313 size_t newlen = P2ROUNDUP(in->in_buflen + CONFIG_CHUNK,
1314 CONFIG_CHUNK);
1315
1316 char *newp = realloc(in->in_buf, newlen);
1317 VERIFY3P(newp, !=, NULL);
1318
1319 in->in_buf = newp;
1320 in->in_buflen = newlen;
1321 }
1322 } while (n > 0);
1323 in->in_buf[cnt] = '\0';
1324
1325 for (p = in->in_buf, nlines = 0; p != NULL; p = strchr(p + 1, '\n'))
1326 nlines++;
1327
1328 in->in_lines = calloc(nlines + 1, sizeof (char *));
1329 VERIFY3P(in->in_lines, !=, NULL);
1330
1331 for (p = in->in_buf, nlines = 0; p != NULL; p = strchr(p + 1, '\n'))
1332 in->in_lines[nlines++] = p;
1333
1334 return (in);
1335
1336 fail:
1337 input_free(in);
1338 return (NULL);
1339 }
1340
1341 static token_t *
1342 input_token(input_cursor_t *ic, boolean_t minus_is_sep)
1343 {
1344 token_t *t = NULL;
1345 if (ic->ic_peek != NULL ) {
1346 t = ic->ic_peek;
1347 ic->ic_peek = NULL;
1348 } else {
1349 t = input_next_token(ic, minus_is_sep);
1350 }
1351
1352 return (t);
1353 }
1354
1355 /* NOTE: Results of input_peek() should NOT be freed */
1356 static const token_t *
1357 input_peek(input_cursor_t *ic, boolean_t minus_is_sep)
1358 {
1359 if (ic->ic_peek != NULL)
1360 return (ic->ic_peek);
1361
1362 ic->ic_peek = input_next_token(ic, minus_is_sep);
1363 return (ic->ic_peek);
1364 }
1365
1366 /*
1367 * Actually get the next token from the input. This is used both by
1368 * input_token() and input_peek() and shouldn't be called by anything else.
1369 */
1370 static token_t *
1371 input_next_token(input_cursor_t *ic, boolean_t minus_is_sep)
1372 {
1373 char *start = NULL, *end = NULL;
1374 const char *linep = NULL;
1375 uint32_t line = 0, col = 0;
1376
1377 VERIFY3P(ic->ic_p, >=, ic->ic_input->in_buf);
1378 VERIFY3P(ic->ic_p, <, ic->ic_input->in_buf + ic->ic_input->in_buflen);
1379
1380 again:
1381 while (*ic->ic_p != '\0' && isspace(*ic->ic_p))
1382 ic->ic_p++;
1383
1384 if (*ic->ic_p == '#') {
1385 /* skip to next line */
1386 while (*ic->ic_p != '\0' && *ic->ic_p != '\n')
1387 ic->ic_p++;
1388 goto again;
1389 }
1390
1391 if (*ic->ic_p == '\0')
1392 return (NULL);
1393
1394 start = ic->ic_p;
1395 end = start + 1;
1396
1397 /* If the first character is a separator, we're done */
1398 if (issep(*start, minus_is_sep)) {
1399 ic->ic_p = end;
1400 goto done;
1401 }
1402
1403 if (*start == '"') {
1404 while (*end != '\0' && *end != '\n' && *end != '"')
1405 end++;
1406
1407 if (*end != '"') {
1408 input_cursor_getpos(ic, start, &linep, &line, &col);
1409 bunyan_error(ic->ic_log, "Unterminated quoted string",
1410 BUNYAN_T_UINT32, "line", line,
1411 BUNYAN_T_UINT32, "col", col,
1412 BUNYAN_T_END);
1413 return (NULL);
1414 }
1415
1416 start++;
1417 ic->ic_p = end + 1;
1418 goto done;
1419 }
1420
1421 while (*end != '\0' && !isspace(*end)) {
1422 if (issep(*end, minus_is_sep) || isspace(*end))
1423 break;
1424 end++;
1425 }
1426 ic->ic_p = end;
1427
1428 done:
1429 input_cursor_getpos(ic, start, &linep, &line, &col);
1430 return (tok_new(start, end, linep, line, col));
1431 }
1432
1433 static void
1434 input_cursor_getpos(input_cursor_t *restrict ic, const char *restrict p,
1435 const char **restrict linepp, uint32_t *restrict linep,
1436 uint32_t *restrict colp)
1437 {
1438 VERIFY3P(ic->ic_input->in_buf, <=, p);
1439 VERIFY3P(ic->ic_input->in_buf + ic->ic_input->in_buflen, >, p);
1440
1441 char **lineidx = ic->ic_input->in_lines;
1442 uint32_t line;
1443 for (line = 1; lineidx[line] != NULL && lineidx[line] <= p; line++)
1444 ;
1445
1446 line--;
1447 *linep = line;
1448 *colp = (uint32_t)(p - lineidx[line]);
1449 *linepp = lineidx[line];
1450 }
1451
1452 static void
1453 input_cursor_init(input_cursor_t *restrict ic, input_t *restrict in,
1454 bunyan_logger_t *blog)
1455 {
1456 (void) memset(ic, 0, sizeof (*ic));
1457 ic->ic_input = in;
1458 ic->ic_p = in->in_buf;
1459 ic->ic_log = blog;
1460 }
1461
1462 static void
1463 input_cursor_fini(input_cursor_t *ic)
1464 {
1465 free(ic->ic_peek);
1466 (void) memset(ic, 0, sizeof (*ic));
1467 }
1468
1469 static void
1470 input_free(input_t *in)
1471 {
1472 if (in == NULL)
1473 return;
1474
1475 free(in->in_buf);
1476 free(in->in_lines);
1477 free(in);
1478 }
1479
1480 #define CHUNK_SZ (8)
1481 /*
1482 * Append a string onto an array of strings. Since these shouldn't be heavily
1483 * called, we're not (currently at least) worried about the possibility
1484 * of excessive realloc() calls.
1485 */
1486 static void
1487 add_str(char ***restrict ppp, size_t *restrict allocp, const char *restrict str)
1488 {
1489 char *newstr = NULL;
1490 char **array = *ppp;
1491 size_t nelems = 0;
1492
1493 while (nelems < *allocp && array[nelems] != NULL)
1494 nelems++;
1495
1496 if (nelems + 2 > *allocp) {
1497 char **newarray = NULL;
1498 size_t newsize = *allocp + CHUNK_SZ;
1499 size_t amt = newsize * sizeof (char *);
1500
1501 VERIFY3U(amt, >, newsize);
1502 VERIFY3U(amt, >=, sizeof (char *));
1503
1504 /* realloc_array() would be nice */
1505 newarray = realloc(array, amt);
1506 VERIFY3P(newarray, !=, NULL);
1507
1508 *ppp = array = newarray;
1509 *allocp = newsize;
1510 }
1511
1512 newstr = strdup(str);
1513 VERIFY3P(newstr, !=, NULL);
1514
1515 array[nelems++] = newstr;
1516 array[nelems] = NULL;
1517 }
1518
1519 static void
1520 add_xf(void *restrict ptr, config_xf_t *restrict xf, boolean_t ptr_is_rule)
1521 {
1522 config_xf_t **xfp = NULL;
1523 size_t cur = 0;
1524 size_t nxf = 0;
1525
1526 if (ptr_is_rule) {
1527 config_rule_t *crp = ptr;
1528
1529 cur = crp->rule_nxf;
1530 xfp = crp->rule_xf;
1531 } else {
1532 config_t *cp = ptr;
1533
1534 cur = cp->cfg_xforms_alloc;
1535 xfp = cp->cfg_xforms;
1536 }
1537
1538 while (nxf < cur && xfp[nxf] != NULL)
1539 nxf++;
1540
1541 if (nxf + 2 > cur) {
1542 config_xf_t **newxf = NULL;
1543 size_t newalloc = cur + CHUNK_SZ;
1544 size_t amt = newalloc * sizeof (config_xf_t *);
1545
1546 VERIFY3U(amt, >, newalloc);
1547 VERIFY3U(amt, >=, sizeof (config_xf_t *));
1548
1549 newxf = realloc(xfp, amt);
1550 VERIFY3P(newxf, !=, NULL);
1551
1552 if (ptr_is_rule) {
1553 config_rule_t *crp = ptr;
1554
1555 crp->rule_nxf = newalloc;
1556 crp->rule_xf = xfp = newxf;
1557 } else {
1558 config_t *cp = ptr;
1559
1560 cp->cfg_xforms = xfp = newxf;
1561 cp->cfg_xforms_alloc = newalloc;
1562 }
1563 }
1564
1565 xfp[nxf++] = xf;
1566 xfp[nxf] = NULL;
1567 }
1568
1569 static void
1570 add_rule(config_t *restrict cfg, config_rule_t *restrict rule)
1571 {
1572 /* TODO: validate label value is unique */
1573 size_t nrules = 0;
1574
1575 while (nrules < cfg->cfg_rules_alloc && cfg->cfg_rules[nrules] != NULL)
1576 nrules++;
1577
1578 if (nrules + 2 > cfg->cfg_rules_alloc) {
1579 config_rule_t **newrules = NULL;
1580 size_t newalloc = cfg->cfg_rules_alloc + CHUNK_SZ;
1581 size_t amt = newalloc * sizeof (config_rule_t *);
1582
1583 VERIFY3U(amt, >, newalloc);
1584 VERIFY3U(amt, >=, sizeof (config_rule_t *));
1585
1586 newrules = realloc(cfg->cfg_rules, amt);
1587 VERIFY3P(newrules, !=, NULL);
1588
1589 cfg->cfg_rules = newrules;
1590 cfg->cfg_rules_alloc = newalloc;
1591 }
1592
1593 rule->rule_config = cfg;
1594 cfg->cfg_rules[nrules++] = rule;
1595 cfg->cfg_rules[nrules] = NULL;
1596 }
1597
1598 /* Is the given character a token separator? */
1599 static boolean_t
1600 issep(char c, boolean_t minus_is_sep)
1601 {
1602 switch (c) {
1603 case '{': case '}':
1604 case '(': case ')':
1605 case '/':
1606 return (B_TRUE);
1607 case '-':
1608 if (minus_is_sep)
1609 return (B_TRUE);
1610 break;
1611 }
1612 return (B_FALSE);
1613 }