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