Annotation of src/usr.bin/sudo/parse.yacc, Revision 1.1
1.1 ! millert 1: %{
! 2: /*
! 3: * Copyright (c) 1996, 1998, 1999 Todd C. Miller <Todd.Miller@courtesan.com>
! 4: * All rights reserved.
! 5: *
! 6: * This code is derived from software contributed by Chris Jepeway
! 7: * <jepeway@cs.utk.edu>
! 8: *
! 9: * Redistribution and use in source and binary forms, with or without
! 10: * modification, are permitted provided that the following conditions
! 11: * are met:
! 12: *
! 13: * 1. Redistributions of source code must retain the above copyright
! 14: * notice, this list of conditions and the following disclaimer.
! 15: *
! 16: * 2. Redistributions in binary form must reproduce the above copyright
! 17: * notice, this list of conditions and the following disclaimer in the
! 18: * documentation and/or other materials provided with the distribution.
! 19: *
! 20: * 3. The name of the author may not be used to endorse or promote products
! 21: * derived from this software without specific prior written permission.
! 22: *
! 23: * 4. Products derived from this software may not be called "Sudo" nor
! 24: * may "Sudo" appear in their names without specific prior written
! 25: * permission from the author.
! 26: *
! 27: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
! 28: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
! 29: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
! 30: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
! 31: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
! 32: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
! 33: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
! 34: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
! 35: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! 36: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 37: */
! 38:
! 39: /*
! 40: * XXX - the whole opFOO naming thing is somewhat bogus.
! 41: *
! 42: * XXX - the way things are stored for printmatches is stupid,
! 43: * they should be stored as elements in an array and then
! 44: * list_matches() can format things the way it wants.
! 45: */
! 46:
! 47: #include "config.h"
! 48: #include <stdio.h>
! 49: #ifdef STDC_HEADERS
! 50: #include <stdlib.h>
! 51: #endif /* STDC_HEADERS */
! 52: #ifdef HAVE_UNISTD_H
! 53: #include <unistd.h>
! 54: #endif /* HAVE_UNISTD_H */
! 55: #include <pwd.h>
! 56: #include <sys/types.h>
! 57: #include <sys/param.h>
! 58: #ifdef HAVE_STRING_H
! 59: #include <string.h>
! 60: #endif /* HAVE_STRING_H */
! 61: #ifdef HAVE_STRINGS_H
! 62: #include <strings.h>
! 63: #endif /* HAVE_STRINGS_H */
! 64: #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
! 65: #include <malloc.h>
! 66: #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
! 67: #if defined(YYBISON) && defined(HAVE_ALLOCA_H) && !defined(__GNUC__)
! 68: #include <alloca.h>
! 69: #endif /* YYBISON && HAVE_ALLOCA_H && !__GNUC__ */
! 70: #ifdef HAVE_LSEARCH
! 71: #include <search.h>
! 72: #endif /* HAVE_LSEARCH */
! 73:
! 74: #include "sudo.h"
! 75: #include "parse.h"
! 76:
! 77: #ifndef HAVE_LSEARCH
! 78: #include "emul/search.h"
! 79: #endif /* HAVE_LSEARCH */
! 80:
! 81: #ifndef lint
! 82: static const char rcsid[] = "$Sudo: parse.yacc,v 1.166 1999/10/07 21:20:57 millert Exp $";
! 83: #endif /* lint */
! 84:
! 85: /*
! 86: * Globals
! 87: */
! 88: extern int sudolineno, parse_error;
! 89: int errorlineno = -1;
! 90: int clearaliases = TRUE;
! 91: int printmatches = FALSE;
! 92: int pedantic = FALSE;
! 93:
! 94: /*
! 95: * Alias types
! 96: */
! 97: #define HOST_ALIAS 1
! 98: #define CMND_ALIAS 2
! 99: #define USER_ALIAS 3
! 100: #define RUNAS_ALIAS 4
! 101:
! 102: /*
! 103: * The matching stack, initial space allocated in init_parser().
! 104: */
! 105: struct matchstack *match;
! 106: int top = 0, stacksize = 0;
! 107:
! 108: #define push \
! 109: do { \
! 110: if (top >= stacksize) { \
! 111: while ((stacksize += STACKINCREMENT) < top); \
! 112: match = (struct matchstack *) erealloc(match, sizeof(struct matchstack) * stacksize); \
! 113: } \
! 114: match[top].user = -1; \
! 115: match[top].cmnd = -1; \
! 116: match[top].host = -1; \
! 117: match[top].runas = -1; \
! 118: match[top].nopass = def_flag(I_AUTHENTICATE) ? -1 : TRUE; \
! 119: top++; \
! 120: } while (0)
! 121:
! 122: #define pushcp \
! 123: do { \
! 124: if (top >= stacksize) { \
! 125: while ((stacksize += STACKINCREMENT) < top); \
! 126: match = (struct matchstack *) erealloc(match, sizeof(struct matchstack) * stacksize); \
! 127: } \
! 128: match[top].user = match[top-1].user; \
! 129: match[top].cmnd = match[top-1].cmnd; \
! 130: match[top].host = match[top-1].host; \
! 131: match[top].runas = match[top-1].runas; \
! 132: match[top].nopass = match[top-1].nopass; \
! 133: top++; \
! 134: } while (0)
! 135:
! 136: #define pop \
! 137: { \
! 138: if (top == 0) \
! 139: yyerror("matching stack underflow"); \
! 140: else \
! 141: top--; \
! 142: }
! 143:
! 144: /*
! 145: * Shortcuts for append()
! 146: */
! 147: #define append_cmnd(s, p) append(s, &cm_list[cm_list_len].cmnd, \
! 148: &cm_list[cm_list_len].cmnd_len, &cm_list[cm_list_len].cmnd_size, p)
! 149:
! 150: #define append_runas(s, p) append(s, &cm_list[cm_list_len].runas, \
! 151: &cm_list[cm_list_len].runas_len, &cm_list[cm_list_len].runas_size, p)
! 152:
! 153: #define append_entries(s, p) append(s, &ga_list[ga_list_len-1].entries, \
! 154: &ga_list[ga_list_len-1].entries_len, \
! 155: &ga_list[ga_list_len-1].entries_size, p)
! 156:
! 157: /*
! 158: * The stack for printmatches. A list of allowed commands for the user.
! 159: */
! 160: static struct command_match *cm_list = NULL;
! 161: static size_t cm_list_len = 0, cm_list_size = 0;
! 162:
! 163: /*
! 164: * List of Cmnd_Aliases and expansions for `sudo -l'
! 165: */
! 166: static int in_alias = FALSE;
! 167: static size_t ga_list_len = 0, ga_list_size = 0;
! 168: static struct generic_alias *ga_list = NULL;
! 169:
! 170: /*
! 171: * Does this Defaults list pertain to this user?
! 172: */
! 173: static int defaults_matches = 0;
! 174:
! 175: /*
! 176: * Local protoypes
! 177: */
! 178: static int add_alias __P((char *, int, int));
! 179: static void append __P((char *, char **, size_t *, size_t *, char *));
! 180: static void expand_ga_list __P((void));
! 181: static void expand_match_list __P((void));
! 182: static aliasinfo *find_alias __P((char *, int));
! 183: static int more_aliases __P((void));
! 184: void init_parser __P((void));
! 185: void yyerror __P((char *));
! 186:
! 187: void
! 188: yyerror(s)
! 189: char *s;
! 190: {
! 191: /* Save the line the first error occured on. */
! 192: if (errorlineno == -1)
! 193: errorlineno = sudolineno ? sudolineno - 1 : 0;
! 194: if (s) {
! 195: #ifndef TRACELEXER
! 196: (void) fprintf(stderr, ">>> sudoers file: %s, line %d <<<\n", s,
! 197: sudolineno ? sudolineno - 1 : 0);
! 198: #else
! 199: (void) fprintf(stderr, "<*> ");
! 200: #endif
! 201: }
! 202: parse_error = TRUE;
! 203: }
! 204: %}
! 205:
! 206: %union {
! 207: char *string;
! 208: int BOOLEAN;
! 209: struct sudo_command command;
! 210: int tok;
! 211: }
! 212:
! 213: %start file /* special start symbol */
! 214: %token <command> COMMAND /* absolute pathname w/ optional args */
! 215: %token <string> ALIAS /* an UPPERCASE alias name */
! 216: %token <string> NTWKADDR /* w.x.y.z */
! 217: %token <string> FQHOST /* foo.bar.com */
! 218: %token <string> NETGROUP /* a netgroup (+NAME) */
! 219: %token <string> USERGROUP /* a usergroup (%NAME) */
! 220: %token <string> WORD /* a word */
! 221: %token <tok> DEFAULTS /* Defaults entry */
! 222: %token <tok> DEFAULTS_HOST /* Host-specific defaults entry */
! 223: %token <tok> DEFAULTS_USER /* User-specific defaults entry */
! 224: %token <tok> RUNAS /* ( runas_list ) */
! 225: %token <tok> NOPASSWD /* no passwd req for command */
! 226: %token <tok> PASSWD /* passwd req for command (default) */
! 227: %token <tok> ALL /* ALL keyword */
! 228: %token <tok> COMMENT /* comment and/or carriage return */
! 229: %token <tok> HOSTALIAS /* Host_Alias keyword */
! 230: %token <tok> CMNDALIAS /* Cmnd_Alias keyword */
! 231: %token <tok> USERALIAS /* User_Alias keyword */
! 232: %token <tok> RUNASALIAS /* Runas_Alias keyword */
! 233: %token <tok> ':' '=' ',' '!' /* union member tokens */
! 234: %token <tok> ERROR
! 235:
! 236: /*
! 237: * NOTE: these are not true booleans as there are actually 3 possible values:
! 238: * 1) TRUE (positive match)
! 239: * 0) FALSE (negative match due to a '!' somewhere)
! 240: * -1) No match (don't change the value of *_matches)
! 241: */
! 242: %type <BOOLEAN> cmnd
! 243: %type <BOOLEAN> host
! 244: %type <BOOLEAN> runasuser
! 245: %type <BOOLEAN> user
! 246:
! 247: %%
! 248:
! 249: file : entry
! 250: | file entry
! 251: ;
! 252:
! 253: entry : COMMENT
! 254: { ; }
! 255: | error COMMENT
! 256: { yyerrok; }
! 257: | { push; } userlist privileges {
! 258: while (top && user_matches != TRUE)
! 259: pop;
! 260: }
! 261: | USERALIAS useraliases
! 262: { ; }
! 263: | HOSTALIAS hostaliases
! 264: { ; }
! 265: | CMNDALIAS cmndaliases
! 266: { ; }
! 267: | RUNASALIAS runasaliases
! 268: { ; }
! 269: | defaults_line
! 270: { ; }
! 271: ;
! 272:
! 273: defaults_line : defaults_type defaults_list
! 274:
! 275: defaults_type : DEFAULTS {
! 276: defaults_matches = TRUE;
! 277: }
! 278: | DEFAULTS_USER { push; } userlist {
! 279: defaults_matches = user_matches;
! 280: pop;
! 281: }
! 282: | DEFAULTS_HOST { push; } hostlist {
! 283: defaults_matches = host_matches;
! 284: pop;
! 285: }
! 286: ;
! 287:
! 288: defaults_list : defaults_entry
! 289: | defaults_entry ',' defaults_list
! 290:
! 291: defaults_entry : WORD {
! 292: if (defaults_matches && !set_default($1, NULL, 1)) {
! 293: yyerror(NULL);
! 294: YYERROR;
! 295: }
! 296: free($1);
! 297: }
! 298: | '!' WORD {
! 299: if (defaults_matches && !set_default($2, NULL, 0)) {
! 300: yyerror(NULL);
! 301: YYERROR;
! 302: }
! 303: free($2);
! 304: }
! 305: | WORD '=' WORD {
! 306: /* XXX - need to support quoted values */
! 307: if (defaults_matches && !set_default($1, $3, 1)) {
! 308: yyerror(NULL);
! 309: YYERROR;
! 310: }
! 311: free($1);
! 312: free($3);
! 313: }
! 314:
! 315: privileges : privilege
! 316: | privileges ':' privilege
! 317: ;
! 318:
! 319: privilege : hostlist '=' cmndspeclist {
! 320: /*
! 321: * We already did a push if necessary in
! 322: * cmndspec so just reset some values so
! 323: * the next 'privilege' gets a clean slate.
! 324: */
! 325: host_matches = -1;
! 326: runas_matches = -1;
! 327: if (def_flag(I_AUTHENTICATE))
! 328: no_passwd = -1;
! 329: else
! 330: no_passwd = TRUE;
! 331: }
! 332: ;
! 333:
! 334: ophost : host {
! 335: if ($1 != -1)
! 336: host_matches = $1;
! 337: }
! 338: | '!' host {
! 339: if ($2 != -1)
! 340: host_matches = ! $2;
! 341: }
! 342:
! 343: host : ALL {
! 344: $$ = TRUE;
! 345: }
! 346: | NTWKADDR {
! 347: if (addr_matches($1))
! 348: $$ = TRUE;
! 349: else
! 350: $$ = -1;
! 351: free($1);
! 352: }
! 353: | NETGROUP {
! 354: if (netgr_matches($1, user_host, NULL))
! 355: $$ = TRUE;
! 356: else
! 357: $$ = -1;
! 358: free($1);
! 359: }
! 360: | WORD {
! 361: if (strcasecmp(user_shost, $1) == 0)
! 362: $$ = TRUE;
! 363: else
! 364: $$ = -1;
! 365: free($1);
! 366: }
! 367: | FQHOST {
! 368: if (strcasecmp(user_host, $1) == 0)
! 369: $$ = TRUE;
! 370: else
! 371: $$ = -1;
! 372: free($1);
! 373: }
! 374: | ALIAS {
! 375: aliasinfo *aip = find_alias($1, HOST_ALIAS);
! 376:
! 377: /* could be an all-caps hostname */
! 378: if (aip)
! 379: $$ = aip->val;
! 380: else if (strcasecmp(user_shost, $1) == 0)
! 381: $$ = TRUE;
! 382: else {
! 383: if (pedantic) {
! 384: (void) fprintf(stderr,
! 385: "%s: undeclared Host_Alias `%s' referenced near line %d\n",
! 386: (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
! 387: if (pedantic > 1) {
! 388: yyerror(NULL);
! 389: YYERROR;
! 390: }
! 391: }
! 392: $$ = -1;
! 393: }
! 394: free($1);
! 395: }
! 396: ;
! 397:
! 398: cmndspeclist : cmndspec
! 399: | cmndspeclist ',' cmndspec
! 400: ;
! 401:
! 402: cmndspec : runasspec nopasswd opcmnd {
! 403: /*
! 404: * Push the entry onto the stack if it is worth
! 405: * saving and clear cmnd_matches for next cmnd.
! 406: *
! 407: * We need to save at least one entry on
! 408: * the stack so sudoers_lookup() can tell that
! 409: * the user was listed in sudoers. Also, we
! 410: * need to be able to tell whether or not a
! 411: * user was listed for this specific host.
! 412: */
! 413: if (user_matches != -1 && host_matches != -1 &&
! 414: cmnd_matches != -1 && runas_matches != -1)
! 415: pushcp;
! 416: else if (user_matches != -1 && (top == 1 ||
! 417: (top == 2 && host_matches != -1 &&
! 418: match[0].host == -1)))
! 419: pushcp;
! 420: cmnd_matches = -1;
! 421: }
! 422: ;
! 423:
! 424: opcmnd : cmnd {
! 425: if ($1 != -1)
! 426: cmnd_matches = $1;
! 427: }
! 428: | '!' {
! 429: if (printmatches == TRUE) {
! 430: if (in_alias == TRUE)
! 431: append_entries("!", ", ");
! 432: else if (host_matches == TRUE &&
! 433: user_matches == TRUE)
! 434: append_cmnd("!", NULL);
! 435: }
! 436: } cmnd {
! 437: if ($3 != -1)
! 438: cmnd_matches = ! $3;
! 439: }
! 440: ;
! 441:
! 442: runasspec : /* empty */ {
! 443: if (printmatches == TRUE && host_matches == TRUE &&
! 444: user_matches == TRUE) {
! 445: if (runas_matches == -1) {
! 446: cm_list[cm_list_len].runas_len = 0;
! 447: } else {
! 448: /* Inherit runas data. */
! 449: cm_list[cm_list_len].runas =
! 450: estrdup(cm_list[cm_list_len-1].runas);
! 451: cm_list[cm_list_len].runas_len =
! 452: cm_list[cm_list_len-1].runas_len;
! 453: cm_list[cm_list_len].runas_size =
! 454: cm_list[cm_list_len-1].runas_size;
! 455: }
! 456: }
! 457: /*
! 458: * If this is the first entry in a command list
! 459: * then check against default runas user.
! 460: */
! 461: if (runas_matches == -1)
! 462: runas_matches = (strcmp(*user_runas,
! 463: def_str(I_RUNAS_DEF)) == 0);
! 464: }
! 465: | RUNAS runaslist { ; }
! 466: ;
! 467:
! 468: runaslist : oprunasuser
! 469: | runaslist ',' oprunasuser
! 470: ;
! 471:
! 472: oprunasuser : runasuser {
! 473: if ($1 != -1)
! 474: runas_matches = $1;
! 475: }
! 476: | '!' {
! 477: if (printmatches == TRUE) {
! 478: if (in_alias == TRUE)
! 479: append_entries("!", ", ");
! 480: else if (host_matches == TRUE &&
! 481: user_matches == TRUE)
! 482: append_runas("!", ", ");
! 483: }
! 484: } runasuser {
! 485: if ($3 != -1)
! 486: runas_matches = ! $3;
! 487: }
! 488:
! 489: runasuser : WORD {
! 490: if (printmatches == TRUE) {
! 491: if (in_alias == TRUE)
! 492: append_entries($1, ", ");
! 493: else if (host_matches == TRUE &&
! 494: user_matches == TRUE)
! 495: append_runas($1, ", ");
! 496: }
! 497: if (strcmp($1, *user_runas) == 0)
! 498: $$ = TRUE;
! 499: else
! 500: $$ = -1;
! 501: free($1);
! 502: }
! 503: | USERGROUP {
! 504: if (printmatches == TRUE) {
! 505: if (in_alias == TRUE)
! 506: append_entries($1, ", ");
! 507: else if (host_matches == TRUE &&
! 508: user_matches == TRUE)
! 509: append_runas($1, ", ");
! 510: }
! 511: if (usergr_matches($1, *user_runas))
! 512: $$ = TRUE;
! 513: else
! 514: $$ = -1;
! 515: free($1);
! 516: }
! 517: | NETGROUP {
! 518: if (printmatches == TRUE) {
! 519: if (in_alias == TRUE)
! 520: append_entries($1, ", ");
! 521: else if (host_matches == TRUE &&
! 522: user_matches == TRUE)
! 523: append_runas($1, ", ");
! 524: }
! 525: if (netgr_matches($1, NULL, *user_runas))
! 526: $$ = TRUE;
! 527: else
! 528: $$ = -1;
! 529: free($1);
! 530: }
! 531: | ALIAS {
! 532: aliasinfo *aip = find_alias($1, RUNAS_ALIAS);
! 533:
! 534: if (printmatches == TRUE) {
! 535: if (in_alias == TRUE)
! 536: append_entries($1, ", ");
! 537: else if (host_matches == TRUE &&
! 538: user_matches == TRUE)
! 539: append_runas($1, ", ");
! 540: }
! 541: /* could be an all-caps username */
! 542: if (aip)
! 543: $$ = aip->val;
! 544: else if (strcmp($1, *user_runas) == 0)
! 545: $$ = TRUE;
! 546: else {
! 547: if (pedantic) {
! 548: (void) fprintf(stderr,
! 549: "%s: undeclared Runas_Alias `%s' referenced near line %d\n",
! 550: (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
! 551: if (pedantic > 1) {
! 552: yyerror(NULL);
! 553: YYERROR;
! 554: }
! 555: }
! 556: $$ = -1;
! 557: }
! 558: free($1);
! 559: }
! 560: | ALL {
! 561: if (printmatches == TRUE) {
! 562: if (in_alias == TRUE)
! 563: append_entries("ALL", ", ");
! 564: else if (host_matches == TRUE &&
! 565: user_matches == TRUE)
! 566: append_runas("ALL", ", ");
! 567: }
! 568: $$ = TRUE;
! 569: }
! 570: ;
! 571:
! 572: nopasswd : /* empty */ {
! 573: /* Inherit NOPASSWD/PASSWD status. */
! 574: if (printmatches == TRUE && host_matches == TRUE &&
! 575: user_matches == TRUE) {
! 576: if (no_passwd == TRUE)
! 577: cm_list[cm_list_len].nopasswd = TRUE;
! 578: else
! 579: cm_list[cm_list_len].nopasswd = FALSE;
! 580: }
! 581: }
! 582: | NOPASSWD {
! 583: no_passwd = TRUE;
! 584: if (printmatches == TRUE && host_matches == TRUE &&
! 585: user_matches == TRUE)
! 586: cm_list[cm_list_len].nopasswd = TRUE;
! 587: }
! 588: | PASSWD {
! 589: no_passwd = FALSE;
! 590: if (printmatches == TRUE && host_matches == TRUE &&
! 591: user_matches == TRUE)
! 592: cm_list[cm_list_len].nopasswd = FALSE;
! 593: }
! 594: ;
! 595:
! 596: cmnd : ALL {
! 597: if (printmatches == TRUE) {
! 598: if (in_alias == TRUE)
! 599: append_entries("ALL", ", ");
! 600: else if (host_matches == TRUE &&
! 601: user_matches == TRUE) {
! 602: append_cmnd("ALL", NULL);
! 603: expand_match_list();
! 604: }
! 605: }
! 606:
! 607: $$ = TRUE;
! 608:
! 609: if (safe_cmnd)
! 610: free(safe_cmnd);
! 611: safe_cmnd = estrdup(user_cmnd);
! 612: }
! 613: | ALIAS {
! 614: aliasinfo *aip;
! 615:
! 616: if (printmatches == TRUE) {
! 617: if (in_alias == TRUE)
! 618: append_entries($1, ", ");
! 619: else if (host_matches == TRUE &&
! 620: user_matches == TRUE) {
! 621: append_cmnd($1, NULL);
! 622: expand_match_list();
! 623: }
! 624: }
! 625:
! 626: if ((aip = find_alias($1, CMND_ALIAS)))
! 627: $$ = aip->val;
! 628: else {
! 629: if (pedantic) {
! 630: (void) fprintf(stderr,
! 631: "%s: undeclared Cmnd_Alias `%s' referenced near line %d\n",
! 632: (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
! 633: if (pedantic > 1) {
! 634: yyerror(NULL);
! 635: YYERROR;
! 636: }
! 637: }
! 638: $$ = -1;
! 639: }
! 640: free($1);
! 641: }
! 642: | COMMAND {
! 643: if (printmatches == TRUE) {
! 644: if (in_alias == TRUE) {
! 645: append_entries($1.cmnd, ", ");
! 646: if ($1.args)
! 647: append_entries($1.args, " ");
! 648: }
! 649: if (host_matches == TRUE &&
! 650: user_matches == TRUE) {
! 651: append_cmnd($1.cmnd, NULL);
! 652: if ($1.args)
! 653: append_cmnd($1.args, " ");
! 654: expand_match_list();
! 655: }
! 656: }
! 657:
! 658: if (command_matches(user_cmnd, user_args,
! 659: $1.cmnd, $1.args))
! 660: $$ = TRUE;
! 661: else
! 662: $$ = -1;
! 663:
! 664: free($1.cmnd);
! 665: if ($1.args)
! 666: free($1.args);
! 667: }
! 668: ;
! 669:
! 670: hostaliases : hostalias
! 671: | hostaliases ':' hostalias
! 672: ;
! 673:
! 674: hostalias : ALIAS { push; } '=' hostlist {
! 675: if ((host_matches != -1 || pedantic) &&
! 676: !add_alias($1, HOST_ALIAS, host_matches))
! 677: YYERROR;
! 678: pop;
! 679: }
! 680: ;
! 681:
! 682: hostlist : ophost
! 683: | hostlist ',' ophost
! 684: ;
! 685:
! 686: cmndaliases : cmndalias
! 687: | cmndaliases ':' cmndalias
! 688: ;
! 689:
! 690: cmndalias : ALIAS {
! 691: push;
! 692: if (printmatches == TRUE) {
! 693: in_alias = TRUE;
! 694: /* Allocate space for ga_list if necessary. */
! 695: expand_ga_list();
! 696: ga_list[ga_list_len-1].type = CMND_ALIAS;
! 697: ga_list[ga_list_len-1].alias = estrdup($1);
! 698: }
! 699: } '=' cmndlist {
! 700: if ((cmnd_matches != -1 || pedantic) &&
! 701: !add_alias($1, CMND_ALIAS, cmnd_matches))
! 702: YYERROR;
! 703: pop;
! 704: free($1);
! 705:
! 706: if (printmatches == TRUE)
! 707: in_alias = FALSE;
! 708: }
! 709: ;
! 710:
! 711: cmndlist : opcmnd { ; }
! 712: | cmndlist ',' opcmnd
! 713: ;
! 714:
! 715: runasaliases : runasalias
! 716: | runasaliases ':' runasalias
! 717: ;
! 718:
! 719: runasalias : ALIAS {
! 720: push;
! 721: if (printmatches == TRUE) {
! 722: in_alias = TRUE;
! 723: /* Allocate space for ga_list if necessary. */
! 724: expand_ga_list();
! 725: ga_list[ga_list_len-1].type = RUNAS_ALIAS;
! 726: ga_list[ga_list_len-1].alias = estrdup($1);
! 727: }
! 728: } '=' runaslist {
! 729: if ((runas_matches != -1 || pedantic) &&
! 730: !add_alias($1, RUNAS_ALIAS, runas_matches))
! 731: YYERROR;
! 732: pop;
! 733: free($1);
! 734:
! 735: if (printmatches == TRUE)
! 736: in_alias = FALSE;
! 737: }
! 738: ;
! 739:
! 740: useraliases : useralias
! 741: | useraliases ':' useralias
! 742: ;
! 743:
! 744: useralias : ALIAS { push; } '=' userlist {
! 745: if ((user_matches != -1 || pedantic) &&
! 746: !add_alias($1, USER_ALIAS, user_matches))
! 747: YYERROR;
! 748: pop;
! 749: free($1);
! 750: }
! 751: ;
! 752:
! 753: userlist : opuser
! 754: | userlist ',' opuser
! 755: ;
! 756:
! 757: opuser : user {
! 758: if ($1 != -1)
! 759: user_matches = $1;
! 760: }
! 761: | '!' user {
! 762: if ($2 != -1)
! 763: user_matches = ! $2;
! 764: }
! 765:
! 766: user : WORD {
! 767: if (strcmp($1, user_name) == 0)
! 768: $$ = TRUE;
! 769: else
! 770: $$ = -1;
! 771: free($1);
! 772: }
! 773: | USERGROUP {
! 774: if (usergr_matches($1, user_name))
! 775: $$ = TRUE;
! 776: else
! 777: $$ = -1;
! 778: free($1);
! 779: }
! 780: | NETGROUP {
! 781: if (netgr_matches($1, NULL, user_name))
! 782: $$ = TRUE;
! 783: else
! 784: $$ = -1;
! 785: free($1);
! 786: }
! 787: | ALIAS {
! 788: aliasinfo *aip = find_alias($1, USER_ALIAS);
! 789:
! 790: /* could be an all-caps username */
! 791: if (aip)
! 792: $$ = aip->val;
! 793: else if (strcmp($1, user_name) == 0)
! 794: $$ = TRUE;
! 795: else {
! 796: if (pedantic) {
! 797: (void) fprintf(stderr,
! 798: "%s: undeclared User_Alias `%s' referenced near line %d\n",
! 799: (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
! 800: if (pedantic > 1)
! 801: YYERROR;
! 802: }
! 803: $$ = -1;
! 804: }
! 805: free($1);
! 806: }
! 807: | ALL {
! 808: $$ = TRUE;
! 809: }
! 810: ;
! 811:
! 812: %%
! 813:
! 814: #define MOREALIASES (32)
! 815: aliasinfo *aliases = NULL;
! 816: size_t naliases = 0;
! 817: size_t nslots = 0;
! 818:
! 819:
! 820: /*
! 821: * Compare two aliasinfo structures, strcmp() style.
! 822: * Note that we do *not* compare their values.
! 823: */
! 824: static int
! 825: aliascmp(a1, a2)
! 826: const VOID *a1, *a2;
! 827: {
! 828: int r;
! 829: aliasinfo *ai1, *ai2;
! 830:
! 831: ai1 = (aliasinfo *) a1;
! 832: ai2 = (aliasinfo *) a2;
! 833: if ((r = strcmp(ai1->name, ai2->name)) == 0)
! 834: r = ai1->type - ai2->type;
! 835:
! 836: return(r);
! 837: }
! 838:
! 839: /*
! 840: * Compare two generic_alias structures, strcmp() style.
! 841: */
! 842: static int
! 843: genaliascmp(entry, key)
! 844: const VOID *entry, *key;
! 845: {
! 846: int r;
! 847: struct generic_alias *ga1, *ga2;
! 848:
! 849: ga1 = (struct generic_alias *) key;
! 850: ga2 = (struct generic_alias *) entry;
! 851: if ((r = strcmp(ga1->alias, ga2->alias)) == 0)
! 852: r = ga1->type - ga2->type;
! 853:
! 854: return(r);
! 855: }
! 856:
! 857:
! 858: /*
! 859: * Adds the named alias of the specified type to the aliases list.
! 860: */
! 861: static int
! 862: add_alias(alias, type, val)
! 863: char *alias;
! 864: int type;
! 865: int val;
! 866: {
! 867: aliasinfo ai, *aip;
! 868: size_t onaliases;
! 869: char s[512];
! 870:
! 871: if (naliases >= nslots && !more_aliases()) {
! 872: (void) snprintf(s, sizeof(s), "Out of memory defining alias `%s'",
! 873: alias);
! 874: yyerror(s);
! 875: return(FALSE);
! 876: }
! 877:
! 878: ai.type = type;
! 879: ai.val = val;
! 880: ai.name = estrdup(alias);
! 881: onaliases = naliases;
! 882:
! 883: aip = (aliasinfo *) lsearch((VOID *)&ai, (VOID *)aliases, &naliases,
! 884: sizeof(ai), aliascmp);
! 885: if (aip == NULL) {
! 886: (void) snprintf(s, sizeof(s), "Aliases corrupted defining alias `%s'",
! 887: alias);
! 888: yyerror(s);
! 889: return(FALSE);
! 890: }
! 891: if (onaliases == naliases) {
! 892: (void) snprintf(s, sizeof(s), "Alias `%s' already defined", alias);
! 893: yyerror(s);
! 894: return(FALSE);
! 895: }
! 896:
! 897: return(TRUE);
! 898: }
! 899:
! 900: /*
! 901: * Searches for the named alias of the specified type.
! 902: */
! 903: static aliasinfo *
! 904: find_alias(alias, type)
! 905: char *alias;
! 906: int type;
! 907: {
! 908: aliasinfo ai;
! 909:
! 910: ai.name = alias;
! 911: ai.type = type;
! 912:
! 913: return((aliasinfo *) lfind((VOID *)&ai, (VOID *)aliases, &naliases,
! 914: sizeof(ai), aliascmp));
! 915: }
! 916:
! 917: /*
! 918: * Allocates more space for the aliases list.
! 919: */
! 920: static int
! 921: more_aliases()
! 922: {
! 923:
! 924: nslots += MOREALIASES;
! 925: if (nslots == MOREALIASES)
! 926: aliases = (aliasinfo *) malloc(nslots * sizeof(aliasinfo));
! 927: else
! 928: aliases = (aliasinfo *) realloc(aliases, nslots * sizeof(aliasinfo));
! 929:
! 930: return(aliases != NULL);
! 931: }
! 932:
! 933: /*
! 934: * Lists the contents of the aliases list.
! 935: */
! 936: void
! 937: dumpaliases()
! 938: {
! 939: size_t n;
! 940:
! 941: for (n = 0; n < naliases; n++) {
! 942: if (aliases[n].val == -1)
! 943: continue;
! 944:
! 945: switch (aliases[n].type) {
! 946: case HOST_ALIAS:
! 947: (void) puts("HOST_ALIAS");
! 948: break;
! 949:
! 950: case CMND_ALIAS:
! 951: (void) puts("CMND_ALIAS");
! 952: break;
! 953:
! 954: case USER_ALIAS:
! 955: (void) puts("USER_ALIAS");
! 956: break;
! 957:
! 958: case RUNAS_ALIAS:
! 959: (void) puts("RUNAS_ALIAS");
! 960: break;
! 961: }
! 962: (void) printf("\t%s: %d\n", aliases[n].name, aliases[n].val);
! 963: }
! 964: }
! 965:
! 966: /*
! 967: * Lists the contents of cm_list and ga_list for `sudo -l'.
! 968: */
! 969: void
! 970: list_matches()
! 971: {
! 972: int i;
! 973: char *p;
! 974: struct generic_alias *ga, key;
! 975:
! 976: (void) printf("User %s may run the following commands on this host:\n",
! 977: user_name);
! 978: for (i = 0; i < cm_list_len; i++) {
! 979:
! 980: /* Print the runas list. */
! 981: (void) fputs(" ", stdout);
! 982: if (cm_list[i].runas) {
! 983: (void) putchar('(');
! 984: p = strtok(cm_list[i].runas, ", ");
! 985: do {
! 986: if (p != cm_list[i].runas)
! 987: (void) fputs(", ", stdout);
! 988:
! 989: key.alias = p;
! 990: key.type = RUNAS_ALIAS;
! 991: if ((ga = (struct generic_alias *) lfind((VOID *) &key,
! 992: (VOID *) &ga_list[0], &ga_list_len, sizeof(key), genaliascmp)))
! 993: (void) fputs(ga->entries, stdout);
! 994: else
! 995: (void) fputs(p, stdout);
! 996: } while ((p = strtok(NULL, ", ")));
! 997: (void) fputs(") ", stdout);
! 998: } else {
! 999: (void) printf("(%s) ", def_str(I_RUNAS_DEF));
! 1000: }
! 1001:
! 1002: /* Is a password required? */
! 1003: if (cm_list[i].nopasswd == TRUE && def_flag(I_AUTHENTICATE))
! 1004: (void) fputs("NOPASSWD: ", stdout);
! 1005: else if (cm_list[i].nopasswd == FALSE && !def_flag(I_AUTHENTICATE))
! 1006: (void) fputs("PASSWD: ", stdout);
! 1007:
! 1008: /* Print the actual command or expanded Cmnd_Alias. */
! 1009: key.alias = cm_list[i].cmnd;
! 1010: key.type = CMND_ALIAS;
! 1011: if ((ga = (struct generic_alias *) lfind((VOID *) &key,
! 1012: (VOID *) &ga_list[0], &ga_list_len, sizeof(key), genaliascmp)))
! 1013: (void) puts(ga->entries);
! 1014: else
! 1015: (void) puts(cm_list[i].cmnd);
! 1016: }
! 1017:
! 1018: /* Be nice and free up space now that we are done. */
! 1019: for (i = 0; i < ga_list_len; i++) {
! 1020: free(ga_list[i].alias);
! 1021: free(ga_list[i].entries);
! 1022: }
! 1023: free(ga_list);
! 1024: ga_list = NULL;
! 1025:
! 1026: for (i = 0; i < cm_list_len; i++) {
! 1027: free(cm_list[i].runas);
! 1028: free(cm_list[i].cmnd);
! 1029: }
! 1030: free(cm_list);
! 1031: cm_list = NULL;
! 1032: cm_list_len = 0;
! 1033: cm_list_size = 0;
! 1034: }
! 1035:
! 1036: /*
! 1037: * Appends a source string to the destination, optionally prefixing a separator.
! 1038: */
! 1039: static void
! 1040: append(src, dstp, dst_len, dst_size, separator)
! 1041: char *src, **dstp;
! 1042: size_t *dst_len, *dst_size;
! 1043: char *separator;
! 1044: {
! 1045: size_t src_len = strlen(src);
! 1046: char *dst = *dstp;
! 1047:
! 1048: /*
! 1049: * Only add the separator if there is something to separate from.
! 1050: * If the last char is a '!', don't apply the separator (XXX).
! 1051: */
! 1052: if (separator && dst && dst[*dst_len - 1] != '!')
! 1053: src_len += strlen(separator);
! 1054: else
! 1055: separator = NULL;
! 1056:
! 1057: /* Assumes dst will be NULL if not set. */
! 1058: if (dst == NULL) {
! 1059: dst = (char *) emalloc(BUFSIZ);
! 1060: *dst_size = BUFSIZ;
! 1061: *dst_len = 0;
! 1062: *dstp = dst;
! 1063: }
! 1064:
! 1065: /* Allocate more space if necessary. */
! 1066: if (*dst_size <= *dst_len + src_len) {
! 1067: while (*dst_size <= *dst_len + src_len)
! 1068: *dst_size += BUFSIZ;
! 1069:
! 1070: dst = (char *) erealloc(dst, *dst_size);
! 1071: *dstp = dst;
! 1072: }
! 1073:
! 1074: /* Copy src -> dst adding a separator if appropriate and adjust len. */
! 1075: dst += *dst_len;
! 1076: *dst_len += src_len;
! 1077: *dst = '\0';
! 1078: if (separator)
! 1079: (void) strcat(dst, separator);
! 1080: (void) strcat(dst, src);
! 1081: }
! 1082:
! 1083: /*
! 1084: * Frees up space used by the aliases list and resets the associated counters.
! 1085: */
! 1086: void
! 1087: reset_aliases()
! 1088: {
! 1089: size_t n;
! 1090:
! 1091: if (aliases) {
! 1092: for (n = 0; n < naliases; n++)
! 1093: free(aliases[n].name);
! 1094: free(aliases);
! 1095: aliases = NULL;
! 1096: }
! 1097: naliases = nslots = 0;
! 1098: }
! 1099:
! 1100: /*
! 1101: * Increments ga_list_len, allocating more space as necessary.
! 1102: */
! 1103: static void
! 1104: expand_ga_list()
! 1105: {
! 1106:
! 1107: if (++ga_list_len >= ga_list_size) {
! 1108: while ((ga_list_size += STACKINCREMENT) < ga_list_len)
! 1109: ;
! 1110: ga_list = (struct generic_alias *)
! 1111: erealloc(ga_list, sizeof(struct generic_alias) * ga_list_size);
! 1112: }
! 1113:
! 1114: ga_list[ga_list_len - 1].entries = NULL;
! 1115: }
! 1116:
! 1117: /*
! 1118: * Increments cm_list_len, allocating more space as necessary.
! 1119: */
! 1120: static void
! 1121: expand_match_list()
! 1122: {
! 1123:
! 1124: if (++cm_list_len >= cm_list_size) {
! 1125: while ((cm_list_size += STACKINCREMENT) < cm_list_len)
! 1126: ;
! 1127: if (cm_list == NULL)
! 1128: cm_list_len = 0; /* start at 0 since it is a subscript */
! 1129: cm_list = (struct command_match *)
! 1130: erealloc(cm_list, sizeof(struct command_match) * cm_list_size);
! 1131: }
! 1132:
! 1133: cm_list[cm_list_len].runas = cm_list[cm_list_len].cmnd = NULL;
! 1134: cm_list[cm_list_len].nopasswd = FALSE;
! 1135: }
! 1136:
! 1137: /*
! 1138: * Frees up spaced used by a previous parser run and allocates new space
! 1139: * for various data structures.
! 1140: */
! 1141: void
! 1142: init_parser()
! 1143: {
! 1144:
! 1145: /* Free up old data structures if we run the parser more than once. */
! 1146: if (match) {
! 1147: free(match);
! 1148: match = NULL;
! 1149: top = 0;
! 1150: parse_error = FALSE;
! 1151: errorlineno = -1;
! 1152: sudolineno = 1;
! 1153: }
! 1154:
! 1155: /* Allocate space for the matching stack. */
! 1156: stacksize = STACKINCREMENT;
! 1157: match = (struct matchstack *) emalloc(sizeof(struct matchstack) * stacksize);
! 1158:
! 1159: /* Allocate space for the match list (for `sudo -l'). */
! 1160: if (printmatches == TRUE)
! 1161: expand_match_list();
! 1162: }