fix typo in changelog
[debian/sudo] / parse.lex
1 %{
2 /*
3  * Copyright (c) 1996, 1998-2004, 2007
4  *      Todd C. Miller <Todd.Miller@courtesan.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
18  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
19  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20  *
21  * Sponsored in part by the Defense Advanced Research Projects
22  * Agency (DARPA) and Air Force Research Laboratory, Air Force
23  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
24  */
25
26 #include <config.h>
27
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <stdio.h>
31 #ifdef STDC_HEADERS
32 # include <stdlib.h>
33 # include <stddef.h>
34 #else
35 # ifdef HAVE_STDLIB_H
36 #  include <stdlib.h>
37 # endif
38 #endif /* STDC_HEADERS */
39 #ifdef HAVE_STRING_H
40 # include <string.h>
41 #else
42 # ifdef HAVE_STRINGS_H
43 #  include <strings.h>
44 # endif
45 #endif /* HAVE_STRING_H */
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif /* HAVE_UNISTD_H */
49 #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
50 # include <malloc.h>
51 #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
52 #include <ctype.h>
53 #include "sudo.h"
54 #include "parse.h"
55 #include <sudo.tab.h>
56
57 #ifndef lint
58 __unused static const char rcsid[] = "$Sudo: parse.lex,v 1.132.2.10 2008/06/26 11:53:50 millert Exp $";
59 #endif /* lint */
60
61 #undef yywrap           /* guard against a yywrap macro */
62
63 extern YYSTYPE yylval;
64 extern int clearaliases;
65 int sudolineno = 1;
66 static int sawspace = 0;
67 static int arg_len = 0;
68 static int arg_size = 0;
69
70 static int ipv6_valid           __P((const char *s));
71 static void _fill               __P((char *, int, int));
72 static void append              __P((char *, int));
73 static void fill_cmnd           __P((char *, int));
74 static void fill_args           __P((char *, int, int));
75 extern void reset_aliases       __P((void));
76 extern void yyerror             __P((char *));
77
78 #define fill(a, b)              _fill(a, b, 0)
79
80 /* realloc() to size + COMMANDARGINC to make room for command args */
81 #define COMMANDARGINC   64
82
83 #ifdef TRACELEXER
84 #define LEXTRACE(msg)   fputs(msg, stderr)
85 #else
86 #define LEXTRACE(msg)
87 #endif
88 %}
89
90 HEX16                   [0-9A-Fa-f]{1,4}
91 OCTET                   (1?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5])
92 IPV4ADDR                {OCTET}(\.{OCTET}){3}
93 IPV6ADDR                ({HEX16}?:){2,7}{HEX16}?|({HEX16}?:){2,6}:{IPV4ADDR}
94
95 HOSTNAME                [[:alnum:]_-]+
96 WORD                    ([^#>@!=:,\(\) \t\n\\]|\\[^\n])+
97 ENVAR                   ([^#!=, \t\n\\\"]|\\[^\n])([^#=, \t\n\\]|\\[^\n])*
98 DEFVAR                  [a-z_]+
99
100 /* XXX - convert GOTRUNAS to exclusive state (GOTDEFS cannot be) */
101 %s      GOTRUNAS
102 %s      GOTDEFS
103 %x      GOTCMND
104 %x      STARTDEFS
105 %x      INDEFS
106 %x      INSTR
107
108 %%
109 <GOTDEFS>[[:blank:]]+   BEGIN STARTDEFS;
110
111 <STARTDEFS>{DEFVAR}     {
112                             BEGIN INDEFS;
113                             LEXTRACE("DEFVAR ");
114                             fill(yytext, yyleng);
115                             return(DEFVAR);
116                         }
117
118 <INDEFS>{
119     ,                   {
120                             BEGIN STARTDEFS;
121                             LEXTRACE(", ");
122                             return(',');
123                         }                       /* return ',' */
124
125     =                   {
126                             LEXTRACE("= ");
127                             return('=');
128                         }                       /* return '=' */
129
130     \+=                 {
131                             LEXTRACE("+= ");
132                             return('+');
133                         }                       /* return '+' */
134
135     -=                  {
136                             LEXTRACE("-= ");
137                             return('-');
138                         }                       /* return '-' */
139
140     \"                  {
141                             LEXTRACE("BEGINSTR ");
142                             yylval.string = NULL;
143                             BEGIN INSTR;
144                         }
145
146     {ENVAR}             {
147                             LEXTRACE("WORD(2) ");
148                             fill(yytext, yyleng);
149                             return(WORD);
150                         }
151 }
152
153 <INSTR>{
154     \\[[:blank:]]*\n[[:blank:]]*        {
155                             /* Line continuation char followed by newline. */
156                             ++sudolineno;
157                             LEXTRACE("\n");
158                         }
159
160     \"                  {
161                             LEXTRACE("ENDSTR ");
162                             BEGIN INDEFS;
163                             return(WORD);
164                         }
165
166     \\                  {
167                             LEXTRACE("BACKSLASH ");
168                             append(yytext, yyleng);
169                         }
170
171     ([^\"\n\\]|\\\")+   {
172                             LEXTRACE("STRBODY ");
173                             append(yytext, yyleng);
174                         }
175 }
176
177 <GOTCMND>{
178     \\[\*\?\[\]\!]      {
179                             /* quoted fnmatch glob char, pass verbatim */
180                             LEXTRACE("QUOTEDCHAR ");
181                             fill_args(yytext, 2, sawspace);
182                             sawspace = FALSE;
183                         }
184
185     \\[:\\,= \t#]       {
186                             /* quoted sudoers special char, strip backslash */
187                             LEXTRACE("QUOTEDCHAR ");
188                             fill_args(yytext + 1, 1, sawspace);
189                             sawspace = FALSE;
190                         }
191
192     [#:\,=\n]           {
193                             BEGIN INITIAL;
194                             unput(*yytext);
195                             return(COMMAND);
196                         }                       /* end of command line args */
197
198     [^\\:, \t\n]+       {
199                             LEXTRACE("ARG ");
200                             fill_args(yytext, yyleng, sawspace);
201                             sawspace = FALSE;
202                         }                       /* a command line arg */
203 }
204
205 <INITIAL>^Defaults[:@>]? {
206                             BEGIN GOTDEFS;
207                             switch (yytext[8]) {
208                                 case ':':
209                                     LEXTRACE("DEFAULTS_USER ");
210                                     return(DEFAULTS_USER);
211                                 case '>':
212                                     LEXTRACE("DEFAULTS_RUNAS ");
213                                     return(DEFAULTS_RUNAS);
214                                 case '@':
215                                     LEXTRACE("DEFAULTS_HOST ");
216                                     return(DEFAULTS_HOST);
217                                 default:
218                                     LEXTRACE("DEFAULTS ");
219                                     return(DEFAULTS);
220                             }
221                         }
222
223 <INITIAL>^(Host|Cmnd|User|Runas)_Alias  {
224                             fill(yytext, yyleng);
225                             switch (*yytext) {
226                                 case 'H':
227                                     LEXTRACE("HOSTALIAS ");
228                                     return(HOSTALIAS);
229                                 case 'C':
230                                     LEXTRACE("CMNDALIAS ");
231                                     return(CMNDALIAS);
232                                 case 'U':
233                                     LEXTRACE("USERALIAS ");
234                                     return(USERALIAS);
235                                 case 'R':
236                                     LEXTRACE("RUNASALIAS ");
237                                     BEGIN GOTRUNAS;
238                                     return(RUNASALIAS);
239                             }
240                         }
241
242 NOPASSWD[[:blank:]]*:   {
243                                 /* cmnd does not require passwd for this user */
244                                 LEXTRACE("NOPASSWD ");
245                                 return(NOPASSWD);
246                         }
247
248 PASSWD[[:blank:]]*:     {
249                                 /* cmnd requires passwd for this user */
250                                 LEXTRACE("PASSWD ");
251                                 return(PASSWD);
252                         }
253
254 NOEXEC[[:blank:]]*:     {
255                                 LEXTRACE("NOEXEC ");
256                                 return(NOEXEC);
257                         }
258
259 EXEC[[:blank:]]*:       {
260                                 LEXTRACE("EXEC ");
261                                 return(EXEC);
262                         }
263
264 SETENV[[:blank:]]*:     {
265                                 LEXTRACE("SETENV ");
266                                 return(SETENV);
267                         }
268
269 NOSETENV[[:blank:]]*:   {
270                                 LEXTRACE("NOSETENV ");
271                                 return(NOSETENV);
272                         }
273
274 \+{WORD}                {
275                             /* netgroup */
276                             fill(yytext, yyleng);
277                             LEXTRACE("NETGROUP ");
278                             return(NETGROUP);
279                         }
280
281 \%{WORD}                {
282                             /* UN*X group */
283                             fill(yytext, yyleng);
284                             LEXTRACE("GROUP ");
285                             return(USERGROUP);
286                         }
287
288 {IPV4ADDR}(\/{IPV4ADDR})? {
289                             fill(yytext, yyleng);
290                             LEXTRACE("NTWKADDR ");
291                             return(NTWKADDR);
292                         }
293
294 {IPV4ADDR}\/([12][0-9]*|3[0-2]*) {
295                             fill(yytext, yyleng);
296                             LEXTRACE("NTWKADDR ");
297                             return(NTWKADDR);
298                         }
299
300 {IPV6ADDR}(\/{IPV6ADDR})? {
301                             if (!ipv6_valid(yytext)) {
302                                 LEXTRACE("ERROR ");
303                                 return(ERROR);
304                             }
305                             fill(yytext, yyleng);
306                             LEXTRACE("NTWKADDR ");
307                             return(NTWKADDR);
308                         }
309
310 {IPV6ADDR}\/([0-9]|[1-9][0-9]|1[01][0-9]|12[0-8]) {
311                             if (!ipv6_valid(yytext)) {
312                                 LEXTRACE("ERROR ");
313                                 return(ERROR);
314                             }
315                             fill(yytext, yyleng);
316                             LEXTRACE("NTWKADDR ");
317                             return(NTWKADDR);
318                         }
319
320 <INITIAL>\(             {
321                                 BEGIN GOTRUNAS;
322                                 LEXTRACE("RUNAS ");
323                                 return (RUNAS);
324                         }
325
326 [[:upper:]][[:upper:][:digit:]_]* {
327                             if (strcmp(yytext, "ALL") == 0) {
328                                 LEXTRACE("ALL ");
329                                 return(ALL);
330                             }
331 #ifdef HAVE_SELINUX
332                             /* XXX - restrict type/role to initial state */
333                             if (strcmp(yytext, "TYPE") == 0) {
334                                 LEXTRACE("TYPE ");
335                                 return(TYPE);
336                             }
337                             if (strcmp(yytext, "ROLE") == 0) {
338                                 LEXTRACE("ROLE ");
339                                 return(ROLE);
340                             }
341 #endif /* HAVE_SELINUX */
342                             fill(yytext, yyleng);
343                             LEXTRACE("ALIAS ");
344                             return(ALIAS);
345                         }
346
347 <GOTRUNAS>(#[0-9-]+|{WORD}) {
348                             /* username/uid that user can run command as */
349                             fill(yytext, yyleng);
350                             LEXTRACE("WORD(3) ");
351                             return(WORD);
352                         }
353
354 <GOTRUNAS>#[^0-9-].*\n  {
355                             BEGIN INITIAL;
356                             ++sudolineno;
357                             LEXTRACE("\n");
358                             return(COMMENT);
359                         }
360
361 <GOTRUNAS>\)            {
362                             BEGIN INITIAL;
363                         }
364
365 sudoedit                {
366                             BEGIN GOTCMND;
367                             LEXTRACE("COMMAND ");
368                             fill_cmnd(yytext, yyleng);
369                         }                       /* sudo -e */
370
371 \/(\\[\,:= \t#]|[^\,:=\\ \t\n#])+       {
372                             /* directories can't have args... */
373                             if (yytext[yyleng - 1] == '/') {
374                                 LEXTRACE("COMMAND ");
375                                 fill_cmnd(yytext, yyleng);
376                                 return(COMMAND);
377                             } else {
378                                 BEGIN GOTCMND;
379                                 LEXTRACE("COMMAND ");
380                                 fill_cmnd(yytext, yyleng);
381                             }
382                         }                       /* a pathname */
383
384 <INITIAL,GOTDEFS>{WORD} {
385                             /* a word */
386                             fill(yytext, yyleng);
387                             LEXTRACE("WORD(4) ");
388                             return(WORD);
389                         }
390
391 ,                       {
392                             LEXTRACE(", ");
393                             return(',');
394                         }                       /* return ',' */
395
396 =                       {
397                             LEXTRACE("= ");
398                             return('=');
399                         }                       /* return '=' */
400
401 :                       {
402                             LEXTRACE(": ");
403                             return(':');
404                         }                       /* return ':' */
405
406 <*>!+                   {
407                             if (yyleng % 2 == 1)
408                                 return('!');    /* return '!' */
409                         }
410
411 <*>\n                   {
412                             BEGIN INITIAL;
413                             ++sudolineno;
414                             LEXTRACE("\n");
415                             return(COMMENT);
416                         }                       /* return newline */
417
418 <*>[[:blank:]]+         {                       /* throw away space/tabs */
419                             sawspace = TRUE;    /* but remember for fill_args */
420                         }
421
422 <*>\\[[:blank:]]*\n     {
423                             sawspace = TRUE;    /* remember for fill_args */
424                             ++sudolineno;
425                             LEXTRACE("\n\t");
426                         }                       /* throw away EOL after \ */
427
428 <INITIAL,STARTDEFS,INDEFS>#.*\n {
429                             BEGIN INITIAL;
430                             ++sudolineno;
431                             LEXTRACE("\n");
432                             return(COMMENT);
433                         }                       /* return comments */
434
435 <*>.                    {
436                             LEXTRACE("ERROR ");
437                             return(ERROR);
438                         }       /* parse error */
439
440 <*><<EOF>>              {
441                             if (YY_START != INITIAL) {
442                                 BEGIN INITIAL;
443                                 LEXTRACE("ERROR ");
444                                 return(ERROR);
445                             }
446                             yyterminate();
447                         }
448
449 %%
450 static void
451 _fill(src, len, olen)
452     char *src;
453     int len, olen;
454 {
455     int i, j;
456     char *dst;
457
458     dst = olen ? realloc(yylval.string, olen + len + 1) : malloc(len + 1);
459     if (dst == NULL) {
460         yyerror("unable to allocate memory");
461         return;
462     }
463     yylval.string = dst;
464
465     /* Copy the string and collapse any escaped characters. */
466     dst += olen;
467     for (i = 0, j = 0; i < len; i++, j++) {
468         if (src[i] == '\\' && i != len - 1)
469             dst[j] = src[++i];
470         else
471             dst[j] = src[i];
472     }
473     dst[j] = '\0';
474 }
475
476 static void
477 append(src, len)
478     char *src;
479     int len;
480 {
481     int olen = 0;
482
483     if (yylval.string != NULL)
484         olen = strlen(yylval.string);
485
486     _fill(src, len, olen);
487 }
488
489 static void
490 fill_cmnd(s, len)
491     char *s;
492     int len;
493 {
494     arg_len = arg_size = 0;
495
496     yylval.command.cmnd = (char *) malloc(++len);
497     if (yylval.command.cmnd == NULL) {
498         yyerror("unable to allocate memory");
499         return;
500     }
501
502     /* copy the string and NULL-terminate it (escapes handled by fnmatch) */
503     (void) strlcpy(yylval.command.cmnd, s, len);
504
505     yylval.command.args = NULL;
506 }
507
508 static void
509 fill_args(s, len, addspace)
510     char *s;
511     int len;
512     int addspace;
513 {
514     int new_len;
515     char *p;
516
517     if (yylval.command.args == NULL) {
518         addspace = 0;
519         new_len = len;
520     } else
521         new_len = arg_len + len + addspace;
522
523     if (new_len >= arg_size) {
524         /* Allocate more space than we need for subsequent args */
525         while (new_len >= (arg_size += COMMANDARGINC))
526             ;
527
528         p = yylval.command.args ?
529             (char *) realloc(yylval.command.args, arg_size) :
530             (char *) malloc(arg_size);
531         if (p == NULL) {
532             efree(yylval.command.args);
533             yyerror("unable to allocate memory");
534             return;
535         } else
536             yylval.command.args = p;
537     }
538
539     /* Efficiently append the arg (with a leading space if needed). */
540     p = yylval.command.args + arg_len;
541     if (addspace)
542         *p++ = ' ';
543     if (strlcpy(p, s, arg_size - (p - yylval.command.args)) != len)
544         yyerror("fill_args: buffer overflow");  /* paranoia */
545     arg_len = new_len;
546 }
547
548 /*
549  * Check to make sure an IPv6 address does not contain multiple instances
550  * of the string "::".  Assumes strlen(s) >= 1.
551  * Returns TRUE if address is valid else FALSE.
552  */
553 static int
554 ipv6_valid(s)
555     const char *s;
556 {
557     int nmatch = 0;
558
559     for (; *s != '\0'; s++) {
560         if (s[0] == ':' && s[1] == ':') {
561             if (++nmatch > 1)
562                 break;
563         }
564         if (s[0] == '/')
565             nmatch = 0;                 /* reset if we hit netmask */
566     }
567
568     return (nmatch <= 1);
569 }
570
571 int
572 yywrap()
573 {
574
575     /* Free space used by the aliases unless called by testsudoers. */
576     if (clearaliases)
577         reset_aliases();
578
579     return(TRUE);
580 }