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

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