]> granicus.if.org Git - sudo/commitdiff
First cut at refactoring some of the selinux code so it can be used
authorTodd C. Miller <Todd.Miller@courtesan.com>
Sun, 27 Sep 2009 13:03:56 +0000 (13:03 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Sun, 27 Sep 2009 13:03:56 +0000 (13:03 +0000)
in conjunction with sudo's transcript support.

script.c
selinux.c
sudo.c
sudo.h

index 56be5831db28520d949f9203a0a7aca275b53ea7..0624977025ea1e9cfebb468ab1b17cfcd6229bad 100644 (file)
--- a/script.c
+++ b/script.c
@@ -82,7 +82,13 @@ static sig_atomic_t alive = 1;
 static pid_t child;
 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 sigchild __P((int signo));
 static void sigwinch __P((int signo));
@@ -312,8 +318,8 @@ log_output(output, n, then, now, ofile, tfile)
 
 int
 script_execv(path, argv)
-    const char *path;
-    char *const argv[];
+    char *path;
+    char *argv[];
 {
     int n, nready;
     fd_set *fdsr, *fdsw;
@@ -321,6 +327,7 @@ script_execv(path, argv)
     struct timeval now, then;
     sigaction_t sa;
     FILE *idfile, *ofile, *tfile;
+    int rbac_enabled = 0;
 
     if ((idfile = fdopen(script_fds[SFD_LOG], "w")) == NULL)
        log_error(USE_ERRNO, "fdopen");
@@ -329,13 +336,26 @@ script_execv(path, argv)
     if ((tfile = fdopen(script_fds[SFD_TIMING], "w")) == NULL)
        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();
     if (child == -1)
        log_error(USE_ERRNO, "Can't fork");
     if (child == 0) {
        /* fork child, setup tty and exec command */
-       script_child(path, argv);
-       return(-1); /* execv failure */
+       script_child(path, argv, rbac_enabled);
+       warning("unable to execute %s", path);
+       _exit(127);
     }
 
     /* Setup signal handlers for child exit and window size changes. */
@@ -508,9 +528,10 @@ script_execv(path, argv)
 }
 
 static void
-script_child(path, argv)
-    const char *path;
-    char *const argv[];
+script_child(path, argv, rbac_enabled)
+    char *path;
+    char *argv[];
+    int rbac_enabled;
 {
     /*
      * 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_OUTPUT]);
     close(script_fds[SFD_TIMING]);
+#ifdef HAVE_SELINUX
+    if (rbac_enabled)
+      selinux_execv(path, argv);
+    else
+#endif
     execv(path, argv);
 }
 
@@ -599,22 +625,20 @@ get_pty(master, slave)
     int *master;
     int *slave;
 {
-    char line[PATH_MAX];
     struct group *gr;
     gid_t ttygid = -1;
 
     if ((gr = sudo_getgrnam("tty")) != NULL)
        ttygid = gr->gr_gid;
 
-    if (openpty(master, slave, line, NULL, NULL) != 0)
+    if (openpty(master, slave, slavename, NULL, NULL) != 0)
        return(0);
-    (void) chown(line, runas_pw->pw_uid, ttygid);
+    (void) chown(slavename, runas_pw->pw_uid, ttygid);
     return(1);
 }
 
 #else
 # ifdef HAVE_GRANTPT
-
 #  ifndef HAVE_POSIX_OPENPT
 static int
 posix_openpt(oflag)
@@ -652,18 +676,18 @@ get_pty(master, slave)
        close(*master);
        return(0);
     }
-    *slave = open(line, O_RDWR, 0);
+    strlcpy(slavename, line, sizeof(slavename));
+    *slave = open(slavename, O_RDWR, 0);
     if (*slave == -1) {
        close(*master);
        return(0);
     }
-    (void) chown(line, runas_pw->pw_uid, -1);
+    (void) chown(slavename, runas_pw->pw_uid, -1);
     return(1);
 }
 
 # else /* !HAVE_GRANTPT */
 
-static char line[] = "/dev/ptyXX";
 static int
 get_pty(master, slave)
     int *master;
@@ -677,22 +701,22 @@ get_pty(master, slave)
        ttygid = gr->gr_gid;
 
     for (bank = "pqrs"; *bank != '\0'; bank++) {
-       line[sizeof("/dev/ptyX") - 2] = *bank;
+       slavename[sizeof("/dev/ptyX") - 2] = *bank;
        for (cp = "0123456789abcdef"; *cp != '\0'; cp++) {
-           line[sizeof("/dev/ptyXX") - 2] = *cp;
-           *master = open(line, O_RDWR, 0);
+           slavename[sizeof("/dev/ptyXX") - 2] = *cp;
+           *master = open(slavename, O_RDWR, 0);
            if (*master == -1) {
                if (errno == ENOENT)
                    return(0); /* out of ptys */
                continue; /* already in use */
            }
-           line[sizeof("/dev/p") - 2] = 't';
-           (void) chown(line, runas_pw->pw_uid, ttygid);
-           (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
+           slavename[sizeof("/dev/p") - 2] = 't';
+           (void) chown(slavename, runas_pw->pw_uid, ttygid);
+           (void) chmod(slavename, S_IRUSR|S_IWUSR|S_IWGRP);
 #  ifdef HAVE_REVOKE
-           (void) revoke(line);
+           (void) revoke(slavename);
 #  endif
-           *slave = open(line, O_RDWR, 0);
+           *slave = open(slavename, O_RDWR, 0);
            if (*slave != -1)
                    return(1); /* success */
            (void) close(*master);
index a8fec790e761c54cf10f1ca5ccd35dc64c2f9c4c..38224dd54c352b46cbacdc65a884908a9334bd12 100644 (file)
--- a/selinux.c
+++ b/selinux.c
 __unused static const char rcsid[] = "$Sudo$";
 #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.
  * fd             - referencing the opened ttyn
@@ -95,49 +101,36 @@ skip_relabel:
 
 /*
  * 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,
+relabel_tty(int ttyfd, security_context_t new_context,
+    security_context_t *tty_context, security_context_t *new_tty_context,
     int enforcing)
 {
-    int fd;
     security_context_t tty_con = NULL;
     security_context_t new_tty_con = NULL;
 
-    if (!ttyn)
-       return(0);
-
-    /* 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 (fgetfilecon(ttyfd, &tty_con) < 0) {
+       warning("unable to get current tty context, not relabeling tty");
        if (enforcing)
            goto error;
     }
 
     if (tty_con && (security_compute_relabel(new_context, tty_con,
        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)
            goto error;
     }
 
     if (new_tty_con != NULL) {
-       if (fsetfilecon(fd, new_tty_con) < 0) {
-           warning("unable to set new context for %s", ttyn);
+       if (fsetfilecon(ttyfd, new_tty_con) < 0) {
+           warning("unable to set new tty context");
            if (enforcing)
                goto error;
        }
@@ -145,11 +138,10 @@ relabel_tty(const char *ttyn, security_context_t new_context,
 
     *tty_context = tty_con;
     *new_tty_context = new_tty_con;
-    return(fd);
+    return(0);
 
 error:
     freecon(tty_con);
-    close(fd);
     return(-1);
 }
 
@@ -220,25 +212,11 @@ error:
 }
 
 /* 
- * 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 tty context in preparation for fork/exec.
  */
 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. */
     if (getprevcon(&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)
        error(EXIT_FAILURE, "unable to determine enforcing mode.");
 
-    
 #ifdef DEBUG
     warningx("your old context was %s", old_context);
 #endif
     new_context = get_exec_context(old_context, role, type);
     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);
     if (ttyfd < 0)
        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 new tty context is %s", new_tty_context);
 #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();
     if (childPid < 0) {
@@ -311,30 +346,7 @@ selinux_exec(char *role, char *type, char **argv, int login_shell)
     if (ttyfd != STDERR_FILENO)
        goto error;
 
-    if (setexeccon(new_context)) {
-       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);
+    selinux_execv(safe_cmnd, argv);
 
 error:
     _exit(EXIT_FAILURE);
diff --git a/sudo.c b/sudo.c
index 8a442fdd3bc064e75e1f80001c8868827f41254c..7ce2e41a84e90e15a92d5905cca71dee45a72bc5 100644 (file)
--- a/sudo.c
+++ b/sudo.c
@@ -560,27 +560,24 @@ main(argc, argv, envp)
        closefrom(def_closefrom);
 #endif
 
-#ifndef PROFILING
+#ifdef PROFILING
+       exit(0);
+#endif
        if (ISSET(sudo_mode, MODE_BACKGROUND) && fork() > 0) {
-           syslog(LOG_AUTH|LOG_ERR, "fork");
+           syslog(LOG_AUTH|LOG_ERR, "fork"); /* XXX */
            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
+       }
 #ifdef _PATH_SUDO_TRANSCRIPT
-           if (def_transcript)
-               script_execv(safe_cmnd, NewArgv);
-           else
+       if (def_transcript)
+           script_execv(safe_cmnd, NewArgv);
+       else
 #endif
-               execv(safe_cmnd, NewArgv);
-       }
-#else
-       exit(0);
-#endif /* PROFILING */
+#ifdef HAVE_SELINUX
+       if (is_selinux_enabled() > 0 && user_role != NULL)
+           selinux_exec(user_role, user_type, NewArgv);
+       else
+#endif
+       execv(safe_cmnd, NewArgv);
        /*
         * If we got here then execve() failed...
         */
diff --git a/sudo.h b/sudo.h
index ebb08cb626973d4620435ff56ad905f403fcca20..0843abbd2d49c5b30ab7d9ea2eaa4be0a7dc3855 100644 (file)
--- a/sudo.h
+++ b/sudo.h
@@ -320,13 +320,15 @@ struct group *sudo_getgrnam __P((const char *));
 struct group *sudo_fakegrnam __P((const char *));
 struct group *sudo_getgrgid __P((gid_t));
 #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
 #ifdef HAVE_GETUSERATTR
 void aix_setlimits __P((char *));
 #endif
 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_setup __P((void));
 int term_cbreak __P((int));