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