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