go back to using explicit install calls in rules due to renaming required
[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 \+{WORD}                {
342                             /* netgroup */
343                             if (!fill(yytext, yyleng))
344                                 yyterminate();
345                             LEXTRACE("NETGROUP ");
346                             return(NETGROUP);
347                         }
348
349 \%:?{WORD}              {
350                             /* UN*X group */
351                             if (!fill(yytext, yyleng))
352                                 yyterminate();
353                             LEXTRACE("USERGROUP ");
354                             return(USERGROUP);
355                         }
356
357 {IPV4ADDR}(\/{IPV4ADDR})? {
358                             if (!fill(yytext, yyleng))
359                                 yyterminate();
360                             LEXTRACE("NTWKADDR ");
361                             return(NTWKADDR);
362                         }
363
364 {IPV4ADDR}\/([12][0-9]*|3[0-2]*) {
365                             if (!fill(yytext, yyleng))
366                                 yyterminate();
367                             LEXTRACE("NTWKADDR ");
368                             return(NTWKADDR);
369                         }
370
371 {IPV6ADDR}(\/{IPV6ADDR})? {
372                             if (!ipv6_valid(yytext)) {
373                                 LEXTRACE("ERROR ");
374                                 return(ERROR);
375                             }
376                             if (!fill(yytext, yyleng))
377                                 yyterminate();
378                             LEXTRACE("NTWKADDR ");
379                             return(NTWKADDR);
380                         }
381
382 {IPV6ADDR}\/([0-9]|[1-9][0-9]|1[01][0-9]|12[0-8]) {
383                             if (!ipv6_valid(yytext)) {
384                                 LEXTRACE("ERROR ");
385                                 return(ERROR);
386                             }
387                             if (!fill(yytext, yyleng))
388                                 yyterminate();
389                             LEXTRACE("NTWKADDR ");
390                             return(NTWKADDR);
391                         }
392
393 [[:upper:]][[:upper:][:digit:]_]* {
394                             if (strcmp(yytext, "ALL") == 0) {
395                                 LEXTRACE("ALL ");
396                                 return(ALL);
397                             }
398 #ifdef HAVE_SELINUX
399                             /* XXX - restrict type/role to initial state */
400                             if (strcmp(yytext, "TYPE") == 0) {
401                                 LEXTRACE("TYPE ");
402                                 return(TYPE);
403                             }
404                             if (strcmp(yytext, "ROLE") == 0) {
405                                 LEXTRACE("ROLE ");
406                                 return(ROLE);
407                             }
408 #endif /* HAVE_SELINUX */
409                             if (!fill(yytext, yyleng))
410                                 yyterminate();
411                             LEXTRACE("ALIAS ");
412                             return(ALIAS);
413                         }
414
415 <GOTDEFS>({PATH}|sudoedit) {
416                             /* no command args allowed for Defaults!/path */
417                             if (!fill_cmnd(yytext, yyleng))
418                                 yyterminate();
419                             LEXTRACE("COMMAND ");
420                             return(COMMAND);
421                         }
422
423 sudoedit                {
424                             BEGIN GOTCMND;
425                             LEXTRACE("COMMAND ");
426                             if (!fill_cmnd(yytext, yyleng))
427                                 yyterminate();
428                         }                       /* sudo -e */
429
430 {PATH}                  {
431                             /* directories can't have args... */
432                             if (yytext[yyleng - 1] == '/') {
433                                 LEXTRACE("COMMAND ");
434                                 if (!fill_cmnd(yytext, yyleng))
435                                     yyterminate();
436                                 return(COMMAND);
437                             } else {
438                                 BEGIN GOTCMND;
439                                 LEXTRACE("COMMAND ");
440                                 if (!fill_cmnd(yytext, yyleng))
441                                     yyterminate();
442                             }
443                         }                       /* a pathname */
444
445 <INITIAL,GOTDEFS>\"[^"\n]+\" {
446                             /* a quoted user/group name */
447                             if (!fill(yytext + 1, yyleng - 2))
448                                 yyterminate();
449                             switch (yytext[1]) {
450                             case '%':
451                                 LEXTRACE("USERGROUP ");
452                                 return(USERGROUP);
453                             case '+':
454                                 LEXTRACE("NETGROUP ");
455                                 return(NETGROUP);
456                             default:
457                                 LEXTRACE("WORD(4) ");
458                                 return(WORD);
459                             }
460                         }
461
462 <INITIAL,GOTDEFS>({ID}|{WORD}) {
463                             /* a word */
464                             if (!fill(yytext, yyleng))
465                                 yyterminate();
466                             LEXTRACE("WORD(5) ");
467                             return(WORD);
468                         }
469
470 \(                      {
471                             LEXTRACE("( ");
472                             return ('(');
473                         }
474
475 \)                      {
476                             LEXTRACE(") ");
477                             return(')');
478                         }
479
480 ,                       {
481                             LEXTRACE(", ");
482                             return(',');
483                         }                       /* return ',' */
484
485 =                       {
486                             LEXTRACE("= ");
487                             return('=');
488                         }                       /* return '=' */
489
490 :                       {
491                             LEXTRACE(": ");
492                             return(':');
493                         }                       /* return ':' */
494
495 <*>!+                   {
496                             if (yyleng % 2 == 1)
497                                 return('!');    /* return '!' */
498                         }
499
500 <*>\n                   {
501                             BEGIN INITIAL;
502                             ++sudolineno;
503                             LEXTRACE("\n");
504                             return(COMMENT);
505                         }                       /* return newline */
506
507 <*>[[:blank:]]+         {                       /* throw away space/tabs */
508                             sawspace = TRUE;    /* but remember for fill_args */
509                         }
510
511 <*>\\[[:blank:]]*\n     {
512                             sawspace = TRUE;    /* remember for fill_args */
513                             ++sudolineno;
514                             LEXTRACE("\n\t");
515                         }                       /* throw away EOL after \ */
516
517 <INITIAL,STARTDEFS,INDEFS>#(-[^\n0-9].*|[^\n0-9-].*)?\n {
518                             BEGIN INITIAL;
519                             ++sudolineno;
520                             LEXTRACE("\n");
521                             return(COMMENT);
522                         }                       /* comment, not uid/gid */
523
524 <*>.                    {
525                             LEXTRACE("ERROR ");
526                             return(ERROR);
527                         }       /* parse error */
528
529 <*><<EOF>>              {
530                             if (YY_START != INITIAL) {
531                                 BEGIN INITIAL;
532                                 LEXTRACE("ERROR ");
533                                 return(ERROR);
534                             }
535                             if (!pop_include())
536                                 yyterminate();
537                         }
538
539 %%
540 static unsigned char
541 hexchar(s)
542     const char *s;
543 {
544     int i;
545     int result = 0;
546
547     s += 2; /* skip \\x */
548     for (i = 0; i < 2; i++) {
549         switch (*s) {
550         case 'A':
551         case 'a':
552             result += 10;
553             break;
554         case 'B':
555         case 'b':
556             result += 11;
557             break;
558         case 'C':
559         case 'c':
560             result += 12;
561             break;
562         case 'D':
563         case 'd':
564             result += 13;
565             break;
566         case 'E':
567         case 'e':
568             result += 14;
569             break;
570         case 'F':
571         case 'f':
572             result += 15;
573             break;
574         default:
575             result += *s - '0';
576             break;
577         }
578         if (i == 0) {
579             result *= 16;
580             s++;
581         }
582     }
583     return((unsigned char)result);
584 }
585
586 static int
587 _fill(src, len, olen)
588     char *src;
589     int len, olen;
590 {
591     char *dst;
592
593     dst = olen ? realloc(yylval.string, olen + len + 1) : malloc(len + 1);
594     if (dst == NULL) {
595         yyerror("unable to allocate memory");
596         return(FALSE);
597     }
598     yylval.string = dst;
599
600     /* Copy the string and collapse any escaped characters. */
601     dst += olen;
602     while (len--) {
603         if (*src == '\\' && len) {
604             if (src[1] == 'x' && len >= 3 && 
605                 isxdigit((unsigned char) src[2]) &&
606                 isxdigit((unsigned char) src[3])) {
607                 *dst++ = hexchar(src);
608                 src += 4;
609                 len -= 3;
610             } else {
611                 src++;
612                 len--;
613                 *dst++ = *src++;
614             }
615         } else {
616             *dst++ = *src++;
617         }
618     }
619     *dst = '\0';
620     return(TRUE);
621 }
622
623 static int
624 append(src, len)
625     char *src;
626     int len;
627 {
628     int olen = 0;
629
630     if (yylval.string != NULL)
631         olen = strlen(yylval.string);
632
633     return(_fill(src, len, olen));
634 }
635
636 #define SPECIAL(c) \
637     ((c) == ',' || (c) == ':' || (c) == '=' || (c) == ' ' || (c) == '\t' || (c) == '#')
638
639 static int
640 fill_cmnd(src, len)
641     char *src;
642     int len;
643 {
644     char *dst;
645     int i;
646
647     arg_len = arg_size = 0;
648
649     dst = yylval.command.cmnd = (char *) malloc(len + 1);
650     if (yylval.command.cmnd == NULL) {
651         yyerror("unable to allocate memory");
652         return(FALSE);
653     }
654
655     /* Copy the string and collapse any escaped sudo-specific characters. */
656     for (i = 0; i < len; i++) {
657         if (src[i] == '\\' && i != len - 1 && SPECIAL(src[i + 1]))
658             *dst++ = src[++i];
659         else
660             *dst++ = src[i];
661     }
662     *dst = '\0';
663
664     yylval.command.args = NULL;
665     return(TRUE);
666 }
667
668 static int
669 fill_args(s, len, addspace)
670     char *s;
671     int len;
672     int addspace;
673 {
674     int new_len;
675     char *p;
676
677     if (yylval.command.args == NULL) {
678         addspace = 0;
679         new_len = len;
680     } else
681         new_len = arg_len + len + addspace;
682
683     if (new_len >= arg_size) {
684         /* Allocate more space than we need for subsequent args */
685         while (new_len >= (arg_size += COMMANDARGINC))
686             ;
687
688         p = yylval.command.args ?
689             (char *) realloc(yylval.command.args, arg_size) :
690             (char *) malloc(arg_size);
691         if (p == NULL) {
692             efree(yylval.command.args);
693             yyerror("unable to allocate memory");
694             return(FALSE);
695         } else
696             yylval.command.args = p;
697     }
698
699     /* Efficiently append the arg (with a leading space if needed). */
700     p = yylval.command.args + arg_len;
701     if (addspace)
702         *p++ = ' ';
703     if (strlcpy(p, s, arg_size - (p - yylval.command.args)) != len) {
704         yyerror("fill_args: buffer overflow");  /* paranoia */
705         return(FALSE);
706     }
707     arg_len = new_len;
708     return(TRUE);
709 }
710
711 struct path_list {
712     char *path;
713     struct path_list *next;
714 };
715
716 struct include_stack {
717     YY_BUFFER_STATE bs;
718     char *path;
719     struct path_list *more; /* more files in case of includedir */
720     int lineno;
721     int keepopen;
722 };
723
724 static int
725 pl_compare(v1, v2)
726     const void *v1;
727     const void *v2;
728 {
729     const struct path_list * const *p1 = v1;
730     const struct path_list * const *p2 = v2;
731
732     return(strcmp((*p1)->path, (*p2)->path));
733 }
734
735 static char *
736 switch_dir(stack, dirpath)
737     struct include_stack *stack;
738     char *dirpath;
739 {
740     DIR *dir;
741     int i, count = 0;
742     char *path = NULL;
743     struct dirent *dent;
744     struct stat sb;
745     struct path_list *pl, *first = NULL;
746     struct path_list **sorted = NULL;
747
748     if (!(dir = opendir(dirpath))) {
749         yyerror(dirpath);
750         return(NULL);
751     }
752     while ((dent = readdir(dir))) {
753         /* Ignore files that end in '~' or have a '.' in them. */
754         if (dent->d_name[0] == '\0' || dent->d_name[NAMLEN(dent) - 1] == '~'
755             || strchr(dent->d_name, '.') != NULL) {
756             continue;
757         }
758         if (asprintf(&path, "%s/%s", dirpath, dent->d_name) == -1) {
759             closedir(dir);
760             goto bad;
761         }
762         if (stat(path, &sb) != 0 || !S_ISREG(sb.st_mode)) {
763             efree(path);
764             continue;
765         }
766         pl = malloc(sizeof(*pl));
767         if (pl == NULL)
768             goto bad;
769         pl->path = path;
770         pl->next = first;
771         first = pl;
772         count++;
773     }
774     closedir(dir);
775
776     if (count == 0)
777         goto done;
778
779     /* Sort the list as an array. */
780     sorted = malloc(sizeof(*sorted) * count);
781     if (sorted == NULL)
782         goto bad;
783     pl = first;
784     for (i = 0; i < count; i++) {
785         sorted[i] = pl;
786         pl = pl->next;
787     }
788     qsort(sorted, count, sizeof(*sorted), pl_compare);
789
790     /* Apply sorting to the list. */
791     first = sorted[0];
792     sorted[count - 1]->next = NULL;
793     for (i = 1; i < count; i++)
794         sorted[i - 1]->next = sorted[i];
795     efree(sorted);
796
797     /* Pull out the first element for parsing, leave the rest for later. */
798     if (count) {
799         path = first->path;
800         pl = first->next;
801         efree(first);
802         stack->more = pl;
803     } else {
804         path = NULL;
805     }
806 done:
807     efree(dirpath);
808     return(path);
809 bad:
810     while (first != NULL) {
811         pl = first;
812         first = pl->next;
813         free(pl->path);
814         free(pl);
815     }
816     efree(sorted);
817     efree(dirpath);
818     efree(path);
819     return(NULL);
820 }
821
822 #define MAX_SUDOERS_DEPTH       128
823 #define SUDOERS_STACK_INCREMENT 16
824
825 static size_t istacksize, idepth;
826 static struct include_stack *istack;
827 static int keepopen;
828
829 void
830 init_lexer()
831 {
832     struct path_list *pl;
833
834     while (idepth) {
835         idepth--;
836         while ((pl = istack[idepth].more) != NULL) {
837             istack[idepth].more = pl->next;
838             efree(pl->path);
839             efree(pl);
840         }
841         efree(istack[idepth].path);
842         if (idepth && !istack[idepth].keepopen)
843             fclose(istack[idepth].bs->yy_input_file);
844         yy_delete_buffer(istack[idepth].bs);
845     }
846     efree(istack);
847     istack = NULL;
848     istacksize = idepth = 0;
849     keepopen = FALSE;
850 }
851
852 static int
853 _push_include(path, isdir)
854     char *path;
855     int isdir;
856 {
857     struct path_list *pl;
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         while ((fp = open_sudoers(path, FALSE, &keepopen)) == NULL) {
880             /* Unable to open path in includedir, go to next one, if any. */
881             efree(path);
882             if ((pl = istack[idepth].more) == NULL)
883                 return(FALSE);
884             path = pl->path;
885             istack[idepth].more = pl->next;
886             efree(pl);
887         }
888     } else {
889         if ((fp = open_sudoers(path, TRUE, &keepopen)) == NULL) {
890             yyerror(path);
891             return(FALSE);
892         }
893         istack[idepth].more = NULL;
894     }
895     /* Push the old (current) file and open the new one. */
896     istack[idepth].path = sudoers; /* push old path */
897     istack[idepth].bs = YY_CURRENT_BUFFER;
898     istack[idepth].lineno = sudolineno;
899     istack[idepth].keepopen = keepopen;
900     idepth++;
901     sudolineno = 1;
902     sudoers = path;
903     yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
904
905     return(TRUE);
906 }
907
908 static int
909 pop_include()
910 {
911     struct path_list *pl;
912     FILE *fp;
913
914     if (idepth == 0)
915         return(FALSE);
916
917     if (!keepopen)
918         fclose(YY_CURRENT_BUFFER->yy_input_file);
919     yy_delete_buffer(YY_CURRENT_BUFFER);
920     /* If we are in an include dir, move to the next file. */
921     while ((pl = istack[idepth - 1].more) != NULL) {
922         fp = open_sudoers(pl->path, FALSE, &keepopen);
923         if (fp != NULL) {
924             istack[idepth - 1].more = pl->next;
925             efree(sudoers);
926             sudoers = pl->path;
927             sudolineno = 1;
928             yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
929             efree(pl);
930             break;
931         }
932         /* Unable to open path in include dir, go to next one. */
933         istack[idepth - 1].more = pl->next;
934         efree(pl->path);
935         efree(pl);
936     }
937     /* If no path list, just pop the last dir on the stack. */
938     if (pl == NULL) {
939         idepth--;
940         yy_switch_to_buffer(istack[idepth].bs);
941         efree(sudoers);
942         sudoers = istack[idepth].path;
943         sudolineno = istack[idepth].lineno;
944         keepopen = istack[idepth].keepopen;
945     }
946     return(TRUE);
947 }
948
949 static char *
950 parse_include(base)
951     char *base;
952 {
953     char *cp, *ep, *path;
954     int len = 0, subst = 0;
955     size_t shost_len = 0;
956
957     /* Pull out path from #include line. */
958     cp = base + sizeof("#include");
959     if (*cp == 'i')
960         cp += 3; /* includedir */
961     while (isblank((unsigned char) *cp))
962         cp++;
963     ep = cp;
964     while (*ep != '\0' && !isspace((unsigned char) *ep)) {
965         if (ep[0] == '%' && ep[1] == 'h') {
966             shost_len = strlen(user_shost);
967             len += shost_len - 2;
968             subst = 1;
969         }
970         ep++;
971     }
972
973     /* Make a copy of path and return it. */
974     len += (int)(ep - cp);
975     if ((path = malloc(len + 1)) == NULL)
976         yyerror("unable to allocate memory");
977     if (subst) {
978         /* substitute for %h */
979         char *pp = path;
980         while (cp < ep) {
981             if (cp[0] == '%' && cp[1] == 'h') {
982                 memcpy(pp, user_shost, shost_len);
983                 pp += shost_len;
984                 cp += 2;
985                 continue;
986             }
987             *pp++ = *cp++;
988         }
989         *pp = '\0';
990     } else {
991         memcpy(path, cp, len);
992         path[len] = '\0';
993     }
994
995     /* Push any excess characters (e.g. comment, newline) back to the lexer */
996     if (*ep != '\0')
997         yyless((int)(ep - base));
998
999     return(path);
1000 }
1001
1002 /*
1003  * Check to make sure an IPv6 address does not contain multiple instances
1004  * of the string "::".  Assumes strlen(s) >= 1.
1005  * Returns TRUE if address is valid else FALSE.
1006  */
1007 static int
1008 ipv6_valid(s)
1009     const char *s;
1010 {
1011     int nmatch = 0;
1012
1013     for (; *s != '\0'; s++) {
1014         if (s[0] == ':' && s[1] == ':') {
1015             if (++nmatch > 1)
1016                 break;
1017         }
1018         if (s[0] == '/')
1019             nmatch = 0;                 /* reset if we hit netmask */
1020     }
1021
1022     return (nmatch <= 1);
1023 }