[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.31

1.31    ! deraadt     1: /* $OpenBSD: parse.y,v 1.30 2021/11/30 20:08:15 tobias 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>
1.30      tobias     21: #include <limits.h>
1.1       tedu       22: #include <unistd.h>
                     23: #include <stdint.h>
                     24: #include <stdarg.h>
                     25: #include <stdio.h>
                     26: #include <string.h>
                     27: #include <err.h>
                     28:
                     29: #include "doas.h"
                     30:
                     31: typedef struct {
                     32:        union {
                     33:                struct {
                     34:                        int action;
                     35:                        int options;
1.7       zhuk       36:                        const char *cmd;
                     37:                        const char **cmdargs;
1.1       tedu       38:                        const char **envlist;
                     39:                };
1.26      tedu       40:                const char **strlist;
1.1       tedu       41:                const char *str;
                     42:        };
1.30      tobias     43:        unsigned long lineno;
                     44:        unsigned long colno;
1.1       tedu       45: } yystype;
                     46: #define YYSTYPE yystype
                     47:
                     48: FILE *yyfp;
                     49:
                     50: struct rule **rules;
1.29      millert    51: size_t nrules;
                     52: static size_t maxrules;
1.22      deraadt    53:
1.30      tobias     54: int parse_error = 0;
1.1       tedu       55:
1.22      deraadt    56: static void yyerror(const char *, ...);
                     57: static int yylex(void);
1.23      tedu       58:
                     59: static size_t
                     60: arraylen(const char **arr)
                     61: {
                     62:        size_t cnt = 0;
                     63:
                     64:        while (*arr) {
                     65:                cnt++;
                     66:                arr++;
                     67:        }
                     68:        return cnt;
                     69: }
1.4       nicm       70:
1.1       tedu       71: %}
                     72:
1.7       zhuk       73: %token TPERMIT TDENY TAS TCMD TARGS
1.28      kn         74: %token TNOPASS TNOLOG TPERSIST TKEEPENV TSETENV
1.1       tedu       75: %token TSTRING
                     76:
                     77: %%
                     78:
                     79: grammar:       /* empty */
                     80:                | grammar '\n'
                     81:                | grammar rule '\n'
1.10      zhuk       82:                | error '\n'
1.1       tedu       83:                ;
                     84:
                     85: rule:          action ident target cmd {
                     86:                        struct rule *r;
1.31    ! deraadt    87:
1.1       tedu       88:                        r = calloc(1, sizeof(*r));
1.2       nicm       89:                        if (!r)
                     90:                                errx(1, "can't allocate rule");
1.1       tedu       91:                        r->action = $1.action;
                     92:                        r->options = $1.options;
                     93:                        r->envlist = $1.envlist;
                     94:                        r->ident = $2.str;
                     95:                        r->target = $3.str;
1.7       zhuk       96:                        r->cmd = $4.cmd;
                     97:                        r->cmdargs = $4.cmdargs;
1.1       tedu       98:                        if (nrules == maxrules) {
                     99:                                if (maxrules == 0)
1.29      millert   100:                                        maxrules = 32;
                    101:                                rules = reallocarray(rules, maxrules,
                    102:                                    2 * sizeof(*rules));
                    103:                                if (!rules)
1.1       tedu      104:                                        errx(1, "can't allocate rules");
1.29      millert   105:                                maxrules *= 2;
1.1       tedu      106:                        }
                    107:                        rules[nrules++] = r;
                    108:                } ;
                    109:
                    110: action:                TPERMIT options {
                    111:                        $$.action = PERMIT;
                    112:                        $$.options = $2.options;
                    113:                        $$.envlist = $2.envlist;
                    114:                } | TDENY {
                    115:                        $$.action = DENY;
1.19      tedu      116:                        $$.options = 0;
                    117:                        $$.envlist = NULL;
1.1       tedu      118:                } ;
                    119:
1.19      tedu      120: options:       /* none */ {
                    121:                        $$.options = 0;
                    122:                        $$.envlist = NULL;
                    123:                } | options option {
1.1       tedu      124:                        $$.options = $1.options | $2.options;
                    125:                        $$.envlist = $1.envlist;
1.21      tedu      126:                        if (($$.options & (NOPASS|PERSIST)) == (NOPASS|PERSIST)) {
                    127:                                yyerror("can't combine nopass and persist");
                    128:                                YYERROR;
                    129:                        }
1.1       tedu      130:                        if ($2.envlist) {
1.10      zhuk      131:                                if ($$.envlist) {
1.19      tedu      132:                                        yyerror("can't have two setenv sections");
1.10      zhuk      133:                                        YYERROR;
                    134:                                } else
1.1       tedu      135:                                        $$.envlist = $2.envlist;
                    136:                        }
                    137:                } ;
                    138: option:                TNOPASS {
                    139:                        $$.options = NOPASS;
1.19      tedu      140:                        $$.envlist = NULL;
1.28      kn        141:                } | TNOLOG {
                    142:                        $$.options = NOLOG;
                    143:                        $$.envlist = NULL;
1.20      tedu      144:                } | TPERSIST {
                    145:                        $$.options = PERSIST;
                    146:                        $$.envlist = NULL;
1.1       tedu      147:                } | TKEEPENV {
                    148:                        $$.options = KEEPENV;
1.19      tedu      149:                        $$.envlist = NULL;
1.26      tedu      150:                } | TSETENV '{' strlist '}' {
1.19      tedu      151:                        $$.options = 0;
1.26      tedu      152:                        $$.envlist = $3.strlist;
1.1       tedu      153:                } ;
                    154:
1.26      tedu      155: strlist:       /* empty */ {
                    156:                        if (!($$.strlist = calloc(1, sizeof(char *))))
                    157:                                errx(1, "can't allocate strlist");
                    158:                } | strlist TSTRING {
                    159:                        int nstr = arraylen($1.strlist);
1.31    ! deraadt   160:
1.26      tedu      161:                        if (!($$.strlist = reallocarray($1.strlist, nstr + 2,
1.6       benno     162:                            sizeof(char *))))
1.26      tedu      163:                                errx(1, "can't allocate strlist");
                    164:                        $$.strlist[nstr] = $2.str;
                    165:                        $$.strlist[nstr + 1] = NULL;
1.24      tedu      166:                } ;
1.16      djm       167:
                    168:
1.1       tedu      169: ident:         TSTRING {
                    170:                        $$.str = $1.str;
                    171:                } ;
                    172:
                    173: target:                /* optional */ {
                    174:                        $$.str = NULL;
                    175:                } | TAS TSTRING {
                    176:                        $$.str = $2.str;
                    177:                } ;
                    178:
                    179: cmd:           /* optional */ {
1.7       zhuk      180:                        $$.cmd = NULL;
                    181:                        $$.cmdargs = NULL;
                    182:                } | TCMD TSTRING args {
                    183:                        $$.cmd = $2.str;
                    184:                        $$.cmdargs = $3.cmdargs;
                    185:                } ;
                    186:
                    187: args:          /* empty */ {
                    188:                        $$.cmdargs = NULL;
1.26      tedu      189:                } | TARGS strlist {
                    190:                        $$.cmdargs = $2.strlist;
1.1       tedu      191:                } ;
                    192:
                    193: %%
                    194:
                    195: void
                    196: yyerror(const char *fmt, ...)
                    197: {
                    198:        va_list va;
                    199:
1.15      gsoares   200:        fprintf(stderr, "doas: ");
1.1       tedu      201:        va_start(va, fmt);
1.10      zhuk      202:        vfprintf(stderr, fmt, va);
                    203:        va_end(va);
1.30      tobias    204:        fprintf(stderr, " at line %lu\n", yylval.lineno + 1);
                    205:        parse_error = 1;
1.1       tedu      206: }
                    207:
1.22      deraadt   208: static struct keyword {
1.1       tedu      209:        const char *word;
                    210:        int token;
                    211: } keywords[] = {
                    212:        { "deny", TDENY },
                    213:        { "permit", TPERMIT },
                    214:        { "as", TAS },
                    215:        { "cmd", TCMD },
1.7       zhuk      216:        { "args", TARGS },
1.1       tedu      217:        { "nopass", TNOPASS },
1.28      kn        218:        { "nolog", TNOLOG },
1.20      tedu      219:        { "persist", TPERSIST },
1.1       tedu      220:        { "keepenv", TKEEPENV },
1.19      tedu      221:        { "setenv", TSETENV },
1.1       tedu      222: };
                    223:
                    224: int
                    225: yylex(void)
                    226: {
                    227:        char buf[1024], *ebuf, *p, *str;
1.30      tobias    228:        int c, quoted = 0, quotes = 0, qerr = 0, escape = 0, nonkw = 0;
                    229:        unsigned long qpos = 0;
1.29      millert   230:        size_t i;
1.1       tedu      231:
                    232:        p = buf;
                    233:        ebuf = buf + sizeof(buf);
1.9       zhuk      234:
1.5       benno     235: repeat:
1.9       zhuk      236:        /* skip whitespace first */
                    237:        for (c = getc(yyfp); c == ' ' || c == '\t'; c = getc(yyfp))
1.10      zhuk      238:                yylval.colno++;
1.9       zhuk      239:
                    240:        /* check for special one-character constructions */
1.1       tedu      241:        switch (c) {
1.9       zhuk      242:                case '\n':
1.10      zhuk      243:                        yylval.colno = 0;
                    244:                        yylval.lineno++;
1.9       zhuk      245:                        /* FALLTHROUGH */
                    246:                case '{':
                    247:                case '}':
                    248:                        return c;
                    249:                case '#':
                    250:                        /* skip comments; NUL is allowed; no continuation */
                    251:                        while ((c = getc(yyfp)) != '\n')
                    252:                                if (c == EOF)
1.14      tedu      253:                                        goto eof;
1.10      zhuk      254:                        yylval.colno = 0;
                    255:                        yylval.lineno++;
1.9       zhuk      256:                        return c;
                    257:                case EOF:
1.14      tedu      258:                        goto eof;
1.1       tedu      259:        }
1.9       zhuk      260:
                    261:        /* parsing next word */
1.10      zhuk      262:        for (;; c = getc(yyfp), yylval.colno++) {
1.3       zhuk      263:                switch (c) {
1.9       zhuk      264:                case '\0':
1.30      tobias    265:                        yyerror("unallowed character NUL in column %lu",
1.11      deraadt   266:                            yylval.colno + 1);
1.9       zhuk      267:                        escape = 0;
                    268:                        continue;
                    269:                case '\\':
                    270:                        escape = !escape;
                    271:                        if (escape)
                    272:                                continue;
                    273:                        break;
1.3       zhuk      274:                case '\n':
1.30      tobias    275:                        if (quotes && !qerr) {
                    276:                                yyerror("unterminated quotes in column %lu",
1.10      zhuk      277:                                    qpos + 1);
1.30      tobias    278:                                qerr = 1;
                    279:                        }
1.9       zhuk      280:                        if (escape) {
                    281:                                nonkw = 1;
                    282:                                escape = 0;
1.30      tobias    283:                                yylval.colno = ULONG_MAX;
1.12      mikeb     284:                                yylval.lineno++;
1.9       zhuk      285:                                continue;
                    286:                        }
                    287:                        goto eow;
                    288:                case EOF:
                    289:                        if (escape)
1.30      tobias    290:                                yyerror("unterminated escape in column %lu",
1.10      zhuk      291:                                    yylval.colno);
1.30      tobias    292:                        if (quotes && !qerr)
                    293:                                yyerror("unterminated quotes in column %lu",
1.10      zhuk      294:                                    qpos + 1);
                    295:                        goto eow;
1.3       zhuk      296:                case '{':
                    297:                case '}':
                    298:                case '#':
                    299:                case ' ':
                    300:                case '\t':
1.9       zhuk      301:                        if (!escape && !quotes)
                    302:                                goto eow;
                    303:                        break;
                    304:                case '"':
                    305:                        if (!escape) {
1.30      tobias    306:                                quoted = 1;
1.9       zhuk      307:                                quotes = !quotes;
                    308:                                if (quotes) {
                    309:                                        nonkw = 1;
1.30      tobias    310:                                        qerr = 0;
1.10      zhuk      311:                                        qpos = yylval.colno;
1.9       zhuk      312:                                }
                    313:                                continue;
                    314:                        }
1.3       zhuk      315:                }
1.1       tedu      316:                *p++ = c;
1.13      tedu      317:                if (p == ebuf) {
1.10      zhuk      318:                        yyerror("too long line");
1.13      tedu      319:                        p = buf;
                    320:                }
1.9       zhuk      321:                escape = 0;
1.1       tedu      322:        }
1.9       zhuk      323:
1.3       zhuk      324: eow:
1.1       tedu      325:        *p = 0;
                    326:        if (c != EOF)
                    327:                ungetc(c, yyfp);
1.9       zhuk      328:        if (p == buf) {
                    329:                /*
1.11      deraadt   330:                 * There could be a number of reasons for empty buffer,
                    331:                 * and we handle all of them here, to avoid cluttering
                    332:                 * the main loop.
1.9       zhuk      333:                 */
                    334:                if (c == EOF)
1.14      tedu      335:                        goto eof;
1.30      tobias    336:                else if (!quoted)    /* accept, e.g., empty args: cmd foo args "" */
1.9       zhuk      337:                        goto repeat;
                    338:        }
                    339:        if (!nonkw) {
                    340:                for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
                    341:                        if (strcmp(buf, keywords[i].word) == 0)
                    342:                                return keywords[i].token;
                    343:                }
1.1       tedu      344:        }
                    345:        if ((str = strdup(buf)) == NULL)
1.27      krw       346:                err(1, "%s", __func__);
1.1       tedu      347:        yylval.str = str;
                    348:        return TSTRING;
1.14      tedu      349:
                    350: eof:
                    351:        if (ferror(yyfp))
                    352:                yyerror("input error reading config");
                    353:        return 0;
1.1       tedu      354: }