1 /************************************************************************
2 *
3 * newrole
4 *
5 * SYNOPSIS:
6 *
7 * This program allows a user to change their SELinux RBAC role and/or
8 * SELinux TE type (domain) in a manner similar to the way the traditional
9 * UNIX su program allows a user to change their identity.
10 *
11 * USAGE:
12 *
13 * newrole [ -r role ] [ -t type ] [ -l level ] [ -V ] [ -- args ]
14 *
15 * BUILD OPTIONS:
16 *
17 * option USE_PAM:
18 *
19 * Set the USE_PAM constant if you want to authenticate users via PAM.
20 * If USE_PAM is not set, users will be authenticated via direct
21 * access to the shadow password file.
22 *
23 * If you decide to use PAM must be told how to handle newrole. A
24 * good rule-of-thumb might be to tell PAM to handle newrole in the
25 * same way it handles su, except that you should remove the pam_rootok.so
26 * entry so that even root must re-authenticate to change roles.
27 *
28 * If you choose not to use PAM, make sure you have a shadow passwd file
29 * in /etc/shadow. You can use a symlink if your shadow passwd file
30 * lives in another directory. Example:
31 * su
32 * cd /etc
33 * ln -s /etc/auth/shadow shadow
34 *
35 * If you decide not to use PAM, you will also have to make newrole
36 * setuid root, so that it can read the shadow passwd file.
37 *
38 *
39 * Authors:
40 * Anthony Colatrella
41 * Tim Fraser
42 * Steve Grubb <sgrubb@redhat.com>
43 * Darrel Goeddel <DGoeddel@trustedcs.com>
44 * Michael Thompson <mcthomps@us.ibm.com>
45 * Dan Walsh <dwalsh@redhat.com>
46 *
47 *************************************************************************/
48
49 #define _GNU_SOURCE
50
51 #if defined(AUDIT_LOG_PRIV) && !defined(USE_AUDIT)
52 #error AUDIT_LOG_PRIV needs the USE_AUDIT option
53 #endif
54 #if defined(NAMESPACE_PRIV) && !defined(USE_PAM)
55 #error NAMESPACE_PRIV needs the USE_PAM option
56 #endif
57
58 #include <stdio.h>
59 #include <stdlib.h> /* for malloc(), realloc(), free() */
60 #include <pwd.h> /* for getpwuid() */
61 #include <ctype.h>
62 #include <sys/types.h> /* to make getuid() and getpwuid() happy */
63 #include <sys/wait.h> /* for wait() */
64 #include <getopt.h> /* for getopt_long() form of getopt() */
65 #include <fcntl.h>
66 #include <string.h>
67 #include <errno.h>
68 #include <selinux/selinux.h> /* for is_selinux_enabled() */
69 #include <selinux/context.h> /* for context-mangling functions */
70 #include <selinux/get_default_type.h>
71 #include <selinux/get_context_list.h> /* for SELINUX_DEFAULTUSER */
72 #include <signal.h>
73 #include <unistd.h> /* for getuid(), exit(), getopt() */
74 #ifdef USE_AUDIT
75 #include <libaudit.h>
76 #endif
77 #if defined(AUDIT_LOG_PRIV) || defined(NAMESPACE_PRIV)
78 #include <sys/prctl.h>
79 #include <cap-ng.h>
80 #endif
81 #ifdef USE_NLS
82 #include <locale.h> /* for setlocale() */
83 #include <libintl.h> /* for gettext() */
84 #define _(msgid) gettext (msgid)
85 #else
86 #define _(msgid) (msgid)
87 #endif
88 #ifndef PACKAGE
89 #define PACKAGE "policycoreutils" /* the name of this package lang translation */
90 #endif
91
92 #define TRUE 1
93 #define FALSE 0
94
95 /* USAGE_STRING describes the command-line args of this program. */
96 #define USAGE_STRING "USAGE: newrole [ -r role ] [ -t type ] [ -l level ] [ -p ] [ -V ] [ -- args ]"
97
98 #ifdef USE_PAM
99 #define PAM_SERVICE_CONFIG "/etc/selinux/newrole_pam.conf";
100 #endif
101
102 #define DEFAULT_PATH "/usr/bin:/bin"
103 #define DEFAULT_CONTEXT_SIZE 255 /* first guess at context size */
104
105 extern char **environ;
106
107 /**
108 * Construct from the current range and specified desired level a resulting
109 * range. If the specified level is a range, return that. If it is not, then
110 * construct a range with level as the sensitivity and clearance of the current
111 * context.
112 *
113 * newlevel - the level specified on the command line
114 * range - the range in the current context
115 *
116 * Returns malloc'd memory
117 */
build_new_range(char * newlevel,const char * range)118 static char *build_new_range(char *newlevel, const char *range)
119 {
120 char *newrangep = NULL;
121 const char *tmpptr;
122 size_t len;
123
124 /* a missing or empty string */
125 if (!range || !strlen(range) || !newlevel || !strlen(newlevel))
126 return NULL;
127
128 /* if the newlevel is actually a range - just use that */
129 if (strchr(newlevel, '-')) {
130 newrangep = strdup(newlevel);
131 return newrangep;
132 }
133
134 /* look for MLS range in current context */
135 tmpptr = strchr(range, '-');
136 if (tmpptr) {
137 /* we are inserting into a ranged MLS context */
138 len = strlen(newlevel) + 1 + strlen(tmpptr + 1) + 1;
139 newrangep = (char *)malloc(len);
140 if (!newrangep)
141 return NULL;
142 snprintf(newrangep, len, "%s-%s", newlevel, tmpptr + 1);
143 } else {
144 /* we are inserting into a currently non-ranged MLS context */
145 if (!strcmp(newlevel, range)) {
146 newrangep = strdup(range);
147 } else {
148 len = strlen(newlevel) + 1 + strlen(range) + 1;
149 newrangep = (char *)malloc(len);
150 if (!newrangep)
151 return NULL;
152 snprintf(newrangep, len, "%s-%s", newlevel, range);
153 }
154 }
155
156 return newrangep;
157 }
158
159 #ifdef USE_PAM
160
161 /************************************************************************
162 *
163 * All PAM code goes in this section.
164 *
165 ************************************************************************/
166 #include <security/pam_appl.h> /* for PAM functions */
167 #include <security/pam_misc.h> /* for misc_conv PAM utility function */
168
169 const char *service_name = "newrole";
170
171 /* authenticate_via_pam()
172 *
173 * in: pw - struct containing data from our user's line in
174 * the passwd file.
175 * out: nothing
176 * return: value condition
177 * ----- ---------
178 * 1 PAM thinks that the user authenticated themselves properly
179 * 0 otherwise
180 *
181 * This function uses PAM to authenticate the user running this
182 * program. This is the only function in this program that makes PAM
183 * calls.
184 */
authenticate_via_pam(const char * ttyn,pam_handle_t * pam_handle)185 int authenticate_via_pam(const char *ttyn, pam_handle_t * pam_handle)
186 {
187
188 int result = 0; /* set to 0 (not authenticated) by default */
189 int pam_rc; /* pam return code */
190 const char *tty_name;
191
192 if (ttyn) {
193 if (strncmp(ttyn, "/dev/", 5) == 0)
194 tty_name = ttyn + 5;
195 else
196 tty_name = ttyn;
197
198 pam_rc = pam_set_item(pam_handle, PAM_TTY, tty_name);
199 if (pam_rc != PAM_SUCCESS) {
200 fprintf(stderr, _("failed to set PAM_TTY\n"));
201 goto out;
202 }
203 }
204
205 /* Ask PAM to authenticate the user running this program */
206 pam_rc = pam_authenticate(pam_handle, 0);
207 if (pam_rc != PAM_SUCCESS) {
208 goto out;
209 }
210
211 /* Ask PAM to verify acct_mgmt */
212 pam_rc = pam_acct_mgmt(pam_handle, 0);
213 if (pam_rc == PAM_SUCCESS) {
214 result = 1; /* user authenticated OK! */
215 }
216
217 out:
218 return result;
219 } /* authenticate_via_pam() */
220
221 #include "hashtab.h"
222
free_hashtab_entry(hashtab_key_t key,hashtab_datum_t d,void * args)223 static int free_hashtab_entry(hashtab_key_t key, hashtab_datum_t d,
224 void *args __attribute__ ((unused)))
225 {
226 free(key);
227 free(d);
228 return 0;
229 }
230
reqsymhash(hashtab_t h,const_hashtab_key_t key)231 static unsigned int reqsymhash(hashtab_t h, const_hashtab_key_t key)
232 {
233 char *p, *keyp;
234 size_t size;
235 unsigned int val;
236
237 val = 0;
238 keyp = (char *)key;
239 size = strlen(keyp);
240 for (p = keyp; ((size_t) (p - keyp)) < size; p++)
241 val =
242 (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p);
243 return val & (h->size - 1);
244 }
245
reqsymcmp(hashtab_t h,const_hashtab_key_t key1,const_hashtab_key_t key2)246 static int reqsymcmp(hashtab_t h
247 __attribute__ ((unused)), const_hashtab_key_t key1,
248 const_hashtab_key_t key2)
249 {
250 return strcmp(key1, key2);
251 }
252
253 static hashtab_t app_service_names = NULL;
254 #define PAM_SERVICE_SLOTS 64
255
process_pam_config(FILE * cfg)256 static int process_pam_config(FILE * cfg)
257 {
258 const char *config_file_path = PAM_SERVICE_CONFIG;
259 char *line_buf = NULL;
260 unsigned long lineno = 0;
261 size_t len = 0;
262 char *app = NULL;
263 char *service = NULL;
264 int ret;
265
266 while (getline(&line_buf, &len, cfg) > 0) {
267 char *buffer = line_buf;
268 lineno++;
269 while (isspace(*buffer))
270 buffer++;
271 if (buffer[0] == '#')
272 continue;
273 if (buffer[0] == '\n' || buffer[0] == '\0')
274 continue;
275
276 app = service = NULL;
277 ret = sscanf(buffer, "%ms %ms\n", &app, &service);
278 if (ret < 2 || !app || !service)
279 goto err;
280
281 ret = hashtab_insert(app_service_names, app, service);
282 if (ret == HASHTAB_OVERFLOW) {
283 fprintf(stderr,
284 _
285 ("newrole: service name configuration hashtable overflow\n"));
286 goto err;
287 }
288 }
289
290 free(line_buf);
291 return 0;
292 err:
293 free(app);
294 free(service);
295 fprintf(stderr, _("newrole: %s: error on line %lu.\n"),
296 config_file_path, lineno);
297 free(line_buf);
298 return -1;
299 }
300
301 /*
302 * Read config file ignoring comment lines.
303 * Files specified one per line executable with a corresponding
304 * pam service name.
305 */
read_pam_config(void)306 static int read_pam_config(void)
307 {
308 const char *config_file_path = PAM_SERVICE_CONFIG;
309 FILE *cfg = NULL;
310 cfg = fopen(config_file_path, "r");
311 if (!cfg)
312 return 0; /* This configuration is optional. */
313 app_service_names =
314 hashtab_create(reqsymhash, reqsymcmp, PAM_SERVICE_SLOTS);
315 if (!app_service_names)
316 goto err;
317 if (process_pam_config(cfg))
318 goto err;
319 fclose(cfg);
320 return 0;
321 err:
322 fclose(cfg);
323 return -1;
324 }
325
326 #else /* else !USE_PAM */
327
328 /************************************************************************
329 *
330 * All shadow passwd code goes in this section.
331 *
332 ************************************************************************/
333 #include <shadow.h> /* for shadow passwd functions */
334 #include <string.h> /* for strlen(), memset() */
335
336 #define PASSWORD_PROMPT _("Password:") /* prompt for getpass() */
337
338 /* authenticate_via_shadow_passwd()
339 *
340 * in: uname - the calling user's user name
341 * out: nothing
342 * return: value condition
343 * ----- ---------
344 * 1 user authenticated themselves properly according to the
345 * shadow passwd file.
346 * 0 otherwise
347 *
348 * This function uses the shadow passwd file to thenticate the user running
349 * this program.
350 */
authenticate_via_shadow_passwd(const char * uname)351 int authenticate_via_shadow_passwd(const char *uname)
352 {
353 struct spwd *p_shadow_line;
354 char *unencrypted_password_s;
355 char *encrypted_password_s;
356
357 setspent();
358 p_shadow_line = getspnam(uname);
359 endspent();
360 if (!(p_shadow_line)) {
361 fprintf(stderr, _("Cannot find your entry in the shadow "
362 "passwd file.\n"));
363 return 0;
364 }
365
366 /* Ask user to input unencrypted password */
367 if (!(unencrypted_password_s = getpass(PASSWORD_PROMPT))) {
368 fprintf(stderr, _("getpass cannot open /dev/tty\n"));
369 return 0;
370 }
371
372 /* Use crypt() to encrypt user's input password. */
373 encrypted_password_s = crypt(unencrypted_password_s,
374 p_shadow_line->sp_pwdp);
375 memset(unencrypted_password_s, 0, strlen(unencrypted_password_s));
376 return (!strcmp(encrypted_password_s, p_shadow_line->sp_pwdp));
377 }
378 #endif /* if/else USE_PAM */
379
380 /**
381 * This function checks to see if the shell is known in /etc/shells.
382 * If so, it returns 1. On error or illegal shell, it returns 0.
383 */
verify_shell(const char * shell_name)384 static int verify_shell(const char *shell_name)
385 {
386 int found = 0;
387 const char *buf;
388
389 if (!(shell_name && shell_name[0]))
390 return found;
391
392 while ((buf = getusershell()) != NULL) {
393 /* ignore comments */
394 if (*buf == '#')
395 continue;
396
397 /* check the shell skipping newline char */
398 if (!strcmp(shell_name, buf)) {
399 found = 1;
400 break;
401 }
402 }
403 endusershell();
404 return found;
405 }
406
407 /**
408 * Determine the Linux user identity to re-authenticate.
409 * If supported and set, use the login uid, as this should be more stable.
410 * Otherwise, use the real uid.
411 *
412 * This function assigns malloc'd memory into the pw_copy struct.
413 * Returns zero on success, non-zero otherwise
414 */
extract_pw_data(struct passwd * pw_copy)415 int extract_pw_data(struct passwd *pw_copy)
416 {
417 uid_t uid;
418 struct passwd *pw;
419
420 #ifdef USE_AUDIT
421 uid = audit_getloginuid();
422 if (uid == (uid_t) - 1)
423 uid = getuid();
424 #else
425 uid = getuid();
426 #endif
427
428 setpwent();
429 pw = getpwuid(uid);
430 endpwent();
431 if (!(pw && pw->pw_name && pw->pw_name[0] && pw->pw_shell
432 && pw->pw_shell[0] && pw->pw_dir && pw->pw_dir[0])) {
433 fprintf(stderr,
434 _("cannot find valid entry in the passwd file.\n"));
435 return -1;
436 }
437
438 *pw_copy = *pw;
439 pw = pw_copy;
440 pw->pw_name = strdup(pw->pw_name);
441 pw->pw_dir = strdup(pw->pw_dir);
442 pw->pw_shell = strdup(pw->pw_shell);
443
444 if (!(pw->pw_name && pw->pw_dir && pw->pw_shell)) {
445 fprintf(stderr, _("Out of memory!\n"));
446 goto out_free;
447 }
448
449 if (verify_shell(pw->pw_shell) == 0) {
450 fprintf(stderr, _("Error! Shell is not valid.\n"));
451 goto out_free;
452 }
453 return 0;
454
455 out_free:
456 free(pw->pw_name);
457 free(pw->pw_dir);
458 free(pw->pw_shell);
459 return -1;
460 }
461
462 /**
463 * Either restore the original environment, or set up a minimal one.
464 *
465 * The minimal environment contains:
466 * TERM, DISPLAY and XAUTHORITY - if they are set, preserve values
467 * HOME, SHELL, USER and LOGNAME - set to contents of /etc/passwd
468 * PATH - set to default value DEFAULT_PATH
469 *
470 * Returns zero on success, non-zero otherwise
471 */
restore_environment(int preserve_environment,char ** old_environ,const struct passwd * pw)472 static int restore_environment(int preserve_environment,
473 char **old_environ, const struct passwd *pw)
474 {
475 char const *term_env;
476 char const *display_env;
477 char const *xauthority_env;
478 char *term = NULL; /* temporary container */
479 char *display = NULL; /* temporary container */
480 char *xauthority = NULL; /* temporary container */
481 int rc;
482
483 environ = old_environ;
484
485 if (preserve_environment)
486 return 0;
487
488 term_env = getenv("TERM");
489 display_env = getenv("DISPLAY");
490 xauthority_env = getenv("XAUTHORITY");
491
492 /* Save the variable values we want */
493 if (term_env)
494 term = strdup(term_env);
495 if (display_env)
496 display = strdup(display_env);
497 if (xauthority_env)
498 xauthority = strdup(xauthority_env);
499 if ((term_env && !term) || (display_env && !display) ||
500 (xauthority_env && !xauthority)) {
501 rc = -1;
502 goto out;
503 }
504
505 /* Construct a new environment */
506 if ((rc = clearenv())) {
507 fprintf(stderr, _("Unable to clear environment\n"));
508 goto out;
509 }
510
511 /* Restore that which we saved */
512 if (term)
513 rc |= setenv("TERM", term, 1);
514 if (display)
515 rc |= setenv("DISPLAY", display, 1);
516 if (xauthority)
517 rc |= setenv("XAUTHORITY", xauthority, 1);
518 rc |= setenv("HOME", pw->pw_dir, 1);
519 rc |= setenv("SHELL", pw->pw_shell, 1);
520 rc |= setenv("USER", pw->pw_name, 1);
521 rc |= setenv("LOGNAME", pw->pw_name, 1);
522 rc |= setenv("PATH", DEFAULT_PATH, 1);
523 out:
524 free(term);
525 free(display);
526 free(xauthority);
527 return rc;
528 }
529
530 /**
531 * This function will drop the capabilities so that we are left
532 * only with access to the audit system. If the user is root, we leave
533 * the capabilities alone since they already should have access to the
534 * audit netlink socket.
535 *
536 * Returns zero on success, non-zero otherwise
537 */
538 #if defined(AUDIT_LOG_PRIV) && !defined(NAMESPACE_PRIV)
drop_capabilities(int full)539 static int drop_capabilities(int full)
540 {
541 uid_t uid = getuid();
542 if (!uid) return 0;
543
544 capng_setpid(getpid());
545 capng_clear(CAPNG_SELECT_CAPS);
546
547 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
548 fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n"));
549 return -1;
550 }
551
552 /* Change uid */
553 if (setresuid(uid, uid, uid)) {
554 fprintf(stderr, _("Error changing uid, aborting.\n"));
555 return -1;
556 }
557
558 if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0) {
559 fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n"));
560 return -1;
561 }
562
563 if (! full)
564 capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_AUDIT_WRITE);
565 return capng_apply(CAPNG_SELECT_CAPS);
566 }
567 #elif defined(NAMESPACE_PRIV)
568 /**
569 * This function will drop the capabilities so that we are left
570 * only with access to the audit system and the ability to raise
571 * CAP_SYS_ADMIN, CAP_DAC_OVERRIDE, CAP_FOWNER and CAP_CHOWN,
572 * before invoking pam_namespace. These capabilities are needed
573 * for performing bind mounts/unmounts and to create potential new
574 * instance directories with appropriate DAC attributes. If the
575 * user is root, we leave the capabilities alone since they already
576 * should have access to the audit netlink socket and should have
577 * the ability to create/mount/unmount instance directories.
578 *
579 * Returns zero on success, non-zero otherwise
580 */
drop_capabilities(int full)581 static int drop_capabilities(int full)
582 {
583 uid_t uid = getuid();
584 if (!uid) return 0;
585
586 capng_setpid(getpid());
587 capng_clear(CAPNG_SELECT_CAPS);
588
589 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
590 fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n"));
591 return -1;
592 }
593
594 /* Change uid */
595 if (setresuid(uid, uid, uid)) {
596 fprintf(stderr, _("Error changing uid, aborting.\n"));
597 return -1;
598 }
599
600 if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0) {
601 fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n"));
602 return -1;
603 }
604
605 if (! full)
606 capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_SYS_ADMIN , CAP_FOWNER , CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_AUDIT_WRITE, -1);
607
608 return capng_apply(CAPNG_SELECT_CAPS);
609 }
610
611 #else
drop_capabilities(int full)612 static inline int drop_capabilities(__attribute__ ((__unused__)) int full)
613 {
614 return 0;
615 }
616 #endif
617
618 #ifdef NAMESPACE_PRIV
619 /**
620 * This function will set the uid values to be that of caller's uid, and
621 * will drop any privilages which maybe have been raised.
622 */
transition_to_caller_uid()623 static int transition_to_caller_uid()
624 {
625 uid_t uid = getuid();
626
627 if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0) {
628 fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n"));
629 return -1;
630 }
631
632 if (setresuid(uid, uid, uid)) {
633 fprintf(stderr, _("Error changing uid, aborting.\n"));
634 return -1;
635 }
636 return 0;
637 }
638 #endif
639
640 #ifdef AUDIT_LOG_PRIV
641 /* Send audit message */
642 static
send_audit_message(int success,security_context_t old_context,security_context_t new_context,const char * ttyn)643 int send_audit_message(int success, security_context_t old_context,
644 security_context_t new_context, const char *ttyn)
645 {
646 char *msg = NULL;
647 int rc;
648 int audit_fd = audit_open();
649
650 if (audit_fd < 0) {
651 fprintf(stderr, _("Error connecting to audit system.\n"));
652 return -1;
653 }
654 if (asprintf(&msg, "newrole: old-context=%s new-context=%s",
655 old_context ? old_context : "?",
656 new_context ? new_context : "?") < 0) {
657 fprintf(stderr, _("Error allocating memory.\n"));
658 rc = -1;
659 goto out;
660 }
661 rc = audit_log_user_message(audit_fd, AUDIT_USER_ROLE_CHANGE,
662 msg, NULL, NULL, ttyn, success);
663 if (rc <= 0) {
664 fprintf(stderr, _("Error sending audit message.\n"));
665 rc = -1;
666 goto out;
667 }
668 rc = 0;
669 out:
670 free(msg);
671 close(audit_fd);
672 return rc;
673 }
674 #else
675 static inline
send_audit_message(int success,security_context_t old_context,security_context_t new_context,const char * ttyn)676 int send_audit_message(int success __attribute__ ((unused)),
677 security_context_t old_context
678 __attribute__ ((unused)),
679 security_context_t new_context
680 __attribute__ ((unused)), const char *ttyn
681 __attribute__ ((unused)))
682 {
683 return 0;
684 }
685 #endif
686
687 /**
688 * This function attempts to relabel the tty. If this function fails, then
689 * the fd is closed, the contexts are free'd and -1 is returned. On success,
690 * a valid fd is returned and tty_context and new_tty_context are set.
691 *
692 * This function will not fail if it can not relabel the tty when selinux is
693 * in permissive mode.
694 */
relabel_tty(const char * ttyn,security_context_t new_context,security_context_t * tty_context,security_context_t * new_tty_context)695 static int relabel_tty(const char *ttyn, security_context_t new_context,
696 security_context_t * tty_context,
697 security_context_t * new_tty_context)
698 {
699 int fd, rc;
700 int enforcing = security_getenforce();
701 security_context_t tty_con = NULL;
702 security_context_t new_tty_con = NULL;
703
704 if (!ttyn)
705 return 0;
706
707 if (enforcing < 0) {
708 fprintf(stderr, _("Could not determine enforcing mode.\n"));
709 return -1;
710 }
711
712 /* Re-open TTY descriptor */
713 fd = open(ttyn, O_RDWR | O_NONBLOCK);
714 if (fd < 0) {
715 fprintf(stderr, _("Error! Could not open %s.\n"), ttyn);
716 return fd;
717 }
718 /* this craziness is to make sure we cann't block on open and deadlock */
719 rc = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
720 if (rc) {
721 fprintf(stderr, _("Error! Could not clear O_NONBLOCK on %s\n"), ttyn);
722 close(fd);
723 return rc;
724 }
725
726 if (fgetfilecon(fd, &tty_con) < 0) {
727 fprintf(stderr, _("%s! Could not get current context "
728 "for %s, not relabeling tty.\n"),
729 enforcing ? "Error" : "Warning", ttyn);
730 if (enforcing)
731 goto close_fd;
732 }
733
734 if (tty_con &&
735 (security_compute_relabel(new_context, tty_con,
736 string_to_security_class("chr_file"), &new_tty_con) < 0)) {
737 fprintf(stderr, _("%s! Could not get new context for %s, "
738 "not relabeling tty.\n"),
739 enforcing ? "Error" : "Warning", ttyn);
740 if (enforcing)
741 goto close_fd;
742 }
743
744 if (new_tty_con)
745 if (fsetfilecon(fd, new_tty_con) < 0) {
746 fprintf(stderr,
747 _("%s! Could not set new context for %s\n"),
748 enforcing ? "Error" : "Warning", ttyn);
749 freecon(new_tty_con);
750 new_tty_con = NULL;
751 if (enforcing)
752 goto close_fd;
753 }
754
755 *tty_context = tty_con;
756 *new_tty_context = new_tty_con;
757 return fd;
758
759 close_fd:
760 freecon(tty_con);
761 close(fd);
762 return -1;
763 }
764
765 /**
766 * This function attempts to revert the relabeling done to the tty.
767 * fd - referencing the opened ttyn
768 * ttyn - name of tty to restore
769 * tty_context - original context of the tty
770 * new_tty_context - context tty was relabeled to
771 *
772 * Returns zero on success, non-zero otherwise
773 */
restore_tty_label(int fd,const char * ttyn,security_context_t tty_context,security_context_t new_tty_context)774 static int restore_tty_label(int fd, const char *ttyn,
775 security_context_t tty_context,
776 security_context_t new_tty_context)
777 {
778 int rc = 0;
779 security_context_t chk_tty_context = NULL;
780
781 if (!ttyn)
782 goto skip_relabel;
783
784 if (!new_tty_context)
785 goto skip_relabel;
786
787 /* Verify that the tty still has the context set by newrole. */
788 if ((rc = fgetfilecon(fd, &chk_tty_context)) < 0) {
789 fprintf(stderr, "Could not fgetfilecon %s.\n", ttyn);
790 goto skip_relabel;
791 }
792
793 if ((rc = strcmp(chk_tty_context, new_tty_context))) {
794 fprintf(stderr, _("%s changed labels.\n"), ttyn);
795 goto skip_relabel;
796 }
797
798 if ((rc = fsetfilecon(fd, tty_context)) < 0)
799 fprintf(stderr,
800 _("Warning! Could not restore context for %s\n"), ttyn);
801 skip_relabel:
802 freecon(chk_tty_context);
803 return rc;
804 }
805
806 /**
807 * Parses and validates the provided command line options and
808 * constructs a new context based on our old context and the
809 * arguments specified on the command line. On success
810 * new_context will be set to valid values, otherwise its value
811 * is left unchanged.
812 *
813 * Returns zero on success, non-zero otherwise.
814 */
parse_command_line_arguments(int argc,char ** argv,char * ttyn,security_context_t old_context,security_context_t * new_context,int * preserve_environment)815 static int parse_command_line_arguments(int argc, char **argv, char *ttyn,
816 security_context_t old_context,
817 security_context_t * new_context,
818 int *preserve_environment)
819 {
820 int flag_index; /* flag index in argv[] */
821 int clflag; /* holds codes for command line flags */
822 char *role_s = NULL; /* role spec'd by user in argv[] */
823 char *type_s = NULL; /* type spec'd by user in argv[] */
824 char *type_ptr = NULL; /* stores malloc'd data from get_default_type */
825 char *level_s = NULL; /* level spec'd by user in argv[] */
826 char *range_ptr = NULL;
827 security_context_t new_con = NULL;
828 security_context_t tty_con = NULL;
829 context_t context = NULL; /* manipulatable form of new_context */
830 const struct option long_options[] = {
831 {"role", 1, 0, 'r'},
832 {"type", 1, 0, 't'},
833 {"level", 1, 0, 'l'},
834 {"preserve-environment", 0, 0, 'p'},
835 {"version", 0, 0, 'V'},
836 {NULL, 0, 0, 0}
837 };
838
839 *preserve_environment = 0;
840 while (1) {
841 clflag = getopt_long(argc, argv, "r:t:l:pV", long_options,
842 &flag_index);
843 if (clflag == -1)
844 break;
845
846 switch (clflag) {
847 case 'V':
848 printf("newrole: %s version %s\n", PACKAGE, VERSION);
849 exit(0);
850 break;
851 case 'p':
852 *preserve_environment = 1;
853 break;
854 case 'r':
855 if (role_s) {
856 fprintf(stderr,
857 _("Error: multiple roles specified\n"));
858 return -1;
859 }
860 role_s = optarg;
861 break;
862 case 't':
863 if (type_s) {
864 fprintf(stderr,
865 _("Error: multiple types specified\n"));
866 return -1;
867 }
868 type_s = optarg;
869 break;
870 case 'l':
871 if (!is_selinux_mls_enabled()) {
872 fprintf(stderr, _("Sorry, -l may be used with "
873 "SELinux MLS support.\n"));
874 return -1;
875 }
876 if (level_s) {
877 fprintf(stderr, _("Error: multiple levels "
878 "specified\n"));
879 return -1;
880 }
881 if (ttyn) {
882 if (fgetfilecon(STDIN_FILENO, &tty_con) >= 0) {
883 if (selinux_check_securetty_context
884 (tty_con) < 0) {
885 fprintf(stderr,
886 _
887 ("Error: you are not allowed to change levels on a non secure terminal \n"));
888 freecon(tty_con);
889 return -1;
890 }
891 freecon(tty_con);
892 }
893 }
894
895 level_s = optarg;
896 break;
897 default:
898 fprintf(stderr, "%s\n", USAGE_STRING);
899 return -1;
900 }
901 }
902
903 /* Verify that the combination of command-line arguments are viable */
904 if (!(role_s || type_s || level_s)) {
905 fprintf(stderr, "%s\n", USAGE_STRING);
906 return -1;
907 }
908
909 /* Fill in a default type if one hasn't been specified. */
910 if (role_s && !type_s) {
911 /* get_default_type() returns malloc'd memory */
912 if (get_default_type(role_s, &type_ptr)) {
913 fprintf(stderr, _("Couldn't get default type.\n"));
914 send_audit_message(0, old_context, new_con, ttyn);
915 return -1;
916 }
917 type_s = type_ptr;
918 }
919
920 /* Create a temporary new context structure we extract and modify */
921 context = context_new(old_context);
922 if (!context) {
923 fprintf(stderr, _("failed to get new context.\n"));
924 goto err_free;
925 }
926
927 /* Modify the temporary new context */
928 if (role_s)
929 if (context_role_set(context, role_s)) {
930 fprintf(stderr, _("failed to set new role %s\n"),
931 role_s);
932 goto err_free;
933 }
934
935 if (type_s)
936 if (context_type_set(context, type_s)) {
937 fprintf(stderr, _("failed to set new type %s\n"),
938 type_s);
939 goto err_free;
940 }
941
942 if (level_s) {
943 range_ptr =
944 build_new_range(level_s, context_range_get(context));
945 if (!range_ptr) {
946 fprintf(stderr,
947 _("failed to build new range with level %s\n"),
948 level_s);
949 goto err_free;
950 }
951 if (context_range_set(context, range_ptr)) {
952 fprintf(stderr, _("failed to set new range %s\n"),
953 range_ptr);
954 goto err_free;
955 }
956 }
957
958 /* Construct the final new context */
959 if (!(new_con = context_str(context))) {
960 fprintf(stderr, _("failed to convert new context to string\n"));
961 goto err_free;
962 }
963
964 if (security_check_context(new_con) < 0) {
965 fprintf(stderr, _("%s is not a valid context\n"), new_con);
966 send_audit_message(0, old_context, new_con, ttyn);
967 goto err_free;
968 }
969
970 *new_context = strdup(new_con);
971 if (!*new_context) {
972 fprintf(stderr, _("Unable to allocate memory for new_context"));
973 goto err_free;
974 }
975
976 free(type_ptr);
977 free(range_ptr);
978 context_free(context);
979 return 0;
980
981 err_free:
982 free(type_ptr);
983 free(range_ptr);
984 /* Don't free new_con, context_free(context) handles this */
985 context_free(context);
986 return -1;
987 }
988
989 /**
990 * Take care of any signal setup
991 */
set_signal_handles(void)992 static int set_signal_handles(void)
993 {
994 sigset_t empty;
995
996 /* Empty the signal mask in case someone is blocking a signal */
997 if (sigemptyset(&empty)) {
998 fprintf(stderr, _("Unable to obtain empty signal set\n"));
999 return -1;
1000 }
1001
1002 (void)sigprocmask(SIG_SETMASK, &empty, NULL);
1003
1004 /* Terminate on SIGHUP. */
1005 if (signal(SIGHUP, SIG_DFL) == SIG_ERR) {
1006 fprintf(stderr, _("Unable to set SIGHUP handler\n"));
1007 return -1;
1008 }
1009
1010 return 0;
1011 }
1012
1013 /************************************************************************
1014 *
1015 * All code used for both PAM and shadow passwd goes in this section.
1016 *
1017 ************************************************************************/
1018
main(int argc,char * argv[])1019 int main(int argc, char *argv[])
1020 {
1021 security_context_t new_context = NULL; /* target security context */
1022 security_context_t old_context = NULL; /* original securiy context */
1023 security_context_t tty_context = NULL; /* current context of tty */
1024 security_context_t new_tty_context = NULL; /* new context of tty */
1025
1026 struct passwd pw; /* struct derived from passwd file line */
1027 char *ttyn = NULL; /* tty path */
1028
1029 char **old_environ;
1030 int preserve_environment;
1031
1032 int fd;
1033 pid_t childPid = 0;
1034 char *shell_argv0 = NULL;
1035 int rc;
1036
1037 #ifdef USE_PAM
1038 int pam_status; /* pam return code */
1039 pam_handle_t *pam_handle; /* opaque handle used by all PAM functions */
1040
1041 /* This is a jump table of functions for PAM to use when it wants to *
1042 * communicate with the user. We'll be using misc_conv(), which is *
1043 * provided for us via pam_misc.h. */
1044 struct pam_conv pam_conversation = {
1045 misc_conv,
1046 NULL
1047 };
1048 #endif
1049
1050 /*
1051 * Step 0: Setup
1052 *
1053 * Do some intial setup, including dropping capabilities, checking
1054 * if it makes sense to continue to run newrole, and setting up
1055 * a scrubbed environment.
1056 */
1057 if (drop_capabilities(FALSE)) {
1058 perror(_("Sorry, newrole failed to drop capabilities\n"));
1059 return -1;
1060 }
1061 if (set_signal_handles())
1062 return -1;
1063
1064 #ifdef USE_NLS
1065 setlocale(LC_ALL, "");
1066 bindtextdomain(PACKAGE, LOCALEDIR);
1067 textdomain(PACKAGE);
1068 #endif
1069
1070 old_environ = environ;
1071 environ = NULL;
1072
1073 if (!is_selinux_enabled()) {
1074 fprintf(stderr, _("Sorry, newrole may be used only on "
1075 "a SELinux kernel.\n"));
1076 return -1;
1077 }
1078
1079 if (security_getenforce() < 0) {
1080 fprintf(stderr, _("Could not determine enforcing mode.\n"));
1081 return -1;
1082 }
1083
1084 /*
1085 * Step 1: Parse command line and valid arguments
1086 *
1087 * old_context and ttyn are required for audit logging,
1088 * context validation and pam
1089 */
1090 if (getprevcon(&old_context)) {
1091 fprintf(stderr, _("failed to get old_context.\n"));
1092 return -1;
1093 }
1094
1095 ttyn = ttyname(STDIN_FILENO);
1096 if (!ttyn || *ttyn == '\0') {
1097 fprintf(stderr,
1098 _("Warning! Could not retrieve tty information.\n"));
1099 }
1100
1101 if (parse_command_line_arguments(argc, argv, ttyn, old_context,
1102 &new_context, &preserve_environment))
1103 return -1;
1104
1105 /*
1106 * Step 2: Authenticate the user.
1107 *
1108 * Re-authenticate the user running this program.
1109 * This is just to help confirm user intent (vs. invocation by
1110 * malicious software), not to authorize the operation (which is covered
1111 * by policy). Trusted path mechanism would be preferred.
1112 */
1113 if (extract_pw_data(&pw))
1114 goto err_free;
1115
1116 #ifdef USE_PAM
1117 if (read_pam_config()) {
1118 fprintf(stderr,
1119 _("error on reading PAM service configuration.\n"));
1120 goto err_free;
1121 }
1122
1123 if (app_service_names != NULL && optind < argc) {
1124 if (strcmp(argv[optind], "-c") == 0 && optind < (argc - 1)) {
1125 /*
1126 * Check for a separate pam service name for the
1127 * command when invoked by newrole.
1128 */
1129 char *cmd = NULL;
1130 rc = sscanf(argv[optind + 1], "%ms", &cmd);
1131 if (rc != EOF && cmd) {
1132 char *app_service_name =
1133 (char *)hashtab_search(app_service_names,
1134 cmd);
1135 free(cmd);
1136 if (app_service_name != NULL)
1137 service_name = app_service_name;
1138 }
1139 }
1140 }
1141
1142 pam_status = pam_start(service_name, pw.pw_name, &pam_conversation,
1143 &pam_handle);
1144 if (pam_status != PAM_SUCCESS) {
1145 fprintf(stderr, _("failed to initialize PAM\n"));
1146 goto err_free;
1147 }
1148
1149 if (!authenticate_via_pam(ttyn, pam_handle))
1150 #else
1151 if (!authenticate_via_shadow_passwd(pw.pw_name))
1152 #endif
1153 {
1154 fprintf(stderr, _("newrole: incorrect password for %s\n"),
1155 pw.pw_name);
1156 send_audit_message(0, old_context, new_context, ttyn);
1157 goto err_close_pam;
1158 }
1159
1160 /*
1161 * Step 3: Handle relabeling of the tty.
1162 *
1163 * Once we authenticate the user, we know that we want to proceed with
1164 * the action. Prior to this point, no changes are made the to system.
1165 */
1166 fd = relabel_tty(ttyn, new_context, &tty_context, &new_tty_context);
1167 if (fd < 0)
1168 goto err_close_pam;
1169
1170 /*
1171 * Step 4: Fork
1172 *
1173 * Fork, allowing parent to clean up after shell has executed.
1174 * Child: reopen stdin, stdout, stderr and exec shell
1175 * Parnet: wait for child to die and restore tty's context
1176 */
1177 childPid = fork();
1178 if (childPid < 0) {
1179 /* fork failed, no child to worry about */
1180 int errsv = errno;
1181 fprintf(stderr, _("newrole: failure forking: %s"),
1182 strerror(errsv));
1183 if (restore_tty_label(fd, ttyn, tty_context, new_tty_context))
1184 fprintf(stderr, _("Unable to restore tty label...\n"));
1185 if (close(fd))
1186 fprintf(stderr, _("Failed to close tty properly\n"));
1187 goto err_close_pam;
1188 } else if (childPid) {
1189 /* PARENT
1190 * It doesn't make senes to exit early on errors at this point,
1191 * since we are doing cleanup which needs to be done.
1192 * We can exit with a bad rc though
1193 */
1194 pid_t pid;
1195 int exit_code = 0;
1196 int status;
1197
1198 do {
1199 pid = wait(&status);
1200 } while (pid < 0 && errno == EINTR);
1201
1202 /* Preserve child exit status, unless there is another error. */
1203 if (WIFEXITED(status))
1204 exit_code = WEXITSTATUS(status);
1205
1206 if (restore_tty_label(fd, ttyn, tty_context, new_tty_context)) {
1207 fprintf(stderr, _("Unable to restore tty label...\n"));
1208 exit_code = -1;
1209 }
1210 freecon(tty_context);
1211 freecon(new_tty_context);
1212 if (close(fd)) {
1213 fprintf(stderr, _("Failed to close tty properly\n"));
1214 exit_code = -1;
1215 }
1216 #ifdef USE_PAM
1217 #ifdef NAMESPACE_PRIV
1218 pam_status = pam_close_session(pam_handle, 0);
1219 if (pam_status != PAM_SUCCESS) {
1220 fprintf(stderr, "pam_close_session failed with %s\n",
1221 pam_strerror(pam_handle, pam_status));
1222 exit_code = -1;
1223 }
1224 #endif
1225 rc = pam_end(pam_handle, pam_status);
1226 if (rc != PAM_SUCCESS) {
1227 fprintf(stderr, "pam_end failed with %s\n",
1228 pam_strerror(pam_handle, rc));
1229 exit_code = -1;
1230 }
1231 hashtab_map(app_service_names, free_hashtab_entry, NULL);
1232 hashtab_destroy(app_service_names);
1233 #endif
1234 free(pw.pw_name);
1235 free(pw.pw_dir);
1236 free(pw.pw_shell);
1237 free(shell_argv0);
1238 return exit_code;
1239 }
1240
1241 /* CHILD */
1242 /* Close the tty and reopen descriptors 0 through 2 */
1243 if (ttyn) {
1244 if (close(fd) || close(0) || close(1) || close(2)) {
1245 fprintf(stderr, _("Could not close descriptors.\n"));
1246 goto err_close_pam;
1247 }
1248 fd = open(ttyn, O_RDWR | O_NONBLOCK);
1249 if (fd != 0)
1250 goto err_close_pam;
1251 rc = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
1252 if (rc)
1253 goto err_close_pam;
1254
1255 fd = open(ttyn, O_RDWR | O_NONBLOCK);
1256 if (fd != 1)
1257 goto err_close_pam;
1258 rc = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
1259 if (rc)
1260 goto err_close_pam;
1261
1262 fd = open(ttyn, O_RDWR | O_NONBLOCK);
1263 if (fd != 2)
1264 goto err_close_pam;
1265 rc = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
1266 if (rc)
1267 goto err_close_pam;
1268
1269 }
1270 /*
1271 * Step 5: Execute a new shell with the new context in `new_context'.
1272 *
1273 * Establish context, namesapce and any options for the new shell
1274 */
1275 if (optind < 1)
1276 optind = 1;
1277
1278 /* This is ugly, but use newrole's argv for the exec'd shells argv */
1279 if (asprintf(&shell_argv0, "-%s", pw.pw_shell) < 0) {
1280 fprintf(stderr, _("Error allocating shell's argv0.\n"));
1281 shell_argv0 = NULL;
1282 goto err_close_pam;
1283 }
1284 argv[optind - 1] = shell_argv0;
1285
1286 if (setexeccon(new_context)) {
1287 fprintf(stderr, _("Could not set exec context to %s.\n"),
1288 new_context);
1289 goto err_close_pam;
1290 }
1291 #ifdef NAMESPACE_PRIV
1292 /* Ask PAM to setup session for user running this program */
1293 pam_status = pam_open_session(pam_handle, 0);
1294 if (pam_status != PAM_SUCCESS) {
1295 fprintf(stderr, "pam_open_session failed with %s\n",
1296 pam_strerror(pam_handle, pam_status));
1297 goto err_close_pam;
1298 }
1299 #endif
1300
1301 if (send_audit_message(1, old_context, new_context, ttyn)) {
1302 fprintf(stderr, _("Failed to send audit message"));
1303 goto err_close_pam_session;
1304 }
1305 freecon(old_context); old_context=NULL;
1306 freecon(new_context); new_context=NULL;
1307
1308 #ifdef NAMESPACE_PRIV
1309 if (transition_to_caller_uid()) {
1310 fprintf(stderr, _("Failed to transition to namespace\n"));
1311 goto err_close_pam_session;
1312 }
1313 #endif
1314
1315 if (drop_capabilities(TRUE)) {
1316 fprintf(stderr, _("Failed to drop capabilities %m\n"));
1317 goto err_close_pam_session;
1318 }
1319 /* Handle environment changes */
1320 if (restore_environment(preserve_environment, old_environ, &pw)) {
1321 fprintf(stderr, _("Unable to restore the environment, "
1322 "aborting\n"));
1323 goto err_close_pam_session;
1324 }
1325 execv(pw.pw_shell, argv + optind - 1);
1326
1327 /*
1328 * Error path cleanup
1329 *
1330 * If we reach here, then we failed to exec the new shell.
1331 */
1332 perror(_("failed to exec shell\n"));
1333 err_close_pam_session:
1334 #ifdef NAMESPACE_PRIV
1335 pam_status = pam_close_session(pam_handle, 0);
1336 if (pam_status != PAM_SUCCESS)
1337 fprintf(stderr, "pam_close_session failed with %s\n",
1338 pam_strerror(pam_handle, pam_status));
1339 #endif
1340 err_close_pam:
1341 #ifdef USE_PAM
1342 rc = pam_end(pam_handle, pam_status);
1343 if (rc != PAM_SUCCESS)
1344 fprintf(stderr, "pam_end failed with %s\n",
1345 pam_strerror(pam_handle, rc));
1346 #endif
1347 err_free:
1348 freecon(tty_context);
1349 freecon(new_tty_context);
1350 freecon(old_context);
1351 freecon(new_context);
1352 free(pw.pw_name);
1353 free(pw.pw_dir);
1354 free(pw.pw_shell);
1355 free(shell_argv0);
1356 #ifdef USE_PAM
1357 if (app_service_names) {
1358 hashtab_map(app_service_names, free_hashtab_entry, NULL);
1359 hashtab_destroy(app_service_names);
1360 }
1361 #endif
1362 return -1;
1363 } /* main() */
1364