File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / plugins / sudoers / toke.l
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Oct 9 09:29:52 2012 UTC (11 years, 10 months ago) by misho
Branches: sudo, MAIN
CVS tags: HEAD
sudo

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

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