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