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>