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