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