Imported Upstream version 1.8.2
[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 #ifndef HAVE_SELINUX
496                         got_alias:
497 #endif
498                             if (!fill(yytext, yyleng))
499                                 yyterminate();
500                             LEXTRACE("ALIAS ");
501                             return ALIAS;
502                         }
503
504 <GOTDEFS>({PATH}|sudoedit) {
505                             /* no command args allowed for Defaults!/path */
506                             if (!fill_cmnd(yytext, yyleng))
507                                 yyterminate();
508                             LEXTRACE("COMMAND ");
509                             return COMMAND;
510                         }
511
512 sudoedit                {
513                             BEGIN GOTCMND;
514                             LEXTRACE("COMMAND ");
515                             if (!fill_cmnd(yytext, yyleng))
516                                 yyterminate();
517                         }                       /* sudo -e */
518
519 {PATH}                  {
520                             /* directories can't have args... */
521                             if (yytext[yyleng - 1] == '/') {
522                                 LEXTRACE("COMMAND ");
523                                 if (!fill_cmnd(yytext, yyleng))
524                                     yyterminate();
525                                 return COMMAND;
526                             } else {
527                                 BEGIN GOTCMND;
528                                 LEXTRACE("COMMAND ");
529                                 if (!fill_cmnd(yytext, yyleng))
530                                     yyterminate();
531                             }
532                         }                       /* a pathname */
533
534 <INITIAL,GOTDEFS>\" {
535                             LEXTRACE("BEGINSTR ");
536                             yylval.string = NULL;
537                             prev_state = YY_START;
538                             BEGIN INSTR;
539                         }
540
541 <INITIAL,GOTDEFS>({ID}|{WORD}) {
542                             /* a word */
543                             if (!fill(yytext, yyleng))
544                                 yyterminate();
545                             LEXTRACE("WORD(5) ");
546                             return WORD;
547                         }
548
549 \(                      {
550                             LEXTRACE("( ");
551                             return '(';
552                         }
553
554 \)                      {
555                             LEXTRACE(") ");
556                             return ')';
557                         }
558
559 ,                       {
560                             LEXTRACE(", ");
561                             return ',';
562                         }                       /* return ',' */
563
564 =                       {
565                             LEXTRACE("= ");
566                             return '=';
567                         }                       /* return '=' */
568
569 :                       {
570                             LEXTRACE(": ");
571                             return ':';
572                         }                       /* return ':' */
573
574 <*>!+                   {
575                             if (yyleng & 1) {
576                                 LEXTRACE("!");
577                                 return '!';     /* return '!' */
578                             }
579                         }
580
581 <*>\n                   {
582                             if (YY_START == INSTR) {
583                                 LEXTRACE("ERROR ");
584                                 return ERROR;   /* line break in string */
585                             }
586                             BEGIN INITIAL;
587                             ++sudolineno;
588                             continued = FALSE;
589                             LEXTRACE("\n");
590                             return COMMENT;
591                         }                       /* return newline */
592
593 <*>[[:blank:]]+         {                       /* throw away space/tabs */
594                             sawspace = TRUE;    /* but remember for fill_args */
595                         }
596
597 <*>\\[[:blank:]]*\n     {
598                             sawspace = TRUE;    /* remember for fill_args */
599                             ++sudolineno;
600                             continued = TRUE;
601                         }                       /* throw away EOL after \ */
602
603 <INITIAL,STARTDEFS,INDEFS>#(-[^\n0-9].*|[^\n0-9-].*)?\n {
604                             BEGIN INITIAL;
605                             ++sudolineno;
606                             continued = FALSE;
607                             LEXTRACE("#\n");
608                             return COMMENT;
609                         }                       /* comment, not uid/gid */
610
611 <*>.                    {
612                             LEXTRACE("ERROR ");
613                             return ERROR;
614                         }       /* parse error */
615
616 <*><<EOF>>              {
617                             if (YY_START != INITIAL) {
618                                 BEGIN INITIAL;
619                                 LEXTRACE("ERROR ");
620                                 return ERROR;
621                             }
622                             if (!pop_include())
623                                 yyterminate();
624                         }
625
626 %%
627 struct path_list {
628     char *path;
629     struct path_list *next;
630 };
631
632 struct include_stack {
633     YY_BUFFER_STATE bs;
634     char *path;
635     struct path_list *more; /* more files in case of includedir */
636     int lineno;
637     int keepopen;
638 };
639
640 static int
641 pl_compare(const void *v1, const void *v2)
642 {
643     const struct path_list * const *p1 = v1;
644     const struct path_list * const *p2 = v2;
645
646     return strcmp((*p1)->path, (*p2)->path);
647 }
648
649 static char *
650 switch_dir(struct include_stack *stack, char *dirpath)
651 {
652     DIR *dir;
653     int i, count = 0;
654     char *path = NULL;
655     struct dirent *dent;
656     struct stat sb;
657     struct path_list *pl, *first = NULL;
658     struct path_list **sorted = NULL;
659
660     if (!(dir = opendir(dirpath))) {
661         if (errno != ENOENT) {
662             char *errbuf;
663             if (asprintf(&errbuf, _("%s: %s"), dirpath, strerror(errno)) != -1) {
664                 yyerror(errbuf);
665                 free(errbuf);
666             } else {
667                 yyerror(_("unable to allocate memory"));
668             }
669         }
670         goto done;
671     }
672     while ((dent = readdir(dir))) {
673         /* Ignore files that end in '~' or have a '.' in them. */
674         if (dent->d_name[0] == '\0' || dent->d_name[NAMLEN(dent) - 1] == '~'
675             || strchr(dent->d_name, '.') != NULL) {
676             continue;
677         }
678         if (asprintf(&path, "%s/%s", dirpath, dent->d_name) == -1) {
679             closedir(dir);
680             goto bad;
681         }
682         if (stat(path, &sb) != 0 || !S_ISREG(sb.st_mode)) {
683             efree(path);
684             path = NULL;
685             continue;
686         }
687         pl = malloc(sizeof(*pl));
688         if (pl == NULL)
689             goto bad;
690         pl->path = path;
691         pl->next = first;
692         first = pl;
693         count++;
694     }
695     closedir(dir);
696
697     if (count == 0)
698         goto done;
699
700     /* Sort the list as an array. */
701     sorted = malloc(sizeof(*sorted) * count);
702     if (sorted == NULL)
703         goto bad;
704     pl = first;
705     for (i = 0; i < count; i++) {
706         sorted[i] = pl;
707         pl = pl->next;
708     }
709     qsort(sorted, count, sizeof(*sorted), pl_compare);
710
711     /* Apply sorting to the list. */
712     first = sorted[0];
713     sorted[count - 1]->next = NULL;
714     for (i = 1; i < count; i++)
715         sorted[i - 1]->next = sorted[i];
716     efree(sorted);
717
718     /* Pull out the first element for parsing, leave the rest for later. */
719     if (count) {
720         path = first->path;
721         pl = first->next;
722         efree(first);
723         stack->more = pl;
724     } else {
725         path = NULL;
726     }
727 done:
728     efree(dirpath);
729     return path;
730 bad:
731     while (first != NULL) {
732         pl = first;
733         first = pl->next;
734         free(pl->path);
735         free(pl);
736     }
737     efree(sorted);
738     efree(dirpath);
739     efree(path);
740     return NULL;
741 }
742
743 #define MAX_SUDOERS_DEPTH       128
744 #define SUDOERS_STACK_INCREMENT 16
745
746 static size_t istacksize, idepth;
747 static struct include_stack *istack;
748 static int keepopen;
749
750 void
751 init_lexer(void)
752 {
753     struct path_list *pl;
754
755     while (idepth) {
756         idepth--;
757         while ((pl = istack[idepth].more) != NULL) {
758             istack[idepth].more = pl->next;
759             efree(pl->path);
760             efree(pl);
761         }
762         efree(istack[idepth].path);
763         if (idepth && !istack[idepth].keepopen)
764             fclose(istack[idepth].bs->yy_input_file);
765         yy_delete_buffer(istack[idepth].bs);
766     }
767     efree(istack);
768     istack = NULL;
769     istacksize = idepth = 0;
770     sudolineno = 1;
771     keepopen = FALSE;
772     sawspace = FALSE;
773     continued = FALSE;
774     prev_state = INITIAL;
775 }
776
777 static int
778 _push_include(char *path, int isdir)
779 {
780     struct path_list *pl;
781     FILE *fp;
782
783     /* push current state onto stack */
784     if (idepth >= istacksize) {
785         if (idepth > MAX_SUDOERS_DEPTH) {
786             yyerror(_("too many levels of includes"));
787             return FALSE;
788         }
789         istacksize += SUDOERS_STACK_INCREMENT;
790         istack = (struct include_stack *) realloc(istack,
791             sizeof(*istack) * istacksize);
792         if (istack == NULL) {
793             yyerror(_("unable to allocate memory"));
794             return FALSE;
795         }
796     }
797     if (isdir) {
798         if (!(path = switch_dir(&istack[idepth], path))) {
799             /* switch_dir() called yyerror() for us */
800             return FALSE;
801         }
802         while ((fp = open_sudoers(path, FALSE, &keepopen)) == NULL) {
803             /* Unable to open path in includedir, go to next one, if any. */
804             efree(path);
805             if ((pl = istack[idepth].more) == NULL)
806                 return FALSE;
807             path = pl->path;
808             istack[idepth].more = pl->next;
809             efree(pl);
810         }
811     } else {
812         if ((fp = open_sudoers(path, TRUE, &keepopen)) == NULL) {
813             char *errbuf;
814             if (asprintf(&errbuf, _("%s: %s"), path, strerror(errno)) != -1) {
815                 yyerror(errbuf);
816                 free(errbuf);
817             } else {
818                 yyerror(_("unable to allocate memory"));
819             }
820             return FALSE;
821         }
822         istack[idepth].more = NULL;
823     }
824     /* Push the old (current) file and open the new one. */
825     istack[idepth].path = sudoers; /* push old path */
826     istack[idepth].bs = YY_CURRENT_BUFFER;
827     istack[idepth].lineno = sudolineno;
828     istack[idepth].keepopen = keepopen;
829     idepth++;
830     sudolineno = 1;
831     sudoers = path;
832     yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
833
834     return TRUE;
835 }
836
837 static int
838 pop_include(void)
839 {
840     struct path_list *pl;
841     FILE *fp;
842
843     if (idepth == 0)
844         return FALSE;
845
846     if (!keepopen)
847         fclose(YY_CURRENT_BUFFER->yy_input_file);
848     yy_delete_buffer(YY_CURRENT_BUFFER);
849     /* If we are in an include dir, move to the next file. */
850     while ((pl = istack[idepth - 1].more) != NULL) {
851         fp = open_sudoers(pl->path, FALSE, &keepopen);
852         if (fp != NULL) {
853             istack[idepth - 1].more = pl->next;
854             efree(sudoers);
855             sudoers = pl->path;
856             sudolineno = 1;
857             yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
858             efree(pl);
859             break;
860         }
861         /* Unable to open path in include dir, go to next one. */
862         istack[idepth - 1].more = pl->next;
863         efree(pl->path);
864         efree(pl);
865     }
866     /* If no path list, just pop the last dir on the stack. */
867     if (pl == NULL) {
868         idepth--;
869         yy_switch_to_buffer(istack[idepth].bs);
870         efree(sudoers);
871         sudoers = istack[idepth].path;
872         sudolineno = istack[idepth].lineno;
873         keepopen = istack[idepth].keepopen;
874     }
875     return TRUE;
876 }
877
878 static char *
879 parse_include(char *base)
880 {
881     char *cp, *ep, *path;
882     int len = 0, subst = 0;
883     size_t shost_len = 0;
884
885     /* Pull out path from #include line. */
886     cp = base + sizeof("#include");
887     if (*cp == 'i')
888         cp += 3; /* includedir */
889     while (isblank((unsigned char) *cp))
890         cp++;
891     ep = cp;
892     while (*ep != '\0' && !isspace((unsigned char) *ep)) {
893         if (ep[0] == '%' && ep[1] == 'h') {
894             shost_len = strlen(user_shost);
895             len += shost_len - 2;
896             subst = 1;
897         }
898         ep++;
899     }
900
901     /* Make a copy of path and return it. */
902     len += (int)(ep - cp);
903     if ((path = malloc(len + 1)) == NULL) {
904         yyerror(_("unable to allocate memory"));
905         return NULL;
906     }
907     if (subst) {
908         /* substitute for %h */
909         char *pp = path;
910         while (cp < ep) {
911             if (cp[0] == '%' && cp[1] == 'h') {
912                 memcpy(pp, user_shost, shost_len);
913                 pp += shost_len;
914                 cp += 2;
915                 continue;
916             }
917             *pp++ = *cp++;
918         }
919         *pp = '\0';
920     } else {
921         memcpy(path, cp, len);
922         path[len] = '\0';
923     }
924
925     /* Push any excess characters (e.g. comment, newline) back to the lexer */
926     if (*ep != '\0')
927         yyless((int)(ep - base));
928
929     return path;
930 }
931
932 #ifdef TRACELEXER
933 static int
934 sudoers_trace_print(const char *msg)
935 {
936     return fputs(msg, stderr);
937 }
938 #endif /* TRACELEXER */