parse.y revision 1.2
1/* $OpenBSD: parse.y,v 1.2 2005/04/04 22:22:55 hshoexer Exp $ */ 2 3/* 4 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 5 * Copyright (c) 2001 Markus Friedl. All rights reserved. 6 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 7 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 8 * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org> 9 * 10 * Permission to use, copy, modify, and distribute this software for any 11 * purpose with or without fee is hereby granted, provided that the above 12 * copyright notice and this permission notice appear in all copies. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 */ 22 23%{ 24#include <sys/types.h> 25#include <sys/queue.h> 26#include <sys/socket.h> 27#include <netinet/in.h> 28#include <arpa/inet.h> 29 30#include <ctype.h> 31#include <err.h> 32#include <errno.h> 33#include <limits.h> 34#include <stdarg.h> 35#include <stdio.h> 36#include <string.h> 37#include <syslog.h> 38 39#include "ipsecctl.h" 40 41static struct ipsecctl *ipsec = NULL; 42static FILE *fin = NULL; 43static int lineno = 1; 44static int errors = 0; 45static int debug = 0; 46 47int yyerror(const char *, ...); 48int yyparse(void); 49int kw_cmp(const void *, const void *); 50int lookup(char *); 51int lgetc(FILE *); 52int lungetc(int); 53int findeol(void); 54int yylex(void); 55 56TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 57struct sym { 58 TAILQ_ENTRY(sym) entries; 59 int used; 60 int persist; 61 char *nam; 62 char *val; 63}; 64 65int symset(const char *, const char *, int); 66int cmdline_symset(char *); 67char *symget(const char *); 68int atoul(char *, u_long *); 69struct ipsec_addr *host(const char *); 70struct ipsec_addr *copyhost(const struct ipsec_addr *); 71struct ipsec_rule *create_rule(u_int8_t, struct ipsec_addr *, struct 72 ipsec_addr *, struct ipsec_addr *, u_int8_t, 73 char *, char *, u_int16_t); 74struct ipsec_rule *reverse_rule(struct ipsec_rule *); 75 76typedef struct { 77 union { 78 u_int32_t number; 79 u_int8_t dir; 80 char *string; 81 int log; 82 u_int8_t protocol; 83 struct { 84 struct ipsec_addr *src; 85 struct ipsec_addr *dst; 86 } hosts; 87 struct ipsec_addr *peer; 88 struct ipsec_addr *host; 89 struct { 90 char *srcid; 91 char *dstid; 92 } ids; 93 char *id; 94 u_int16_t authtype; 95 } v; 96 int lineno; 97} YYSTYPE; 98 99%} 100 101%token FLOW FROM ESP AH IN PEER ON OUT TO SRCID DSTID RSA PSK 102%token ERROR 103%token <v.string> STRING 104%type <v.dir> dir 105%type <v.protocol> protocol 106%type <v.number> number 107%type <v.hosts> hosts 108%type <v.peer> peer 109%type <v.host> host 110%type <v.ids> ids 111%type <v.id> id 112%type <v.authtype> authtype 113%% 114 115grammar : /* empty */ 116 | grammar '\n' 117 | grammar flowrule '\n' 118 | grammar error '\n' { errors++; } 119 ; 120 121number : STRING { 122 unsigned long ulval; 123 124 if (atoul($1, &ulval) == -1) { 125 yyerror("%s is not a number", $1); 126 free($1); 127 YYERROR; 128 } else 129 $$ = ulval; 130 free($1); 131 } 132 133flowrule : FLOW ipsecrule { } 134 ; 135 136ipsecrule : protocol dir hosts peer ids authtype { 137 struct ipsec_rule *r; 138 139 r = create_rule($2, $3.src, $3.dst, $4, $1, $5.srcid, 140 $5.dstid, $6); 141 if (r == NULL) 142 YYERROR; 143 r->nr = ipsec->rule_nr++; 144 145 if (ipsecctl_add_rule(ipsec, r)) 146 errx(1, "esprule: ipsecctl_add_rule"); 147 148 /* Create and add reverse rule. */ 149 if ($2 == IPSEC_INOUT) { 150 r = reverse_rule(r); 151 r->nr = ipsec->rule_nr++; 152 153 if (ipsecctl_add_rule(ipsec, r)) 154 errx(1, "esprule: ipsecctl_add_rule"); 155 } 156 } 157 ; 158 159protocol : /* empty */ { $$ = IPSEC_ESP; } 160 | ESP { $$ = IPSEC_ESP; } 161 | AH { $$ = IPSEC_AH; } 162 ; 163 164dir : /* empty */ { $$ = IPSEC_INOUT; } 165 | IN { $$ = IPSEC_IN; } 166 | OUT { $$ = IPSEC_OUT; } 167 ; 168 169hosts : FROM host TO host { 170 $$.src = $2; 171 $$.dst = $4; 172 } 173 ; 174 175peer : /* empty */ { $$ = NULL; } 176 | PEER STRING { 177 if (($$ = host($2)) == NULL) { 178 free($2); 179 yyerror("could not parse host specification"); 180 YYERROR; 181 } 182 free($2); 183 } 184 ; 185 186host : STRING { 187 if (($$ = host($1)) == NULL) { 188 free($1); 189 yyerror("could not parse host specification"); 190 YYERROR; 191 } 192 free($1); 193 } 194 | STRING '/' number { 195 char *buf; 196 197 if (asprintf(&buf, "%s/%u", $1, $3) == -1) 198 err(1, "host: asprintf"); 199 free($1); 200 if (($$ = host(buf)) == NULL) { 201 free(buf); 202 yyerror("could not parse host specification"); 203 YYERROR; 204 } 205 free(buf); 206 } 207 ; 208 209ids : /* empty */ { 210 $$.srcid = NULL; 211 $$.dstid = NULL; 212 } 213 | SRCID id DSTID id { 214 $$.srcid = $2; 215 $$.dstid = $4; 216 } 217 | SRCID id { 218 $$.srcid = $2; 219 $$.dstid = NULL; 220 } 221 | DSTID id { 222 $$.srcid = NULL; 223 $$.dstid = $2; 224 } 225 ; 226 227id : STRING { $$ = $1; } 228 ; 229 230authtype : /* empty */ { $$ = 0; } 231 | RSA { $$ = AUTH_RSA; } 232 | PSK { $$ = AUTH_PSK; } 233 ; 234%% 235 236struct keywords { 237 const char *k_name; 238 int k_val; 239}; 240 241int 242yyerror(const char *fmt, ...) 243{ 244 va_list ap; 245 extern char *infile; 246 247 errors = 1; 248 va_start(ap, fmt); 249 fprintf(stderr, "%s: %d: ", infile, yyval.lineno); 250 vfprintf(stderr, fmt, ap); 251 fprintf(stderr, "\n"); 252 va_end(ap); 253 return (0); 254} 255 256int 257kw_cmp(const void *k, const void *e) 258{ 259 return (strcmp(k, ((const struct keywords *)e)->k_name)); 260} 261 262int 263lookup(char *s) 264{ 265 /* this has to be sorted always */ 266 static const struct keywords keywords[] = { 267 { "ah", AH}, 268 { "dstid", DSTID}, 269 { "esp", ESP}, 270 { "flow", FLOW}, 271 { "from", FROM}, 272 { "in", IN}, 273 { "out", OUT}, 274 { "peer", PEER}, 275 { "psk", PSK}, 276 { "rsa", RSA}, 277 { "srcid", SRCID}, 278 { "to", TO}, 279 }; 280 const struct keywords *p; 281 282 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 283 sizeof(keywords[0]), kw_cmp); 284 285 if (p) { 286 if (debug > 1) 287 fprintf(stderr, "%s: %d\n", s, p->k_val); 288 return (p->k_val); 289 } else { 290 if (debug > 1) 291 fprintf(stderr, "string: %s\n", s); 292 return (STRING); 293 } 294} 295 296#define MAXPUSHBACK 128 297 298char *parsebuf; 299int parseindex; 300char pushback_buffer[MAXPUSHBACK]; 301int pushback_index = 0; 302 303int 304lgetc(FILE *f) 305{ 306 int c, next; 307 308 if (parsebuf) { 309 /* Read character from the parsebuffer instead of input. */ 310 if (parseindex >= 0) { 311 c = parsebuf[parseindex++]; 312 if (c != '\0') 313 return (c); 314 parsebuf = NULL; 315 } else 316 parseindex++; 317 } 318 319 if (pushback_index) 320 return (pushback_buffer[--pushback_index]); 321 322 while ((c = getc(f)) == '\\') { 323 next = getc(f); 324 if (next != '\n') { 325 if (isspace(next)) 326 yyerror("whitespace after \\"); 327 ungetc(next, f); 328 break; 329 } 330 yylval.lineno = lineno; 331 lineno++; 332 } 333 if (c == '\t' || c == ' ') { 334 /* Compress blanks to a single space. */ 335 do { 336 c = getc(f); 337 } while (c == '\t' || c == ' '); 338 ungetc(c, f); 339 c = ' '; 340 } 341 342 return (c); 343} 344 345int 346lungetc(int c) 347{ 348 if (c == EOF) 349 return (EOF); 350 if (parsebuf) { 351 parseindex--; 352 if (parseindex >= 0) 353 return (c); 354 } 355 if (pushback_index < MAXPUSHBACK-1) 356 return (pushback_buffer[pushback_index++] = c); 357 else 358 return (EOF); 359} 360 361int 362findeol(void) 363{ 364 int c; 365 366 parsebuf = NULL; 367 pushback_index = 0; 368 369 /* skip to either EOF or the first real EOL */ 370 while (1) { 371 c = lgetc(fin); 372 if (c == '\n') { 373 lineno++; 374 break; 375 } 376 if (c == EOF) 377 break; 378 } 379 return (ERROR); 380} 381 382int 383yylex(void) 384{ 385 char buf[8096]; 386 char *p, *val; 387 int endc, c; 388 int token; 389 390top: 391 p = buf; 392 while ((c = lgetc(fin)) == ' ') 393 ; /* nothing */ 394 395 yylval.lineno = lineno; 396 if (c == '#') 397 while ((c = lgetc(fin)) != '\n' && c != EOF) 398 ; /* nothing */ 399 if (c == '$' && parsebuf == NULL) { 400 while (1) { 401 if ((c = lgetc(fin)) == EOF) 402 return (0); 403 404 if (p + 1 >= buf + sizeof(buf) - 1) { 405 yyerror("string too long"); 406 return (findeol()); 407 } 408 if (isalnum(c) || c == '_') { 409 *p++ = (char)c; 410 continue; 411 } 412 *p = '\0'; 413 lungetc(c); 414 break; 415 } 416 val = symget(buf); 417 if (val == NULL) { 418 yyerror("macro \"%s\" not defined", buf); 419 return (findeol()); 420 } 421 parsebuf = val; 422 parseindex = 0; 423 goto top; 424 } 425 426 switch (c) { 427 case '\'': 428 case '"': 429 endc = c; 430 while (1) { 431 if ((c = lgetc(fin)) == EOF) 432 return (0); 433 if (c == endc) { 434 *p = '\0'; 435 break; 436 } 437 if (c == '\n') { 438 lineno++; 439 continue; 440 } 441 if (p + 1 >= buf + sizeof(buf) - 1) { 442 yyerror("string too long"); 443 return (findeol()); 444 } 445 *p++ = (char)c; 446 } 447 yylval.v.string = strdup(buf); 448 if (yylval.v.string == NULL) 449 err(1, "yylex: strdup"); 450 return (STRING); 451 } 452 453#define allowed_in_string(x) \ 454 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 455 x != '{' && x != '}' && x != '<' && x != '>' && \ 456 x != '!' && x != '=' && x != '/' && x != '#' && \ 457 x != ',')) 458 459 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 460 do { 461 *p++ = c; 462 if ((unsigned)(p-buf) >= sizeof(buf)) { 463 yyerror("string too long"); 464 return (findeol()); 465 } 466 } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c))); 467 lungetc(c); 468 *p = '\0'; 469 if ((token = lookup(buf)) == STRING) 470 if ((yylval.v.string = strdup(buf)) == NULL) 471 err(1, "yylex: strdup"); 472 return (token); 473 } 474 if (c == '\n') { 475 yylval.lineno = lineno; 476 lineno++; 477 } 478 if (c == EOF) 479 return (0); 480 return (c); 481} 482 483int 484parse_rules(FILE *input, struct ipsecctl *ipsecx) 485{ 486 struct sym *sym, *next; 487 488 ipsec = ipsecx; 489 fin = input; 490 lineno = 1; 491 errors = 0; 492 493 yyparse(); 494 495 /* Free macros and check which have not been used. */ 496 for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { 497 next = TAILQ_NEXT(sym, entries); 498 free(sym->nam); 499 free(sym->val); 500 TAILQ_REMOVE(&symhead, sym, entries); 501 free(sym); 502 } 503 504 return (errors ? -1 : 0); 505} 506 507int 508symset(const char *nam, const char *val, int persist) 509{ 510 struct sym *sym; 511 512 for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); 513 sym = TAILQ_NEXT(sym, entries)) 514 ; /* nothing */ 515 516 if (sym != NULL) { 517 if (sym->persist == 1) 518 return (0); 519 else { 520 free(sym->nam); 521 free(sym->val); 522 TAILQ_REMOVE(&symhead, sym, entries); 523 free(sym); 524 } 525 } 526 if ((sym = calloc(1, sizeof(*sym))) == NULL) 527 return (-1); 528 529 sym->nam = strdup(nam); 530 if (sym->nam == NULL) { 531 free(sym); 532 return (-1); 533 } 534 sym->val = strdup(val); 535 if (sym->val == NULL) { 536 free(sym->nam); 537 free(sym); 538 return (-1); 539 } 540 sym->used = 0; 541 sym->persist = persist; 542 TAILQ_INSERT_TAIL(&symhead, sym, entries); 543 return (0); 544} 545 546int 547cmdline_symset(char *s) 548{ 549 char *sym, *val; 550 int ret; 551 size_t len; 552 553 if ((val = strrchr(s, '=')) == NULL) 554 return (-1); 555 556 len = strlen(s) - strlen(val) + 1; 557 if ((sym = malloc(len)) == NULL) 558 err(1, "cmdline_symset: malloc"); 559 560 strlcpy(sym, s, len); 561 562 ret = symset(sym, val + 1, 1); 563 free(sym); 564 565 return (ret); 566} 567 568char * 569symget(const char *nam) 570{ 571 struct sym *sym; 572 573 TAILQ_FOREACH(sym, &symhead, entries) 574 if (strcmp(nam, sym->nam) == 0) { 575 sym->used = 1; 576 return (sym->val); 577 } 578 return (NULL); 579} 580 581int 582atoul(char *s, u_long *ulvalp) 583{ 584 u_long ulval; 585 char *ep; 586 587 errno = 0; 588 ulval = strtoul(s, &ep, 0); 589 if (s[0] == '\0' || *ep != '\0') 590 return (-1); 591 if (errno == ERANGE && ulval == ULONG_MAX) 592 return (-1); 593 *ulvalp = ulval; 594 return (0); 595} 596 597struct ipsec_addr * 598host(const char *s) 599{ 600 struct ipsec_addr *ipa; 601 int i, bits = 32; 602 603 /* XXX for now only AF_INET. */ 604 605 ipa = calloc(1, sizeof(struct ipsec_addr)); 606 if (ipa == NULL) 607 err(1, "calloc"); 608 609 if (strrchr(s, '/') != NULL) { 610 bits = inet_net_pton(AF_INET, s, &ipa->v4, sizeof(ipa->v4)); 611 if (bits == -1 || bits > 32) { 612 free(ipa); 613 return(NULL); 614 } 615 } else { 616 if (inet_pton(AF_INET, s, &ipa->v4) != 1) { 617 free(ipa); 618 return NULL; 619 } 620 } 621 622 memset(&ipa->v4mask, 0, sizeof(ipa->v4mask)); 623 if (bits == 32) { 624 ipa->v4mask.mask32 = 0xffffffff; 625 ipa->netaddress = 0; 626 } else { 627 for (i = 31; i > 31 - bits; i--) 628 ipa->v4mask.mask32 |= (1 << i); 629 ipa->v4mask.mask32 = htonl(ipa->v4mask.mask32); 630 ipa->netaddress = 1; 631 } 632 633 ipa->af = AF_INET; 634 635 return ipa; 636} 637 638struct ipsec_addr * 639copyhost(const struct ipsec_addr *src) 640{ 641 struct ipsec_addr *dst; 642 643 dst = calloc(1, sizeof(struct ipsec_addr)); 644 if (dst == NULL) 645 err(1, "calloc"); 646 647 memcpy(dst, src, sizeof(struct ipsec_addr)); 648 return dst; 649} 650 651struct ipsec_rule * 652create_rule(u_int8_t dir, struct ipsec_addr *src, struct ipsec_addr *dst, 653 struct ipsec_addr *peer, u_int8_t proto, char *srcid, char *dstid, 654 u_int16_t authtype) 655{ 656 struct ipsec_rule *r; 657 658 r = calloc(1, sizeof(struct ipsec_rule)); 659 if (r == NULL) 660 err(1, "calloc"); 661 662 if (dir == IPSEC_INOUT) 663 r->direction = IPSEC_OUT; 664 else 665 r->direction = dir; 666 667 r->src = src; 668 r->dst = dst; 669 670 if (peer == NULL) { 671 /* Set peer to remote host. Must be a host address. */ 672 if (r->direction == IPSEC_IN) { 673 if (r->src->netaddress) { 674 yyerror("no peer specified"); 675 goto errout; 676 } 677 r->peer = copyhost(r->src); 678 } else { 679 if (r->dst->netaddress) { 680 yyerror("no peer specified"); 681 goto errout; 682 } 683 r->peer = copyhost(r->dst); 684 } 685 } else 686 r->peer = peer; 687 688 r->proto = proto; 689 r->auth.srcid = srcid; 690 r->auth.dstid = dstid; 691 r->auth.idtype = ID_FQDN; /* XXX For now only FQDN. */ 692#ifdef notyet 693 r->auth.type = authtype; 694#endif 695 696 return r; 697 698errout: 699 free(r); 700 if (srcid) 701 free(srcid); 702 if (dstid) 703 free(dstid); 704 free(src); 705 free(dst); 706 707 return NULL; 708} 709 710struct ipsec_rule * 711reverse_rule(struct ipsec_rule *rule) 712{ 713 struct ipsec_rule *reverse; 714 715 reverse = calloc(1, sizeof(struct ipsec_rule)); 716 if (reverse == NULL) 717 err(1, "calloc"); 718 719 if (rule->direction == (u_int8_t)IPSEC_OUT) 720 reverse->direction = (u_int8_t)IPSEC_IN; 721 else 722 reverse->direction = (u_int8_t)IPSEC_OUT; 723 724 reverse->src = copyhost(rule->dst); 725 reverse->dst = copyhost(rule->src); 726 reverse->peer = copyhost(rule->peer); 727 reverse->proto = (u_int8_t)rule->proto; 728 729 if (rule->auth.dstid && (reverse->auth.srcid = 730 strdup(rule->auth.dstid)) == NULL) 731 err(1, "strdup"); 732 if (rule->auth.srcid && (reverse->auth.dstid = 733 strdup(rule->auth.srcid)) == NULL) 734 err(1, "strdup"); 735 reverse->auth.idtype = rule->auth.idtype; 736 reverse->auth.type = rule->auth.type; 737 738 return reverse; 739} 740