From: Vadim Zhukov Date: Wed, 22 Jul 2015 20:15:24 +0000 (+0000) Subject: Implement quoting support in doas.conf. Now you can pass environment X-Git-Tag: v0.1~65 X-Git-Url: https://git.armaanb.net/?p=opendoas.git;a=commitdiff_plain;h=2b0167bb7af792320ca8c2655297598891004633 Implement quoting support in doas.conf. Now you can pass environment variables and arguments with almost any values. As a bonus, doas will now point to exact place where syntax error occured most of times; there is some room for improvement, though. okay tedu@ --- diff --git a/doas.conf.5 b/doas.conf.5 index 6f93164..dd006a5 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: doas.conf.5,v 1.8 2015/07/21 11:04:06 zhuk Exp $ +.\" $OpenBSD: doas.conf.5,v 1.9 2015/07/22 06:30:12 jmc Exp $ .\" .\"Copyright (c) 2015 Ted Unangst .\" @@ -13,7 +13,7 @@ .\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.Dd $Mdocdate: July 21 2015 $ +.Dd $Mdocdate: July 22 2015 $ .Dt DOAS.CONF 5 .Os .Sh NAME @@ -82,11 +82,25 @@ alone means that command should be run without any arguments. .Pp The last matching rule determines the action taken. .Pp -The current line can be extended over multiple lines using a backslash -.Pq Sq \e . Comments can be put anywhere in the file using a hash mark .Pq Sq # , and extend to the end of the current line. +.Pp +The following quoting rules apply: +.Bl -dash +.It +The text between a pair of double quotes +.Pq Sq \&" +is taken as is. +.It +The backslash +.Pq Sq \e +escapes next character, including new line character, outside comment; +as a result, comments may not be extended over multiple lines. +.It +If quotes or backslash are used in the word, this word won't be +considered a keyword. +.El .Sh EXAMPLES The following example permits users in group wsrc to build ports, wheel to execute commands as root while keeping the environment diff --git a/parse.y b/parse.y index eabb939..bd7d7e6 100644 --- a/parse.y +++ b/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.7 2015/07/21 11:04:06 zhuk Exp $ */ +/* $OpenBSD: parse.y,v 1.8 2015/07/21 16:12:04 tedu Exp $ */ /* * Copyright (c) 2015 Ted Unangst * @@ -190,59 +190,114 @@ struct keyword { int yylex(void) { + static int colno = 1, lineno = 1; + char buf[1024], *ebuf, *p, *str; - int i, c, next; + int i, c, quotes = 0, escape = 0, qpos = 0, nonkw = 0; p = buf; ebuf = buf + sizeof(buf); + repeat: - c = getc(yyfp); + /* skip whitespace first */ + for (c = getc(yyfp); c == ' ' || c == '\t'; c = getc(yyfp)) + colno++; + + /* check for special one-character constructions */ switch (c) { - case ' ': - case '\t': - goto repeat; /* skip spaces */ - case '\\': - next = getc(yyfp); - if (next == '\n') - goto repeat; - else - c = next; - case '\n': - case '{': - case '}': - return c; - case '#': - while ((c = getc(yyfp)) != '\n' && c != EOF) - ; /* skip comments */ - if (c == EOF) + case '\n': + colno = 1; + lineno++; + /* FALLTHROUGH */ + case '{': + case '}': + return c; + case '#': + /* skip comments; NUL is allowed; no continuation */ + while ((c = getc(yyfp)) != '\n') + if (c == EOF) + return 0; + colno = 1; + lineno++; + return c; + case EOF: return 0; - return c; - case EOF: - return 0; } - while (1) { + + /* parsing next word */ + for (;; c = getc(yyfp), colno++) { switch (c) { + case '\0': + yyerror("unallowed character NUL at " + "line %d, column %d", lineno, colno); + escape = 0; + continue; + case '\\': + escape = !escape; + if (escape) + continue; + break; case '\n': + if (quotes) + yyerror("unterminated quotes at line %d, column %d", + lineno, qpos); + if (escape) { + nonkw = 1; + escape = 0; + continue; + } + goto eow; + case EOF: + if (escape) + yyerror("unterminated escape at line %d, column %d", + lineno, colno - 1); + if (quotes) + yyerror("unterminated quotes at line %d, column %d", + lineno, qpos); + /* FALLTHROUGH */ case '{': case '}': case '#': case ' ': case '\t': - case EOF: - goto eow; + if (!escape && !quotes) + goto eow; + break; + case '"': + if (!escape) { + quotes = !quotes; + if (quotes) { + nonkw = 1; + qpos = colno; + } + continue; + } } *p++ = c; if (p == ebuf) - yyerror("too much stuff"); - c = getc(yyfp); + yyerror("too long line %d", lineno); + escape = 0; } + eow: *p = 0; if (c != EOF) ungetc(c, yyfp); - for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { - if (strcmp(buf, keywords[i].word) == 0) - return keywords[i].token; + if (p == buf) { + /* + * There could be a number of reasons for empty buffer, and we handle + * all of them here, to avoid cluttering the main loop. + */ + if (c == EOF) + return 0; + else if (!qpos) /* accept, e.g., empty args: cmd foo args "" */ + goto repeat; + } + if (!nonkw) { + for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { + if (strcmp(buf, keywords[i].word) == 0) + return keywords[i].token; + } } if ((str = strdup(buf)) == NULL) err(1, "strdup");