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_BOTH); 550 if (capng_lock() < 0) 551 return -1; 552 553 /* Change uid */ 554 if (setresuid(uid, uid, uid)) { 555 fprintf(stderr, _("Error changing uid, aborting.\n")); 556 return -1; 557 } 558 if (! full) 559 capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_AUDIT_WRITE); 560 return capng_apply(CAPNG_SELECT_BOTH); 561 } 562 #elif defined(NAMESPACE_PRIV) 563 /** 564 * This function will drop the capabilities so that we are left 565 * only with access to the audit system and the ability to raise 566 * CAP_SYS_ADMIN, CAP_DAC_OVERRIDE, CAP_FOWNER and CAP_CHOWN, 567 * before invoking pam_namespace. These capabilities are needed 568 * for performing bind mounts/unmounts and to create potential new 569 * instance directories with appropriate DAC attributes. If the 570 * user is root, we leave the capabilities alone since they already 571 * should have access to the audit netlink socket and should have 572 * the ability to create/mount/unmount instance directories. 573 * 574 * Returns zero on success, non-zero otherwise 575 */ 576 static int drop_capabilities(int full) 577 { 578 capng_setpid(getpid()); 579 capng_clear(CAPNG_SELECT_BOTH); 580 if (capng_lock() < 0) 581 return -1; 582 583 uid_t uid = getuid(); 584 /* Change uid */ 585 if (setresuid(uid, uid, uid)) { 586 fprintf(stderr, _("Error changing uid, aborting.\n")); 587 return -1; 588 } 589 if (! full) 590 capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_SYS_ADMIN , CAP_FOWNER , CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_SETPCAP, -1); 591 return capng_apply(CAPNG_SELECT_BOTH); 592 } 593 594 #else 595 static inline int drop_capabilities(__attribute__ ((__unused__)) int full) 596 { 597 return 0; 598 } 599 #endif 600 601 #ifdef NAMESPACE_PRIV 602 /** 603 * This function will set the uid values to be that of caller's uid, and 604 * will drop any privilages which maybe have been raised. 605 */ 606 static int transition_to_caller_uid() 607 { 608 uid_t uid = getuid(); 609 610 if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0) { 611 fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n")); 612 return -1; 613 } 614 615 if (setresuid(uid, uid, uid)) { 616 fprintf(stderr, _("Error changing uid, aborting.\n")); 617 return -1; 618 } 619 return 0; 620 } 621 #endif 622 623 #ifdef AUDIT_LOG_PRIV 624 /* Send audit message */ 625 static 626 int send_audit_message(int success, security_context_t old_context, 627 security_context_t new_context, const char *ttyn) 628 { 629 char *msg = NULL; 630 int rc; 631 int audit_fd = audit_open(); 632 633 if (audit_fd < 0) { 634 fprintf(stderr, _("Error connecting to audit system.\n")); 635 return -1; 636 } 637 if (asprintf(&msg, "newrole: old-context=%s new-context=%s", 638 old_context ? old_context : "?", 639 new_context ? new_context : "?") < 0) { 640 fprintf(stderr, _("Error allocating memory.\n")); 641 rc = -1; 642 goto out; 643 } 644 rc = audit_log_user_message(audit_fd, AUDIT_USER_ROLE_CHANGE, 645 msg, NULL, NULL, ttyn, success); 646 if (rc <= 0) { 647 fprintf(stderr, _("Error sending audit message.\n")); 648 rc = -1; 649 goto out; 650 } 651 rc = 0; 652 out: 653 free(msg); 654 close(audit_fd); 655 return rc; 656 } 657 #else 658 static inline 659 int send_audit_message(int success __attribute__ ((unused)), 660 security_context_t old_context 661 __attribute__ ((unused)), 662 security_context_t new_context 663 __attribute__ ((unused)), const char *ttyn 664 __attribute__ ((unused))) 665 { 666 return 0; 667 } 668 #endif 669 670 /** 671 * This function attempts to relabel the tty. If this function fails, then 672 * the fd is closed, the contexts are free'd and -1 is returned. On success, 673 * a valid fd is returned and tty_context and new_tty_context are set. 674 * 675 * This function will not fail if it can not relabel the tty when selinux is 676 * in permissive mode. 677 */ 678 static int relabel_tty(const char *ttyn, security_context_t new_context, 679 security_context_t * tty_context, 680 security_context_t * new_tty_context) 681 { 682 int fd; 683 int enforcing = security_getenforce(); 684 security_context_t tty_con = NULL; 685 security_context_t new_tty_con = NULL; 686 687 if (!ttyn) 688 return 0; 689 690 if (enforcing < 0) { 691 fprintf(stderr, _("Could not determine enforcing mode.\n")); 692 return -1; 693 } 694 695 /* Re-open TTY descriptor */ 696 fd = open(ttyn, O_RDWR | O_NONBLOCK); 697 if (fd < 0) { 698 fprintf(stderr, _("Error! Could not open %s.\n"), ttyn); 699 return fd; 700 } 701 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK); 702 703 if (fgetfilecon(fd, &tty_con) < 0) { 704 fprintf(stderr, _("%s! Could not get current context " 705 "for %s, not relabeling tty.\n"), 706 enforcing ? "Error" : "Warning", ttyn); 707 if (enforcing) 708 goto close_fd; 709 } 710 711 if (tty_con && 712 (security_compute_relabel(new_context, tty_con, 713 string_to_security_class("chr_file"), &new_tty_con) < 0)) { 714 fprintf(stderr, _("%s! Could not get new context for %s, " 715 "not relabeling tty.\n"), 716 enforcing ? "Error" : "Warning", ttyn); 717 if (enforcing) 718 goto close_fd; 719 } 720 721 if (new_tty_con) 722 if (fsetfilecon(fd, new_tty_con) < 0) { 723 fprintf(stderr, 724 _("%s! Could not set new context for %s\n"), 725 enforcing ? "Error" : "Warning", ttyn); 726 freecon(new_tty_con); 727 new_tty_con = NULL; 728 if (enforcing) 729 goto close_fd; 730 } 731 732 *tty_context = tty_con; 733 *new_tty_context = new_tty_con; 734 return fd; 735 736 close_fd: 737 freecon(tty_con); 738 close(fd); 739 return -1; 740 } 741 742 /** 743 * This function attempts to revert the relabeling done to the tty. 744 * fd - referencing the opened ttyn 745 * ttyn - name of tty to restore 746 * tty_context - original context of the tty 747 * new_tty_context - context tty was relabeled to 748 * 749 * Returns zero on success, non-zero otherwise 750 */ 751 static int restore_tty_label(int fd, const char *ttyn, 752 security_context_t tty_context, 753 security_context_t new_tty_context) 754 { 755 int rc = 0; 756 security_context_t chk_tty_context = NULL; 757 758 if (!ttyn) 759 goto skip_relabel; 760 761 if (!new_tty_context) 762 goto skip_relabel; 763 764 /* Verify that the tty still has the context set by newrole. */ 765 if ((rc = fgetfilecon(fd, &chk_tty_context)) < 0) { 766 fprintf(stderr, "Could not fgetfilecon %s.\n", ttyn); 767 goto skip_relabel; 768 } 769 770 if ((rc = strcmp(chk_tty_context, new_tty_context))) { 771 fprintf(stderr, _("%s changed labels.\n"), ttyn); 772 goto skip_relabel; 773 } 774 775 if ((rc = fsetfilecon(fd, tty_context)) < 0) 776 fprintf(stderr, 777 _("Warning! Could not restore context for %s\n"), ttyn); 778 skip_relabel: 779 freecon(chk_tty_context); 780 return rc; 781 } 782 783 /** 784 * Parses and validates the provided command line options and 785 * constructs a new context based on our old context and the 786 * arguments specified on the command line. On success 787 * new_context will be set to valid values, otherwise its value 788 * is left unchanged. 789 * 790 * Returns zero on success, non-zero otherwise. 791 */ 792 static int parse_command_line_arguments(int argc, char **argv, char *ttyn, 793 security_context_t old_context, 794 security_context_t * new_context, 795 int *preserve_environment) 796 { 797 int flag_index; /* flag index in argv[] */ 798 int clflag; /* holds codes for command line flags */ 799 char *role_s = NULL; /* role spec'd by user in argv[] */ 800 char *type_s = NULL; /* type spec'd by user in argv[] */ 801 char *type_ptr = NULL; /* stores malloc'd data from get_default_type */ 802 char *level_s = NULL; /* level spec'd by user in argv[] */ 803 char *range_ptr = NULL; 804 security_context_t new_con = NULL; 805 security_context_t tty_con = NULL; 806 context_t context = NULL; /* manipulatable form of new_context */ 807 const struct option long_options[] = { 808 {"role", 1, 0, 'r'}, 809 {"type", 1, 0, 't'}, 810 {"level", 1, 0, 'l'}, 811 {"preserve-environment", 0, 0, 'p'}, 812 {"version", 0, 0, 'V'}, 813 {NULL, 0, 0, 0} 814 }; 815 816 *preserve_environment = 0; 817 while (1) { 818 clflag = getopt_long(argc, argv, "r:t:l:pV", long_options, 819 &flag_index); 820 if (clflag == -1) 821 break; 822 823 switch (clflag) { 824 case 'V': 825 printf("newrole: %s version %s\n", PACKAGE, VERSION); 826 exit(0); 827 break; 828 case 'p': 829 *preserve_environment = 1; 830 break; 831 case 'r': 832 if (role_s) { 833 fprintf(stderr, 834 _("Error: multiple roles specified\n")); 835 return -1; 836 } 837 role_s = optarg; 838 break; 839 case 't': 840 if (type_s) { 841 fprintf(stderr, 842 _("Error: multiple types specified\n")); 843 return -1; 844 } 845 type_s = optarg; 846 break; 847 case 'l': 848 if (!is_selinux_mls_enabled()) { 849 fprintf(stderr, _("Sorry, -l may be used with " 850 "SELinux MLS support.\n")); 851 return -1; 852 } 853 if (level_s) { 854 fprintf(stderr, _("Error: multiple levels " 855 "specified\n")); 856 return -1; 857 } 858 if (ttyn) { 859 if (fgetfilecon(STDIN_FILENO, &tty_con) >= 0) { 860 if (selinux_check_securetty_context 861 (tty_con) < 0) { 862 fprintf(stderr, 863 _ 864 ("Error: you are not allowed to change levels on a non secure terminal \n")); 865 freecon(tty_con); 866 return -1; 867 } 868 freecon(tty_con); 869 } 870 } 871 872 level_s = optarg; 873 break; 874 default: 875 fprintf(stderr, "%s\n", USAGE_STRING); 876 return -1; 877 } 878 } 879 880 /* Verify that the combination of command-line arguments are viable */ 881 if (!(role_s || type_s || level_s)) { 882 fprintf(stderr, "%s\n", USAGE_STRING); 883 return -1; 884 } 885 886 /* Fill in a default type if one hasn't been specified. */ 887 if (role_s && !type_s) { 888 /* get_default_type() returns malloc'd memory */ 889 if (get_default_type(role_s, &type_ptr)) { 890 fprintf(stderr, _("Couldn't get default type.\n")); 891 send_audit_message(0, old_context, new_con, ttyn); 892 return -1; 893 } 894 type_s = type_ptr; 895 } 896 897 /* Create a temporary new context structure we extract and modify */ 898 context = context_new(old_context); 899 if (!context) { 900 fprintf(stderr, _("failed to get new context.\n")); 901 goto err_free; 902 } 903 904 /* Modify the temporary new context */ 905 if (role_s) 906 if (context_role_set(context, role_s)) { 907 fprintf(stderr, _("failed to set new role %s\n"), 908 role_s); 909 goto err_free; 910 } 911 912 if (type_s) 913 if (context_type_set(context, type_s)) { 914 fprintf(stderr, _("failed to set new type %s\n"), 915 type_s); 916 goto err_free; 917 } 918 919 if (level_s) { 920 range_ptr = 921 build_new_range(level_s, context_range_get(context)); 922 if (!range_ptr) { 923 fprintf(stderr, 924 _("failed to build new range with level %s\n"), 925 level_s); 926 goto err_free; 927 } 928 if (context_range_set(context, range_ptr)) { 929 fprintf(stderr, _("failed to set new range %s\n"), 930 range_ptr); 931 goto err_free; 932 } 933 } 934 935 /* Construct the final new context */ 936 if (!(new_con = context_str(context))) { 937 fprintf(stderr, _("failed to convert new context to string\n")); 938 goto err_free; 939 } 940 941 if (security_check_context(new_con) < 0) { 942 fprintf(stderr, _("%s is not a valid context\n"), new_con); 943 send_audit_message(0, old_context, new_con, ttyn); 944 goto err_free; 945 } 946 947 *new_context = strdup(new_con); 948 if (!*new_context) { 949 fprintf(stderr, _("Unable to allocate memory for new_context")); 950 goto err_free; 951 } 952 953 free(type_ptr); 954 free(range_ptr); 955 context_free(context); 956 return 0; 957 958 err_free: 959 free(type_ptr); 960 free(range_ptr); 961 /* Don't free new_con, context_free(context) handles this */ 962 context_free(context); 963 return -1; 964 } 965 966 /** 967 * Take care of any signal setup 968 */ 969 static int set_signal_handles(void) 970 { 971 sigset_t empty; 972 973 /* Empty the signal mask in case someone is blocking a signal */ 974 if (sigemptyset(&empty)) { 975 fprintf(stderr, _("Unable to obtain empty signal set\n")); 976 return -1; 977 } 978 979 (void)sigprocmask(SIG_SETMASK, &empty, NULL); 980 981 /* Terminate on SIGHUP. */ 982 if (signal(SIGHUP, SIG_DFL) == SIG_ERR) { 983 fprintf(stderr, _("Unable to set SIGHUP handler\n")); 984 return -1; 985 } 986 987 return 0; 988 } 989 990 /************************************************************************ 991 * 992 * All code used for both PAM and shadow passwd goes in this section. 993 * 994 ************************************************************************/ 995 996 int main(int argc, char *argv[]) 997 { 998 security_context_t new_context = NULL; /* target security context */ 999 security_context_t old_context = NULL; /* original securiy context */ 1000 security_context_t tty_context = NULL; /* current context of tty */ 1001 security_context_t new_tty_context = NULL; /* new context of tty */ 1002 1003 struct passwd pw; /* struct derived from passwd file line */ 1004 char *ttyn = NULL; /* tty path */ 1005 1006 char **old_environ; 1007 int preserve_environment; 1008 1009 int fd; 1010 pid_t childPid = 0; 1011 char *shell_argv0 = NULL; 1012 1013 #ifdef USE_PAM 1014 int rc; 1015 int pam_status; /* pam return code */ 1016 pam_handle_t *pam_handle; /* opaque handle used by all PAM functions */ 1017 1018 /* This is a jump table of functions for PAM to use when it wants to * 1019 * communicate with the user. We'll be using misc_conv(), which is * 1020 * provided for us via pam_misc.h. */ 1021 struct pam_conv pam_conversation = { 1022 misc_conv, 1023 NULL 1024 }; 1025 #endif 1026 1027 /* 1028 * Step 0: Setup 1029 * 1030 * Do some intial setup, including dropping capabilities, checking 1031 * if it makes sense to continue to run newrole, and setting up 1032 * a scrubbed environment. 1033 */ 1034 if (drop_capabilities(FALSE)) { 1035 perror(_("Sorry, newrole failed to drop capabilities\n")); 1036 return -1; 1037 } 1038 if (set_signal_handles()) 1039 return -1; 1040 1041 #ifdef USE_NLS 1042 setlocale(LC_ALL, ""); 1043 bindtextdomain(PACKAGE, LOCALEDIR); 1044 textdomain(PACKAGE); 1045 #endif 1046 1047 old_environ = environ; 1048 environ = NULL; 1049 1050 if (!is_selinux_enabled()) { 1051 fprintf(stderr, _("Sorry, newrole may be used only on " 1052 "a SELinux kernel.\n")); 1053 return -1; 1054 } 1055 1056 if (security_getenforce() < 0) { 1057 fprintf(stderr, _("Could not determine enforcing mode.\n")); 1058 return -1; 1059 } 1060 1061 /* 1062 * Step 1: Parse command line and valid arguments 1063 * 1064 * old_context and ttyn are required for audit logging, 1065 * context validation and pam 1066 */ 1067 if (getprevcon(&old_context)) { 1068 fprintf(stderr, _("failed to get old_context.\n")); 1069 return -1; 1070 } 1071 1072 ttyn = ttyname(STDIN_FILENO); 1073 if (!ttyn || *ttyn == '\0') { 1074 fprintf(stderr, 1075 _("Warning! Could not retrieve tty information.\n")); 1076 } 1077 1078 if (parse_command_line_arguments(argc, argv, ttyn, old_context, 1079 &new_context, &preserve_environment)) 1080 return -1; 1081 1082 /* 1083 * Step 2: Authenticate the user. 1084 * 1085 * Re-authenticate the user running this program. 1086 * This is just to help confirm user intent (vs. invocation by 1087 * malicious software), not to authorize the operation (which is covered 1088 * by policy). Trusted path mechanism would be preferred. 1089 */ 1090 if (extract_pw_data(&pw)) 1091 goto err_free; 1092 1093 #ifdef USE_PAM 1094 if (read_pam_config()) { 1095 fprintf(stderr, 1096 _("error on reading PAM service configuration.\n")); 1097 goto err_free; 1098 } 1099 1100 if (app_service_names != NULL && optind < argc) { 1101 if (strcmp(argv[optind], "-c") == 0 && optind < (argc - 1)) { 1102 /* 1103 * Check for a separate pam service name for the 1104 * command when invoked by newrole. 1105 */ 1106 char *cmd = NULL; 1107 rc = sscanf(argv[optind + 1], "%ms", &cmd); 1108 if (rc != EOF && cmd) { 1109 char *app_service_name = 1110 (char *)hashtab_search(app_service_names, 1111 cmd); 1112 free(cmd); 1113 if (app_service_name != NULL) 1114 service_name = app_service_name; 1115 } 1116 } 1117 } 1118 1119 pam_status = pam_start(service_name, pw.pw_name, &pam_conversation, 1120 &pam_handle); 1121 if (pam_status != PAM_SUCCESS) { 1122 fprintf(stderr, _("failed to initialize PAM\n")); 1123 goto err_free; 1124 } 1125 1126 if (!authenticate_via_pam(ttyn, pam_handle)) 1127 #else 1128 if (!authenticate_via_shadow_passwd(pw.pw_name)) 1129 #endif 1130 { 1131 fprintf(stderr, _("newrole: incorrect password for %s\n"), 1132 pw.pw_name); 1133 send_audit_message(0, old_context, new_context, ttyn); 1134 goto err_close_pam; 1135 } 1136 1137 /* 1138 * Step 3: Handle relabeling of the tty. 1139 * 1140 * Once we authenticate the user, we know that we want to proceed with 1141 * the action. Prior to this point, no changes are made the to system. 1142 */ 1143 fd = relabel_tty(ttyn, new_context, &tty_context, &new_tty_context); 1144 if (fd < 0) 1145 goto err_close_pam; 1146 1147 /* 1148 * Step 4: Fork 1149 * 1150 * Fork, allowing parent to clean up after shell has executed. 1151 * Child: reopen stdin, stdout, stderr and exec shell 1152 * Parnet: wait for child to die and restore tty's context 1153 */ 1154 childPid = fork(); 1155 if (childPid < 0) { 1156 /* fork failed, no child to worry about */ 1157 int errsv = errno; 1158 fprintf(stderr, _("newrole: failure forking: %s"), 1159 strerror(errsv)); 1160 if (restore_tty_label(fd, ttyn, tty_context, new_tty_context)) 1161 fprintf(stderr, _("Unable to restore tty label...\n")); 1162 if (close(fd)) 1163 fprintf(stderr, _("Failed to close tty properly\n")); 1164 goto err_close_pam; 1165 } else if (childPid) { 1166 /* PARENT 1167 * It doesn't make senes to exit early on errors at this point, 1168 * since we are doing cleanup which needs to be done. 1169 * We can exit with a bad rc though 1170 */ 1171 pid_t pid; 1172 int exit_code = 0; 1173 int status; 1174 1175 do { 1176 pid = wait(&status); 1177 } while (pid < 0 && errno == EINTR); 1178 1179 /* Preserve child exit status, unless there is another error. */ 1180 if (WIFEXITED(status)) 1181 exit_code = WEXITSTATUS(status); 1182 1183 if (restore_tty_label(fd, ttyn, tty_context, new_tty_context)) { 1184 fprintf(stderr, _("Unable to restore tty label...\n")); 1185 exit_code = -1; 1186 } 1187 freecon(tty_context); 1188 freecon(new_tty_context); 1189 if (close(fd)) { 1190 fprintf(stderr, _("Failed to close tty properly\n")); 1191 exit_code = -1; 1192 } 1193 #ifdef USE_PAM 1194 #ifdef NAMESPACE_PRIV 1195 pam_status = pam_close_session(pam_handle, 0); 1196 if (pam_status != PAM_SUCCESS) { 1197 fprintf(stderr, "pam_close_session failed with %s\n", 1198 pam_strerror(pam_handle, pam_status)); 1199 exit_code = -1; 1200 } 1201 #endif 1202 rc = pam_end(pam_handle, pam_status); 1203 if (rc != PAM_SUCCESS) { 1204 fprintf(stderr, "pam_end failed with %s\n", 1205 pam_strerror(pam_handle, rc)); 1206 exit_code = -1; 1207 } 1208 hashtab_map(app_service_names, free_hashtab_entry, NULL); 1209 hashtab_destroy(app_service_names); 1210 #endif 1211 free(pw.pw_name); 1212 free(pw.pw_dir); 1213 free(pw.pw_shell); 1214 free(shell_argv0); 1215 return exit_code; 1216 } 1217 1218 /* CHILD */ 1219 /* Close the tty and reopen descriptors 0 through 2 */ 1220 if (ttyn) { 1221 if (close(fd) || close(0) || close(1) || close(2)) { 1222 fprintf(stderr, _("Could not close descriptors.\n")); 1223 goto err_close_pam; 1224 } 1225 fd = open(ttyn, O_RDWR | O_NONBLOCK); 1226 if (fd != 0) 1227 goto err_close_pam; 1228 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK); 1229 fd = open(ttyn, O_RDWR | O_NONBLOCK); 1230 if (fd != 1) 1231 goto err_close_pam; 1232 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK); 1233 fd = open(ttyn, O_RDWR | O_NONBLOCK); 1234 if (fd != 2) 1235 goto err_close_pam; 1236 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK); 1237 1238 } 1239 /* 1240 * Step 5: Execute a new shell with the new context in `new_context'. 1241 * 1242 * Establish context, namesapce and any options for the new shell 1243 */ 1244 if (optind < 1) 1245 optind = 1; 1246 1247 /* This is ugly, but use newrole's argv for the exec'd shells argv */ 1248 if (asprintf(&shell_argv0, "-%s", pw.pw_shell) < 0) { 1249 fprintf(stderr, _("Error allocating shell's argv0.\n")); 1250 shell_argv0 = NULL; 1251 goto err_close_pam; 1252 } 1253 argv[optind - 1] = shell_argv0; 1254 1255 if (setexeccon(new_context)) { 1256 fprintf(stderr, _("Could not set exec context to %s.\n"), 1257 new_context); 1258 goto err_close_pam; 1259 } 1260 #ifdef NAMESPACE_PRIV 1261 /* Ask PAM to setup session for user running this program */ 1262 pam_status = pam_open_session(pam_handle, 0); 1263 if (pam_status != PAM_SUCCESS) { 1264 fprintf(stderr, "pam_open_session failed with %s\n", 1265 pam_strerror(pam_handle, pam_status)); 1266 goto err_close_pam; 1267 } 1268 #endif 1269 1270 if (send_audit_message(1, old_context, new_context, ttyn)) 1271 goto err_close_pam_session; 1272 freecon(old_context); old_context=NULL; 1273 freecon(new_context); new_context=NULL; 1274 1275 #ifdef NAMESPACE_PRIV 1276 if (transition_to_caller_uid()) 1277 goto err_close_pam_session; 1278 #endif 1279 1280 if (drop_capabilities(TRUE)) 1281 goto err_close_pam_session; 1282 1283 /* Handle environment changes */ 1284 if (restore_environment(preserve_environment, old_environ, &pw)) { 1285 fprintf(stderr, _("Unable to restore the environment, " 1286 "aborting\n")); 1287 goto err_close_pam_session; 1288 } 1289 execv(pw.pw_shell, argv + optind - 1); 1290 1291 /* 1292 * Error path cleanup 1293 * 1294 * If we reach here, then we failed to exec the new shell. 1295 */ 1296 perror(_("failed to exec shell\n")); 1297 err_close_pam_session: 1298 #ifdef NAMESPACE_PRIV 1299 pam_status = pam_close_session(pam_handle, 0); 1300 if (pam_status != PAM_SUCCESS) 1301 fprintf(stderr, "pam_close_session failed with %s\n", 1302 pam_strerror(pam_handle, pam_status)); 1303 #endif 1304 err_close_pam: 1305 #ifdef USE_PAM 1306 rc = pam_end(pam_handle, pam_status); 1307 if (rc != PAM_SUCCESS) 1308 fprintf(stderr, "pam_end failed with %s\n", 1309 pam_strerror(pam_handle, rc)); 1310 #endif 1311 err_free: 1312 freecon(tty_context); 1313 freecon(new_tty_context); 1314 freecon(old_context); 1315 freecon(new_context); 1316 free(pw.pw_name); 1317 free(pw.pw_dir); 1318 free(pw.pw_shell); 1319 free(shell_argv0); 1320 #ifdef USE_PAM 1321 if (app_service_names) { 1322 hashtab_map(app_service_names, free_hashtab_entry, NULL); 1323 hashtab_destroy(app_service_names); 1324 } 1325 #endif 1326 return -1; 1327 } /* main() */ 1328