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 }