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

Annotation of src/usr.bin/doas/parse.y, Revision 1.16

1.16    ! djm         1: /* $OpenBSD: parse.y,v 1.15 2016/04/27 02:35:55 gsoares Exp $ */
1.1       tedu        2: /*
                      3:  * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     16:  */
                     17:
                     18: %{
                     19: #include <sys/types.h>
                     20: #include <ctype.h>
                     21: #include <unistd.h>
                     22: #include <stdint.h>
                     23: #include <stdarg.h>
                     24: #include <stdio.h>
                     25: #include <string.h>
                     26: #include <err.h>
                     27:
                     28: #include "doas.h"
                     29:
                     30: typedef struct {
                     31:        union {
                     32:                struct {
                     33:                        int action;
                     34:                        int options;
1.7       zhuk       35:                        const char *cmd;
                     36:                        const char **cmdargs;
1.1       tedu       37:                        const char **envlist;
1.16    ! djm        38:                        const char **setenvlist;
1.1       tedu       39:                };
                     40:                const char *str;
                     41:        };
1.10      zhuk       42:        int lineno;
                     43:        int colno;
1.1       tedu       44: } yystype;
                     45: #define YYSTYPE yystype
                     46:
                     47: FILE *yyfp;
                     48:
                     49: struct rule **rules;
                     50: int nrules, maxrules;
1.10      zhuk       51: int parse_errors = 0;
1.1       tedu       52:
1.4       nicm       53: void yyerror(const char *, ...);
                     54: int yylex(void);
                     55: int yyparse(void);
                     56:
1.1       tedu       57: %}
                     58:
1.7       zhuk       59: %token TPERMIT TDENY TAS TCMD TARGS
1.16    ! djm        60: %token TNOPASS TKEEPENV TSETENV
1.1       tedu       61: %token TSTRING
                     62:
                     63: %%
                     64:
                     65: grammar:       /* empty */
                     66:                | grammar '\n'
                     67:                | grammar rule '\n'
1.10      zhuk       68:                | error '\n'
1.1       tedu       69:                ;
                     70:
                     71: rule:          action ident target cmd {
                     72:                        struct rule *r;
                     73:                        r = calloc(1, sizeof(*r));
1.2       nicm       74:                        if (!r)
                     75:                                errx(1, "can't allocate rule");
1.1       tedu       76:                        r->action = $1.action;
                     77:                        r->options = $1.options;
                     78:                        r->envlist = $1.envlist;
1.16    ! djm        79:                        r->setenvlist = $1.setenvlist;
1.1       tedu       80:                        r->ident = $2.str;
                     81:                        r->target = $3.str;
1.7       zhuk       82:                        r->cmd = $4.cmd;
                     83:                        r->cmdargs = $4.cmdargs;
1.1       tedu       84:                        if (nrules == maxrules) {
                     85:                                if (maxrules == 0)
                     86:                                        maxrules = 63;
                     87:                                else
                     88:                                        maxrules *= 2;
1.6       benno      89:                                if (!(rules = reallocarray(rules, maxrules,
                     90:                                    sizeof(*rules))))
1.1       tedu       91:                                        errx(1, "can't allocate rules");
                     92:                        }
                     93:                        rules[nrules++] = r;
                     94:                } ;
                     95:
                     96: action:                TPERMIT options {
                     97:                        $$.action = PERMIT;
                     98:                        $$.options = $2.options;
                     99:                        $$.envlist = $2.envlist;
1.16    ! djm       100:                        $$.setenvlist = $2.setenvlist;
1.1       tedu      101:                } | TDENY {
                    102:                        $$.action = DENY;
                    103:                } ;
                    104:
                    105: options:       /* none */
                    106:                | options option {
                    107:                        $$.options = $1.options | $2.options;
                    108:                        $$.envlist = $1.envlist;
                    109:                        if ($2.envlist) {
1.10      zhuk      110:                                if ($$.envlist) {
                    111:                                        yyerror("can't have two keepenv sections");
                    112:                                        YYERROR;
                    113:                                } else
1.1       tedu      114:                                        $$.envlist = $2.envlist;
                    115:                        }
1.16    ! djm       116:                        $$.setenvlist = $1.setenvlist;
        !           117:                        if ($2.setenvlist) {
        !           118:                                if ($$.setenvlist) {
        !           119:                                        yyerror("can't have two setenv sections");
        !           120:                                        YYERROR;
        !           121:                                } else
        !           122:                                        $$.setenvlist = $2.setenvlist;
        !           123:                        }
1.1       tedu      124:                } ;
1.16    ! djm       125:
1.1       tedu      126: option:                TNOPASS {
                    127:                        $$.options = NOPASS;
                    128:                } | TKEEPENV {
                    129:                        $$.options = KEEPENV;
                    130:                } | TKEEPENV '{' envlist '}' {
                    131:                        $$.options = KEEPENV;
                    132:                        $$.envlist = $3.envlist;
1.16    ! djm       133:                } | TSETENV '{' setenvlist '}' {
        !           134:                        $$.options = SETENV;
        !           135:                        $$.setenvlist = $3.setenvlist;
1.1       tedu      136:                } ;
                    137:
                    138: envlist:       /* empty */ {
                    139:                        if (!($$.envlist = calloc(1, sizeof(char *))))
                    140:                                errx(1, "can't allocate envlist");
                    141:                } | envlist TSTRING {
                    142:                        int nenv = arraylen($1.envlist);
1.6       benno     143:                        if (!($$.envlist = reallocarray($1.envlist, nenv + 2,
                    144:                            sizeof(char *))))
1.1       tedu      145:                                errx(1, "can't allocate envlist");
                    146:                        $$.envlist[nenv] = $2.str;
                    147:                        $$.envlist[nenv + 1] = NULL;
                    148:                }
                    149:
1.16    ! djm       150: setenvlist:    /* empty */ {
        !           151:                        if (!($$.setenvlist = calloc(1, sizeof(char *))))
        !           152:                                errx(1, "can't allocate setenvlist");
        !           153:                } | setenvlist TSTRING '=' TSTRING {
        !           154:                        int nenv = arraylen($1.setenvlist);
        !           155:                        char *cp = NULL;
        !           156:
        !           157:                        if (*$2.str == '\0' || strchr($2.str, '=') != NULL) {
        !           158:                                yyerror("invalid setenv expression");
        !           159:                                YYERROR;
        !           160:                        }
        !           161:                        if (!($$.setenvlist = reallocarray($1.setenvlist,
        !           162:                            nenv + 2, sizeof(char *))))
        !           163:                                errx(1, "can't allocate envlist");
        !           164:                        $$.setenvlist[nenv] = NULL;
        !           165:                        if (asprintf(&cp, "%s=%s", $2.str, $4.str) <= 0 ||
        !           166:                            cp == NULL)
        !           167:                                errx(1,"asprintf failed");
        !           168:                        $$.setenvlist[nenv] = cp;
        !           169:                        $$.setenvlist[nenv + 1] = NULL;
        !           170:                }
        !           171:
1.1       tedu      172:
                    173: ident:         TSTRING {
                    174:                        $$.str = $1.str;
                    175:                } ;
                    176:
                    177: target:                /* optional */ {
                    178:                        $$.str = NULL;
                    179:                } | TAS TSTRING {
                    180:                        $$.str = $2.str;
                    181:                } ;
                    182:
                    183: cmd:           /* optional */ {
1.7       zhuk      184:                        $$.cmd = NULL;
                    185:                        $$.cmdargs = NULL;
                    186:                } | TCMD TSTRING args {
                    187:                        $$.cmd = $2.str;
                    188:                        $$.cmdargs = $3.cmdargs;
                    189:                } ;
                    190:
                    191: args:          /* empty */ {
                    192:                        $$.cmdargs = NULL;
                    193:                } | TARGS argslist {
                    194:                        $$.cmdargs = $2.cmdargs;
                    195:                } ;
                    196:
                    197: argslist:      /* empty */ {
                    198:                        if (!($$.cmdargs = calloc(1, sizeof(char *))))
                    199:                                errx(1, "can't allocate args");
                    200:                } | argslist TSTRING {
                    201:                        int nargs = arraylen($1.cmdargs);
1.11      deraadt   202:                        if (!($$.cmdargs = reallocarray($1.cmdargs, nargs + 2,
                    203:                            sizeof(char *))))
1.7       zhuk      204:                                errx(1, "can't allocate args");
                    205:                        $$.cmdargs[nargs] = $2.str;
                    206:                        $$.cmdargs[nargs + 1] = NULL;
1.1       tedu      207:                } ;
                    208:
                    209: %%
                    210:
                    211: void
                    212: yyerror(const char *fmt, ...)
                    213: {
                    214:        va_list va;
                    215:
1.15      gsoares   216:        fprintf(stderr, "doas: ");
1.1       tedu      217:        va_start(va, fmt);
1.10      zhuk      218:        vfprintf(stderr, fmt, va);
                    219:        va_end(va);
                    220:        fprintf(stderr, " at line %d\n", yylval.lineno + 1);
                    221:        parse_errors++;
1.1       tedu      222: }
                    223:
                    224: struct keyword {
                    225:        const char *word;
                    226:        int token;
                    227: } keywords[] = {
                    228:        { "deny", TDENY },
                    229:        { "permit", TPERMIT },
                    230:        { "as", TAS },
                    231:        { "cmd", TCMD },
1.7       zhuk      232:        { "args", TARGS },
1.1       tedu      233:        { "nopass", TNOPASS },
                    234:        { "keepenv", TKEEPENV },
1.16    ! djm       235:        { "setenv", TSETENV },
1.1       tedu      236: };
                    237:
                    238: int
                    239: yylex(void)
                    240: {
                    241:        char buf[1024], *ebuf, *p, *str;
1.10      zhuk      242:        int i, c, quotes = 0, escape = 0, qpos = -1, nonkw = 0;
1.1       tedu      243:
                    244:        p = buf;
                    245:        ebuf = buf + sizeof(buf);
1.9       zhuk      246:
1.5       benno     247: repeat:
1.9       zhuk      248:        /* skip whitespace first */
                    249:        for (c = getc(yyfp); c == ' ' || c == '\t'; c = getc(yyfp))
1.10      zhuk      250:                yylval.colno++;
1.9       zhuk      251:
                    252:        /* check for special one-character constructions */
1.1       tedu      253:        switch (c) {
1.9       zhuk      254:                case '\n':
1.10      zhuk      255:                        yylval.colno = 0;
                    256:                        yylval.lineno++;
1.9       zhuk      257:                        /* FALLTHROUGH */
                    258:                case '{':
                    259:                case '}':
1.16    ! djm       260:                case '=':
1.9       zhuk      261:                        return c;
                    262:                case '#':
                    263:                        /* skip comments; NUL is allowed; no continuation */
                    264:                        while ((c = getc(yyfp)) != '\n')
                    265:                                if (c == EOF)
1.14      tedu      266:                                        goto eof;
1.10      zhuk      267:                        yylval.colno = 0;
                    268:                        yylval.lineno++;
1.9       zhuk      269:                        return c;
                    270:                case EOF:
1.14      tedu      271:                        goto eof;
1.1       tedu      272:        }
1.9       zhuk      273:
                    274:        /* parsing next word */
1.10      zhuk      275:        for (;; c = getc(yyfp), yylval.colno++) {
1.3       zhuk      276:                switch (c) {
1.9       zhuk      277:                case '\0':
1.11      deraadt   278:                        yyerror("unallowed character NUL in column %d",
                    279:                            yylval.colno + 1);
1.9       zhuk      280:                        escape = 0;
                    281:                        continue;
                    282:                case '\\':
                    283:                        escape = !escape;
                    284:                        if (escape)
                    285:                                continue;
                    286:                        break;
1.3       zhuk      287:                case '\n':
1.9       zhuk      288:                        if (quotes)
1.10      zhuk      289:                                yyerror("unterminated quotes in column %d",
                    290:                                    qpos + 1);
1.9       zhuk      291:                        if (escape) {
                    292:                                nonkw = 1;
                    293:                                escape = 0;
1.12      mikeb     294:                                yylval.colno = 0;
                    295:                                yylval.lineno++;
1.9       zhuk      296:                                continue;
                    297:                        }
                    298:                        goto eow;
                    299:                case EOF:
                    300:                        if (escape)
1.10      zhuk      301:                                yyerror("unterminated escape in column %d",
                    302:                                    yylval.colno);
1.9       zhuk      303:                        if (quotes)
1.10      zhuk      304:                                yyerror("unterminated quotes in column %d",
                    305:                                    qpos + 1);
                    306:                        goto eow;
1.9       zhuk      307:                        /* FALLTHROUGH */
1.3       zhuk      308:                case '{':
                    309:                case '}':
                    310:                case '#':
                    311:                case ' ':
                    312:                case '\t':
1.16    ! djm       313:                case '=':
1.9       zhuk      314:                        if (!escape && !quotes)
                    315:                                goto eow;
                    316:                        break;
                    317:                case '"':
                    318:                        if (!escape) {
                    319:                                quotes = !quotes;
                    320:                                if (quotes) {
                    321:                                        nonkw = 1;
1.10      zhuk      322:                                        qpos = yylval.colno;
1.9       zhuk      323:                                }
                    324:                                continue;
                    325:                        }
1.3       zhuk      326:                }
1.1       tedu      327:                *p++ = c;
1.13      tedu      328:                if (p == ebuf) {
1.10      zhuk      329:                        yyerror("too long line");
1.13      tedu      330:                        p = buf;
                    331:                }
1.9       zhuk      332:                escape = 0;
1.1       tedu      333:        }
1.9       zhuk      334:
1.3       zhuk      335: eow:
1.1       tedu      336:        *p = 0;
                    337:        if (c != EOF)
                    338:                ungetc(c, yyfp);
1.9       zhuk      339:        if (p == buf) {
                    340:                /*
1.11      deraadt   341:                 * There could be a number of reasons for empty buffer,
                    342:                 * and we handle all of them here, to avoid cluttering
                    343:                 * the main loop.
1.9       zhuk      344:                 */
                    345:                if (c == EOF)
1.14      tedu      346:                        goto eof;
1.10      zhuk      347:                else if (qpos == -1)    /* accept, e.g., empty args: cmd foo args "" */
1.9       zhuk      348:                        goto repeat;
                    349:        }
                    350:        if (!nonkw) {
                    351:                for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
                    352:                        if (strcmp(buf, keywords[i].word) == 0)
                    353:                                return keywords[i].token;
                    354:                }
1.1       tedu      355:        }
                    356:        if ((str = strdup(buf)) == NULL)
                    357:                err(1, "strdup");
                    358:        yylval.str = str;
                    359:        return TSTRING;
1.14      tedu      360:
                    361: eof:
                    362:        if (ferror(yyfp))
                    363:                yyerror("input error reading config");
                    364:        return 0;
1.1       tedu      365: }