/* Return the default security context for the given username */ static security_context_t ssh_selinux_getctxbyname(char *pwname) { security_context_t sc = NULL; char *sename = NULL, *lvl = NULL; int r; #ifdef HAVE_GETSEUSERBYNAME if (getseuserbyname(pwname, &sename, &lvl) != 0) return NULL; #else sename = pwname; lvl = NULL; #endif #ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL r = get_default_context_with_level(sename, lvl, NULL, &sc); #else r = get_default_context(sename, NULL, &sc); #endif if (r != 0) { switch (security_getenforce()) { case -1: fatal("%s: ssh_selinux_getctxbyname: " "security_getenforce() failed", __func__); case 0: error("%s: Failed to get default SELinux security " "context for %s", __func__, pwname); sc = NULL; break; default: fatal("%s: Failed to get default SELinux security " "context for %s (in enforcing mode)", __func__, pwname); } } #ifdef HAVE_GETSEUSERBYNAME if (sename != NULL) xfree(sename); if (lvl != NULL) xfree(lvl); #endif return sc; }
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); } }
/* selinux_prepare_fork - Initialize context switching * * Returns * - 0 if everything is OK, * - +1 if the code should continue, even if SELinux wouldn't allow * (for instance due to permissive mode) * - -1 if the code should not continue */ int selinux_prepare_fork(char * name) { #ifndef SELINUX return 0; #else char * newcon = 0; char * curcon = 0; struct av_decision avd; int rc; int permissive = 0; int dom_permissive = 0; char * sename = 0; char * selevel = 0; /* * See if SELinux is enabled. * If not, then we can immediately tell the code * that everything is OK. */ rc = is_selinux_enabled(); if (rc == 0) { out(DEBUG, "SELinux is not enabled.\n"); return 0; } else if (rc == -1) { out(WARN, "Could not check SELinux state (is_selinux_enabled() failed)\n"); return 1; }; out(DEBUG, "SELinux is enabled.\n"); /* * See if SELinux is in enforcing mode * or permissive mode */ rc = security_getenforce(); if (rc == 0) { permissive = 1; } else if (rc == 1) { permissive = 0; } else { out(WARN, "Could not check SELinux mode (security_getenforce() failed)\n"); } out(DEBUG, "SELinux mode is %s\n", permissive ? "permissive" : "enforcing"); /* * Get the current SELinux context of the process. * Always interesting to log this for end users * trying to debug a possible issue. */ rc = getcon(&curcon); if (rc) { out(WARN, "Could not get current SELinux context (getcon() failed)\n"); if (permissive) return +1; else return -1; }; out(DEBUG, "Currently in SELinux context \"%s\"\n", (char *) curcon); /* * Get the SELinux user given the Linux user * name passed on to this function. */ rc = getseuserbyname(name, &sename, &selevel); if (rc) { out(WARN, "Could not find SELinux user for Linux user \"%s\" (getseuserbyname() failed)\n", name); freecon(curcon); if (permissive) return +1; else return -1; }; out(DEBUG, "SELinux user for Linux user \"%s\" is \"%s\"\n", name, sename); /* * Find out what the context is that this process should transition * to. */ rc = get_default_context(sename, NULL, &newcon); if (rc) { out(WARN, "Could not deduce default context for SELinux user \"%s\" given our current context (\"%s\")\n", sename, (char *) curcon); freecon(curcon); if (permissive) return +1; else return -1; }; out(DEBUG, "SELinux context to transition to is \"%s\"\n", (char *) newcon); /* * Now let's look if we are allowed to transition to the new context. * We currently only check the transition access for the process class. However, * transitioning is a bit more complex (execute rights on target context, * entrypoint of that context for the new domain, no constraints like target * domain not being a valid one, MLS constraints, etc.). */ rc = security_compute_av_flags(curcon, newcon, SECCLASS_PROCESS, PROCESS__TRANSITION, &avd); if (rc) { out(WARN, "Could not deduce rights for transitioning \"%s\" -> \"%s\" (security_compute_av_flags() failed)\n", (char *) curcon, (char *) newcon); freecon(curcon); freecon(newcon); if (permissive) return +1; else return -1; }; /* Validate the response * * We are interested in two things: * - Is the transition allowed, but also * - Is the permissive flag set * * If the permissive flag is set, then we * know the current domain is permissive * (even if the rest of the system is in * enforcing mode). */ if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) { out(DEBUG, "The SELINUX_AVD_FLAGS_PERMISSIVE flag is set, so domain is permissive.\n"); dom_permissive = 1; }; if (!(avd.allowed & PROCESS__TRANSITION)) { // The transition is denied if (permissive) { out(DEBUG, "Transition is not allowed by SELinux, but permissive mode is enabled. Continuing.\n"); }; if (dom_permissive) { out(DEBUG, "Transition is not allowed by SELinux, but domain is in permissive mode. Continuing.\n"); }; if ((permissive == 0) && (dom_permissive == 0)) { out(WARN, "The domain transition is not allowed and we are not in permissive mode.\n"); freecon(curcon); freecon(newcon); return -1; }; }; /* * Set the context for the fork (process execution). */ rc = setexeccon(newcon); if (rc) { out(WARN, "Could not set execution context (setexeccon() failed)\n"); freecon(curcon); freecon(newcon); if ((permissive) || (dom_permissive)) return +1; else return -1; }; freecon(newcon); freecon(curcon); return 0; #endif };
static Bool StartClient(struct verify_info *verify, struct display *d, int *pidp, char *name, char *passwd #ifdef WITH_CONSOLE_KIT , char *ck_session_cookie #endif ) { char **f; const char *home; char *failsafeArgv[2]; int pid; #ifdef HAS_SETUSERCONTEXT struct passwd *pwd; #endif #ifdef USE_PAM pam_handle_t *pamh = thepamh(); #endif if (verify->argv) { WDMDebug("StartSession %s: ", verify->argv[0]); for (f = verify->argv; *f; f++) WDMDebug("%s ", *f); WDMDebug("; "); } if (verify->userEnviron) { for (f = verify->userEnviron; *f; f++) WDMDebug("%s ", *f); WDMDebug("\n"); } /* Do system-dependent login setup here */ if (setgid(verify->gid) < 0) { WDMError("setgid %d (user \"%s\") failed, errno=%d\n", verify->gid, name, errno); return (0); } #if defined(BSD) && (BSD >= 199103) if (setlogin(name) < 0) { WDMError("setlogin for \"%s\" failed, errno=%d", name, errno); return (0); } #endif if (initgroups(name, verify->gid) < 0) { WDMError("initgroups for \"%s\" failed, errno=%d\n", name, errno); return (0); } #ifdef USE_PAM if (pamh) { if (pam_setcred(pamh, PAM_ESTABLISH_CRED) != PAM_SUCCESS) { WDMError("pam_setcred failed, errno=%d\n", errno); log_to_audit_system(0); pam_end(pamh, PAM_SUCCESS); pamh = NULL; return 0; } pam_open_session(pamh, 0); log_to_audit_system(1); /* pass in environment variables set by libpam and modules it called */ { long i; char **pam_env = pam_getenvlist(pamh); for (i = 0; pam_env && pam_env[i]; i++) { verify->userEnviron = WDMPutEnv(verify->userEnviron, pam_env[i]); } } } #endif switch (pid = fork()) { case 0: CleanUpChild(); #ifdef XDMCP /* The chooser socket is not closed by CleanUpChild() */ DestroyWellKnownSockets(); #endif if (setuid(verify->uid) < 0) { WDMError("setuid %d (user \"%s\") failed, errno=%d\n", verify->uid, name, errno); return (0); } /* * for Security Enhanced Linux, * set the default security context for this user. */ #ifdef WITH_SELINUX if (is_selinux_enabled()) { security_context_t scontext; if (get_default_context(name, NULL, &scontext)) WDMError("Failed to get default security context" " for %s.", name); WDMDebug("setting security context to %s", scontext); if (setexeccon(scontext)) { freecon(scontext); WDMError("Failed to set exec security context %s " "for %s.", scontext, name); } freecon(scontext); } #endif /* * for user-based authorization schemes, * use the password to get the user's credentials. */ bzero(passwd, strlen(passwd)); #ifdef WITH_CONSOLE_KIT if (ck_session_cookie != NULL) { verify->userEnviron = WDMSetEnv(verify->userEnviron, "XDG_SESSION_COOKIE", ck_session_cookie); } #endif SetUserAuthorization(d, verify); home = WDMGetEnv(verify->userEnviron, "HOME"); if (home) if (chdir(home) == -1) { WDMError("user \"%s\": cannot chdir to home \"%s\" (err %d), using \"/\"\n", WDMGetEnv(verify->userEnviron, "USER"), home, errno); chdir("/"); verify->userEnviron = WDMSetEnv(verify->userEnviron, "HOME", "/"); } if (verify->argv) { WDMDebug("executing session %s\n", verify->argv[0]); execute(verify->argv, verify->userEnviron); WDMError("Session \"%s\" execution failed (err %d)\n", verify->argv[0], errno); } else { WDMError("Session has no command/arguments\n"); } failsafeArgv[0] = d->failsafeClient; failsafeArgv[1] = 0; execute(failsafeArgv, verify->userEnviron); exit(1); case -1: bzero(passwd, strlen(passwd)); WDMDebug("StartSession, fork failed\n"); WDMError("can't start session on \"%s\", fork failed, errno=%d\n", d->name, errno); return 0; default: bzero(passwd, strlen(passwd)); WDMDebug("StartSession, fork succeeded %d\n", pid); *pidp = pid; return 1; } }
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; }