Annotation of embedaddon/sudo/src/selinux.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (c) 2009-2010 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: 
        !            52: static struct selinux_state {
        !            53:     security_context_t old_context;
        !            54:     security_context_t new_context;
        !            55:     security_context_t tty_context;
        !            56:     security_context_t new_tty_context;
        !            57:     const char *ttyn;
        !            58:     int ttyfd;
        !            59:     int enforcing;
        !            60: } se_state;
        !            61: 
        !            62: #ifdef HAVE_LINUX_AUDIT
        !            63: static int
        !            64: audit_role_change(const security_context_t old_context,
        !            65:     const security_context_t new_context, const char *ttyn)
        !            66: {
        !            67:     int au_fd, rc;
        !            68:     char *message;
        !            69: 
        !            70:     au_fd = audit_open();
        !            71:     if (au_fd == -1) {
        !            72:         /* Kernel may not have audit support. */
        !            73:         if (errno != EINVAL && errno != EPROTONOSUPPORT && errno != EAFNOSUPPORT
        !            74: )
        !            75:             error(1, _("unable to open audit system"));
        !            76:        return -1;
        !            77:     }
        !            78: 
        !            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(_("unable to send audit message"));
        !            86: 
        !            87:     efree(message);
        !            88:     close(au_fd);
        !            89: 
        !            90:     return 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: 
        !           107:     if (se_state.ttyfd == -1 || se_state.new_tty_context == NULL)
        !           108:        goto skip_relabel;
        !           109: 
        !           110:     /* Verify that the tty still has the context set by sudo. */
        !           111:     if ((retval = fgetfilecon(se_state.ttyfd, &chk_tty_context)) < 0) {
        !           112:        warning(_("unable to fgetfilecon %s"), se_state.ttyn);
        !           113:        goto skip_relabel;
        !           114:     }
        !           115: 
        !           116:     if ((retval = strcmp(chk_tty_context, se_state.new_tty_context))) {
        !           117:        warningx(_("%s changed labels"), se_state.ttyn);
        !           118:        goto skip_relabel;
        !           119:     }
        !           120: 
        !           121:     if ((retval = fsetfilecon(se_state.ttyfd, se_state.tty_context)) < 0)
        !           122:        warning(_("unable to restore context for %s"), se_state.ttyn);
        !           123: 
        !           124: skip_relabel:
        !           125:     if (se_state.ttyfd != -1) {
        !           126:        close(se_state.ttyfd);
        !           127:        se_state.ttyfd = -1;
        !           128:     }
        !           129:     if (chk_tty_context != NULL) {
        !           130:        freecon(chk_tty_context);
        !           131:        chk_tty_context = NULL;
        !           132:     }
        !           133:     return retval;
        !           134: }
        !           135: 
        !           136: /*
        !           137:  * This function attempts to relabel the tty. If this function fails, then
        !           138:  * the contexts are free'd and -1 is returned. On success, 0 is returned
        !           139:  * and tty_context and new_tty_context are set.
        !           140:  *
        !           141:  * This function will not fail if it can not relabel the tty when selinux is
        !           142:  * in permissive mode.
        !           143:  */
        !           144: static int
        !           145: relabel_tty(const char *ttyn, int ptyfd)
        !           146: {
        !           147:     security_context_t tty_con = NULL;
        !           148:     security_context_t new_tty_con = NULL;
        !           149:     int fd;
        !           150: 
        !           151:     se_state.ttyfd = ptyfd;
        !           152: 
        !           153:     /* It is perfectly legal to have no tty. */
        !           154:     if (ptyfd == -1 && ttyn == NULL)
        !           155:        return 0;
        !           156: 
        !           157:     /* If sudo is not allocating a pty for the command, open current tty. */
        !           158:     if (ptyfd == -1) {
        !           159:        se_state.ttyfd = open(ttyn, O_RDWR|O_NONBLOCK);
        !           160:        if (se_state.ttyfd == -1) {
        !           161:            warning(_("unable to open %s, not relabeling tty"), ttyn);
        !           162:            if (se_state.enforcing)
        !           163:                goto bad;
        !           164:        }
        !           165:        (void)fcntl(se_state.ttyfd, F_SETFL,
        !           166:            fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
        !           167:     }
        !           168: 
        !           169:     if (fgetfilecon(se_state.ttyfd, &tty_con) < 0) {
        !           170:        warning(_("unable to get current tty context, not relabeling tty"));
        !           171:        if (se_state.enforcing)
        !           172:            goto bad;
        !           173:     }
        !           174: 
        !           175:     if (tty_con && (security_compute_relabel(se_state.new_context, tty_con,
        !           176:        SECCLASS_CHR_FILE, &new_tty_con) < 0)) {
        !           177:        warning(_("unable to get new tty context, not relabeling tty"));
        !           178:        if (se_state.enforcing)
        !           179:            goto bad;
        !           180:     }
        !           181: 
        !           182:     if (new_tty_con != NULL) {
        !           183:        if (fsetfilecon(se_state.ttyfd, new_tty_con) < 0) {
        !           184:            warning(_("unable to set new tty context"));
        !           185:            if (se_state.enforcing)
        !           186:                goto bad;
        !           187:        }
        !           188:     }
        !           189: 
        !           190:     if (ptyfd != -1) {
        !           191:        /* Reopen pty that was relabeled, std{in,out,err} are reset later. */
        !           192:        se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY, 0);
        !           193:        if (se_state.ttyfd == -1) {
        !           194:            warning(_("unable to open %s"), ttyn);
        !           195:            if (se_state.enforcing)
        !           196:                goto bad;
        !           197:        }
        !           198:        if (dup2(se_state.ttyfd, ptyfd) == -1) {
        !           199:            warning("dup2");
        !           200:            goto bad;
        !           201:        }
        !           202:     } else {
        !           203:        /* Re-open tty to get new label and reset std{in,out,err} */
        !           204:        close(se_state.ttyfd);
        !           205:        se_state.ttyfd = open(ttyn, O_RDWR|O_NONBLOCK);
        !           206:        if (se_state.ttyfd == -1) {
        !           207:            warning(_("unable to open %s"), ttyn);
        !           208:            goto bad;
        !           209:        }
        !           210:        (void)fcntl(se_state.ttyfd, F_SETFL,
        !           211:            fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
        !           212:        for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) {
        !           213:            if (isatty(fd) && dup2(se_state.ttyfd, fd) == -1) {
        !           214:                warning("dup2");
        !           215:                goto bad;
        !           216:            }
        !           217:        }
        !           218:     }
        !           219:     /* Retain se_state.ttyfd so we can restore label when command finishes. */
        !           220:     (void)fcntl(se_state.ttyfd, F_SETFD, FD_CLOEXEC);
        !           221: 
        !           222:     se_state.ttyn = ttyn;
        !           223:     se_state.tty_context = tty_con;
        !           224:     se_state.new_tty_context = new_tty_con;
        !           225:     return 0;
        !           226: 
        !           227: bad:
        !           228:     if (se_state.ttyfd != -1 && se_state.ttyfd != ptyfd) {
        !           229:        close(se_state.ttyfd);
        !           230:        se_state.ttyfd = -1;
        !           231:     }
        !           232:     freecon(tty_con);
        !           233:     return -1;
        !           234: }
        !           235: 
        !           236: /*
        !           237:  * Returns a new security context based on the old context and the
        !           238:  * specified role and type.
        !           239:  */
        !           240: security_context_t
        !           241: get_exec_context(security_context_t old_context, const char *role, const char *type)
        !           242: {
        !           243:     security_context_t new_context = NULL;
        !           244:     context_t context = NULL;
        !           245:     char *typebuf = NULL;
        !           246:     
        !           247:     /* We must have a role, the type is optional (we can use the default). */
        !           248:     if (!role) {
        !           249:        warningx(_("you must specify a role for type %s"), type);
        !           250:        errno = EINVAL;
        !           251:        return NULL;
        !           252:     }
        !           253:     if (!type) {
        !           254:        if (get_default_type(role, &typebuf)) {
        !           255:            warningx(_("unable to get default type for role %s"), role);
        !           256:            errno = EINVAL;
        !           257:            return NULL;
        !           258:        }
        !           259:        type = typebuf;
        !           260:     }
        !           261:     
        !           262:     /* 
        !           263:      * Expand old_context into a context_t so that we extract and modify 
        !           264:      * its components easily. 
        !           265:      */
        !           266:     context = context_new(old_context);
        !           267:     
        !           268:     /*
        !           269:      * Replace the role and type in "context" with the role and
        !           270:      * type we will be running the command as.
        !           271:      */
        !           272:     if (context_role_set(context, role)) {
        !           273:        warning(_("failed to set new role %s"), role);
        !           274:        goto bad;
        !           275:     }
        !           276:     if (context_type_set(context, type)) {
        !           277:        warning(_("failed to set new type %s"), type);
        !           278:        goto bad;
        !           279:     }
        !           280:       
        !           281:     /*
        !           282:      * Convert "context" back into a string and verify it.
        !           283:      */
        !           284:     new_context = estrdup(context_str(context));
        !           285:     if (security_check_context(new_context) < 0) {
        !           286:        warningx(_("%s is not a valid context"), new_context);
        !           287:        errno = EINVAL;
        !           288:        goto bad;
        !           289:     }
        !           290: 
        !           291: #ifdef DEBUG
        !           292:     warningx("Your new context is %s", new_context);
        !           293: #endif
        !           294: 
        !           295:     context_free(context);
        !           296:     return new_context;
        !           297: 
        !           298: bad:
        !           299:     free(typebuf);
        !           300:     context_free(context);
        !           301:     freecon(new_context);
        !           302:     return NULL;
        !           303: }
        !           304: 
        !           305: /* 
        !           306:  * Set the exec and tty contexts in preparation for fork/exec.
        !           307:  * Must run as root, before the uid change.
        !           308:  * If ptyfd is not -1, it indicates we are running
        !           309:  * in a pty and do not need to reset std{in,out,err}.
        !           310:  * Returns 0 on success and -1 on failure.
        !           311:  */
        !           312: int
        !           313: selinux_setup(const char *role, const char *type, const char *ttyn,
        !           314:     int ptyfd)
        !           315: {
        !           316:     int rval = -1;
        !           317: 
        !           318:     /* Store the caller's SID in old_context. */
        !           319:     if (getprevcon(&se_state.old_context)) {
        !           320:        warning(_("failed to get old_context"));
        !           321:        goto done;
        !           322:     }
        !           323: 
        !           324:     se_state.enforcing = security_getenforce();
        !           325:     if (se_state.enforcing < 0) {
        !           326:        warning(_("unable to determine enforcing mode."));
        !           327:        goto done;
        !           328:     }
        !           329: 
        !           330: #ifdef DEBUG
        !           331:     warningx("your old context was %s", se_state.old_context);
        !           332: #endif
        !           333:     se_state.new_context = get_exec_context(se_state.old_context, role, type);
        !           334:     if (!se_state.new_context)
        !           335:        goto done;
        !           336:     
        !           337:     if (relabel_tty(ttyn, ptyfd) < 0) {
        !           338:        warning(_("unable to setup tty context for %s"), se_state.new_context);
        !           339:        goto done;
        !           340:     }
        !           341: 
        !           342: #ifdef DEBUG
        !           343:     if (se_state.ttyfd != -1) {
        !           344:        warningx("your old tty context is %s", se_state.tty_context);
        !           345:        warningx("your new tty context is %s", se_state.new_tty_context);
        !           346:     }
        !           347: #endif
        !           348: 
        !           349: #ifdef HAVE_LINUX_AUDIT
        !           350:     audit_role_change(se_state.old_context, se_state.new_context,
        !           351:        se_state.ttyn);
        !           352: #endif
        !           353: 
        !           354:     rval = 0;
        !           355: 
        !           356: done:
        !           357:     return rval;
        !           358: }
        !           359: 
        !           360: void
        !           361: selinux_execve(const char *path, char *argv[], char *envp[])
        !           362: {
        !           363:     char **nargv;
        !           364:     int argc, serrno;
        !           365: 
        !           366:     if (setexeccon(se_state.new_context)) {
        !           367:        warning(_("unable to set exec context to %s"), se_state.new_context);
        !           368:        if (se_state.enforcing)
        !           369:            return;
        !           370:     }
        !           371: 
        !           372: #ifdef HAVE_SETKEYCREATECON
        !           373:     if (setkeycreatecon(se_state.new_context)) {
        !           374:        warning(_("unable to set key creation context to %s"), se_state.new_context);
        !           375:        if (se_state.enforcing)
        !           376:            return;
        !           377:     }
        !           378: #endif /* HAVE_SETKEYCREATECON */
        !           379: 
        !           380:     for (argc = 0; argv[argc] != NULL; argc++)
        !           381:        continue;
        !           382: 
        !           383:     /* Build new argv with sesh as argv[0]. */
        !           384:     nargv = emalloc2(argc + 2, sizeof(char *));
        !           385:     nargv[0] = *argv[0] == '-' ? "-sesh" : "sesh";
        !           386:     nargv[1] = (char *)path;
        !           387:     memcpy(&nargv[2], &argv[1], argc * sizeof(char *)); /* copies NULL */
        !           388: 
        !           389:     execve(_PATH_SUDO_SESH, nargv, envp);
        !           390:     serrno = errno;
        !           391:     free(nargv);
        !           392:     errno = serrno;
        !           393: }

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