First cut at refactoring some of the selinux code so it can be used

in conjunction with sudo's transcript support.
This commit is contained in:
Todd C. Miller
2009-09-27 13:03:56 +00:00
parent c6a8c71d2b
commit 7d19478501
4 changed files with 147 additions and 112 deletions

View File

@@ -82,7 +82,13 @@ static sig_atomic_t alive = 1;
static pid_t child; static pid_t child;
static int child_status; static int child_status;
static void script_child __P((const char *path, char *const argv[])); #if defined(HAVE_OPENPTY) || defined(HAVE_GRANTPT)
static char slavename[PATH_MAX];
#else
static char slavename[] = "/dev/ptyXX";
#endif
static void script_child __P((char *path, char *argv[], int));
static void sync_winsize __P((int src, int dst)); static void sync_winsize __P((int src, int dst));
static void sigchild __P((int signo)); static void sigchild __P((int signo));
static void sigwinch __P((int signo)); static void sigwinch __P((int signo));
@@ -312,8 +318,8 @@ log_output(output, n, then, now, ofile, tfile)
int int
script_execv(path, argv) script_execv(path, argv)
const char *path; char *path;
char *const argv[]; char *argv[];
{ {
int n, nready; int n, nready;
fd_set *fdsr, *fdsw; fd_set *fdsr, *fdsw;
@@ -321,6 +327,7 @@ script_execv(path, argv)
struct timeval now, then; struct timeval now, then;
sigaction_t sa; sigaction_t sa;
FILE *idfile, *ofile, *tfile; FILE *idfile, *ofile, *tfile;
int rbac_enabled = 0;
if ((idfile = fdopen(script_fds[SFD_LOG], "w")) == NULL) if ((idfile = fdopen(script_fds[SFD_LOG], "w")) == NULL)
log_error(USE_ERRNO, "fdopen"); log_error(USE_ERRNO, "fdopen");
@@ -329,13 +336,26 @@ script_execv(path, argv)
if ((tfile = fdopen(script_fds[SFD_TIMING], "w")) == NULL) if ((tfile = fdopen(script_fds[SFD_TIMING], "w")) == NULL)
log_error(USE_ERRNO, "fdopen"); log_error(USE_ERRNO, "fdopen");
#ifdef HAVE_SELINUX
rbac_enabled = is_selinux_enabled() > 0 && user_role != NULL;
if (rbac_enabled) {
selinux_prefork(user_role, user_type, script_fds[SFD_SLAVE]);
/* Re-open slave fd after it has been relabeled */
close(script_fds[SFD_SLAVE]);
script_fds[SFD_SLAVE] = open(slavename, O_RDWR, 0);
if (script_fds[SFD_SLAVE] == -1)
log_error(USE_ERRNO, "cannot open %s", slavename);
}
#endif
child = fork(); child = fork();
if (child == -1) if (child == -1)
log_error(USE_ERRNO, "Can't fork"); log_error(USE_ERRNO, "Can't fork");
if (child == 0) { if (child == 0) {
/* fork child, setup tty and exec command */ /* fork child, setup tty and exec command */
script_child(path, argv); script_child(path, argv, rbac_enabled);
return(-1); /* execv failure */ warning("unable to execute %s", path);
_exit(127);
} }
/* Setup signal handlers for child exit and window size changes. */ /* Setup signal handlers for child exit and window size changes. */
@@ -508,9 +528,10 @@ script_execv(path, argv)
} }
static void static void
script_child(path, argv) script_child(path, argv, rbac_enabled)
const char *path; char *path;
char *const argv[]; char *argv[];
int rbac_enabled;
{ {
/* /*
* Create new session, make slave controlling terminal and * Create new session, make slave controlling terminal and
@@ -539,6 +560,11 @@ script_child(path, argv)
close(script_fds[SFD_LOG]); close(script_fds[SFD_LOG]);
close(script_fds[SFD_OUTPUT]); close(script_fds[SFD_OUTPUT]);
close(script_fds[SFD_TIMING]); close(script_fds[SFD_TIMING]);
#ifdef HAVE_SELINUX
if (rbac_enabled)
selinux_execv(path, argv);
else
#endif
execv(path, argv); execv(path, argv);
} }
@@ -599,22 +625,20 @@ get_pty(master, slave)
int *master; int *master;
int *slave; int *slave;
{ {
char line[PATH_MAX];
struct group *gr; struct group *gr;
gid_t ttygid = -1; gid_t ttygid = -1;
if ((gr = sudo_getgrnam("tty")) != NULL) if ((gr = sudo_getgrnam("tty")) != NULL)
ttygid = gr->gr_gid; ttygid = gr->gr_gid;
if (openpty(master, slave, line, NULL, NULL) != 0) if (openpty(master, slave, slavename, NULL, NULL) != 0)
return(0); return(0);
(void) chown(line, runas_pw->pw_uid, ttygid); (void) chown(slavename, runas_pw->pw_uid, ttygid);
return(1); return(1);
} }
#else #else
# ifdef HAVE_GRANTPT # ifdef HAVE_GRANTPT
# ifndef HAVE_POSIX_OPENPT # ifndef HAVE_POSIX_OPENPT
static int static int
posix_openpt(oflag) posix_openpt(oflag)
@@ -652,18 +676,18 @@ get_pty(master, slave)
close(*master); close(*master);
return(0); return(0);
} }
*slave = open(line, O_RDWR, 0); strlcpy(slavename, line, sizeof(slavename));
*slave = open(slavename, O_RDWR, 0);
if (*slave == -1) { if (*slave == -1) {
close(*master); close(*master);
return(0); return(0);
} }
(void) chown(line, runas_pw->pw_uid, -1); (void) chown(slavename, runas_pw->pw_uid, -1);
return(1); return(1);
} }
# else /* !HAVE_GRANTPT */ # else /* !HAVE_GRANTPT */
static char line[] = "/dev/ptyXX";
static int static int
get_pty(master, slave) get_pty(master, slave)
int *master; int *master;
@@ -677,22 +701,22 @@ get_pty(master, slave)
ttygid = gr->gr_gid; ttygid = gr->gr_gid;
for (bank = "pqrs"; *bank != '\0'; bank++) { for (bank = "pqrs"; *bank != '\0'; bank++) {
line[sizeof("/dev/ptyX") - 2] = *bank; slavename[sizeof("/dev/ptyX") - 2] = *bank;
for (cp = "0123456789abcdef"; *cp != '\0'; cp++) { for (cp = "0123456789abcdef"; *cp != '\0'; cp++) {
line[sizeof("/dev/ptyXX") - 2] = *cp; slavename[sizeof("/dev/ptyXX") - 2] = *cp;
*master = open(line, O_RDWR, 0); *master = open(slavename, O_RDWR, 0);
if (*master == -1) { if (*master == -1) {
if (errno == ENOENT) if (errno == ENOENT)
return(0); /* out of ptys */ return(0); /* out of ptys */
continue; /* already in use */ continue; /* already in use */
} }
line[sizeof("/dev/p") - 2] = 't'; slavename[sizeof("/dev/p") - 2] = 't';
(void) chown(line, runas_pw->pw_uid, ttygid); (void) chown(slavename, runas_pw->pw_uid, ttygid);
(void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP); (void) chmod(slavename, S_IRUSR|S_IWUSR|S_IWGRP);
# ifdef HAVE_REVOKE # ifdef HAVE_REVOKE
(void) revoke(line); (void) revoke(slavename);
# endif # endif
*slave = open(line, O_RDWR, 0); *slave = open(slavename, O_RDWR, 0);
if (*slave != -1) if (*slave != -1)
return(1); /* success */ return(1); /* success */
(void) close(*master); (void) close(*master);

144
selinux.c
View File

@@ -52,6 +52,12 @@
__unused static const char rcsid[] = "$Sudo$"; __unused static const char rcsid[] = "$Sudo$";
#endif /* lint */ #endif /* lint */
static security_context_t old_context;
static security_context_t new_context;
static security_context_t tty_context;
static security_context_t new_tty_context;
static int enforcing;
/* /*
* This function attempts to revert the relabeling done to the tty. * This function attempts to revert the relabeling done to the tty.
* fd - referencing the opened ttyn * fd - referencing the opened ttyn
@@ -95,49 +101,36 @@ skip_relabel:
/* /*
* This function attempts to relabel the tty. If this function fails, then * 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, * the contexts are free'd and -1 is returned. On success, 0 is returned
* a valid fd is returned and tty_context and new_tty_context are set. * and tty_context and new_tty_context are set.
* *
* This function will not fail if it can not relabel the tty when selinux is * This function will not fail if it can not relabel the tty when selinux is
* in permissive mode. * in permissive mode.
*/ */
static int static int
relabel_tty(const char *ttyn, security_context_t new_context, relabel_tty(int ttyfd, security_context_t new_context,
security_context_t *tty_context, security_context_t *new_tty_context, security_context_t *tty_context, security_context_t *new_tty_context,
int enforcing) int enforcing)
{ {
int fd;
security_context_t tty_con = NULL; security_context_t tty_con = NULL;
security_context_t new_tty_con = NULL; security_context_t new_tty_con = NULL;
if (!ttyn) if (fgetfilecon(ttyfd, &tty_con) < 0) {
return(0); warning("unable to get current tty context, not relabeling tty");
/* Re-open TTY descriptor */
fd = open(ttyn, O_RDWR | O_NONBLOCK);
if (fd == -1) {
warning("unable to open %s", ttyn);
return(-1);
}
(void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
if (fgetfilecon(fd, &tty_con) < 0) {
warning("unable to get current context for %s, not relabeling tty",
ttyn);
if (enforcing) if (enforcing)
goto error; goto error;
} }
if (tty_con && (security_compute_relabel(new_context, tty_con, if (tty_con && (security_compute_relabel(new_context, tty_con,
SECCLASS_CHR_FILE, &new_tty_con) < 0)) { SECCLASS_CHR_FILE, &new_tty_con) < 0)) {
warning("unable to get new context for %s, not relabeling tty", ttyn); warning("unable to get new tty context, not relabeling tty");
if (enforcing) if (enforcing)
goto error; goto error;
} }
if (new_tty_con != NULL) { if (new_tty_con != NULL) {
if (fsetfilecon(fd, new_tty_con) < 0) { if (fsetfilecon(ttyfd, new_tty_con) < 0) {
warning("unable to set new context for %s", ttyn); warning("unable to set new tty context");
if (enforcing) if (enforcing)
goto error; goto error;
} }
@@ -145,11 +138,10 @@ relabel_tty(const char *ttyn, security_context_t new_context,
*tty_context = tty_con; *tty_context = tty_con;
*new_tty_context = new_tty_con; *new_tty_context = new_tty_con;
return(fd); return(0);
error: error:
freecon(tty_con); freecon(tty_con);
close(fd);
return(-1); return(-1);
} }
@@ -220,25 +212,11 @@ error:
} }
/* /*
* If the program is being run with a different security context we * Set the tty context in preparation for fork/exec.
* 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.
*/ */
void void
selinux_exec(char *role, char *type, char **argv, int login_shell) selinux_prefork(char *role, char *type, int ttyfd)
{ {
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')
error(EXIT_FAILURE, "unable to determine tty");
/* Store the caller's SID in old_context. */ /* Store the caller's SID in old_context. */
if (getprevcon(&old_context)) if (getprevcon(&old_context))
error(EXIT_FAILURE, "failed to get old_context"); error(EXIT_FAILURE, "failed to get old_context");
@@ -247,15 +225,14 @@ selinux_exec(char *role, char *type, char **argv, int login_shell)
if (enforcing < 0) if (enforcing < 0)
error(EXIT_FAILURE, "unable to determine enforcing mode."); error(EXIT_FAILURE, "unable to determine enforcing mode.");
#ifdef DEBUG #ifdef DEBUG
warningx("your old context was %s", old_context); warningx("your old context was %s", old_context);
#endif #endif
new_context = get_exec_context(old_context, role, type); new_context = get_exec_context(old_context, role, type);
if (!new_context) if (!new_context)
exit(EXIT_FAILURE); error(EXIT_FAILURE, "unable to get exec context");
ttyfd = relabel_tty(user_ttypath, new_context, &tty_context, ttyfd = relabel_tty(ttyfd, new_context, &tty_context,
&new_tty_context, enforcing); &new_tty_context, enforcing);
if (ttyfd < 0) if (ttyfd < 0)
error(EXIT_FAILURE, "unable to setup tty context for %s", new_context); error(EXIT_FAILURE, "unable to setup tty context for %s", new_context);
@@ -264,6 +241,64 @@ selinux_exec(char *role, char *type, char **argv, int login_shell)
warningx("your old tty context is %s", tty_context); warningx("your old tty context is %s", tty_context);
warningx("your new tty context is %s", new_tty_context); warningx("your new tty context is %s", new_tty_context);
#endif #endif
}
void
selinux_execv(char *path, char **argv)
{
if (setexeccon(new_context)) {
warning("unable to set exec context to %s", new_context);
if (enforcing)
return;
}
if (setkeycreatecon(new_context)) {
warning("unable to set key creation context to %s", new_context);
if (enforcing)
return;
}
#ifdef WITH_AUDIT
if (send_audit_message(1, old_context, new_context, user_ttypath))
return;
#endif
/* We use the "spare" slot in argv to store sesh. */
--argv;
argv[0] = *argv[1] == '-' ? "-sesh" : "sesh";
argv[1] = path;
execv(_PATH_SUDO_SESH, argv);
warning("%s", path);
}
/*
* 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.
*/
void
selinux_exec(char *role, char *type, char **argv)
{
pid_t childPid;
int enforcing, ttyfd;
/* Must have a tty. */
if (user_ttypath == NULL || *user_ttypath == '\0')
error(EXIT_FAILURE, "unable to determine tty");
/* Re-open TTY descriptor */
ttyfd = open(user_ttypath, O_RDWR | O_NONBLOCK);
if (ttyfd == -1)
error(EXIT_FAILURE, "unable to open %s", user_ttypath);
(void)fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
/*
* Get the old and new security and tty contexts, sets the new
* tty context on ttyfd.
*/
selinux_prefork(role, type, ttyfd);
childPid = fork(); childPid = fork();
if (childPid < 0) { if (childPid < 0) {
@@ -311,30 +346,7 @@ selinux_exec(char *role, char *type, char **argv, int login_shell)
if (ttyfd != STDERR_FILENO) if (ttyfd != STDERR_FILENO)
goto error; goto error;
if (setexeccon(new_context)) { selinux_execv(safe_cmnd, argv);
warning("unable to set exec context to %s", new_context);
if (enforcing)
goto error;
}
if (setkeycreatecon(new_context)) {
warning("unable to set key creation context to %s", new_context);
if (enforcing)
goto error;
}
#ifdef WITH_AUDIT
if (send_audit_message(1, old_context, new_context, user_ttypath))
goto error;
#endif
/* We use the "spare" slot in argv to store sesh. */
--argv;
argv[0] = login_shell ? "-sesh" : "sesh";
argv[1] = safe_cmnd;
execv(_PATH_SUDO_SESH, argv);
warning("%s", safe_cmnd);
error: error:
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);

23
sudo.c
View File

@@ -560,27 +560,24 @@ main(argc, argv, envp)
closefrom(def_closefrom); closefrom(def_closefrom);
#endif #endif
#ifndef PROFILING #ifdef PROFILING
if (ISSET(sudo_mode, MODE_BACKGROUND) && fork() > 0) {
syslog(LOG_AUTH|LOG_ERR, "fork");
exit(0); exit(0);
} else {
#ifdef HAVE_SELINUX
/* XXX - script support */
if (is_selinux_enabled() > 0 && user_role != NULL)
selinux_exec(user_role, user_type, NewArgv,
ISSET(sudo_mode, MODE_LOGIN_SHELL));
#endif #endif
if (ISSET(sudo_mode, MODE_BACKGROUND) && fork() > 0) {
syslog(LOG_AUTH|LOG_ERR, "fork"); /* XXX */
exit(0);
}
#ifdef _PATH_SUDO_TRANSCRIPT #ifdef _PATH_SUDO_TRANSCRIPT
if (def_transcript) if (def_transcript)
script_execv(safe_cmnd, NewArgv); script_execv(safe_cmnd, NewArgv);
else else
#endif
#ifdef HAVE_SELINUX
if (is_selinux_enabled() > 0 && user_role != NULL)
selinux_exec(user_role, user_type, NewArgv);
else
#endif #endif
execv(safe_cmnd, NewArgv); execv(safe_cmnd, NewArgv);
}
#else
exit(0);
#endif /* PROFILING */
/* /*
* If we got here then execve() failed... * If we got here then execve() failed...
*/ */

6
sudo.h
View File

@@ -320,13 +320,15 @@ struct group *sudo_getgrnam __P((const char *));
struct group *sudo_fakegrnam __P((const char *)); struct group *sudo_fakegrnam __P((const char *));
struct group *sudo_getgrgid __P((gid_t)); struct group *sudo_getgrgid __P((gid_t));
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
void selinux_exec __P((char *, char *, char **, int)); void selinux_exec __P((char *, char *, char **));
void selinux_execv __P((char *, char **));
void selinux_prefork __P((char *, char *, int));
#endif #endif
#ifdef HAVE_GETUSERATTR #ifdef HAVE_GETUSERATTR
void aix_setlimits __P((char *)); void aix_setlimits __P((char *));
#endif #endif
int script_duplow __P((int)); int script_duplow __P((int));
int script_execv __P((const char *, char * const *)); int script_execv __P((char *, char **));
void script_nextid __P((void)); void script_nextid __P((void));
void script_setup __P((void)); void script_setup __P((void));
int term_cbreak __P((int)); int term_cbreak __P((int));