X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=selinux.c;h=2c03e6c7ed4069912194bb2a5c52888fe22520ab;hb=e728e9e78f6751aee7d45d2105d58f2033e04e83;hp=7d498549558f77c31a625a196527ad3672986ff2;hpb=6850c41061e133f6ab21bc84330f62d6058fd27c;p=debian%2Fsudo diff --git a/selinux.c b/selinux.c index 7d49854..2c03e6c 100644 --- a/selinux.c +++ b/selinux.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2009-2010 Todd C. Miller * Copyright (c) 2008 Dan Walsh * * Borrowed heavily from newrole source code @@ -32,11 +33,10 @@ #include #include #include -#include #include #include #include -#ifdef WITH_AUDIT +#ifdef HAVE_LINUX_AUDIT #include #endif @@ -47,111 +47,158 @@ #include #include "sudo.h" -#include "pathnames.h" - -#ifndef lint -__unused static const char rcsid[] = "$Sudo: selinux.c,v 1.2.2.4 2008/02/22 20:33:10 millert Exp $"; -#endif /* lint */ +#include "linux_audit.h" + +static struct selinux_state { + security_context_t old_context; + security_context_t new_context; + security_context_t tty_context; + security_context_t new_tty_context; + const char *ttyn; + int ttyfd; + int enforcing; +} se_state; /* * This function attempts to revert the relabeling done to the tty. * fd - referencing the opened ttyn * ttyn - name of tty to restore - * tty_context - original context of the tty - * new_tty_context - context tty was relabeled to * * Returns zero on success, non-zero otherwise */ -static int -restore_tty_label(int fd, const char *ttyn, security_context_t tty_context, - security_context_t new_tty_context) +int +selinux_restore_tty(void) { - int rc = 0; + int retval = 0; security_context_t chk_tty_context = NULL; - if (!ttyn) - goto skip_relabel; - - if (!new_tty_context) - goto skip_relabel; + if (se_state.ttyfd == -1 || se_state.new_tty_context == NULL) + goto skip_relabel; /* Verify that the tty still has the context set by sudo. */ - if ((rc = fgetfilecon(fd, &chk_tty_context)) < 0) { - warn("unable to fgetfilecon %s", ttyn); - goto skip_relabel; + if ((retval = fgetfilecon(se_state.ttyfd, &chk_tty_context)) < 0) { + warning("unable to fgetfilecon %s", se_state.ttyn); + goto skip_relabel; } - if ((rc = strcmp(chk_tty_context, new_tty_context))) { - warnx("%s changed labels.", ttyn); - goto skip_relabel; + if ((retval = strcmp(chk_tty_context, se_state.new_tty_context))) { + warningx("%s changed labels.", se_state.ttyn); + goto skip_relabel; } - if ((rc = fsetfilecon(fd, tty_context)) < 0) - warn("unable to restore context for %s", ttyn); + if ((retval = fsetfilecon(se_state.ttyfd, se_state.tty_context)) < 0) + warning("unable to restore context for %s", se_state.ttyn); skip_relabel: - freecon(chk_tty_context); - return(rc); + if (se_state.ttyfd != -1) { + close(se_state.ttyfd); + se_state.ttyfd = -1; + } + if (chk_tty_context != NULL) { + freecon(chk_tty_context); + chk_tty_context = NULL; + } + return retval; } /* * This function attempts to relabel the tty. If this function fails, then - * the fd is closed, the contexts are free'd and -1 is returned. On success, - * a valid fd is returned and tty_context and new_tty_context are set. + * the contexts are free'd and -1 is returned. On success, 0 is returned + * and tty_context and new_tty_context are set. * * This function will not fail if it can not relabel the tty when selinux is * in permissive mode. */ static int -relabel_tty(const char *ttyn, security_context_t new_context, - security_context_t * tty_context, security_context_t * new_tty_context, - int enforcing) +relabel_tty(const char *ttyn, int ptyfd) { - int fd; security_context_t tty_con = NULL; security_context_t new_tty_con = NULL; + int fd; - if (!ttyn) - return(0); + se_state.ttyfd = ptyfd; - /* Re-open TTY descriptor */ - fd = open(ttyn, O_RDWR | O_NONBLOCK); - if (fd == -1) { - warn("unable to open %s", ttyn); - return(-1); + /* It is perfectly legal to have no tty. */ + if (ptyfd == -1 && ttyn == NULL) + return 0; + + /* If sudo is not allocating a pty for the command, open current tty. */ + if (ptyfd == -1) { + se_state.ttyfd = open(ttyn, O_RDWR|O_NONBLOCK); + if (se_state.ttyfd == -1) { + warning("unable to open %s, not relabeling tty", ttyn); + if (se_state.enforcing) + goto bad; + } + (void)fcntl(se_state.ttyfd, F_SETFL, + fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK); } - (void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK); - if (fgetfilecon(fd, &tty_con) < 0) { - warn("unable to get current context for %s, not relabeling tty", - ttyn); - if (enforcing) - goto error; + if (fgetfilecon(se_state.ttyfd, &tty_con) < 0) { + warning("unable to get current tty context, not relabeling tty"); + if (se_state.enforcing) + goto bad; } - if (tty_con && (security_compute_relabel(new_context, tty_con, + if (tty_con && (security_compute_relabel(se_state.new_context, tty_con, SECCLASS_CHR_FILE, &new_tty_con) < 0)) { - warn("unable to get new context for %s, not relabeling tty", ttyn); - if (enforcing) - goto error; + warning("unable to get new tty context, not relabeling tty"); + if (se_state.enforcing) + goto bad; } if (new_tty_con != NULL) { - if (fsetfilecon(fd, new_tty_con) < 0) { - warn("unable to set new context for %s", ttyn); - if (enforcing) - goto error; + if (fsetfilecon(se_state.ttyfd, new_tty_con) < 0) { + warning("unable to set new tty context"); + if (se_state.enforcing) + goto bad; } } - *tty_context = tty_con; - *new_tty_context = new_tty_con; - return(fd); - -error: + if (ptyfd != -1) { + /* Reopen pty that was relabeled, std{in,out,err} are reset later. */ + se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY, 0); + if (se_state.ttyfd == -1) { + warning("cannot open %s", ttyn); + if (se_state.enforcing) + goto bad; + } + if (dup2(se_state.ttyfd, ptyfd) == -1) { + warning("dup2"); + goto bad; + } + } else { + /* Re-open tty to get new label and reset std{in,out,err} */ + close(se_state.ttyfd); + se_state.ttyfd = open(ttyn, O_RDWR|O_NONBLOCK); + if (se_state.ttyfd == -1) { + warning("unable to open %s", ttyn); + goto bad; + } + (void)fcntl(se_state.ttyfd, F_SETFL, + fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK); + for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) { + if (isatty(fd) && dup2(se_state.ttyfd, fd) == -1) { + warning("dup2"); + goto bad; + } + } + } + /* Retain se_state.ttyfd so we can restore label when command finishes. */ + (void)fcntl(se_state.ttyfd, F_SETFD, FD_CLOEXEC); + + se_state.ttyn = ttyn; + se_state.tty_context = tty_con; + se_state.new_tty_context = new_tty_con; + return 0; + +bad: + if (se_state.ttyfd != -1 && se_state.ttyfd != ptyfd) { + close(se_state.ttyfd); + se_state.ttyfd = -1; + } freecon(tty_con); - close(fd); - return(-1); + return -1; } /* @@ -159,7 +206,7 @@ error: * specified role and type. */ security_context_t -get_exec_context(security_context_t old_context, char *role, char *type) +get_exec_context(security_context_t old_context, const char *role, const char *type) { security_context_t new_context = NULL; context_t context = NULL; @@ -167,13 +214,15 @@ get_exec_context(security_context_t old_context, char *role, char *type) /* We must have a role, the type is optional (we can use the default). */ if (!role) { - warnx("you must specify a role."); - return(NULL); + warningx("you must specify a role for type %s", type); + errno = EINVAL; + return NULL; } if (!type) { if (get_default_type(role, &typebuf)) { - warnx("unable to get default type"); - return(NULL); + warningx("unable to get default type for role %s", role); + errno = EINVAL; + return NULL; } type = typebuf; } @@ -189,12 +238,12 @@ get_exec_context(security_context_t old_context, char *role, char *type) * type we will be running the command as. */ if (context_role_set(context, role)) { - warnx("failed to set new role %s", role); - goto error; + warning("failed to set new role %s", role); + goto bad; } if (context_type_set(context, type)) { - warnx("failed to set new type %s", type); - goto error; + warning("failed to set new type %s", type); + goto bad; } /* @@ -202,141 +251,101 @@ get_exec_context(security_context_t old_context, char *role, char *type) */ new_context = estrdup(context_str(context)); if (security_check_context(new_context) < 0) { - warnx("%s is not a valid context", new_context); - goto error; + warningx("%s is not a valid context", new_context); + errno = EINVAL; + goto bad; } #ifdef DEBUG - warnx("Your new context is %s", new_context); + warningx("Your new context is %s", new_context); #endif context_free(context); - return(new_context); + return new_context; -error: +bad: free(typebuf); context_free(context); freecon(new_context); - return(NULL); + return NULL; } /* - * If the program is being run with a different security context we - * need to go through an intermediary process for the transition to - * be allowed by the policy. We use the "sesh" shell for this, which - * will simply execute the command pass to it on the command line. + * Set the exec and tty contexts in preparation for fork/exec. + * Must run as root, before the uid change. + * If ptyfd is not -1, it indicates we are running + * in a pty and do not need to reset std{in,out,err}. + * Returns 0 on success and -1 on failure. */ -void -selinux_exec(char *role, char *type, char **argv, char **envp, int login_shell) +int +selinux_setup(const char *role, const char *type, const char *ttyn, + int ptyfd) { - security_context_t old_context = NULL; - security_context_t new_context = NULL; - security_context_t tty_context = NULL; - security_context_t new_tty_context = NULL; - pid_t childPid; - int enforcing, ttyfd; - - /* Must have a tty. */ - if (user_ttypath == NULL || *user_ttypath == '\0') - err(EXIT_FAILURE, "unable to determine tty"); + int rval = -1; /* Store the caller's SID in old_context. */ - if (getprevcon(&old_context)) - err(EXIT_FAILURE, "failed to get old_context"); + if (getprevcon(&se_state.old_context)) { + warning("failed to get old_context"); + goto done; + } - enforcing = security_getenforce(); - if (enforcing < 0) - err(EXIT_FAILURE, "unable to determine enforcing mode."); + se_state.enforcing = security_getenforce(); + if (se_state.enforcing < 0) { + warning("unable to determine enforcing mode."); + goto done; + } - #ifdef DEBUG - warnx("your old context was %s", old_context); + warningx("your old context was %s", se_state.old_context); #endif - new_context = get_exec_context(old_context, role, type); - if (!new_context) - exit(EXIT_FAILURE); + se_state.new_context = get_exec_context(se_state.old_context, role, type); + if (!se_state.new_context) + goto done; - ttyfd = relabel_tty(user_ttypath, new_context, &tty_context, - &new_tty_context, enforcing); - if (ttyfd < 0) - err(EXIT_FAILURE, "unable to setup tty context for %s", new_context); + if (relabel_tty(ttyn, ptyfd) < 0) { + warning("unable to setup tty context for %s", se_state.new_context); + goto done; + } #ifdef DEBUG - warnx("your old tty context is %s", tty_context); - warnx("your new tty context is %s", new_tty_context); + if (se_state.ttyfd != -1) { + warningx("your old tty context is %s", se_state.tty_context); + warningx("your new tty context is %s", se_state.new_tty_context); + } #endif - childPid = fork(); - if (childPid < 0) { - /* fork failed, no child to worry about */ - warn("unable to fork"); - if (restore_tty_label(ttyfd, user_ttypath, tty_context, new_tty_context)) - warnx("unable to restore tty label"); - exit(EXIT_FAILURE); - } else if (childPid) { - pid_t pid; - int status; - - /* Parent, wait for child to finish. */ - do { - pid = waitpid(childPid, &status, 0); - } while (pid == -1 && errno == EINTR); - - if (pid == -1) - err(EXIT_FAILURE, "waitpid"); - - if (restore_tty_label(ttyfd, user_ttypath, tty_context, new_tty_context)) - errx(EXIT_FAILURE, "unable to restore tty label"); - - /* Preserve child exit status. */ - if (WIFEXITED(status)) - exit(WEXITSTATUS(status)); - exit(EXIT_FAILURE); - } - /* Child */ - /* Close the tty and reopen descriptors 0 through 2 */ - if (close(ttyfd) || close(STDIN_FILENO) || close(STDOUT_FILENO) || - close(STDERR_FILENO)) { - warn("could not close descriptors"); - goto error; - } - ttyfd = open(user_ttypath, O_RDONLY | O_NONBLOCK); - if (ttyfd != STDIN_FILENO) - goto error; - fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK); - ttyfd = open(user_ttypath, O_RDWR | O_NONBLOCK); - if (ttyfd != STDOUT_FILENO) - goto error; - fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK); - ttyfd = dup(STDOUT_FILENO); - if (ttyfd != STDERR_FILENO) - goto error; - - if (setexeccon(new_context)) { - warn("unable to set exec context to %s", new_context); - if (enforcing) - goto error; - } +#ifdef HAVE_LINUX_AUDIT + linux_audit_role_change(se_state.old_context, se_state.new_context, + se_state.ttyn); +#endif + + rval = 0; - if (setkeycreatecon(new_context)) { - warn("unable to set key creation context to %s", new_context); - if (enforcing) - goto error; +done: + return rval; +} + +void +selinux_execve(const char *path, char *argv[], char *envp[]) +{ + if (setexeccon(se_state.new_context)) { + warning("unable to set exec context to %s", se_state.new_context); + if (se_state.enforcing) + return; } -#ifdef WITH_AUDIT - if (send_audit_message(1, old_context, new_context, user_ttypath)) - goto error; -#endif +#ifdef HAVE_SETKEYCREATECON + if (setkeycreatecon(se_state.new_context)) { + warning("unable to set key creation context to %s", se_state.new_context); + if (se_state.enforcing) + return; + } +#endif /* HAVE_SETKEYCREATECON */ /* We use the "spare" slot in argv to store sesh. */ --argv; - argv[0] = login_shell ? "-sesh" : "sesh"; - argv[1] = safe_cmnd; + argv[0] = *argv[1] == '-' ? "-sesh" : "sesh"; + argv[1] = (char *)path; execve(_PATH_SUDO_SESH, argv, envp); - warn("%s", safe_cmnd); - -error: - _exit(EXIT_FAILURE); }