[BACK]Return to parse.yacc CVS log [TXT][DIR] Up to [local] / src / usr.bin / sudo

Annotation of src/usr.bin/sudo/parse.yacc, Revision 1.5.2.1

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