login.c revision 46007
11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 321590Srgrimes */ 331590Srgrimes 3423246Swosch#if 0 351590Srgrimesstatic char copyright[] = 361590Srgrimes"@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\ 371590Srgrimes The Regents of the University of California. All rights reserved.\n"; 3823246Swosch#endif 391590Srgrimes 401590Srgrimes#ifndef lint 4127605Scharnier#if 0 421590Srgrimesstatic char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94"; 4327605Scharnier#endif 4427605Scharnierstatic const char rcsid[] = 4546007Sache "$Id: login.c,v 1.46 1999/04/07 14:05:03 brian Exp $"; 461590Srgrimes#endif /* not lint */ 471590Srgrimes 481590Srgrimes/* 491590Srgrimes * login [ name ] 501590Srgrimes * login -h hostname (for telnetd, etc.) 511590Srgrimes * login -f name (for pre-authenticated login: datakit, xterm, etc.) 521590Srgrimes */ 531590Srgrimes 5423246Swosch#include <sys/copyright.h> 551590Srgrimes#include <sys/param.h> 561590Srgrimes#include <sys/stat.h> 571590Srgrimes#include <sys/time.h> 581590Srgrimes#include <sys/resource.h> 591590Srgrimes#include <sys/file.h> 6016423Sache#include <netinet/in.h> 6116423Sache#include <arpa/inet.h> 621590Srgrimes 631590Srgrimes#include <err.h> 641590Srgrimes#include <errno.h> 651590Srgrimes#include <grp.h> 6640102Smarkm#include <libutil.h> 6741079Sjdp#include <login_cap.h> 6816423Sache#include <netdb.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#include <utmp.h> 791590Srgrimes 8042850Sabial#ifndef NO_PAM 8141279Sjdp#include <security/pam_appl.h> 8241279Sjdp#include <security/pam_misc.h> 8342850Sabial#endif 843702Spst 851590Srgrimes#include "pathnames.h" 861590Srgrimes 871590Srgrimesvoid badlogin __P((char *)); 881590Srgrimesvoid checknologin __P((void)); 891590Srgrimesvoid dolastlog __P((int)); 901590Srgrimesvoid getloginname __P((void)); 9121528Sdavidnvoid motd __P((char *)); 921590Srgrimesint rootterm __P((char *)); 931590Srgrimesvoid sigint __P((int)); 941590Srgrimesvoid sleepexit __P((int)); 9523985Sdavidnvoid refused __P((char *,char *,int)); 961590Srgrimeschar *stypeof __P((char *)); 971590Srgrimesvoid timedout __P((int)); 9829922Smarkmint login_access __P((char *, char *)); 992224Sguidovoid login_fbtab __P((char *, uid_t, gid_t)); 1001590Srgrimes 10142850Sabial#ifndef NO_PAM 10241279Sjdpstatic int auth_pam __P((void)); 10342850Sabial#endif 10441279Sjdpstatic int auth_traditional __P((void)); 1051590Srgrimesextern void login __P((struct utmp *)); 10627605Scharnierstatic void usage __P((void)); 1071590Srgrimes 1081590Srgrimes#define TTYGRPNAME "tty" /* name of group to own ttys */ 10923985Sdavidn#define DEFAULT_BACKOFF 3 11023985Sdavidn#define DEFAULT_RETRIES 10 1111590Srgrimes 1121590Srgrimes/* 1131590Srgrimes * This bounds the time given to login. Not a define so it can 1141590Srgrimes * be patched on machines where it's too small. 1151590Srgrimes */ 1161590Srgrimesu_int timeout = 300; 1171590Srgrimes 11842272Seivind/* Buffer for signal handling of timeout */ 11942272Seivindjmp_buf timeout_buf; 12042272Seivind 1211590Srgrimesstruct passwd *pwd; 1221590Srgrimesint failures; 12323985Sdavidnchar *term, *envinit[1], *hostname, *username, *tty; 12416423Sachechar full_hostname[MAXHOSTNAMELEN]; 1251590Srgrimes 1261590Srgrimesint 1271590Srgrimesmain(argc, argv) 1281590Srgrimes int argc; 1291590Srgrimes char *argv[]; 1301590Srgrimes{ 1311590Srgrimes extern char **environ; 1321590Srgrimes struct group *gr; 1331590Srgrimes struct stat st; 1341590Srgrimes struct timeval tp; 1351590Srgrimes struct utmp utmp; 13623985Sdavidn int rootok, retries, backoff; 1371590Srgrimes int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval; 1384878Sugen int changepass; 13923985Sdavidn time_t warntime; 14035559Speter uid_t uid, euid; 14146007Sache gid_t egid; 14245431Sbrian char *p, *ttyn; 14342272Seivind char tbuf[MAXPATHLEN + 2]; 14442272Seivind char tname[sizeof(_PATH_TTY) + 10]; 14523985Sdavidn char *shell = NULL; 14621528Sdavidn login_cap_t *lc = NULL; 1471590Srgrimes 14842272Seivind (void)signal(SIGQUIT, SIG_IGN); 14942272Seivind (void)signal(SIGINT, SIG_IGN); 15042272Seivind if (setjmp(timeout_buf)) { 15142272Seivind if (failures) 15242272Seivind badlogin(tbuf); 15342272Seivind (void)fprintf(stderr, 15442272Seivind "Login timed out after %d seconds\n", timeout); 15542272Seivind exit(0); 15642272Seivind } 1571590Srgrimes (void)signal(SIGALRM, timedout); 1581590Srgrimes (void)alarm(timeout); 1591590Srgrimes (void)setpriority(PRIO_PROCESS, 0, 0); 1601590Srgrimes 1611590Srgrimes openlog("login", LOG_ODELAY, LOG_AUTH); 1621590Srgrimes 1631590Srgrimes /* 1641590Srgrimes * -p is used by getty to tell login not to destroy the environment 1651590Srgrimes * -f is used to skip a second login authentication 1661590Srgrimes * -h is used by other servers to pass the name of the remote 1671590Srgrimes * host to login so that it may be placed in utmp and wtmp 1681590Srgrimes */ 1693205Spst *full_hostname = '\0'; 17023985Sdavidn term = NULL; 1711590Srgrimes 1721590Srgrimes fflag = hflag = pflag = 0; 1731590Srgrimes uid = getuid(); 17435557Speter euid = geteuid(); 17546007Sache egid = getegid(); 17624360Simp while ((ch = getopt(argc, argv, "fh:p")) != -1) 1771590Srgrimes switch (ch) { 1781590Srgrimes case 'f': 1791590Srgrimes fflag = 1; 1801590Srgrimes break; 1811590Srgrimes case 'h': 1821590Srgrimes if (uid) 1831590Srgrimes errx(1, "-h option: %s", strerror(EPERM)); 1841590Srgrimes hflag = 1; 1853205Spst strncpy(full_hostname, optarg, sizeof(full_hostname)-1); 18636559Samurai 18745431Sbrian trimdomain(optarg, UT_HOSTSIZE); 18836559Samurai 18916423Sache if (strlen(optarg) > UT_HOSTSIZE) { 19016423Sache struct hostent *hp = gethostbyname(optarg); 19116423Sache 19216423Sache if (hp != NULL) { 19316423Sache struct in_addr in; 19416423Sache 19516423Sache memmove(&in, hp->h_addr, sizeof(in)); 19616423Sache optarg = strdup(inet_ntoa(in)); 19716423Sache } else 19816423Sache optarg = "invalid hostname"; 19916423Sache } 2001590Srgrimes hostname = optarg; 2011590Srgrimes break; 2021590Srgrimes case 'p': 2031590Srgrimes pflag = 1; 2041590Srgrimes break; 2051590Srgrimes case '?': 2061590Srgrimes default: 2071590Srgrimes if (!uid) 2081590Srgrimes syslog(LOG_ERR, "invalid flag %c", ch); 20927605Scharnier usage(); 2101590Srgrimes } 2111590Srgrimes argc -= optind; 2121590Srgrimes argv += optind; 2131590Srgrimes 2141590Srgrimes if (*argv) { 2151590Srgrimes username = *argv; 2161590Srgrimes ask = 0; 2171590Srgrimes } else 2181590Srgrimes ask = 1; 2191590Srgrimes 2201590Srgrimes for (cnt = getdtablesize(); cnt > 2; cnt--) 2211590Srgrimes (void)close(cnt); 2221590Srgrimes 2231590Srgrimes ttyn = ttyname(STDIN_FILENO); 2241590Srgrimes if (ttyn == NULL || *ttyn == '\0') { 2251590Srgrimes (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); 2261590Srgrimes ttyn = tname; 2271590Srgrimes } 22823985Sdavidn if ((tty = strrchr(ttyn, '/')) != NULL) 2291590Srgrimes ++tty; 2301590Srgrimes else 2311590Srgrimes tty = ttyn; 2321590Srgrimes 23323985Sdavidn /* 23423985Sdavidn * Get "login-retries" & "login-backoff" from default class 23523985Sdavidn */ 23623985Sdavidn lc = login_getclass(NULL); 23723985Sdavidn retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES, DEFAULT_RETRIES); 23823985Sdavidn backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF, DEFAULT_BACKOFF); 23923985Sdavidn login_close(lc); 24023985Sdavidn lc = NULL; 24121528Sdavidn 2421590Srgrimes for (cnt = 0;; ask = 1) { 2431590Srgrimes if (ask) { 2441590Srgrimes fflag = 0; 2451590Srgrimes getloginname(); 2461590Srgrimes } 2471590Srgrimes rootlogin = 0; 24821528Sdavidn rootok = rootterm(tty); /* Default (auth may change) */ 24921528Sdavidn 2501590Srgrimes if (strlen(username) > UT_NAMESIZE) 2511590Srgrimes username[UT_NAMESIZE] = '\0'; 2521590Srgrimes 2531590Srgrimes /* 2541590Srgrimes * Note if trying multiple user names; log failures for 2551590Srgrimes * previous user name, but don't bother logging one failure 2561590Srgrimes * for nonexistent name (mistyped username). 2571590Srgrimes */ 2581590Srgrimes if (failures && strcmp(tbuf, username)) { 2591590Srgrimes if (failures > (pwd ? 0 : 1)) 2601590Srgrimes badlogin(tbuf); 2611590Srgrimes } 26227605Scharnier (void)strncpy(tbuf, username, sizeof tbuf-1); 26327605Scharnier tbuf[sizeof tbuf-1] = '\0'; 2641590Srgrimes 26541279Sjdp pwd = getpwnam(username); 26623985Sdavidn 26723985Sdavidn /* 2681590Srgrimes * if we have a valid account name, and it doesn't have a 2691590Srgrimes * password, or the -f option was specified and the caller 2701590Srgrimes * is root or the caller isn't changing their uid, don't 2711590Srgrimes * authenticate. 2721590Srgrimes */ 27321528Sdavidn if (pwd != NULL) { 2743205Spst if (pwd->pw_uid == 0) 2753205Spst rootlogin = 1; 2763205Spst 27723985Sdavidn if (fflag && (uid == (uid_t)0 || 27823985Sdavidn uid == (uid_t)pwd->pw_uid)) { 2793205Spst /* already authenticated */ 2803205Spst break; 2813205Spst } else if (pwd->pw_passwd[0] == '\0') { 28224321Sdavidn if (!rootlogin || rootok) { 28324251Sdavidn /* pretend password okay */ 28424251Sdavidn rval = 0; 28524251Sdavidn goto ttycheck; 28624251Sdavidn } 2873205Spst } 2883205Spst } 2893205Spst 2901590Srgrimes fflag = 0; 2911590Srgrimes 2921590Srgrimes (void)setpriority(PRIO_PROCESS, 0, -4); 2931590Srgrimes 29442850Sabial#ifndef NO_PAM 29541279Sjdp /* 29641279Sjdp * Try to authenticate using PAM. If a PAM system error 29741279Sjdp * occurs, perhaps because of a botched configuration, 29841279Sjdp * then fall back to using traditional Unix authentication. 29941279Sjdp */ 30041279Sjdp if ((rval = auth_pam()) == -1) 30142850Sabial#endif /* NO_PAM */ 30241279Sjdp rval = auth_traditional(); 30323985Sdavidn 30441279Sjdp (void)setpriority(PRIO_PROCESS, 0, 0); 30523985Sdavidn 30642850Sabial#ifndef NO_PAM 30741279Sjdp /* 30841279Sjdp * PAM authentication may have changed "pwd" to the 30941279Sjdp * entry for the template user. Check again to see if 31041279Sjdp * this is a root login after all. 31141279Sjdp */ 31241279Sjdp if (pwd != NULL && pwd->pw_uid == 0) 31341279Sjdp rootlogin = 1; 31442850Sabial#endif /* NO_PAM */ 3151590Srgrimes 3163205Spst ttycheck: 3171590Srgrimes /* 3181590Srgrimes * If trying to log in as root without Kerberos, 3191590Srgrimes * but with insecure terminal, refuse the login attempt. 3201590Srgrimes */ 32124222Sdavidn if (pwd && !rval) { 32224222Sdavidn if (rootlogin && !rootok) 32324222Sdavidn refused(NULL, "NOROOT", 0); 32424222Sdavidn else /* valid password & authenticated */ 32524222Sdavidn break; 3261590Srgrimes } 3271590Srgrimes 3281590Srgrimes (void)printf("Login incorrect\n"); 3291590Srgrimes failures++; 33023985Sdavidn 33123985Sdavidn /* 33223985Sdavidn * we allow up to 'retry' (10) tries, 33323985Sdavidn * but after 'backoff' (3) we start backing off 33423985Sdavidn */ 33523985Sdavidn if (++cnt > backoff) { 33623985Sdavidn if (cnt >= retries) { 3371590Srgrimes badlogin(username); 3381590Srgrimes sleepexit(1); 3391590Srgrimes } 34038374Sjkoshy sleep((u_int)((cnt - backoff) * 5)); 3411590Srgrimes } 3421590Srgrimes } 3431590Srgrimes 3441590Srgrimes /* committed to login -- turn off timeout */ 3451590Srgrimes (void)alarm((u_int)0); 3461590Srgrimes 3471590Srgrimes endpwent(); 3481590Srgrimes 34941279Sjdp /* 35041279Sjdp * Establish the login class. 35141279Sjdp */ 35241279Sjdp lc = login_getpwclass(pwd); 35341279Sjdp 3541590Srgrimes /* if user not super-user, check for disabled logins */ 3551590Srgrimes if (!rootlogin) 35621528Sdavidn auth_checknologin(lc); 3571590Srgrimes 35821528Sdavidn quietlog = login_getcapbool(lc, "hushlogin", 0); 35946007Sache /* Switching needed for NFS with root access disabled */ 36046007Sache (void)setegid(pwd->pw_gid); 36135557Speter (void)seteuid(rootlogin ? 0 : pwd->pw_uid); 36221528Sdavidn if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) { 36326021Spst if (login_getcapbool(lc, "requirehome", 0)) 36423985Sdavidn refused("Home directory not available", "HOMEDIR", 1); 36524485Sdavidn if (chdir("/") < 0) 36623985Sdavidn refused("Cannot find root directory", "ROOTDIR", 1); 3671590Srgrimes pwd->pw_dir = "/"; 36823985Sdavidn if (!quietlog || *pwd->pw_dir) 36923985Sdavidn printf("No home directory.\nLogging in with home = \"/\".\n"); 3701590Srgrimes } 37135557Speter (void)seteuid(euid); 37246007Sache (void)setegid(egid); 37321528Sdavidn if (!quietlog) 37421528Sdavidn quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; 3751590Srgrimes 3761590Srgrimes if (pwd->pw_change || pwd->pw_expire) 3771590Srgrimes (void)gettimeofday(&tp, (struct timezone *)NULL); 3782532Sjkh 37930564Sjoerg#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ 38021528Sdavidn 38123985Sdavidn warntime = login_getcaptime(lc, "warnpassword", 38223985Sdavidn DEFAULT_WARN, DEFAULT_WARN); 38323985Sdavidn 3844878Sugen changepass=0; 38521528Sdavidn if (pwd->pw_change) { 3861590Srgrimes if (tp.tv_sec >= pwd->pw_change) { 3871590Srgrimes (void)printf("Sorry -- your password has expired.\n"); 3884878Sugen changepass=1; 38923985Sdavidn syslog(LOG_INFO, 39023985Sdavidn "%s Password expired - forcing change", 39123985Sdavidn pwd->pw_name); 39223985Sdavidn } else if (pwd->pw_change - tp.tv_sec < warntime && !quietlog) 39323985Sdavidn (void)printf("Warning: your password expires on %s", 39423985Sdavidn ctime(&pwd->pw_change)); 39523985Sdavidn } 39623985Sdavidn 39723985Sdavidn warntime = login_getcaptime(lc, "warnexpire", 39823985Sdavidn DEFAULT_WARN, DEFAULT_WARN); 39923985Sdavidn 40021528Sdavidn if (pwd->pw_expire) { 4011590Srgrimes if (tp.tv_sec >= pwd->pw_expire) { 40223985Sdavidn refused("Sorry -- your account has expired", 40323985Sdavidn "EXPIRED", 1); 40423985Sdavidn } else if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog) 40523985Sdavidn (void)printf("Warning: your account expires on %s", 40623985Sdavidn ctime(&pwd->pw_expire)); 40721528Sdavidn } 4081590Srgrimes 40921528Sdavidn if (lc != NULL) { 41021528Sdavidn if (hostname) { 41121528Sdavidn struct hostent *hp = gethostbyname(full_hostname); 41221528Sdavidn 41321528Sdavidn if (hp == NULL) 41421528Sdavidn optarg = NULL; 41521528Sdavidn else { 41621528Sdavidn struct in_addr in; 41721528Sdavidn memmove(&in, hp->h_addr, sizeof(in)); 41821528Sdavidn optarg = strdup(inet_ntoa(in)); 41921528Sdavidn } 42023985Sdavidn if (!auth_hostok(lc, full_hostname, optarg)) 42123985Sdavidn refused("Permission denied", "HOST", 1); 42221528Sdavidn } 42321528Sdavidn 42423985Sdavidn if (!auth_ttyok(lc, tty)) 42523985Sdavidn refused("Permission denied", "TTY", 1); 42621528Sdavidn 42723985Sdavidn if (!auth_timeok(lc, time(NULL))) 42823985Sdavidn refused("Logins not available right now", "TIME", 1); 42921528Sdavidn } 43023985Sdavidn shell=login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell); 43123985Sdavidn if (*pwd->pw_shell == '\0') 43223985Sdavidn pwd->pw_shell = _PATH_BSHELL; 43323985Sdavidn if (*shell == '\0') /* Not overridden */ 43423985Sdavidn shell = pwd->pw_shell; 43523985Sdavidn if ((shell = strdup(shell)) == NULL) { 43623985Sdavidn syslog(LOG_NOTICE, "memory allocation error"); 43723985Sdavidn sleepexit(1); 43823985Sdavidn } 43921528Sdavidn 44021528Sdavidn#ifdef LOGIN_ACCESS 44123985Sdavidn if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0) 44223985Sdavidn refused("Permission denied", "ACCESS", 1); 44321528Sdavidn#endif /* LOGIN_ACCESS */ 44421528Sdavidn 4451590Srgrimes /* Nothing else left to fail -- really log in. */ 4461590Srgrimes memset((void *)&utmp, 0, sizeof(utmp)); 4471590Srgrimes (void)time(&utmp.ut_time); 4481590Srgrimes (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); 4491590Srgrimes if (hostname) 4501590Srgrimes (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); 4511590Srgrimes (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); 4521590Srgrimes login(&utmp); 4531590Srgrimes 4541590Srgrimes dolastlog(quietlog); 4551590Srgrimes 4562224Sguido /* 4572224Sguido * Set device protections, depending on what terminal the 4582224Sguido * user is logged in. This feature is used on Suns to give 4592224Sguido * console users better privacy. 4602224Sguido */ 4612224Sguido login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); 4622224Sguido 46323985Sdavidn (void)chown(ttyn, pwd->pw_uid, 46423985Sdavidn (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); 4651590Srgrimes 46623985Sdavidn /* 46723985Sdavidn * Preserve TERM if it happens to be already set. 46823985Sdavidn */ 46924222Sdavidn if ((term = getenv("TERM")) != NULL) 47024222Sdavidn term = strdup(term); 4711590Srgrimes 47223985Sdavidn /* 47323985Sdavidn * Exclude cons/vt/ptys only, assume dialup otherwise 47423985Sdavidn * TODO: Make dialup tty determination a library call 47523985Sdavidn * for consistency (finger etc.) 47623985Sdavidn */ 47724894Sdavidn if (hostname==NULL && isdialuptty(tty)) 4781590Srgrimes syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); 4791590Srgrimes 4803205Spst#ifdef LOGALL 4813205Spst /* 4823205Spst * Syslog each successful login, so we don't have to watch hundreds 4833205Spst * of wtmp or lastlogin files. 4843205Spst */ 48523985Sdavidn if (hostname) 48623985Sdavidn syslog(LOG_INFO, "login from %s on %s as %s", 48723985Sdavidn full_hostname, tty, pwd->pw_name); 48823985Sdavidn else 48923985Sdavidn syslog(LOG_INFO, "login on %s as %s", 49023985Sdavidn tty, pwd->pw_name); 49121528Sdavidn#endif 49221528Sdavidn 49323985Sdavidn /* 49423985Sdavidn * If fflag is on, assume caller/authenticator has logged root login. 49523985Sdavidn */ 49623985Sdavidn if (rootlogin && fflag == 0) 49723985Sdavidn { 49823985Sdavidn if (hostname) 49923985Sdavidn syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s", 50023985Sdavidn username, tty, full_hostname); 50123985Sdavidn else 50223985Sdavidn syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", 50323985Sdavidn username, tty); 50423985Sdavidn } 50523985Sdavidn 50623985Sdavidn /* 50723985Sdavidn * Destroy environment unless user has requested its preservation. 50823985Sdavidn * We need to do this before setusercontext() because that may 50923985Sdavidn * set or reset some environment variables. 51023985Sdavidn */ 51121528Sdavidn if (!pflag) 51221528Sdavidn environ = envinit; 51321528Sdavidn 51423985Sdavidn /* 51523985Sdavidn * We don't need to be root anymore, so 51621528Sdavidn * set the user and session context 51721528Sdavidn */ 51841279Sjdp if (setlogin(username) != 0) { 51941279Sjdp syslog(LOG_ERR, "setlogin(%s): %m - exiting", username); 52041279Sjdp exit(1); 52141279Sjdp } 52241279Sjdp if (setusercontext(lc, pwd, pwd->pw_uid, 52341279Sjdp LOGIN_SETALL & ~LOGIN_SETLOGIN) != 0) { 52421528Sdavidn syslog(LOG_ERR, "setusercontext() failed - exiting"); 52521528Sdavidn exit(1); 5263205Spst } 52721528Sdavidn 52823148Sache (void)setenv("SHELL", pwd->pw_shell, 1); 52921528Sdavidn (void)setenv("HOME", pwd->pw_dir, 1); 53023985Sdavidn if (term != NULL && *term != '\0') 53121528Sdavidn (void)setenv("TERM", term, 1); /* Preset overrides */ 53221528Sdavidn else { 53323985Sdavidn (void)setenv("TERM", stypeof(tty), 0); /* Fallback doesn't */ 53421528Sdavidn } 53541279Sjdp (void)setenv("LOGNAME", username, 1); 53641279Sjdp (void)setenv("USER", username, 1); 53721528Sdavidn (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0); 53821528Sdavidn 5391590Srgrimes if (!quietlog) { 54023985Sdavidn char *cw; 54123985Sdavidn 54223985Sdavidn cw = login_getcapstr(lc, "copyright", NULL, NULL); 54321528Sdavidn if (cw != NULL && access(cw, F_OK) == 0) 54421528Sdavidn motd(cw); 54521528Sdavidn else 54623985Sdavidn (void)printf("%s\n\t%s %s\n", 54723985Sdavidn "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994", 54823985Sdavidn "The Regents of the University of California. ", 54923985Sdavidn "All rights reserved."); 55023985Sdavidn 55123985Sdavidn (void)printf("\n"); 55223985Sdavidn 55321528Sdavidn cw = login_getcapstr(lc, "welcome", NULL, NULL); 55421528Sdavidn if (cw == NULL || access(cw, F_OK) != 0) 55521528Sdavidn cw = _PATH_MOTDFILE; 55621528Sdavidn motd(cw); 55723985Sdavidn 55821528Sdavidn cw = getenv("MAIL"); /* $MAIL may have been set by class */ 55921528Sdavidn if (cw != NULL) { 56021528Sdavidn strncpy(tbuf, cw, sizeof(tbuf)); 56121528Sdavidn tbuf[sizeof(tbuf)-1] = '\0'; 56221528Sdavidn } else 56323985Sdavidn snprintf(tbuf, sizeof(tbuf), "%s/%s", 56423985Sdavidn _PATH_MAILDIR, pwd->pw_name); 5651590Srgrimes if (stat(tbuf, &st) == 0 && st.st_size != 0) 56623985Sdavidn (void)printf("You have %smail.\n", 56723985Sdavidn (st.st_mtime > st.st_atime) ? "new " : ""); 5681590Srgrimes } 5691590Srgrimes 57021528Sdavidn login_close(lc); 5713205Spst 5721590Srgrimes (void)signal(SIGALRM, SIG_DFL); 5731590Srgrimes (void)signal(SIGQUIT, SIG_DFL); 5741590Srgrimes (void)signal(SIGINT, SIG_DFL); 5751590Srgrimes (void)signal(SIGTSTP, SIG_IGN); 5761590Srgrimes 5774878Sugen if (changepass) { 57821528Sdavidn if (system(_PATH_CHPASS) != 0) 5794878Sugen sleepexit(1); 5804878Sugen } 5814878Sugen 58223985Sdavidn /* 58323985Sdavidn * Login shells have a leading '-' in front of argv[0] 58423985Sdavidn */ 58523985Sdavidn tbuf[0] = '-'; 58623985Sdavidn (void)strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell); 58723985Sdavidn 58821528Sdavidn execlp(shell, tbuf, 0); 58921528Sdavidn err(1, "%s", shell); 5901590Srgrimes} 5911590Srgrimes 59241279Sjdpstatic int 59341279Sjdpauth_traditional() 59441279Sjdp{ 59541279Sjdp int rval; 59641279Sjdp char *p; 59741279Sjdp char *ep; 59841279Sjdp char *salt; 59941279Sjdp 60041279Sjdp rval = 1; 60141279Sjdp salt = pwd != NULL ? pwd->pw_passwd : "xx"; 60241279Sjdp 60341279Sjdp p = getpass("Password:"); 60441279Sjdp ep = crypt(p, salt); 60541279Sjdp 60641279Sjdp if (pwd) { 60741279Sjdp if (!p[0] && pwd->pw_passwd[0]) 60841279Sjdp ep = ":"; 60941279Sjdp if (strcmp(ep, pwd->pw_passwd) == 0) 61041279Sjdp rval = 0; 61141279Sjdp } 61241279Sjdp 61341279Sjdp /* clear entered password */ 61441279Sjdp memset(p, 0, strlen(p)); 61541279Sjdp return rval; 61641279Sjdp} 61741279Sjdp 61842850Sabial#ifndef NO_PAM 61941279Sjdp/* 62041279Sjdp * Attempt to authenticate the user using PAM. Returns 0 if the user is 62141279Sjdp * authenticated, or 1 if not authenticated. If some sort of PAM system 62241279Sjdp * error occurs (e.g., the "/etc/pam.conf" file is missing) then this 62341279Sjdp * function returns -1. This can be used as an indication that we should 62441279Sjdp * fall back to a different authentication mechanism. 62541279Sjdp */ 62641279Sjdpstatic int 62741279Sjdpauth_pam() 62841279Sjdp{ 62941279Sjdp pam_handle_t *pamh = NULL; 63041279Sjdp const char *tmpl_user; 63141279Sjdp const void *item; 63241279Sjdp int rval; 63341279Sjdp int e; 63441279Sjdp static struct pam_conv conv = { misc_conv, NULL }; 63541279Sjdp 63641279Sjdp if ((e = pam_start("login", username, &conv, &pamh)) != PAM_SUCCESS) { 63741279Sjdp syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e)); 63841279Sjdp return -1; 63941279Sjdp } 64041279Sjdp if ((e = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) { 64141279Sjdp syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s", 64241279Sjdp pam_strerror(pamh, e)); 64341279Sjdp return -1; 64441279Sjdp } 64541279Sjdp if (hostname != NULL && 64641279Sjdp (e = pam_set_item(pamh, PAM_RHOST, full_hostname)) != PAM_SUCCESS) { 64741279Sjdp syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", 64841279Sjdp pam_strerror(pamh, e)); 64941279Sjdp return -1; 65041279Sjdp } 65141279Sjdp e = pam_authenticate(pamh, 0); 65241279Sjdp switch (e) { 65341279Sjdp 65441279Sjdp case PAM_SUCCESS: 65541279Sjdp /* 65641279Sjdp * With PAM we support the concept of a "template" 65741279Sjdp * user. The user enters a login name which is 65841279Sjdp * authenticated by PAM, usually via a remote service 65941279Sjdp * such as RADIUS or TACACS+. If authentication 66041279Sjdp * succeeds, a different but related "template" name 66141279Sjdp * is used for setting the credentials, shell, and 66241279Sjdp * home directory. The name the user enters need only 66341279Sjdp * exist on the remote authentication server, but the 66441279Sjdp * template name must be present in the local password 66541279Sjdp * database. 66641279Sjdp * 66741279Sjdp * This is supported by two various mechanisms in the 66841279Sjdp * individual modules. However, from the application's 66941279Sjdp * point of view, the template user is always passed 67041279Sjdp * back as a changed value of the PAM_USER item. 67141279Sjdp */ 67241279Sjdp if ((e = pam_get_item(pamh, PAM_USER, &item)) == 67341279Sjdp PAM_SUCCESS) { 67441279Sjdp tmpl_user = (const char *) item; 67541279Sjdp if (strcmp(username, tmpl_user) != 0) 67641279Sjdp pwd = getpwnam(tmpl_user); 67741279Sjdp } else 67841279Sjdp syslog(LOG_ERR, "Couldn't get PAM_USER: %s", 67941279Sjdp pam_strerror(pamh, e)); 68041279Sjdp rval = 0; 68141279Sjdp break; 68241279Sjdp 68341279Sjdp case PAM_AUTH_ERR: 68441279Sjdp case PAM_USER_UNKNOWN: 68541279Sjdp case PAM_MAXTRIES: 68641279Sjdp rval = 1; 68741279Sjdp break; 68841279Sjdp 68941279Sjdp default: 69041279Sjdp syslog(LOG_ERR, "auth_pam: %s", pam_strerror(pamh, e)); 69141279Sjdp rval = -1; 69241279Sjdp break; 69341279Sjdp } 69441279Sjdp if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 69541279Sjdp syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 69641279Sjdp rval = -1; 69741279Sjdp } 69841279Sjdp return rval; 69941279Sjdp} 70042850Sabial#endif /* NO_PAM */ 70141279Sjdp 70227605Scharnierstatic void 70327605Scharnierusage() 70427605Scharnier{ 70527605Scharnier (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n"); 70627605Scharnier exit(1); 70727605Scharnier} 7081590Srgrimes 70923985Sdavidn/* 71023985Sdavidn * Allow for authentication style and/or kerberos instance 71123985Sdavidn * */ 71221528Sdavidn 71321528Sdavidn#define NBUFSIZ UT_NAMESIZE + 64 71421528Sdavidn 7151590Srgrimesvoid 7161590Srgrimesgetloginname() 7171590Srgrimes{ 7181590Srgrimes int ch; 7191590Srgrimes char *p; 7201590Srgrimes static char nbuf[NBUFSIZ]; 7211590Srgrimes 7221590Srgrimes for (;;) { 7231590Srgrimes (void)printf("login: "); 7241590Srgrimes for (p = nbuf; (ch = getchar()) != '\n'; ) { 7251590Srgrimes if (ch == EOF) { 7261590Srgrimes badlogin(username); 7271590Srgrimes exit(0); 7281590Srgrimes } 7291590Srgrimes if (p < nbuf + (NBUFSIZ - 1)) 7301590Srgrimes *p++ = ch; 7311590Srgrimes } 7321590Srgrimes if (p > nbuf) 7331590Srgrimes if (nbuf[0] == '-') 7341590Srgrimes (void)fprintf(stderr, 7351590Srgrimes "login names may not start with '-'.\n"); 7361590Srgrimes else { 7371590Srgrimes *p = '\0'; 7381590Srgrimes username = nbuf; 7391590Srgrimes break; 7401590Srgrimes } 7411590Srgrimes } 7421590Srgrimes} 7431590Srgrimes 7441590Srgrimesint 7451590Srgrimesrootterm(ttyn) 7461590Srgrimes char *ttyn; 7471590Srgrimes{ 7481590Srgrimes struct ttyent *t; 74923985Sdavidn 7501590Srgrimes return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE); 7511590Srgrimes} 7521590Srgrimes 75321528Sdavidnvolatile int motdinterrupt; 7541590Srgrimes 75521528Sdavidn/* ARGSUSED */ 7561590Srgrimesvoid 75721528Sdavidnsigint(signo) 75821528Sdavidn int signo; 7591590Srgrimes{ 76021528Sdavidn motdinterrupt = 1; 76121528Sdavidn} 76221528Sdavidn 76321528Sdavidnvoid 76421528Sdavidnmotd(motdfile) 76521528Sdavidn char *motdfile; 76621528Sdavidn{ 7671590Srgrimes int fd, nchars; 7681590Srgrimes sig_t oldint; 76921528Sdavidn char tbuf[256]; 7701590Srgrimes 77121528Sdavidn if ((fd = open(motdfile, O_RDONLY, 0)) < 0) 7721590Srgrimes return; 77321528Sdavidn motdinterrupt = 0; 7741590Srgrimes oldint = signal(SIGINT, sigint); 77521528Sdavidn while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 && !motdinterrupt) 77621528Sdavidn (void)write(fileno(stdout), tbuf, nchars); 7771590Srgrimes (void)signal(SIGINT, oldint); 7781590Srgrimes (void)close(fd); 7791590Srgrimes} 7801590Srgrimes 7811590Srgrimes/* ARGSUSED */ 7821590Srgrimesvoid 7831590Srgrimestimedout(signo) 7841590Srgrimes int signo; 7851590Srgrimes{ 78642272Seivind longjmp(timeout_buf, signo); 7871590Srgrimes} 7881590Srgrimes 7891590Srgrimes 7901590Srgrimesvoid 7911590Srgrimesdolastlog(quiet) 7921590Srgrimes int quiet; 7931590Srgrimes{ 7941590Srgrimes struct lastlog ll; 7951590Srgrimes int fd; 7961590Srgrimes 7971590Srgrimes if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { 7981590Srgrimes (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 7991590Srgrimes if (!quiet) { 8001590Srgrimes if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && 8011590Srgrimes ll.ll_time != 0) { 8021590Srgrimes (void)printf("Last login: %.*s ", 8031590Srgrimes 24-5, (char *)ctime(&ll.ll_time)); 8041590Srgrimes if (*ll.ll_host != '\0') 8051590Srgrimes (void)printf("from %.*s\n", 8061590Srgrimes (int)sizeof(ll.ll_host), 8071590Srgrimes ll.ll_host); 8081590Srgrimes else 8091590Srgrimes (void)printf("on %.*s\n", 8101590Srgrimes (int)sizeof(ll.ll_line), 8111590Srgrimes ll.ll_line); 8121590Srgrimes } 8131590Srgrimes (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 8141590Srgrimes } 8151590Srgrimes memset((void *)&ll, 0, sizeof(ll)); 8161590Srgrimes (void)time(&ll.ll_time); 8171590Srgrimes (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); 8181590Srgrimes if (hostname) 8191590Srgrimes (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); 8201590Srgrimes (void)write(fd, (char *)&ll, sizeof(ll)); 8211590Srgrimes (void)close(fd); 8221590Srgrimes } 8231590Srgrimes} 8241590Srgrimes 8251590Srgrimesvoid 8261590Srgrimesbadlogin(name) 8271590Srgrimes char *name; 8281590Srgrimes{ 8291590Srgrimes 8301590Srgrimes if (failures == 0) 8311590Srgrimes return; 8321590Srgrimes if (hostname) { 8331590Srgrimes syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s", 83416423Sache failures, failures > 1 ? "S" : "", full_hostname); 8351590Srgrimes syslog(LOG_AUTHPRIV|LOG_NOTICE, 8361590Srgrimes "%d LOGIN FAILURE%s FROM %s, %s", 83716423Sache failures, failures > 1 ? "S" : "", full_hostname, name); 8381590Srgrimes } else { 8391590Srgrimes syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s", 8401590Srgrimes failures, failures > 1 ? "S" : "", tty); 8411590Srgrimes syslog(LOG_AUTHPRIV|LOG_NOTICE, 8421590Srgrimes "%d LOGIN FAILURE%s ON %s, %s", 8431590Srgrimes failures, failures > 1 ? "S" : "", tty, name); 8441590Srgrimes } 84542272Seivind failures = 0; 8461590Srgrimes} 8471590Srgrimes 8481590Srgrimes#undef UNKNOWN 8491590Srgrimes#define UNKNOWN "su" 8501590Srgrimes 8511590Srgrimeschar * 8521590Srgrimesstypeof(ttyid) 8531590Srgrimes char *ttyid; 8541590Srgrimes{ 85523985Sdavidn 8561590Srgrimes struct ttyent *t; 85723985Sdavidn 8581590Srgrimes return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN); 8591590Srgrimes} 8601590Srgrimes 8611590Srgrimesvoid 86223985Sdavidnrefused(msg, rtype, lout) 86323985Sdavidn char *msg; 86423985Sdavidn char *rtype; 86523985Sdavidn int lout; 86623985Sdavidn{ 86723985Sdavidn 86823985Sdavidn if (msg != NULL) 86923985Sdavidn printf("%s.\n", msg); 87023985Sdavidn if (hostname) 87123985Sdavidn syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s", 87223985Sdavidn pwd->pw_name, rtype, full_hostname, tty); 87323985Sdavidn else 87423985Sdavidn syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s", 87523985Sdavidn pwd->pw_name, rtype, tty); 87623985Sdavidn if (lout) 87723985Sdavidn sleepexit(1); 87823985Sdavidn} 87923985Sdavidn 88023985Sdavidnvoid 8811590Srgrimessleepexit(eval) 8821590Srgrimes int eval; 8831590Srgrimes{ 88423985Sdavidn 8851590Srgrimes (void)sleep(5); 8861590Srgrimes exit(eval); 8871590Srgrimes} 888