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