]> git.armaanb.net Git - opendoas.git/commitdiff
Implement quoting support in doas.conf. Now you can pass environment
authorVadim Zhukov <zhuk@openbsd.org>
Wed, 22 Jul 2015 20:15:24 +0000 (20:15 +0000)
committerVadim Zhukov <zhuk@openbsd.org>
Wed, 22 Jul 2015 20:15:24 +0000 (20:15 +0000)
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@

doas.conf.5
parse.y

index 6f9316411a240d3009fd656e8b052ddeea09f84b..dd006a584661d27b2695cce99035c624022e9968 100644 (file)
@@ -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 <tedu@openbsd.org>
 .\"
@@ -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 eabb939b5e9ec85441dcf26ae6d54e000eb07132..bd7d7e61713008fa0b8ad604aeb2b02fbb2ebf72 100644 (file)
--- 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 <tedu@openbsd.org>
  *
@@ -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");