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