]> git.gag.com Git - debian/sudo/blob - plugins/sudoers/toke.l
Imported Upstream version 1.8.1p2
[debian/sudo] / plugins / sudoers / toke.l
1 %{
2 /*
3  * Copyright (c) 1996, 1998-2005, 2007-2011
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 <sys/stat.h>
31 #include <stdio.h>
32 #ifdef STDC_HEADERS
33 # include <stdlib.h>
34 # include <stddef.h>
35 #else
36 # ifdef HAVE_STDLIB_H
37 #  include <stdlib.h>
38 # endif
39 #endif /* STDC_HEADERS */
40 #ifdef HAVE_STRING_H
41 # include <string.h>
42 #endif /* HAVE_STRING_H */
43 #ifdef HAVE_STRINGS_H
44 # include <strings.h>
45 #endif /* HAVE_STRINGS_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 #ifdef HAVE_DIRENT_H
53 # include <dirent.h>
54 # define NAMLEN(dirent) strlen((dirent)->d_name)
55 #else
56 # define dirent direct
57 # define NAMLEN(dirent) (dirent)->d_namlen
58 # ifdef HAVE_SYS_NDIR_H
59 #  include <sys/ndir.h>
60 # endif
61 # ifdef HAVE_SYS_DIR_H
62 #  include <sys/dir.h>
63 # endif
64 # ifdef HAVE_NDIR_H
65 #  include <ndir.h>
66 # endif
67 #endif
68 #include <errno.h>
69 #include <ctype.h>
70 #include "sudoers.h"
71 #include "parse.h"
72 #include "toke.h"
73 #include <gram.h>
74
75 extern YYSTYPE yylval;
76 extern int parse_error;
77 int sudolineno;
78 char *sudoers;
79
80 static int continued, prev_state, sawspace;
81
82 static int _push_include(char *, int);
83 static int pop_include(void);
84 static char *parse_include(char *);
85
86 #ifdef TRACELEXER
87 static int sudoers_trace_print(const char *msg);
88 #else
89 # define sudoers_trace_print NULL
90 #endif
91 int (*trace_print)(const char *msg) = sudoers_trace_print;
92
93 #define push_include(_p)        (_push_include((_p), FALSE))
94 #define push_includedir(_p)     (_push_include((_p), TRUE))
95 %}
96
97 HEX16                   [0-9A-Fa-f]{1,4}
98 OCTET                   (1?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5])
99 IPV4ADDR                {OCTET}(\.{OCTET}){3}
100 IPV6ADDR                ({HEX16}?:){2,7}{HEX16}?|({HEX16}?:){2,6}:{IPV4ADDR}
101
102 HOSTNAME                [[:alnum:]_-]+
103 WORD                    ([^#>!=:,\(\) \t\n\\\"]|\\[^\n])+
104 ID                      #-?[0-9]+
105 PATH                    \/(\\[\,:= \t#]|[^\,:=\\ \t\n#])+
106 ENVAR                   ([^#!=, \t\n\\\"]|\\[^\n])([^#=, \t\n\\\"]|\\[^\n])*
107 DEFVAR                  [a-z_]+
108
109 %option noinput
110 %option nounput
111 %option noyywrap
112
113 %s      GOTDEFS
114 %x      GOTCMND
115 %x      STARTDEFS
116 %x      INDEFS
117 %x      INSTR
118
119 %%
120 <GOTDEFS>[[:blank:]]*,[[:blank:]]* {
121                             LEXTRACE(", ");
122                             return ',';
123                         }                       /* return ',' */
124
125 <GOTDEFS>[[:blank:]]+   BEGIN STARTDEFS;
126
127 <STARTDEFS>{DEFVAR}     {
128                             BEGIN INDEFS;
129                             LEXTRACE("DEFVAR ");
130                             if (!fill(yytext, yyleng))
131                                 yyterminate();
132                             return DEFVAR;
133                         }
134
135 <INDEFS>{
136     ,                   {
137                             BEGIN STARTDEFS;
138                             LEXTRACE(", ");
139                             return ',';
140                         }                       /* return ',' */
141
142     =                   {
143                             LEXTRACE("= ");
144                             return '=';
145                         }                       /* return '=' */
146
147     \+=                 {
148                             LEXTRACE("+= ");
149                             return '+';
150                         }                       /* return '+' */
151
152     -=                  {
153                             LEXTRACE("-= ");
154                             return '-';
155                         }                       /* return '-' */
156
157     \"                  {
158                             LEXTRACE("BEGINSTR ");
159                             yylval.string = NULL;
160                             prev_state = YY_START;
161                             BEGIN INSTR;
162                         }
163
164     {ENVAR}             {
165                             LEXTRACE("WORD(2) ");
166                             if (!fill(yytext, yyleng))
167                                 yyterminate();
168                             return WORD;
169                         }
170 }
171
172 <INSTR>{
173     \\[[:blank:]]*\n[[:blank:]]*        {
174                             /* Line continuation char followed by newline. */
175                             ++sudolineno;
176                             continued = TRUE;
177                         }
178
179     \"                  {
180                             LEXTRACE("ENDSTR ");
181                             BEGIN prev_state;
182
183                             if (yylval.string == NULL) {
184                                 LEXTRACE("ERROR "); /* empty string */
185                                 return ERROR;
186                             }
187                             if (prev_state == INITIAL) {
188                                 switch (yylval.string[0]) {
189                                 case '%':
190                                     if (yylval.string[1] == '\0' ||
191                                         (yylval.string[1] == ':' &&
192                                         yylval.string[2] == '\0')) {
193                                         LEXTRACE("ERROR "); /* empty group */
194                                         return ERROR;
195                                     }
196                                     LEXTRACE("USERGROUP ");
197                                     return USERGROUP;
198                                 case '+':
199                                     if (yylval.string[1] == '\0') {
200                                         LEXTRACE("ERROR "); /* empty netgroup */
201                                         return ERROR;
202                                     }
203                                     LEXTRACE("NETGROUP ");
204                                     return NETGROUP;
205                                 }
206                             }
207                             LEXTRACE("WORD(4) ");
208                             return WORD;
209                         }
210
211     \\                  {
212                             LEXTRACE("BACKSLASH ");
213                             if (!append(yytext, yyleng))
214                                 yyterminate();
215                         }
216
217     ([^\"\n\\]|\\\")+   {
218                             LEXTRACE("STRBODY ");
219                             if (!append(yytext, yyleng))
220                                 yyterminate();
221                         }
222 }
223
224 <GOTCMND>{
225     \\[\*\?\[\]\!]      {
226                             /* quoted fnmatch glob char, pass verbatim */
227                             LEXTRACE("QUOTEDCHAR ");
228                             if (!fill_args(yytext, 2, sawspace))
229                                 yyterminate();
230                             sawspace = FALSE;
231                         }
232
233     \\[:\\,= \t#]       {
234                             /* quoted sudoers special char, strip backslash */
235                             LEXTRACE("QUOTEDCHAR ");
236                             if (!fill_args(yytext + 1, 1, sawspace))
237                                 yyterminate();
238                             sawspace = FALSE;
239                         }
240
241     [#:\,=\n]           {
242                             BEGIN INITIAL;
243                             yyless(0);
244                             return COMMAND;
245                         }                       /* end of command line args */
246
247     [^#\\:, \t\n]+      {
248                             LEXTRACE("ARG ");
249                             if (!fill_args(yytext, yyleng, sawspace))
250                                 yyterminate();
251                             sawspace = FALSE;
252                         }                       /* a command line arg */
253 }
254
255 <INITIAL>^#include[[:blank:]]+\/.*\n {
256                             char *path;
257
258                             if (continued) {
259                                 LEXTRACE("ERROR ");
260                                 return ERROR;
261                             }
262
263                             if ((path = parse_include(yytext)) == NULL)
264                                 yyterminate();
265
266                             LEXTRACE("INCLUDE\n");
267
268                             /* Push current buffer and switch to include file */
269                             if (!push_include(path))
270                                 yyterminate();
271                         }
272
273 <INITIAL>^#includedir[[:blank:]]+\/.*\n {
274                             char *path;
275
276                             if (continued) {
277                                 LEXTRACE("ERROR ");
278                                 return ERROR;
279                             }
280
281                             if ((path = parse_include(yytext)) == NULL)
282                                 yyterminate();
283
284                             LEXTRACE("INCLUDEDIR\n");
285
286                             /*
287                              * Push current buffer and switch to include file.
288                              * We simply ignore empty directories.
289                              */
290                             if (!push_includedir(path) && parse_error)
291                                 yyterminate();
292                         }
293
294 <INITIAL>^[[:blank:]]*Defaults([:@>\!][[:blank:]]*\!*\"?({ID}|{WORD}))? {
295                             char deftype;
296                             int n;
297
298                             if (continued) {
299                                 LEXTRACE("ERROR ");
300                                 return ERROR;
301                             }
302
303                             for (n = 0; isblank((unsigned char)yytext[n]); n++)
304                                 continue;
305                             n += sizeof("Defaults") - 1;
306                             if ((deftype = yytext[n++]) != '\0') {
307                                 while (isblank((unsigned char)yytext[n]))
308                                     n++;
309                             }
310                             BEGIN GOTDEFS;
311                             switch (deftype) {
312                                 case ':':
313                                     yyless(n);
314                                     LEXTRACE("DEFAULTS_USER ");
315                                     return DEFAULTS_USER;
316                                 case '>':
317                                     yyless(n);
318                                     LEXTRACE("DEFAULTS_RUNAS ");
319                                     return DEFAULTS_RUNAS;
320                                 case '@':
321                                     yyless(n);
322                                     LEXTRACE("DEFAULTS_HOST ");
323                                     return DEFAULTS_HOST;
324                                 case '!':
325                                     yyless(n);
326                                     LEXTRACE("DEFAULTS_CMND ");
327                                     return DEFAULTS_CMND;
328                                 default:
329                                     LEXTRACE("DEFAULTS ");
330                                     return DEFAULTS;
331                             }
332                         }
333
334 <INITIAL>^[[:blank:]]*(Host|Cmnd|User|Runas)_Alias      {
335                             int n;
336
337                             if (continued) {
338                                 LEXTRACE("ERROR ");
339                                 return ERROR;
340                             }
341
342                             for (n = 0; isblank((unsigned char)yytext[n]); n++)
343                                 continue;
344                             switch (yytext[n]) {
345                                 case 'H':
346                                     LEXTRACE("HOSTALIAS ");
347                                     return HOSTALIAS;
348                                 case 'C':
349                                     LEXTRACE("CMNDALIAS ");
350                                     return CMNDALIAS;
351                                 case 'U':
352                                     LEXTRACE("USERALIAS ");
353                                     return USERALIAS;
354                                 case 'R':
355                                     LEXTRACE("RUNASALIAS ");
356                                     return RUNASALIAS;
357                             }
358                         }
359
360 NOPASSWD[[:blank:]]*:   {
361                                 /* cmnd does not require passwd for this user */
362                                 LEXTRACE("NOPASSWD ");
363                                 return NOPASSWD;
364                         }
365
366 PASSWD[[:blank:]]*:     {
367                                 /* cmnd requires passwd for this user */
368                                 LEXTRACE("PASSWD ");
369                                 return PASSWD;
370                         }
371
372 NOEXEC[[:blank:]]*:     {
373                                 LEXTRACE("NOEXEC ");
374                                 return NOEXEC;
375                         }
376
377 EXEC[[:blank:]]*:       {
378                                 LEXTRACE("EXEC ");
379                                 return EXEC;
380                         }
381
382 SETENV[[:blank:]]*:     {
383                                 LEXTRACE("SETENV ");
384                                 return SETENV;
385                         }
386
387 NOSETENV[[:blank:]]*:   {
388                                 LEXTRACE("NOSETENV ");
389                                 return NOSETENV;
390                         }
391
392 LOG_OUTPUT[[:blank:]]*: {
393                                 LEXTRACE("LOG_OUTPUT ");
394                                 return LOG_OUTPUT;
395                         }
396
397 NOLOG_OUTPUT[[:blank:]]*:       {
398                                 LEXTRACE("NOLOG_OUTPUT ");
399                                 return NOLOG_OUTPUT;
400                         }
401
402 LOG_INPUT[[:blank:]]*:  {
403                                 LEXTRACE("LOG_INPUT ");
404                                 return LOG_INPUT;
405                         }
406
407 NOLOG_INPUT[[:blank:]]*:        {
408                                 LEXTRACE("NOLOG_INPUT ");
409                                 return NOLOG_INPUT;
410                         }
411
412 <INITIAL,GOTDEFS>(\+|\%|\%:) {
413                             /* empty group or netgroup */
414                             LEXTRACE("ERROR ");
415                             return ERROR;
416                         }
417
418 \+{WORD}                {
419                             /* netgroup */
420                             if (!fill(yytext, yyleng))
421                                 yyterminate();
422                             LEXTRACE("NETGROUP ");
423                             return NETGROUP;
424                         }
425
426 \%:?({WORD}|{ID})       {
427                             /* group */
428                             if (!fill(yytext, yyleng))
429                                 yyterminate();
430                             LEXTRACE("USERGROUP ");
431                             return USERGROUP;
432                         }
433
434 {IPV4ADDR}(\/{IPV4ADDR})? {
435                             if (!fill(yytext, yyleng))
436                                 yyterminate();
437                             LEXTRACE("NTWKADDR ");
438                             return NTWKADDR;
439                         }
440
441 {IPV4ADDR}\/([12]?[0-9]|3[0-2]) {
442                             if (!fill(yytext, yyleng))
443                                 yyterminate();
444                             LEXTRACE("NTWKADDR ");
445                             return NTWKADDR;
446                         }
447
448 {IPV6ADDR}(\/{IPV6ADDR})? {
449                             if (!ipv6_valid(yytext)) {
450                                 LEXTRACE("ERROR ");
451                                 return ERROR;
452                             }
453                             if (!fill(yytext, yyleng))
454                                 yyterminate();
455                             LEXTRACE("NTWKADDR ");
456                             return NTWKADDR;
457                         }
458
459 {IPV6ADDR}\/([0-9]|[1-9][0-9]|1[01][0-9]|12[0-8]) {
460                             if (!ipv6_valid(yytext)) {
461                                 LEXTRACE("ERROR ");
462                                 return ERROR;
463                             }
464                             if (!fill(yytext, yyleng))
465                                 yyterminate();
466                             LEXTRACE("NTWKADDR ");
467                             return NTWKADDR;
468                         }
469
470 ALL {
471                             LEXTRACE("ALL ");
472                             return ALL;
473
474                         }
475
476 <INITIAL>ROLE {
477 #ifdef HAVE_SELINUX
478                             LEXTRACE("ROLE ");
479                             return ROLE;
480 #else
481                             goto got_alias;
482 #endif
483                         }
484
485 <INITIAL>TYPE {
486 #ifdef HAVE_SELINUX
487                             LEXTRACE("TYPE ");
488                             return TYPE;
489 #else
490                             goto got_alias;
491 #endif
492                         }
493
494 [[:upper:]][[:upper:][:digit:]_]* {
495                         got_alias:
496                             if (!fill(yytext, yyleng))
497                                 yyterminate();
498                             LEXTRACE("ALIAS ");
499                             return ALIAS;
500                         }
501
502 <GOTDEFS>({PATH}|sudoedit) {
503                             /* no command args allowed for Defaults!/path */
504                             if (!fill_cmnd(yytext, yyleng))
505                                 yyterminate();
506                             LEXTRACE("COMMAND ");
507                             return COMMAND;
508                         }
509
510 sudoedit                {
511                             BEGIN GOTCMND;
512                             LEXTRACE("COMMAND ");
513                             if (!fill_cmnd(yytext, yyleng))
514                                 yyterminate();
515                         }                       /* sudo -e */
516
517 {PATH}                  {
518                             /* directories can't have args... */
519                             if (yytext[yyleng - 1] == '/') {
520                                 LEXTRACE("COMMAND ");
521                                 if (!fill_cmnd(yytext, yyleng))
522                                     yyterminate();
523                                 return COMMAND;
524                             } else {
525                                 BEGIN GOTCMND;
526                                 LEXTRACE("COMMAND ");
527                                 if (!fill_cmnd(yytext, yyleng))
528                                     yyterminate();
529                             }
530                         }                       /* a pathname */
531
532 <INITIAL,GOTDEFS>\" {
533                             LEXTRACE("BEGINSTR ");
534                             yylval.string = NULL;
535                             prev_state = YY_START;
536                             BEGIN INSTR;
537                         }
538
539 <INITIAL,GOTDEFS>({ID}|{WORD}) {
540                             /* a word */
541                             if (!fill(yytext, yyleng))
542                                 yyterminate();
543                             LEXTRACE("WORD(5) ");
544                             return WORD;
545                         }
546
547 \(                      {
548                             LEXTRACE("( ");
549                             return '(';
550                         }
551
552 \)                      {
553                             LEXTRACE(") ");
554                             return ')';
555                         }
556
557 ,                       {
558                             LEXTRACE(", ");
559                             return ',';
560                         }                       /* return ',' */
561
562 =                       {
563                             LEXTRACE("= ");
564                             return '=';
565                         }                       /* return '=' */
566
567 :                       {
568                             LEXTRACE(": ");
569                             return ':';
570                         }                       /* return ':' */
571
572 <*>!+                   {
573                             if (yyleng & 1) {
574                                 LEXTRACE("!");
575                                 return '!';     /* return '!' */
576                             }
577                         }
578
579 <*>\n                   {
580                             if (YY_START == INSTR) {
581                                 LEXTRACE("ERROR ");
582                                 return ERROR;   /* line break in string */
583                             }
584                             BEGIN INITIAL;
585                             ++sudolineno;
586                             continued = FALSE;
587                             LEXTRACE("\n");
588                             return COMMENT;
589                         }                       /* return newline */
590
591 <*>[[:blank:]]+         {                       /* throw away space/tabs */
592                             sawspace = TRUE;    /* but remember for fill_args */
593                         }
594
595 <*>\\[[:blank:]]*\n     {
596                             sawspace = TRUE;    /* remember for fill_args */
597                             ++sudolineno;
598                             continued = TRUE;
599                         }                       /* throw away EOL after \ */
600
601 <INITIAL,STARTDEFS,INDEFS>#(-[^\n0-9].*|[^\n0-9-].*)?\n {
602                             BEGIN INITIAL;
603                             ++sudolineno;
604                             continued = FALSE;
605                             LEXTRACE("#\n");
606                             return COMMENT;
607                         }                       /* comment, not uid/gid */
608
609 <*>.                    {
610                             LEXTRACE("ERROR ");
611                             return ERROR;
612                         }       /* parse error */
613
614 <*><<EOF>>              {
615                             if (YY_START != INITIAL) {
616                                 BEGIN INITIAL;
617                                 LEXTRACE("ERROR ");
618                                 return ERROR;
619                             }
620                             if (!pop_include())
621                                 yyterminate();
622                         }
623
624 %%
625 struct path_list {
626     char *path;
627     struct path_list *next;
628 };
629
630 struct include_stack {
631     YY_BUFFER_STATE bs;
632     char *path;
633     struct path_list *more; /* more files in case of includedir */
634     int lineno;
635     int keepopen;
636 };
637
638 static int
639 pl_compare(const void *v1, const void *v2)
640 {
641     const struct path_list * const *p1 = v1;
642     const struct path_list * const *p2 = v2;
643
644     return strcmp((*p1)->path, (*p2)->path);
645 }
646
647 static char *
648 switch_dir(struct include_stack *stack, char *dirpath)
649 {
650     DIR *dir;
651     int i, count = 0;
652     char *path = NULL;
653     struct dirent *dent;
654     struct stat sb;
655     struct path_list *pl, *first = NULL;
656     struct path_list **sorted = NULL;
657
658     if (!(dir = opendir(dirpath))) {
659         if (errno != ENOENT) {
660             char *errbuf;
661             if (asprintf(&errbuf, "%s: %s", dirpath, strerror(errno)) != -1) {
662                 yyerror(errbuf);
663                 free(errbuf);
664             } else {
665                 yyerror("unable to allocate memory");
666             }
667         }
668         goto done;
669     }
670     while ((dent = readdir(dir))) {
671         /* Ignore files that end in '~' or have a '.' in them. */
672         if (dent->d_name[0] == '\0' || dent->d_name[NAMLEN(dent) - 1] == '~'
673             || strchr(dent->d_name, '.') != NULL) {
674             continue;
675         }
676         if (asprintf(&path, "%s/%s", dirpath, dent->d_name) == -1) {
677             closedir(dir);
678             goto bad;
679         }
680         if (stat(path, &sb) != 0 || !S_ISREG(sb.st_mode)) {
681             efree(path);
682             path = NULL;
683             continue;
684         }
685         pl = malloc(sizeof(*pl));
686         if (pl == NULL)
687             goto bad;
688         pl->path = path;
689         pl->next = first;
690         first = pl;
691         count++;
692     }
693     closedir(dir);
694
695     if (count == 0)
696         goto done;
697
698     /* Sort the list as an array. */
699     sorted = malloc(sizeof(*sorted) * count);
700     if (sorted == NULL)
701         goto bad;
702     pl = first;
703     for (i = 0; i < count; i++) {
704         sorted[i] = pl;
705         pl = pl->next;
706     }
707     qsort(sorted, count, sizeof(*sorted), pl_compare);
708
709     /* Apply sorting to the list. */
710     first = sorted[0];
711     sorted[count - 1]->next = NULL;
712     for (i = 1; i < count; i++)
713         sorted[i - 1]->next = sorted[i];
714     efree(sorted);
715
716     /* Pull out the first element for parsing, leave the rest for later. */
717     if (count) {
718         path = first->path;
719         pl = first->next;
720         efree(first);
721         stack->more = pl;
722     } else {
723         path = NULL;
724     }
725 done:
726     efree(dirpath);
727     return path;
728 bad:
729     while (first != NULL) {
730         pl = first;
731         first = pl->next;
732         free(pl->path);
733         free(pl);
734     }
735     efree(sorted);
736     efree(dirpath);
737     efree(path);
738     return NULL;
739 }
740
741 #define MAX_SUDOERS_DEPTH       128
742 #define SUDOERS_STACK_INCREMENT 16
743
744 static size_t istacksize, idepth;
745 static struct include_stack *istack;
746 static int keepopen;
747
748 void
749 init_lexer(void)
750 {
751     struct path_list *pl;
752
753     while (idepth) {
754         idepth--;
755         while ((pl = istack[idepth].more) != NULL) {
756             istack[idepth].more = pl->next;
757             efree(pl->path);
758             efree(pl);
759         }
760         efree(istack[idepth].path);
761         if (idepth && !istack[idepth].keepopen)
762             fclose(istack[idepth].bs->yy_input_file);
763         yy_delete_buffer(istack[idepth].bs);
764     }
765     efree(istack);
766     istack = NULL;
767     istacksize = idepth = 0;
768     sudolineno = 1;
769     keepopen = FALSE;
770     sawspace = FALSE;
771     continued = FALSE;
772     prev_state = INITIAL;
773 }
774
775 static int
776 _push_include(char *path, int isdir)
777 {
778     struct path_list *pl;
779     FILE *fp;
780
781     /* push current state onto stack */
782     if (idepth >= istacksize) {
783         if (idepth > MAX_SUDOERS_DEPTH) {
784             yyerror("too many levels of includes");
785             return FALSE;
786         }
787         istacksize += SUDOERS_STACK_INCREMENT;
788         istack = (struct include_stack *) realloc(istack,
789             sizeof(*istack) * istacksize);
790         if (istack == NULL) {
791             yyerror("unable to allocate memory");
792             return FALSE;
793         }
794     }
795     if (isdir) {
796         if (!(path = switch_dir(&istack[idepth], path))) {
797             /* switch_dir() called yyerror() for us */
798             return FALSE;
799         }
800         while ((fp = open_sudoers(path, FALSE, &keepopen)) == NULL) {
801             /* Unable to open path in includedir, go to next one, if any. */
802             efree(path);
803             if ((pl = istack[idepth].more) == NULL)
804                 return FALSE;
805             path = pl->path;
806             istack[idepth].more = pl->next;
807             efree(pl);
808         }
809     } else {
810         if ((fp = open_sudoers(path, TRUE, &keepopen)) == NULL) {
811             char *errbuf;
812             if (asprintf(&errbuf, "%s: %s", path, strerror(errno)) != -1) {
813                 yyerror(errbuf);
814                 free(errbuf);
815             } else {
816                 yyerror("unable to allocate memory");
817             }
818             return FALSE;
819         }
820         istack[idepth].more = NULL;
821     }
822     /* Push the old (current) file and open the new one. */
823     istack[idepth].path = sudoers; /* push old path */
824     istack[idepth].bs = YY_CURRENT_BUFFER;
825     istack[idepth].lineno = sudolineno;
826     istack[idepth].keepopen = keepopen;
827     idepth++;
828     sudolineno = 1;
829     sudoers = path;
830     yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
831
832     return TRUE;
833 }
834
835 static int
836 pop_include(void)
837 {
838     struct path_list *pl;
839     FILE *fp;
840
841     if (idepth == 0)
842         return FALSE;
843
844     if (!keepopen)
845         fclose(YY_CURRENT_BUFFER->yy_input_file);
846     yy_delete_buffer(YY_CURRENT_BUFFER);
847     /* If we are in an include dir, move to the next file. */
848     while ((pl = istack[idepth - 1].more) != NULL) {
849         fp = open_sudoers(pl->path, FALSE, &keepopen);
850         if (fp != NULL) {
851             istack[idepth - 1].more = pl->next;
852             efree(sudoers);
853             sudoers = pl->path;
854             sudolineno = 1;
855             yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
856             efree(pl);
857             break;
858         }
859         /* Unable to open path in include dir, go to next one. */
860         istack[idepth - 1].more = pl->next;
861         efree(pl->path);
862         efree(pl);
863     }
864     /* If no path list, just pop the last dir on the stack. */
865     if (pl == NULL) {
866         idepth--;
867         yy_switch_to_buffer(istack[idepth].bs);
868         efree(sudoers);
869         sudoers = istack[idepth].path;
870         sudolineno = istack[idepth].lineno;
871         keepopen = istack[idepth].keepopen;
872     }
873     return TRUE;
874 }
875
876 static char *
877 parse_include(char *base)
878 {
879     char *cp, *ep, *path;
880     int len = 0, subst = 0;
881     size_t shost_len = 0;
882
883     /* Pull out path from #include line. */
884     cp = base + sizeof("#include");
885     if (*cp == 'i')
886         cp += 3; /* includedir */
887     while (isblank((unsigned char) *cp))
888         cp++;
889     ep = cp;
890     while (*ep != '\0' && !isspace((unsigned char) *ep)) {
891         if (ep[0] == '%' && ep[1] == 'h') {
892             shost_len = strlen(user_shost);
893             len += shost_len - 2;
894             subst = 1;
895         }
896         ep++;
897     }
898
899     /* Make a copy of path and return it. */
900     len += (int)(ep - cp);
901     if ((path = malloc(len + 1)) == NULL)
902         yyerror("unable to allocate memory");
903     if (subst) {
904         /* substitute for %h */
905         char *pp = path;
906         while (cp < ep) {
907             if (cp[0] == '%' && cp[1] == 'h') {
908                 memcpy(pp, user_shost, shost_len);
909                 pp += shost_len;
910                 cp += 2;
911                 continue;
912             }
913             *pp++ = *cp++;
914         }
915         *pp = '\0';
916     } else {
917         memcpy(path, cp, len);
918         path[len] = '\0';
919     }
920
921     /* Push any excess characters (e.g. comment, newline) back to the lexer */
922     if (*ep != '\0')
923         yyless((int)(ep - base));
924
925     return path;
926 }
927
928 #ifdef TRACELEXER
929 static int
930 sudoers_trace_print(const char *msg)
931 {
932     return fputs(msg, stderr);
933 }
934 #endif /* TRACELEXER */