Merge commit 'upstream/1.7.2p2'
[debian/sudo] / selinux.c
1 /*
2  * Copyright (c) 2008 Dan Walsh <dwalsh@redhat.com>
3  *
4  * Borrowed heavily from newrole source code
5  * Authors:
6  *      Anthony Colatrella
7  *      Tim Fraser
8  *      Steve Grubb <sgrubb@redhat.com>
9  *      Darrel Goeddel <DGoeddel@trustedcs.com>
10  *      Michael Thompson <mcthomps@us.ibm.com>
11  *      Dan Walsh <dwalsh@redhat.com>
12  *
13  * Permission to use, copy, modify, and distribute this software for any
14  * purpose with or without fee is hereby granted, provided that the above
15  * copyright notice and this permission notice appear in all copies.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
18  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
20  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
23  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24  */
25
26 #include <config.h>
27
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stddef.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <signal.h>
38 #ifdef WITH_AUDIT
39 #include <libaudit.h>
40 #endif
41
42 #include <selinux/flask.h>             /* for SECCLASS_CHR_FILE */
43 #include <selinux/selinux.h>           /* for is_selinux_enabled() */
44 #include <selinux/context.h>           /* for context-mangling functions */
45 #include <selinux/get_default_type.h>
46 #include <selinux/get_context_list.h>
47
48 #include "sudo.h"
49 #include "pathnames.h"
50
51 #ifndef lint
52 __unused static const char rcsid[] = "$Sudo: selinux.c,v 1.5 2008/02/22 20:33:00 millert Exp $";
53 #endif /* lint */
54
55 /*
56  * This function attempts to revert the relabeling done to the tty.
57  * fd              - referencing the opened ttyn
58  * ttyn            - name of tty to restore
59  * tty_context     - original context of the tty
60  * new_tty_context - context tty was relabeled to
61  *
62  * Returns zero on success, non-zero otherwise
63  */
64 static int
65 restore_tty_label(int fd, const char *ttyn, security_context_t tty_context,
66     security_context_t new_tty_context)
67 {
68     int rc = 0;
69     security_context_t chk_tty_context = NULL;
70
71     if (!ttyn)
72             goto skip_relabel;
73
74     if (!new_tty_context)
75             goto skip_relabel;
76
77     /* Verify that the tty still has the context set by sudo. */
78     if ((rc = fgetfilecon(fd, &chk_tty_context)) < 0) {
79             warning("unable to fgetfilecon %s", ttyn);
80             goto skip_relabel;
81     }
82
83     if ((rc = strcmp(chk_tty_context, new_tty_context))) {
84             warningx("%s changed labels.", ttyn);
85             goto skip_relabel;
86     }
87
88     if ((rc = fsetfilecon(fd, tty_context)) < 0)
89         warning("unable to restore context for %s", ttyn);
90
91 skip_relabel:
92     freecon(chk_tty_context);
93     return(rc);
94 }
95
96 /*
97  * This function attempts to relabel the tty. If this function fails, then
98  * the fd is closed, the contexts are free'd and -1 is returned. On success,
99  * a valid fd is returned and tty_context and new_tty_context are set.
100  *
101  * This function will not fail if it can not relabel the tty when selinux is
102  * in permissive mode.
103  */
104 static int
105 relabel_tty(const char *ttyn, security_context_t new_context,
106     security_context_t * tty_context, security_context_t * new_tty_context,
107     int enforcing)
108 {
109     int fd;
110     security_context_t tty_con = NULL;
111     security_context_t new_tty_con = NULL;
112
113     if (!ttyn)
114         return(0);
115
116     /* Re-open TTY descriptor */
117     fd = open(ttyn, O_RDWR | O_NONBLOCK);
118     if (fd == -1) {
119         warning("unable to open %s", ttyn);
120         return(-1);
121     }
122     (void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
123
124     if (fgetfilecon(fd, &tty_con) < 0) {
125         warning("unable to get current context for %s, not relabeling tty",
126             ttyn);
127         if (enforcing)
128             goto error;
129     }
130
131     if (tty_con && (security_compute_relabel(new_context, tty_con,
132         SECCLASS_CHR_FILE, &new_tty_con) < 0)) {
133         warning("unable to get new context for %s, not relabeling tty", ttyn);
134         if (enforcing)
135             goto error;
136     }
137
138     if (new_tty_con != NULL) {
139         if (fsetfilecon(fd, new_tty_con) < 0) {
140             warning("unable to set new context for %s", ttyn);
141             if (enforcing)
142                 goto error;
143         }
144     }
145
146     *tty_context = tty_con;
147     *new_tty_context = new_tty_con;
148     return(fd);
149
150 error:
151     freecon(tty_con);
152     close(fd);
153     return(-1);
154 }
155
156 /*
157  * Returns a new security context based on the old context and the
158  * specified role and type.
159  */
160 security_context_t
161 get_exec_context(security_context_t old_context, char *role, char *type)
162 {
163     security_context_t new_context = NULL;
164     context_t context = NULL;
165     char *typebuf = NULL;
166     
167     /* We must have a role, the type is optional (we can use the default). */
168     if (!role) {
169         warningx("you must specify a role.");
170         return(NULL);
171     }
172     if (!type) {
173         if (get_default_type(role, &typebuf)) {
174             warningx("unable to get default type");
175             return(NULL);
176         }
177         type = typebuf;
178     }
179     
180     /* 
181      * Expand old_context into a context_t so that we extract and modify 
182      * its components easily. 
183      */
184     context = context_new(old_context);
185     
186     /*
187      * Replace the role and type in "context" with the role and
188      * type we will be running the command as.
189      */
190     if (context_role_set(context, role)) {
191         warningx("failed to set new role %s", role);
192         goto error;
193     }
194     if (context_type_set(context, type)) {
195         warningx("failed to set new type %s", type);
196         goto error;
197     }
198       
199     /*
200      * Convert "context" back into a string and verify it.
201      */
202     new_context = estrdup(context_str(context));
203     if (security_check_context(new_context) < 0) {
204         warningx("%s is not a valid context", new_context);
205         goto error;
206     }
207
208 #ifdef DEBUG
209     warningx("Your new context is %s", new_context);
210 #endif
211
212     context_free(context);
213     return(new_context);
214
215 error:
216     free(typebuf);
217     context_free(context);
218     freecon(new_context);
219     return(NULL);
220 }
221
222 /* 
223  * If the program is being run with a different security context we
224  * need to go through an intermediary process for the transition to
225  * be allowed by the policy.  We use the "sesh" shell for this, which
226  * will simply execute the command pass to it on the command line.
227  */
228 void
229 selinux_exec(char *role, char *type, char **argv, int login_shell)
230 {
231     security_context_t old_context = NULL;
232     security_context_t new_context = NULL;
233     security_context_t tty_context = NULL;
234     security_context_t new_tty_context = NULL;
235     pid_t childPid;
236     int enforcing, ttyfd;
237
238     /* Must have a tty. */
239     if (user_ttypath == NULL || *user_ttypath == '\0')
240         error(EXIT_FAILURE, "unable to determine tty");
241
242     /* Store the caller's SID in old_context. */
243     if (getprevcon(&old_context))
244         error(EXIT_FAILURE, "failed to get old_context");
245
246     enforcing = security_getenforce();
247     if (enforcing < 0)
248         error(EXIT_FAILURE, "unable to determine enforcing mode.");
249
250     
251 #ifdef DEBUG
252     warningx("your old context was %s", old_context);
253 #endif
254     new_context = get_exec_context(old_context, role, type);
255     if (!new_context)
256         exit(EXIT_FAILURE);
257     
258     ttyfd = relabel_tty(user_ttypath, new_context, &tty_context,
259         &new_tty_context, enforcing);
260     if (ttyfd < 0)
261         error(EXIT_FAILURE, "unable to setup tty context for %s", new_context);
262
263 #ifdef DEBUG
264     warningx("your old tty context is %s", tty_context);
265     warningx("your new tty context is %s", new_tty_context);
266 #endif
267
268     childPid = fork();
269     if (childPid < 0) {
270         /* fork failed, no child to worry about */
271         warning("unable to fork");
272         if (restore_tty_label(ttyfd, user_ttypath, tty_context, new_tty_context))
273             warningx("unable to restore tty label");
274         exit(EXIT_FAILURE);
275     } else if (childPid) {
276         pid_t pid;
277         int status;
278         
279         /* Parent, wait for child to finish. */
280         do {
281                 pid = waitpid(childPid, &status, 0);
282         } while (pid == -1 && errno == EINTR);
283
284         if (pid == -1)
285             error(EXIT_FAILURE, "waitpid");
286         
287         if (restore_tty_label(ttyfd, user_ttypath, tty_context, new_tty_context))
288             errorx(EXIT_FAILURE, "unable to restore tty label");
289
290         /* Preserve child exit status. */
291         if (WIFEXITED(status))
292             exit(WEXITSTATUS(status));
293         exit(EXIT_FAILURE);
294     }
295     /* Child */
296     /* Close the tty and reopen descriptors 0 through 2 */
297     if (close(ttyfd) || close(STDIN_FILENO) || close(STDOUT_FILENO) ||
298         close(STDERR_FILENO)) {
299         warning("could not close descriptors");
300         goto error;
301     }
302     ttyfd = open(user_ttypath, O_RDONLY | O_NONBLOCK);
303     if (ttyfd != STDIN_FILENO)
304         goto error;
305     fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
306     ttyfd = open(user_ttypath, O_RDWR | O_NONBLOCK);
307     if (ttyfd != STDOUT_FILENO)
308         goto error;
309     fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
310     ttyfd = dup(STDOUT_FILENO);
311     if (ttyfd != STDERR_FILENO)
312         goto error;
313
314     if (setexeccon(new_context)) {
315         warning("unable to set exec context to %s", new_context);
316         if (enforcing)
317             goto error;
318     }
319
320     if (setkeycreatecon(new_context)) {
321         warning("unable to set key creation context to %s", new_context);
322         if (enforcing)
323             goto error;
324     }
325
326 #ifdef WITH_AUDIT
327     if (send_audit_message(1, old_context, new_context, user_ttypath)) 
328         goto error;
329 #endif
330
331     /* We use the "spare" slot in argv to store sesh. */
332     --argv;
333     argv[0] = login_shell ? "-sesh" : "sesh";
334     argv[1] = safe_cmnd;
335
336     execv(_PATH_SUDO_SESH, argv);
337     warning("%s", safe_cmnd);
338
339 error:
340     _exit(EXIT_FAILURE);
341 }