man pages get built in the build- directories
[debian/sudo] / toke.l
1 %{
2 /*
3  * Copyright (c) 1996, 1998-2005, 2007-2008
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 <gram.h>
56
57 #ifndef lint
58 __unused static const char rcsid[] = "$Sudo: toke.l,v 1.27 2008/11/24 00:41:36 millert Exp $";
59 #endif /* lint */
60
61 extern YYSTYPE yylval;
62 int sudolineno = 1;
63 char *sudoers;
64 static int sawspace = 0;
65 static int arg_len = 0;
66 static int arg_size = 0;
67
68 static int append               __P((char *, int));
69 static int _fill                __P((char *, int, int));
70 static int fill_cmnd            __P((char *, int));
71 static int fill_args            __P((char *, int, int));
72 static int switch_buffer        __P((char *));
73 static int ipv6_valid           __P((const char *s));
74 static char *parse_include      __P((char *));
75 extern void yyerror             __P((const char *));
76
77 #define fill(a, b)              _fill(a, b, 0)
78
79 #define push_include(_p)        (switch_buffer((_p)))
80 #define pop_include()           (switch_buffer(NULL))
81
82 /* realloc() to size + COMMANDARGINC to make room for command args */
83 #define COMMANDARGINC   64
84
85 #ifdef TRACELEXER
86 #define LEXTRACE(msg)   fputs(msg, stderr)
87 #else
88 #define LEXTRACE(msg)
89 #endif
90 %}
91
92 HEX16                   [0-9A-Fa-f]{1,4}
93 OCTET                   (1?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5])
94 IPV4ADDR                {OCTET}(\.{OCTET}){3}
95 IPV6ADDR                ({HEX16}?:){2,7}{HEX16}?|({HEX16}?:){2,6}:{IPV4ADDR}
96
97 HOSTNAME                [[:alnum:]_-]+
98 WORD                    ([^#>!=:,\(\) \t\n\\]|\\[^\n])+
99 ID                      #-?[0-9]+
100 PATH                    \/(\\[\,:= \t#]|[^\,:=\\ \t\n#])+
101 ENVAR                   ([^#!=, \t\n\\\"]|\\[^\n])([^#=, \t\n\\\"]|\\[^\n])*
102 DEFVAR                  [a-z_]+
103
104 %option nounput
105 %option noyywrap
106
107 %s      GOTDEFS
108 %x      GOTCMND
109 %x      STARTDEFS
110 %x      INDEFS
111 %x      INSTR
112
113 %%
114 <GOTDEFS>[[:blank:]]+   BEGIN STARTDEFS;
115
116 <STARTDEFS>{DEFVAR}     {
117                             BEGIN INDEFS;
118                             LEXTRACE("DEFVAR ");
119                             if (!fill(yytext, yyleng))
120                                 yyterminate();
121                             return(DEFVAR);
122                         }
123
124 <INDEFS>{
125     ,                   {
126                             BEGIN STARTDEFS;
127                             LEXTRACE(", ");
128                             return(',');
129                         }                       /* return ',' */
130
131     =                   {
132                             LEXTRACE("= ");
133                             return('=');
134                         }                       /* return '=' */
135
136     \+=                 {
137                             LEXTRACE("+= ");
138                             return('+');
139                         }                       /* return '+' */
140
141     -=                  {
142                             LEXTRACE("-= ");
143                             return('-');
144                         }                       /* return '-' */
145
146     \"                  {
147                             LEXTRACE("BEGINSTR ");
148                             yylval.string = NULL;
149                             BEGIN INSTR;
150                         }
151
152     {ENVAR}             {
153                             LEXTRACE("WORD(2) ");
154                             if (!fill(yytext, yyleng))
155                                 yyterminate();
156                             return(WORD);
157                         }
158 }
159
160 <INSTR>{
161     \\[[:blank:]]*\n[[:blank:]]*        {
162                             /* Line continuation char followed by newline. */
163                             ++sudolineno;
164                             LEXTRACE("\n");
165                         }
166
167     \"                  {
168                             LEXTRACE("ENDSTR ");
169                             BEGIN INDEFS;
170                             return(WORD);
171                         }
172
173     \\                  {
174                             LEXTRACE("BACKSLASH ");
175                             if (!append(yytext, yyleng))
176                                 yyterminate();
177                         }
178
179     ([^\"\n\\]|\\\")+   {
180                             LEXTRACE("STRBODY ");
181                             if (!append(yytext, yyleng))
182                                 yyterminate();
183                         }
184 }
185
186 <GOTCMND>{
187     \\[\*\?\[\]\!]      {
188                             /* quoted fnmatch glob char, pass verbatim */
189                             LEXTRACE("QUOTEDCHAR ");
190                             if (!fill_args(yytext, 2, sawspace))
191                                 yyterminate();
192                             sawspace = FALSE;
193                         }
194
195     \\[:\\,= \t#]       {
196                             /* quoted sudoers special char, strip backslash */
197                             LEXTRACE("QUOTEDCHAR ");
198                             if (!fill_args(yytext + 1, 1, sawspace))
199                                 yyterminate();
200                             sawspace = FALSE;
201                         }
202
203     [#:\,=\n]           {
204                             BEGIN INITIAL;
205                             yyless(0);
206                             return(COMMAND);
207                         }                       /* end of command line args */
208
209     [^\\:, \t\n]+       {
210                             LEXTRACE("ARG ");
211                             if (!fill_args(yytext, yyleng, sawspace))
212                                 yyterminate();
213                             sawspace = FALSE;
214                         }                       /* a command line arg */
215 }
216
217 <INITIAL>^#include[[:blank:]]+\/.*\n {
218                             char *path;
219
220                             if ((path = parse_include(yytext)) == NULL)
221                                 yyterminate();
222
223                             LEXTRACE("INCLUDE\n");
224
225                             /* Push current buffer and switch to include file */
226                             if (!push_include(path))
227                                 yyterminate();
228                         }
229
230 <INITIAL>^[[:blank:]]*Defaults([:@>\!]{WORD})? {
231                             int n;
232                             for (n = 0; isblank((unsigned char)yytext[n]); n++)
233                                 continue;
234                             n += 8;
235                             BEGIN GOTDEFS;
236                             switch (yytext[n++]) {
237                                 case ':':
238                                     yyless(n);
239                                     LEXTRACE("DEFAULTS_USER ");
240                                     return(DEFAULTS_USER);
241                                 case '>':
242                                     yyless(n);
243                                     LEXTRACE("DEFAULTS_RUNAS ");
244                                     return(DEFAULTS_RUNAS);
245                                 case '@':
246                                     yyless(n);
247                                     LEXTRACE("DEFAULTS_HOST ");
248                                     return(DEFAULTS_HOST);
249                                 case '!':
250                                     yyless(n);
251                                     LEXTRACE("DEFAULTS_CMND ");
252                                     return(DEFAULTS_CMND);
253                                 default:
254                                     LEXTRACE("DEFAULTS ");
255                                     return(DEFAULTS);
256                             }
257                         }
258
259 <INITIAL>^[[:blank:]]*(Host|Cmnd|User|Runas)_Alias      {
260                             int n;
261                             for (n = 0; isblank((unsigned char)yytext[n]); n++)
262                                 continue;
263                             switch (yytext[n]) {
264                                 case 'H':
265                                     LEXTRACE("HOSTALIAS ");
266                                     return(HOSTALIAS);
267                                 case 'C':
268                                     LEXTRACE("CMNDALIAS ");
269                                     return(CMNDALIAS);
270                                 case 'U':
271                                     LEXTRACE("USERALIAS ");
272                                     return(USERALIAS);
273                                 case 'R':
274                                     LEXTRACE("RUNASALIAS ");
275                                     return(RUNASALIAS);
276                             }
277                         }
278
279 NOPASSWD[[:blank:]]*:   {
280                                 /* cmnd does not require passwd for this user */
281                                 LEXTRACE("NOPASSWD ");
282                                 return(NOPASSWD);
283                         }
284
285 PASSWD[[:blank:]]*:     {
286                                 /* cmnd requires passwd for this user */
287                                 LEXTRACE("PASSWD ");
288                                 return(PASSWD);
289                         }
290
291 NOEXEC[[:blank:]]*:     {
292                                 LEXTRACE("NOEXEC ");
293                                 return(NOEXEC);
294                         }
295
296 EXEC[[:blank:]]*:       {
297                                 LEXTRACE("EXEC ");
298                                 return(EXEC);
299                         }
300
301 SETENV[[:blank:]]*:     {
302                                 LEXTRACE("SETENV ");
303                                 return(SETENV);
304                         }
305
306 NOSETENV[[:blank:]]*:   {
307                                 LEXTRACE("NOSETENV ");
308                                 return(NOSETENV);
309                         }
310
311 \+{WORD}                {
312                             /* netgroup */
313                             if (!fill(yytext, yyleng))
314                                 yyterminate();
315                             LEXTRACE("NETGROUP ");
316                             return(NETGROUP);
317                         }
318
319 \%{WORD}                {
320                             /* UN*X group */
321                             if (!fill(yytext, yyleng))
322                                 yyterminate();
323                             LEXTRACE("USERGROUP ");
324                             return(USERGROUP);
325                         }
326
327 {IPV4ADDR}(\/{IPV4ADDR})? {
328                             if (!fill(yytext, yyleng))
329                                 yyterminate();
330                             LEXTRACE("NTWKADDR ");
331                             return(NTWKADDR);
332                         }
333
334 {IPV4ADDR}\/([12][0-9]*|3[0-2]*) {
335                             if (!fill(yytext, yyleng))
336                                 yyterminate();
337                             LEXTRACE("NTWKADDR ");
338                             return(NTWKADDR);
339                         }
340
341 {IPV6ADDR}(\/{IPV6ADDR})? {
342                             if (!ipv6_valid(yytext)) {
343                                 LEXTRACE("ERROR ");
344                                 return(ERROR);
345                             }
346                             if (!fill(yytext, yyleng))
347                                 yyterminate();
348                             LEXTRACE("NTWKADDR ");
349                             return(NTWKADDR);
350                         }
351
352 {IPV6ADDR}\/([0-9]|[1-9][0-9]|1[01][0-9]|12[0-8]) {
353                             if (!ipv6_valid(yytext)) {
354                                 LEXTRACE("ERROR ");
355                                 return(ERROR);
356                             }
357                             if (!fill(yytext, yyleng))
358                                 yyterminate();
359                             LEXTRACE("NTWKADDR ");
360                             return(NTWKADDR);
361                         }
362
363 [[:upper:]][[:upper:][:digit:]_]* {
364                             if (strcmp(yytext, "ALL") == 0) {
365                                 LEXTRACE("ALL ");
366                                 return(ALL);
367                             }
368 #ifdef HAVE_SELINUX
369                             /* XXX - restrict type/role to initial state */
370                             if (strcmp(yytext, "TYPE") == 0) {
371                                 LEXTRACE("TYPE ");
372                                 return(TYPE);
373                             }
374                             if (strcmp(yytext, "ROLE") == 0) {
375                                 LEXTRACE("ROLE ");
376                                 return(ROLE);
377                             }
378 #endif /* HAVE_SELINUX */
379                             if (!fill(yytext, yyleng))
380                                 yyterminate();
381                             LEXTRACE("ALIAS ");
382                             return(ALIAS);
383                         }
384
385 <GOTDEFS>({PATH}|sudoedit) {
386                             /* no command args allowed for Defaults!/path */
387                             if (!fill_cmnd(yytext, yyleng))
388                                 yyterminate();
389                             LEXTRACE("COMMAND ");
390                             return(COMMAND);
391                         }
392
393 sudoedit                {
394                             BEGIN GOTCMND;
395                             LEXTRACE("COMMAND ");
396                             if (!fill_cmnd(yytext, yyleng))
397                                 yyterminate();
398                         }                       /* sudo -e */
399
400 {PATH}                  {
401                             /* directories can't have args... */
402                             if (yytext[yyleng - 1] == '/') {
403                                 LEXTRACE("COMMAND ");
404                                 if (!fill_cmnd(yytext, yyleng))
405                                     yyterminate();
406                                 return(COMMAND);
407                             } else {
408                                 BEGIN GOTCMND;
409                                 LEXTRACE("COMMAND ");
410                                 if (!fill_cmnd(yytext, yyleng))
411                                     yyterminate();
412                             }
413                         }                       /* a pathname */
414
415 <INITIAL,GOTDEFS>({ID}|{WORD}) {
416                             /* a word */
417                             if (!fill(yytext, yyleng))
418                                 yyterminate();
419                             LEXTRACE("WORD(4) ");
420                             return(WORD);
421                         }
422
423 \(                      {
424                             LEXTRACE("( ");
425                             return ('(');
426                         }
427
428 \)                      {
429                             LEXTRACE(") ");
430                             return(')');
431                         }
432
433 ,                       {
434                             LEXTRACE(", ");
435                             return(',');
436                         }                       /* return ',' */
437
438 =                       {
439                             LEXTRACE("= ");
440                             return('=');
441                         }                       /* return '=' */
442
443 :                       {
444                             LEXTRACE(": ");
445                             return(':');
446                         }                       /* return ':' */
447
448 <*>!+                   {
449                             if (yyleng % 2 == 1)
450                                 return('!');    /* return '!' */
451                         }
452
453 <*>\n                   {
454                             BEGIN INITIAL;
455                             ++sudolineno;
456                             LEXTRACE("\n");
457                             return(COMMENT);
458                         }                       /* return newline */
459
460 <*>[[:blank:]]+         {                       /* throw away space/tabs */
461                             sawspace = TRUE;    /* but remember for fill_args */
462                         }
463
464 <*>\\[[:blank:]]*\n     {
465                             sawspace = TRUE;    /* remember for fill_args */
466                             ++sudolineno;
467                             LEXTRACE("\n\t");
468                         }                       /* throw away EOL after \ */
469
470 <INITIAL,STARTDEFS,INDEFS>#([^\n0-9-].*)?\n     {
471                             BEGIN INITIAL;
472                             ++sudolineno;
473                             LEXTRACE("\n");
474                             return(COMMENT);
475                         }                       /* return comments */
476
477 <*>.                    {
478                             LEXTRACE("ERROR ");
479                             return(ERROR);
480                         }       /* parse error */
481
482 <*><<EOF>>              {
483                             if (YY_START != INITIAL) {
484                                 BEGIN INITIAL;
485                                 LEXTRACE("ERROR ");
486                                 return(ERROR);
487                             }
488                             if (!pop_include())
489                                 yyterminate();
490                         }
491
492 %%
493 static int
494 _fill(src, len, olen)
495     char *src;
496     int len, olen;
497 {
498     int i, j;
499     char *dst;
500
501     dst = olen ? realloc(yylval.string, olen + len + 1) : malloc(len + 1);
502     if (dst == NULL) {
503         yyerror("unable to allocate memory");
504         return(FALSE);
505     }
506     yylval.string = dst;
507
508     /* Copy the string and collapse any escaped characters. */
509     dst += olen;
510     for (i = 0, j = 0; i < len; i++, j++) {
511         if (src[i] == '\\' && i != len - 1)
512             dst[j] = src[++i];
513         else
514             dst[j] = src[i];
515     }
516     dst[j] = '\0';
517     return(TRUE);
518 }
519
520 static int
521 append(src, len)
522     char *src;
523     int len;
524 {
525     int olen = 0;
526
527     if (yylval.string != NULL)
528         olen = strlen(yylval.string);
529
530     return(_fill(src, len, olen));
531 }
532
533 #define SPECIAL(c) \
534     ((c) == ',' || (c) == ':' || (c) == '=' || (c) == ' ' || (c) == '\t' || (c) == '#')
535
536 static int
537 fill_cmnd(src, len)
538     char *src;
539     int len;
540 {
541     char *dst;
542     int i;
543
544     arg_len = arg_size = 0;
545
546     dst = yylval.command.cmnd = (char *) malloc(len + 1);
547     if (yylval.command.cmnd == NULL) {
548         yyerror("unable to allocate memory");
549         return(FALSE);
550     }
551
552     /* Copy the string and collapse any escaped sudo-specific characters. */
553     for (i = 0; i < len; i++) {
554         if (src[i] == '\\' && i != len - 1 && SPECIAL(src[i + 1]))
555             *dst++ = src[++i];
556         else
557             *dst++ = src[i];
558     }
559     *dst = '\0';
560
561     yylval.command.args = NULL;
562     return(TRUE);
563 }
564
565 static int
566 fill_args(s, len, addspace)
567     char *s;
568     int len;
569     int addspace;
570 {
571     int new_len;
572     char *p;
573
574     if (yylval.command.args == NULL) {
575         addspace = 0;
576         new_len = len;
577     } else
578         new_len = arg_len + len + addspace;
579
580     if (new_len >= arg_size) {
581         /* Allocate more space than we need for subsequent args */
582         while (new_len >= (arg_size += COMMANDARGINC))
583             ;
584
585         p = yylval.command.args ?
586             (char *) realloc(yylval.command.args, arg_size) :
587             (char *) malloc(arg_size);
588         if (p == NULL) {
589             efree(yylval.command.args);
590             yyerror("unable to allocate memory");
591             return(FALSE);
592         } else
593             yylval.command.args = p;
594     }
595
596     /* Efficiently append the arg (with a leading space if needed). */
597     p = yylval.command.args + arg_len;
598     if (addspace)
599         *p++ = ' ';
600     if (strlcpy(p, s, arg_size - (p - yylval.command.args)) != len) {
601         yyerror("fill_args: buffer overflow");  /* paranoia */
602         return(FALSE);
603     }
604     arg_len = new_len;
605     return(TRUE);
606 }
607
608 struct sudoers_state {
609     YY_BUFFER_STATE bs;
610     char *path;
611     int lineno;
612 };
613
614 #define MAX_SUDOERS_DEPTH       128
615 #define SUDOERS_STACK_INCREMENT 16
616
617 static int
618 switch_buffer(path)
619     char *path;
620 {
621     static size_t stacksize, depth;
622     static struct sudoers_state *state;
623     static int keepopen;
624     FILE *fp;
625
626     if (path != NULL) {
627         /* push current state */
628         if (depth >= stacksize) {
629             if (depth > MAX_SUDOERS_DEPTH) {
630                 yyerror("too many levels of includes");
631                 return(FALSE);
632             }
633             stacksize += SUDOERS_STACK_INCREMENT;
634             state = (struct sudoers_state *) realloc(state,
635                 sizeof(state) * stacksize);
636             if (state == NULL) {
637                 yyerror("unable to allocate memory");
638                 return(FALSE);
639             }
640         }
641         if ((fp = open_sudoers(path, &keepopen)) == NULL) {
642             yyerror(path);
643             return(FALSE);
644         }
645         state[depth].bs = YY_CURRENT_BUFFER;
646         state[depth].path = sudoers;
647         state[depth].lineno = sudolineno;
648         depth++;
649         sudolineno = 1;
650         sudoers = path;
651         yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
652     } else {
653         /* pop */
654         if (depth == 0)
655             return(FALSE);
656         depth--;
657         if (!keepopen)
658             fclose(YY_CURRENT_BUFFER->yy_input_file);
659         yy_delete_buffer(YY_CURRENT_BUFFER);
660         yy_switch_to_buffer(state[depth].bs);
661         efree(sudoers);
662         sudoers = state[depth].path;
663         sudolineno = state[depth].lineno;
664         keepopen = FALSE;
665     }
666     return(TRUE);
667 }
668
669 static char *
670 parse_include(base)
671     char *base;
672 {
673     char *cp, *ep, *path;
674     int len;
675
676     /* Pull out path from #include line. */
677     cp = base + sizeof("#include");
678     while (isblank((unsigned char) *cp))
679         cp++;
680     ep = cp;
681     while (*ep != '\0' && !isspace((unsigned char) *ep))
682         ep++;
683
684     /* Make a copy of path and return it. */
685     len = (int)(ep - cp);
686     if ((path = malloc(len + 1)) == NULL)
687         yyerror("unable to allocate memory");
688     memcpy(path, cp, len);
689     path[len] = '\0';
690
691     /* Push any excess characters (e.g. comment, newline) back to the lexer */
692     if (*ep != '\0')
693         yyless((int)(ep - base));
694
695     return(path);
696 }
697
698 /*
699  * Check to make sure an IPv6 address does not contain multiple instances
700  * of the string "::".  Assumes strlen(s) >= 1.
701  * Returns TRUE if address is valid else FALSE.
702  */
703 static int
704 ipv6_valid(s)
705     const char *s;
706 {
707     int nmatch = 0;
708
709     for (; *s != '\0'; s++) {
710         if (s[0] == ':' && s[1] == ':') {
711             if (++nmatch > 1)
712                 break;
713         }
714         if (s[0] == '/')
715             nmatch = 0;                 /* reset if we hit netmask */
716     }
717
718     return (nmatch <= 1);
719 }