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