login.c revision 169177
11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994 31590Srgrimes * The Regents of the University of California. All rights reserved. 489615Sdes * Copyright (c) 2002 Networks Associates Technologies, Inc. 589615Sdes * All rights reserved. 61590Srgrimes * 789615Sdes * Portions of this software were developed for the FreeBSD Project by 889615Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network 989615Sdes * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 1089615Sdes * ("CBOSS"), as part of the DARPA CHATS research program. 1189615Sdes * 121590Srgrimes * Redistribution and use in source and binary forms, with or without 131590Srgrimes * modification, are permitted provided that the following conditions 141590Srgrimes * are met: 151590Srgrimes * 1. Redistributions of source code must retain the above copyright 161590Srgrimes * notice, this list of conditions and the following disclaimer. 171590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 181590Srgrimes * notice, this list of conditions and the following disclaimer in the 191590Srgrimes * documentation and/or other materials provided with the distribution. 201590Srgrimes * 3. All advertising materials mentioning features or use of this software 211590Srgrimes * must display the following acknowledgement: 221590Srgrimes * This product includes software developed by the University of 231590Srgrimes * California, Berkeley and its contributors. 241590Srgrimes * 4. Neither the name of the University nor the names of its contributors 251590Srgrimes * may be used to endorse or promote products derived from this software 261590Srgrimes * without specific prior written permission. 271590Srgrimes * 281590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 291590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 301590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 311590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 321590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 331590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 341590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 351590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 361590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 371590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 381590Srgrimes * SUCH DAMAGE. 391590Srgrimes */ 401590Srgrimes 4187628Sdwmalone#if 0 4287628Sdwmalone#ifndef lint 4387628Sdwmalonestatic char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94"; 4487628Sdwmalone#endif 4587628Sdwmalone#endif 4687628Sdwmalone 4787233Smarkm#include <sys/cdefs.h> 4887233Smarkm__FBSDID("$FreeBSD: head/usr.bin/login/login.c 169177 2007-05-01 16:02:44Z ache $"); 4987233Smarkm 501590Srgrimes/* 511590Srgrimes * login [ name ] 521590Srgrimes * login -h hostname (for telnetd, etc.) 531590Srgrimes * login -f name (for pre-authenticated login: datakit, xterm, etc.) 541590Srgrimes */ 551590Srgrimes 5687233Smarkm#include <sys/copyright.h> 5787180Smarkm#include <sys/param.h> 5887180Smarkm#include <sys/file.h> 591590Srgrimes#include <sys/stat.h> 601590Srgrimes#include <sys/time.h> 611590Srgrimes#include <sys/resource.h> 6287180Smarkm#include <sys/wait.h> 631590Srgrimes 641590Srgrimes#include <err.h> 651590Srgrimes#include <errno.h> 661590Srgrimes#include <grp.h> 6740102Smarkm#include <libutil.h> 6841079Sjdp#include <login_cap.h> 691590Srgrimes#include <pwd.h> 701590Srgrimes#include <setjmp.h> 711590Srgrimes#include <signal.h> 721590Srgrimes#include <stdio.h> 731590Srgrimes#include <stdlib.h> 741590Srgrimes#include <string.h> 751590Srgrimes#include <syslog.h> 761590Srgrimes#include <ttyent.h> 771590Srgrimes#include <unistd.h> 781590Srgrimes 7941279Sjdp#include <security/pam_appl.h> 8091714Sdes#include <security/openpam.h> 813702Spst 8287173Smarkm#include "login.h" 831590Srgrimes#include "pathnames.h" 841590Srgrimes 8589994Sdesstatic int auth_pam(void); 8689994Sdesstatic void bail(int, int); 8789994Sdesstatic int export(const char *); 8889994Sdesstatic void export_pam_environment(void); 8989994Sdesstatic int motd(const char *); 9089994Sdesstatic void badlogin(char *); 9189994Sdesstatic char *getloginname(void); 9289994Sdesstatic void pam_syslog(const char *); 9389994Sdesstatic void pam_cleanup(void); 9489994Sdesstatic void refused(const char *, const char *, int); 9589994Sdesstatic const char *stypeof(char *); 9689994Sdesstatic void sigint(int); 9789994Sdesstatic void timedout(int); 9889994Sdesstatic void usage(void); 991590Srgrimes 10087173Smarkm#define TTYGRPNAME "tty" /* group to own ttys */ 10187173Smarkm#define DEFAULT_BACKOFF 3 10287173Smarkm#define DEFAULT_RETRIES 10 10376786Sobrien#define DEFAULT_PROMPT "login: " 10476786Sobrien#define DEFAULT_PASSWD_PROMPT "Password:" 10589994Sdes#define TERM_UNKNOWN "su" 10687173Smarkm#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ 10789994Sdes#define NO_SLEEP_EXIT 0 10889994Sdes#define SLEEP_EXIT 5 1091590Srgrimes 1101590Srgrimes/* 1111590Srgrimes * This bounds the time given to login. Not a define so it can 1121590Srgrimes * be patched on machines where it's too small. 1131590Srgrimes */ 11489994Sdesstatic u_int timeout = 300; 1151590Srgrimes 11642272Seivind/* Buffer for signal handling of timeout */ 11789994Sdesstatic jmp_buf timeout_buf; 11842272Seivind 11989994Sdesstruct passwd *pwd; 12089994Sdesstatic int failures; 1211590Srgrimes 12289994Sdesstatic char *envinit[1]; /* empty environment list */ 12389994Sdes 12489994Sdes/* 12589994Sdes * Command line flags and arguments 12689994Sdes */ 12789994Sdesstatic int fflag; /* -f: do not perform authentication */ 12889994Sdesstatic int hflag; /* -h: login from remote host */ 12989994Sdesstatic char *hostname; /* hostname from command line */ 13089994Sdesstatic int pflag; /* -p: preserve environment */ 13189994Sdes 13289994Sdes/* 13389994Sdes * User name 13489994Sdes */ 13589994Sdesstatic char *username; /* user name */ 13689994Sdesstatic char *olduser; /* previous user name */ 13789994Sdes 13889994Sdes/* 13989994Sdes * Prompts 14089994Sdes */ 14189994Sdesstatic char default_prompt[] = DEFAULT_PROMPT; 14294203Srustatic const char *prompt; 14389994Sdesstatic char default_passwd_prompt[] = DEFAULT_PASSWD_PROMPT; 14494203Srustatic const char *passwd_prompt; 14589994Sdes 14689994Sdesstatic char *tty; 14789994Sdes 14889994Sdes/* 14989994Sdes * PAM data 15089994Sdes */ 15189994Sdesstatic pam_handle_t *pamh = NULL; 15291714Sdesstatic struct pam_conv pamc = { openpam_ttyconv, NULL }; 15389994Sdesstatic int pam_err; 15489994Sdesstatic int pam_silent = PAM_SILENT; 15589994Sdesstatic int pam_cred_established; 15689994Sdesstatic int pam_session_established; 15789994Sdes 1581590Srgrimesint 15989994Sdesmain(int argc, char *argv[]) 1601590Srgrimes{ 1611590Srgrimes struct group *gr; 1621590Srgrimes struct stat st; 16389994Sdes int retries, backoff; 16489994Sdes int ask, ch, cnt, quietlog, rootlogin, rval; 16535559Speter uid_t uid, euid; 16646007Sache gid_t egid; 16789994Sdes char *term; 16845431Sbrian char *p, *ttyn; 16942272Seivind char tname[sizeof(_PATH_TTY) + 10]; 17094203Sru char *arg0; 17198960Sache const char *tp; 17294203Sru const char *shell = NULL; 17321528Sdavidn login_cap_t *lc = NULL; 174146867Smaxim login_cap_t *lc_user = NULL; 17574874Smarkm pid_t pid; 176165152Scsjp#ifdef USE_BSM_AUDIT 177155312Swsalamon char auditsuccess = 1; 178165152Scsjp#endif 1791590Srgrimes 18042272Seivind (void)signal(SIGQUIT, SIG_IGN); 18142272Seivind (void)signal(SIGINT, SIG_IGN); 18276942Sguido (void)signal(SIGHUP, SIG_IGN); 18342272Seivind if (setjmp(timeout_buf)) { 18442272Seivind if (failures) 18589994Sdes badlogin(username); 18676788Sobrien (void)fprintf(stderr, "Login timed out after %d seconds\n", 18776788Sobrien timeout); 18889994Sdes bail(NO_SLEEP_EXIT, 0); 18942272Seivind } 1901590Srgrimes (void)signal(SIGALRM, timedout); 1911590Srgrimes (void)alarm(timeout); 1921590Srgrimes (void)setpriority(PRIO_PROCESS, 0, 0); 1931590Srgrimes 1941590Srgrimes openlog("login", LOG_ODELAY, LOG_AUTH); 1951590Srgrimes 1961590Srgrimes uid = getuid(); 19735557Speter euid = geteuid(); 19846007Sache egid = getegid(); 19989994Sdes 20024360Simp while ((ch = getopt(argc, argv, "fh:p")) != -1) 2011590Srgrimes switch (ch) { 2021590Srgrimes case 'f': 2031590Srgrimes fflag = 1; 2041590Srgrimes break; 2051590Srgrimes case 'h': 20689994Sdes if (uid != 0) 2071590Srgrimes errx(1, "-h option: %s", strerror(EPERM)); 20889994Sdes if (strlen(optarg) >= MAXHOSTNAMELEN) 20981555Smike errx(1, "-h option: %s: exceeds maximum " 21081555Smike "hostname size", optarg); 21189994Sdes hflag = 1; 2121590Srgrimes hostname = optarg; 2131590Srgrimes break; 2141590Srgrimes case 'p': 2151590Srgrimes pflag = 1; 2161590Srgrimes break; 2171590Srgrimes case '?': 2181590Srgrimes default: 21989994Sdes if (uid == 0) 2201590Srgrimes syslog(LOG_ERR, "invalid flag %c", ch); 22127605Scharnier usage(); 2221590Srgrimes } 2231590Srgrimes argc -= optind; 2241590Srgrimes argv += optind; 2251590Srgrimes 22689994Sdes if (argc > 0) { 22789994Sdes username = strdup(*argv); 22889994Sdes if (username == NULL) 22989994Sdes err(1, "strdup()"); 2301590Srgrimes ask = 0; 23189994Sdes } else { 2321590Srgrimes ask = 1; 23389994Sdes } 2341590Srgrimes 235107585Sdes setproctitle("-%s", getprogname()); 236107585Sdes 2371590Srgrimes for (cnt = getdtablesize(); cnt > 2; cnt--) 2381590Srgrimes (void)close(cnt); 2391590Srgrimes 24089994Sdes /* 24189994Sdes * Get current TTY 24289994Sdes */ 2431590Srgrimes ttyn = ttyname(STDIN_FILENO); 2441590Srgrimes if (ttyn == NULL || *ttyn == '\0') { 2451590Srgrimes (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); 2461590Srgrimes ttyn = tname; 2471590Srgrimes } 24823985Sdavidn if ((tty = strrchr(ttyn, '/')) != NULL) 2491590Srgrimes ++tty; 2501590Srgrimes else 2511590Srgrimes tty = ttyn; 2521590Srgrimes 25323985Sdavidn /* 25423985Sdavidn * Get "login-retries" & "login-backoff" from default class 25523985Sdavidn */ 25623985Sdavidn lc = login_getclass(NULL); 257114010Sdes prompt = login_getcapstr(lc, "login_prompt", 25889994Sdes default_prompt, default_prompt); 25976786Sobrien passwd_prompt = login_getcapstr(lc, "passwd_prompt", 26087173Smarkm default_passwd_prompt, default_passwd_prompt); 26189994Sdes retries = login_getcapnum(lc, "login-retries", 26289994Sdes DEFAULT_RETRIES, DEFAULT_RETRIES); 26389994Sdes backoff = login_getcapnum(lc, "login-backoff", 26489994Sdes DEFAULT_BACKOFF, DEFAULT_BACKOFF); 26523985Sdavidn login_close(lc); 26623985Sdavidn lc = NULL; 26721528Sdavidn 26889994Sdes /* 26989994Sdes * Try to authenticate the user until we succeed or time out. 27089994Sdes */ 2711590Srgrimes for (cnt = 0;; ask = 1) { 2721590Srgrimes if (ask) { 2731590Srgrimes fflag = 0; 27489994Sdes if (olduser != NULL) 27589994Sdes free(olduser); 27689994Sdes olduser = username; 27789994Sdes username = getloginname(); 2781590Srgrimes } 2791590Srgrimes rootlogin = 0; 28021528Sdavidn 2811590Srgrimes /* 2821590Srgrimes * Note if trying multiple user names; log failures for 2831590Srgrimes * previous user name, but don't bother logging one failure 2841590Srgrimes * for nonexistent name (mistyped username). 2851590Srgrimes */ 28689994Sdes if (failures && strcmp(olduser, username) != 0) { 2871590Srgrimes if (failures > (pwd ? 0 : 1)) 28889994Sdes badlogin(olduser); 2891590Srgrimes } 2901590Srgrimes 29123985Sdavidn /* 29289994Sdes * Load the PAM policy and set some variables 2931590Srgrimes */ 29489994Sdes pam_err = pam_start("login", username, &pamc, &pamh); 29589994Sdes if (pam_err != PAM_SUCCESS) { 29689994Sdes pam_syslog("pam_start()"); 297165152Scsjp#ifdef USE_BSM_AUDIT 298155312Swsalamon au_login_fail("PAM Error", 1); 299165152Scsjp#endif 30089994Sdes bail(NO_SLEEP_EXIT, 1); 3013205Spst } 30289994Sdes pam_err = pam_set_item(pamh, PAM_TTY, tty); 30389994Sdes if (pam_err != PAM_SUCCESS) { 30489994Sdes pam_syslog("pam_set_item(PAM_TTY)"); 305165152Scsjp#ifdef USE_BSM_AUDIT 306155312Swsalamon au_login_fail("PAM Error", 1); 307165152Scsjp#endif 30889994Sdes bail(NO_SLEEP_EXIT, 1); 30989994Sdes } 310110966Sdes pam_err = pam_set_item(pamh, PAM_RHOST, hostname); 31189994Sdes if (pam_err != PAM_SUCCESS) { 31289994Sdes pam_syslog("pam_set_item(PAM_RHOST)"); 313165152Scsjp#ifdef USE_BSM_AUDIT 314155312Swsalamon au_login_fail("PAM Error", 1); 315165152Scsjp#endif 31689994Sdes bail(NO_SLEEP_EXIT, 1); 31789994Sdes } 31897376Sdes 31989994Sdes pwd = getpwnam(username); 32041279Sjdp if (pwd != NULL && pwd->pw_uid == 0) 32141279Sjdp rootlogin = 1; 3221590Srgrimes 3231590Srgrimes /* 32489994Sdes * If the -f option was specified and the caller is 32589994Sdes * root or the caller isn't changing their uid, don't 32689994Sdes * authenticate. 3271590Srgrimes */ 32889994Sdes if (pwd != NULL && fflag && 32989994Sdes (uid == (uid_t)0 || uid == (uid_t)pwd->pw_uid)) { 33089994Sdes /* already authenticated */ 33189994Sdes rval = 0; 332165152Scsjp#ifdef USE_BSM_AUDIT 333155312Swsalamon auditsuccess = 0; /* opened a terminal window only */ 334165152Scsjp#endif 33589994Sdes } else { 33689994Sdes fflag = 0; 33789994Sdes (void)setpriority(PRIO_PROCESS, 0, -4); 33889994Sdes rval = auth_pam(); 33989994Sdes (void)setpriority(PRIO_PROCESS, 0, 0); 3401590Srgrimes } 3411590Srgrimes 34289994Sdes if (pwd && rval == 0) 34389994Sdes break; 34489994Sdes 34589994Sdes pam_cleanup(); 34697376Sdes 347155312Swsalamon /* 348155312Swsalamon * We are not exiting here, but this corresponds to a failed 349155312Swsalamon * login event, so set exitstatus to 1. 350155312Swsalamon */ 351165152Scsjp#ifdef USE_BSM_AUDIT 352155312Swsalamon au_login_fail("Login incorrect", 1); 353165152Scsjp#endif 354155312Swsalamon 3551590Srgrimes (void)printf("Login incorrect\n"); 3561590Srgrimes failures++; 35723985Sdavidn 358157215Scognet pwd = NULL; 359157215Scognet 36023985Sdavidn /* 36189994Sdes * Allow up to 'retry' (10) attempts, but start 36289994Sdes * backing off after 'backoff' (3) attempts. 36323985Sdavidn */ 36423985Sdavidn if (++cnt > backoff) { 36523985Sdavidn if (cnt >= retries) { 3661590Srgrimes badlogin(username); 36789994Sdes bail(SLEEP_EXIT, 1); 3681590Srgrimes } 36938374Sjkoshy sleep((u_int)((cnt - backoff) * 5)); 3701590Srgrimes } 3711590Srgrimes } 3721590Srgrimes 3731590Srgrimes /* committed to login -- turn off timeout */ 3741590Srgrimes (void)alarm((u_int)0); 37576942Sguido (void)signal(SIGHUP, SIG_DFL); 3761590Srgrimes 3771590Srgrimes endpwent(); 3781590Srgrimes 379165152Scsjp#ifdef USE_BSM_AUDIT 380155312Swsalamon /* Audit successful login. */ 381155312Swsalamon if (auditsuccess) 382155312Swsalamon au_login_success(); 383165152Scsjp#endif 384155312Swsalamon 38541279Sjdp /* 38641279Sjdp * Establish the login class. 38741279Sjdp */ 38841279Sjdp lc = login_getpwclass(pwd); 389146867Smaxim lc_user = login_getuserclass(pwd); 39041279Sjdp 391146867Smaxim if (!(quietlog = login_getcapbool(lc_user, "hushlogin", 0))) 392146867Smaxim quietlog = login_getcapbool(lc, "hushlogin", 0); 39397376Sdes 39483519Srwatson /* 39583519Srwatson * Switching needed for NFS with root access disabled. 39683519Srwatson * 39783519Srwatson * XXX: This change fails to modify the additional groups for the 39883519Srwatson * process, and as such, may restrict rights normally granted 39983519Srwatson * through those groups. 40083519Srwatson */ 40146007Sache (void)setegid(pwd->pw_gid); 40235557Speter (void)seteuid(rootlogin ? 0 : pwd->pw_uid); 40321528Sdavidn if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) { 40426021Spst if (login_getcapbool(lc, "requirehome", 0)) 40523985Sdavidn refused("Home directory not available", "HOMEDIR", 1); 40697376Sdes if (chdir("/") < 0) 40723985Sdavidn refused("Cannot find root directory", "ROOTDIR", 1); 40823985Sdavidn if (!quietlog || *pwd->pw_dir) 40923985Sdavidn printf("No home directory.\nLogging in with home = \"/\".\n"); 41087173Smarkm pwd->pw_dir = strdup("/"); 41187173Smarkm if (pwd->pw_dir == NULL) { 41287173Smarkm syslog(LOG_NOTICE, "strdup(): %m"); 41389994Sdes bail(SLEEP_EXIT, 1); 41487173Smarkm } 4151590Srgrimes } 41635557Speter (void)seteuid(euid); 41746007Sache (void)setegid(egid); 418125055Sfjoe if (!quietlog) { 41921528Sdavidn quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; 420125055Sfjoe if (!quietlog) 421125055Sfjoe pam_silent = 0; 422125055Sfjoe } 42397376Sdes 42497376Sdes shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell); 42523985Sdavidn if (*pwd->pw_shell == '\0') 42687173Smarkm pwd->pw_shell = strdup(_PATH_BSHELL); 42787173Smarkm if (pwd->pw_shell == NULL) { 42887173Smarkm syslog(LOG_NOTICE, "strdup(): %m"); 42989994Sdes bail(SLEEP_EXIT, 1); 43087173Smarkm } 43123985Sdavidn if (*shell == '\0') /* Not overridden */ 43223985Sdavidn shell = pwd->pw_shell; 43323985Sdavidn if ((shell = strdup(shell)) == NULL) { 43481555Smike syslog(LOG_NOTICE, "strdup(): %m"); 43589994Sdes bail(SLEEP_EXIT, 1); 43623985Sdavidn } 43721528Sdavidn 4382224Sguido /* 4392224Sguido * Set device protections, depending on what terminal the 4402224Sguido * user is logged in. This feature is used on Suns to give 4412224Sguido * console users better privacy. 4422224Sguido */ 4432224Sguido login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); 4442224Sguido 44550124Simp /* 44650124Simp * Clear flags of the tty. None should be set, and when the 44750124Simp * user sets them otherwise, this can cause the chown to fail. 44850124Simp * Since it isn't clear that flags are useful on character 44950124Simp * devices, we just clear them. 450102141Simp * 451102141Simp * We don't log in the case of EOPNOTSUPP because dev might be 452102141Simp * on NFS, which doesn't support chflags. 453102141Simp * 454102141Simp * We don't log in the EROFS because that means that /dev is on 455102141Simp * a read only file system and we assume that the permissions there 456102141Simp * are sane. 45750124Simp */ 458102141Simp if (ttyn != tname && chflags(ttyn, 0)) 459102141Simp if (errno != EOPNOTSUPP && errno != EROFS) 460102141Simp syslog(LOG_ERR, "chflags(%s): %m", ttyn); 46189994Sdes if (ttyn != tname && chown(ttyn, pwd->pw_uid, 46250124Simp (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid)) 463102141Simp if (errno != EROFS) 464114048Srwatson syslog(LOG_ERR, "chown(%s): %m", ttyn); 4651590Srgrimes 46623985Sdavidn /* 46723985Sdavidn * Exclude cons/vt/ptys only, assume dialup otherwise 46823985Sdavidn * TODO: Make dialup tty determination a library call 46923985Sdavidn * for consistency (finger etc.) 47023985Sdavidn */ 47189994Sdes if (hflag && isdialuptty(tty)) 4721590Srgrimes syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); 4731590Srgrimes 4743205Spst#ifdef LOGALL 4753205Spst /* 47689994Sdes * Syslog each successful login, so we don't have to watch 47789994Sdes * hundreds of wtmp or lastlogin files. 4783205Spst */ 47989994Sdes if (hflag) 48023985Sdavidn syslog(LOG_INFO, "login from %s on %s as %s", 48189994Sdes hostname, tty, pwd->pw_name); 48223985Sdavidn else 48323985Sdavidn syslog(LOG_INFO, "login on %s as %s", 48423985Sdavidn tty, pwd->pw_name); 48521528Sdavidn#endif 48621528Sdavidn 48723985Sdavidn /* 48889994Sdes * If fflag is on, assume caller/authenticator has logged root 48989994Sdes * login. 49023985Sdavidn */ 49189994Sdes if (rootlogin && fflag == 0) { 49289994Sdes if (hflag) 49323985Sdavidn syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s", 49489994Sdes username, tty, hostname); 49523985Sdavidn else 49623985Sdavidn syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", 49776788Sobrien username, tty); 49823985Sdavidn } 49923985Sdavidn 50023985Sdavidn /* 50189994Sdes * Destroy environment unless user has requested its 50289994Sdes * preservation - but preserve TERM in all cases 50323985Sdavidn */ 50489994Sdes term = getenv("TERM"); 50521528Sdavidn if (!pflag) 50621528Sdavidn environ = envinit; 50789994Sdes if (term != NULL) 50889994Sdes setenv("TERM", term, 0); 50921528Sdavidn 51023985Sdavidn /* 51174874Smarkm * PAM modules might add supplementary groups during pam_setcred(). 51274874Smarkm */ 51374874Smarkm if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { 51497376Sdes syslog(LOG_ERR, "setusercontext() failed - exiting"); 51589994Sdes bail(NO_SLEEP_EXIT, 1); 51674874Smarkm } 51774874Smarkm 51889994Sdes pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED); 51989994Sdes if (pam_err != PAM_SUCCESS) { 52089994Sdes pam_syslog("pam_setcred()"); 52189994Sdes bail(NO_SLEEP_EXIT, 1); 52289994Sdes } 52389994Sdes pam_cred_established = 1; 52497376Sdes 52589994Sdes pam_err = pam_open_session(pamh, pam_silent); 52689994Sdes if (pam_err != PAM_SUCCESS) { 52789994Sdes pam_syslog("pam_open_session()"); 52889994Sdes bail(NO_SLEEP_EXIT, 1); 52989994Sdes } 53089994Sdes pam_session_established = 1; 53174874Smarkm 53289994Sdes /* 53389994Sdes * We must fork() before setuid() because we need to call 53489994Sdes * pam_close_session() as root. 53589994Sdes */ 53689994Sdes pid = fork(); 53789994Sdes if (pid < 0) { 53889994Sdes err(1, "fork"); 53989994Sdes } else if (pid != 0) { 54074874Smarkm /* 54189994Sdes * Parent: wait for child to finish, then clean up 54289994Sdes * session. 54374874Smarkm */ 544110549Sdes int status; 545107585Sdes setproctitle("-%s [pam]", getprogname()); 546110549Sdes waitpid(pid, &status, 0); 54789994Sdes bail(NO_SLEEP_EXIT, 0); 54874874Smarkm } 54974874Smarkm 55074874Smarkm /* 55189994Sdes * NOTICE: We are now in the child process! 55221528Sdavidn */ 55397376Sdes 55489994Sdes /* 55589994Sdes * Add any environment variables the PAM modules may have set. 55689994Sdes */ 55789994Sdes export_pam_environment(); 55889994Sdes 55989994Sdes /* 56089994Sdes * We're done with PAM now; our parent will deal with the rest. 56189994Sdes */ 56291714Sdes pam_end(pamh, 0); 56389994Sdes pamh = NULL; 56489994Sdes 56589994Sdes /* 56689994Sdes * We don't need to be root anymore, so set the login name and 56789994Sdes * the UID. 56889994Sdes */ 56941279Sjdp if (setlogin(username) != 0) { 57097376Sdes syslog(LOG_ERR, "setlogin(%s): %m - exiting", username); 57189994Sdes bail(NO_SLEEP_EXIT, 1); 57241279Sjdp } 57341279Sjdp if (setusercontext(lc, pwd, pwd->pw_uid, 57474874Smarkm LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) { 57597376Sdes syslog(LOG_ERR, "setusercontext() failed - exiting"); 57621528Sdavidn exit(1); 5773205Spst } 57821528Sdavidn 57923148Sache (void)setenv("SHELL", pwd->pw_shell, 1); 58021528Sdavidn (void)setenv("HOME", pwd->pw_dir, 1); 58198960Sache /* Overwrite "term" from login.conf(5) for any known TERM */ 58298990Sache if (term == NULL && (tp = stypeof(tty)) != NULL) 58398960Sache (void)setenv("TERM", tp, 1); 58498960Sache else 58598960Sache (void)setenv("TERM", TERM_UNKNOWN, 0); 58641279Sjdp (void)setenv("LOGNAME", username, 1); 58741279Sjdp (void)setenv("USER", username, 1); 58821528Sdavidn (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0); 58921528Sdavidn 5901590Srgrimes if (!quietlog) { 59194203Sru const char *cw; 59223985Sdavidn 59323985Sdavidn cw = login_getcapstr(lc, "copyright", NULL, NULL); 59489994Sdes if (cw == NULL || motd(cw) == -1) 59589994Sdes (void)printf("%s", copyright); 59623985Sdavidn 59723985Sdavidn (void)printf("\n"); 59823985Sdavidn 59921528Sdavidn cw = login_getcapstr(lc, "welcome", NULL, NULL); 60089994Sdes if (cw != NULL && access(cw, F_OK) == 0) 60189994Sdes motd(cw); 60289994Sdes else 60389994Sdes motd(_PATH_MOTDFILE); 60423985Sdavidn 605146867Smaxim if (login_getcapbool(lc_user, "nocheckmail", 0) == 0 && 606146867Smaxim login_getcapbool(lc, "nocheckmail", 0) == 0) { 607100825Sdwmalone char *cx; 608100825Sdwmalone 60986450Srwatson /* $MAIL may have been set by class. */ 610100825Sdwmalone cx = getenv("MAIL"); 611100825Sdwmalone if (cx == NULL) { 612100825Sdwmalone asprintf(&cx, "%s/%s", 61386450Srwatson _PATH_MAILDIR, pwd->pw_name); 61489994Sdes } 615100825Sdwmalone if (cx && stat(cx, &st) == 0 && st.st_size != 0) 61686450Srwatson (void)printf("You have %smail.\n", 61786450Srwatson (st.st_mtime > st.st_atime) ? "new " : ""); 61889994Sdes if (getenv("MAIL") == NULL) 619100825Sdwmalone free(cx); 62086450Srwatson } 6211590Srgrimes } 6221590Srgrimes 623146867Smaxim login_close(lc_user); 62421528Sdavidn login_close(lc); 6253205Spst 6261590Srgrimes (void)signal(SIGALRM, SIG_DFL); 6271590Srgrimes (void)signal(SIGQUIT, SIG_DFL); 6281590Srgrimes (void)signal(SIGINT, SIG_DFL); 6291590Srgrimes (void)signal(SIGTSTP, SIG_IGN); 6301590Srgrimes 63123985Sdavidn /* 63223985Sdavidn * Login shells have a leading '-' in front of argv[0] 63323985Sdavidn */ 63489994Sdes p = strrchr(pwd->pw_shell, '/'); 63589994Sdes if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) { 63681555Smike syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size", 63781555Smike username); 63881555Smike errx(1, "shell exceeds maximum pathname size"); 63989994Sdes } else if (arg0 == NULL) { 64089994Sdes err(1, "asprintf()"); 64181555Smike } 64223985Sdavidn 64389994Sdes execlp(shell, arg0, (char *)0); 64421528Sdavidn err(1, "%s", shell); 64597376Sdes 64689994Sdes /* 64789994Sdes * That's it, folks! 64889994Sdes */ 6491590Srgrimes} 6501590Srgrimes 65141279Sjdp/* 65241279Sjdp * Attempt to authenticate the user using PAM. Returns 0 if the user is 65341279Sjdp * authenticated, or 1 if not authenticated. If some sort of PAM system 65441279Sjdp * error occurs (e.g., the "/etc/pam.conf" file is missing) then this 65541279Sjdp * function returns -1. This can be used as an indication that we should 65641279Sjdp * fall back to a different authentication mechanism. 65741279Sjdp */ 65841279Sjdpstatic int 65989994Sdesauth_pam(void) 66041279Sjdp{ 66141279Sjdp const char *tmpl_user; 66241279Sjdp const void *item; 66341279Sjdp int rval; 66441279Sjdp 66589994Sdes pam_err = pam_authenticate(pamh, pam_silent); 66689994Sdes switch (pam_err) { 66741279Sjdp 66841279Sjdp case PAM_SUCCESS: 66941279Sjdp /* 67041279Sjdp * With PAM we support the concept of a "template" 67141279Sjdp * user. The user enters a login name which is 67241279Sjdp * authenticated by PAM, usually via a remote service 67341279Sjdp * such as RADIUS or TACACS+. If authentication 67441279Sjdp * succeeds, a different but related "template" name 67541279Sjdp * is used for setting the credentials, shell, and 67641279Sjdp * home directory. The name the user enters need only 67741279Sjdp * exist on the remote authentication server, but the 67841279Sjdp * template name must be present in the local password 67941279Sjdp * database. 68041279Sjdp * 68141279Sjdp * This is supported by two various mechanisms in the 68241279Sjdp * individual modules. However, from the application's 68341279Sjdp * point of view, the template user is always passed 68441279Sjdp * back as a changed value of the PAM_USER item. 68541279Sjdp */ 68689994Sdes pam_err = pam_get_item(pamh, PAM_USER, &item); 68789994Sdes if (pam_err == PAM_SUCCESS) { 68889994Sdes tmpl_user = (const char *)item; 68941279Sjdp if (strcmp(username, tmpl_user) != 0) 69041279Sjdp pwd = getpwnam(tmpl_user); 69189994Sdes } else { 69289994Sdes pam_syslog("pam_get_item(PAM_USER)"); 69389994Sdes } 69441279Sjdp rval = 0; 69541279Sjdp break; 69641279Sjdp 69741279Sjdp case PAM_AUTH_ERR: 69841279Sjdp case PAM_USER_UNKNOWN: 69941279Sjdp case PAM_MAXTRIES: 70041279Sjdp rval = 1; 70141279Sjdp break; 70241279Sjdp 70341279Sjdp default: 70489994Sdes pam_syslog("pam_authenticate()"); 70541279Sjdp rval = -1; 70641279Sjdp break; 70741279Sjdp } 70874874Smarkm 70974874Smarkm if (rval == 0) { 71089994Sdes pam_err = pam_acct_mgmt(pamh, pam_silent); 71189994Sdes switch (pam_err) { 71289994Sdes case PAM_SUCCESS: 71389994Sdes break; 71489994Sdes case PAM_NEW_AUTHTOK_REQD: 71589994Sdes pam_err = pam_chauthtok(pamh, 71689994Sdes pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK); 71789994Sdes if (pam_err != PAM_SUCCESS) { 71889994Sdes pam_syslog("pam_chauthtok()"); 71974874Smarkm rval = 1; 72074874Smarkm } 72189994Sdes break; 72289994Sdes default: 72389994Sdes pam_syslog("pam_acct_mgmt()"); 72474874Smarkm rval = 1; 72589994Sdes break; 72674874Smarkm } 72741279Sjdp } 72874874Smarkm 72974874Smarkm if (rval != 0) { 73089994Sdes pam_end(pamh, pam_err); 73174874Smarkm pamh = NULL; 73274874Smarkm } 73389994Sdes return (rval); 73441279Sjdp} 73572215Snectar 73689994Sdes/* 73789994Sdes * Export any environment variables PAM modules may have set 73889994Sdes */ 73989994Sdesstatic void 74087177Smarkmexport_pam_environment() 74172215Snectar{ 74289994Sdes char **pam_env; 74389994Sdes char **pp; 74472215Snectar 74589994Sdes pam_env = pam_getenvlist(pamh); 74689994Sdes if (pam_env != NULL) { 74789994Sdes for (pp = pam_env; *pp != NULL; pp++) { 748169177Sache (void)export(*pp); 749169177Sache free(*pp); 75089994Sdes } 75172215Snectar } 75272215Snectar} 75372215Snectar 75472215Snectar/* 75589994Sdes * Perform sanity checks on an environment variable: 75672215Snectar * - Make sure there is an '=' in the string. 75772215Snectar * - Make sure the string doesn't run on too long. 75872215Snectar * - Do not export certain variables. This list was taken from the 75972215Snectar * Solaris pam_putenv(3) man page. 76089994Sdes * Then export it. 76172215Snectar */ 76272215Snectarstatic int 76389994Sdesexport(const char *s) 76472215Snectar{ 76572215Snectar static const char *noexport[] = { 76672215Snectar "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH", 76772215Snectar "IFS", "PATH", NULL 76872215Snectar }; 76972215Snectar const char **pp; 77072215Snectar size_t n; 77172215Snectar 77272215Snectar if (strlen(s) > 1024 || strchr(s, '=') == NULL) 77389994Sdes return (0); 77472215Snectar if (strncmp(s, "LD_", 3) == 0) 77589994Sdes return (0); 77672215Snectar for (pp = noexport; *pp != NULL; pp++) { 77772215Snectar n = strlen(*pp); 77872215Snectar if (s[n] == '=' && strncmp(s, *pp, n) == 0) 77989994Sdes return (0); 78072215Snectar } 78189994Sdes (void)putenv(s); 78289994Sdes return (1); 78372215Snectar} 78441279Sjdp 78527605Scharnierstatic void 78687177Smarkmusage() 78727605Scharnier{ 78876791Sobrien 78927605Scharnier (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n"); 79027605Scharnier exit(1); 79127605Scharnier} 7921590Srgrimes 79323985Sdavidn/* 79489994Sdes * Prompt user and read login name from stdin. 79574874Smarkm */ 79689994Sdesstatic char * 79787177Smarkmgetloginname() 7981590Srgrimes{ 79989994Sdes char *nbuf, *p; 8001590Srgrimes int ch; 8011590Srgrimes 80289994Sdes nbuf = malloc(MAXLOGNAME); 80389994Sdes if (nbuf == NULL) 80489994Sdes err(1, "malloc()"); 80589994Sdes do { 80681555Smike (void)printf("%s", prompt); 8071590Srgrimes for (p = nbuf; (ch = getchar()) != '\n'; ) { 8081590Srgrimes if (ch == EOF) { 8091590Srgrimes badlogin(username); 81089994Sdes bail(NO_SLEEP_EXIT, 0); 8111590Srgrimes } 81289994Sdes if (p < nbuf + MAXLOGNAME - 1) 8131590Srgrimes *p++ = ch; 8141590Srgrimes } 81589994Sdes } while (p == nbuf); 81697376Sdes 81789994Sdes *p = '\0'; 81889994Sdes if (nbuf[0] == '-') { 81989994Sdes pam_silent = 0; 82089994Sdes memmove(nbuf, nbuf + 1, strlen(nbuf)); 82189994Sdes } else { 82289994Sdes pam_silent = PAM_SILENT; 8231590Srgrimes } 82489994Sdes return nbuf; 8251590Srgrimes} 8261590Srgrimes 82789994Sdes/* 82889994Sdes * SIGINT handler for motd(). 82989994Sdes */ 83089994Sdesstatic volatile int motdinterrupt; 83189994Sdesstatic void 83289994Sdessigint(int signo __unused) 8331590Srgrimes{ 83421528Sdavidn motdinterrupt = 1; 83521528Sdavidn} 83621528Sdavidn 83789994Sdes/* 83889994Sdes * Display the contents of a file (such as /etc/motd). 83989994Sdes */ 84089994Sdesstatic int 84189994Sdesmotd(const char *motdfile) 84221528Sdavidn{ 8431590Srgrimes sig_t oldint; 84489994Sdes FILE *f; 84589994Sdes int ch; 8461590Srgrimes 84789994Sdes if ((f = fopen(motdfile, "r")) == NULL) 84889994Sdes return (-1); 84921528Sdavidn motdinterrupt = 0; 8501590Srgrimes oldint = signal(SIGINT, sigint); 85189994Sdes while ((ch = fgetc(f)) != EOF && !motdinterrupt) 85289994Sdes putchar(ch); 85389994Sdes signal(SIGINT, oldint); 85489994Sdes if (ch != EOF || ferror(f)) { 85589994Sdes fclose(f); 85689994Sdes return (-1); 85789994Sdes } 85889994Sdes fclose(f); 85989994Sdes return (0); 8601590Srgrimes} 8611590Srgrimes 86289994Sdes/* 86389994Sdes * SIGALRM handler, to enforce login prompt timeout. 86489994Sdes * 86589994Sdes * XXX This can potentially confuse the hell out of PAM. We should 86689994Sdes * XXX instead implement a conversation function that returns 86789994Sdes * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal 86889994Sdes * XXX handler just set a flag. 86989994Sdes */ 87089994Sdesstatic void 87189994Sdestimedout(int signo __unused) 8721590Srgrimes{ 87376788Sobrien 87442272Seivind longjmp(timeout_buf, signo); 8751590Srgrimes} 8761590Srgrimes 877105164Sphkstatic void 87889994Sdesbadlogin(char *name) 8791590Srgrimes{ 8801590Srgrimes 8811590Srgrimes if (failures == 0) 8821590Srgrimes return; 88389994Sdes if (hflag) { 8841590Srgrimes syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s", 88589994Sdes failures, failures > 1 ? "S" : "", hostname); 8861590Srgrimes syslog(LOG_AUTHPRIV|LOG_NOTICE, 8871590Srgrimes "%d LOGIN FAILURE%s FROM %s, %s", 88889994Sdes failures, failures > 1 ? "S" : "", hostname, name); 8891590Srgrimes } else { 8901590Srgrimes syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s", 8911590Srgrimes failures, failures > 1 ? "S" : "", tty); 8921590Srgrimes syslog(LOG_AUTHPRIV|LOG_NOTICE, 8931590Srgrimes "%d LOGIN FAILURE%s ON %s, %s", 8941590Srgrimes failures, failures > 1 ? "S" : "", tty, name); 8951590Srgrimes } 89642272Seivind failures = 0; 8971590Srgrimes} 8981590Srgrimes 89987173Smarkmconst char * 90089994Sdesstypeof(char *ttyid) 9011590Srgrimes{ 9021590Srgrimes struct ttyent *t; 90323985Sdavidn 90481555Smike if (ttyid != NULL && *ttyid != '\0') { 90581555Smike t = getttynam(ttyid); 90681555Smike if (t != NULL && t->ty_type != NULL) 90781555Smike return (t->ty_type); 90881555Smike } 90998960Sache return (NULL); 9101590Srgrimes} 9111590Srgrimes 912105164Sphkstatic void 91389994Sdesrefused(const char *msg, const char *rtype, int lout) 91423985Sdavidn{ 91523985Sdavidn 91623985Sdavidn if (msg != NULL) 91723985Sdavidn printf("%s.\n", msg); 91889994Sdes if (hflag) 91923985Sdavidn syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s", 92089994Sdes pwd->pw_name, rtype, hostname, tty); 92123985Sdavidn else 92223985Sdavidn syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s", 92376788Sobrien pwd->pw_name, rtype, tty); 92423985Sdavidn if (lout) 92589994Sdes bail(SLEEP_EXIT, 1); 92623985Sdavidn} 92723985Sdavidn 92889994Sdes/* 92989994Sdes * Log a PAM error 93089994Sdes */ 931105164Sphkstatic void 93289994Sdespam_syslog(const char *msg) 9331590Srgrimes{ 93489994Sdes syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err)); 93589994Sdes} 93623985Sdavidn 93789994Sdes/* 93889994Sdes * Shut down PAM 93989994Sdes */ 940105164Sphkstatic void 94189994Sdespam_cleanup() 94289994Sdes{ 94389994Sdes 94489994Sdes if (pamh != NULL) { 94589994Sdes if (pam_session_established) { 94689994Sdes pam_err = pam_close_session(pamh, 0); 94789994Sdes if (pam_err != PAM_SUCCESS) 94889994Sdes pam_syslog("pam_close_session()"); 94989994Sdes } 95089994Sdes pam_session_established = 0; 95189994Sdes if (pam_cred_established) { 95289994Sdes pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED); 95389994Sdes if (pam_err != PAM_SUCCESS) 95489994Sdes pam_syslog("pam_setcred()"); 95589994Sdes } 95689994Sdes pam_cred_established = 0; 95789994Sdes pam_end(pamh, pam_err); 95889994Sdes pamh = NULL; 95989994Sdes } 96089994Sdes} 96189994Sdes 96289994Sdes/* 96389994Sdes * Exit, optionally after sleeping a few seconds 96489994Sdes */ 96589994Sdesvoid 96689994Sdesbail(int sec, int eval) 96789994Sdes{ 96889994Sdes 96989994Sdes pam_cleanup(); 970165152Scsjp#ifdef USE_BSM_AUDIT 971157215Scognet if (pwd != NULL) 972157215Scognet audit_logout(); 973165152Scsjp#endif 97489994Sdes (void)sleep(sec); 9751590Srgrimes exit(eval); 9761590Srgrimes} 977