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