static void setup_tty (void) { TERMIO termio; int erasechar; int killchar; if (GTTY (0, &termio) == 0) { /* get terminal characteristics */ /* * Add your favorite terminal modes here ... */ termio.c_lflag |= ISIG | ICANON | ECHO | ECHOE; termio.c_iflag |= ICRNL; #if defined(ECHOKE) && defined(ECHOCTL) termio.c_lflag |= ECHOKE | ECHOCTL; #endif #if defined(ECHOPRT) && defined(NOFLSH) && defined(TOSTOP) termio.c_lflag &= ~(ECHOPRT | NOFLSH | TOSTOP); #endif #ifdef ONLCR termio.c_oflag |= ONLCR; #endif /* leave these values unchanged if not specified in login.defs */ erasechar = getdef_num ("ERASECHAR", (int) termio.c_cc[VERASE]); killchar = getdef_num ("KILLCHAR", (int) termio.c_cc[VKILL]); termio.c_cc[VERASE] = (cc_t) erasechar; termio.c_cc[VKILL] = (cc_t) killchar; /* Make sure the values were valid. * getdef_num cannot validate this. */ if (erasechar != (int) termio.c_cc[VERASE]) { fprintf (stderr, _("configuration error - cannot parse %s value: '%d'"), "ERASECHAR", erasechar); exit (1); } if (killchar != (int) termio.c_cc[VKILL]) { fprintf (stderr, _("configuration error - cannot parse %s value: '%d'"), "KILLCHAR", killchar); exit (1); } /* * ttymon invocation prefers this, but these settings * won't come into effect after the first username login */ (void) STTY (0, &termio); } }
static void setup_tty() { TERMIO termio; GTTY (0, &termio); /* get terminal characteristics */ /* * Add your favorite terminal modes here ... */ #ifndef USE_SGTTY termio.c_lflag |= ISIG|ICANON|ECHO|ECHOE; termio.c_iflag |= ICRNL; #if defined(ECHOKE) && defined(ECHOCTL) termio.c_lflag |= ECHOKE|ECHOCTL; #endif #if defined(ECHOPRT) && defined(NOFLSH) && defined(TOSTOP) termio.c_lflag &= ~(ECHOPRT|NOFLSH|TOSTOP); #endif #ifdef ONLCR termio.c_oflag |= ONLCR; #endif #ifdef SUN4 /* * Terminal setup for SunOS 4.1 courtesy of Steve Allen * at UCO/Lick. */ termio.c_cc[VEOF] = '\04'; termio.c_cflag &= ~CSIZE; termio.c_cflag |= (PARENB|CS7); termio.c_lflag |= (ISIG|ICANON|ECHO|IEXTEN); termio.c_iflag |= (BRKINT|IGNPAR|ISTRIP|IMAXBEL|ICRNL|IXON); termio.c_iflag &= ~IXANY; termio.c_oflag |= (XTABS|OPOST|ONLCR); #endif termio.c_cc[VERASE] = getdef_num("ERASECHAR", '\b'); termio.c_cc[VKILL] = getdef_num("KILLCHAR", '\025'); /* * ttymon invocation prefers this, but these settings won't come into * effect after the first username login */ #else #endif /* !BSD */ STTY (0, &termio); }
void Ssu::storeAuthorizedKeys(QByteArray data){ QDir dir; SsuLog *ssuLog = SsuLog::instance(); int uid_min = getdef_num("UID_MIN", -1); QString homePath; if (getuid() >= uid_min){ homePath = dir.homePath(); } else if (getuid() == 0){ // place authorized_keys in the default users home when run with uid0 struct passwd *pw = getpwuid(uid_min); if (pw == NULL){ ssuLog->print(LOG_DEBUG, QString("Unable to find password entry for uid %1") .arg(uid_min)); return; } //homePath = QString(pw->pw_dir); homePath = pw->pw_dir; // use users uid/gid for creating the directories and files setegid(pw->pw_gid); seteuid(uid_min); ssuLog->print(LOG_DEBUG, QString("Dropping to %1/%2 for writing authorized keys") .arg(uid_min) .arg(pw->pw_gid)); } else return; homePath = Sandbox::map(homePath); if (dir.exists(homePath + "/.ssh/authorized_keys")){ ssuLog->print(LOG_DEBUG, QString(".ssh/authorized_keys already exists in %1") .arg(homePath)); restoreUid(); return; } if (!dir.exists(homePath + "/.ssh")) if (!dir.mkdir(homePath + "/.ssh")){ ssuLog->print(LOG_DEBUG, QString("Unable to create .ssh in %1") .arg(homePath)); restoreUid(); return; } QFile::setPermissions(homePath + "/.ssh", QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); QFile authorizedKeys(homePath + "/.ssh/authorized_keys"); authorizedKeys.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate); authorizedKeys.setPermissions(QFile::ReadOwner | QFile::WriteOwner); QTextStream out(&authorizedKeys); out << data; out.flush(); authorizedKeys.close(); restoreUid(); }
void chown_tty (const struct passwd *info) { struct group *grent; gid_t gid; /* * See if login.defs has some value configured for the port group * ID. Otherwise, use the user's primary group ID. */ grent = getgr_nam_gid (getdef_str ("TTYGROUP")); if (NULL != grent) { gid = grent->gr_gid; } else { gid = info->pw_gid; } /* * Change the permissions on the TTY to be owned by the user with * the group as determined above. */ if ( (fchown (STDIN_FILENO, info->pw_uid, gid) != 0) || (fchmod (STDIN_FILENO, getdef_num ("TTYPERM", 0600)) != 0)) { int err = errno; fprintf (stderr, _("Unable to change owner or mode of tty stdin: %s"), strerror (err)); SYSLOG ((LOG_WARN, "unable to change owner or mode of tty stdin for user `%s': %s\n", info->pw_name, strerror (err))); if (EROFS != err) { closelog (); exit (EXIT_FAILURE); } } #ifdef __linux__ /* * Please don't add code to chown /dev/vcs* to the user logging in - * it's a potential security hole. I wouldn't like the previous user * to hold the file descriptor open and watch my screen. We don't * have the *BSD revoke() system call yet, and vhangup() only works * for tty devices (which vcs* is not). --marekm */ #endif }
QDBusConnection SsuCoreConfig::userSessionBus(){ int uid_min = getdef_num("UID_MIN", -1); // For calls from valid UID we assume that they are properly logged in users. // If they are not the call will fail, but it's their fault. if (getuid() >= uid_min){ return QDBusConnection::sessionBus(); } else { // DBus security policy will prevent this beeing used by callers other // than root at the moment. Still do it generic in case DBus policy will // be extended later, and just use the usual 'DBus: THOU SHALL NOT PASS!' // @TODO the uid to be used should be determined using the logind API from // systemd package to support multiuser systems in the future QString sessionBusAddress=QString("unix:path=/run/user/%1/dbus/user_bus_socket") .arg(uid_min); return QDBusConnection::connectToBus(sessionBusAddress, "userSessionBus"); } }
/* * check_pw_file - check the content of the passwd file */ static void check_pw_file (int *errors, bool *changed) { struct commonio_entry *pfe, *tpfe; struct passwd *pwd; struct spwd *spw; /* * Loop through the entire password file. */ for (pfe = __pw_get_head (); NULL != pfe; pfe = pfe->next) { /* * If this is a NIS line, skip it. You can't "know" what NIS * is going to do without directly asking NIS ... */ if (('+' == pfe->line[0]) || ('-' == pfe->line[0])) { continue; } /* * Start with the entries that are completely corrupt. They * have no (struct passwd) entry because they couldn't be * parsed properly. */ if (NULL == pfe->eptr) { /* * Tell the user this entire line is bogus and ask * them to delete it. */ puts (_("invalid password file entry")); printf (_("delete line '%s'? "), pfe->line); *errors += 1; /* * prompt the user to delete the entry or not */ if (!yes_or_no (read_only)) { continue; } /* * All password file deletions wind up here. This * code removes the current entry from the linked * list. When done, it skips back to the top of the * loop to try out the next list element. */ delete_pw: SYSLOG ((LOG_INFO, "delete passwd line '%s'", pfe->line)); *changed = true; __pw_del_entry (pfe); continue; } /* * Password structure is good, start using it. */ pwd = pfe->eptr; /* * Make sure this entry has a unique name. */ for (tpfe = __pw_get_head (); NULL != tpfe; tpfe = tpfe->next) { const struct passwd *ent = tpfe->eptr; /* * Don't check this entry */ if (tpfe == pfe) { continue; } /* * Don't check invalid entries. */ if (NULL == ent) { continue; } if (strcmp (pwd->pw_name, ent->pw_name) != 0) { continue; } /* * Tell the user this entry is a duplicate of * another and ask them to delete it. */ puts (_("duplicate password entry")); printf (_("delete line '%s'? "), pfe->line); *errors += 1; /* * prompt the user to delete the entry or not */ if (yes_or_no (read_only)) { goto delete_pw; } } /* * Check for invalid usernames. --marekm */ if (!is_valid_user_name (pwd->pw_name)) { printf (_("invalid user name '%s'\n"), pwd->pw_name); *errors += 1; } /* * Check for invalid user ID. */ if (pwd->pw_uid == (uid_t)-1) { printf (_("invalid user ID '%lu'\n"), (long unsigned int)pwd->pw_uid); *errors += 1; } /* * Make sure the primary group exists */ /* local, no need for xgetgrgid */ if (!quiet && (NULL == getgrgid (pwd->pw_gid))) { /* * No primary group, just give a warning */ printf (_("user '%s': no group %lu\n"), pwd->pw_name, (unsigned long) pwd->pw_gid); *errors += 1; } /* * Make sure the home directory exists */ if (!quiet && (access (pwd->pw_dir, F_OK) != 0)) { /* * Home directory doesn't exist, give a warning */ printf (_("user '%s': directory '%s' does not exist\n"), pwd->pw_name, pwd->pw_dir); *errors += 1; } /* * Make sure the login shell is executable */ if ( !quiet && ('\0' != pwd->pw_shell[0]) && (access (pwd->pw_shell, F_OK) != 0)) { /* * Login shell doesn't exist, give a warning */ printf (_("user '%s': program '%s' does not exist\n"), pwd->pw_name, pwd->pw_shell); *errors += 1; } /* * Make sure this entry exists in the /etc/shadow file. */ if (is_shadow) { spw = (struct spwd *) spw_locate (pwd->pw_name); if (NULL == spw) { printf (_("no matching password file entry in %s\n"), spw_file); printf (_("add user '%s' in %s? "), pwd->pw_name, spw_file); *errors += 1; if (yes_or_no (read_only)) { struct spwd sp; struct passwd pw; sp.sp_namp = pwd->pw_name; sp.sp_pwdp = pwd->pw_passwd; sp.sp_min = getdef_num ("PASS_MIN_DAYS", -1); sp.sp_max = getdef_num ("PASS_MAX_DAYS", -1); sp.sp_warn = getdef_num ("PASS_WARN_AGE", -1); sp.sp_inact = -1; sp.sp_expire = -1; sp.sp_flag = SHADOW_SP_FLAG_UNSET; sp.sp_lstchg = (long) time ((time_t *) 0) / SCALE; if (0 == sp.sp_lstchg) { /* Better disable aging than * requiring a password change */ sp.sp_lstchg = -1; } *changed = true; if (spw_update (&sp) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, spw_dbname (), sp.sp_namp); exit (E_CANTUPDATE); } /* remove password from /etc/passwd */ pw = *pwd; pw.pw_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */ if (pw_update (&pw) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, pw_dbname (), pw.pw_name); exit (E_CANTUPDATE); } } } else { /* The passwd entry has a shadow counterpart. * Make sure no passwords are in passwd. */ if (strcmp (pwd->pw_passwd, SHADOW_PASSWD_STRING) != 0) { printf (_("user %s has an entry in %s, but its password field in %s is not set to 'x'\n"), pwd->pw_name, spw_file, pwd_file); *errors += 1; } } } } }
int main (int argc, char **argv) { const struct passwd *pw; struct passwd pwent; const struct spwd *sp; struct spwd spent; Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); OPENLOG ("pwconv"); process_flags (argc, argv); #ifdef WITH_TCB if (getdef_bool("USE_TCB")) { fprintf (stderr, _("%s: can't work with tcb enabled\n"), Prog); exit (E_FAILURE); } #endif /* WITH_TCB */ if (pw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, pw_dbname ()); fail_exit (E_PWDBUSY); } pw_locked = true; if (pw_open (O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); fail_exit (E_MISSING); } if (spw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, spw_dbname ()); fail_exit (E_PWDBUSY); } spw_locked = true; if (spw_open (O_CREAT | O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ()); fail_exit (E_FAILURE); } /* * Remove /etc/shadow entries for users not in /etc/passwd. */ (void) spw_rewind (); while ((sp = spw_next ()) != NULL) { if (pw_locate (sp->sp_namp) != NULL) { continue; } if (spw_remove (sp->sp_namp) == 0) { /* * This shouldn't happen (the entry exists) but... */ fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, sp->sp_namp, spw_dbname ()); fail_exit (E_FAILURE); } } /* * Update shadow entries which don't have "x" as pw_passwd. Add any * missing shadow entries. */ (void) pw_rewind (); while ((pw = pw_next ()) != NULL) { sp = spw_locate (pw->pw_name); if (NULL != sp) { /* do we need to update this entry? */ if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) { continue; } /* update existing shadow entry */ spent = *sp; } else { /* add new shadow entry */ memset (&spent, 0, sizeof spent); spent.sp_namp = pw->pw_name; spent.sp_min = getdef_num ("PASS_MIN_DAYS", -1); spent.sp_max = getdef_num ("PASS_MAX_DAYS", -1); spent.sp_warn = getdef_num ("PASS_WARN_AGE", -1); spent.sp_inact = -1; spent.sp_expire = -1; spent.sp_flag = SHADOW_SP_FLAG_UNSET; } spent.sp_pwdp = pw->pw_passwd; spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE; if (0 == spent.sp_lstchg) { /* Better disable aging than requiring a password * change */ spent.sp_lstchg = -1; } if (spw_update (&spent) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, spw_dbname (), spent.sp_namp); fail_exit (E_FAILURE); } /* remove password from /etc/passwd */ pwent = *pw; pwent.pw_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */ if (pw_update (&pwent) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, pw_dbname (), pwent.pw_name); fail_exit (E_FAILURE); } } if (spw_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); fail_exit (E_FAILURE); } if (pw_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); fail_exit (E_FAILURE); } /* /etc/passwd- (backup file) */ if (chmod (PASSWD_FILE "-", 0600) != 0) { fprintf (stderr, _("%s: failed to change the mode of %s to 0600\n"), Prog, PASSWD_FILE "-"); SYSLOG ((LOG_ERR, "failed to change the mode of %s to 0600", PASSWD_FILE "-")); /* continue */ } if (pw_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); /* continue */ } if (spw_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); /* continue */ } nscd_flush_cache ("passwd"); return E_SUCCESS; }
/* * add_passwd - add or update the encrypted password */ static int add_passwd (struct passwd *pwd, const char *password) { const struct spwd *sp; struct spwd spent; char *cp; #ifndef USE_PAM void *crypt_arg = NULL; if (crypt_method != NULL) { #ifdef USE_SHA_CRYPT if (sflg) { crypt_arg = &sha_rounds; } #endif /* USE_SHA_CRYPT */ } /* * In the case of regular password files, this is real easy - pwd * points to the entry in the password file. Shadow files are * harder since there are zillions of things to do ... */ if (!is_shadow) { return update_passwd (pwd, password); } #endif /* USE_PAM */ /* * Do the first and easiest shadow file case. The user already * exists in the shadow password file. */ sp = spw_locate (pwd->pw_name); #ifndef USE_PAM if (NULL != sp) { spent = *sp; if ( (NULL != crypt_method) && (0 == strcmp(crypt_method, "NONE"))) { spent.sp_pwdp = (char *)password; } else { const char *salt = crypt_make_salt (crypt_method, crypt_arg); cp = pw_encrypt (password, salt); if (NULL == cp) { fprintf (stderr, _("%s: failed to crypt password with salt '%s': %s\n"), Prog, salt, strerror (errno)); return 1; } spent.sp_pwdp = cp; } spent.sp_lstchg = (long) gettime () / SCALE; if (0 == spent.sp_lstchg) { /* Better disable aging than requiring a password * change */ spent.sp_lstchg = -1; } return (spw_update (&spent) == 0); } /* * Pick the next easiest case - the user has an encrypted password * which isn't equal to "x". The password was set to "x" earlier * when the entry was created, so this user would have to have had * the password set someplace else. */ if (strcmp (pwd->pw_passwd, "x") != 0) { return update_passwd (pwd, password); } #else /* USE_PAM */ /* * If there is already a shadow entry, do not touch it. * If there is already a passwd entry with a password, do not * touch it. * The password will be updated later for all users using PAM. */ if ( (NULL != sp) || (strcmp (pwd->pw_passwd, "x") != 0)) { return 0; } #endif /* USE_PAM */ /* * Now the really hard case - I need to create an entirely new * shadow password file entry. */ spent.sp_namp = pwd->pw_name; #ifndef USE_PAM if ((crypt_method != NULL) && (0 == strcmp(crypt_method, "NONE"))) { spent.sp_pwdp = (char *)password; } else { const char *salt = crypt_make_salt (crypt_method, crypt_arg); cp = pw_encrypt (password, salt); if (NULL == cp) { fprintf (stderr, _("%s: failed to crypt password with salt '%s': %s\n"), Prog, salt, strerror (errno)); return 1; } spent.sp_pwdp = cp; } #else /* * Lock the password. * The password will be updated later for all users using PAM. */ spent.sp_pwdp = "!"; #endif spent.sp_lstchg = (long) gettime () / SCALE; if (0 == spent.sp_lstchg) { /* Better disable aging than requiring a password change */ spent.sp_lstchg = -1; } spent.sp_min = getdef_num ("PASS_MIN_DAYS", 0); /* 10000 is infinity this week */ spent.sp_max = getdef_num ("PASS_MAX_DAYS", 10000); spent.sp_warn = getdef_num ("PASS_WARN_AGE", -1); spent.sp_inact = -1; spent.sp_expire = -1; spent.sp_flag = SHADOW_SP_FLAG_UNSET; return (spw_update (&spent) == 0); }
/* * login - create a new login session for a user * * login is typically called by getty as the second step of a * new user session. getty is responsible for setting the line * characteristics to a reasonable set of values and getting * the name of the user to be logged in. login may also be * called to create a new user session on a pty for a variety * of reasons, such as X servers or network logins. * * the flags which login supports are * * -p - preserve the environment * -r - perform autologin protocol for rlogin * -f - do not perform authentication, user is preauthenticated * -h - the name of the remote host */ int main (int argc, char **argv) { const char *tmptty; char tty[BUFSIZ]; #ifdef RLOGIN char term[128] = ""; #endif /* RLOGIN */ #if defined(HAVE_STRFTIME) && !defined(USE_PAM) char ptime[80]; #endif unsigned int delay; unsigned int retries; bool failed; bool subroot = false; #ifndef USE_PAM bool is_console; #endif int err; const char *cp; char *tmp; char fromhost[512]; struct passwd *pwd = NULL; char **envp = environ; const char *failent_user; /*@null@*/struct utmp *utent; #ifdef USE_PAM int retcode; pid_t child; char *pam_user = NULL; #else struct spwd *spwd = NULL; #endif /* * Some quick initialization. */ sanitize_env (); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); initenv (); amroot = (getuid () == 0); Prog = Basename (argv[0]); if (geteuid() != 0) { fprintf (stderr, _("%s: Cannot possibly work without effective root\n"), Prog); exit (1); } process_flags (argc, argv); if ((isatty (0) == 0) || (isatty (1) == 0) || (isatty (2) == 0)) { exit (1); /* must be a terminal */ } utent = get_current_utmp (); /* * Be picky if run by normal users (possible if installed setuid * root), but not if run by root. This way it still allows logins * even if your getty is broken, or if something corrupts utmp, * but users must "exec login" which will use the existing utmp * entry (will not overwrite remote hostname). --marekm */ if (!amroot && (NULL == utent)) { (void) puts (_("No utmp entry. You must exec \"login\" from the lowest level \"sh\"")); exit (1); } /* NOTE: utent might be NULL afterwards */ tmptty = ttyname (0); if (NULL == tmptty) { tmptty = "UNKNOWN"; } STRFCPY (tty, tmptty); #ifndef USE_PAM is_console = console (tty); #endif if (rflg || hflg) { /* * Add remote hostname to the environment. I think * (not sure) I saw it once on Irix. --marekm */ addenv ("REMOTEHOST", hostname); } if (fflg) { preauth_flag = true; } if (hflg) { reason = PW_RLOGIN; } #ifdef RLOGIN if (rflg) { assert (NULL == username); username = xmalloc (USER_NAME_MAX_LENGTH + 1); username[USER_NAME_MAX_LENGTH] = '\0'; if (do_rlogin (hostname, username, USER_NAME_MAX_LENGTH, term, sizeof term)) { preauth_flag = true; } else { free (username); username = NULL; } } #endif /* RLOGIN */ OPENLOG ("login"); setup_tty (); #ifndef USE_PAM (void) umask (getdef_num ("UMASK", GETDEF_DEFAULT_UMASK)); { /* * Use the ULIMIT in the login.defs file, and if * there isn't one, use the default value. The * user may have one for themselves, but otherwise, * just take what you get. */ long limit = getdef_long ("ULIMIT", -1L); if (limit != -1) { set_filesize_limit (limit); } } #endif /* * The entire environment will be preserved if the -p flag * is used. */ if (pflg) { while (NULL != *envp) { /* add inherited environment, */ addenv (*envp, NULL); /* some variables change later */ envp++; } } #ifdef RLOGIN if (term[0] != '\0') { addenv ("TERM", term); } else #endif /* RLOGIN */ { /* preserve TERM from getty */ if (!pflg) { tmp = getenv ("TERM"); if (NULL != tmp) { addenv ("TERM", tmp); } } } init_env (); if (optind < argc) { /* now set command line variables */ set_env (argc - optind, &argv[optind]); } if (rflg || hflg) { cp = hostname; #ifdef HAVE_STRUCT_UTMP_UT_HOST } else if ((NULL != utent) && ('\0' != utent->ut_host[0])) { cp = utent->ut_host; #endif /* HAVE_STRUCT_UTMP_UT_HOST */ } else { cp = ""; } if ('\0' != *cp) { snprintf (fromhost, sizeof fromhost, " on '%.100s' from '%.200s'", tty, cp); } else { snprintf (fromhost, sizeof fromhost, " on '%.100s'", tty); } top: /* only allow ALARM sec. for login */ (void) signal (SIGALRM, alarm_handler); timeout = getdef_unum ("LOGIN_TIMEOUT", ALARM); if (timeout > 0) { (void) alarm (timeout); } environ = newenvp; /* make new environment active */ delay = getdef_unum ("FAIL_DELAY", 1); retries = getdef_unum ("LOGIN_RETRIES", RETRIES); #ifdef USE_PAM retcode = pam_start ("login", username, &conv, &pamh); if (retcode != PAM_SUCCESS) { fprintf (stderr, _("login: PAM Failure, aborting: %s\n"), pam_strerror (pamh, retcode)); SYSLOG ((LOG_ERR, "Couldn't initialize PAM: %s", pam_strerror (pamh, retcode))); exit (99); } /* * hostname & tty are either set to NULL or their correct values, * depending on how much we know. We also set PAM's fail delay to * ours. * * PAM_RHOST and PAM_TTY are used for authentication, only use * information coming from login or from the caller (e.g. no utmp) */ retcode = pam_set_item (pamh, PAM_RHOST, hostname); PAM_FAIL_CHECK; retcode = pam_set_item (pamh, PAM_TTY, tty); PAM_FAIL_CHECK; #ifdef HAS_PAM_FAIL_DELAY retcode = pam_fail_delay (pamh, 1000000 * delay); PAM_FAIL_CHECK; #endif /* if fflg, then the user has already been authenticated */ if (!fflg) { unsigned int failcount = 0; char hostn[256]; char loginprompt[256]; /* That's one hell of a prompt :) */ /* Make the login prompt look like we want it */ if (gethostname (hostn, sizeof (hostn)) == 0) { snprintf (loginprompt, sizeof (loginprompt), _("%s login: "******"login: "******"TOO MANY LOGIN TRIES (%u)%s FOR '%s'", failcount, fromhost, failent_user)); fprintf(stderr, _("Maximum number of tries exceeded (%u)\n"), failcount); PAM_END; exit(0); } else if (retcode == PAM_ABORT) { /* Serious problems, quit now */ (void) fputs (_("login: abort requested by PAM\n"), stderr); SYSLOG ((LOG_ERR,"PAM_ABORT returned from pam_authenticate()")); PAM_END; exit(99); } else if (retcode != PAM_SUCCESS) { SYSLOG ((LOG_NOTICE,"FAILED LOGIN (%u)%s FOR '%s', %s", failcount, fromhost, failent_user, pam_strerror (pamh, retcode))); failed = true; } if (!failed) { break; } #ifdef WITH_AUDIT audit_fd = audit_open (); audit_log_acct_message (audit_fd, AUDIT_USER_LOGIN, NULL, /* Prog. name */ "login", failent_user, AUDIT_NO_ID, hostname, NULL, /* addr */ tty, 0); /* result */ close (audit_fd); #endif /* WITH_AUDIT */ (void) puts (""); (void) puts (_("Login incorrect")); if (failcount >= retries) { SYSLOG ((LOG_NOTICE, "TOO MANY LOGIN TRIES (%u)%s FOR '%s'", failcount, fromhost, failent_user)); fprintf(stderr, _("Maximum number of tries exceeded (%u)\n"), failcount); PAM_END; exit(0); } /* * Let's give it another go around. * Even if a username was given on the command * line, prompt again for the username. */ retcode = pam_set_item (pamh, PAM_USER, NULL); PAM_FAIL_CHECK; } /* We don't get here unless they were authenticated above */ (void) alarm (0); } /* Check the account validity */ retcode = pam_acct_mgmt (pamh, 0); if (retcode == PAM_NEW_AUTHTOK_REQD) { retcode = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK); } PAM_FAIL_CHECK; /* Open the PAM session */ get_pam_user (&pam_user); retcode = pam_open_session (pamh, hushed (pam_user) ? PAM_SILENT : 0); PAM_FAIL_CHECK; /* Grab the user information out of the password file for future usage * First get the username that we are actually using, though. * * From now on, we will discard changes of the user (PAM_USER) by * PAM APIs. */ get_pam_user (&pam_user); if (NULL != username) { free (username); } username = pam_user; failent_user = get_failent_user (username); pwd = xgetpwnam (username); if (NULL == pwd) { SYSLOG ((LOG_ERR, "cannot find user %s", failent_user)); exit (1); } /* This set up the process credential (group) and initialize the * supplementary group access list. * This has to be done before pam_setcred */ if (setup_groups (pwd) != 0) { exit (1); } retcode = pam_setcred (pamh, PAM_ESTABLISH_CRED); PAM_FAIL_CHECK; /* NOTE: If pam_setcred changes PAM_USER, this will not be taken * into account. */ #else /* ! USE_PAM */ while (true) { /* repeatedly get login/password pairs */ /* user_passwd is always a pointer to this constant string * or a passwd or shadow password that will be memzero by * pw_free / spw_free. * Do not free() user_passwd. */ const char *user_passwd = "!"; /* Do some cleanup to avoid keeping entries we do not need * anymore. */ if (NULL != pwd) { pw_free (pwd); pwd = NULL; } if (NULL != spwd) { spw_free (spwd); spwd = NULL; } failed = false; /* haven't failed authentication yet */ if (NULL == username) { /* need to get a login id */ if (subroot) { closelog (); exit (1); } preauth_flag = false; username = xmalloc (USER_NAME_MAX_LENGTH + 1); username[USER_NAME_MAX_LENGTH] = '\0'; login_prompt (_("\n%s login: "******"!", * the account is locked and the user cannot * login, even if they have been * "pre-authenticated." */ if ( ('!' == user_passwd[0]) || ('*' == user_passwd[0])) { failed = true; } } if (strcmp (user_passwd, SHADOW_PASSWD_STRING) == 0) { spwd = xgetspnam (username); if (NULL != spwd) { user_passwd = spwd->sp_pwdp; } else { /* The user exists in passwd, but not in * shadow. SHADOW_PASSWD_STRING indicates * that the password shall be in shadow. */ SYSLOG ((LOG_WARN, "no shadow password for '%s'%s", username, fromhost)); } } /* * The -r and -f flags provide a name which has already * been authenticated by some server. */ if (preauth_flag) { goto auth_ok; } if (pw_auth (user_passwd, username, reason, (char *) 0) == 0) { goto auth_ok; } SYSLOG ((LOG_WARN, "invalid password for '%s' %s", failent_user, fromhost)); failed = true; auth_ok: /* * This is the point where all authenticated users wind up. * If you reach this far, your password has been * authenticated and so on. */ if ( !failed && (NULL != pwd) && (0 == pwd->pw_uid) && !is_console) { SYSLOG ((LOG_CRIT, "ILLEGAL ROOT LOGIN %s", fromhost)); failed = true; } if ( !failed && !login_access (username, ('\0' != *hostname) ? hostname : tty)) { SYSLOG ((LOG_WARN, "LOGIN '%s' REFUSED %s", username, fromhost)); failed = true; } if ( (NULL != pwd) && getdef_bool ("FAILLOG_ENAB") && !failcheck (pwd->pw_uid, &faillog, failed)) { SYSLOG ((LOG_CRIT, "exceeded failure limit for '%s' %s", username, fromhost)); failed = true; } if (!failed) { break; } /* don't log non-existent users */ if ((NULL != pwd) && getdef_bool ("FAILLOG_ENAB")) { failure (pwd->pw_uid, tty, &faillog); } if (getdef_str ("FTMP_FILE") != NULL) { #ifdef USE_UTMPX struct utmpx *failent = prepare_utmpx (failent_user, tty, /* FIXME: or fromhost? */hostname, utent); #else /* !USE_UTMPX */ struct utmp *failent = prepare_utmp (failent_user, tty, hostname, utent); #endif /* !USE_UTMPX */ failtmp (failent_user, failent); free (failent); } retries--; if (retries <= 0) { SYSLOG ((LOG_CRIT, "REPEATED login failures%s", fromhost)); } /* * If this was a passwordless account and we get here, login * was denied (securetty, faillog, etc.). There was no * password prompt, so do it now (will always fail - the bad * guys won't see that the passwordless account exists at * all). --marekm */ if (user_passwd[0] == '\0') { pw_auth ("!", username, reason, (char *) 0); } /* * Authentication of this user failed. * The username must be confirmed in the next try. */ free (username); username = NULL; /* * Wait a while (a la SVR4 /usr/bin/login) before attempting * to login the user again. If the earlier alarm occurs * before the sleep() below completes, login will exit. */ if (delay > 0) { (void) sleep (delay); } (void) puts (_("Login incorrect")); /* allow only one attempt with -r or -f */ if (rflg || fflg || (retries <= 0)) { closelog (); exit (1); } } /* while (true) */ #endif /* ! USE_PAM */ assert (NULL != username); assert (NULL != pwd); (void) alarm (0); /* turn off alarm clock */ #ifndef USE_PAM /* PAM does this */ /* * porttime checks moved here, after the user has been * authenticated. now prints a message, as suggested * by Ivan Nejgebauer <*****@*****.**>. --marekm */ if ( getdef_bool ("PORTTIME_CHECKS_ENAB") && !isttytime (username, tty, time ((time_t *) 0))) { SYSLOG ((LOG_WARN, "invalid login time for '%s'%s", username, fromhost)); closelog (); bad_time_notify (); exit (1); } check_nologin (pwd->pw_uid == 0); #endif if (getenv ("IFS")) { /* don't export user IFS ... */ addenv ("IFS= \t\n", NULL); /* ... instead, set a safe IFS */ } if (pwd->pw_shell[0] == '*') { /* subsystem root */ pwd->pw_shell++; /* skip the '*' */ subsystem (pwd); /* figure out what to execute */ subroot = true; /* say I was here again */ endpwent (); /* close all of the file which were */ endgrent (); /* open in the original rooted file */ endspent (); /* system. they will be re-opened */ #ifdef SHADOWGRP endsgent (); /* in the new rooted file system */ #endif goto top; /* go do all this all over again */ } #ifdef WITH_AUDIT audit_fd = audit_open (); audit_log_acct_message (audit_fd, AUDIT_USER_LOGIN, NULL, /* Prog. name */ "login", username, AUDIT_NO_ID, hostname, NULL, /* addr */ tty, 1); /* result */ close (audit_fd); #endif /* WITH_AUDIT */ #ifndef USE_PAM /* pam_lastlog handles this */ if (getdef_bool ("LASTLOG_ENAB")) { /* give last login and log this one */ dolastlog (&ll, pwd, tty, hostname); } #endif #ifndef USE_PAM /* PAM handles this as well */ /* * Have to do this while we still have root privileges, otherwise we * don't have access to /etc/shadow. */ if (NULL != spwd) { /* check for age of password */ if (expire (pwd, spwd)) { /* The user updated her password, get the new * entries. * Use the x variants because we need to keep the * entry for a long time, and there might be other * getxxyy in between. */ pw_free (pwd); pwd = xgetpwnam (username); if (NULL == pwd) { SYSLOG ((LOG_ERR, "cannot find user %s after update of expired password", username)); exit (1); } spw_free (spwd); spwd = xgetspnam (username); } } setup_limits (pwd); /* nice, ulimit etc. */ #endif /* ! USE_PAM */ chown_tty (pwd); #ifdef USE_PAM /* * We must fork before setuid() because we need to call * pam_close_session() as root. */ (void) signal (SIGINT, SIG_IGN); child = fork (); if (child < 0) { /* error in fork() */ fprintf (stderr, _("%s: failure forking: %s"), Prog, strerror (errno)); PAM_END; exit (0); } else if (child != 0) { /* * parent - wait for child to finish, then cleanup * session */ wait (NULL); PAM_END; exit (0); } /* child */ #endif /* If we were init, we need to start a new session */ if (getppid() == 1) { setsid(); if (ioctl(0, TIOCSCTTY, 1) != 0) { fprintf (stderr, _("TIOCSCTTY failed on %s"), tty); } } /* * The utmp entry needs to be updated to indicate the new status * of the session, the new PID and SID. */ update_utmp (username, tty, hostname, utent); /* The pwd and spwd entries for the user have been copied. * * Close all the files so that unauthorized access won't occur. */ endpwent (); /* stop access to password file */ endgrent (); /* stop access to group file */ endspent (); /* stop access to shadow passwd file */ #ifdef SHADOWGRP endsgent (); /* stop access to shadow group file */ #endif /* Drop root privileges */ #ifndef USE_PAM if (setup_uid_gid (pwd, is_console)) #else /* The group privileges were already dropped. * See setup_groups() above. */ if (change_uid (pwd)) #endif { exit (1); } setup_env (pwd); /* set env vars, cd to the home dir */ #ifdef USE_PAM { const char *const *env; env = (const char *const *) pam_getenvlist (pamh); while ((NULL != env) && (NULL != *env)) { addenv (*env, NULL); env++; } } #endif (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); if (!hushed (username)) { addenv ("HUSHLOGIN=FALSE", NULL); /* * pam_unix, pam_mail and pam_lastlog should take care of * this */ #ifndef USE_PAM motd (); /* print the message of the day */ if ( getdef_bool ("FAILLOG_ENAB") && (0 != faillog.fail_cnt)) { failprint (&faillog); /* Reset the lockout times if logged in */ if ( (0 != faillog.fail_max) && (faillog.fail_cnt >= faillog.fail_max)) { (void) puts (_("Warning: login re-enabled after temporary lockout.")); SYSLOG ((LOG_WARN, "login '%s' re-enabled after temporary lockout (%d failures)", username, (int) faillog.fail_cnt)); } } if ( getdef_bool ("LASTLOG_ENAB") && (ll.ll_time != 0)) { time_t ll_time = ll.ll_time; #ifdef HAVE_STRFTIME (void) strftime (ptime, sizeof (ptime), "%a %b %e %H:%M:%S %z %Y", localtime (&ll_time)); printf (_("Last login: %s on %s"), ptime, ll.ll_line); #else printf (_("Last login: %.19s on %s"), ctime (&ll_time), ll.ll_line); #endif #ifdef HAVE_LL_HOST /* __linux__ || SUN4 */ if ('\0' != ll.ll_host[0]) { printf (_(" from %.*s"), (int) sizeof ll.ll_host, ll.ll_host); } #endif printf (".\n"); } agecheck (spwd); mailcheck (); /* report on the status of mail */ #endif /* !USE_PAM */ } else { addenv ("HUSHLOGIN=TRUE", NULL); } ttytype (tty); (void) signal (SIGQUIT, SIG_DFL); /* default quit signal */ (void) signal (SIGTERM, SIG_DFL); /* default terminate signal */ (void) signal (SIGALRM, SIG_DFL); /* default alarm signal */ (void) signal (SIGHUP, SIG_DFL); /* added this. --marekm */ (void) signal (SIGINT, SIG_DFL); /* default interrupt signal */ if (0 == pwd->pw_uid) { SYSLOG ((LOG_NOTICE, "ROOT LOGIN %s", fromhost)); } else if (getdef_bool ("LOG_OK_LOGINS")) { SYSLOG ((LOG_INFO, "'%s' logged in %s", username, fromhost)); } closelog (); tmp = getdef_str ("FAKE_SHELL"); if (NULL != tmp) { err = shell (tmp, pwd->pw_shell, newenvp); /* fake shell */ } else { /* exec the shell finally */ err = shell (pwd->pw_shell, (char *) 0, newenvp); } return ((err == ENOENT) ? E_CMD_NOTFOUND : E_CMD_NOEXEC); }
/* * new_password - validate old password and replace with new (both old and * new in global "char crypt_passwd[128]") */ static int new_password (const struct passwd *pw) { char *clear; /* Pointer to clear text */ char *cipher; /* Pointer to cipher text */ char *cp; /* Pointer to getpass() response */ char orig[200]; /* Original password */ char pass[200]; /* New password */ int i; /* Counter for retries */ int warned; int pass_max_len = -1; char *method; #ifdef HAVE_LIBCRACK_HIST int HistUpdate (const char *, const char *); #endif /* HAVE_LIBCRACK_HIST */ /* * Authenticate the user. The user will be prompted for their own * password. */ if (!amroot && crypt_passwd[0]) { clear = getpass (_("Old password: "******"incorrect password for %s", pw->pw_name)); sleep (1); fprintf (stderr, _("Incorrect password for %s.\n"), pw->pw_name); return -1; } STRFCPY (orig, clear); strzero (clear); strzero (cipher); } else { orig[0] = '\0'; } /* * Get the new password. The user is prompted for the new password * and has five tries to get it right. The password will be tested * for strength, unless it is the root user. This provides an escape * for initial login passwords. */ if ((method = getdef_str ("ENCRYPT_METHOD")) == NULL) { if (!getdef_bool ("MD5_CRYPT_ENAB")) { pass_max_len = getdef_num ("PASS_MAX_LEN", 8); } } else { if ( (strcmp (method, "MD5") == 0) #ifdef USE_SHA_CRYPT || (strcmp (method, "SHA256") == 0) || (strcmp (method, "SHA512") == 0) #endif /* USE_SHA_CRYPT */ ) { pass_max_len = -1; } else { pass_max_len = getdef_num ("PASS_MAX_LEN", 8); } } if (!qflg) { if (pass_max_len == -1) { printf (_( "Enter the new password (minimum of %d characters)\n" "Please use a combination of upper and lower case letters and numbers.\n"), getdef_num ("PASS_MIN_LEN", 5)); } else { printf (_( "Enter the new password (minimum of %d, maximum of %d characters)\n" "Please use a combination of upper and lower case letters and numbers.\n"), getdef_num ("PASS_MIN_LEN", 5), pass_max_len); } } warned = 0; for (i = getdef_num ("PASS_CHANGE_TRIES", 5); i > 0; i--) { cp = getpass (_("New password: "******"Try again.")); continue; } /* * If enabled, warn about weak passwords even if you are * root (enter this password again to use it anyway). * --marekm */ if (amroot && !warned && getdef_bool ("PASS_ALWAYS_WARN") && (!obscure (orig, pass, pw) || reuse (pass, pw))) { puts (_("\nWarning: weak password (enter it again to use it anyway).")); warned++; continue; } cp = getpass (_("Re-enter new password: "******"They don't match; try again.\n"), stderr); } else { strzero (cp); break; } } memzero (orig, sizeof orig); if (i == 0) { memzero (pass, sizeof pass); return -1; } /* * Encrypt the password, then wipe the cleartext password. */ cp = pw_encrypt (pass, crypt_make_salt (NULL, NULL)); memzero (pass, sizeof pass); #ifdef HAVE_LIBCRACK_HIST HistUpdate (pw->pw_name, crypt_passwd); #endif /* HAVE_LIBCRACK_HIST */ STRFCPY (crypt_passwd, cp); return 0; }
static char * prompt_password(const char *prompt, int with_echo) { static char nostring[1] = ""; static char *return_value; volatile int tty_opened; static FILE *ifp, *ofp; volatile int is_tty; #ifdef HAVE_SIGACTION struct sigaction old_sigact; #else RETSIGTYPE (*old_signal)(); #endif TERMIO old_modes; int max_asterisks = getdef_num("GETPASS_ASTERISKS", -1); /* * set a flag so the SIGINT signal can be re-sent if it * is caught */ sig_caught = 0; return_value = NULL; tty_opened = 0; /* * if /dev/tty can't be opened, getpass() needs to read * from stdin and write to stderr instead. */ if (!(ifp = fopen("/dev/tty", "r+"))) { ifp = stdin; ofp = stderr; } else { ofp = ifp; tty_opened = 1; } setbuf(ifp, (char *) 0); /* * the current tty modes must be saved so they can be * restored later on. echo will be turned off, except * for the newline character */ is_tty = 1; if (GTTY(fileno(ifp), &old_modes)) { is_tty = 0; #if 0 /* to make getpass work with redirected stdin */ return_value = NULL; goto out2; #endif } #ifdef USE_SETJMP /* * If we get a SIGINT, sig_catch() will jump here - * no need to press Enter after Ctrl-C. */ if (sigsetjmp(intr, 1)) goto out; #endif #ifdef HAVE_SIGACTION sigact.sa_handler = sig_catch; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, &old_sigact); #else old_signal = signal(SIGINT, sig_catch); #endif if (is_tty) { TERMIO new_modes = old_modes; if (max_asterisks < 0) new_modes.c_lflag |= ICANON; else new_modes.c_lflag &= ~(ICANON); if (with_echo) new_modes.c_lflag |= (ECHO | ECHOE | ECHOK); else new_modes.c_lflag &= ~(ECHO | ECHOE | ECHOK); new_modes.c_lflag |= ECHONL; if (STTY(fileno(ifp), &new_modes)) goto out; } /* * the prompt is output, and the response read without * echoing. the trailing newline must be removed. if * the fgets() returns an error, a NULL pointer is * returned. */ if ((fputs(prompt, ofp) != EOF) && (fflush(ofp) != EOF)) return_value = readpass(ifp, ofp, with_echo, max_asterisks); out: /* * the old SIGINT handler is restored after the tty * modes. then /dev/tty is closed if it was opened in * the beginning. finally, if a signal was caught it * is sent to this process for normal processing. */ if (is_tty) { if (STTY(fileno(ifp), &old_modes)) return_value = NULL; } #ifdef HAVE_SIGACTION (void) sigaction (SIGINT, &old_sigact, NULL); #else (void) signal (SIGINT, old_signal); #endif #if 0 out2: #endif if (tty_opened) (void) fclose(ifp); if (sig_caught) { kill(getpid(), SIGINT); return_value = NULL; } if (!return_value) { nostring[0] = '\0'; return_value = nostring; } return return_value; }