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