Annotation of embedaddon/sudo/plugins/sudoers/toke.l, revision 1.1.1.3
1.1 misho 1: %{
2: /*
1.1.1.2 misho 3: * Copyright (c) 1996, 1998-2005, 2007-2012
1.1 misho 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>
1.1.1.2 misho 74: #include "lbuf.h"
75: #include "secure_path.h"
1.1 misho 76:
77: extern YYSTYPE yylval;
1.1.1.2 misho 78: extern bool parse_error;
79: extern bool sudoers_warnings;
1.1 misho 80: int sudolineno;
1.1.1.2 misho 81: int last_token;
1.1 misho 82: char *sudoers;
83:
1.1.1.2 misho 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;
1.1 misho 89:
1.1.1.2 misho 90: static bool continued, sawspace;
91: static int prev_state;
92:
93: static bool _push_include(char *, bool);
94: static bool pop_include(void);
1.1 misho 95: static char *parse_include(char *);
96:
97: int (*trace_print)(const char *msg) = sudoers_trace_print;
98:
1.1.1.2 misho 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))
1.1 misho 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(", ");
1.1.1.2 misho 135: LEXRETURN(',');
1.1 misho 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();
1.1.1.2 misho 145: LEXRETURN(DEFVAR);
1.1 misho 146: }
147:
148: <INDEFS>{
149: , {
150: BEGIN STARTDEFS;
151: LEXTRACE(", ");
1.1.1.2 misho 152: LEXRETURN(',');
1.1 misho 153: } /* return ',' */
154:
155: = {
156: LEXTRACE("= ");
1.1.1.2 misho 157: LEXRETURN('=');
1.1 misho 158: } /* return '=' */
159:
160: \+= {
161: LEXTRACE("+= ");
1.1.1.2 misho 162: LEXRETURN('+');
1.1 misho 163: } /* return '+' */
164:
165: -= {
166: LEXTRACE("-= ");
1.1.1.2 misho 167: LEXRETURN('-');
1.1 misho 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();
1.1.1.2 misho 181: LEXRETURN(WORD);
1.1 misho 182: }
183: }
184:
185: <INSTR>{
186: \\[[:blank:]]*\n[[:blank:]]* {
187: /* Line continuation char followed by newline. */
1.1.1.2 misho 188: sudolineno++;
189: continued = true;
1.1 misho 190: }
191:
192: \" {
193: LEXTRACE("ENDSTR ");
194: BEGIN prev_state;
195:
196: if (yylval.string == NULL) {
197: LEXTRACE("ERROR "); /* empty string */
1.1.1.2 misho 198: LEXRETURN(ERROR);
1.1 misho 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 */
1.1.1.2 misho 207: LEXRETURN(ERROR);
1.1 misho 208: }
209: LEXTRACE("USERGROUP ");
1.1.1.2 misho 210: LEXRETURN(USERGROUP);
1.1 misho 211: case '+':
212: if (yylval.string[1] == '\0') {
213: LEXTRACE("ERROR "); /* empty netgroup */
1.1.1.2 misho 214: LEXRETURN(ERROR);
1.1 misho 215: }
216: LEXTRACE("NETGROUP ");
1.1.1.2 misho 217: LEXRETURN(NETGROUP);
1.1 misho 218: }
219: }
220: LEXTRACE("WORD(4) ");
1.1.1.2 misho 221: LEXRETURN(WORD);
1.1 misho 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();
1.1.1.2 misho 243: sawspace = false;
1.1 misho 244: }
245:
246: \\[:\\,= \t#] {
247: /* quoted sudoers special char, strip backslash */
248: LEXTRACE("QUOTEDCHAR ");
249: if (!fill_args(yytext + 1, 1, sawspace))
250: yyterminate();
1.1.1.2 misho 251: sawspace = false;
1.1 misho 252: }
253:
254: [#:\,=\n] {
255: BEGIN INITIAL;
256: yyless(0);
1.1.1.2 misho 257: LEXRETURN(COMMAND);
1.1 misho 258: } /* end of command line args */
259:
260: [^#\\:, \t\n]+ {
261: LEXTRACE("ARG ");
262: if (!fill_args(yytext, yyleng, sawspace))
263: yyterminate();
1.1.1.2 misho 264: sawspace = false;
1.1 misho 265: } /* a command line arg */
266: }
267:
1.1.1.2 misho 268: <INITIAL>^#include[[:blank:]]+.*\n {
1.1 misho 269: char *path;
270:
271: if (continued) {
272: LEXTRACE("ERROR ");
1.1.1.2 misho 273: LEXRETURN(ERROR);
1.1 misho 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:
1.1.1.2 misho 286: <INITIAL>^#includedir[[:blank:]]+.*\n {
1.1 misho 287: char *path;
288:
289: if (continued) {
290: LEXTRACE("ERROR ");
1.1.1.2 misho 291: LEXRETURN(ERROR);
1.1 misho 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 ");
1.1.1.2 misho 313: LEXRETURN(ERROR);
1.1 misho 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 ");
1.1.1.2 misho 328: LEXRETURN(DEFAULTS_USER);
1.1 misho 329: case '>':
330: yyless(n);
331: LEXTRACE("DEFAULTS_RUNAS ");
1.1.1.2 misho 332: LEXRETURN(DEFAULTS_RUNAS);
1.1 misho 333: case '@':
334: yyless(n);
335: LEXTRACE("DEFAULTS_HOST ");
1.1.1.2 misho 336: LEXRETURN(DEFAULTS_HOST);
1.1 misho 337: case '!':
338: yyless(n);
339: LEXTRACE("DEFAULTS_CMND ");
1.1.1.2 misho 340: LEXRETURN(DEFAULTS_CMND);
1.1 misho 341: default:
342: LEXTRACE("DEFAULTS ");
1.1.1.2 misho 343: LEXRETURN(DEFAULTS);
1.1 misho 344: }
345: }
346:
347: <INITIAL>^[[:blank:]]*(Host|Cmnd|User|Runas)_Alias {
348: int n;
349:
350: if (continued) {
351: LEXTRACE("ERROR ");
1.1.1.2 misho 352: LEXRETURN(ERROR);
1.1 misho 353: }
354:
355: for (n = 0; isblank((unsigned char)yytext[n]); n++)
356: continue;
357: switch (yytext[n]) {
358: case 'H':
359: LEXTRACE("HOSTALIAS ");
1.1.1.2 misho 360: LEXRETURN(HOSTALIAS);
1.1 misho 361: case 'C':
362: LEXTRACE("CMNDALIAS ");
1.1.1.2 misho 363: LEXRETURN(CMNDALIAS);
1.1 misho 364: case 'U':
365: LEXTRACE("USERALIAS ");
1.1.1.2 misho 366: LEXRETURN(USERALIAS);
1.1 misho 367: case 'R':
368: LEXTRACE("RUNASALIAS ");
1.1.1.2 misho 369: LEXRETURN(RUNASALIAS);
1.1 misho 370: }
371: }
372:
373: NOPASSWD[[:blank:]]*: {
374: /* cmnd does not require passwd for this user */
375: LEXTRACE("NOPASSWD ");
1.1.1.2 misho 376: LEXRETURN(NOPASSWD);
1.1 misho 377: }
378:
379: PASSWD[[:blank:]]*: {
380: /* cmnd requires passwd for this user */
381: LEXTRACE("PASSWD ");
1.1.1.2 misho 382: LEXRETURN(PASSWD);
1.1 misho 383: }
384:
385: NOEXEC[[:blank:]]*: {
386: LEXTRACE("NOEXEC ");
1.1.1.2 misho 387: LEXRETURN(NOEXEC);
1.1 misho 388: }
389:
390: EXEC[[:blank:]]*: {
391: LEXTRACE("EXEC ");
1.1.1.2 misho 392: LEXRETURN(EXEC);
1.1 misho 393: }
394:
395: SETENV[[:blank:]]*: {
396: LEXTRACE("SETENV ");
1.1.1.2 misho 397: LEXRETURN(SETENV);
1.1 misho 398: }
399:
400: NOSETENV[[:blank:]]*: {
401: LEXTRACE("NOSETENV ");
1.1.1.2 misho 402: LEXRETURN(NOSETENV);
1.1 misho 403: }
404:
405: LOG_OUTPUT[[:blank:]]*: {
406: LEXTRACE("LOG_OUTPUT ");
1.1.1.2 misho 407: LEXRETURN(LOG_OUTPUT);
1.1 misho 408: }
409:
410: NOLOG_OUTPUT[[:blank:]]*: {
411: LEXTRACE("NOLOG_OUTPUT ");
1.1.1.2 misho 412: LEXRETURN(NOLOG_OUTPUT);
1.1 misho 413: }
414:
415: LOG_INPUT[[:blank:]]*: {
416: LEXTRACE("LOG_INPUT ");
1.1.1.2 misho 417: LEXRETURN(LOG_INPUT);
1.1 misho 418: }
419:
420: NOLOG_INPUT[[:blank:]]*: {
421: LEXTRACE("NOLOG_INPUT ");
1.1.1.2 misho 422: LEXRETURN(NOLOG_INPUT);
1.1 misho 423: }
424:
425: <INITIAL,GOTDEFS>(\+|\%|\%:) {
426: /* empty group or netgroup */
427: LEXTRACE("ERROR ");
1.1.1.2 misho 428: LEXRETURN(ERROR);
1.1 misho 429: }
430:
431: \+{WORD} {
432: /* netgroup */
433: if (!fill(yytext, yyleng))
434: yyterminate();
435: LEXTRACE("NETGROUP ");
1.1.1.2 misho 436: LEXRETURN(NETGROUP);
1.1 misho 437: }
438:
439: \%:?({WORD}|{ID}) {
440: /* group */
441: if (!fill(yytext, yyleng))
442: yyterminate();
443: LEXTRACE("USERGROUP ");
1.1.1.2 misho 444: LEXRETURN(USERGROUP);
1.1 misho 445: }
446:
447: {IPV4ADDR}(\/{IPV4ADDR})? {
448: if (!fill(yytext, yyleng))
449: yyterminate();
450: LEXTRACE("NTWKADDR ");
1.1.1.2 misho 451: LEXRETURN(NTWKADDR);
1.1 misho 452: }
453:
454: {IPV4ADDR}\/([12]?[0-9]|3[0-2]) {
455: if (!fill(yytext, yyleng))
456: yyterminate();
457: LEXTRACE("NTWKADDR ");
1.1.1.2 misho 458: LEXRETURN(NTWKADDR);
1.1 misho 459: }
460:
461: {IPV6ADDR}(\/{IPV6ADDR})? {
462: if (!ipv6_valid(yytext)) {
463: LEXTRACE("ERROR ");
1.1.1.2 misho 464: LEXRETURN(ERROR);
1.1 misho 465: }
466: if (!fill(yytext, yyleng))
467: yyterminate();
468: LEXTRACE("NTWKADDR ");
1.1.1.2 misho 469: LEXRETURN(NTWKADDR);
1.1 misho 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 ");
1.1.1.2 misho 475: LEXRETURN(ERROR);
1.1 misho 476: }
477: if (!fill(yytext, yyleng))
478: yyterminate();
479: LEXTRACE("NTWKADDR ");
1.1.1.2 misho 480: LEXRETURN(NTWKADDR);
1.1 misho 481: }
482:
483: ALL {
484: LEXTRACE("ALL ");
1.1.1.2 misho 485: LEXRETURN(ALL);
1.1 misho 486:
487: }
488:
489: <INITIAL>ROLE {
490: #ifdef HAVE_SELINUX
491: LEXTRACE("ROLE ");
1.1.1.2 misho 492: LEXRETURN(ROLE);
1.1 misho 493: #else
494: goto got_alias;
495: #endif
496: }
497:
498: <INITIAL>TYPE {
499: #ifdef HAVE_SELINUX
500: LEXTRACE("TYPE ");
1.1.1.2 misho 501: LEXRETURN(TYPE);
1.1 misho 502: #else
503: goto got_alias;
504: #endif
505: }
1.1.1.3 ! misho 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: }
1.1 misho 523:
524: [[:upper:]][[:upper:][:digit:]_]* {
525: got_alias:
526: if (!fill(yytext, yyleng))
527: yyterminate();
528: LEXTRACE("ALIAS ");
1.1.1.2 misho 529: LEXRETURN(ALIAS);
1.1 misho 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 ");
1.1.1.2 misho 537: LEXRETURN(COMMAND);
1.1 misho 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();
1.1.1.2 misho 553: LEXRETURN(COMMAND);
1.1 misho 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) ");
1.1.1.2 misho 574: LEXRETURN(WORD);
1.1 misho 575: }
576:
577: \( {
578: LEXTRACE("( ");
1.1.1.2 misho 579: LEXRETURN('(');
1.1 misho 580: }
581:
582: \) {
583: LEXTRACE(") ");
1.1.1.2 misho 584: LEXRETURN(')');
1.1 misho 585: }
586:
587: , {
588: LEXTRACE(", ");
1.1.1.2 misho 589: LEXRETURN(',');
1.1 misho 590: } /* return ',' */
591:
592: = {
593: LEXTRACE("= ");
1.1.1.2 misho 594: LEXRETURN('=');
1.1 misho 595: } /* return '=' */
596:
597: : {
598: LEXTRACE(": ");
1.1.1.2 misho 599: LEXRETURN(':');
1.1 misho 600: } /* return ':' */
601:
602: <*>!+ {
603: if (yyleng & 1) {
604: LEXTRACE("!");
1.1.1.2 misho 605: LEXRETURN('!'); /* return '!' */
1.1 misho 606: }
607: }
608:
609: <*>\n {
610: if (YY_START == INSTR) {
611: LEXTRACE("ERROR ");
1.1.1.2 misho 612: LEXRETURN(ERROR); /* line break in string */
1.1 misho 613: }
614: BEGIN INITIAL;
1.1.1.2 misho 615: sudolineno++;
616: continued = false;
1.1 misho 617: LEXTRACE("\n");
1.1.1.2 misho 618: LEXRETURN(COMMENT);
1.1 misho 619: } /* return newline */
620:
621: <*>[[:blank:]]+ { /* throw away space/tabs */
1.1.1.2 misho 622: sawspace = true; /* but remember for fill_args */
1.1 misho 623: }
624:
625: <*>\\[[:blank:]]*\n {
1.1.1.2 misho 626: sawspace = true; /* remember for fill_args */
627: sudolineno++;
628: continued = true;
1.1 misho 629: } /* throw away EOL after \ */
630:
631: <INITIAL,STARTDEFS,INDEFS>#(-[^\n0-9].*|[^\n0-9-].*)?\n {
632: BEGIN INITIAL;
1.1.1.2 misho 633: sudolineno++;
634: continued = false;
1.1 misho 635: LEXTRACE("#\n");
1.1.1.2 misho 636: LEXRETURN(COMMENT);
1.1 misho 637: } /* comment, not uid/gid */
638:
639: <*>. {
640: LEXTRACE("ERROR ");
1.1.1.2 misho 641: LEXRETURN(ERROR);
1.1 misho 642: } /* parse error */
643:
644: <*><<EOF>> {
645: if (YY_START != INITIAL) {
646: BEGIN INITIAL;
647: LEXTRACE("ERROR ");
1.1.1.2 misho 648: LEXRETURN(ERROR);
1.1 misho 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;
1.1.1.2 misho 665: bool keepopen;
1.1 misho 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;
1.1.1.2 misho 687: debug_decl(switch_dir, SUDO_DEBUG_PARSER)
1.1 misho 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);
1.1.1.2 misho 758: debug_return_str(path);
1.1 misho 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);
1.1.1.2 misho 769: debug_return_str(NULL);
1.1 misho 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;
1.1.1.2 misho 777: static bool keepopen;
1.1 misho 778:
779: void
780: init_lexer(void)
781: {
782: struct path_list *pl;
1.1.1.2 misho 783: debug_decl(init_lexer, SUDO_DEBUG_PARSER)
1.1 misho 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;
1.1.1.2 misho 801: keepopen = false;
802: sawspace = false;
803: continued = false;
1.1 misho 804: prev_state = INITIAL;
1.1.1.2 misho 805:
806: debug_return;
1.1 misho 807: }
808:
1.1.1.2 misho 809: static bool
810: _push_include(char *path, bool isdir)
1.1 misho 811: {
812: struct path_list *pl;
813: FILE *fp;
1.1.1.2 misho 814: debug_decl(_push_include, SUDO_DEBUG_PARSER)
1.1 misho 815:
816: /* push current state onto stack */
817: if (idepth >= istacksize) {
818: if (idepth > MAX_SUDOERS_DEPTH) {
819: yyerror(_("too many levels of includes"));
1.1.1.2 misho 820: debug_return_bool(false);
1.1 misho 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"));
1.1.1.2 misho 827: debug_return_bool(false);
1.1 misho 828: }
829: }
830: if (isdir) {
1.1.1.2 misho 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: }
1.1 misho 866: if (!(path = switch_dir(&istack[idepth], path))) {
867: /* switch_dir() called yyerror() for us */
1.1.1.2 misho 868: debug_return_bool(false);
1.1 misho 869: }
1.1.1.2 misho 870: while ((fp = open_sudoers(path, false, &keepopen)) == NULL) {
1.1 misho 871: /* Unable to open path in includedir, go to next one, if any. */
872: efree(path);
873: if ((pl = istack[idepth].more) == NULL)
1.1.1.2 misho 874: debug_return_bool(false);
1.1 misho 875: path = pl->path;
876: istack[idepth].more = pl->next;
877: efree(pl);
878: }
879: } else {
1.1.1.2 misho 880: if ((fp = open_sudoers(path, true, &keepopen)) == NULL) {
1.1.1.3 ! misho 881: /* The error was already printed by open_sudoers() */
! 882: yyerror(NULL);
1.1.1.2 misho 883: debug_return_bool(false);
1.1 misho 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:
1.1.1.2 misho 897: debug_return_bool(true);
1.1 misho 898: }
899:
1.1.1.2 misho 900: static bool
1.1 misho 901: pop_include(void)
902: {
903: struct path_list *pl;
904: FILE *fp;
1.1.1.2 misho 905: debug_decl(pop_include, SUDO_DEBUG_PARSER)
1.1 misho 906:
907: if (idepth == 0)
1.1.1.2 misho 908: debug_return_bool(false);
1.1 misho 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) {
1.1.1.2 misho 915: fp = open_sudoers(pl->path, false, &keepopen);
1.1 misho 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: }
1.1.1.2 misho 939: debug_return_bool(true);
1.1 misho 940: }
941:
942: static char *
943: parse_include(char *base)
944: {
1.1.1.2 misho 945: char *cp, *ep, *path, *pp;
946: int dirlen = 0, len = 0, subst = 0;
1.1 misho 947: size_t shost_len = 0;
1.1.1.2 misho 948: debug_decl(parse_include, SUDO_DEBUG_PARSER)
1.1 misho 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:
1.1.1.2 misho 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. */
1.1 misho 974: len += (int)(ep - cp);
1.1.1.2 misho 975: path = pp = malloc(len + dirlen + 1);
976: if (path == NULL) {
1.1 misho 977: yyerror(_("unable to allocate memory"));
1.1.1.2 misho 978: debug_return_str(NULL);
979: }
980: if (dirlen) {
981: memcpy(path, sudoers, dirlen);
982: pp += dirlen;
1.1 misho 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 {
1.1.1.2 misho 997: memcpy(pp, cp, len);
998: pp[len] = '\0';
1.1 misho 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:
1.1.1.2 misho 1005: debug_return_str(path);
1.1 misho 1006: }
1007:
1008: #ifdef TRACELEXER
1.1.1.3 ! misho 1009: int
1.1 misho 1010: sudoers_trace_print(const char *msg)
1011: {
1012: return fputs(msg, stderr);
1013: }
1.1.1.2 misho 1014: #else
1.1.1.3 ! misho 1015: int
1.1.1.2 misho 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: }
1.1 misho 1036: #endif /* TRACELEXER */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>