File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / plugins / sudoers / toke.l
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 16:12:54 2014 UTC (10 years, 3 months ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_10p3_0, v1_8_10p3, HEAD
sudo v 1.8.10p3

    1: %{
    2: /*
    3:  * Copyright (c) 1996, 1998-2005, 2007-2013
    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 */
   45: #if defined(HAVE_STDINT_H)
   46: # include <stdint.h>
   47: #elif defined(HAVE_INTTYPES_H)
   48: # include <inttypes.h>
   49: #endif
   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>
   78: #include "lbuf.h"
   79: #include "sha2.h"
   80: #include "secure_path.h"
   81: 
   82: int sudolineno;			/* current sudoers line number. */
   83: int last_token;			/* last token that was parsed. */
   84: char *sudoers;			/* sudoers file being parsed. */
   85: 
   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;
   91: 
   92: static bool continued, sawspace;
   93: static int prev_state;
   94: static int digest_len;
   95: 
   96: static bool _push_include(char *, bool);
   97: static bool pop_include(void);
   98: static char *parse_include(char *);
   99: 
  100: int (*trace_print)(const char *msg) = sudoers_trace_print;
  101: 
  102: #define LEXRETURN(n)	do {	\
  103: 	last_token = (n);	\
  104: 	return (n);		\
  105: } while (0)
  106: 
  107: #define ECHO	ignore_result(fwrite(sudoerstext, sudoersleng, 1, sudoersout))
  108: 
  109: #define	push_include(_p)	(_push_include((_p), false))
  110: #define	push_includedir(_p)	(_push_include((_p), true))
  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
  128: %option prefix="sudoers"
  129: 
  130: %s	GOTDEFS
  131: %x	GOTCMND
  132: %x	STARTDEFS
  133: %x	INDEFS
  134: %x	INSTR
  135: %s	WANTDIGEST
  136: 
  137: %%
  138: <GOTDEFS>[[:blank:]]*,[[:blank:]]* {
  139: 			    LEXTRACE(", ");
  140: 			    LEXRETURN(',');
  141: 			}			/* return ',' */
  142: 
  143: <GOTDEFS>[[:blank:]]+	BEGIN STARTDEFS;
  144: 
  145: <STARTDEFS>{DEFVAR}	{
  146: 			    BEGIN INDEFS;
  147: 			    LEXTRACE("DEFVAR ");
  148: 			    if (!fill(sudoerstext, sudoersleng))
  149: 				yyterminate();
  150: 			    LEXRETURN(DEFVAR);
  151: 			}
  152: 
  153: <INDEFS>{
  154:     ,			{
  155: 			    BEGIN STARTDEFS;
  156: 			    LEXTRACE(", ");
  157: 			    LEXRETURN(',');
  158: 			}			/* return ',' */
  159: 
  160:     =			{
  161: 			    LEXTRACE("= ");
  162: 			    LEXRETURN('=');
  163: 			}			/* return '=' */
  164: 
  165:     \+=			{
  166: 			    LEXTRACE("+= ");
  167: 			    LEXRETURN('+');
  168: 			}			/* return '+' */
  169: 
  170:     -=			{
  171: 			    LEXTRACE("-= ");
  172: 			    LEXRETURN('-');
  173: 			}			/* return '-' */
  174: 
  175:     \"			{
  176: 			    LEXTRACE("BEGINSTR ");
  177: 			    sudoerslval.string = NULL;
  178: 			    prev_state = YY_START;
  179: 			    BEGIN INSTR;
  180: 			}
  181: 
  182:     {ENVAR}		{
  183: 			    LEXTRACE("WORD(2) ");
  184: 			    if (!fill(sudoerstext, sudoersleng))
  185: 				yyterminate();
  186: 			    LEXRETURN(WORD);
  187: 			}
  188: }
  189: 
  190: <INSTR>{
  191:     \\[[:blank:]]*\n[[:blank:]]*	{
  192: 			    /* Line continuation char followed by newline. */
  193: 			    sudolineno++;
  194: 			    continued = true;
  195: 			}
  196: 
  197:     \"			{
  198: 			    LEXTRACE("ENDSTR ");
  199: 			    BEGIN prev_state;
  200: 
  201: 			    if (sudoerslval.string == NULL) {
  202: 				LEXTRACE("ERROR "); /* empty string */
  203: 				LEXRETURN(ERROR);
  204: 			    }
  205: 			    if (prev_state == INITIAL) {
  206: 				switch (sudoerslval.string[0]) {
  207: 				case '%':
  208: 				    if (sudoerslval.string[1] == '\0' ||
  209: 					(sudoerslval.string[1] == ':' &&
  210: 					sudoerslval.string[2] == '\0')) {
  211: 					LEXTRACE("ERROR "); /* empty group */
  212: 					LEXRETURN(ERROR);
  213: 				    }
  214: 				    LEXTRACE("USERGROUP ");
  215: 				    LEXRETURN(USERGROUP);
  216: 				case '+':
  217: 				    if (sudoerslval.string[1] == '\0') {
  218: 					LEXTRACE("ERROR "); /* empty netgroup */
  219: 					LEXRETURN(ERROR);
  220: 				    }
  221: 				    LEXTRACE("NETGROUP ");
  222: 				    LEXRETURN(NETGROUP);
  223: 				}
  224: 			    }
  225: 			    LEXTRACE("WORD(4) ");
  226: 			    LEXRETURN(WORD);
  227: 			}
  228: 
  229:     \\			{
  230: 			    LEXTRACE("BACKSLASH ");
  231: 			    if (!append(sudoerstext, sudoersleng))
  232: 				yyterminate();
  233: 			}
  234: 
  235:     ([^\"\n\\]|\\\")+	{
  236: 			    LEXTRACE("STRBODY ");
  237: 			    if (!append(sudoerstext, sudoersleng))
  238: 				yyterminate();
  239: 			}
  240: }
  241: 
  242: <GOTCMND>{
  243:     \\[\*\?\[\]\!]	{
  244: 			    /* quoted fnmatch glob char, pass verbatim */
  245: 			    LEXTRACE("QUOTEDCHAR ");
  246: 			    if (!fill_args(sudoerstext, 2, sawspace))
  247: 				yyterminate();
  248: 			    sawspace = false;
  249: 			}
  250: 
  251:     \\[:\\,= \t#]	{
  252: 			    /* quoted sudoers special char, strip backslash */
  253: 			    LEXTRACE("QUOTEDCHAR ");
  254: 			    if (!fill_args(sudoerstext + 1, 1, sawspace))
  255: 				yyterminate();
  256: 			    sawspace = false;
  257: 			}
  258: 
  259:     [#:\,=\n]		{
  260: 			    BEGIN INITIAL;
  261: 			    yyless(0);
  262: 			    LEXRETURN(COMMAND);
  263: 			}			/* end of command line args */
  264: 
  265:     [^#\\:, \t\n]+ 	{
  266: 			    LEXTRACE("ARG ");
  267: 			    if (!fill_args(sudoerstext, sudoersleng, sawspace))
  268: 				yyterminate();
  269: 			    sawspace = false;
  270: 			}			/* a command line arg */
  271: }
  272: 
  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. */
  288: 			    int len;
  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: 
  307: <INITIAL>^#include[[:blank:]]+.*\n {
  308: 			    char *path;
  309: 
  310: 			    if (continued) {
  311: 				LEXTRACE("ERROR ");
  312: 				LEXRETURN(ERROR);
  313: 			    }
  314: 
  315: 			    if ((path = parse_include(sudoerstext)) == NULL)
  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: 
  325: <INITIAL>^#includedir[[:blank:]]+.*\n {
  326: 			    char *path;
  327: 
  328: 			    if (continued) {
  329: 				LEXTRACE("ERROR ");
  330: 				LEXRETURN(ERROR);
  331: 			    }
  332: 
  333: 			    if ((path = parse_include(sudoerstext)) == NULL)
  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 ");
  352: 				LEXRETURN(ERROR);
  353: 			    }
  354: 
  355: 			    for (n = 0; isblank((unsigned char)sudoerstext[n]); n++)
  356: 				continue;
  357: 			    n += sizeof("Defaults") - 1;
  358: 			    if ((deftype = sudoerstext[n++]) != '\0') {
  359: 				while (isblank((unsigned char)sudoerstext[n]))
  360: 				    n++;
  361: 			    }
  362: 			    BEGIN GOTDEFS;
  363: 			    switch (deftype) {
  364: 				case ':':
  365: 				    yyless(n);
  366: 				    LEXTRACE("DEFAULTS_USER ");
  367: 				    LEXRETURN(DEFAULTS_USER);
  368: 				case '>':
  369: 				    yyless(n);
  370: 				    LEXTRACE("DEFAULTS_RUNAS ");
  371: 				    LEXRETURN(DEFAULTS_RUNAS);
  372: 				case '@':
  373: 				    yyless(n);
  374: 				    LEXTRACE("DEFAULTS_HOST ");
  375: 				    LEXRETURN(DEFAULTS_HOST);
  376: 				case '!':
  377: 				    yyless(n);
  378: 				    LEXTRACE("DEFAULTS_CMND ");
  379: 				    LEXRETURN(DEFAULTS_CMND);
  380: 				default:
  381: 				    LEXTRACE("DEFAULTS ");
  382: 				    LEXRETURN(DEFAULTS);
  383: 			    }
  384: 			}
  385: 
  386: <INITIAL>^[[:blank:]]*(Host|Cmnd|User|Runas)_Alias	{
  387: 			    int n;
  388: 
  389: 			    if (continued) {
  390: 				LEXTRACE("ERROR ");
  391: 				LEXRETURN(ERROR);
  392: 			    }
  393: 
  394: 			    for (n = 0; isblank((unsigned char)sudoerstext[n]); n++)
  395: 				continue;
  396: 			    switch (sudoerstext[n]) {
  397: 				case 'H':
  398: 				    LEXTRACE("HOSTALIAS ");
  399: 				    LEXRETURN(HOSTALIAS);
  400: 				case 'C':
  401: 				    LEXTRACE("CMNDALIAS ");
  402: 				    LEXRETURN(CMNDALIAS);
  403: 				case 'U':
  404: 				    LEXTRACE("USERALIAS ");
  405: 				    LEXRETURN(USERALIAS);
  406: 				case 'R':
  407: 				    LEXTRACE("RUNASALIAS ");
  408: 				    LEXRETURN(RUNASALIAS);
  409: 			    }
  410: 			}
  411: 
  412: NOPASSWD[[:blank:]]*:	{
  413: 				/* cmnd does not require passwd for this user */
  414: 			    	LEXTRACE("NOPASSWD ");
  415: 			    	LEXRETURN(NOPASSWD);
  416: 			}
  417: 
  418: PASSWD[[:blank:]]*:	{
  419: 				/* cmnd requires passwd for this user */
  420: 			    	LEXTRACE("PASSWD ");
  421: 			    	LEXRETURN(PASSWD);
  422: 			}
  423: 
  424: NOEXEC[[:blank:]]*:	{
  425: 			    	LEXTRACE("NOEXEC ");
  426: 			    	LEXRETURN(NOEXEC);
  427: 			}
  428: 
  429: EXEC[[:blank:]]*:	{
  430: 			    	LEXTRACE("EXEC ");
  431: 			    	LEXRETURN(EXEC);
  432: 			}
  433: 
  434: SETENV[[:blank:]]*:	{
  435: 			    	LEXTRACE("SETENV ");
  436: 			    	LEXRETURN(SETENV);
  437: 			}
  438: 
  439: NOSETENV[[:blank:]]*:	{
  440: 			    	LEXTRACE("NOSETENV ");
  441: 			    	LEXRETURN(NOSETENV);
  442: 			}
  443: 
  444: LOG_OUTPUT[[:blank:]]*:	{
  445: 			    	LEXTRACE("LOG_OUTPUT ");
  446: 			    	LEXRETURN(LOG_OUTPUT);
  447: 			}
  448: 
  449: NOLOG_OUTPUT[[:blank:]]*:	{
  450: 			    	LEXTRACE("NOLOG_OUTPUT ");
  451: 			    	LEXRETURN(NOLOG_OUTPUT);
  452: 			}
  453: 
  454: LOG_INPUT[[:blank:]]*:	{
  455: 			    	LEXTRACE("LOG_INPUT ");
  456: 			    	LEXRETURN(LOG_INPUT);
  457: 			}
  458: 
  459: NOLOG_INPUT[[:blank:]]*:	{
  460: 			    	LEXTRACE("NOLOG_INPUT ");
  461: 			    	LEXRETURN(NOLOG_INPUT);
  462: 			}
  463: 
  464: <INITIAL,GOTDEFS>(\+|\%|\%:) {
  465: 			    /* empty group or netgroup */
  466: 			    LEXTRACE("ERROR ");
  467: 			    LEXRETURN(ERROR);
  468: 			}
  469: 
  470: \+{WORD}		{
  471: 			    /* netgroup */
  472: 			    if (!fill(sudoerstext, sudoersleng))
  473: 				yyterminate();
  474: 			    LEXTRACE("NETGROUP ");
  475: 			    LEXRETURN(NETGROUP);
  476: 			}
  477: 
  478: \%:?({WORD}|{ID})	{
  479: 			    /* group */
  480: 			    if (!fill(sudoerstext, sudoersleng))
  481: 				yyterminate();
  482: 			    LEXTRACE("USERGROUP ");
  483: 			    LEXRETURN(USERGROUP);
  484: 			}
  485: 
  486: {IPV4ADDR}(\/{IPV4ADDR})? {
  487: 			    if (!fill(sudoerstext, sudoersleng))
  488: 				yyterminate();
  489: 			    LEXTRACE("NTWKADDR ");
  490: 			    LEXRETURN(NTWKADDR);
  491: 			}
  492: 
  493: {IPV4ADDR}\/([12]?[0-9]|3[0-2]) {
  494: 			    if (!fill(sudoerstext, sudoersleng))
  495: 				yyterminate();
  496: 			    LEXTRACE("NTWKADDR ");
  497: 			    LEXRETURN(NTWKADDR);
  498: 			}
  499: 
  500: {IPV6ADDR}(\/{IPV6ADDR})? {
  501: 			    if (!ipv6_valid(sudoerstext)) {
  502: 				LEXTRACE("ERROR ");
  503: 				LEXRETURN(ERROR);
  504: 			    }
  505: 			    if (!fill(sudoerstext, sudoersleng))
  506: 				yyterminate();
  507: 			    LEXTRACE("NTWKADDR ");
  508: 			    LEXRETURN(NTWKADDR);
  509: 			}
  510: 
  511: {IPV6ADDR}\/([0-9]|[1-9][0-9]|1[01][0-9]|12[0-8]) {
  512: 			    if (!ipv6_valid(sudoerstext)) {
  513: 				LEXTRACE("ERROR ");
  514: 				LEXRETURN(ERROR);
  515: 			    }
  516: 			    if (!fill(sudoerstext, sudoersleng))
  517: 				yyterminate();
  518: 			    LEXTRACE("NTWKADDR ");
  519: 			    LEXRETURN(NTWKADDR);
  520: 			}
  521: 
  522: ALL {
  523: 			    LEXTRACE("ALL ");
  524: 			    LEXRETURN(ALL);
  525: 
  526: 			}
  527: 
  528: <INITIAL>ROLE {
  529: #ifdef HAVE_SELINUX
  530: 			    LEXTRACE("ROLE ");
  531: 			    LEXRETURN(ROLE);
  532: #else
  533: 			    goto got_alias;
  534: #endif
  535: 			}
  536: 
  537: <INITIAL>TYPE {
  538: #ifdef HAVE_SELINUX
  539: 			    LEXTRACE("TYPE ");
  540: 			    LEXRETURN(TYPE);
  541: #else
  542: 			    goto got_alias;
  543: #endif
  544: 			}
  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: 			}
  562: 
  563: [[:upper:]][[:upper:][:digit:]_]* {
  564: 			got_alias:
  565: 			    if (!fill(sudoerstext, sudoersleng))
  566: 				yyterminate();
  567: 			    LEXTRACE("ALIAS ");
  568: 			    LEXRETURN(ALIAS);
  569: 			}
  570: 
  571: <GOTDEFS>({PATH}|sudoedit) {
  572: 			    /* XXX - no way to specify digest for command */
  573: 			    /* no command args allowed for Defaults!/path */
  574: 			    if (!fill_cmnd(sudoerstext, sudoersleng))
  575: 				yyterminate();
  576: 			    LEXTRACE("COMMAND ");
  577: 			    LEXRETURN(COMMAND);
  578: 			}
  579: 
  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: 
  608: sudoedit		{
  609: 			    BEGIN GOTCMND;
  610: 			    LEXTRACE("COMMAND ");
  611: 			    if (!fill_cmnd(sudoerstext, sudoersleng))
  612: 				yyterminate();
  613: 			}			/* sudo -e */
  614: 
  615: {PATH}			{
  616: 			    /* directories can't have args... */
  617: 			    if (sudoerstext[sudoersleng - 1] == '/') {
  618: 				LEXTRACE("COMMAND ");
  619: 				if (!fill_cmnd(sudoerstext, sudoersleng))
  620: 				    yyterminate();
  621: 				LEXRETURN(COMMAND);
  622: 			    } else {
  623: 				BEGIN GOTCMND;
  624: 				LEXTRACE("COMMAND ");
  625: 				if (!fill_cmnd(sudoerstext, sudoersleng))
  626: 				    yyterminate();
  627: 			    }
  628: 			}			/* a pathname */
  629: 
  630: <INITIAL,GOTDEFS>\" {
  631: 			    LEXTRACE("BEGINSTR ");
  632: 			    sudoerslval.string = NULL;
  633: 			    prev_state = YY_START;
  634: 			    BEGIN INSTR;
  635: 			}
  636: 
  637: <INITIAL,GOTDEFS>({ID}|{WORD}) {
  638: 			    /* a word */
  639: 			    if (!fill(sudoerstext, sudoersleng))
  640: 				yyterminate();
  641: 			    LEXTRACE("WORD(5) ");
  642: 			    LEXRETURN(WORD);
  643: 			}
  644: 
  645: \(			{
  646: 			    LEXTRACE("( ");
  647: 			    LEXRETURN('(');
  648: 			}
  649: 
  650: \)			{
  651: 			    LEXTRACE(") ");
  652: 			    LEXRETURN(')');
  653: 			}
  654: 
  655: ,			{
  656: 			    LEXTRACE(", ");
  657: 			    LEXRETURN(',');
  658: 			}			/* return ',' */
  659: 
  660: =			{
  661: 			    LEXTRACE("= ");
  662: 			    LEXRETURN('=');
  663: 			}			/* return '=' */
  664: 
  665: :			{
  666: 			    LEXTRACE(": ");
  667: 			    LEXRETURN(':');
  668: 			}			/* return ':' */
  669: 
  670: <*>!+			{
  671: 			    if (sudoersleng & 1) {
  672: 				LEXTRACE("!");
  673: 				LEXRETURN('!');	/* return '!' */
  674: 			    }
  675: 			}
  676: 
  677: <*>\n			{
  678: 			    if (YY_START == INSTR) {
  679: 				LEXTRACE("ERROR ");
  680: 				LEXRETURN(ERROR);	/* line break in string */
  681: 			    }
  682: 			    BEGIN INITIAL;
  683: 			    sudolineno++;
  684: 			    continued = false;
  685: 			    LEXTRACE("\n");
  686: 			    LEXRETURN(COMMENT);
  687: 			}			/* return newline */
  688: 
  689: <*>[[:blank:]]+		{			/* throw away space/tabs */
  690: 			    sawspace = true;	/* but remember for fill_args */
  691: 			}
  692: 
  693: <*>\\[[:blank:]]*\n	{
  694: 			    sawspace = true;	/* remember for fill_args */
  695: 			    sudolineno++;
  696: 			    continued = true;
  697: 			}			/* throw away EOL after \ */
  698: 
  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: 			    }
  709: 			    LEXTRACE("#\n");
  710: 			    LEXRETURN(COMMENT);
  711: 			}			/* comment, not uid/gid */
  712: 
  713: <*>.			{
  714: 			    LEXTRACE("ERROR ");
  715: 			    LEXRETURN(ERROR);
  716: 			}	/* parse error */
  717: 
  718: <*><<EOF>>		{
  719: 			    if (YY_START != INITIAL) {
  720: 			    	BEGIN INITIAL;
  721: 				LEXTRACE("ERROR ");
  722: 				LEXRETURN(ERROR);
  723: 			    }
  724: 			    if (!pop_include())
  725: 				yyterminate();
  726: 			}
  727: 
  728: %%
  729: struct path_list {
  730:     SLIST_ENTRY(path_list) entries;
  731:     char *path;
  732: };
  733: 
  734: SLIST_HEAD(path_list_head, path_list);
  735: 
  736: struct include_stack {
  737:     YY_BUFFER_STATE bs;
  738:     char *path;
  739:     struct path_list_head more; /* more files in case of includedir */
  740:     int lineno;
  741:     bool keepopen;
  742: };
  743: 
  744: /*
  745:  * Compare two struct path_list structs in reverse order.
  746:  */
  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: 
  753:     return strcmp((*p2)->path, (*p1)->path);
  754: }
  755: 
  756: static char *
  757: switch_dir(struct include_stack *stack, char *dirpath)
  758: {
  759:     DIR *dir;
  760:     unsigned int i, count = 0;
  761:     unsigned int max_paths = 32;
  762:     char *path = NULL;
  763:     struct dirent *dent;
  764:     struct stat sb;
  765:     struct path_list *pl, **paths = NULL;
  766:     debug_decl(switch_dir, SUDO_DEBUG_PARSER)
  767: 
  768:     if (!(dir = opendir(dirpath))) {
  769: 	if (errno != ENOENT) {
  770: 	    warning("%s", dirpath);
  771: 	    sudoerserror(NULL);
  772: 	}
  773: 	goto done;
  774:     }
  775:     paths = malloc(sizeof(*paths) * max_paths);
  776:     if (paths == NULL) {
  777: 	closedir(dir);
  778: 	goto bad;
  779:     }
  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;
  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;
  810: 	path = NULL;
  811:     }
  812:     closedir(dir);
  813: 
  814:     if (count == 0)
  815: 	goto done;
  816: 
  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. */
  821:     for (i = 0; i < count; i++) {
  822: 	SLIST_INSERT_HEAD(&stack->more, paths[i], entries);
  823:     }
  824: 
  825:     /* Pull out the first element for parsing, leave the rest for later. */
  826:     pl = SLIST_FIRST(&stack->more);
  827:     SLIST_REMOVE_HEAD(&stack->more, entries);
  828:     path = pl->path;
  829:     efree(pl);
  830: done:
  831:     efree(paths);
  832:     efree(dirpath);
  833:     debug_return_str(path);
  834: bad:
  835:     for (i = 0; i < count; i++) {
  836: 	efree(paths[i]->path);
  837: 	efree(paths[i]);
  838:     }
  839:     efree(paths);
  840:     efree(dirpath);
  841:     efree(path);
  842:     debug_return_str(NULL);
  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;
  850: static bool keepopen;
  851: 
  852: void
  853: init_lexer(void)
  854: {
  855:     struct path_list *pl;
  856:     debug_decl(init_lexer, SUDO_DEBUG_PARSER)
  857: 
  858:     while (idepth) {
  859: 	idepth--;
  860: 	while ((pl = SLIST_FIRST(&istack[idepth].more)) != NULL) {
  861: 	    SLIST_REMOVE_HEAD(&istack[idepth].more, entries);
  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);
  868: 	sudoers_delete_buffer(istack[idepth].bs);
  869:     }
  870:     efree(istack);
  871:     istack = NULL;
  872:     istacksize = idepth = 0;
  873:     sudolineno = 1;
  874:     keepopen = false;
  875:     sawspace = false;
  876:     continued = false;
  877:     prev_state = INITIAL;
  878: 
  879:     debug_return;
  880: }
  881: 
  882: static bool
  883: _push_include(char *path, bool isdir)
  884: {
  885:     struct path_list *pl;
  886:     FILE *fp;
  887:     debug_decl(_push_include, SUDO_DEBUG_PARSER)
  888: 
  889:     /* push current state onto stack */
  890:     if (idepth >= istacksize) {
  891: 	struct include_stack *new_istack;
  892: 
  893: 	if (idepth > MAX_SUDOERS_DEPTH) {
  894: 	    sudoerserror(N_("too many levels of includes"));
  895: 	    debug_return_bool(false);
  896: 	}
  897: 	istacksize += SUDOERS_STACK_INCREMENT;
  898: 	new_istack = (struct include_stack *) realloc(istack,
  899: 	    sizeof(*istack) * istacksize);
  900: 	if (new_istack == NULL) {
  901: 	    warning(NULL);
  902: 	    sudoerserror(NULL);
  903: 	    debug_return_bool(false);
  904: 	}
  905: 	istack = new_istack;
  906:     }
  907:     SLIST_INIT(&istack[idepth].more);
  908:     if (isdir) {
  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) {
  923: 		    warningx(U_("%s is owned by uid %u, should be %u"),   
  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) {
  930: 		    warningx(U_("%s is world writable"), path);
  931: 		}
  932: 		debug_return_bool(false);
  933: 	    case SUDO_PATH_GROUP_WRITABLE:
  934: 		if (sudoers_warnings) {
  935: 		    warningx(U_("%s is owned by gid %u, should be %u"),
  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: 	}
  944: 	if (!(path = switch_dir(&istack[idepth], path))) {
  945: 	    /* switch_dir() called sudoerserror() for us */
  946: 	    debug_return_bool(false);
  947: 	}
  948: 	while ((fp = open_sudoers(path, false, &keepopen)) == NULL) {
  949: 	    /* Unable to open path in includedir, go to next one, if any. */
  950: 	    efree(path);
  951: 	    if ((pl = SLIST_FIRST(&istack[idepth].more)) == NULL)
  952: 		debug_return_bool(false);
  953: 	    SLIST_REMOVE_HEAD(&istack[idepth].more, entries);
  954: 	    path = pl->path;
  955: 	    efree(pl);
  956: 	}
  957:     } else {
  958: 	if ((fp = open_sudoers(path, true, &keepopen)) == NULL) {
  959: 	    /* The error was already printed by open_sudoers() */
  960: 	    sudoerserror(NULL);
  961: 	    debug_return_bool(false);
  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;
  972:     sudoers_switch_to_buffer(sudoers_create_buffer(fp, YY_BUF_SIZE));
  973: 
  974:     debug_return_bool(true);
  975: }
  976: 
  977: static bool
  978: pop_include(void)
  979: {
  980:     struct path_list *pl;
  981:     FILE *fp;
  982:     debug_decl(pop_include, SUDO_DEBUG_PARSER)
  983: 
  984:     if (idepth == 0)
  985: 	debug_return_bool(false);
  986: 
  987:     if (!keepopen)
  988: 	fclose(YY_CURRENT_BUFFER->yy_input_file);
  989:     sudoers_delete_buffer(YY_CURRENT_BUFFER);
  990:     /* If we are in an include dir, move to the next file. */
  991:     while ((pl = SLIST_FIRST(&istack[idepth - 1].more)) != NULL) {
  992: 	SLIST_REMOVE_HEAD(&istack[idepth - 1].more, entries);
  993: 	fp = open_sudoers(pl->path, false, &keepopen);
  994: 	if (fp != NULL) {
  995: 	    efree(sudoers);
  996: 	    sudoers = pl->path;
  997: 	    sudolineno = 1;
  998: 	    sudoers_switch_to_buffer(sudoers_create_buffer(fp, YY_BUF_SIZE));
  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--;
 1009: 	sudoers_switch_to_buffer(istack[idepth].bs);
 1010: 	efree(sudoers);
 1011: 	sudoers = istack[idepth].path;
 1012: 	sudolineno = istack[idepth].lineno;
 1013: 	keepopen = istack[idepth].keepopen;
 1014:     }
 1015:     debug_return_bool(true);
 1016: }
 1017: 
 1018: static char *
 1019: parse_include(char *base)
 1020: {
 1021:     char *cp, *ep, *path, *pp;
 1022:     int dirlen = 0, len = 0, subst = 0;
 1023:     size_t shost_len = 0;
 1024:     debug_decl(parse_include, SUDO_DEBUG_PARSER)
 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: 
 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. */
 1050:     len += (int)(ep - cp);
 1051:     path = pp = malloc(len + dirlen + 1);
 1052:     if (path == NULL) {
 1053: 	warning(NULL);
 1054: 	sudoerserror(NULL);
 1055: 	debug_return_str(NULL);
 1056:     }
 1057:     if (dirlen) {
 1058: 	memcpy(path, sudoers, dirlen);
 1059: 	pp += dirlen;
 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 {
 1074: 	memcpy(pp, cp, len);
 1075: 	pp[len] = '\0';
 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: 
 1082:     debug_return_str(path);
 1083: }
 1084: 
 1085: #ifdef TRACELEXER
 1086: int
 1087: sudoers_trace_print(const char *msg)
 1088: {
 1089:     return fputs(msg, stderr);
 1090: }
 1091: #else
 1092: int
 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: }
 1113: #endif /* TRACELEXER */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>