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

    1: /*
    2:  * Copyright (c) 2007-2013 Todd C. Miller <Todd.Miller@courtesan.com>
    3:  *
    4:  * Permission to use, copy, modify, and distribute this software for any
    5:  * purpose with or without fee is hereby granted, provided that the above
    6:  * copyright notice and this permission notice appear in all copies.
    7:  *
    8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   15:  */
   16: 
   17: #include <config.h>
   18: 
   19: #include <sys/types.h>
   20: #include <sys/stat.h>
   21: 
   22: #include <stdio.h>
   23: #ifdef STDC_HEADERS
   24: # include <stdlib.h>
   25: # include <stddef.h>
   26: #else
   27: # ifdef HAVE_STDLIB_H
   28: #  include <stdlib.h>
   29: # endif
   30: #endif /* STDC_HEADERS */
   31: #ifdef HAVE_STRING_H
   32: # include <string.h>
   33: #endif /* HAVE_STRING_H */
   34: #ifdef HAVE_STRINGS_H
   35: # include <strings.h>
   36: #endif /* HAVE_STRINGS_H */
   37: #ifdef HAVE_UNISTD_H
   38: # include <unistd.h>
   39: #endif /* HAVE_UNISTD_H */
   40: #include <pwd.h>
   41: #include <grp.h>
   42: #include <ctype.h>
   43: 
   44: #include "sudoers.h"
   45: #include "lbuf.h"
   46: 
   47: extern struct sudo_nss sudo_nss_file;
   48: #ifdef HAVE_LDAP
   49: extern struct sudo_nss sudo_nss_ldap;
   50: #endif
   51: #ifdef HAVE_SSSD
   52: extern struct sudo_nss sudo_nss_sss;
   53: #endif
   54: 
   55: #if (defined(HAVE_LDAP) || defined(HAVE_SSSD)) && defined(_PATH_NSSWITCH_CONF)
   56: /*
   57:  * Read in /etc/nsswitch.conf
   58:  * Returns a tail queue of matches.
   59:  */
   60: struct sudo_nss_list *
   61: sudo_read_nss(void)
   62: {
   63:     FILE *fp;
   64:     char *cp, *line = NULL;
   65:     size_t linesize = 0;
   66: #ifdef HAVE_SSSD
   67:     bool saw_sss = false;
   68: #endif
   69:     bool saw_files = false;
   70:     bool saw_ldap = false;
   71:     bool got_match = false;
   72:     static struct sudo_nss_list snl = TAILQ_HEAD_INITIALIZER(snl);
   73:     debug_decl(sudo_read_nss, SUDO_DEBUG_NSS)
   74: 
   75:     if ((fp = fopen(_PATH_NSSWITCH_CONF, "r")) == NULL)
   76: 	goto nomatch;
   77: 
   78:     while (sudo_parseln(&line, &linesize, NULL, fp) != -1) {
   79: 	/* Skip blank or comment lines */
   80: 	if (*line == '\0')
   81: 	    continue;
   82: 
   83: 	/* Look for a line starting with "sudoers:" */
   84: 	if (strncasecmp(line, "sudoers:", 8) != 0)
   85: 	    continue;
   86: 
   87: 	/* Parse line */
   88: 	for ((cp = strtok(line + 8, " \t")); cp != NULL; (cp = strtok(NULL, " \t"))) {
   89: 	    if (strcasecmp(cp, "files") == 0 && !saw_files) {
   90: 		TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries);
   91: 		got_match = true;
   92: #ifdef HAVE_LDAP
   93: 	    } else if (strcasecmp(cp, "ldap") == 0 && !saw_ldap) {
   94: 		TAILQ_INSERT_TAIL(&snl, &sudo_nss_ldap, entries);
   95: 		got_match = true;
   96: #endif
   97: #ifdef HAVE_SSSD
   98: 	    } else if (strcasecmp(cp, "sss") == 0 && !saw_sss) {
   99: 		TAILQ_INSERT_TAIL(&snl, &sudo_nss_sss, entries);
  100: 		got_match = true;
  101: #endif
  102: 	    } else if (strcasecmp(cp, "[NOTFOUND=return]") == 0 && got_match) {
  103: 		/* NOTFOUND affects the most recent entry */
  104: 		TAILQ_LAST(&snl, sudo_nss_list)->ret_if_notfound = true;
  105: 		got_match = false;
  106: 	    } else if (strcasecmp(cp, "[SUCCESS=return]") == 0 && got_match) {
  107: 		/* SUCCESS affects the most recent entry */
  108: 		TAILQ_LAST(&snl, sudo_nss_list)->ret_if_found = true;
  109: 		got_match = false;
  110: 	    } else
  111: 		got_match = false;
  112: 	}
  113: 	/* Only parse the first "sudoers:" line */
  114: 	break;
  115:     }
  116:     free(line);
  117:     fclose(fp);
  118: 
  119: nomatch:
  120:     /* Default to files only if no matches */
  121:     if (TAILQ_EMPTY(&snl))
  122: 	TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries);
  123: 
  124:     debug_return_ptr(&snl);
  125: }
  126: 
  127: #else /* (HAVE_LDAP || HAVE_SSSD) && _PATH_NSSWITCH_CONF */
  128: 
  129: # if (defined(HAVE_LDAP) || defined(HAVE_SSSD)) && defined(_PATH_NETSVC_CONF)
  130: 
  131: /*
  132:  * Read in /etc/netsvc.conf (like nsswitch.conf on AIX)
  133:  * Returns a tail queue of matches.
  134:  */
  135: struct sudo_nss_list *
  136: sudo_read_nss(void)
  137: {
  138:     FILE *fp;
  139:     char *cp, *ep, *line = NULL;
  140:     size_t linesize = 0;
  141: #ifdef HAVE_SSSD
  142:     bool saw_sss = false;
  143: #endif
  144:     bool saw_files = false;
  145:     bool saw_ldap = false;
  146:     bool got_match = false;
  147:     static struct sudo_nss_list snl = TAILQ_HEAD_INITIALIZER(snl);
  148:     debug_decl(sudo_read_nss, SUDO_DEBUG_NSS)
  149: 
  150:     if ((fp = fopen(_PATH_NETSVC_CONF, "r")) == NULL)
  151: 	goto nomatch;
  152: 
  153:     while (sudo_parseln(&line, &linesize, NULL, fp) != -1) {
  154: 	/* Skip blank or comment lines */
  155: 	if (*(cp = line) == '\0')
  156: 	    continue;
  157: 
  158: 	/* Look for a line starting with "sudoers = " */
  159: 	if (strncasecmp(cp, "sudoers", 7) != 0)
  160: 	    continue;
  161: 	cp += 7;
  162: 	while (isspace((unsigned char)*cp))
  163: 	    cp++;
  164: 	if (*cp++ != '=')
  165: 	    continue;
  166: 
  167: 	/* Parse line */
  168: 	for ((cp = strtok(cp, ",")); cp != NULL; (cp = strtok(NULL, ","))) {
  169: 	    /* Trim leading whitespace. */
  170: 	    while (isspace((unsigned char)*cp))
  171: 		cp++;
  172: 
  173: 	    if (!saw_files && strncasecmp(cp, "files", 5) == 0 &&
  174: 		(isspace((unsigned char)cp[5]) || cp[5] == '\0')) {
  175: 		TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries);
  176: 		got_match = true;
  177: 		ep = &cp[5];
  178: #ifdef HAVE_LDAP
  179: 	    } else if (!saw_ldap && strncasecmp(cp, "ldap", 4) == 0 &&
  180: 		(isspace((unsigned char)cp[4]) || cp[4] == '\0')) {
  181: 		TAILQ_INSERT_TAIL(&snl, &sudo_nss_ldap, entries);
  182: 		got_match = true;
  183: 		ep = &cp[4];
  184: #endif
  185: #ifdef HAVE_SSSD
  186: 	    } else if (!saw_sss && strncasecmp(cp, "sss", 3) == 0 &&
  187: 		(isspace((unsigned char)cp[3]) || cp[3] == '\0')) {
  188: 		TAILQ_INSERT_TAIL(&snl, &sudo_nss_sss, entries);
  189: 		got_match = true;
  190: 		ep = &cp[3];
  191: #endif
  192: 	    } else {
  193: 		got_match = false;
  194: 	    }
  195: 
  196: 	    /* check for = auth qualifier */
  197: 	    if (got_match && *ep) {
  198: 		cp = ep;
  199: 		while (isspace((unsigned char)*cp) || *cp == '=')
  200: 		    cp++;
  201: 		if (strncasecmp(cp, "auth", 4) == 0 &&
  202: 		    (isspace((unsigned char)cp[4]) || cp[4] == '\0')) {
  203: 		    TAILQ_LAST(&snl, sudo_nss_list)->ret_if_found = true;
  204: 		}
  205: 	    }
  206: 	}
  207: 	/* Only parse the first "sudoers" line */
  208: 	break;
  209:     }
  210:     fclose(fp);
  211: 
  212: nomatch:
  213:     /* Default to files only if no matches */
  214:     if (TAILQ_EMPTY(&snl))
  215: 	TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries);
  216: 
  217:     debug_return_ptr(&snl);
  218: }
  219: 
  220: # else /* !_PATH_NETSVC_CONF && !_PATH_NSSWITCH_CONF */
  221: 
  222: /*
  223:  * Non-nsswitch.conf version with hard-coded order.
  224:  */
  225: struct sudo_nss_list *
  226: sudo_read_nss(void)
  227: {
  228:     static struct sudo_nss_list snl = TAILQ_HEAD_INITIALIZER(snl);
  229:     debug_decl(sudo_read_nss, SUDO_DEBUG_NSS)
  230: 
  231: #  ifdef HAVE_SSSD
  232:     TAILQ_INSERT_TAIL(&snl, &sudo_nss_sss, entries);
  233: #  endif
  234: #  ifdef HAVE_LDAP
  235:     TAILQ_INSERT_TAIL(&snl, &sudo_nss_ldap, entries);
  236: #  endif
  237:     TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries);
  238: 
  239:     debug_return_ptr(&snl);
  240: }
  241: 
  242: # endif /* !HAVE_LDAP || !_PATH_NETSVC_CONF */
  243: 
  244: #endif /* HAVE_LDAP && _PATH_NSSWITCH_CONF */
  245: 
  246: static int
  247: output(const char *buf)
  248: {
  249:     struct sudo_conv_message msg;
  250:     struct sudo_conv_reply repl;
  251:     debug_decl(output, SUDO_DEBUG_NSS)
  252: 
  253:     /* Call conversation function */
  254:     memset(&msg, 0, sizeof(msg));
  255:     msg.msg_type = SUDO_CONV_INFO_MSG;
  256:     msg.msg = buf;
  257:     memset(&repl, 0, sizeof(repl));
  258:     if (sudo_conv(1, &msg, &repl) == -1)
  259: 	debug_return_int(0);
  260:     debug_return_int(strlen(buf));
  261: }
  262: 
  263: /*
  264:  * Print out privileges for the specified user.
  265:  * We only get here if the user is allowed to run something.
  266:  */
  267: void
  268: display_privs(struct sudo_nss_list *snl, struct passwd *pw)
  269: {
  270:     struct sudo_nss *nss;
  271:     struct lbuf defs, privs;
  272:     struct stat sb;
  273:     int cols, count, olen;
  274:     debug_decl(display_privs, SUDO_DEBUG_NSS)
  275: 
  276:     cols = sudo_user.cols;
  277:     if (fstat(STDOUT_FILENO, &sb) == 0 && S_ISFIFO(sb.st_mode))
  278: 	cols = 0;
  279:     lbuf_init(&defs, output, 4, NULL, cols);
  280:     lbuf_init(&privs, output, 8, NULL, cols);
  281: 
  282:     /* Display defaults from all sources. */
  283:     lbuf_append(&defs, _("Matching Defaults entries for %s on %s:\n"),
  284: 	pw->pw_name, user_srunhost);
  285:     count = 0;
  286:     TAILQ_FOREACH(nss, snl, entries) {
  287: 	count += nss->display_defaults(nss, pw, &defs);
  288:     }
  289:     if (count)
  290: 	lbuf_append(&defs, "\n\n");
  291:     else
  292: 	defs.len = 0;
  293: 
  294:     /* Display Runas and Cmnd-specific defaults from all sources. */
  295:     olen = defs.len;
  296:     lbuf_append(&defs, _("Runas and Command-specific defaults for %s:\n"),
  297: 	pw->pw_name);
  298:     count = 0;
  299:     TAILQ_FOREACH(nss, snl, entries) {
  300: 	count += nss->display_bound_defaults(nss, pw, &defs);
  301:     }
  302:     if (count)
  303: 	lbuf_append(&defs, "\n\n");
  304:     else
  305: 	defs.len = olen;
  306: 
  307:     /* Display privileges from all sources. */
  308:     lbuf_append(&privs,
  309: 	_("User %s may run the following commands on %s:\n"),
  310: 	pw->pw_name, user_srunhost);
  311:     count = 0;
  312:     TAILQ_FOREACH(nss, snl, entries) {
  313: 	count += nss->display_privs(nss, pw, &privs);
  314:     }
  315:     if (count == 0) {
  316: 	defs.len = 0;
  317: 	privs.len = 0;
  318: 	lbuf_append(&privs, _("User %s is not allowed to run sudo on %s.\n"),
  319: 	    pw->pw_name, user_shost);
  320:     }
  321:     lbuf_print(&defs);
  322:     lbuf_print(&privs);
  323: 
  324:     lbuf_destroy(&defs);
  325:     lbuf_destroy(&privs);
  326: 
  327:     debug_return;
  328: }
  329: 
  330: /*
  331:  * Check user_cmnd against sudoers and print the matching entry if the
  332:  * command is allowed.
  333:  * Returns true if the command is allowed, else false.
  334:  */
  335: bool
  336: display_cmnd(struct sudo_nss_list *snl, struct passwd *pw)
  337: {
  338:     struct sudo_nss *nss;
  339:     debug_decl(display_cmnd, SUDO_DEBUG_NSS)
  340: 
  341:     TAILQ_FOREACH(nss, snl, entries) {
  342: 	if (nss->display_cmnd(nss, pw) == 0)
  343: 	    debug_return_bool(true);
  344:     }
  345:     debug_return_bool(false);
  346: }

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