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