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) || (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, 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)), 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 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 */ 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 */ 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 */ 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 */ 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 */ 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) 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 */ 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 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 */ 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 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 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 */ 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 */ 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 */ 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 */ 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 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