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