static security_context_t security_label_tty(pam_handle_t *pamh, char *tty, security_context_t usercon) { char ttybuf[PATH_MAX]; int status=0; security_context_t newdev_context=NULL; /* The new context of a device */ security_context_t prev_context=NULL; /* The new context of a device */ const char *ptr; if(strncmp("/dev/", tty, 5)) { snprintf(ttybuf,sizeof(ttybuf),"/dev/%s",tty); ptr = ttybuf; } else ptr = tty; if (getfilecon(ptr, &prev_context) < 0) { if(errno != ENOENT) pam_syslog(pamh, LOG_NOTICE, "Warning! Could not get current context for %s, not relabeling: %m", ptr); return NULL; } if( security_compute_relabel(usercon,prev_context,SECCLASS_CHR_FILE, &newdev_context)!=0) { pam_syslog(pamh, LOG_NOTICE, "Warning! Could not get new context for %s, not relabeling: %m", ptr); pam_syslog(pamh, LOG_NOTICE, "usercon=%s, prev_context=%s", usercon, prev_context); freecon(prev_context); return NULL; } status=setfilecon(ptr,newdev_context); if (status) { pam_syslog(pamh, LOG_NOTICE, "Warning! Could not relabel %s with %s, not relabeling: %m", ptr,newdev_context); freecon(prev_context); prev_context=NULL; } freecon(newdev_context); return prev_context; }
/* Set the TTY context for the specified user */ void ssh_selinux_setup_pty(char *pwname, const char *tty) { security_context_t new_tty_ctx = NULL; security_context_t user_ctx = NULL; security_context_t old_tty_ctx = NULL; if (!ssh_selinux_enabled()) return; debug3("%s: setting TTY context on %s", __func__, tty); user_ctx = ssh_selinux_getctxbyname(pwname); /* XXX: should these calls fatal() upon failure in enforcing mode? */ if (getfilecon(tty, &old_tty_ctx) == -1) { error("%s: getfilecon: %s", __func__, strerror(errno)); goto out; } if (security_compute_relabel(user_ctx, old_tty_ctx, SECCLASS_CHR_FILE, &new_tty_ctx) != 0) { error("%s: security_compute_relabel: %s", __func__, strerror(errno)); goto out; } if (setfilecon(tty, new_tty_ctx) != 0) error("%s: setfilecon: %s", __func__, strerror(errno)); out: if (new_tty_ctx != NULL) freecon(new_tty_ctx); if (old_tty_ctx != NULL) freecon(old_tty_ctx); if (user_ctx != NULL) freecon(user_ctx); debug3("%s: done", __func__); }
static void initselinux(char *username, char *full_tty, security_context_t *user_sid) { security_context_t old_tty_sid, new_tty_sid; if (!is_selinux_enabled()) return; if (get_default_context(username, NULL, user_sid)) { bb_error_msg_and_die("can't get SID for %s", username); } if (getfilecon(full_tty, &old_tty_sid) < 0) { bb_perror_msg_and_die("getfilecon(%s) failed", full_tty); } if (security_compute_relabel(*user_sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0) { bb_perror_msg_and_die("security_change_sid(%s) failed", full_tty); } if (setfilecon(full_tty, new_tty_sid) != 0) { bb_perror_msg_and_die("chsid(%s, %s) failed", full_tty, new_tty_sid); } }
/* * This function attempts to relabel the tty. If this function fails, then * the contexts are free'd and -1 is returned. On success, 0 is returned * and tty_context and new_tty_context are set. * * This function will not fail if it can not relabel the tty when selinux is * in permissive mode. */ static int relabel_tty(const char *ttyn, int ptyfd) { security_context_t tty_con = NULL; security_context_t new_tty_con = NULL; struct stat sb; int fd; debug_decl(relabel_tty, SUDO_DEBUG_SELINUX) se_state.ttyfd = ptyfd; /* It is perfectly legal to have no tty. */ if (ptyfd == -1 && ttyn == NULL) debug_return_int(0); /* If sudo is not allocating a pty for the command, open current tty. */ if (ptyfd == -1) { se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY|O_NONBLOCK); if (se_state.ttyfd == -1 || fstat(se_state.ttyfd, &sb) == -1) { sudo_warn(U_("unable to open %s, not relabeling tty"), ttyn); goto bad; } if (!S_ISCHR(sb.st_mode)) { sudo_warn(U_("%s is not a character device, not relabeling tty"), ttyn); goto bad; } (void)fcntl(se_state.ttyfd, F_SETFL, fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK); } if (fgetfilecon(se_state.ttyfd, &tty_con) < 0) { sudo_warn(U_("unable to get current tty context, not relabeling tty")); goto bad; } if (tty_con) { security_class_t tclass = string_to_security_class("chr_file"); if (tclass == 0) { sudo_warn(U_("unknown security class \"chr_file\", not relabeling tty")); goto bad; } if (security_compute_relabel(se_state.new_context, tty_con, tclass, &new_tty_con) < 0) { sudo_warn(U_("unable to get new tty context, not relabeling tty")); goto bad; } } if (new_tty_con != NULL) { if (fsetfilecon(se_state.ttyfd, new_tty_con) < 0) { sudo_warn(U_("unable to set new tty context")); goto bad; } } if (ptyfd != -1) { /* Reopen pty that was relabeled, std{in,out,err} are reset later. */ se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY, 0); if (se_state.ttyfd == -1 || fstat(se_state.ttyfd, &sb) == -1) { sudo_warn(U_("unable to open %s"), ttyn); goto bad; } if (!S_ISCHR(sb.st_mode)) { sudo_warn(U_("%s is not a character device, not relabeling tty"), ttyn); goto bad; } if (dup2(se_state.ttyfd, ptyfd) == -1) { sudo_warn("dup2"); goto bad; } } else { /* Re-open tty to get new label and reset std{in,out,err} */ close(se_state.ttyfd); se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY|O_NONBLOCK); if (se_state.ttyfd == -1 || fstat(se_state.ttyfd, &sb) == -1) { sudo_warn(U_("unable to open %s"), ttyn); goto bad; } if (!S_ISCHR(sb.st_mode)) { sudo_warn(U_("%s is not a character device, not relabeling tty"), ttyn); goto bad; } (void)fcntl(se_state.ttyfd, F_SETFL, fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK); for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) { if (isatty(fd) && dup2(se_state.ttyfd, fd) == -1) { sudo_warn("dup2"); goto bad; } } } /* Retain se_state.ttyfd so we can restore label when command finishes. */ (void)fcntl(se_state.ttyfd, F_SETFD, FD_CLOEXEC); se_state.ttyn = ttyn; se_state.tty_context = tty_con; se_state.new_tty_context = new_tty_con; debug_return_int(0); bad: if (se_state.ttyfd != -1 && se_state.ttyfd != ptyfd) { close(se_state.ttyfd); se_state.ttyfd = -1; } freecon(tty_con); debug_return_int(se_state.enforcing ? -1 : 0); }
int login_main(int argc, char **argv) { char tty[BUFSIZ]; char full_tty[200]; char fromhost[512]; char username[USERNAME_SIZE]; const char *tmp; int amroot; int flag; int failed; int count=0; struct passwd *pw, pw_copy; #ifdef CONFIG_WHEEL_GROUP struct group *grp; #endif int opt_preserve = 0; int opt_fflag = 0; char *opt_host = 0; int alarmstarted = 0; #ifdef CONFIG_SELINUX security_context_t stat_sid = NULL, sid = NULL, old_tty_sid=NULL, new_tty_sid=NULL; #endif username[0]=0; amroot = ( getuid ( ) == 0 ); signal ( SIGALRM, alarm_handler ); alarm ( TIMEOUT ); alarmstarted = 1; while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) { switch ( flag ) { case 'p': opt_preserve = 1; break; case 'f': /* * username must be a separate token * (-f root, *NOT* -froot). --marekm */ if ( optarg != argv[optind-1] ) bb_show_usage( ); if ( !amroot ) /* Auth bypass only if real UID is zero */ bb_error_msg_and_die ( "-f permission denied" ); safe_strncpy(username, optarg, USERNAME_SIZE); opt_fflag = 1; break; case 'h': opt_host = optarg; break; default: bb_show_usage( ); } } if (optind < argc) // user from command line (getty) safe_strncpy(username, argv[optind], USERNAME_SIZE); if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 )) return EXIT_FAILURE; /* Must be a terminal */ #ifdef CONFIG_FEATURE_UTMP checkutmp ( !amroot ); #endif tmp = ttyname ( 0 ); if ( tmp && ( strncmp ( tmp, "/dev/", 5 ) == 0 )) safe_strncpy ( tty, tmp + 5, sizeof( tty )); else if ( tmp && *tmp == '/' ) safe_strncpy ( tty, tmp, sizeof( tty )); else safe_strncpy ( tty, "UNKNOWN", sizeof( tty )); #ifdef CONFIG_FEATURE_UTMP if ( amroot ) memset ( utent.ut_host, 0, sizeof utent.ut_host ); #endif if ( opt_host ) { #ifdef CONFIG_FEATURE_UTMP safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host )); #endif snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s' from `%.200s'", tty, opt_host ); } else snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s'", tty ); setpgrp(); openlog ( "login", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH ); while ( 1 ) { failed = 0; if ( !username[0] ) if(!login_prompt ( username )) return EXIT_FAILURE; if ( !alarmstarted && ( TIMEOUT > 0 )) { alarm ( TIMEOUT ); alarmstarted = 1; } if (!( pw = getpwnam ( username ))) { pw_copy.pw_name = "UNKNOWN"; pw_copy.pw_passwd = "!"; opt_fflag = 0; failed = 1; } else pw_copy = *pw; pw = &pw_copy; if (( pw-> pw_passwd [0] == '!' ) || ( pw-> pw_passwd[0] == '*' )) failed = 1; if ( opt_fflag ) { opt_fflag = 0; goto auth_ok; } if (!failed && ( pw-> pw_uid == 0 ) && ( !check_tty ( tty ))) failed = 1; /* Don't check the password if password entry is empty (!) */ if ( !pw-> pw_passwd[0] ) goto auth_ok; /* authorization takes place here */ if ( correct_password ( pw )) goto auth_ok; failed = 1; auth_ok: if ( !failed) break; bb_do_delay(FAIL_DELAY); puts("Login incorrect"); username[0] = 0; if ( ++count == 3 ) { syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw->pw_name, fromhost); return EXIT_FAILURE; } } alarm ( 0 ); if ( check_nologin ( pw-> pw_uid == 0 )) return EXIT_FAILURE; #ifdef CONFIG_FEATURE_UTMP setutmp ( username, tty ); #endif if ( *tty != '/' ) snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty); else safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 ); #ifdef CONFIG_SELINUX if (is_selinux_enabled()) { struct stat st; int rc; if (get_default_context(username, NULL, &sid)) { fprintf(stderr, "Unable to get SID for %s\n", username); exit(1); } rc = getfilecon(full_tty,&stat_sid); freecon(stat_sid); if ((rc<0) || (stat(full_tty, &st)<0)) { fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", full_tty, strerror(errno)); return EXIT_FAILURE; } if (security_compute_relabel (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0) { fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", full_tty, strerror(errno)); return EXIT_FAILURE; } if(setfilecon(full_tty, new_tty_sid) != 0) { fprintf(stderr, "chsid(%.100s, %s) failed: %.100s\n", full_tty, new_tty_sid, strerror(errno)); return EXIT_FAILURE; } freecon(sid); freecon(old_tty_sid); freecon(new_tty_sid); } #endif if ( !is_my_tty ( full_tty )) syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty ); /* Try these, but don't complain if they fail * (for example when the root fs is read only) */ chown ( full_tty, pw-> pw_uid, pw-> pw_gid ); chmod ( full_tty, 0600 ); change_identity ( pw ); tmp = pw-> pw_shell; if(!tmp || !*tmp) tmp = DEFAULT_SHELL; setup_environment ( tmp, 1, !opt_preserve, pw ); motd ( ); signal ( SIGALRM, SIG_DFL ); /* default alarm signal */ if ( pw-> pw_uid == 0 ) syslog ( LOG_INFO, "root login %s\n", fromhost ); #ifdef CONFIG_SELINUX set_current_security_context(sid); #endif run_shell ( tmp, 1, 0, 0); /* exec the shell finally. */ return EXIT_FAILURE; }
/* * This function attempts to relabel the tty. If this function fails, then * the contexts are free'd and -1 is returned. On success, 0 is returned * and tty_context and new_tty_context are set. * * This function will not fail if it can not relabel the tty when selinux is * in permissive mode. */ static int relabel_tty(const char *ttyn, int ptyfd) { security_context_t tty_con = NULL; security_context_t new_tty_con = NULL; int fd; se_state.ttyfd = ptyfd; /* It is perfectly legal to have no tty. */ if (ptyfd == -1 && ttyn == NULL) return 0; /* If sudo is not allocating a pty for the command, open current tty. */ if (ptyfd == -1) { se_state.ttyfd = open(ttyn, O_RDWR|O_NONBLOCK); if (se_state.ttyfd == -1) { warning("unable to open %s, not relabeling tty", ttyn); if (se_state.enforcing) goto bad; } (void)fcntl(se_state.ttyfd, F_SETFL, fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK); } if (fgetfilecon(se_state.ttyfd, &tty_con) < 0) { warning("unable to get current tty context, not relabeling tty"); if (se_state.enforcing) goto bad; } if (tty_con && (security_compute_relabel(se_state.new_context, tty_con, SECCLASS_CHR_FILE, &new_tty_con) < 0)) { warning("unable to get new tty context, not relabeling tty"); if (se_state.enforcing) goto bad; } if (new_tty_con != NULL) { if (fsetfilecon(se_state.ttyfd, new_tty_con) < 0) { warning("unable to set new tty context"); if (se_state.enforcing) goto bad; } } if (ptyfd != -1) { /* Reopen pty that was relabeled, std{in,out,err} are reset later. */ se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY, 0); if (se_state.ttyfd == -1) { warning("cannot open %s", ttyn); if (se_state.enforcing) goto bad; } if (dup2(se_state.ttyfd, ptyfd) == -1) { warning("dup2"); goto bad; } } else { /* Re-open tty to get new label and reset std{in,out,err} */ close(se_state.ttyfd); se_state.ttyfd = open(ttyn, O_RDWR|O_NONBLOCK); if (se_state.ttyfd == -1) { warning("unable to open %s", ttyn); goto bad; } (void)fcntl(se_state.ttyfd, F_SETFL, fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK); for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) { if (isatty(fd) && dup2(se_state.ttyfd, fd) == -1) { warning("dup2"); goto bad; } } } /* Retain se_state.ttyfd so we can restore label when command finishes. */ (void)fcntl(se_state.ttyfd, F_SETFD, FD_CLOEXEC); se_state.ttyn = ttyn; se_state.tty_context = tty_con; se_state.new_tty_context = new_tty_con; return 0; bad: if (se_state.ttyfd != -1 && se_state.ttyfd != ptyfd) { close(se_state.ttyfd); se_state.ttyfd = -1; } freecon(tty_con); return -1; }