login.c revision 57546
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[] = 4550477Speter "$FreeBSD: head/usr.bin/login/login.c 57546 2000-02-28 01:48:50Z ache $"; 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> 5757339Sshin#include <sys/socket.h> 581590Srgrimes#include <sys/time.h> 591590Srgrimes#include <sys/resource.h> 601590Srgrimes#include <sys/file.h> 6116423Sache#include <netinet/in.h> 6216423Sache#include <arpa/inet.h> 631590Srgrimes 641590Srgrimes#include <err.h> 651590Srgrimes#include <errno.h> 661590Srgrimes#include <grp.h> 6740102Smarkm#include <libutil.h> 6841079Sjdp#include <login_cap.h> 6916423Sache#include <netdb.h> 701590Srgrimes#include <pwd.h> 711590Srgrimes#include <setjmp.h> 721590Srgrimes#include <signal.h> 731590Srgrimes#include <stdio.h> 741590Srgrimes#include <stdlib.h> 751590Srgrimes#include <string.h> 761590Srgrimes#include <syslog.h> 771590Srgrimes#include <ttyent.h> 781590Srgrimes#include <unistd.h> 791590Srgrimes#include <utmp.h> 801590Srgrimes 8142850Sabial#ifndef NO_PAM 8241279Sjdp#include <security/pam_appl.h> 8341279Sjdp#include <security/pam_misc.h> 8442850Sabial#endif 853702Spst 861590Srgrimes#include "pathnames.h" 871590Srgrimes 8857339Sshin/* wrapper for KAME-special getnameinfo() */ 8957339Sshin#ifndef NI_WITHSCOPEID 9057339Sshin#define NI_WITHSCOPEID 0 9157339Sshin#endif 9257339Sshin 931590Srgrimesvoid badlogin __P((char *)); 941590Srgrimesvoid checknologin __P((void)); 951590Srgrimesvoid dolastlog __P((int)); 961590Srgrimesvoid getloginname __P((void)); 9721528Sdavidnvoid motd __P((char *)); 981590Srgrimesint rootterm __P((char *)); 991590Srgrimesvoid sigint __P((int)); 1001590Srgrimesvoid sleepexit __P((int)); 10123985Sdavidnvoid refused __P((char *,char *,int)); 1021590Srgrimeschar *stypeof __P((char *)); 1031590Srgrimesvoid timedout __P((int)); 10429922Smarkmint login_access __P((char *, char *)); 1052224Sguidovoid login_fbtab __P((char *, uid_t, gid_t)); 1061590Srgrimes 10742850Sabial#ifndef NO_PAM 10841279Sjdpstatic int auth_pam __P((void)); 10942850Sabial#endif 11041279Sjdpstatic int auth_traditional __P((void)); 1111590Srgrimesextern void login __P((struct utmp *)); 11227605Scharnierstatic void usage __P((void)); 1131590Srgrimes 1141590Srgrimes#define TTYGRPNAME "tty" /* name of group to own ttys */ 11523985Sdavidn#define DEFAULT_BACKOFF 3 11623985Sdavidn#define DEFAULT_RETRIES 10 1171590Srgrimes 1181590Srgrimes/* 1191590Srgrimes * This bounds the time given to login. Not a define so it can 1201590Srgrimes * be patched on machines where it's too small. 1211590Srgrimes */ 1221590Srgrimesu_int timeout = 300; 1231590Srgrimes 12442272Seivind/* Buffer for signal handling of timeout */ 12542272Seivindjmp_buf timeout_buf; 12642272Seivind 1271590Srgrimesstruct passwd *pwd; 1281590Srgrimesint failures; 12923985Sdavidnchar *term, *envinit[1], *hostname, *username, *tty; 13016423Sachechar full_hostname[MAXHOSTNAMELEN]; 1311590Srgrimes 1321590Srgrimesint 1331590Srgrimesmain(argc, argv) 1341590Srgrimes int argc; 1351590Srgrimes char *argv[]; 1361590Srgrimes{ 1371590Srgrimes extern char **environ; 1381590Srgrimes struct group *gr; 1391590Srgrimes struct stat st; 1401590Srgrimes struct timeval tp; 1411590Srgrimes struct utmp utmp; 14223985Sdavidn int rootok, retries, backoff; 1431590Srgrimes int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval; 1444878Sugen int changepass; 14523985Sdavidn time_t warntime; 14635559Speter uid_t uid, euid; 14746007Sache gid_t egid; 14845431Sbrian char *p, *ttyn; 14942272Seivind char tbuf[MAXPATHLEN + 2]; 15042272Seivind char tname[sizeof(_PATH_TTY) + 10]; 15123985Sdavidn char *shell = NULL; 15221528Sdavidn login_cap_t *lc = NULL; 1531590Srgrimes 15442272Seivind (void)signal(SIGQUIT, SIG_IGN); 15542272Seivind (void)signal(SIGINT, SIG_IGN); 15642272Seivind if (setjmp(timeout_buf)) { 15742272Seivind if (failures) 15842272Seivind badlogin(tbuf); 15942272Seivind (void)fprintf(stderr, 16042272Seivind "Login timed out after %d seconds\n", timeout); 16142272Seivind exit(0); 16242272Seivind } 1631590Srgrimes (void)signal(SIGALRM, timedout); 1641590Srgrimes (void)alarm(timeout); 1651590Srgrimes (void)setpriority(PRIO_PROCESS, 0, 0); 1661590Srgrimes 1671590Srgrimes openlog("login", LOG_ODELAY, LOG_AUTH); 1681590Srgrimes 1691590Srgrimes /* 1701590Srgrimes * -p is used by getty to tell login not to destroy the environment 1711590Srgrimes * -f is used to skip a second login authentication 1721590Srgrimes * -h is used by other servers to pass the name of the remote 1731590Srgrimes * host to login so that it may be placed in utmp and wtmp 1741590Srgrimes */ 1753205Spst *full_hostname = '\0'; 17623985Sdavidn term = NULL; 1771590Srgrimes 1781590Srgrimes fflag = hflag = pflag = 0; 1791590Srgrimes uid = getuid(); 18035557Speter euid = geteuid(); 18146007Sache egid = getegid(); 18224360Simp while ((ch = getopt(argc, argv, "fh:p")) != -1) 1831590Srgrimes switch (ch) { 1841590Srgrimes case 'f': 1851590Srgrimes fflag = 1; 1861590Srgrimes break; 1871590Srgrimes case 'h': 1881590Srgrimes if (uid) 1891590Srgrimes errx(1, "-h option: %s", strerror(EPERM)); 1901590Srgrimes hflag = 1; 1913205Spst strncpy(full_hostname, optarg, sizeof(full_hostname)-1); 19236559Samurai 19345431Sbrian trimdomain(optarg, UT_HOSTSIZE); 19436559Samurai 19516423Sache if (strlen(optarg) > UT_HOSTSIZE) { 19657339Sshin struct addrinfo hints, *res; 19757339Sshin int ga_err; 19857339Sshin 19957339Sshin memset(&hints, 0, sizeof(hints)); 20057339Sshin hints.ai_family = AF_UNSPEC; 20157339Sshin ga_err = getaddrinfo(optarg, NULL, &hints, 20257339Sshin &res); 20357339Sshin if (ga_err == 0) { 20457339Sshin char hostbuf[MAXHOSTNAMELEN]; 20516423Sache 20657339Sshin getnameinfo(res->ai_addr, 20757339Sshin res->ai_addrlen, 20857339Sshin hostbuf, 20957339Sshin sizeof(hostbuf), NULL, 0, 21057339Sshin NI_NUMERICHOST| 21157339Sshin NI_WITHSCOPEID); 21257339Sshin optarg = strdup(hostbuf); 21316423Sache } else 21416423Sache optarg = "invalid hostname"; 21557339Sshin if (res != NULL) 21657339Sshin freeaddrinfo(res); 21716423Sache } 2181590Srgrimes hostname = optarg; 2191590Srgrimes break; 2201590Srgrimes case 'p': 2211590Srgrimes pflag = 1; 2221590Srgrimes break; 2231590Srgrimes case '?': 2241590Srgrimes default: 2251590Srgrimes if (!uid) 2261590Srgrimes syslog(LOG_ERR, "invalid flag %c", ch); 22727605Scharnier usage(); 2281590Srgrimes } 2291590Srgrimes argc -= optind; 2301590Srgrimes argv += optind; 2311590Srgrimes 2321590Srgrimes if (*argv) { 2331590Srgrimes username = *argv; 2341590Srgrimes ask = 0; 2351590Srgrimes } else 2361590Srgrimes ask = 1; 2371590Srgrimes 2381590Srgrimes for (cnt = getdtablesize(); cnt > 2; cnt--) 2391590Srgrimes (void)close(cnt); 2401590Srgrimes 2411590Srgrimes ttyn = ttyname(STDIN_FILENO); 2421590Srgrimes if (ttyn == NULL || *ttyn == '\0') { 2431590Srgrimes (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); 2441590Srgrimes ttyn = tname; 2451590Srgrimes } 24623985Sdavidn if ((tty = strrchr(ttyn, '/')) != NULL) 2471590Srgrimes ++tty; 2481590Srgrimes else 2491590Srgrimes tty = ttyn; 2501590Srgrimes 25123985Sdavidn /* 25223985Sdavidn * Get "login-retries" & "login-backoff" from default class 25323985Sdavidn */ 25423985Sdavidn lc = login_getclass(NULL); 25523985Sdavidn retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES, DEFAULT_RETRIES); 25623985Sdavidn backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF, DEFAULT_BACKOFF); 25723985Sdavidn login_close(lc); 25823985Sdavidn lc = NULL; 25921528Sdavidn 2601590Srgrimes for (cnt = 0;; ask = 1) { 2611590Srgrimes if (ask) { 2621590Srgrimes fflag = 0; 2631590Srgrimes getloginname(); 2641590Srgrimes } 2651590Srgrimes rootlogin = 0; 26621528Sdavidn rootok = rootterm(tty); /* Default (auth may change) */ 26721528Sdavidn 2681590Srgrimes if (strlen(username) > UT_NAMESIZE) 2691590Srgrimes username[UT_NAMESIZE] = '\0'; 2701590Srgrimes 2711590Srgrimes /* 2721590Srgrimes * Note if trying multiple user names; log failures for 2731590Srgrimes * previous user name, but don't bother logging one failure 2741590Srgrimes * for nonexistent name (mistyped username). 2751590Srgrimes */ 2761590Srgrimes if (failures && strcmp(tbuf, username)) { 2771590Srgrimes if (failures > (pwd ? 0 : 1)) 2781590Srgrimes badlogin(tbuf); 2791590Srgrimes } 28027605Scharnier (void)strncpy(tbuf, username, sizeof tbuf-1); 28127605Scharnier tbuf[sizeof tbuf-1] = '\0'; 2821590Srgrimes 28341279Sjdp pwd = getpwnam(username); 28423985Sdavidn 28523985Sdavidn /* 2861590Srgrimes * if we have a valid account name, and it doesn't have a 2871590Srgrimes * password, or the -f option was specified and the caller 2881590Srgrimes * is root or the caller isn't changing their uid, don't 2891590Srgrimes * authenticate. 2901590Srgrimes */ 29121528Sdavidn if (pwd != NULL) { 2923205Spst if (pwd->pw_uid == 0) 2933205Spst rootlogin = 1; 2943205Spst 29523985Sdavidn if (fflag && (uid == (uid_t)0 || 29623985Sdavidn uid == (uid_t)pwd->pw_uid)) { 2973205Spst /* already authenticated */ 2983205Spst break; 2993205Spst } else if (pwd->pw_passwd[0] == '\0') { 30024321Sdavidn if (!rootlogin || rootok) { 30124251Sdavidn /* pretend password okay */ 30224251Sdavidn rval = 0; 30324251Sdavidn goto ttycheck; 30424251Sdavidn } 3053205Spst } 3063205Spst } 3073205Spst 3081590Srgrimes fflag = 0; 3091590Srgrimes 3101590Srgrimes (void)setpriority(PRIO_PROCESS, 0, -4); 3111590Srgrimes 31242850Sabial#ifndef NO_PAM 31341279Sjdp /* 31441279Sjdp * Try to authenticate using PAM. If a PAM system error 31541279Sjdp * occurs, perhaps because of a botched configuration, 31641279Sjdp * then fall back to using traditional Unix authentication. 31741279Sjdp */ 31841279Sjdp if ((rval = auth_pam()) == -1) 31942850Sabial#endif /* NO_PAM */ 32041279Sjdp rval = auth_traditional(); 32123985Sdavidn 32241279Sjdp (void)setpriority(PRIO_PROCESS, 0, 0); 32323985Sdavidn 32442850Sabial#ifndef NO_PAM 32541279Sjdp /* 32641279Sjdp * PAM authentication may have changed "pwd" to the 32741279Sjdp * entry for the template user. Check again to see if 32841279Sjdp * this is a root login after all. 32941279Sjdp */ 33041279Sjdp if (pwd != NULL && pwd->pw_uid == 0) 33141279Sjdp rootlogin = 1; 33242850Sabial#endif /* NO_PAM */ 3331590Srgrimes 3343205Spst ttycheck: 3351590Srgrimes /* 3361590Srgrimes * If trying to log in as root without Kerberos, 3371590Srgrimes * but with insecure terminal, refuse the login attempt. 3381590Srgrimes */ 33924222Sdavidn if (pwd && !rval) { 34024222Sdavidn if (rootlogin && !rootok) 34124222Sdavidn refused(NULL, "NOROOT", 0); 34224222Sdavidn else /* valid password & authenticated */ 34324222Sdavidn break; 3441590Srgrimes } 3451590Srgrimes 3461590Srgrimes (void)printf("Login incorrect\n"); 3471590Srgrimes failures++; 34823985Sdavidn 34923985Sdavidn /* 35023985Sdavidn * we allow up to 'retry' (10) tries, 35123985Sdavidn * but after 'backoff' (3) we start backing off 35223985Sdavidn */ 35323985Sdavidn if (++cnt > backoff) { 35423985Sdavidn if (cnt >= retries) { 3551590Srgrimes badlogin(username); 3561590Srgrimes sleepexit(1); 3571590Srgrimes } 35838374Sjkoshy sleep((u_int)((cnt - backoff) * 5)); 3591590Srgrimes } 3601590Srgrimes } 3611590Srgrimes 3621590Srgrimes /* committed to login -- turn off timeout */ 3631590Srgrimes (void)alarm((u_int)0); 3641590Srgrimes 3651590Srgrimes endpwent(); 3661590Srgrimes 36741279Sjdp /* 36841279Sjdp * Establish the login class. 36941279Sjdp */ 37041279Sjdp lc = login_getpwclass(pwd); 37141279Sjdp 3721590Srgrimes /* if user not super-user, check for disabled logins */ 3731590Srgrimes if (!rootlogin) 37421528Sdavidn auth_checknologin(lc); 3751590Srgrimes 37621528Sdavidn quietlog = login_getcapbool(lc, "hushlogin", 0); 37746007Sache /* Switching needed for NFS with root access disabled */ 37846007Sache (void)setegid(pwd->pw_gid); 37935557Speter (void)seteuid(rootlogin ? 0 : pwd->pw_uid); 38021528Sdavidn if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) { 38126021Spst if (login_getcapbool(lc, "requirehome", 0)) 38223985Sdavidn refused("Home directory not available", "HOMEDIR", 1); 38324485Sdavidn if (chdir("/") < 0) 38423985Sdavidn refused("Cannot find root directory", "ROOTDIR", 1); 38523985Sdavidn if (!quietlog || *pwd->pw_dir) 38623985Sdavidn printf("No home directory.\nLogging in with home = \"/\".\n"); 38757546Sache pwd->pw_dir = "/"; 3881590Srgrimes } 38935557Speter (void)seteuid(euid); 39046007Sache (void)setegid(egid); 39121528Sdavidn if (!quietlog) 39221528Sdavidn quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; 3931590Srgrimes 3941590Srgrimes if (pwd->pw_change || pwd->pw_expire) 3951590Srgrimes (void)gettimeofday(&tp, (struct timezone *)NULL); 3962532Sjkh 39730564Sjoerg#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ 39821528Sdavidn 39923985Sdavidn warntime = login_getcaptime(lc, "warnpassword", 40023985Sdavidn DEFAULT_WARN, DEFAULT_WARN); 40123985Sdavidn 4024878Sugen changepass=0; 40321528Sdavidn if (pwd->pw_change) { 4041590Srgrimes if (tp.tv_sec >= pwd->pw_change) { 4051590Srgrimes (void)printf("Sorry -- your password has expired.\n"); 4064878Sugen changepass=1; 40723985Sdavidn syslog(LOG_INFO, 40823985Sdavidn "%s Password expired - forcing change", 40923985Sdavidn pwd->pw_name); 41023985Sdavidn } else if (pwd->pw_change - tp.tv_sec < warntime && !quietlog) 41123985Sdavidn (void)printf("Warning: your password expires on %s", 41223985Sdavidn ctime(&pwd->pw_change)); 41323985Sdavidn } 41423985Sdavidn 41523985Sdavidn warntime = login_getcaptime(lc, "warnexpire", 41623985Sdavidn DEFAULT_WARN, DEFAULT_WARN); 41723985Sdavidn 41821528Sdavidn if (pwd->pw_expire) { 4191590Srgrimes if (tp.tv_sec >= pwd->pw_expire) { 42023985Sdavidn refused("Sorry -- your account has expired", 42123985Sdavidn "EXPIRED", 1); 42223985Sdavidn } else if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog) 42323985Sdavidn (void)printf("Warning: your account expires on %s", 42423985Sdavidn ctime(&pwd->pw_expire)); 42521528Sdavidn } 4261590Srgrimes 42721528Sdavidn if (lc != NULL) { 42821528Sdavidn if (hostname) { 42957339Sshin struct addrinfo hints, *res; 43057339Sshin int ga_err; 43121528Sdavidn 43257339Sshin memset(&hints, 0, sizeof(hints)); 43357339Sshin hints.ai_family = AF_UNSPEC; 43457339Sshin ga_err = getaddrinfo(full_hostname, NULL, &hints, 43557339Sshin &res); 43657339Sshin if (ga_err == 0) { 43757339Sshin char hostbuf[MAXHOSTNAMELEN]; 43857339Sshin 43957339Sshin getnameinfo(res->ai_addr, res->ai_addrlen, 44057339Sshin hostbuf, sizeof(hostbuf), NULL, 0, 44157339Sshin NI_NUMERICHOST|NI_WITHSCOPEID); 44257339Sshin optarg = strdup(hostbuf); 44357339Sshin } else 44421528Sdavidn optarg = NULL; 44557339Sshin if (res != NULL) 44657339Sshin freeaddrinfo(res); 44723985Sdavidn if (!auth_hostok(lc, full_hostname, optarg)) 44823985Sdavidn refused("Permission denied", "HOST", 1); 44921528Sdavidn } 45021528Sdavidn 45123985Sdavidn if (!auth_ttyok(lc, tty)) 45223985Sdavidn refused("Permission denied", "TTY", 1); 45321528Sdavidn 45423985Sdavidn if (!auth_timeok(lc, time(NULL))) 45523985Sdavidn refused("Logins not available right now", "TIME", 1); 45621528Sdavidn } 45723985Sdavidn shell=login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell); 45823985Sdavidn if (*pwd->pw_shell == '\0') 45923985Sdavidn pwd->pw_shell = _PATH_BSHELL; 46023985Sdavidn if (*shell == '\0') /* Not overridden */ 46123985Sdavidn shell = pwd->pw_shell; 46223985Sdavidn if ((shell = strdup(shell)) == NULL) { 46323985Sdavidn syslog(LOG_NOTICE, "memory allocation error"); 46423985Sdavidn sleepexit(1); 46523985Sdavidn } 46621528Sdavidn 46721528Sdavidn#ifdef LOGIN_ACCESS 46823985Sdavidn if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0) 46923985Sdavidn refused("Permission denied", "ACCESS", 1); 47021528Sdavidn#endif /* LOGIN_ACCESS */ 47121528Sdavidn 4721590Srgrimes /* Nothing else left to fail -- really log in. */ 4731590Srgrimes memset((void *)&utmp, 0, sizeof(utmp)); 4741590Srgrimes (void)time(&utmp.ut_time); 4751590Srgrimes (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); 4761590Srgrimes if (hostname) 4771590Srgrimes (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); 4781590Srgrimes (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); 4791590Srgrimes login(&utmp); 4801590Srgrimes 4811590Srgrimes dolastlog(quietlog); 4821590Srgrimes 4832224Sguido /* 4842224Sguido * Set device protections, depending on what terminal the 4852224Sguido * user is logged in. This feature is used on Suns to give 4862224Sguido * console users better privacy. 4872224Sguido */ 4882224Sguido login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); 4892224Sguido 49050124Simp /* 49150124Simp * Clear flags of the tty. None should be set, and when the 49250124Simp * user sets them otherwise, this can cause the chown to fail. 49350124Simp * Since it isn't clear that flags are useful on character 49450124Simp * devices, we just clear them. 49550124Simp */ 49650124Simp if (chflags(ttyn, 0)) 49750124Simp syslog(LOG_ERR, "chmod(%s): %m", ttyn); 49850124Simp if (chown(ttyn, pwd->pw_uid, 49950124Simp (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid)) 50050124Simp syslog(LOG_ERR, "chmod(%s): %m", ttyn); 5011590Srgrimes 50250124Simp 50323985Sdavidn /* 50423985Sdavidn * Preserve TERM if it happens to be already set. 50523985Sdavidn */ 50624222Sdavidn if ((term = getenv("TERM")) != NULL) 50724222Sdavidn term = strdup(term); 5081590Srgrimes 50923985Sdavidn /* 51023985Sdavidn * Exclude cons/vt/ptys only, assume dialup otherwise 51123985Sdavidn * TODO: Make dialup tty determination a library call 51223985Sdavidn * for consistency (finger etc.) 51323985Sdavidn */ 51424894Sdavidn if (hostname==NULL && isdialuptty(tty)) 5151590Srgrimes syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); 5161590Srgrimes 5173205Spst#ifdef LOGALL 5183205Spst /* 5193205Spst * Syslog each successful login, so we don't have to watch hundreds 5203205Spst * of wtmp or lastlogin files. 5213205Spst */ 52223985Sdavidn if (hostname) 52323985Sdavidn syslog(LOG_INFO, "login from %s on %s as %s", 52423985Sdavidn full_hostname, tty, pwd->pw_name); 52523985Sdavidn else 52623985Sdavidn syslog(LOG_INFO, "login on %s as %s", 52723985Sdavidn tty, pwd->pw_name); 52821528Sdavidn#endif 52921528Sdavidn 53023985Sdavidn /* 53123985Sdavidn * If fflag is on, assume caller/authenticator has logged root login. 53223985Sdavidn */ 53323985Sdavidn if (rootlogin && fflag == 0) 53423985Sdavidn { 53523985Sdavidn if (hostname) 53623985Sdavidn syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s", 53723985Sdavidn username, tty, full_hostname); 53823985Sdavidn else 53923985Sdavidn syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", 54023985Sdavidn username, tty); 54123985Sdavidn } 54223985Sdavidn 54323985Sdavidn /* 54423985Sdavidn * Destroy environment unless user has requested its preservation. 54523985Sdavidn * We need to do this before setusercontext() because that may 54623985Sdavidn * set or reset some environment variables. 54723985Sdavidn */ 54821528Sdavidn if (!pflag) 54921528Sdavidn environ = envinit; 55021528Sdavidn 55123985Sdavidn /* 55223985Sdavidn * We don't need to be root anymore, so 55321528Sdavidn * set the user and session context 55421528Sdavidn */ 55541279Sjdp if (setlogin(username) != 0) { 55641279Sjdp syslog(LOG_ERR, "setlogin(%s): %m - exiting", username); 55741279Sjdp exit(1); 55841279Sjdp } 55941279Sjdp if (setusercontext(lc, pwd, pwd->pw_uid, 56041279Sjdp LOGIN_SETALL & ~LOGIN_SETLOGIN) != 0) { 56121528Sdavidn syslog(LOG_ERR, "setusercontext() failed - exiting"); 56221528Sdavidn exit(1); 5633205Spst } 56421528Sdavidn 56523148Sache (void)setenv("SHELL", pwd->pw_shell, 1); 56621528Sdavidn (void)setenv("HOME", pwd->pw_dir, 1); 56723985Sdavidn if (term != NULL && *term != '\0') 56821528Sdavidn (void)setenv("TERM", term, 1); /* Preset overrides */ 56921528Sdavidn else { 57023985Sdavidn (void)setenv("TERM", stypeof(tty), 0); /* Fallback doesn't */ 57121528Sdavidn } 57241279Sjdp (void)setenv("LOGNAME", username, 1); 57341279Sjdp (void)setenv("USER", username, 1); 57421528Sdavidn (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0); 57521528Sdavidn 5761590Srgrimes if (!quietlog) { 57723985Sdavidn char *cw; 57823985Sdavidn 57923985Sdavidn cw = login_getcapstr(lc, "copyright", NULL, NULL); 58021528Sdavidn if (cw != NULL && access(cw, F_OK) == 0) 58121528Sdavidn motd(cw); 58221528Sdavidn else 58323985Sdavidn (void)printf("%s\n\t%s %s\n", 58423985Sdavidn "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994", 58523985Sdavidn "The Regents of the University of California. ", 58623985Sdavidn "All rights reserved."); 58723985Sdavidn 58823985Sdavidn (void)printf("\n"); 58923985Sdavidn 59021528Sdavidn cw = login_getcapstr(lc, "welcome", NULL, NULL); 59121528Sdavidn if (cw == NULL || access(cw, F_OK) != 0) 59221528Sdavidn cw = _PATH_MOTDFILE; 59321528Sdavidn motd(cw); 59423985Sdavidn 59521528Sdavidn cw = getenv("MAIL"); /* $MAIL may have been set by class */ 59621528Sdavidn if (cw != NULL) { 59721528Sdavidn strncpy(tbuf, cw, sizeof(tbuf)); 59821528Sdavidn tbuf[sizeof(tbuf)-1] = '\0'; 59921528Sdavidn } else 60023985Sdavidn snprintf(tbuf, sizeof(tbuf), "%s/%s", 60123985Sdavidn _PATH_MAILDIR, pwd->pw_name); 6021590Srgrimes if (stat(tbuf, &st) == 0 && st.st_size != 0) 60323985Sdavidn (void)printf("You have %smail.\n", 60423985Sdavidn (st.st_mtime > st.st_atime) ? "new " : ""); 6051590Srgrimes } 6061590Srgrimes 60721528Sdavidn login_close(lc); 6083205Spst 6091590Srgrimes (void)signal(SIGALRM, SIG_DFL); 6101590Srgrimes (void)signal(SIGQUIT, SIG_DFL); 6111590Srgrimes (void)signal(SIGINT, SIG_DFL); 6121590Srgrimes (void)signal(SIGTSTP, SIG_IGN); 6131590Srgrimes 6144878Sugen if (changepass) { 61521528Sdavidn if (system(_PATH_CHPASS) != 0) 6164878Sugen sleepexit(1); 6174878Sugen } 6184878Sugen 61923985Sdavidn /* 62023985Sdavidn * Login shells have a leading '-' in front of argv[0] 62123985Sdavidn */ 62223985Sdavidn tbuf[0] = '-'; 62323985Sdavidn (void)strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell); 62423985Sdavidn 62521528Sdavidn execlp(shell, tbuf, 0); 62621528Sdavidn err(1, "%s", shell); 6271590Srgrimes} 6281590Srgrimes 62941279Sjdpstatic int 63041279Sjdpauth_traditional() 63141279Sjdp{ 63241279Sjdp int rval; 63341279Sjdp char *p; 63441279Sjdp char *ep; 63541279Sjdp char *salt; 63641279Sjdp 63741279Sjdp rval = 1; 63841279Sjdp salt = pwd != NULL ? pwd->pw_passwd : "xx"; 63941279Sjdp 64041279Sjdp p = getpass("Password:"); 64141279Sjdp ep = crypt(p, salt); 64241279Sjdp 64341279Sjdp if (pwd) { 64441279Sjdp if (!p[0] && pwd->pw_passwd[0]) 64541279Sjdp ep = ":"; 64641279Sjdp if (strcmp(ep, pwd->pw_passwd) == 0) 64741279Sjdp rval = 0; 64841279Sjdp } 64941279Sjdp 65041279Sjdp /* clear entered password */ 65141279Sjdp memset(p, 0, strlen(p)); 65241279Sjdp return rval; 65341279Sjdp} 65441279Sjdp 65542850Sabial#ifndef NO_PAM 65641279Sjdp/* 65741279Sjdp * Attempt to authenticate the user using PAM. Returns 0 if the user is 65841279Sjdp * authenticated, or 1 if not authenticated. If some sort of PAM system 65941279Sjdp * error occurs (e.g., the "/etc/pam.conf" file is missing) then this 66041279Sjdp * function returns -1. This can be used as an indication that we should 66141279Sjdp * fall back to a different authentication mechanism. 66241279Sjdp */ 66341279Sjdpstatic int 66441279Sjdpauth_pam() 66541279Sjdp{ 66641279Sjdp pam_handle_t *pamh = NULL; 66741279Sjdp const char *tmpl_user; 66841279Sjdp const void *item; 66941279Sjdp int rval; 67041279Sjdp int e; 67141279Sjdp static struct pam_conv conv = { misc_conv, NULL }; 67241279Sjdp 67341279Sjdp if ((e = pam_start("login", username, &conv, &pamh)) != PAM_SUCCESS) { 67441279Sjdp syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e)); 67541279Sjdp return -1; 67641279Sjdp } 67741279Sjdp if ((e = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) { 67841279Sjdp syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s", 67941279Sjdp pam_strerror(pamh, e)); 68041279Sjdp return -1; 68141279Sjdp } 68241279Sjdp if (hostname != NULL && 68341279Sjdp (e = pam_set_item(pamh, PAM_RHOST, full_hostname)) != PAM_SUCCESS) { 68441279Sjdp syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", 68541279Sjdp pam_strerror(pamh, e)); 68641279Sjdp return -1; 68741279Sjdp } 68841279Sjdp e = pam_authenticate(pamh, 0); 68941279Sjdp switch (e) { 69041279Sjdp 69141279Sjdp case PAM_SUCCESS: 69241279Sjdp /* 69341279Sjdp * With PAM we support the concept of a "template" 69441279Sjdp * user. The user enters a login name which is 69541279Sjdp * authenticated by PAM, usually via a remote service 69641279Sjdp * such as RADIUS or TACACS+. If authentication 69741279Sjdp * succeeds, a different but related "template" name 69841279Sjdp * is used for setting the credentials, shell, and 69941279Sjdp * home directory. The name the user enters need only 70041279Sjdp * exist on the remote authentication server, but the 70141279Sjdp * template name must be present in the local password 70241279Sjdp * database. 70341279Sjdp * 70441279Sjdp * This is supported by two various mechanisms in the 70541279Sjdp * individual modules. However, from the application's 70641279Sjdp * point of view, the template user is always passed 70741279Sjdp * back as a changed value of the PAM_USER item. 70841279Sjdp */ 70941279Sjdp if ((e = pam_get_item(pamh, PAM_USER, &item)) == 71041279Sjdp PAM_SUCCESS) { 71141279Sjdp tmpl_user = (const char *) item; 71241279Sjdp if (strcmp(username, tmpl_user) != 0) 71341279Sjdp pwd = getpwnam(tmpl_user); 71441279Sjdp } else 71541279Sjdp syslog(LOG_ERR, "Couldn't get PAM_USER: %s", 71641279Sjdp pam_strerror(pamh, e)); 71741279Sjdp rval = 0; 71841279Sjdp break; 71941279Sjdp 72041279Sjdp case PAM_AUTH_ERR: 72141279Sjdp case PAM_USER_UNKNOWN: 72241279Sjdp case PAM_MAXTRIES: 72341279Sjdp rval = 1; 72441279Sjdp break; 72541279Sjdp 72641279Sjdp default: 72741279Sjdp syslog(LOG_ERR, "auth_pam: %s", pam_strerror(pamh, e)); 72841279Sjdp rval = -1; 72941279Sjdp break; 73041279Sjdp } 73141279Sjdp if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 73241279Sjdp syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 73341279Sjdp rval = -1; 73441279Sjdp } 73541279Sjdp return rval; 73641279Sjdp} 73742850Sabial#endif /* NO_PAM */ 73841279Sjdp 73927605Scharnierstatic void 74027605Scharnierusage() 74127605Scharnier{ 74227605Scharnier (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n"); 74327605Scharnier exit(1); 74427605Scharnier} 7451590Srgrimes 74623985Sdavidn/* 74723985Sdavidn * Allow for authentication style and/or kerberos instance 74823985Sdavidn * */ 74921528Sdavidn 75021528Sdavidn#define NBUFSIZ UT_NAMESIZE + 64 75121528Sdavidn 7521590Srgrimesvoid 7531590Srgrimesgetloginname() 7541590Srgrimes{ 7551590Srgrimes int ch; 7561590Srgrimes char *p; 7571590Srgrimes static char nbuf[NBUFSIZ]; 7581590Srgrimes 7591590Srgrimes for (;;) { 7601590Srgrimes (void)printf("login: "); 7611590Srgrimes for (p = nbuf; (ch = getchar()) != '\n'; ) { 7621590Srgrimes if (ch == EOF) { 7631590Srgrimes badlogin(username); 7641590Srgrimes exit(0); 7651590Srgrimes } 7661590Srgrimes if (p < nbuf + (NBUFSIZ - 1)) 7671590Srgrimes *p++ = ch; 7681590Srgrimes } 7691590Srgrimes if (p > nbuf) 7701590Srgrimes if (nbuf[0] == '-') 7711590Srgrimes (void)fprintf(stderr, 7721590Srgrimes "login names may not start with '-'.\n"); 7731590Srgrimes else { 7741590Srgrimes *p = '\0'; 7751590Srgrimes username = nbuf; 7761590Srgrimes break; 7771590Srgrimes } 7781590Srgrimes } 7791590Srgrimes} 7801590Srgrimes 7811590Srgrimesint 7821590Srgrimesrootterm(ttyn) 7831590Srgrimes char *ttyn; 7841590Srgrimes{ 7851590Srgrimes struct ttyent *t; 78623985Sdavidn 7871590Srgrimes return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE); 7881590Srgrimes} 7891590Srgrimes 79021528Sdavidnvolatile int motdinterrupt; 7911590Srgrimes 79221528Sdavidn/* ARGSUSED */ 7931590Srgrimesvoid 79421528Sdavidnsigint(signo) 79521528Sdavidn int signo; 7961590Srgrimes{ 79721528Sdavidn motdinterrupt = 1; 79821528Sdavidn} 79921528Sdavidn 80021528Sdavidnvoid 80121528Sdavidnmotd(motdfile) 80221528Sdavidn char *motdfile; 80321528Sdavidn{ 8041590Srgrimes int fd, nchars; 8051590Srgrimes sig_t oldint; 80621528Sdavidn char tbuf[256]; 8071590Srgrimes 80821528Sdavidn if ((fd = open(motdfile, O_RDONLY, 0)) < 0) 8091590Srgrimes return; 81021528Sdavidn motdinterrupt = 0; 8111590Srgrimes oldint = signal(SIGINT, sigint); 81221528Sdavidn while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 && !motdinterrupt) 81321528Sdavidn (void)write(fileno(stdout), tbuf, nchars); 8141590Srgrimes (void)signal(SIGINT, oldint); 8151590Srgrimes (void)close(fd); 8161590Srgrimes} 8171590Srgrimes 8181590Srgrimes/* ARGSUSED */ 8191590Srgrimesvoid 8201590Srgrimestimedout(signo) 8211590Srgrimes int signo; 8221590Srgrimes{ 82342272Seivind longjmp(timeout_buf, signo); 8241590Srgrimes} 8251590Srgrimes 8261590Srgrimes 8271590Srgrimesvoid 8281590Srgrimesdolastlog(quiet) 8291590Srgrimes int quiet; 8301590Srgrimes{ 8311590Srgrimes struct lastlog ll; 8321590Srgrimes int fd; 8331590Srgrimes 8341590Srgrimes if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { 8351590Srgrimes (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 8361590Srgrimes if (!quiet) { 8371590Srgrimes if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && 8381590Srgrimes ll.ll_time != 0) { 8391590Srgrimes (void)printf("Last login: %.*s ", 8401590Srgrimes 24-5, (char *)ctime(&ll.ll_time)); 8411590Srgrimes if (*ll.ll_host != '\0') 8421590Srgrimes (void)printf("from %.*s\n", 8431590Srgrimes (int)sizeof(ll.ll_host), 8441590Srgrimes ll.ll_host); 8451590Srgrimes else 8461590Srgrimes (void)printf("on %.*s\n", 8471590Srgrimes (int)sizeof(ll.ll_line), 8481590Srgrimes ll.ll_line); 8491590Srgrimes } 8501590Srgrimes (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 8511590Srgrimes } 8521590Srgrimes memset((void *)&ll, 0, sizeof(ll)); 8531590Srgrimes (void)time(&ll.ll_time); 8541590Srgrimes (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); 8551590Srgrimes if (hostname) 8561590Srgrimes (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); 8571590Srgrimes (void)write(fd, (char *)&ll, sizeof(ll)); 8581590Srgrimes (void)close(fd); 8591590Srgrimes } 8601590Srgrimes} 8611590Srgrimes 8621590Srgrimesvoid 8631590Srgrimesbadlogin(name) 8641590Srgrimes char *name; 8651590Srgrimes{ 8661590Srgrimes 8671590Srgrimes if (failures == 0) 8681590Srgrimes return; 8691590Srgrimes if (hostname) { 8701590Srgrimes syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s", 87116423Sache failures, failures > 1 ? "S" : "", full_hostname); 8721590Srgrimes syslog(LOG_AUTHPRIV|LOG_NOTICE, 8731590Srgrimes "%d LOGIN FAILURE%s FROM %s, %s", 87416423Sache failures, failures > 1 ? "S" : "", full_hostname, name); 8751590Srgrimes } else { 8761590Srgrimes syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s", 8771590Srgrimes failures, failures > 1 ? "S" : "", tty); 8781590Srgrimes syslog(LOG_AUTHPRIV|LOG_NOTICE, 8791590Srgrimes "%d LOGIN FAILURE%s ON %s, %s", 8801590Srgrimes failures, failures > 1 ? "S" : "", tty, name); 8811590Srgrimes } 88242272Seivind failures = 0; 8831590Srgrimes} 8841590Srgrimes 8851590Srgrimes#undef UNKNOWN 8861590Srgrimes#define UNKNOWN "su" 8871590Srgrimes 8881590Srgrimeschar * 8891590Srgrimesstypeof(ttyid) 8901590Srgrimes char *ttyid; 8911590Srgrimes{ 89223985Sdavidn 8931590Srgrimes struct ttyent *t; 89423985Sdavidn 8951590Srgrimes return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN); 8961590Srgrimes} 8971590Srgrimes 8981590Srgrimesvoid 89923985Sdavidnrefused(msg, rtype, lout) 90023985Sdavidn char *msg; 90123985Sdavidn char *rtype; 90223985Sdavidn int lout; 90323985Sdavidn{ 90423985Sdavidn 90523985Sdavidn if (msg != NULL) 90623985Sdavidn printf("%s.\n", msg); 90723985Sdavidn if (hostname) 90823985Sdavidn syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s", 90923985Sdavidn pwd->pw_name, rtype, full_hostname, tty); 91023985Sdavidn else 91123985Sdavidn syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s", 91223985Sdavidn pwd->pw_name, rtype, tty); 91323985Sdavidn if (lout) 91423985Sdavidn sleepexit(1); 91523985Sdavidn} 91623985Sdavidn 91723985Sdavidnvoid 9181590Srgrimessleepexit(eval) 9191590Srgrimes int eval; 9201590Srgrimes{ 92123985Sdavidn 9221590Srgrimes (void)sleep(5); 9231590Srgrimes exit(eval); 9241590Srgrimes} 925