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 (at) redhat.com> 43 * Darrel Goeddel <DGoeddel (at) trustedcs.com> 44 * Michael Thompson <mcthomps (at) us.ibm.com> 45 * Dan Walsh <dwalsh (at) redhat.com> 46 * 47 *************************************************************************/ 48 49 #define _GNU_SOURCE 50 51 #if defined(AUDIT_LOG_PRIV) && !defined(USE_AUDIT) 52 #error AUDIT_LOG_PRIV needs the USE_AUDIT option 53 #endif 54 #if defined(NAMESPACE_PRIV) && !defined(USE_PAM) 55 #error NAMESPACE_PRIV needs the USE_PAM option 56 #endif 57 58 #include <stdio.h> 59 #include <stdlib.h> /* for malloc(), realloc(), free() */ 60 #include <pwd.h> /* for getpwuid() */ 61 #include <ctype.h> 62 #include <sys/types.h> /* to make getuid() and getpwuid() happy */ 63 #include <sys/wait.h> /* for wait() */ 64 #include <getopt.h> /* for getopt_long() form of getopt() */ 65 #include <fcntl.h> 66 #include <string.h> 67 #include <errno.h> 68 #include <selinux/selinux.h> /* for is_selinux_enabled() */ 69 #include <selinux/context.h> /* for context-mangling functions */ 70 #include <selinux/get_default_type.h> 71 #include <selinux/get_context_list.h> /* for SELINUX_DEFAULTUSER */ 72 #include <signal.h> 73 #include <unistd.h> /* for getuid(), exit(), getopt() */ 74 #ifdef USE_AUDIT 75 #include <libaudit.h> 76 #endif 77 #if defined(AUDIT_LOG_PRIV) || defined(NAMESPACE_PRIV) 78 #include <sys/prctl.h> 79 #include <cap-ng.h> 80 #endif 81 #ifdef USE_NLS 82 #include <locale.h> /* for setlocale() */ 83 #include <libintl.h> /* for gettext() */ 84 #define _(msgid) gettext (msgid) 85 #else 86 #define _(msgid) (msgid) 87 #endif 88 #ifndef PACKAGE 89 #define PACKAGE "policycoreutils" /* the name of this package lang translation */ 90 #endif 91 92 #define TRUE 1 93 #define FALSE 0 94 95 /* USAGE_STRING describes the command-line args of this program. */ 96 #define USAGE_STRING "USAGE: newrole [ -r role ] [ -t type ] [ -l level ] [ -p ] [ -V ] [ -- args ]" 97 98 #ifdef USE_PAM 99 #define PAM_SERVICE_CONFIG "/etc/selinux/newrole_pam.conf"; 100 #endif 101 102 #define DEFAULT_PATH "/usr/bin:/bin" 103 #define DEFAULT_CONTEXT_SIZE 255 /* first guess at context size */ 104 105 extern char **environ; 106 107 /** 108 * Construct from the current range and specified desired level a resulting 109 * range. If the specified level is a range, return that. If it is not, then 110 * construct a range with level as the sensitivity and clearance of the current 111 * context. 112 * 113 * newlevel - the level specified on the command line 114 * range - the range in the current context 115 * 116 * Returns malloc'd memory 117 */ 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 */ 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 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 231 static unsigned int reqsymhash(hashtab_t h, const_hashtab_key_t key) 232 { 233 char *p, *keyp; 234 size_t size; 235 unsigned int val; 236 237 val = 0; 238 keyp = (char *)key; 239 size = strlen(keyp); 240 for (p = keyp; ((size_t) (p - keyp)) < size; p++) 241 val = 242 (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p); 243 return val & (h->size - 1); 244 } 245 246 static int reqsymcmp(hashtab_t h 247 __attribute__ ((unused)), const_hashtab_key_t key1, 248 const_hashtab_key_t key2) 249 { 250 return strcmp(key1, key2); 251 } 252 253 static hashtab_t app_service_names = NULL; 254 #define PAM_SERVICE_SLOTS 64 255 256 static int process_pam_config(FILE * cfg) 257 { 258 const char *config_file_path = PAM_SERVICE_CONFIG; 259 char *line_buf = NULL; 260 unsigned long lineno = 0; 261 size_t len = 0; 262 char *app = NULL; 263 char *service = NULL; 264 int ret; 265 266 while (getline(&line_buf, &len, cfg) > 0) { 267 char *buffer = line_buf; 268 lineno++; 269 while (isspace(*buffer)) 270 buffer++; 271 if (buffer[0] == '#') 272 continue; 273 if (buffer[0] == '\n' || buffer[0] == '\0') 274 continue; 275 276 app = service = NULL; 277 ret = sscanf(buffer, "%ms %ms\n", &app, &service); 278 if (ret < 2 || !app || !service) 279 goto err; 280 281 ret = hashtab_insert(app_service_names, app, service); 282 if (ret == HASHTAB_OVERFLOW) { 283 fprintf(stderr, 284 _ 285 ("newrole: service name configuration hashtable overflow\n")); 286 goto err; 287 } 288 } 289 290 free(line_buf); 291 return 0; 292 err: 293 free(app); 294 free(service); 295 fprintf(stderr, _("newrole: %s: error on line %lu.\n"), 296 config_file_path, lineno); 297 free(line_buf); 298 return -1; 299 } 300 301 /* 302 * Read config file ignoring comment lines. 303 * Files specified one per line executable with a corresponding 304 * pam service name. 305 */ 306 static int read_pam_config(void) 307 { 308 const char *config_file_path = PAM_SERVICE_CONFIG; 309 FILE *cfg = NULL; 310 cfg = fopen(config_file_path, "r"); 311 if (!cfg) 312 return 0; /* This configuration is optional. */ 313 app_service_names = 314 hashtab_create(reqsymhash, reqsymcmp, PAM_SERVICE_SLOTS); 315 if (!app_service_names) 316 goto err; 317 if (process_pam_config(cfg)) 318 goto err; 319 fclose(cfg); 320 return 0; 321 err: 322 fclose(cfg); 323 return -1; 324 } 325 326 #else /* else !USE_PAM */ 327 328 /************************************************************************ 329 * 330 * All shadow passwd code goes in this section. 331 * 332 ************************************************************************/ 333 #include <shadow.h> /* for shadow passwd functions */ 334 #include <string.h> /* for strlen(), memset() */ 335 336 #define PASSWORD_PROMPT _("Password:") /* prompt for getpass() */ 337 338 /* authenticate_via_shadow_passwd() 339 * 340 * in: uname - the calling user's user name 341 * out: nothing 342 * return: value condition 343 * ----- --------- 344 * 1 user authenticated themselves properly according to the 345 * shadow passwd file. 346 * 0 otherwise 347 * 348 * This function uses the shadow passwd file to thenticate the user running 349 * this program. 350 */ 351 int authenticate_via_shadow_passwd(const char *uname) 352 { 353 struct spwd *p_shadow_line; 354 char *unencrypted_password_s; 355 char *encrypted_password_s; 356 357 setspent(); 358 p_shadow_line = getspnam(uname); 359 endspent(); 360 if (!(p_shadow_line)) { 361 fprintf(stderr, _("Cannot find your entry in the shadow " 362 "passwd file.\n")); 363 return 0; 364 } 365 366 /* Ask user to input unencrypted password */ 367 if (!(unencrypted_password_s = getpass(PASSWORD_PROMPT))) { 368 fprintf(stderr, _("getpass cannot open /dev/tty\n")); 369 return 0; 370 } 371 372 /* Use crypt() to encrypt user's input password. */ 373 encrypted_password_s = crypt(unencrypted_password_s, 374 p_shadow_line->sp_pwdp); 375 memset(unencrypted_password_s, 0, strlen(unencrypted_password_s)); 376 return (!strcmp(encrypted_password_s, p_shadow_line->sp_pwdp)); 377 } 378 #endif /* if/else USE_PAM */ 379 380 /** 381 * This function checks to see if the shell is known in /etc/shells. 382 * If so, it returns 1. On error or illegal shell, it returns 0. 383 */ 384 static int verify_shell(const char *shell_name) 385 { 386 int found = 0; 387 const char *buf; 388 389 if (!(shell_name && shell_name[0])) 390 return found; 391 392 while ((buf = getusershell()) != NULL) { 393 /* ignore comments */ 394 if (*buf == '#') 395 continue; 396 397 /* check the shell skipping newline char */ 398 if (!strcmp(shell_name, buf)) { 399 found = 1; 400 break; 401 } 402 } 403 endusershell(); 404 return found; 405 } 406 407 /** 408 * Determine the Linux user identity to re-authenticate. 409 * If supported and set, use the login uid, as this should be more stable. 410 * Otherwise, use the real uid. 411 * 412 * This function assigns malloc'd memory into the pw_copy struct. 413 * Returns zero on success, non-zero otherwise 414 */ 415 static int extract_pw_data(struct passwd *pw_copy) 416 { 417 uid_t uid; 418 struct passwd *pw; 419 420 #ifdef USE_AUDIT 421 uid = audit_getloginuid(); 422 if (uid == (uid_t) - 1) 423 uid = getuid(); 424 #else 425 uid = getuid(); 426 #endif 427 428 setpwent(); 429 pw = getpwuid(uid); 430 endpwent(); 431 if (!(pw && pw->pw_name && pw->pw_name[0] && pw->pw_shell 432 && pw->pw_shell[0] && pw->pw_dir && pw->pw_dir[0])) { 433 fprintf(stderr, 434 _("cannot find valid entry in the passwd file.\n")); 435 return -1; 436 } 437 438 *pw_copy = *pw; 439 pw = pw_copy; 440 pw->pw_name = strdup(pw->pw_name); 441 pw->pw_dir = strdup(pw->pw_dir); 442 pw->pw_shell = strdup(pw->pw_shell); 443 444 if (!(pw->pw_name && pw->pw_dir && pw->pw_shell)) { 445 fprintf(stderr, _("Out of memory!\n")); 446 goto out_free; 447 } 448 449 if (verify_shell(pw->pw_shell) == 0) { 450 fprintf(stderr, _("Error! Shell is not valid.\n")); 451 goto out_free; 452 } 453 return 0; 454 455 out_free: 456 free(pw->pw_name); 457 free(pw->pw_dir); 458 free(pw->pw_shell); 459 pw->pw_name = NULL; 460 pw->pw_dir = NULL; 461 pw->pw_shell = NULL; 462 return -1; 463 } 464 465 /** 466 * Either restore the original environment, or set up a minimal one. 467 * 468 * The minimal environment contains: 469 * TERM, DISPLAY and XAUTHORITY - if they are set, preserve values 470 * HOME, SHELL, USER and LOGNAME - set to contents of /etc/passwd 471 * PATH - set to default value DEFAULT_PATH 472 * 473 * Returns zero on success, non-zero otherwise 474 */ 475 static int restore_environment(int preserve_environment, 476 char **old_environ, const struct passwd *pw) 477 { 478 char const *term_env; 479 char const *display_env; 480 char const *xauthority_env; 481 char *term = NULL; /* temporary container */ 482 char *display = NULL; /* temporary container */ 483 char *xauthority = NULL; /* temporary container */ 484 int rc; 485 486 environ = old_environ; 487 488 if (preserve_environment) 489 return 0; 490 491 term_env = getenv("TERM"); 492 display_env = getenv("DISPLAY"); 493 xauthority_env = getenv("XAUTHORITY"); 494 495 /* Save the variable values we want */ 496 if (term_env) 497 term = strdup(term_env); 498 if (display_env) 499 display = strdup(display_env); 500 if (xauthority_env) 501 xauthority = strdup(xauthority_env); 502 if ((term_env && !term) || (display_env && !display) || 503 (xauthority_env && !xauthority)) { 504 rc = -1; 505 goto out; 506 } 507 508 /* Construct a new environment */ 509 if ((rc = clearenv())) { 510 fprintf(stderr, _("Unable to clear environment\n")); 511 goto out; 512 } 513 514 /* Restore that which we saved */ 515 if (term) 516 rc |= setenv("TERM", term, 1); 517 if (display) 518 rc |= setenv("DISPLAY", display, 1); 519 if (xauthority) 520 rc |= setenv("XAUTHORITY", xauthority, 1); 521 rc |= setenv("HOME", pw->pw_dir, 1); 522 rc |= setenv("SHELL", pw->pw_shell, 1); 523 rc |= setenv("USER", pw->pw_name, 1); 524 rc |= setenv("LOGNAME", pw->pw_name, 1); 525 rc |= setenv("PATH", DEFAULT_PATH, 1); 526 out: 527 free(term); 528 free(display); 529 free(xauthority); 530 return rc; 531 } 532 533 /** 534 * This function will drop the capabilities so that we are left 535 * only with access to the audit system. If the user is root, we leave 536 * the capabilities alone since they already should have access to the 537 * audit netlink socket. 538 * 539 * Returns zero on success, non-zero otherwise 540 */ 541 #if defined(AUDIT_LOG_PRIV) && !defined(NAMESPACE_PRIV) 542 static int drop_capabilities(int full) 543 { 544 uid_t uid = getuid(); 545 if (!uid) return 0; 546 547 capng_setpid(getpid()); 548 capng_clear(CAPNG_SELECT_CAPS); 549 550 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { 551 fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n")); 552 return -1; 553 } 554 555 /* Change uid */ 556 if (setresuid(uid, uid, uid)) { 557 fprintf(stderr, _("Error changing uid, aborting.\n")); 558 return -1; 559 } 560 561 if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0) { 562 fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n")); 563 return -1; 564 } 565 566 if (! full) 567 capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_AUDIT_WRITE); 568 return capng_apply(CAPNG_SELECT_CAPS); 569 } 570 #elif defined(NAMESPACE_PRIV) 571 /** 572 * This function will drop the capabilities so that we are left 573 * only with access to the audit system and the ability to raise 574 * CAP_SYS_ADMIN, CAP_DAC_OVERRIDE, CAP_FOWNER and CAP_CHOWN, 575 * before invoking pam_namespace. These capabilities are needed 576 * for performing bind mounts/unmounts and to create potential new 577 * instance directories with appropriate DAC attributes. If the 578 * user is root, we leave the capabilities alone since they already 579 * should have access to the audit netlink socket and should have 580 * the ability to create/mount/unmount instance directories. 581 * 582 * Returns zero on success, non-zero otherwise 583 */ 584 static int drop_capabilities(int full) 585 { 586 uid_t uid = getuid(); 587 if (!uid) return 0; 588 589 capng_setpid(getpid()); 590 capng_clear(CAPNG_SELECT_CAPS); 591 592 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { 593 fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n")); 594 return -1; 595 } 596 597 /* Change uid */ 598 if (setresuid(uid, uid, uid)) { 599 fprintf(stderr, _("Error changing uid, aborting.\n")); 600 return -1; 601 } 602 603 if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0) { 604 fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n")); 605 return -1; 606 } 607 608 if (! full) 609 capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_SYS_ADMIN , CAP_FOWNER , CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_AUDIT_WRITE, -1); 610 611 return capng_apply(CAPNG_SELECT_CAPS); 612 } 613 614 #else 615 static inline int drop_capabilities(__attribute__ ((__unused__)) int full) 616 { 617 return 0; 618 } 619 #endif 620 621 #ifdef NAMESPACE_PRIV 622 /** 623 * This function will set the uid values to be that of caller's uid, and 624 * will drop any privilages which maybe have been raised. 625 */ 626 static int transition_to_caller_uid() 627 { 628 uid_t uid = getuid(); 629 630 if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0) { 631 fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n")); 632 return -1; 633 } 634 635 if (setresuid(uid, uid, uid)) { 636 fprintf(stderr, _("Error changing uid, aborting.\n")); 637 return -1; 638 } 639 return 0; 640 } 641 #endif 642 643 #ifdef AUDIT_LOG_PRIV 644 /* Send audit message */ 645 static 646 int send_audit_message(int success, security_context_t old_context, 647 security_context_t new_context, const char *ttyn) 648 { 649 char *msg = NULL; 650 int rc; 651 int audit_fd = audit_open(); 652 653 if (audit_fd < 0) { 654 fprintf(stderr, _("Error connecting to audit system.\n")); 655 return -1; 656 } 657 if (asprintf(&msg, "newrole: old-context=%s new-context=%s", 658 old_context ? old_context : "?", 659 new_context ? new_context : "?") < 0) { 660 fprintf(stderr, _("Error allocating memory.\n")); 661 rc = -1; 662 goto out; 663 } 664 rc = audit_log_user_message(audit_fd, AUDIT_USER_ROLE_CHANGE, 665 msg, NULL, NULL, ttyn, success); 666 if (rc <= 0) { 667 fprintf(stderr, _("Error sending audit message.\n")); 668 rc = -1; 669 goto out; 670 } 671 rc = 0; 672 out: 673 free(msg); 674 close(audit_fd); 675 return rc; 676 } 677 #else 678 static inline 679 int send_audit_message(int success __attribute__ ((unused)), 680 security_context_t old_context 681 __attribute__ ((unused)), 682 security_context_t new_context 683 __attribute__ ((unused)), const char *ttyn 684 __attribute__ ((unused))) 685 { 686 return 0; 687 } 688 #endif 689 690 /** 691 * This function attempts to relabel the tty. If this function fails, then 692 * the fd is closed, the contexts are free'd and -1 is returned. On success, 693 * a valid fd is returned and tty_context and new_tty_context are set. 694 * 695 * This function will not fail if it can not relabel the tty when selinux is 696 * in permissive mode. 697 */ 698 static int relabel_tty(const char *ttyn, security_context_t new_context, 699 security_context_t * tty_context, 700 security_context_t * new_tty_context) 701 { 702 int fd, rc; 703 int enforcing = security_getenforce(); 704 security_context_t tty_con = NULL; 705 security_context_t new_tty_con = NULL; 706 707 if (!ttyn) 708 return 0; 709 710 if (enforcing < 0) { 711 fprintf(stderr, _("Could not determine enforcing mode.\n")); 712 return -1; 713 } 714 715 /* Re-open TTY descriptor */ 716 fd = open(ttyn, O_RDWR | O_NONBLOCK); 717 if (fd < 0) { 718 fprintf(stderr, _("Error! Could not open %s.\n"), ttyn); 719 return fd; 720 } 721 /* this craziness is to make sure we cann't block on open and deadlock */ 722 rc = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK); 723 if (rc) { 724 fprintf(stderr, _("Error! Could not clear O_NONBLOCK on %s\n"), ttyn); 725 close(fd); 726 return rc; 727 } 728 729 if (fgetfilecon(fd, &tty_con) < 0) { 730 fprintf(stderr, _("%s! Could not get current context " 731 "for %s, not relabeling tty.\n"), 732 enforcing ? "Error" : "Warning", ttyn); 733 if (enforcing) 734 goto close_fd; 735 } 736 737 if (tty_con && 738 (security_compute_relabel(new_context, tty_con, 739 string_to_security_class("chr_file"), &new_tty_con) < 0)) { 740 fprintf(stderr, _("%s! Could not get new context for %s, " 741 "not relabeling tty.\n"), 742 enforcing ? "Error" : "Warning", ttyn); 743 if (enforcing) 744 goto close_fd; 745 } 746 747 if (new_tty_con) 748 if (fsetfilecon(fd, new_tty_con) < 0) { 749 fprintf(stderr, 750 _("%s! Could not set new context for %s\n"), 751 enforcing ? "Error" : "Warning", ttyn); 752 freecon(new_tty_con); 753 new_tty_con = NULL; 754 if (enforcing) 755 goto close_fd; 756 } 757 758 *tty_context = tty_con; 759 *new_tty_context = new_tty_con; 760 return fd; 761 762 close_fd: 763 freecon(tty_con); 764 close(fd); 765 return -1; 766 } 767 768 /** 769 * This function attempts to revert the relabeling done to the tty. 770 * fd - referencing the opened ttyn 771 * ttyn - name of tty to restore 772 * tty_context - original context of the tty 773 * new_tty_context - context tty was relabeled to 774 * 775 * Returns zero on success, non-zero otherwise 776 */ 777 static int restore_tty_label(int fd, const char *ttyn, 778 security_context_t tty_context, 779 security_context_t new_tty_context) 780 { 781 int rc = 0; 782 security_context_t chk_tty_context = NULL; 783 784 if (!ttyn) 785 goto skip_relabel; 786 787 if (!new_tty_context) 788 goto skip_relabel; 789 790 /* Verify that the tty still has the context set by newrole. */ 791 if ((rc = fgetfilecon(fd, &chk_tty_context)) < 0) { 792 fprintf(stderr, "Could not fgetfilecon %s.\n", ttyn); 793 goto skip_relabel; 794 } 795 796 if ((rc = strcmp(chk_tty_context, new_tty_context))) { 797 fprintf(stderr, _("%s changed labels.\n"), ttyn); 798 goto skip_relabel; 799 } 800 801 if ((rc = fsetfilecon(fd, tty_context)) < 0) 802 fprintf(stderr, 803 _("Warning! Could not restore context for %s\n"), ttyn); 804 skip_relabel: 805 freecon(chk_tty_context); 806 return rc; 807 } 808 809 /** 810 * Parses and validates the provided command line options and 811 * constructs a new context based on our old context and the 812 * arguments specified on the command line. On success 813 * new_context will be set to valid values, otherwise its value 814 * is left unchanged. 815 * 816 * Returns zero on success, non-zero otherwise. 817 */ 818 static int parse_command_line_arguments(int argc, char **argv, char *ttyn, 819 security_context_t old_context, 820 security_context_t * new_context, 821 int *preserve_environment) 822 { 823 int flag_index; /* flag index in argv[] */ 824 int clflag; /* holds codes for command line flags */ 825 char *role_s = NULL; /* role spec'd by user in argv[] */ 826 char *type_s = NULL; /* type spec'd by user in argv[] */ 827 char *type_ptr = NULL; /* stores malloc'd data from get_default_type */ 828 char *level_s = NULL; /* level spec'd by user in argv[] */ 829 char *range_ptr = NULL; 830 security_context_t new_con = NULL; 831 security_context_t tty_con = NULL; 832 context_t context = NULL; /* manipulatable form of new_context */ 833 const struct option long_options[] = { 834 {"role", 1, 0, 'r'}, 835 {"type", 1, 0, 't'}, 836 {"level", 1, 0, 'l'}, 837 {"preserve-environment", 0, 0, 'p'}, 838 {"version", 0, 0, 'V'}, 839 {NULL, 0, 0, 0} 840 }; 841 842 *preserve_environment = 0; 843 while (1) { 844 clflag = getopt_long(argc, argv, "r:t:l:pV", long_options, 845 &flag_index); 846 if (clflag == -1) 847 break; 848 849 switch (clflag) { 850 case 'V': 851 printf("newrole: %s version %s\n", PACKAGE, VERSION); 852 exit(0); 853 break; 854 case 'p': 855 *preserve_environment = 1; 856 break; 857 case 'r': 858 if (role_s) { 859 fprintf(stderr, 860 _("Error: multiple roles specified\n")); 861 return -1; 862 } 863 role_s = optarg; 864 break; 865 case 't': 866 if (type_s) { 867 fprintf(stderr, 868 _("Error: multiple types specified\n")); 869 return -1; 870 } 871 type_s = optarg; 872 break; 873 case 'l': 874 if (!is_selinux_mls_enabled()) { 875 fprintf(stderr, _("Sorry, -l may be used with " 876 "SELinux MLS support.\n")); 877 return -1; 878 } 879 if (level_s) { 880 fprintf(stderr, _("Error: multiple levels " 881 "specified\n")); 882 return -1; 883 } 884 if (ttyn) { 885 if (fgetfilecon(STDIN_FILENO, &tty_con) >= 0) { 886 if (selinux_check_securetty_context 887 (tty_con) < 0) { 888 fprintf(stderr, 889 _ 890 ("Error: you are not allowed to change levels on a non secure terminal \n")); 891 freecon(tty_con); 892 return -1; 893 } 894 freecon(tty_con); 895 } 896 } 897 898 level_s = optarg; 899 break; 900 default: 901 fprintf(stderr, "%s\n", USAGE_STRING); 902 return -1; 903 } 904 } 905 906 /* Verify that the combination of command-line arguments are viable */ 907 if (!(role_s || type_s || level_s)) { 908 fprintf(stderr, "%s\n", USAGE_STRING); 909 return -1; 910 } 911 912 /* Fill in a default type if one hasn't been specified. */ 913 if (role_s && !type_s) { 914 /* get_default_type() returns malloc'd memory */ 915 if (get_default_type(role_s, &type_ptr)) { 916 fprintf(stderr, _("Couldn't get default type.\n")); 917 send_audit_message(0, old_context, new_con, ttyn); 918 return -1; 919 } 920 type_s = type_ptr; 921 } 922 923 /* Create a temporary new context structure we extract and modify */ 924 context = context_new(old_context); 925 if (!context) { 926 fprintf(stderr, _("failed to get new context.\n")); 927 goto err_free; 928 } 929 930 /* Modify the temporary new context */ 931 if (role_s) 932 if (context_role_set(context, role_s)) { 933 fprintf(stderr, _("failed to set new role %s\n"), 934 role_s); 935 goto err_free; 936 } 937 938 if (type_s) 939 if (context_type_set(context, type_s)) { 940 fprintf(stderr, _("failed to set new type %s\n"), 941 type_s); 942 goto err_free; 943 } 944 945 if (level_s) { 946 range_ptr = 947 build_new_range(level_s, context_range_get(context)); 948 if (!range_ptr) { 949 fprintf(stderr, 950 _("failed to build new range with level %s\n"), 951 level_s); 952 goto err_free; 953 } 954 if (context_range_set(context, range_ptr)) { 955 fprintf(stderr, _("failed to set new range %s\n"), 956 range_ptr); 957 goto err_free; 958 } 959 } 960 961 /* Construct the final new context */ 962 if (!(new_con = context_str(context))) { 963 fprintf(stderr, _("failed to convert new context to string\n")); 964 goto err_free; 965 } 966 967 if (security_check_context(new_con) < 0) { 968 fprintf(stderr, _("%s is not a valid context\n"), new_con); 969 send_audit_message(0, old_context, new_con, ttyn); 970 goto err_free; 971 } 972 973 *new_context = strdup(new_con); 974 if (!*new_context) { 975 fprintf(stderr, _("Unable to allocate memory for new_context")); 976 goto err_free; 977 } 978 979 free(type_ptr); 980 free(range_ptr); 981 context_free(context); 982 return 0; 983 984 err_free: 985 free(type_ptr); 986 free(range_ptr); 987 /* Don't free new_con, context_free(context) handles this */ 988 context_free(context); 989 return -1; 990 } 991 992 /** 993 * Take care of any signal setup 994 */ 995 static int set_signal_handles(void) 996 { 997 sigset_t empty; 998 999 /* Empty the signal mask in case someone is blocking a signal */ 1000 if (sigemptyset(&empty)) { 1001 fprintf(stderr, _("Unable to obtain empty signal set\n")); 1002 return -1; 1003 } 1004 1005 (void)sigprocmask(SIG_SETMASK, &empty, NULL); 1006 1007 /* Terminate on SIGHUP. */ 1008 if (signal(SIGHUP, SIG_DFL) == SIG_ERR) { 1009 fprintf(stderr, _("Unable to set SIGHUP handler\n")); 1010 return -1; 1011 } 1012 1013 return 0; 1014 } 1015 1016 /************************************************************************ 1017 * 1018 * All code used for both PAM and shadow passwd goes in this section. 1019 * 1020 ************************************************************************/ 1021 1022 int main(int argc, char *argv[]) 1023 { 1024 security_context_t new_context = NULL; /* target security context */ 1025 security_context_t old_context = NULL; /* original securiy context */ 1026 security_context_t tty_context = NULL; /* current context of tty */ 1027 security_context_t new_tty_context = NULL; /* new context of tty */ 1028 1029 struct passwd pw; /* struct derived from passwd file line */ 1030 char *ttyn = NULL; /* tty path */ 1031 1032 char **old_environ; 1033 int preserve_environment; 1034 1035 int fd; 1036 pid_t childPid = 0; 1037 char *shell_argv0 = NULL; 1038 int rc; 1039 1040 #ifdef USE_PAM 1041 int pam_status; /* pam return code */ 1042 pam_handle_t *pam_handle; /* opaque handle used by all PAM functions */ 1043 1044 /* This is a jump table of functions for PAM to use when it wants to * 1045 * communicate with the user. We'll be using misc_conv(), which is * 1046 * provided for us via pam_misc.h. */ 1047 struct pam_conv pam_conversation = { 1048 misc_conv, 1049 NULL 1050 }; 1051 #endif 1052 1053 /* 1054 * Step 0: Setup 1055 * 1056 * Do some intial setup, including dropping capabilities, checking 1057 * if it makes sense to continue to run newrole, and setting up 1058 * a scrubbed environment. 1059 */ 1060 if (drop_capabilities(FALSE)) { 1061 perror(_("Sorry, newrole failed to drop capabilities\n")); 1062 return -1; 1063 } 1064 if (set_signal_handles()) 1065 return -1; 1066 1067 #ifdef USE_NLS 1068 setlocale(LC_ALL, ""); 1069 bindtextdomain(PACKAGE, LOCALEDIR); 1070 textdomain(PACKAGE); 1071 #endif 1072 1073 old_environ = environ; 1074 environ = NULL; 1075 1076 if (!is_selinux_enabled()) { 1077 fprintf(stderr, _("Sorry, newrole may be used only on " 1078 "a SELinux kernel.\n")); 1079 return -1; 1080 } 1081 1082 if (security_getenforce() < 0) { 1083 fprintf(stderr, _("Could not determine enforcing mode.\n")); 1084 return -1; 1085 } 1086 1087 /* 1088 * Step 1: Parse command line and valid arguments 1089 * 1090 * old_context and ttyn are required for audit logging, 1091 * context validation and pam 1092 */ 1093 if (getprevcon(&old_context)) { 1094 fprintf(stderr, _("failed to get old_context.\n")); 1095 return -1; 1096 } 1097 1098 ttyn = ttyname(STDIN_FILENO); 1099 if (!ttyn || *ttyn == '\0') { 1100 fprintf(stderr, 1101 _("Warning! Could not retrieve tty information.\n")); 1102 } 1103 1104 if (parse_command_line_arguments(argc, argv, ttyn, old_context, 1105 &new_context, &preserve_environment)) 1106 return -1; 1107 1108 /* 1109 * Step 2: Authenticate the user. 1110 * 1111 * Re-authenticate the user running this program. 1112 * This is just to help confirm user intent (vs. invocation by 1113 * malicious software), not to authorize the operation (which is covered 1114 * by policy). Trusted path mechanism would be preferred. 1115 */ 1116 memset(&pw, 0, sizeof(pw)); 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