login.c revision 38374
11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994 31590Srgrimes * The Regents of the University of California. All rights reserved. 489615Sdes * 589615Sdes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 789615Sdes * are met: 889615Sdes * 1. Redistributions of source code must retain the above copyright 989615Sdes * notice, this list of conditions and the following disclaimer. 1089615Sdes * 2. Redistributions in binary form must reproduce the above copyright 1189615Sdes * 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 341590Srgrimes#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"; 381590Srgrimes#endif 391590Srgrimes 401590Srgrimes#ifndef lint 4187628Sdwmalone#if 0 4287628Sdwmalonestatic char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94"; 4387628Sdwmalone#endif 4487628Sdwmalonestatic const char rcsid[] = 4587628Sdwmalone "$Id: login.c,v 1.37 1998/07/31 07:22:31 bde Exp $"; 4687628Sdwmalone#endif /* not lint */ 4787233Smarkm 4887233Smarkm/* 4987233Smarkm * login [ name ] 501590Srgrimes * login -h hostname (for telnetd, etc.) 511590Srgrimes * login -f name (for pre-authenticated login: datakit, xterm, etc.) 521590Srgrimes */ 531590Srgrimes 541590Srgrimes#include <sys/copyright.h> 551590Srgrimes#include <sys/param.h> 5687233Smarkm#include <sys/stat.h> 5787180Smarkm#include <sys/time.h> 5887180Smarkm#include <sys/resource.h> 591590Srgrimes#include <sys/file.h> 601590Srgrimes#include <netinet/in.h> 611590Srgrimes#include <arpa/inet.h> 6287180Smarkm 631590Srgrimes#include <err.h> 641590Srgrimes#include <errno.h> 651590Srgrimes#include <grp.h> 661590Srgrimes#include <netdb.h> 6740102Smarkm#include <pwd.h> 6841079Sjdp#include <setjmp.h> 691590Srgrimes#include <signal.h> 701590Srgrimes#include <stdio.h> 711590Srgrimes#include <stdlib.h> 721590Srgrimes#include <string.h> 731590Srgrimes#include <syslog.h> 741590Srgrimes#include <ttyent.h> 751590Srgrimes#include <unistd.h> 761590Srgrimes#include <utmp.h> 771590Srgrimes 781590Srgrimes#ifdef LOGIN_CAP 7941279Sjdp#include <login_cap.h> 8091714Sdes#else /* Undef AUTH as well */ 813702Spst#undef LOGIN_CAP_AUTH 8287173Smarkm#endif 831590Srgrimes 841590Srgrimes/* 8589994Sdes * If LOGIN_CAP_AUTH is activated: 8689994Sdes * kerberose & skey logins are runtime selected via login 8789994Sdes * login_getstyle() and authentication types for login classes 8889994Sdes * The actual login itself is handled via /usr/libexec/login_<style> 8989994Sdes * Valid styles are determined by the auth-type=style,style entries 9089994Sdes * in the login class. 9189994Sdes */ 9289994Sdes#ifdef LOGIN_CAP_AUTH 9389994Sdes#undef KERBEROS 9489994Sdes#undef SKEY 9589994Sdes#endif /* LOGIN_CAP_AUTH */ 9689994Sdes 9789994Sdes#ifdef SKEY 9889994Sdes#include <skey.h> 991590Srgrimes#endif /* SKEY */ 10087173Smarkm 10187173Smarkm#include "pathnames.h" 10287173Smarkm 10376786Sobrienvoid badlogin __P((char *)); 10476786Sobrienvoid checknologin __P((void)); 10589994Sdesvoid dolastlog __P((int)); 10687173Smarkmvoid getloginname __P((void)); 10789994Sdesvoid motd __P((char *)); 10889994Sdesint rootterm __P((char *)); 1091590Srgrimesvoid sigint __P((int)); 1101590Srgrimesvoid sleepexit __P((int)); 1111590Srgrimesvoid refused __P((char *,char *,int)); 1121590Srgrimeschar *stypeof __P((char *)); 1131590Srgrimesvoid timedout __P((int)); 11489994Sdesint login_access __P((char *, char *)); 1151590Srgrimesvoid login_fbtab __P((char *, uid_t, gid_t)); 11642272Seivind#ifdef KERBEROS 11789994Sdesint klogin __P((struct passwd *, char *, char *, char *)); 11842272Seivind#endif 11989994Sdes 12089994Sdesextern void login __P((struct utmp *)); 1211590Srgrimesextern void trimdomain __P((char *, int)); 12289994Sdesstatic void usage __P((void)); 12389994Sdes 12489994Sdes#define TTYGRPNAME "tty" /* name of group to own ttys */ 12589994Sdes#define DEFAULT_BACKOFF 3 12689994Sdes#define DEFAULT_RETRIES 10 12789994Sdes 12889994Sdes/* 12989994Sdes * This bounds the time given to login. Not a define so it can 13089994Sdes * be patched on machines where it's too small. 13189994Sdes */ 13289994Sdesu_int timeout = 300; 13389994Sdes 13489994Sdes#ifdef KERBEROS 13589994Sdesint notickets = 1; 13689994Sdesint noticketsdontcomplain = 1; 13789994Sdeschar *instance; 13889994Sdeschar *krbtkfile_env; 13989994Sdesint authok; 14089994Sdes#endif 14189994Sdes 14294203Srustruct passwd *pwd; 14389994Sdesint failures; 14494203Sruchar *term, *envinit[1], *hostname, *username, *tty; 14589994Sdeschar full_hostname[MAXHOSTNAMELEN]; 14689994Sdes 14789994Sdesint 14889994Sdesmain(argc, argv) 14989994Sdes int argc; 15089994Sdes char *argv[]; 15189994Sdes{ 15291714Sdes extern char **environ; 15389994Sdes struct group *gr; 15489994Sdes struct stat st; 15589994Sdes struct timeval tp; 15689994Sdes struct utmp utmp; 15789994Sdes int rootok, retries, backoff; 1581590Srgrimes int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval; 15989994Sdes int changepass; 1601590Srgrimes time_t warntime; 1611590Srgrimes uid_t uid, euid; 1621590Srgrimes char *domain, *p, *ep, *salt, *ttyn; 16389994Sdes char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; 16489994Sdes char localhost[MAXHOSTNAMELEN]; 16535559Speter char *shell = NULL; 16646007Sache#ifdef LOGIN_CAP 16789994Sdes login_cap_t *lc = NULL; 16845431Sbrian#ifdef LOGIN_CAP_AUTH 16942272Seivind char *style, *authtype; 17094203Sru char *auth_method = NULL; 17198960Sache char *instance = NULL; 17294203Sru int authok; 17321528Sdavidn#endif /* LOGIN_CAP_AUTH */ 174146867Smaxim#endif /* LOGIN_CAP */ 17574874Smarkm#ifdef SKEY 176165152Scsjp int permit_passwd = 0; 177155312Swsalamon#endif /* SKEY */ 178165152Scsjp 1791590Srgrimes (void)signal(SIGALRM, timedout); 18042272Seivind (void)alarm(timeout); 18142272Seivind (void)signal(SIGQUIT, SIG_IGN); 18276942Sguido (void)signal(SIGINT, SIG_IGN); 18342272Seivind (void)setpriority(PRIO_PROCESS, 0, 0); 18442272Seivind 18589994Sdes openlog("login", LOG_ODELAY, LOG_AUTH); 18676788Sobrien 18776788Sobrien /* 18889994Sdes * -p is used by getty to tell login not to destroy the environment 18942272Seivind * -f is used to skip a second login authentication 1901590Srgrimes * -h is used by other servers to pass the name of the remote 1911590Srgrimes * host to login so that it may be placed in utmp and wtmp 1921590Srgrimes */ 1931590Srgrimes *full_hostname = '\0'; 1941590Srgrimes domain = NULL; 1951590Srgrimes term = NULL; 1961590Srgrimes if (gethostname(localhost, sizeof(localhost)) < 0) 19735557Speter syslog(LOG_ERR, "couldn't get local hostname: %m"); 19846007Sache else 19989994Sdes domain = strchr(localhost, '.'); 20024360Simp 2011590Srgrimes fflag = hflag = pflag = 0; 2021590Srgrimes uid = getuid(); 2031590Srgrimes euid = geteuid(); 2041590Srgrimes while ((ch = getopt(argc, argv, "fh:p")) != -1) 2051590Srgrimes switch (ch) { 20689994Sdes case 'f': 2071590Srgrimes fflag = 1; 20889994Sdes break; 20981555Smike case 'h': 21081555Smike if (uid) 21189994Sdes errx(1, "-h option: %s", strerror(EPERM)); 2121590Srgrimes hflag = 1; 2131590Srgrimes strncpy(full_hostname, optarg, sizeof(full_hostname)-1); 2141590Srgrimes if (domain && (p = strchr(optarg, '.')) && 2151590Srgrimes strcasecmp(p, domain) == 0) 2161590Srgrimes *p = 0; 2171590Srgrimes 2181590Srgrimes trimdomain(optarg, UT_HOSTSIZE ); 21989994Sdes 2201590Srgrimes if (strlen(optarg) > UT_HOSTSIZE) { 22127605Scharnier struct hostent *hp = gethostbyname(optarg); 2221590Srgrimes 2231590Srgrimes if (hp != NULL) { 2241590Srgrimes struct in_addr in; 2251590Srgrimes 22689994Sdes memmove(&in, hp->h_addr, sizeof(in)); 22789994Sdes optarg = strdup(inet_ntoa(in)); 22889994Sdes } else 22989994Sdes optarg = "invalid hostname"; 2301590Srgrimes } 23189994Sdes hostname = optarg; 2321590Srgrimes break; 23389994Sdes case 'p': 2341590Srgrimes pflag = 1; 235107585Sdes break; 236107585Sdes case '?': 2371590Srgrimes default: 2381590Srgrimes if (!uid) 2391590Srgrimes syslog(LOG_ERR, "invalid flag %c", ch); 24089994Sdes usage(); 24189994Sdes } 24289994Sdes argc -= optind; 2431590Srgrimes argv += optind; 2441590Srgrimes 2451590Srgrimes if (*argv) { 2461590Srgrimes username = *argv; 2471590Srgrimes ask = 0; 24823985Sdavidn } else 2491590Srgrimes ask = 1; 2501590Srgrimes 2511590Srgrimes for (cnt = getdtablesize(); cnt > 2; cnt--) 2521590Srgrimes (void)close(cnt); 25323985Sdavidn 25423985Sdavidn ttyn = ttyname(STDIN_FILENO); 25523985Sdavidn if (ttyn == NULL || *ttyn == '\0') { 25623985Sdavidn (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); 257114010Sdes ttyn = tname; 25889994Sdes } 25976786Sobrien if ((tty = strrchr(ttyn, '/')) != NULL) 26087173Smarkm ++tty; 26189994Sdes else 26289994Sdes tty = ttyn; 26389994Sdes 26489994Sdes#ifdef LOGIN_CAP_AUTH 26523985Sdavidn authtype = hostname ? "rlogin" : "login"; 26623985Sdavidn#endif 26721528Sdavidn#ifdef LOGIN_CAP 26889994Sdes /* 26989994Sdes * Get "login-retries" & "login-backoff" from default class 27089994Sdes */ 2711590Srgrimes lc = login_getclass(NULL); 2721590Srgrimes retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES, DEFAULT_RETRIES); 2731590Srgrimes backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF, DEFAULT_BACKOFF); 27489994Sdes login_close(lc); 27589994Sdes lc = NULL; 27689994Sdes#else 27789994Sdes retries = DEFAULT_RETRIES; 2781590Srgrimes backoff = DEFAULT_BACKOFF; 2791590Srgrimes#endif 28021528Sdavidn 2811590Srgrimes for (cnt = 0;; ask = 1) { 2821590Srgrimes if (ask) { 2831590Srgrimes fflag = 0; 2841590Srgrimes getloginname(); 2851590Srgrimes } 28689994Sdes rootlogin = 0; 2871590Srgrimes rootok = rootterm(tty); /* Default (auth may change) */ 28889994Sdes#ifdef LOGIN_CAP_AUTH 2891590Srgrimes authok = 0; 2901590Srgrimes if (auth_method = strchr(username, ':')) { 29123985Sdavidn *auth_method = '\0'; 29289994Sdes auth_method++; 2931590Srgrimes if (*auth_method == '\0') 29489994Sdes auth_method = NULL; 29589994Sdes } 29689994Sdes /* 297165152Scsjp * We need to do this regardless of whether 298155312Swsalamon * kerberos is available. 299165152Scsjp */ 30089994Sdes if ((instance = strchr(username, '.')) != NULL) { 3013205Spst if (strncmp(instance, ".root", 5) == 0) 30289994Sdes rootlogin = 1; 30389994Sdes *instance++ = '\0'; 30489994Sdes } else 305165152Scsjp instance = ""; 306155312Swsalamon#else /* !LOGIN_CAP_AUTH */ 307165152Scsjp#ifdef KERBEROS 30889994Sdes if ((instance = strchr(username, '.')) != NULL) { 30989994Sdes if (strncmp(instance, ".root", 5) == 0) 310110966Sdes rootlogin = 1; 31189994Sdes *instance++ = '\0'; 31289994Sdes } else 313165152Scsjp instance = ""; 314155312Swsalamon#endif /* KERBEROS */ 315165152Scsjp#endif /* LOGIN_CAP_AUTH */ 31689994Sdes 31789994Sdes if (strlen(username) > UT_NAMESIZE) 31897376Sdes username[UT_NAMESIZE] = '\0'; 31989994Sdes 32041279Sjdp /* 32141279Sjdp * Note if trying multiple user names; log failures for 3221590Srgrimes * previous user name, but don't bother logging one failure 3231590Srgrimes * for nonexistent name (mistyped username). 32489994Sdes */ 32589994Sdes if (failures && strcmp(tbuf, username)) { 32689994Sdes if (failures > (pwd ? 0 : 1)) 3271590Srgrimes badlogin(tbuf); 32889994Sdes failures = 0; 32989994Sdes } 33089994Sdes (void)strncpy(tbuf, username, sizeof tbuf-1); 33189994Sdes tbuf[sizeof tbuf-1] = '\0'; 332165152Scsjp 333155312Swsalamon if ((pwd = getpwnam(username)) != NULL) 334165152Scsjp salt = pwd->pw_passwd; 33589994Sdes else 33689994Sdes salt = "xx"; 33789994Sdes 33889994Sdes#ifdef LOGIN_CAP 33989994Sdes /* 3401590Srgrimes * Establish the class now, before we might goto 3411590Srgrimes * within the next block. pwd can be NULL since it 34289994Sdes * falls back to the "default" class if it is. 34389994Sdes */ 34489994Sdes if (pwd != NULL) 34589994Sdes (void)seteuid(rootlogin ? 0 : pwd->pw_uid); 34697376Sdes lc = login_getpwclass(pwd); 347155312Swsalamon seteuid(euid); 348155312Swsalamon#endif /* LOGIN_CAP */ 349155312Swsalamon 350155312Swsalamon /* 351165152Scsjp * if we have a valid account name, and it doesn't have a 352155312Swsalamon * password, or the -f option was specified and the caller 353165152Scsjp * is root or the caller isn't changing their uid, don't 354155312Swsalamon * authenticate. 3551590Srgrimes */ 3561590Srgrimes rval = 1; 35723985Sdavidn if (pwd != NULL) { 358157215Scognet if (pwd->pw_uid == 0) 359157215Scognet rootlogin = 1; 36023985Sdavidn 36189994Sdes if (fflag && (uid == (uid_t)0 || 36289994Sdes uid == (uid_t)pwd->pw_uid)) { 36323985Sdavidn /* already authenticated */ 36423985Sdavidn break; 36523985Sdavidn } else if (pwd->pw_passwd[0] == '\0') { 3661590Srgrimes if (!rootlogin || rootok) { 36789994Sdes /* pretend password okay */ 3681590Srgrimes rval = 0; 36938374Sjkoshy goto ttycheck; 3701590Srgrimes } 3711590Srgrimes } 3721590Srgrimes } 3731590Srgrimes 3741590Srgrimes fflag = 0; 37576942Sguido 3761590Srgrimes (void)setpriority(PRIO_PROCESS, 0, -4); 3771590Srgrimes 3781590Srgrimes#ifdef LOGIN_CAP_AUTH 379165152Scsjp /* 380155312Swsalamon * This hands off authorization to an authorization program, 381155312Swsalamon * depending on the styles available for the "auth-login", 382155312Swsalamon * auth-rlogin (or default) authorization styles. 383165152Scsjp * We do this regardless of whether an account exists so that 384155312Swsalamon * the remote user cannot tell a "real" from an invented 38541279Sjdp * account name. If we don't have an account we just fall 38641279Sjdp * back to the first method for the "default" class. 38741279Sjdp */ 38841279Sjdp if (!(style = login_getstyle(lc, auth_method, authtype))) { 389146867Smaxim 39041279Sjdp /* 391146867Smaxim * No available authorization method 392146867Smaxim */ 39397376Sdes rval = 1; 39483519Srwatson (void)printf("No auth method available for %s.\n", 39583519Srwatson authtype); 39683519Srwatson } else { 39783519Srwatson 39883519Srwatson /* 39983519Srwatson * Put back the kerberos instance, if any was given. 40083519Srwatson * Don't worry about the non-kerberos case here, since 40146007Sache * if kerberos is not available or not selected and an 40235557Speter * instance is given at the login prompt, su or rlogin -l, 40321528Sdavidn * then anything else should fail as well. 40426021Spst */ 40523985Sdavidn if (*instance) 40697376Sdes *(instance - 1) = '.'; 40723985Sdavidn 40823985Sdavidn rval = authenticate(username, 40923985Sdavidn lc ? lc->lc_class : "default", 41087173Smarkm style, authtype); 41187173Smarkm /* Junk it again */ 41287173Smarkm if (*instance) 41389994Sdes *(instance - 1) = '\0'; 41487173Smarkm } 4151590Srgrimes 41635557Speter if (!rval) { 41746007Sache char * approvep; 418125055Sfjoe 41921528Sdavidn /* 420125055Sfjoe * If authentication succeeds, run any approval 421125055Sfjoe * program, if applicable for this class. 422125055Sfjoe */ 42397376Sdes approvep = login_getcapstr(lc, "approve", NULL, NULL); 42497376Sdes rval = 1; /* Assume bad login again */ 42523985Sdavidn 42687173Smarkm if (approvep==NULL || 42787173Smarkm auth_script(approvep, approvep, username, 42887173Smarkm lc->lc_class, 0) == 0) { 42989994Sdes int r; 43087173Smarkm 43123985Sdavidn r = auth_scan(AUTH_OKAY); 43223985Sdavidn /* 43323985Sdavidn * See what the authorize program says 43481555Smike */ 43589994Sdes if (r != 0) { 43623985Sdavidn rval = 0; 43721528Sdavidn 4382224Sguido if (!rootok && (r & AUTH_ROOTOKAY)) 4392224Sguido rootok = 1; /* root approved */ 4402224Sguido else 4412224Sguido rootlogin = 0; 4422224Sguido 4432224Sguido if (!authok && (r & AUTH_SECURE)) 4442224Sguido authok = 1; /* secure */ 44550124Simp } 44650124Simp } 44750124Simp } 44850124Simp#else /* !LOGIN_CAP_AUTH */ 44950124Simp#ifdef SKEY 450102141Simp permit_passwd = skeyaccess(username, tty, 451102141Simp hostname ? full_hostname : NULL, 452102141Simp NULL); 453102141Simp p = skey_getpass("Password:", pwd, permit_passwd); 454102141Simp ep = skey_crypt(p, salt, pwd, permit_passwd); 455102141Simp#else /* !SKEY */ 456102141Simp p = getpass("Password:"); 45750124Simp ep = crypt(p, salt); 458102141Simp#endif/* SKEY */ 459102141Simp 460102141Simp if (pwd) { 46189994Sdes if (!p[0] && pwd->pw_passwd[0]) 46250124Simp ep = ":"; 463102141Simp#ifdef KERBEROS 464114048Srwatson#ifdef SKEY 4651590Srgrimes /* 46623985Sdavidn * Do not allow user to type in kerberos password 46723985Sdavidn * over the net (actually, this is ok for encrypted 46823985Sdavidn * links, but we have no way of determining if the 46923985Sdavidn * link is encrypted. 47023985Sdavidn */ 47189994Sdes if (!permit_passwd) { 4721590Srgrimes rval = 1; /* failed */ 4731590Srgrimes } else 4743205Spst#endif /* SKEY */ 4753205Spst rval = klogin(pwd, instance, localhost, p); 47689994Sdes if (rval != 0 && rootlogin && pwd->pw_uid != 0) 47789994Sdes rootlogin = 0; 4783205Spst if (rval == 0) 47989994Sdes authok = 1; /* kerberos authenticated ok */ 48023985Sdavidn else if (rval == 1) /* fallback to unix passwd */ 48189994Sdes rval = strcmp(ep, pwd->pw_passwd); 48223985Sdavidn#else /* !KERBEROS */ 48323985Sdavidn rval = strcmp(ep, pwd->pw_passwd); 48423985Sdavidn#endif /* KERBEROS */ 48521528Sdavidn } 48621528Sdavidn 48723985Sdavidn /* clear entered password */ 48889994Sdes memset(p, 0, strlen(p)); 48989994Sdes#endif /* LOGIN_CAP_AUTH */ 49023985Sdavidn 49189994Sdes (void)setpriority(PRIO_PROCESS, 0, 0); 49289994Sdes 49323985Sdavidn#ifdef LOGIN_CAP_AUTH 49489994Sdes if (rval) 49523985Sdavidn auth_rmfiles(); 49623985Sdavidn#endif 49776788Sobrien ttycheck: 49823985Sdavidn /* 49923985Sdavidn * If trying to log in as root without Kerberos, 50023985Sdavidn * but with insecure terminal, refuse the login attempt. 50189994Sdes */ 50289994Sdes if (pwd && !rval) { 50323985Sdavidn#if defined(KERBEROS) || defined(LOGIN_CAP_AUTH) 50489994Sdes if (authok == 0 && rootlogin && !rootok) 50521528Sdavidn#else 50621528Sdavidn if (rootlogin && !rootok) 50789994Sdes#endif 50889994Sdes refused(NULL, "NOROOT", 0); 50921528Sdavidn else /* valid password & authenticated */ 51023985Sdavidn break; 51174874Smarkm } 51274874Smarkm 51374874Smarkm (void)printf("Login incorrect\n"); 51497376Sdes failures++; 51589994Sdes 51674874Smarkm /* 51774874Smarkm * we allow up to 'retry' (10) tries, 51889994Sdes * but after 'backoff' (3) we start backing off 51989994Sdes */ 52089994Sdes if (++cnt > backoff) { 52189994Sdes if (cnt >= retries) { 52289994Sdes badlogin(username); 52389994Sdes sleepexit(1); 52497376Sdes } 52589994Sdes sleep((u_int)((cnt - backoff) * 5)); 52689994Sdes } 52789994Sdes } 52889994Sdes 52989994Sdes /* committed to login -- turn off timeout */ 53089994Sdes (void)alarm((u_int)0); 53174874Smarkm 53289994Sdes endpwent(); 53389994Sdes 53489994Sdes /* if user not super-user, check for disabled logins */ 53589994Sdes#ifdef LOGIN_CAP 53689994Sdes if (!rootlogin) 53789994Sdes auth_checknologin(lc); 53889994Sdes#else 53989994Sdes if (!rootlogin) 54074874Smarkm checknologin(); 54189994Sdes#endif 54289994Sdes 54374874Smarkm#ifdef LOGIN_CAP 544110549Sdes quietlog = login_getcapbool(lc, "hushlogin", 0); 545107585Sdes#else 546110549Sdes quietlog = 0; 54789994Sdes#endif 54874874Smarkm (void)seteuid(rootlogin ? 0 : pwd->pw_uid); 54974874Smarkm if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) { 55074874Smarkm#ifdef LOGIN_CAP 55189994Sdes if (login_getcapbool(lc, "requirehome", 0)) 55221528Sdavidn refused("Home directory not available", "HOMEDIR", 1); 55397376Sdes#endif 55489994Sdes if (chdir("/") < 0) 55589994Sdes refused("Cannot find root directory", "ROOTDIR", 1); 55689994Sdes pwd->pw_dir = "/"; 55789994Sdes if (!quietlog || *pwd->pw_dir) 55889994Sdes printf("No home directory.\nLogging in with home = \"/\".\n"); 55989994Sdes } 56089994Sdes (void)seteuid(euid); 56189994Sdes if (!quietlog) 56291714Sdes quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; 56389994Sdes 56489994Sdes if (pwd->pw_change || pwd->pw_expire) 56589994Sdes (void)gettimeofday(&tp, (struct timezone *)NULL); 56689994Sdes 56789994Sdes#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ 56889994Sdes 56941279Sjdp#ifdef LOGIN_CAP 57097376Sdes warntime = login_getcaptime(lc, "warnpassword", 57189994Sdes DEFAULT_WARN, DEFAULT_WARN); 57241279Sjdp#else 57341279Sjdp warntime = DEFAULT_WARN; 57474874Smarkm#endif 57597376Sdes 57621528Sdavidn changepass=0; 5773205Spst if (pwd->pw_change) { 57821528Sdavidn if (tp.tv_sec >= pwd->pw_change) { 57923148Sache (void)printf("Sorry -- your password has expired.\n"); 58021528Sdavidn changepass=1; 58198960Sache syslog(LOG_INFO, 58298990Sache "%s Password expired - forcing change", 58398960Sache pwd->pw_name); 58498960Sache } else if (pwd->pw_change - tp.tv_sec < warntime && !quietlog) 58598960Sache (void)printf("Warning: your password expires on %s", 58641279Sjdp ctime(&pwd->pw_change)); 58741279Sjdp } 58821528Sdavidn 58921528Sdavidn#ifdef LOGIN_CAP 5901590Srgrimes warntime = login_getcaptime(lc, "warnexpire", 59194203Sru DEFAULT_WARN, DEFAULT_WARN); 59223985Sdavidn#else 59323985Sdavidn warntime = DEFAULT_WARN; 59489994Sdes#endif 59589994Sdes 59623985Sdavidn if (pwd->pw_expire) { 59723985Sdavidn if (tp.tv_sec >= pwd->pw_expire) { 59823985Sdavidn refused("Sorry -- your account has expired", 59921528Sdavidn "EXPIRED", 1); 60089994Sdes } else if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog) 60189994Sdes (void)printf("Warning: your account expires on %s", 60289994Sdes ctime(&pwd->pw_expire)); 60389994Sdes } 60423985Sdavidn 605146867Smaxim#ifdef LOGIN_CAP 606146867Smaxim if (lc != NULL) { 607100825Sdwmalone if (hostname) { 608100825Sdwmalone struct hostent *hp = gethostbyname(full_hostname); 60986450Srwatson 610100825Sdwmalone if (hp == NULL) 611100825Sdwmalone optarg = NULL; 612100825Sdwmalone else { 61386450Srwatson struct in_addr in; 61489994Sdes memmove(&in, hp->h_addr, sizeof(in)); 615100825Sdwmalone optarg = strdup(inet_ntoa(in)); 61686450Srwatson } 61786450Srwatson if (!auth_hostok(lc, full_hostname, optarg)) 61889994Sdes refused("Permission denied", "HOST", 1); 619100825Sdwmalone } 62086450Srwatson 6211590Srgrimes if (!auth_ttyok(lc, tty)) 6221590Srgrimes refused("Permission denied", "TTY", 1); 623146867Smaxim 62421528Sdavidn if (!auth_timeok(lc, time(NULL))) 6253205Spst refused("Logins not available right now", "TIME", 1); 6261590Srgrimes } 6271590Srgrimes shell=login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell); 6281590Srgrimes#else /* !LOGIN_CAP */ 6291590Srgrimes shell=pwd->pw_shell; 6301590Srgrimes#endif /* LOGIN_CAP */ 63123985Sdavidn if (*pwd->pw_shell == '\0') 63223985Sdavidn pwd->pw_shell = _PATH_BSHELL; 63323985Sdavidn if (*shell == '\0') /* Not overridden */ 63489994Sdes shell = pwd->pw_shell; 63589994Sdes if ((shell = strdup(shell)) == NULL) { 63681555Smike syslog(LOG_NOTICE, "memory allocation error"); 63781555Smike sleepexit(1); 63881555Smike } 63989994Sdes 64089994Sdes#ifdef LOGIN_ACCESS 64181555Smike if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0) 64223985Sdavidn refused("Permission denied", "ACCESS", 1); 64389994Sdes#endif /* LOGIN_ACCESS */ 64421528Sdavidn 64597376Sdes /* Nothing else left to fail -- really log in. */ 64689994Sdes memset((void *)&utmp, 0, sizeof(utmp)); 64789994Sdes (void)time(&utmp.ut_time); 64889994Sdes (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); 6491590Srgrimes if (hostname) 6501590Srgrimes (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); 65141279Sjdp (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); 65241279Sjdp login(&utmp); 65341279Sjdp 65441279Sjdp dolastlog(quietlog); 65541279Sjdp 65641279Sjdp /* 65741279Sjdp * Set device protections, depending on what terminal the 65841279Sjdp * user is logged in. This feature is used on Suns to give 65989994Sdes * console users better privacy. 66041279Sjdp */ 66141279Sjdp login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); 66241279Sjdp 66341279Sjdp (void)chown(ttyn, pwd->pw_uid, 66441279Sjdp (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); 66589994Sdes 66689994Sdes /* 66741279Sjdp * Preserve TERM if it happens to be already set. 66841279Sjdp */ 66941279Sjdp if ((term = getenv("TERM")) != NULL) 67041279Sjdp term = strdup(term); 67141279Sjdp 67241279Sjdp /* 67341279Sjdp * Exclude cons/vt/ptys only, assume dialup otherwise 67441279Sjdp * TODO: Make dialup tty determination a library call 67541279Sjdp * for consistency (finger etc.) 67641279Sjdp */ 67741279Sjdp if (hostname==NULL && isdialuptty(tty)) 67841279Sjdp syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); 67941279Sjdp 68041279Sjdp#ifdef KERBEROS 68141279Sjdp if (!quietlog && notickets == 1 && !noticketsdontcomplain) 68241279Sjdp (void)printf("Warning: no Kerberos tickets issued.\n"); 68341279Sjdp#endif 68441279Sjdp 68541279Sjdp#ifdef LOGALL 68689994Sdes /* 68789994Sdes * Syslog each successful login, so we don't have to watch hundreds 68889994Sdes * of wtmp or lastlogin files. 68941279Sjdp */ 69041279Sjdp if (hostname) 69189994Sdes syslog(LOG_INFO, "login from %s on %s as %s", 69289994Sdes full_hostname, tty, pwd->pw_name); 69389994Sdes else 69441279Sjdp syslog(LOG_INFO, "login on %s as %s", 69541279Sjdp tty, pwd->pw_name); 69641279Sjdp#endif 69741279Sjdp 69841279Sjdp /* 69941279Sjdp * If fflag is on, assume caller/authenticator has logged root login. 70041279Sjdp */ 70141279Sjdp if (rootlogin && fflag == 0) 70241279Sjdp { 70341279Sjdp if (hostname) 70489994Sdes syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s", 70541279Sjdp username, tty, full_hostname); 70641279Sjdp else 70741279Sjdp syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", 70874874Smarkm username, tty); 70974874Smarkm } 71089994Sdes 71189994Sdes /* 71289994Sdes * Destroy environment unless user has requested its preservation. 71389994Sdes * We need to do this before setusercontext() because that may 71489994Sdes * set or reset some environment variables. 71589994Sdes */ 71689994Sdes if (!pflag) 71789994Sdes environ = envinit; 71889994Sdes 71974874Smarkm /* 72074874Smarkm * We don't need to be root anymore, so 72189994Sdes * set the user and session context 72289994Sdes */ 72389994Sdes#ifdef LOGIN_CAP 72474874Smarkm if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL) != 0) { 72589994Sdes syslog(LOG_ERR, "setusercontext() failed - exiting"); 72674874Smarkm exit(1); 72741279Sjdp } 72874874Smarkm#else 72974874Smarkm if (setlogin(pwd->pw_name) < 0) 73089994Sdes syslog(LOG_ERR, "setlogin() failure: %m"); 73174874Smarkm 73274874Smarkm (void)setgid(pwd->pw_gid); 73389994Sdes initgroups(username, pwd->pw_gid); 73441279Sjdp (void)setuid(rootlogin ? 0 : pwd->pw_uid); 73572215Snectar#endif 73689994Sdes 73789994Sdes (void)setenv("SHELL", pwd->pw_shell, 1); 73889994Sdes (void)setenv("HOME", pwd->pw_dir, 1); 73989994Sdes if (term != NULL && *term != '\0') 74087177Smarkm (void)setenv("TERM", term, 1); /* Preset overrides */ 74172215Snectar else { 74289994Sdes (void)setenv("TERM", stypeof(tty), 0); /* Fallback doesn't */ 74389994Sdes } 74472215Snectar (void)setenv("LOGNAME", pwd->pw_name, 1); 74589994Sdes (void)setenv("USER", pwd->pw_name, 1); 74689994Sdes (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0); 74789994Sdes#ifdef KERBEROS 748169177Sache if (krbtkfile_env) 749169177Sache (void)setenv("KRBTKFILE", krbtkfile_env, 1); 75089994Sdes#endif 75172215Snectar#if LOGIN_CAP_AUTH 75272215Snectar auth_env(); 75372215Snectar#endif 75472215Snectar 75589994Sdes#ifdef LOGIN_CAP 75672215Snectar if (!quietlog) { 75772215Snectar char *cw; 75872215Snectar 75972215Snectar cw = login_getcapstr(lc, "copyright", NULL, NULL); 76089994Sdes if (cw != NULL && access(cw, F_OK) == 0) 76172215Snectar motd(cw); 76272215Snectar else 76389994Sdes (void)printf("%s\n\t%s %s\n", 76472215Snectar "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994", 76572215Snectar "The Regents of the University of California. ", 76672215Snectar "All rights reserved."); 76772215Snectar 76872215Snectar (void)printf("\n"); 769171195Sscf 77072215Snectar cw = login_getcapstr(lc, "welcome", NULL, NULL); 77172215Snectar if (cw == NULL || access(cw, F_OK) != 0) 77272215Snectar cw = _PATH_MOTDFILE; 773171195Sscf motd(cw); 77489994Sdes 77572215Snectar cw = getenv("MAIL"); /* $MAIL may have been set by class */ 77689994Sdes if (cw != NULL) { 77772215Snectar strncpy(tbuf, cw, sizeof(tbuf)); 77872215Snectar tbuf[sizeof(tbuf)-1] = '\0'; 77972215Snectar } else 78089994Sdes snprintf(tbuf, sizeof(tbuf), "%s/%s", 78172215Snectar _PATH_MAILDIR, pwd->pw_name); 782171195Sscf#else 783171195Sscf if (!quietlog) { 784171195Sscf (void)printf("%s\n\t%s %s\n", 78589994Sdes "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994", 78672215Snectar "The Regents of the University of California. ", 78741279Sjdp "All rights reserved."); 78827605Scharnier motd(_PATH_MOTDFILE); 78987177Smarkm snprintf(tbuf, sizeof(tbuf), "%s/%s", 79027605Scharnier _PATH_MAILDIR, pwd->pw_name); 79176791Sobrien#endif 79227605Scharnier if (stat(tbuf, &st) == 0 && st.st_size != 0) 79327605Scharnier (void)printf("You have %smail.\n", 79427605Scharnier (st.st_mtime > st.st_atime) ? "new " : ""); 7951590Srgrimes } 79623985Sdavidn 79789994Sdes#ifdef LOGIN_CAP 79874874Smarkm login_close(lc); 79989994Sdes#endif 80087177Smarkm 8011590Srgrimes (void)signal(SIGALRM, SIG_DFL); 80289994Sdes (void)signal(SIGQUIT, SIG_DFL); 8031590Srgrimes (void)signal(SIGINT, SIG_DFL); 8041590Srgrimes (void)signal(SIGTSTP, SIG_IGN); 80589994Sdes 80689994Sdes if (changepass) { 80789994Sdes if (system(_PATH_CHPASS) != 0) 80889994Sdes sleepexit(1); 80981555Smike } 8101590Srgrimes 8111590Srgrimes /* 8121590Srgrimes * Login shells have a leading '-' in front of argv[0] 81389994Sdes */ 8141590Srgrimes tbuf[0] = '-'; 81589994Sdes (void)strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell); 8161590Srgrimes 8171590Srgrimes execlp(shell, tbuf, 0); 81889994Sdes err(1, "%s", shell); 81997376Sdes} 82089994Sdes 82189994Sdesstatic void 82289994Sdesusage() 82389994Sdes{ 82489994Sdes (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n"); 82589994Sdes exit(1); 8261590Srgrimes} 82789994Sdes 8281590Srgrimes/* 8291590Srgrimes * Allow for authentication style and/or kerberos instance 83089994Sdes * */ 83189994Sdes 83289994Sdes#define NBUFSIZ UT_NAMESIZE + 64 83389994Sdes 83489994Sdesvoid 83589994Sdesgetloginname() 8361590Srgrimes{ 83721528Sdavidn int ch; 83821528Sdavidn char *p; 83921528Sdavidn static char nbuf[NBUFSIZ]; 84089994Sdes 84189994Sdes for (;;) { 84289994Sdes (void)printf("login: "); 84389994Sdes for (p = nbuf; (ch = getchar()) != '\n'; ) { 84489994Sdes if (ch == EOF) { 84521528Sdavidn badlogin(username); 8461590Srgrimes exit(0); 84789994Sdes } 84889994Sdes if (p < nbuf + (NBUFSIZ - 1)) 8491590Srgrimes *p++ = ch; 85089994Sdes } 85189994Sdes if (p > nbuf) 85221528Sdavidn if (nbuf[0] == '-') 8531590Srgrimes (void)fprintf(stderr, 85489994Sdes "login names may not start with '-'.\n"); 85589994Sdes else { 85689994Sdes *p = '\0'; 85789994Sdes username = nbuf; 85889994Sdes break; 85989994Sdes } 86089994Sdes } 86189994Sdes} 86289994Sdes 8631590Srgrimesint 8641590Srgrimesrootterm(ttyn) 86589994Sdes char *ttyn; 86689994Sdes{ 86789994Sdes struct ttyent *t; 86889994Sdes 86989994Sdes return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE); 87089994Sdes} 87189994Sdes 87289994Sdesvolatile int motdinterrupt; 87389994Sdes 87489994Sdes/* ARGSUSED */ 8751590Srgrimesvoid 87676788Sobriensigint(signo) 87742272Seivind int signo; 8781590Srgrimes{ 8791590Srgrimes motdinterrupt = 1; 880105164Sphk} 88189994Sdes 8821590Srgrimesvoid 8831590Srgrimesmotd(motdfile) 8841590Srgrimes char *motdfile; 8851590Srgrimes{ 88689994Sdes int fd, nchars; 8871590Srgrimes sig_t oldint; 88889994Sdes char tbuf[256]; 8891590Srgrimes 8901590Srgrimes if ((fd = open(motdfile, O_RDONLY, 0)) < 0) 89189994Sdes return; 8921590Srgrimes motdinterrupt = 0; 8931590Srgrimes oldint = signal(SIGINT, sigint); 8941590Srgrimes while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 && !motdinterrupt) 8951590Srgrimes (void)write(fileno(stdout), tbuf, nchars); 8961590Srgrimes (void)signal(SIGINT, oldint); 8971590Srgrimes (void)close(fd); 8981590Srgrimes} 89942272Seivind 9001590Srgrimes/* ARGSUSED */ 9011590Srgrimesvoid 90287173Smarkmtimedout(signo) 90389994Sdes int signo; 9041590Srgrimes{ 9051590Srgrimes (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout); 90623985Sdavidn exit(0); 90781555Smike} 90881555Smike 90981555Smike#ifndef LOGIN_CAP 91081555Smikevoid 91181555Smikechecknologin() 91298960Sache{ 9131590Srgrimes int fd, nchars; 9141590Srgrimes char tbuf[8192]; 915105164Sphk 91689994Sdes if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) { 91723985Sdavidn while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) 91823985Sdavidn (void)write(fileno(stdout), tbuf, nchars); 91923985Sdavidn sleepexit(0); 92023985Sdavidn } 92189994Sdes} 92223985Sdavidn#endif 92389994Sdes 92423985Sdavidnvoid 92523985Sdavidndolastlog(quiet) 92676788Sobrien int quiet; 92723985Sdavidn{ 92889994Sdes struct lastlog ll; 92923985Sdavidn int fd; 93023985Sdavidn 93189994Sdes if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { 93289994Sdes (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 93389994Sdes if (!quiet) { 934105164Sphk if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && 93589994Sdes ll.ll_time != 0) { 9361590Srgrimes (void)printf("Last login: %.*s ", 93789994Sdes 24-5, (char *)ctime(&ll.ll_time)); 93889994Sdes if (*ll.ll_host != '\0') 93923985Sdavidn (void)printf("from %.*s\n", 94089994Sdes (int)sizeof(ll.ll_host), 94189994Sdes ll.ll_host); 94289994Sdes else 943105164Sphk (void)printf("on %.*s\n", 94489994Sdes (int)sizeof(ll.ll_line), 94589994Sdes ll.ll_line); 94689994Sdes } 94789994Sdes (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 94889994Sdes } 94989994Sdes memset((void *)&ll, 0, sizeof(ll)); 95089994Sdes (void)time(&ll.ll_time); 95189994Sdes (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); 95289994Sdes if (hostname) 95389994Sdes (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); 95489994Sdes (void)write(fd, (char *)&ll, sizeof(ll)); 95589994Sdes (void)close(fd); 95689994Sdes } 95789994Sdes} 95889994Sdes 95989994Sdesvoid 96089994Sdesbadlogin(name) 96189994Sdes char *name; 96289994Sdes{ 96389994Sdes 96489994Sdes if (failures == 0) 96589994Sdes return; 96689994Sdes if (hostname) { 96789994Sdes syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s", 96889994Sdes failures, failures > 1 ? "S" : "", full_hostname); 96989994Sdes syslog(LOG_AUTHPRIV|LOG_NOTICE, 97089994Sdes "%d LOGIN FAILURE%s FROM %s, %s", 97189994Sdes failures, failures > 1 ? "S" : "", full_hostname, name); 97289994Sdes } else { 973165152Scsjp syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s", 974157215Scognet failures, failures > 1 ? "S" : "", tty); 975157215Scognet syslog(LOG_AUTHPRIV|LOG_NOTICE, 976165152Scsjp "%d LOGIN FAILURE%s ON %s, %s", 97789994Sdes failures, failures > 1 ? "S" : "", tty, name); 9781590Srgrimes } 9791590Srgrimes} 980 981#undef UNKNOWN 982#define UNKNOWN "su" 983 984char * 985stypeof(ttyid) 986 char *ttyid; 987{ 988 989 struct ttyent *t; 990 991 return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN); 992} 993 994void 995refused(msg, rtype, lout) 996 char *msg; 997 char *rtype; 998 int lout; 999{ 1000 1001 if (msg != NULL) 1002 printf("%s.\n", msg); 1003 if (hostname) 1004 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s", 1005 pwd->pw_name, rtype, full_hostname, tty); 1006 else 1007 syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s", 1008 pwd->pw_name, rtype, tty); 1009 if (lout) 1010 sleepexit(1); 1011} 1012 1013void 1014sleepexit(eval) 1015 int eval; 1016{ 1017 1018 (void)sleep(5); 1019 exit(eval); 1020} 1021