login.c revision 23246
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 411590Srgrimesstatic char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94"; 421590Srgrimes#endif /* not lint */ 431590Srgrimes 441590Srgrimes/* 451590Srgrimes * login [ name ] 461590Srgrimes * login -h hostname (for telnetd, etc.) 471590Srgrimes * login -f name (for pre-authenticated login: datakit, xterm, etc.) 481590Srgrimes */ 491590Srgrimes 5023246Swosch#include <sys/copyright.h> 511590Srgrimes#include <sys/param.h> 521590Srgrimes#include <sys/stat.h> 531590Srgrimes#include <sys/time.h> 541590Srgrimes#include <sys/resource.h> 551590Srgrimes#include <sys/file.h> 5616423Sache#include <netinet/in.h> 5716423Sache#include <arpa/inet.h> 581590Srgrimes 591590Srgrimes#include <err.h> 601590Srgrimes#include <errno.h> 611590Srgrimes#include <grp.h> 6216423Sache#include <netdb.h> 631590Srgrimes#include <pwd.h> 641590Srgrimes#include <setjmp.h> 651590Srgrimes#include <signal.h> 661590Srgrimes#include <stdio.h> 671590Srgrimes#include <stdlib.h> 681590Srgrimes#include <string.h> 691590Srgrimes#include <syslog.h> 701590Srgrimes#include <ttyent.h> 711590Srgrimes#include <unistd.h> 721590Srgrimes#include <utmp.h> 731590Srgrimes 7421528Sdavidn#ifdef LOGIN_CAP 7521528Sdavidn#include <login_cap.h> 7621528Sdavidn#else /* Undef AUTH as well */ 7721528Sdavidn#undef LOGIN_CAP_AUTH 7821528Sdavidn#endif 7921528Sdavidn 8021528Sdavidn/* If LOGIN_CAP_AUTH is activated: 8121528Sdavidn * kerberose & skey logins are runtime selected via login 8221528Sdavidn * login_getstyle() and authentication types for login classes 8321528Sdavidn * The actual login itself is handled via /usr/libexec/login_<style> 8421528Sdavidn * Valid styles are determined by the auth-type=style,style entries 8521528Sdavidn * in the login class. 8621528Sdavidn */ 8721528Sdavidn#ifdef LOGIN_CAP_AUTH 8821528Sdavidn#undef KERBEROS 8921528Sdavidn#undef SKEY 9021528Sdavidn#else 913702Spst#ifdef SKEY 923702Spst#include <skey.h> 9321528Sdavidn#endif /* SKEY */ 9421528Sdavidn#endif /* LOGIN_CAP_AUTH */ 953702Spst 961590Srgrimes#include "pathnames.h" 971590Srgrimes 981590Srgrimesvoid badlogin __P((char *)); 991590Srgrimesvoid checknologin __P((void)); 1001590Srgrimesvoid dolastlog __P((int)); 1011590Srgrimesvoid getloginname __P((void)); 10221528Sdavidnvoid motd __P((char *)); 1031590Srgrimesint rootterm __P((char *)); 1041590Srgrimesvoid sigint __P((int)); 1051590Srgrimesvoid sleepexit __P((int)); 1061590Srgrimeschar *stypeof __P((char *)); 1071590Srgrimesvoid timedout __P((int)); 1082224Sguidovoid login_fbtab __P((char *, uid_t, gid_t)); 1091590Srgrimes#ifdef KERBEROS 1101590Srgrimesint klogin __P((struct passwd *, char *, char *, char *)); 1111590Srgrimes#endif 1121590Srgrimes 1131590Srgrimesextern void login __P((struct utmp *)); 1141590Srgrimes 1151590Srgrimes#define TTYGRPNAME "tty" /* name of group to own ttys */ 1161590Srgrimes 1171590Srgrimes/* 1181590Srgrimes * This bounds the time given to login. Not a define so it can 1191590Srgrimes * be patched on machines where it's too small. 1201590Srgrimes */ 1211590Srgrimesu_int timeout = 300; 1221590Srgrimes 1231590Srgrimes#ifdef KERBEROS 1241590Srgrimesint notickets = 1; 1255627Swollmanint noticketsdontcomplain = 1; 1261590Srgrimeschar *instance; 1271590Srgrimeschar *krbtkfile_env; 1281590Srgrimesint authok; 1291590Srgrimes#endif 1301590Srgrimes 1311590Srgrimesstruct passwd *pwd; 1321590Srgrimesint failures; 1331590Srgrimeschar term[64], *envinit[1], *hostname, *username, *tty; 13416423Sachechar full_hostname[MAXHOSTNAMELEN]; 1351590Srgrimes 1361590Srgrimesint 1371590Srgrimesmain(argc, argv) 1381590Srgrimes int argc; 1391590Srgrimes char *argv[]; 1401590Srgrimes{ 1411590Srgrimes extern char **environ; 1421590Srgrimes struct group *gr; 1431590Srgrimes struct stat st; 1441590Srgrimes struct timeval tp; 1451590Srgrimes struct utmp utmp; 14621528Sdavidn int rootok; 1471590Srgrimes int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval; 1484878Sugen int changepass; 1491590Srgrimes uid_t uid; 1503205Spst char *domain, *p, *ep, *salt, *ttyn; 1511590Srgrimes char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; 1521590Srgrimes char localhost[MAXHOSTNAMELEN]; 15321528Sdavidn char shell[MAXPATHLEN]; 15421528Sdavidn#ifdef LOGIN_CAP 15521528Sdavidn login_cap_t *lc = NULL; 15621528Sdavidn#ifdef LOGIN_CAP_AUTH 15721528Sdavidn char *style, *authtype; 15821528Sdavidn char *auth_method = NULL; 15921528Sdavidn char *instance = NULL; 16021528Sdavidn int authok; 16121528Sdavidn#else /* !LOGIN_CAP_AUTH */ 16221528Sdavidn#ifdef SKEY 1633205Spst int permit_passwd = 0; 16421528Sdavidn#endif /* SKEY */ 16521528Sdavidn#endif /* LOGIN_CAP_AUTH */ 16621528Sdavidn#endif /* LOGIN_CAP */ 1671590Srgrimes 1681590Srgrimes (void)signal(SIGALRM, timedout); 1691590Srgrimes (void)alarm(timeout); 1701590Srgrimes (void)signal(SIGQUIT, SIG_IGN); 1711590Srgrimes (void)signal(SIGINT, SIG_IGN); 1721590Srgrimes (void)setpriority(PRIO_PROCESS, 0, 0); 1731590Srgrimes 1741590Srgrimes openlog("login", LOG_ODELAY, LOG_AUTH); 1751590Srgrimes 1761590Srgrimes /* 1771590Srgrimes * -p is used by getty to tell login not to destroy the environment 1781590Srgrimes * -f is used to skip a second login authentication 1791590Srgrimes * -h is used by other servers to pass the name of the remote 1801590Srgrimes * host to login so that it may be placed in utmp and wtmp 1811590Srgrimes */ 1823205Spst *full_hostname = '\0'; 1831590Srgrimes domain = NULL; 1841590Srgrimes if (gethostname(localhost, sizeof(localhost)) < 0) 1851590Srgrimes syslog(LOG_ERR, "couldn't get local hostname: %m"); 1861590Srgrimes else 1871590Srgrimes domain = strchr(localhost, '.'); 1881590Srgrimes 1891590Srgrimes fflag = hflag = pflag = 0; 1901590Srgrimes uid = getuid(); 1911590Srgrimes while ((ch = getopt(argc, argv, "fh:p")) != EOF) 1921590Srgrimes switch (ch) { 1931590Srgrimes case 'f': 1941590Srgrimes fflag = 1; 1951590Srgrimes break; 1961590Srgrimes case 'h': 1971590Srgrimes if (uid) 1981590Srgrimes errx(1, "-h option: %s", strerror(EPERM)); 1991590Srgrimes hflag = 1; 2003205Spst strncpy(full_hostname, optarg, sizeof(full_hostname)-1); 2011590Srgrimes if (domain && (p = strchr(optarg, '.')) && 2021590Srgrimes strcasecmp(p, domain) == 0) 2031590Srgrimes *p = 0; 20416423Sache if (strlen(optarg) > UT_HOSTSIZE) { 20516423Sache struct hostent *hp = gethostbyname(optarg); 20616423Sache 20716423Sache if (hp != NULL) { 20816423Sache struct in_addr in; 20916423Sache 21016423Sache memmove(&in, hp->h_addr, sizeof(in)); 21116423Sache optarg = strdup(inet_ntoa(in)); 21216423Sache } else 21316423Sache optarg = "invalid hostname"; 21416423Sache } 2151590Srgrimes hostname = optarg; 2161590Srgrimes break; 2171590Srgrimes case 'p': 2181590Srgrimes pflag = 1; 2191590Srgrimes break; 2201590Srgrimes case '?': 2211590Srgrimes default: 2221590Srgrimes if (!uid) 2231590Srgrimes syslog(LOG_ERR, "invalid flag %c", ch); 2241590Srgrimes (void)fprintf(stderr, 2251590Srgrimes "usage: login [-fp] [-h hostname] [username]\n"); 2261590Srgrimes exit(1); 2271590Srgrimes } 2281590Srgrimes argc -= optind; 2291590Srgrimes argv += optind; 2301590Srgrimes 2311590Srgrimes if (*argv) { 2321590Srgrimes username = *argv; 2331590Srgrimes ask = 0; 2341590Srgrimes } else 2351590Srgrimes ask = 1; 2361590Srgrimes 2371590Srgrimes for (cnt = getdtablesize(); cnt > 2; cnt--) 2381590Srgrimes (void)close(cnt); 2391590Srgrimes 2401590Srgrimes ttyn = ttyname(STDIN_FILENO); 2411590Srgrimes if (ttyn == NULL || *ttyn == '\0') { 2421590Srgrimes (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); 2431590Srgrimes ttyn = tname; 2441590Srgrimes } 2451590Srgrimes if (tty = strrchr(ttyn, '/')) 2461590Srgrimes ++tty; 2471590Srgrimes else 2481590Srgrimes tty = ttyn; 2491590Srgrimes 25021528Sdavidn#ifdef LOGIN_CAP_AUTH 25121528Sdavidn authtype = hostname ? "rlogin" : "login"; 25221528Sdavidn#endif 25321528Sdavidn 2541590Srgrimes for (cnt = 0;; ask = 1) { 2551590Srgrimes if (ask) { 2561590Srgrimes fflag = 0; 2571590Srgrimes getloginname(); 2581590Srgrimes } 2591590Srgrimes rootlogin = 0; 26021528Sdavidn rootok = rootterm(tty); /* Default (auth may change) */ 26121528Sdavidn#ifdef LOGIN_CAP_AUTH 26221528Sdavidn authok = 0; 26321528Sdavidn if (auth_method = strchr(username, ':')) { 26421528Sdavidn *auth_method = '\0'; 26521528Sdavidn auth_method++; 26621528Sdavidn if (*auth_method == '\0') 26721528Sdavidn auth_method = NULL; 26821528Sdavidn } 26921528Sdavidn /* 27021528Sdavidn * We need to do this regardless of whether 27121528Sdavidn * kerberos is available. 27221528Sdavidn */ 2731590Srgrimes if ((instance = strchr(username, '.')) != NULL) { 2741590Srgrimes if (strncmp(instance, ".root", 5) == 0) 2751590Srgrimes rootlogin = 1; 2761590Srgrimes *instance++ = '\0'; 2771590Srgrimes } else 2781590Srgrimes instance = ""; 27921528Sdavidn#else /* !LOGIN_CAP_AUTH */ 28021528Sdavidn#ifdef KERBEROS 28121528Sdavidn if ((instance = strchr(username, '.')) != NULL) { 28221528Sdavidn if (strncmp(instance, ".root", 5) == 0) 28321528Sdavidn rootlogin = 1; 28421528Sdavidn *instance++ = '\0'; 28521528Sdavidn } else 28621528Sdavidn instance = ""; 28721528Sdavidn#endif /* KERBEROS */ 28821528Sdavidn#endif /* LOGIN_CAP_AUTH */ 28921528Sdavidn 2901590Srgrimes if (strlen(username) > UT_NAMESIZE) 2911590Srgrimes username[UT_NAMESIZE] = '\0'; 2921590Srgrimes 2931590Srgrimes /* 2941590Srgrimes * Note if trying multiple user names; log failures for 2951590Srgrimes * previous user name, but don't bother logging one failure 2961590Srgrimes * for nonexistent name (mistyped username). 2971590Srgrimes */ 2981590Srgrimes if (failures && strcmp(tbuf, username)) { 2991590Srgrimes if (failures > (pwd ? 0 : 1)) 3001590Srgrimes badlogin(tbuf); 3011590Srgrimes failures = 0; 3021590Srgrimes } 3031590Srgrimes (void)strcpy(tbuf, username); 3041590Srgrimes 30521528Sdavidn pwd = getpwnam(username); 30621528Sdavidn#ifdef LOGIN_CAP 30721528Sdavidn /* Establish the class now, before we might goto 30821528Sdavidn * within the next block. pwd can be NULL since it 30921528Sdavidn * falls back to the "default" class if it is. 31021528Sdavidn */ 31121528Sdavidn lc = login_getclass(pwd); 31221528Sdavidn#endif /* LOGIN_CAP */ 3131590Srgrimes 3141590Srgrimes /* 3151590Srgrimes * if we have a valid account name, and it doesn't have a 3161590Srgrimes * password, or the -f option was specified and the caller 3171590Srgrimes * is root or the caller isn't changing their uid, don't 3181590Srgrimes * authenticate. 3191590Srgrimes */ 32021528Sdavidn if (pwd != NULL) { 32121528Sdavidn salt = pwd->pw_passwd; 3223205Spst if (pwd->pw_uid == 0) 3233205Spst rootlogin = 1; 3243205Spst 3253205Spst if (fflag && (uid == 0 || uid == pwd->pw_uid)) { 3263205Spst /* already authenticated */ 3273205Spst break; 3283205Spst } else if (pwd->pw_passwd[0] == '\0') { 3293205Spst /* pretend password okay */ 3303205Spst rval = 0; 3313205Spst goto ttycheck; 3323205Spst } 3333205Spst } 33421528Sdavidn else 33521528Sdavidn salt = "xx"; 3363205Spst 3371590Srgrimes fflag = 0; 3381590Srgrimes 3391590Srgrimes (void)setpriority(PRIO_PROCESS, 0, -4); 3401590Srgrimes 34121528Sdavidn#ifdef LOGIN_CAP_AUTH 34221528Sdavidn /* 34321528Sdavidn * This hands off authorisation to an authorisation program, 34421528Sdavidn * depending on the styles available for the "auth-login", 34521528Sdavidn * auth-rlogin (or default) authorisation styles. 34621528Sdavidn * We do this regardless of whether an account exists so that 34721528Sdavidn * the remote user cannot tell a "real" from an invented 34821528Sdavidn * account name. If we don't have an account we just fall 34921528Sdavidn * back to the first method for the "default" class. 35021528Sdavidn */ 35121528Sdavidn if ((style = login_getstyle(lc, auth_method, authtype)) == NULL) { 35221528Sdavidn rval = 1; /* No available authorisation method */ 35321528Sdavidn (void)printf("No auth method available for %s.\n", authtype); 35421528Sdavidn } else { 35521528Sdavidn /* Put back the kerberos instance, if any was given. 35621528Sdavidn * Don't worry about the non-kerberos case here, since 35721528Sdavidn * if kerberos is not available or not selected and an 35821528Sdavidn * instance is given at the login prompt, su or rlogin -l, 35921528Sdavidn * then anything else should fail as well. 36021528Sdavidn */ 36121528Sdavidn if (*instance) 36221528Sdavidn *(instance - 1) = '.'; 36321528Sdavidn rval = authenticate(username, lc ? lc->lc_class : "default", style, authtype); 36421528Sdavidn /* Junk it again */ 36521528Sdavidn if (*instance) 36621528Sdavidn *(instance - 1) = '\0'; 36721528Sdavidn } 36821528Sdavidn 36921528Sdavidn if (!rval) { 37021528Sdavidn /* 37121528Sdavidn * If authentication succeeds, run any approval 37221528Sdavidn * program, if applicable for this class. 37321528Sdavidn */ 37421528Sdavidn char *approvep = login_getcapstr(lc, "approve", NULL, NULL); 37521528Sdavidn rval = 1; /* Assume bad login again */ 37621528Sdavidn if (approvep==NULL || auth_script(approvep, approvep, username, lc->lc_class, 0) == 0) { 37721528Sdavidn int r = auth_scan(AUTH_OKAY); 37821528Sdavidn /* See what the authorise program says */ 37921528Sdavidn if (r != AUTH_NONE) { 38021528Sdavidn rval = 0; 38121528Sdavidn if (!rootok && (r & AUTH_ROOTOKAY)) 38221528Sdavidn rootok = 1; /* root approved */ 38321528Sdavidn else rootlogin = 0; 38421528Sdavidn if (!authok && (r & AUTH_SECURE)) 38521528Sdavidn authok = 1; /* secure */ 38621528Sdavidn } 38721528Sdavidn } 38821528Sdavidn } 38921528Sdavidn#else /* !LOGIN_CAP_AUTH */ 39021528Sdavidn#ifdef SKEY 39121528Sdavidn permit_passwd = skeyaccess(username, tty, hostname ? full_hostname : NULL, NULL); 3923205Spst p = skey_getpass("Password:", pwd, permit_passwd); 3933205Spst ep = skey_crypt(p, salt, pwd, permit_passwd); 39421528Sdavidn#else /* !SKEY */ 3951590Srgrimes p = getpass("Password:"); 3963205Spst ep = crypt(p, salt); 39721528Sdavidn#endif/* SKEY */ 3981590Srgrimes#ifdef KERBEROS 3997800Swollman#ifdef SKEY 40021528Sdavidn if (pwd) { 40121528Sdavidn /* Do not allow user to type in kerberos password 4027800Swollman * over the net (actually, this is ok for encrypted 4037800Swollman * links, but we have no way of determining if the 4047800Swollman * link is encrypted. 4057800Swollman */ 4067893Srgrimes if (!permit_passwd) { 40721528Sdavidn rval = 1; /* force failure */ 4087800Swollman } else 40921528Sdavidn#endif /* SKEY */ 4101590Srgrimes rval = klogin(pwd, instance, localhost, p); 4111590Srgrimes if (rval != 0 && rootlogin && pwd->pw_uid != 0) 4121590Srgrimes rootlogin = 0; 4131590Srgrimes if (rval == 0) 41421528Sdavidn authok = 1; /* kerberos authenticated ok */ 41521528Sdavidn else if (rval == 1) /* fallback to unix passwd */ 4163205Spst rval = strcmp(ep, pwd->pw_passwd); 41721528Sdavidn#ifdef SKEY 4181590Srgrimes } 41921528Sdavidn#endif /* SKEY */ 42021528Sdavidn#else /* !KERBEROS */ 42121950Sjkh if (pwd) 42221950Sjkh rval = strcmp(ep, pwd->pw_passwd); 42321528Sdavidn#endif /* KERBEROS */ 42421528Sdavidn /* clear entered password */ 4251590Srgrimes memset(p, 0, strlen(p)); 42621528Sdavidn#endif /* LOGIN_CAP_AUTH */ 4271590Srgrimes 4281590Srgrimes (void)setpriority(PRIO_PROCESS, 0, 0); 42921528Sdavidn#ifdef LOGIN_CAP 43021528Sdavidn if (rval) 43121528Sdavidn auth_rmfiles(); 43221528Sdavidn#endif 4333205Spst ttycheck: 4341590Srgrimes /* 4351590Srgrimes * If trying to log in as root without Kerberos, 4361590Srgrimes * but with insecure terminal, refuse the login attempt. 4371590Srgrimes */ 43821528Sdavidn#if defined(KERBEROS) || defined(LOGIN_CAP_AUTH) 4391590Srgrimes if (authok == 0) 4401590Srgrimes#endif 44121528Sdavidn if (pwd && !rval && rootlogin && !rootok) { 44221528Sdavidn (void)fprintf(stderr, "%s login refused on this terminal.\n", pwd->pw_name); 4431590Srgrimes if (hostname) 44421528Sdavidn syslog(LOG_NOTICE, "LOGIN %s REFUSED FROM %s ON TTY %s", pwd->pw_name, full_hostname, tty); 4451590Srgrimes else 44621528Sdavidn syslog(LOG_NOTICE, "LOGIN %s REFUSED ON TTY %s", pwd->pw_name, tty); 4471590Srgrimes continue; 4481590Srgrimes } 4491590Srgrimes 45021528Sdavidn if (pwd && !rval) /* valid password & authenticated */ 4511590Srgrimes break; 4521590Srgrimes 4531590Srgrimes (void)printf("Login incorrect\n"); 4541590Srgrimes failures++; 4551590Srgrimes /* we allow 10 tries, but after 3 we start backing off */ 4561590Srgrimes if (++cnt > 3) { 4571590Srgrimes if (cnt >= 10) { 4581590Srgrimes badlogin(username); 4591590Srgrimes sleepexit(1); 4601590Srgrimes } 4611590Srgrimes sleep((u_int)((cnt - 3) * 5)); 4621590Srgrimes } 4631590Srgrimes } 4641590Srgrimes 4651590Srgrimes /* committed to login -- turn off timeout */ 4661590Srgrimes (void)alarm((u_int)0); 4671590Srgrimes 4681590Srgrimes endpwent(); 4691590Srgrimes 4701590Srgrimes /* if user not super-user, check for disabled logins */ 47121528Sdavidn#ifdef LOGIN_CAP 4721590Srgrimes if (!rootlogin) 47321528Sdavidn auth_checknologin(lc); 47421528Sdavidn#else 47521528Sdavidn if (!rootlogin) 4761590Srgrimes checknologin(); 47721528Sdavidn#endif 4781590Srgrimes 47921528Sdavidn#ifdef LOGIN_CAP 48021528Sdavidn quietlog = login_getcapbool(lc, "hushlogin", 0); 48121528Sdavidn#else 48221528Sdavidn quietlog = 0; 48321528Sdavidn#endif 48421528Sdavidn if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) { 48521528Sdavidn#ifdef LOGIN_CAP 48621528Sdavidn if (login_getcapbool(lc, "requirehome", !rootlogin) || chdir("/") < 0) { 48721528Sdavidn (void)printf("No home directory %s!\n", pwd->pw_dir); 48821528Sdavidn sleepexit(1); 48921528Sdavidn } 49021528Sdavidn#else 49121528Sdavidn if (chdir("/") < 0) { 49221528Sdavidn (void)printf("No home directory %s!\n", pwd->pw_dir); 49321528Sdavidn sleepexit(1); 49421528Sdavidn } 49521528Sdavidn#endif 4961590Srgrimes pwd->pw_dir = "/"; 49721528Sdavidn if (!quietlog) 49821528Sdavidn (void)printf("No home directory.\nLogging in with home = \"/\".\n"); 4991590Srgrimes } 50021528Sdavidn if (!quietlog) 50121528Sdavidn quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; 5021590Srgrimes 5031590Srgrimes if (pwd->pw_change || pwd->pw_expire) 5041590Srgrimes (void)gettimeofday(&tp, (struct timezone *)NULL); 5052532Sjkh 50621528Sdavidn#define DEFAULT_WARN (2L * 7L & 86400L) /* Two weeks */ 50721528Sdavidn 5084878Sugen changepass=0; 50921528Sdavidn if (pwd->pw_change) { 5101590Srgrimes if (tp.tv_sec >= pwd->pw_change) { 5111590Srgrimes (void)printf("Sorry -- your password has expired.\n"); 51221528Sdavidn syslog(LOG_NOTICE, "%s Password expired - forcing change", pwd->pw_name); 5134878Sugen changepass=1; 51421528Sdavidn#ifdef LOGIN_CAP 51521528Sdavidn } else { 51621528Sdavidn time_t warntime = (time_t)login_getcaptime(lc, "warnpassword", DEFAULT_WARN, DEFAULT_WARN); 51721528Sdavidn if (pwd->pw_change - tp.tv_sec < warntime && !quietlog) 51821528Sdavidn (void)printf("Warning: your password expires on %s", ctime(&pwd->pw_change)); 51921528Sdavidn } 52021528Sdavidn#else 52121528Sdavidn } else if (pwd->pw_change - tp.tv_sec < DEFAULT_WARN && !quietlog) { 52221528Sdavidn (void)printf("Warning: your password expires on %s", ctime(&pwd->pw_change)); 52321528Sdavidn } 52421528Sdavidn#endif 52521528Sdavidn } 52621528Sdavidn if (pwd->pw_expire) { 5271590Srgrimes if (tp.tv_sec >= pwd->pw_expire) { 5281590Srgrimes (void)printf("Sorry -- your account has expired.\n"); 52921528Sdavidn syslog(LOG_NOTICE, "%s Account expired - login refused", pwd->pw_name); 5301590Srgrimes sleepexit(1); 53121528Sdavidn#ifdef LOGIN_CAP 53221528Sdavidn } else { 53321528Sdavidn time_t warntime = (time_t)login_getcaptime(lc, "warnexpire", DEFAULT_WARN, DEFAULT_WARN); 53421528Sdavidn if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog) 53521528Sdavidn (void)printf("Warning: your account expires on %s", 53621528Sdavidn ctime(&pwd->pw_expire)); 53721528Sdavidn } 53821528Sdavidn#else 53921528Sdavidn } else if (pwd->pw_expire - tp.tv_sec < DEFAULT_WARN && !quietlog) { 5401590Srgrimes (void)printf("Warning: your account expires on %s", 54121528Sdavidn ctime(&pwd->pw_expire)); 54221528Sdavidn } 54321528Sdavidn#endif 54421528Sdavidn } 5451590Srgrimes 54621528Sdavidn#ifdef LOGIN_CAP 54721528Sdavidn if (lc != NULL) { 54821528Sdavidn char *msg = NULL; 54921528Sdavidn 55021528Sdavidn if (hostname) { 55121528Sdavidn struct hostent *hp = gethostbyname(full_hostname); 55221528Sdavidn 55321528Sdavidn if (hp == NULL) 55421528Sdavidn optarg = NULL; 55521528Sdavidn else { 55621528Sdavidn struct in_addr in; 55721528Sdavidn memmove(&in, hp->h_addr, sizeof(in)); 55821528Sdavidn optarg = strdup(inet_ntoa(in)); 55921528Sdavidn } 56021528Sdavidn if (!auth_hostok(lc, full_hostname, optarg)) { 56121528Sdavidn syslog(LOG_NOTICE, "%s LOGIN REFUSED (HOST) FROM %s", pwd->pw_name, full_hostname); 56221528Sdavidn msg = "Permission denied"; 56321528Sdavidn } 56421528Sdavidn } 56521528Sdavidn 56621528Sdavidn if (msg == NULL && !auth_ttyok(lc, tty)) { 56721528Sdavidn syslog(LOG_NOTICE, "%s LOGIN REFUSED (TTY) ON %s", pwd->pw_name, tty); 56821528Sdavidn msg = "Permission denied"; 56921528Sdavidn } 57021528Sdavidn 57121528Sdavidn if (msg == NULL && !auth_timeok(lc, time(NULL))) { 57221528Sdavidn syslog(LOG_NOTICE, "%s LOGIN REFUSED (TIME) %s %s", pwd->pw_name, hostname?"FROM":"ON", hostname?full_hostname:tty); 57321528Sdavidn msg = "Logins not available right now"; 57421528Sdavidn } 57521528Sdavidn 57621528Sdavidn if (msg != NULL) { 57721528Sdavidn printf("%s.\n", msg); 57821528Sdavidn sleepexit(1); 57921528Sdavidn } 58021528Sdavidn } 58121528Sdavidn strncpy(shell, login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell), sizeof shell); 58221528Sdavidn#else /* !LOGIN_CAP */ 58321528Sdavidn strncpy(shell, pwd->pw_shell, sizeof shell); 58421528Sdavidn#endif /* LOGIN_CAP */ 58521528Sdavidn shell[sizeof shell - 1] = '\0'; 58621528Sdavidn 58721528Sdavidn#ifdef LOGIN_ACCESS 58821528Sdavidn if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0) { 58921528Sdavidn printf("Permission denied\n"); 59021528Sdavidn syslog(LOG_NOTICE, "%s LOGIN REFUSED (ACCESS) %s %s", pwd->pw_name, hostname?"FROM":"ON", hostname?full_hostname:tty); 59121528Sdavidn sleepexit(1); 59221528Sdavidn } 59321528Sdavidn#endif /* LOGIN_ACCESS */ 59421528Sdavidn 5951590Srgrimes /* Nothing else left to fail -- really log in. */ 5961590Srgrimes memset((void *)&utmp, 0, sizeof(utmp)); 5971590Srgrimes (void)time(&utmp.ut_time); 5981590Srgrimes (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); 5991590Srgrimes if (hostname) 6001590Srgrimes (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); 6011590Srgrimes (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); 6021590Srgrimes login(&utmp); 6031590Srgrimes 6041590Srgrimes dolastlog(quietlog); 6051590Srgrimes 6062224Sguido /* 6072224Sguido * Set device protections, depending on what terminal the 6082224Sguido * user is logged in. This feature is used on Suns to give 6092224Sguido * console users better privacy. 6102224Sguido */ 6112224Sguido login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); 6122224Sguido 61321528Sdavidn (void)chown(ttyn, pwd->pw_uid, (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); 6141590Srgrimes 61521528Sdavidn /* Preserve TERM if it happens to be already set */ 61621528Sdavidn if ((p = getenv("TERM")) != NULL) { 61721528Sdavidn (void)strncpy(term, p, sizeof(term)); 61821528Sdavidn term[sizeof(term)-1] = '\0'; 61921528Sdavidn } 6201590Srgrimes 62121528Sdavidn /* Exclude cons/vt/ptys only, assume dialup otherwise */ 62221528Sdavidn if (hostname==NULL && strchr("vpqstPQST", tty[sizeof("tty")-1]) == NULL) 6231590Srgrimes syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); 6241590Srgrimes 6251590Srgrimes /* If fflag is on, assume caller/authenticator has logged root login. */ 6261590Srgrimes if (rootlogin && fflag == 0) 62721528Sdavidn syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s%s%s", username, tty, hostname?" FROM ":"", hostname?full_hostname:""); 6281590Srgrimes 6291590Srgrimes#ifdef KERBEROS 6305627Swollman if (!quietlog && notickets == 1 && !noticketsdontcomplain) 6311590Srgrimes (void)printf("Warning: no Kerberos tickets issued.\n"); 6321590Srgrimes#endif 6331590Srgrimes 6343205Spst#ifdef LOGALL 6353205Spst /* 6363205Spst * Syslog each successful login, so we don't have to watch hundreds 6373205Spst * of wtmp or lastlogin files. 6383205Spst */ 63921528Sdavidn syslog(LOG_INFO, "login %s %s as %s", hostname?"from":"on", hostname?full_hostname:tty, pwd->pw_name); 64021528Sdavidn#endif 64121528Sdavidn 64221528Sdavidn /* Destroy environment unless user has requested its preservation. */ 64321528Sdavidn if (!pflag) 64421528Sdavidn environ = envinit; 64521528Sdavidn 64621528Sdavidn /* We don't need to be root anymore, so 64721528Sdavidn * set the user and session context 64821528Sdavidn */ 64921528Sdavidn#ifdef LOGIN_CAP 65021528Sdavidn if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL) != 0) { 65121528Sdavidn syslog(LOG_ERR, "setusercontext() failed - exiting"); 65221528Sdavidn exit(1); 6533205Spst } 65421528Sdavidn#else 65521528Sdavidn if (setlogin(pwd->pw_name) < 0) 65621528Sdavidn syslog(LOG_ERR, "setlogin() failure: %m"); 65721528Sdavidn 65821528Sdavidn (void)setgid(pwd->pw_gid); 65921528Sdavidn initgroups(username, pwd->pw_gid); 66021528Sdavidn (void)setuid(rootlogin ? 0 : pwd->pw_uid); 6613205Spst#endif 6623205Spst 66323148Sache if (*pwd->pw_shell == '\0') { 66423148Sache pwd->pw_shell = _PATH_BSHELL; 66523148Sache if (*shell == '\0') /* Not overridden */ 66623148Sache strcpy(shell, pwd->pw_shell); 66723148Sache } 66823148Sache (void)setenv("SHELL", pwd->pw_shell, 1); 66921528Sdavidn (void)setenv("HOME", pwd->pw_dir, 1); 67021528Sdavidn if (term[0] != '\0') 67121528Sdavidn (void)setenv("TERM", term, 1); /* Preset overrides */ 67221528Sdavidn else { 67321528Sdavidn (void)strncpy(term, stypeof(tty), sizeof(term)); 67421528Sdavidn term[sizeof(term)-1] = '\0'; 67521528Sdavidn (void)setenv("TERM", term, 0); /* Fallback doesn't */ 67621528Sdavidn } 67721528Sdavidn (void)setenv("LOGNAME", pwd->pw_name, 1); 67821528Sdavidn (void)setenv("USER", pwd->pw_name, 1); 67921528Sdavidn (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0); 68021528Sdavidn#if LOGIN_CAP_AUTH 68121528Sdavidn auth_env(); 68221528Sdavidn#else 68321528Sdavidn#ifdef KERBEROS 68421528Sdavidn if (krbtkfile_env) 68521528Sdavidn (void)setenv("KRBTKFILE", krbtkfile_env, 1); 68621528Sdavidn#endif 68721528Sdavidn#endif 68821528Sdavidn 6891590Srgrimes if (!quietlog) { 69021528Sdavidn#ifdef LOGIN_CAP 69121528Sdavidn char *cw = login_getcapstr(lc, "copyright", NULL, NULL); 69221528Sdavidn if (cw != NULL && access(cw, F_OK) == 0) 69321528Sdavidn motd(cw); 69421528Sdavidn else 69521528Sdavidn#endif 69623246Swosch (void)printf("%s\n", copyright); 69721528Sdavidn#ifdef LOGIN_CAP 69821528Sdavidn cw = login_getcapstr(lc, "welcome", NULL, NULL); 69921528Sdavidn if (cw == NULL || access(cw, F_OK) != 0) 70021528Sdavidn cw = _PATH_MOTDFILE; 70121528Sdavidn motd(cw); 70221528Sdavidn cw = getenv("MAIL"); /* $MAIL may have been set by class */ 70321528Sdavidn if (cw != NULL) { 70421528Sdavidn strncpy(tbuf, cw, sizeof(tbuf)); 70521528Sdavidn tbuf[sizeof(tbuf)-1] = '\0'; 70621528Sdavidn } else 70721528Sdavidn snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name); 70821528Sdavidn#else 70921528Sdavidn motd(_PATH_MOTDFILE); 71021528Sdavidn snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name); 71121528Sdavidn#endif 7121590Srgrimes if (stat(tbuf, &st) == 0 && st.st_size != 0) 71321528Sdavidn (void)printf("You have %smail.\n", (st.st_mtime > st.st_atime) ? "new " : ""); 7141590Srgrimes } 7151590Srgrimes 71621528Sdavidn /* Login shells have a leading '-' in front of argv[0] */ 71721528Sdavidn tbuf[0] = '-'; 71823148Sache (void)strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell); 71921528Sdavidn 72021528Sdavidn#ifdef LOGIN_CAP 72121528Sdavidn login_close(lc); 7223205Spst#endif 7233205Spst 7241590Srgrimes (void)signal(SIGALRM, SIG_DFL); 7251590Srgrimes (void)signal(SIGQUIT, SIG_DFL); 7261590Srgrimes (void)signal(SIGINT, SIG_DFL); 7271590Srgrimes (void)signal(SIGTSTP, SIG_IGN); 7281590Srgrimes 7294878Sugen if (changepass) { 73021528Sdavidn if (system(_PATH_CHPASS) != 0) 7314878Sugen sleepexit(1); 7324878Sugen } 7334878Sugen 73421528Sdavidn execlp(shell, tbuf, 0); 73521528Sdavidn err(1, "%s", shell); 7361590Srgrimes} 7371590Srgrimes 7381590Srgrimes 73921528Sdavidn/* Allow for authentication style and/or kerberos instance */ 74021528Sdavidn 74121528Sdavidn#define NBUFSIZ UT_NAMESIZE + 64 74221528Sdavidn 7431590Srgrimesvoid 7441590Srgrimesgetloginname() 7451590Srgrimes{ 7461590Srgrimes int ch; 7471590Srgrimes char *p; 7481590Srgrimes static char nbuf[NBUFSIZ]; 7491590Srgrimes 7501590Srgrimes for (;;) { 7511590Srgrimes (void)printf("login: "); 7521590Srgrimes for (p = nbuf; (ch = getchar()) != '\n'; ) { 7531590Srgrimes if (ch == EOF) { 7541590Srgrimes badlogin(username); 7551590Srgrimes exit(0); 7561590Srgrimes } 7571590Srgrimes if (p < nbuf + (NBUFSIZ - 1)) 7581590Srgrimes *p++ = ch; 7591590Srgrimes } 7601590Srgrimes if (p > nbuf) 7611590Srgrimes if (nbuf[0] == '-') 7621590Srgrimes (void)fprintf(stderr, 7631590Srgrimes "login names may not start with '-'.\n"); 7641590Srgrimes else { 7651590Srgrimes *p = '\0'; 7661590Srgrimes username = nbuf; 7671590Srgrimes break; 7681590Srgrimes } 7691590Srgrimes } 7701590Srgrimes} 7711590Srgrimes 7721590Srgrimesint 7731590Srgrimesrootterm(ttyn) 7741590Srgrimes char *ttyn; 7751590Srgrimes{ 7761590Srgrimes struct ttyent *t; 7771590Srgrimes return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE); 7781590Srgrimes} 7791590Srgrimes 78021528Sdavidnvolatile int motdinterrupt; 7811590Srgrimes 78221528Sdavidn/* ARGSUSED */ 7831590Srgrimesvoid 78421528Sdavidnsigint(signo) 78521528Sdavidn int signo; 7861590Srgrimes{ 78721528Sdavidn motdinterrupt = 1; 78821528Sdavidn} 78921528Sdavidn 79021528Sdavidnvoid 79121528Sdavidnmotd(motdfile) 79221528Sdavidn char *motdfile; 79321528Sdavidn{ 7941590Srgrimes int fd, nchars; 7951590Srgrimes sig_t oldint; 79621528Sdavidn char tbuf[256]; 7971590Srgrimes 79821528Sdavidn if ((fd = open(motdfile, O_RDONLY, 0)) < 0) 7991590Srgrimes return; 80021528Sdavidn motdinterrupt = 0; 8011590Srgrimes oldint = signal(SIGINT, sigint); 80221528Sdavidn while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 && !motdinterrupt) 80321528Sdavidn (void)write(fileno(stdout), tbuf, nchars); 8041590Srgrimes (void)signal(SIGINT, oldint); 8051590Srgrimes (void)close(fd); 8061590Srgrimes} 8071590Srgrimes 8081590Srgrimes/* ARGSUSED */ 8091590Srgrimesvoid 8101590Srgrimestimedout(signo) 8111590Srgrimes int signo; 8121590Srgrimes{ 8131590Srgrimes (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout); 8141590Srgrimes exit(0); 8151590Srgrimes} 8161590Srgrimes 81721528Sdavidn#ifndef LOGIN_CAP 8181590Srgrimesvoid 8191590Srgrimeschecknologin() 8201590Srgrimes{ 8211590Srgrimes int fd, nchars; 8221590Srgrimes char tbuf[8192]; 8231590Srgrimes 8241590Srgrimes if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) { 8251590Srgrimes while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) 8261590Srgrimes (void)write(fileno(stdout), tbuf, nchars); 8271590Srgrimes sleepexit(0); 8281590Srgrimes } 8291590Srgrimes} 83021528Sdavidn#endif 8311590Srgrimes 8321590Srgrimesvoid 8331590Srgrimesdolastlog(quiet) 8341590Srgrimes int quiet; 8351590Srgrimes{ 8361590Srgrimes struct lastlog ll; 8371590Srgrimes int fd; 8381590Srgrimes 8391590Srgrimes if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { 8401590Srgrimes (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 8411590Srgrimes if (!quiet) { 8421590Srgrimes if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && 8431590Srgrimes ll.ll_time != 0) { 8441590Srgrimes (void)printf("Last login: %.*s ", 8451590Srgrimes 24-5, (char *)ctime(&ll.ll_time)); 8461590Srgrimes if (*ll.ll_host != '\0') 8471590Srgrimes (void)printf("from %.*s\n", 8481590Srgrimes (int)sizeof(ll.ll_host), 8491590Srgrimes ll.ll_host); 8501590Srgrimes else 8511590Srgrimes (void)printf("on %.*s\n", 8521590Srgrimes (int)sizeof(ll.ll_line), 8531590Srgrimes ll.ll_line); 8541590Srgrimes } 8551590Srgrimes (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 8561590Srgrimes } 8571590Srgrimes memset((void *)&ll, 0, sizeof(ll)); 8581590Srgrimes (void)time(&ll.ll_time); 8591590Srgrimes (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); 8601590Srgrimes if (hostname) 8611590Srgrimes (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); 8621590Srgrimes (void)write(fd, (char *)&ll, sizeof(ll)); 8631590Srgrimes (void)close(fd); 8641590Srgrimes } 8651590Srgrimes} 8661590Srgrimes 8671590Srgrimesvoid 8681590Srgrimesbadlogin(name) 8691590Srgrimes char *name; 8701590Srgrimes{ 8711590Srgrimes 8721590Srgrimes if (failures == 0) 8731590Srgrimes return; 8741590Srgrimes if (hostname) { 8751590Srgrimes syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s", 87616423Sache failures, failures > 1 ? "S" : "", full_hostname); 8771590Srgrimes syslog(LOG_AUTHPRIV|LOG_NOTICE, 8781590Srgrimes "%d LOGIN FAILURE%s FROM %s, %s", 87916423Sache failures, failures > 1 ? "S" : "", full_hostname, name); 8801590Srgrimes } else { 8811590Srgrimes syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s", 8821590Srgrimes failures, failures > 1 ? "S" : "", tty); 8831590Srgrimes syslog(LOG_AUTHPRIV|LOG_NOTICE, 8841590Srgrimes "%d LOGIN FAILURE%s ON %s, %s", 8851590Srgrimes failures, failures > 1 ? "S" : "", tty, name); 8861590Srgrimes } 8871590Srgrimes} 8881590Srgrimes 8891590Srgrimes#undef UNKNOWN 8901590Srgrimes#define UNKNOWN "su" 8911590Srgrimes 8921590Srgrimeschar * 8931590Srgrimesstypeof(ttyid) 8941590Srgrimes char *ttyid; 8951590Srgrimes{ 8961590Srgrimes struct ttyent *t; 8971590Srgrimes return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN); 8981590Srgrimes} 8991590Srgrimes 9001590Srgrimesvoid 9011590Srgrimessleepexit(eval) 9021590Srgrimes int eval; 9031590Srgrimes{ 9041590Srgrimes (void)sleep(5); 9051590Srgrimes exit(eval); 9061590Srgrimes} 9072532Sjkh 9082532Sjkh 909