1 /*
   2  * Copyright (C) 2002-2008 by Darren Reed.
   3  *
   4  * See the IPFILTER.LICENCE file for details on licencing.
   5  *
   6  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   7  * Use is subject to license terms.
   8  * Copyright 2019 Joyent, Inc.
   9  */
  10 
  11 #include <ctype.h>
  12 #include "ipf.h"
  13 #ifdef  IPFILTER_SCAN
  14 # include "netinet/ip_scan.h"
  15 #endif
  16 #include <sys/ioctl.h>
  17 #include <syslog.h>
  18 #include <uuid/uuid.h>
  19 #ifdef  TEST_LEXER
  20 # define        NO_YACC
  21 union   {
  22         int             num;
  23         char            *str;
  24         struct in_addr  ipa;
  25         i6addr_t        ip6;
  26         uuid_t          uuid;
  27 } yylval;
  28 #endif
  29 #include "lexer.h"
  30 #include "y.tab.h"
  31 
  32 FILE *yyin;
  33 
  34 #define ishex(c)        (ISDIGIT(c) || ((c) >= 'a' && (c) <= 'f') || \
  35                          ((c) >= 'A' && (c) <= 'F'))
  36 #define TOOLONG         -3
  37 
  38 extern int      string_start;
  39 extern int      string_end;
  40 extern char     *string_val;
  41 extern int      pos;
  42 extern int      yydebug;
  43 
  44 char            *yystr = NULL;
  45 int             yytext[YYBUFSIZ+1];
  46 char            yychars[YYBUFSIZ+1];
  47 int             yylineNum = 1;
  48 int             yypos = 0;
  49 int             yylast = -1;
  50 int             yyexpectaddr = 0;
  51 int             yybreakondot = 0;
  52 int             yyvarnext = 0;
  53 int             yytokentype = 0;
  54 wordtab_t       *yywordtab = NULL;
  55 int             yysavedepth = 0;
  56 wordtab_t       *yysavewords[30];
  57 
  58 
  59 static  wordtab_t       *yyfindkey __P((char *));
  60 static  int             yygetc __P((int));
  61 static  void            yyunputc __P((int));
  62 static  int             yyswallow __P((int));
  63 static  char            *yytexttostr __P((int, int));
  64 static  void            yystrtotext __P((char *));
  65 static  char            *yytexttochar __P((void));
  66 
  67 static int yygetc(docont)
  68 int docont;
  69 {
  70         int c;
  71 
  72         if (yypos < yylast) {
  73                 c = yytext[yypos++];
  74                 if (c == '\n')
  75                         yylineNum++;
  76                 return c;
  77         }
  78 
  79         if (yypos == YYBUFSIZ)
  80                 return TOOLONG;
  81 
  82         if (pos >= string_start && pos <= string_end) {
  83                 c = string_val[pos - string_start];
  84                 yypos++;
  85         } else {
  86                 c = fgetc(yyin);
  87                 if (docont && (c == '\\')) {
  88                         c = fgetc(yyin);
  89                         if (c == '\n') {
  90                                 yylineNum++;
  91                                 c = fgetc(yyin);
  92                         }
  93                 }
  94         }
  95         if (c == '\n')
  96                 yylineNum++;
  97         yytext[yypos++] = c;
  98         yylast = yypos;
  99         yytext[yypos] = '\0';
 100 
 101         return c;
 102 }
 103 
 104 
 105 static void yyunputc(c)
 106 int c;
 107 {
 108         if (c == '\n')
 109                 yylineNum--;
 110         yytext[--yypos] = c;
 111 }
 112 
 113 
 114 static int yyswallow(last)
 115 int last;
 116 {
 117         int c;
 118 
 119         while (((c = yygetc(0)) > '\0') && (c != last))
 120                 ;
 121 
 122         if (c != EOF)
 123                 yyunputc(c);
 124         if (c == last)
 125                 return 0;
 126         return -1;
 127 }
 128 
 129 
 130 static char *yytexttochar()
 131 {
 132         int i;
 133 
 134         for (i = 0; i < yypos; i++)
 135                 yychars[i] = (char)(yytext[i] & 0xff);
 136         yychars[i] = '\0';
 137         return yychars;
 138 }
 139 
 140 
 141 static void yystrtotext(str)
 142 char *str;
 143 {
 144         int len;
 145         char *s;
 146 
 147         len = strlen(str);
 148         if (len > YYBUFSIZ)
 149                 len = YYBUFSIZ;
 150 
 151         for (s = str; *s != '\0' && len > 0; s++, len--)
 152                 yytext[yylast++] = *s;
 153         yytext[yylast] = '\0';
 154 }
 155 
 156 
 157 static char *yytexttostr(offset, max)
 158 int offset, max;
 159 {
 160         char *str;
 161         int i;
 162 
 163         if ((yytext[offset] == '\'' || yytext[offset] == '"') &&
 164             (yytext[offset] == yytext[offset + max - 1])) {
 165                 offset++;
 166                 max--;
 167         }
 168 
 169         if (max > yylast)
 170                 max = yylast;
 171         str = malloc(max + 1);
 172         if (str != NULL) {
 173                 for (i = offset; i < max; i++)
 174                         str[i - offset] = (char)(yytext[i] & 0xff);
 175                 str[i - offset] = '\0';
 176         }
 177         return str;
 178 }
 179 
 180 
 181 int yylex()
 182 {
 183         int c, n, isbuilding, rval, lnext, nokey = 0;
 184         char *name;
 185 
 186         isbuilding = 0;
 187         lnext = 0;
 188         rval = 0;
 189 
 190         if (yystr != NULL) {
 191                 free(yystr);
 192                 yystr = NULL;
 193         }
 194 
 195 nextchar:
 196         c = yygetc(0);
 197         if (yydebug > 1)
 198                 printf("yygetc = (%x) %c [%*.*s]\n", c, c, yypos, yypos,
 199                        yytexttochar());
 200 
 201         switch (c)
 202         {
 203         case '\n' :
 204                 lnext = 0;
 205                 nokey = 0;
 206                 /* FALLTHROUGH */
 207         case '\t' :
 208         case '\r' :
 209         case ' ' :
 210                 if (isbuilding == 1) {
 211                         yyunputc(c);
 212                         goto done;
 213                 }
 214                 if (yylast > yypos) {
 215                         bcopy(yytext + yypos, yytext,
 216                               sizeof(yytext[0]) * (yylast - yypos + 1));
 217                 }
 218                 yylast -= yypos;
 219                 yypos = 0;
 220                 lnext = 0;
 221                 nokey = 0;
 222                 goto nextchar;
 223 
 224         case '\\' :
 225                 if (lnext == 0) {
 226                         lnext = 1;
 227                         if (yylast == yypos) {
 228                                 yylast--;
 229                                 yypos--;
 230                         } else
 231                                 yypos--;
 232                         if (yypos == 0)
 233                                 nokey = 1;
 234                         goto nextchar;
 235                 }
 236                 break;
 237         }
 238 
 239         if (lnext == 1) {
 240                 lnext = 0;
 241                 if ((isbuilding == 0) && !ISALNUM(c)) {
 242                         return c;
 243                 }
 244                 goto nextchar;
 245         }
 246 
 247         switch (c)
 248         {
 249         case '#' :
 250                 if (isbuilding == 1) {
 251                         yyunputc(c);
 252                         goto done;
 253                 }
 254                 yyswallow('\n');
 255                 rval = YY_COMMENT;
 256                 goto done;
 257 
 258         case '$' :
 259                 if (isbuilding == 1) {
 260                         yyunputc(c);
 261                         goto done;
 262                 }
 263                 n = yygetc(0);
 264                 if (n == '{') {
 265                         if (yyswallow('}') == -1) {
 266                                 rval = -2;
 267                                 goto done;
 268                         }
 269                         (void) yygetc(0);
 270                 } else {
 271                         if (!ISALPHA(n)) {
 272                                 yyunputc(n);
 273                                 break;
 274                         }
 275                         do {
 276                                 n = yygetc(1);
 277                         } while (ISALPHA(n) || ISDIGIT(n) || n == '_');
 278                         yyunputc(n);
 279                 }
 280 
 281                 name = yytexttostr(1, yypos);           /* skip $ */
 282 
 283                 if (name != NULL) {
 284                         string_val = get_variable(name, NULL, yylineNum);
 285                         free(name);
 286                         if (string_val != NULL) {
 287                                 name = yytexttostr(yypos, yylast);
 288                                 if (name != NULL) {
 289                                         yypos = 0;
 290                                         yylast = 0;
 291                                         yystrtotext(string_val);
 292                                         yystrtotext(name);
 293                                         free(string_val);
 294                                         free(name);
 295                                         goto nextchar;
 296                                 }
 297                                 free(string_val);
 298                         }
 299                 }
 300                 break;
 301 
 302         case '\'':
 303         case '"' :
 304                 if (isbuilding == 1) {
 305                         goto done;
 306                 }
 307                 do {
 308                         n = yygetc(1);
 309                         if (n == EOF || n == TOOLONG) {
 310                                 rval = -2;
 311                                 goto done;
 312                         }
 313                         if (n == '\n') {
 314                                 yyunputc(' ');
 315                                 yypos++;
 316                         }
 317                 } while (n != c);
 318                 rval = YY_STR;
 319                 goto done;
 320                 /* NOTREACHED */
 321 
 322         case EOF :
 323                 yylineNum = 1;
 324                 yypos = 0;
 325                 yylast = -1;
 326                 yyexpectaddr = 0;
 327                 yybreakondot = 0;
 328                 yyvarnext = 0;
 329                 yytokentype = 0;
 330                 return 0;
 331         }
 332 
 333         if (strchr("=,/;{}()@", c) != NULL) {
 334                 if (isbuilding == 1) {
 335                         yyunputc(c);
 336                         goto done;
 337                 }
 338                 rval = c;
 339                 goto done;
 340         } else if (c == '.') {
 341                 if (isbuilding == 0) {
 342                         rval = c;
 343                         goto done;
 344                 }
 345                 if (yybreakondot != 0) {
 346                         yyunputc(c);
 347                         goto done;
 348                 }
 349         }
 350 
 351         switch (c)
 352         {
 353         case '-' :
 354                 if (yyexpectaddr)
 355                         break;
 356                 if (isbuilding == 1)
 357                         break;
 358                 n = yygetc(0);
 359                 if (n == '>') {
 360                         isbuilding = 1;
 361                         goto done;
 362                 }
 363                 yyunputc(n);
 364                 rval = '-';
 365                 goto done;
 366 
 367         case '!' :
 368                 if (isbuilding == 1) {
 369                         yyunputc(c);
 370                         goto done;
 371                 }
 372                 n = yygetc(0);
 373                 if (n == '=') {
 374                         rval = YY_CMP_NE;
 375                         goto done;
 376                 }
 377                 yyunputc(n);
 378                 rval = '!';
 379                 goto done;
 380 
 381         case '<' :
 382                 if (yyexpectaddr)
 383                         break;
 384                 if (isbuilding == 1) {
 385                         yyunputc(c);
 386                         goto done;
 387                 }
 388                 n = yygetc(0);
 389                 if (n == '=') {
 390                         rval = YY_CMP_LE;
 391                         goto done;
 392                 }
 393                 if (n == '>') {
 394                         rval = YY_RANGE_OUT;
 395                         goto done;
 396                 }
 397                 yyunputc(n);
 398                 rval = YY_CMP_LT;
 399                 goto done;
 400 
 401         case '>' :
 402                 if (yyexpectaddr)
 403                         break;
 404                 if (isbuilding == 1) {
 405                         yyunputc(c);
 406                         goto done;
 407                 }
 408                 n = yygetc(0);
 409                 if (n == '=') {
 410                         rval = YY_CMP_GE;
 411                         goto done;
 412                 }
 413                 if (n == '<') {
 414                         rval = YY_RANGE_IN;
 415                         goto done;
 416                 }
 417                 yyunputc(n);
 418                 rval = YY_CMP_GT;
 419                 goto done;
 420         }
 421 
 422         /*
 423          * Now for the reason this is here...IPv6 address parsing.
 424          * The longest string we can expect is of this form:
 425          * 0000:0000:0000:0000:0000:0000:000.000.000.000
 426          * not:
 427          * 0000:0000:0000:0000:0000:0000:0000:0000
 428          */
 429 #ifdef  USE_INET6
 430         if (isbuilding == 0 && (ishex(c) || c == ':')) {
 431                 char ipv6buf[45 + 1], *s, oc;
 432                 int start;
 433 
 434                 start = yypos;
 435                 s = ipv6buf;
 436                 oc = c;
 437 
 438                 /*
 439                  * Perhaps we should implement stricter controls on what we
 440                  * swallow up here, but surely it would just be duplicating
 441                  * the code in inet_pton() anyway.
 442                  */
 443                 do {
 444                         *s++ = c;
 445                         c = yygetc(1);
 446                 } while ((ishex(c) || c == ':' || c == '.') &&
 447                          (s - ipv6buf < 46));
 448                 yyunputc(c);
 449                 *s = '\0';
 450 
 451                 if (inet_pton(AF_INET6, ipv6buf, &yylval.ip6) == 1) {
 452                         rval = YY_IPV6;
 453                         yyexpectaddr = 0;
 454                         goto done;
 455                 }
 456                 yypos = start;
 457                 c = oc;
 458         }
 459 #endif
 460 
 461         /*
 462          * UUID: 2426e38c-9f63-c0b8-cfd5-9aaeaf992d42 or uppercase
 463          */
 464         if (isbuilding == 0 && (ishex(c) || c == '-')) {
 465                 char uuidbuf[UUID_PRINTABLE_STRING_LENGTH], *s, oc;
 466                 int start;
 467 
 468                 start = yypos;
 469                 s = uuidbuf;
 470                 oc = c;
 471 
 472                 /*
 473                  * Don't worry about exact position of hexdigits and hyphens
 474                  * because uuid_parse() will provide the sanity check.
 475                  */
 476                 do {
 477                         *s++ = c;
 478                         c = yygetc(1);
 479                 } while ((ishex(c) || c == '-') &&
 480                     (s - uuidbuf < sizeof (uuidbuf)));
 481                 yyunputc(c);
 482                 *s = '\0';
 483 
 484                 if (uuid_parse(uuidbuf, yylval.uuid) == 0) {
 485                         rval = YY_UUID;
 486                         yyexpectaddr = 0;
 487                         goto done;
 488                 }
 489                 yypos = start;
 490                 c = oc;
 491         }
 492 
 493 
 494         if (c == ':') {
 495                 if (isbuilding == 1) {
 496                         yyunputc(c);
 497                         goto done;
 498                 }
 499                 rval = ':';
 500                 goto done;
 501         }
 502 
 503         if (isbuilding == 0 && c == '0') {
 504                 n = yygetc(0);
 505                 if (n == 'x') {
 506                         do {
 507                                 n = yygetc(1);
 508                         } while (ishex(n));
 509                         yyunputc(n);
 510                         rval = YY_HEX;
 511                         goto done;
 512                 }
 513                 yyunputc(n);
 514         }
 515 
 516         /*
 517          * No negative numbers with leading - sign..
 518          */
 519         if (isbuilding == 0 && ISDIGIT(c)) {
 520                 do {
 521                         n = yygetc(1);
 522                 } while (ISDIGIT(n));
 523                 yyunputc(n);
 524                 rval = YY_NUMBER;
 525                 goto done;
 526         }
 527 
 528         isbuilding = 1;
 529         goto nextchar;
 530 
 531 done:
 532         yystr = yytexttostr(0, yypos);
 533 
 534         if (yydebug)
 535                 printf("isbuilding %d yyvarnext %d nokey %d\n",
 536                        isbuilding, yyvarnext, nokey);
 537         if (isbuilding == 1) {
 538                 wordtab_t *w;
 539 
 540                 w = NULL;
 541                 isbuilding = 0;
 542 
 543                 if ((yyvarnext == 0) && (nokey == 0)) {
 544                         w = yyfindkey(yystr);
 545                         if (w == NULL && yywordtab != NULL) {
 546                                 yyresetdict();
 547                                 w = yyfindkey(yystr);
 548                         }
 549                 } else
 550                         yyvarnext = 0;
 551                 if (w != NULL)
 552                         rval = w->w_value;
 553                 else
 554                         rval = YY_STR;
 555         }
 556 
 557         if (rval == YY_STR && yysavedepth > 0)
 558                 yyresetdict();
 559 
 560         yytokentype = rval;
 561 
 562         if (yydebug)
 563                 printf("lexed(%s) [%d,%d,%d] => %d @%d\n", yystr, string_start,
 564                         string_end, pos, rval, yysavedepth);
 565 
 566         switch (rval)
 567         {
 568         case YY_NUMBER :
 569                 sscanf(yystr, "%u", &yylval.num);
 570                 break;
 571 
 572         case YY_HEX :
 573                 sscanf(yystr, "0x%x", (u_int *)&yylval.num);
 574                 break;
 575 
 576         case YY_STR :
 577                 yylval.str = strdup(yystr);
 578                 break;
 579 
 580         default :
 581                 break;
 582         }
 583 
 584         if (yylast > 0) {
 585                 bcopy(yytext + yypos, yytext,
 586                       sizeof(yytext[0]) * (yylast - yypos + 1));
 587                 yylast -= yypos;
 588                 yypos = 0;
 589         }
 590 
 591         return rval;
 592 }
 593 
 594 
 595 static wordtab_t *yyfindkey(key)
 596 char *key;
 597 {
 598         wordtab_t *w;
 599 
 600         if (yywordtab == NULL)
 601                 return NULL;
 602 
 603         for (w = yywordtab; w->w_word != 0; w++)
 604                 if (strcasecmp(key, w->w_word) == 0)
 605                         return w;
 606         return NULL;
 607 }
 608 
 609 
 610 char *yykeytostr(num)
 611 int num;
 612 {
 613         wordtab_t *w;
 614 
 615         if (yywordtab == NULL)
 616                 return "<unknown>";
 617 
 618         for (w = yywordtab; w->w_word; w++)
 619                 if (w->w_value == num)
 620                         return w->w_word;
 621         return "<unknown>";
 622 }
 623 
 624 
 625 wordtab_t *yysettab(words)
 626 wordtab_t *words;
 627 {
 628         wordtab_t *save;
 629 
 630         save = yywordtab;
 631         yywordtab = words;
 632         return save;
 633 }
 634 
 635 
 636 void yyerror(msg)
 637 char *msg;
 638 {
 639         char *txt, letter[2];
 640         int freetxt = 0;
 641 
 642         if (yytokentype < 256) {
 643                 letter[0] = yytokentype;
 644                 letter[1] = '\0';
 645                 txt =  letter;
 646         } else if (yytokentype == YY_STR || yytokentype == YY_HEX ||
 647                    yytokentype == YY_NUMBER) {
 648                 if (yystr == NULL) {
 649                         txt = yytexttostr(yypos, YYBUFSIZ);
 650                         if (txt == NULL) {
 651                                 fprintf(stderr, "sorry, out of memory,"
 652                                         " bailing out\n");
 653                                 exit(1);
 654                         }
 655                         freetxt = 1;
 656                 } else
 657                         txt = yystr;
 658         } else {
 659                 txt = yykeytostr(yytokentype);
 660                 if (txt == NULL) {
 661                         fprintf(stderr, "sorry, out of memory,"
 662                                 " bailing out\n");
 663                         exit(1);
 664                 }
 665         }
 666         fprintf(stderr, "%s error at \"%s\", line %d\n", msg, txt, yylineNum);
 667         if (freetxt == 1)
 668                 free(txt);
 669         exit(1);
 670 }
 671 
 672 
 673 void yysetdict(newdict)
 674 wordtab_t *newdict;
 675 {
 676         if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) {
 677                 fprintf(stderr, "%d: at maximum dictionary depth\n",
 678                         yylineNum);
 679                 return;
 680         }
 681 
 682         yysavewords[yysavedepth++] = yysettab(newdict);
 683         if (yydebug)
 684                 printf("yysavedepth++ => %d\n", yysavedepth);
 685 }
 686 
 687 void yyresetdict()
 688 {
 689         if (yydebug)
 690                 printf("yyresetdict(%d)\n", yysavedepth);
 691         if (yysavedepth > 0) {
 692                 yysettab(yysavewords[--yysavedepth]);
 693                 if (yydebug)
 694                         printf("yysavedepth-- => %d\n", yysavedepth);
 695         }
 696 }
 697 
 698 
 699 
 700 #ifdef  TEST_LEXER
 701 int main(argc, argv)
 702 int argc;
 703 char *argv[];
 704 {
 705         int n;
 706 
 707         yyin = stdin;
 708 
 709         while ((n = yylex()) != 0)
 710                 printf("%d.n = %d [%s] %d %d\n",
 711                         yylineNum, n, yystr, yypos, yylast);
 712 }
 713 #endif