File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / src / selinux.c
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 16:12:55 2014 UTC (10 years 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) 2009-2013 Todd C. Miller <Todd.Miller@courtesan.com>
    3:  * Copyright (c) 2008 Dan Walsh <dwalsh@redhat.com>
    4:  *
    5:  * Borrowed heavily from newrole source code
    6:  * Authors:
    7:  *	Anthony Colatrella
    8:  *	Tim Fraser
    9:  *	Steve Grubb <sgrubb@redhat.com>
   10:  *	Darrel Goeddel <DGoeddel@trustedcs.com>
   11:  *	Michael Thompson <mcthomps@us.ibm.com>
   12:  *	Dan Walsh <dwalsh@redhat.com>
   13:  *
   14:  * Permission to use, copy, modify, and distribute this software for any
   15:  * purpose with or without fee is hereby granted, provided that the above
   16:  * copyright notice and this permission notice appear in all copies.
   17:  *
   18:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   19:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   20:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   21:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   22:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   23:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   24:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   25:  */
   26: 
   27: #include <config.h>
   28: 
   29: #include <sys/types.h>
   30: #include <sys/wait.h>
   31: #include <stdio.h>
   32: #include <stdlib.h>
   33: #include <stddef.h>
   34: #include <string.h>
   35: #include <unistd.h>
   36: #include <errno.h>
   37: #include <fcntl.h>
   38: #include <signal.h>
   39: 
   40: #include <selinux/flask.h>             /* for SECCLASS_CHR_FILE */
   41: #include <selinux/selinux.h>           /* for is_selinux_enabled() */
   42: #include <selinux/context.h>           /* for context-mangling functions */
   43: #include <selinux/get_default_type.h>
   44: #include <selinux/get_context_list.h>
   45: 
   46: #ifdef HAVE_LINUX_AUDIT
   47: # include <libaudit.h>
   48: #endif
   49: 
   50: #include "sudo.h"
   51: #include "sudo_exec.h"
   52: 
   53: static struct selinux_state {
   54:     security_context_t old_context;
   55:     security_context_t new_context;
   56:     security_context_t tty_context;
   57:     security_context_t new_tty_context;
   58:     const char *ttyn;
   59:     int ttyfd;
   60:     int enforcing;
   61: } se_state;
   62: 
   63: #ifdef HAVE_LINUX_AUDIT
   64: static int
   65: audit_role_change(const security_context_t old_context,
   66:     const security_context_t new_context, const char *ttyn)
   67: {
   68:     int au_fd, rc = -1;
   69:     char *message;
   70:     debug_decl(audit_role_change, SUDO_DEBUG_SELINUX)
   71: 
   72:     au_fd = audit_open();
   73:     if (au_fd == -1) {
   74:         /* Kernel may not have audit support. */
   75:         if (errno != EINVAL && errno != EPROTONOSUPPORT && errno != EAFNOSUPPORT
   76: )
   77:             fatal(U_("unable to open audit system"));
   78:     } else {
   79: 	/* audit role change using the same format as newrole(1) */
   80: 	easprintf(&message, "newrole: old-context=%s new-context=%s",
   81: 	    old_context, new_context);
   82: 	rc = audit_log_user_message(au_fd, AUDIT_USER_ROLE_CHANGE,
   83: 	    message, NULL, NULL, ttyn, 1);
   84: 	if (rc <= 0)
   85: 	    warning(U_("unable to send audit message"));
   86: 	efree(message);
   87: 	close(au_fd);
   88:     }
   89: 
   90:     debug_return_int(rc);
   91: }
   92: #endif
   93: 
   94: /*
   95:  * This function attempts to revert the relabeling done to the tty.
   96:  * fd		   - referencing the opened ttyn
   97:  * ttyn		   - name of tty to restore
   98:  *
   99:  * Returns zero on success, non-zero otherwise
  100:  */
  101: int
  102: selinux_restore_tty(void)
  103: {
  104:     int retval = 0;
  105:     security_context_t chk_tty_context = NULL;
  106:     debug_decl(selinux_restore_tty, SUDO_DEBUG_SELINUX)
  107: 
  108:     if (se_state.ttyfd == -1 || se_state.new_tty_context == NULL)
  109: 	goto skip_relabel;
  110: 
  111:     /* Verify that the tty still has the context set by sudo. */
  112:     if ((retval = fgetfilecon(se_state.ttyfd, &chk_tty_context)) < 0) {
  113: 	warning(U_("unable to fgetfilecon %s"), se_state.ttyn);
  114: 	goto skip_relabel;
  115:     }
  116: 
  117:     if ((retval = strcmp(chk_tty_context, se_state.new_tty_context))) {
  118: 	warningx(U_("%s changed labels"), se_state.ttyn);
  119: 	goto skip_relabel;
  120:     }
  121: 
  122:     if ((retval = fsetfilecon(se_state.ttyfd, se_state.tty_context)) < 0)
  123: 	warning(U_("unable to restore context for %s"), se_state.ttyn);
  124: 
  125: skip_relabel:
  126:     if (se_state.ttyfd != -1) {
  127: 	close(se_state.ttyfd);
  128: 	se_state.ttyfd = -1;
  129:     }
  130:     if (chk_tty_context != NULL) {
  131: 	freecon(chk_tty_context);
  132: 	chk_tty_context = NULL;
  133:     }
  134:     debug_return_int(retval);
  135: }
  136: 
  137: /*
  138:  * This function attempts to relabel the tty. If this function fails, then
  139:  * the contexts are free'd and -1 is returned. On success, 0 is returned
  140:  * and tty_context and new_tty_context are set.
  141:  *
  142:  * This function will not fail if it can not relabel the tty when selinux is
  143:  * in permissive mode.
  144:  */
  145: static int
  146: relabel_tty(const char *ttyn, int ptyfd)
  147: {
  148:     security_context_t tty_con = NULL;
  149:     security_context_t new_tty_con = NULL;
  150:     int fd;
  151:     debug_decl(relabel_tty, SUDO_DEBUG_SELINUX)
  152: 
  153:     se_state.ttyfd = ptyfd;
  154: 
  155:     /* It is perfectly legal to have no tty. */
  156:     if (ptyfd == -1 && ttyn == NULL)
  157: 	debug_return_int(0);
  158: 
  159:     /* If sudo is not allocating a pty for the command, open current tty. */
  160:     if (ptyfd == -1) {
  161: 	se_state.ttyfd = open(ttyn, O_RDWR|O_NONBLOCK);
  162: 	if (se_state.ttyfd == -1) {
  163: 	    warning(U_("unable to open %s, not relabeling tty"), ttyn);
  164: 	    if (se_state.enforcing)
  165: 		goto bad;
  166: 	}
  167: 	(void)fcntl(se_state.ttyfd, F_SETFL,
  168: 	    fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
  169:     }
  170: 
  171:     if (fgetfilecon(se_state.ttyfd, &tty_con) < 0) {
  172: 	warning(U_("unable to get current tty context, not relabeling tty"));
  173: 	if (se_state.enforcing)
  174: 	    goto bad;
  175:     }
  176: 
  177:     if (tty_con && (security_compute_relabel(se_state.new_context, tty_con,
  178: 	SECCLASS_CHR_FILE, &new_tty_con) < 0)) {
  179: 	warning(U_("unable to get new tty context, not relabeling tty"));
  180: 	if (se_state.enforcing)
  181: 	    goto bad;
  182:     }
  183: 
  184:     if (new_tty_con != NULL) {
  185: 	if (fsetfilecon(se_state.ttyfd, new_tty_con) < 0) {
  186: 	    warning(U_("unable to set new tty context"));
  187: 	    if (se_state.enforcing)
  188: 		goto bad;
  189: 	}
  190:     }
  191: 
  192:     if (ptyfd != -1) {
  193: 	/* Reopen pty that was relabeled, std{in,out,err} are reset later. */
  194: 	se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY, 0);
  195: 	if (se_state.ttyfd == -1) {
  196: 	    warning(U_("unable to open %s"), ttyn);
  197: 	    if (se_state.enforcing)
  198: 		goto bad;
  199: 	}
  200: 	if (dup2(se_state.ttyfd, ptyfd) == -1) {
  201: 	    warning("dup2");
  202: 	    goto bad;
  203: 	}
  204:     } else {
  205: 	/* Re-open tty to get new label and reset std{in,out,err} */
  206: 	close(se_state.ttyfd);
  207: 	se_state.ttyfd = open(ttyn, O_RDWR|O_NONBLOCK);
  208: 	if (se_state.ttyfd == -1) {
  209: 	    warning(U_("unable to open %s"), ttyn);
  210: 	    goto bad;
  211: 	}
  212: 	(void)fcntl(se_state.ttyfd, F_SETFL,
  213: 	    fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
  214: 	for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) {
  215: 	    if (isatty(fd) && dup2(se_state.ttyfd, fd) == -1) {
  216: 		warning("dup2");
  217: 		goto bad;
  218: 	    }
  219: 	}
  220:     }
  221:     /* Retain se_state.ttyfd so we can restore label when command finishes. */
  222:     (void)fcntl(se_state.ttyfd, F_SETFD, FD_CLOEXEC);
  223: 
  224:     se_state.ttyn = ttyn;
  225:     se_state.tty_context = tty_con;
  226:     se_state.new_tty_context = new_tty_con;
  227:     debug_return_int(0);
  228: 
  229: bad:
  230:     if (se_state.ttyfd != -1 && se_state.ttyfd != ptyfd) {
  231: 	close(se_state.ttyfd);
  232: 	se_state.ttyfd = -1;
  233:     }
  234:     freecon(tty_con);
  235:     debug_return_int(-1);
  236: }
  237: 
  238: /*
  239:  * Returns a new security context based on the old context and the
  240:  * specified role and type.
  241:  */
  242: security_context_t
  243: get_exec_context(security_context_t old_context, const char *role, const char *type)
  244: {
  245:     security_context_t new_context = NULL;
  246:     context_t context = NULL;
  247:     char *typebuf = NULL;
  248:     debug_decl(get_exec_context, SUDO_DEBUG_SELINUX)
  249:     
  250:     /* We must have a role, the type is optional (we can use the default). */
  251:     if (!role) {
  252: 	warningx(U_("you must specify a role for type %s"), type);
  253: 	errno = EINVAL;
  254: 	goto bad;
  255:     }
  256:     if (!type) {
  257: 	if (get_default_type(role, &typebuf)) {
  258: 	    warningx(U_("unable to get default type for role %s"), role);
  259: 	    errno = EINVAL;
  260: 	    goto bad;
  261: 	}
  262: 	type = typebuf;
  263:     }
  264:     
  265:     /* 
  266:      * Expand old_context into a context_t so that we extract and modify 
  267:      * its components easily. 
  268:      */
  269:     context = context_new(old_context);
  270:     
  271:     /*
  272:      * Replace the role and type in "context" with the role and
  273:      * type we will be running the command as.
  274:      */
  275:     if (context_role_set(context, role)) {
  276: 	warning(U_("failed to set new role %s"), role);
  277: 	goto bad;
  278:     }
  279:     if (context_type_set(context, type)) {
  280: 	warning(U_("failed to set new type %s"), type);
  281: 	goto bad;
  282:     }
  283:       
  284:     /*
  285:      * Convert "context" back into a string and verify it.
  286:      */
  287:     new_context = estrdup(context_str(context));
  288:     if (security_check_context(new_context) < 0) {
  289: 	warningx(U_("%s is not a valid context"), new_context);
  290: 	errno = EINVAL;
  291: 	goto bad;
  292:     }
  293: 
  294: #ifdef DEBUG
  295:     warningx("Your new context is %s", new_context);
  296: #endif
  297: 
  298:     context_free(context);
  299:     debug_return_ptr(new_context);
  300: 
  301: bad:
  302:     efree(typebuf);
  303:     context_free(context);
  304:     freecon(new_context);
  305:     debug_return_ptr(NULL);
  306: }
  307: 
  308: /* 
  309:  * Set the exec and tty contexts in preparation for fork/exec.
  310:  * Must run as root, before the uid change.
  311:  * If ptyfd is not -1, it indicates we are running
  312:  * in a pty and do not need to reset std{in,out,err}.
  313:  * Returns 0 on success and -1 on failure.
  314:  */
  315: int
  316: selinux_setup(const char *role, const char *type, const char *ttyn,
  317:     int ptyfd)
  318: {
  319:     int rval = -1;
  320:     debug_decl(selinux_setup, SUDO_DEBUG_SELINUX)
  321: 
  322:     /* Store the caller's SID in old_context. */
  323:     if (getprevcon(&se_state.old_context)) {
  324: 	warning(U_("failed to get old_context"));
  325: 	goto done;
  326:     }
  327: 
  328:     se_state.enforcing = security_getenforce();
  329:     if (se_state.enforcing < 0) {
  330: 	warning(U_("unable to determine enforcing mode."));
  331: 	goto done;
  332:     }
  333: 
  334: #ifdef DEBUG
  335:     warningx("your old context was %s", se_state.old_context);
  336: #endif
  337:     se_state.new_context = get_exec_context(se_state.old_context, role, type);
  338:     if (!se_state.new_context)
  339: 	goto done;
  340:     
  341:     if (relabel_tty(ttyn, ptyfd) < 0) {
  342: 	warning(U_("unable to set tty context to %s"), se_state.new_context);
  343: 	goto done;
  344:     }
  345: 
  346: #ifdef DEBUG
  347:     if (se_state.ttyfd != -1) {
  348: 	warningx("your old tty context is %s", se_state.tty_context);
  349: 	warningx("your new tty context is %s", se_state.new_tty_context);
  350:     }
  351: #endif
  352: 
  353: #ifdef HAVE_LINUX_AUDIT
  354:     audit_role_change(se_state.old_context, se_state.new_context,
  355: 	se_state.ttyn);
  356: #endif
  357: 
  358:     rval = 0;
  359: 
  360: done:
  361:     debug_return_int(rval);
  362: }
  363: 
  364: void
  365: selinux_execve(const char *path, char *const argv[], char *const envp[],
  366:     int noexec)
  367: {
  368:     char **nargv;
  369:     const char *sesh;
  370:     int argc, serrno;
  371:     debug_decl(selinux_execve, SUDO_DEBUG_SELINUX)
  372: 
  373:     sesh = sudo_conf_sesh_path();
  374:     if (sesh == NULL) {
  375: 	warningx("internal error: sesh path not set");
  376: 	errno = EINVAL;
  377: 	debug_return;
  378:     }
  379: 
  380:     if (setexeccon(se_state.new_context)) {
  381: 	warning(U_("unable to set exec context to %s"), se_state.new_context);
  382: 	if (se_state.enforcing)
  383: 	    debug_return;
  384:     }
  385: 
  386: #ifdef HAVE_SETKEYCREATECON
  387:     if (setkeycreatecon(se_state.new_context)) {
  388: 	warning(U_("unable to set key creation context to %s"), se_state.new_context);
  389: 	if (se_state.enforcing)
  390: 	    debug_return;
  391:     }
  392: #endif /* HAVE_SETKEYCREATECON */
  393: 
  394:     /*
  395:      * Build new argv with sesh as argv[0].
  396:      * If argv[0] ends in -noexec, sesh will disable execute
  397:      * for the command it runs.
  398:      */
  399:     for (argc = 0; argv[argc] != NULL; argc++)
  400: 	continue;
  401:     nargv = emalloc2(argc + 2, sizeof(char *));
  402:     if (noexec)
  403: 	nargv[0] = *argv[0] == '-' ? "-sesh-noexec" : "sesh-noexec";
  404:     else
  405: 	nargv[0] = *argv[0] == '-' ? "-sesh" : "sesh";
  406:     nargv[1] = (char *)path;
  407:     memcpy(&nargv[2], &argv[1], argc * sizeof(char *)); /* copies NULL */
  408: 
  409:     /* sesh will handle noexec for us. */
  410:     sudo_execve(sesh, nargv, envp, false);
  411:     serrno = errno;
  412:     free(nargv);
  413:     errno = serrno;
  414:     debug_return;
  415: }

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