version 1.35, 2000/10/14 20:33:13 |
version 1.36, 2001/05/29 21:39:26 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
*/ |
*/ |
|
/*- |
|
* Copyright (c) 1995 Berkeley Software Design, Inc. All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* 3. All advertising materials mentioning features or use of this software |
|
* must display the following acknowledgement: |
|
* This product includes software developed by Berkeley Software Design, |
|
* Inc. |
|
* 4. The name of Berkeley Software Design, Inc. may not be used to endorse |
|
* or promote products derived from this software without specific prior |
|
* written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
* ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
* SUCH DAMAGE. |
|
* |
|
* BSDI $From: login.c,v 2.28 1999/09/08 22:35:36 prb Exp $ |
|
*/ |
|
|
#ifndef lint |
#ifndef lint |
static char copyright[] = |
static char copyright[] = |
|
|
*/ |
*/ |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
|
#include <sys/socket.h> |
#include <sys/stat.h> |
#include <sys/stat.h> |
#include <sys/time.h> |
#include <sys/time.h> |
#include <sys/resource.h> |
#include <sys/resource.h> |
|
|
#include <fcntl.h> |
#include <fcntl.h> |
#include <grp.h> |
#include <grp.h> |
#include <login_cap.h> |
#include <login_cap.h> |
|
#include <netdb.h> |
#include <pwd.h> |
#include <pwd.h> |
#include <setjmp.h> |
#include <setjmp.h> |
#include <signal.h> |
#include <signal.h> |
|
#include <stdarg.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
|
|
#include <unistd.h> |
#include <unistd.h> |
#include <utmp.h> |
#include <utmp.h> |
#include <util.h> |
#include <util.h> |
#include <skey.h> |
#include <bsd_auth.h> |
|
|
|
#ifdef KERBEROS |
|
#include <kerberosIV/kafs.h> |
|
#endif |
|
|
#include "pathnames.h" |
#include "pathnames.h" |
|
|
void badlogin __P((char *)); |
void badlogin __P((char *)); |
void checknologin __P((void)); |
|
void dolastlog __P((int)); |
void dolastlog __P((int)); |
void getloginname __P((void)); |
void getloginname __P((void)); |
void motd __P((void)); |
void motd __P((void)); |
|
void quickexit __P((int)); |
int rootterm __P((char *)); |
int rootterm __P((char *)); |
void sigint __P((int)); |
void sigint __P((int)); |
void sighup __P((int)); |
void sighup __P((int)); |
void sleepexit __P((int)); |
void sleepexit __P((int)); |
char *stypeof __P((char *)); |
char *stypeof __P((char *)); |
void timedout __P((int)); |
void timedout __P((int)); |
int pwcheck __P((char *, char *, char *, char *)); |
|
#if defined(KERBEROS) || defined(KERBEROS5) |
|
int klogin __P((struct passwd *, char *, char *, char *)); |
|
void kdestroy __P((void)); |
|
void dofork __P((void)); |
|
void kgettokens __P((char *)); |
|
#endif |
|
|
|
extern int check_failedlogin __P((uid_t)); |
extern int check_failedlogin __P((uid_t)); |
extern void log_failedlogin __P((uid_t, char *, char *, char *)); |
extern void log_failedlogin __P((uid_t, char *, char *, char *)); |
|
|
*/ |
*/ |
u_int timeout = 300; |
u_int timeout = 300; |
|
|
#if defined(KERBEROS) || defined(KERBEROS5) |
struct passwd *pwd; |
int notickets = 1; |
|
char *instance; |
|
char *krbtkfile_env; |
|
int authok; |
|
#endif |
|
|
|
struct passwd *pwd; |
|
login_cap_t *lc = NULL; |
login_cap_t *lc = NULL; |
|
auth_session_t *as = NULL; |
int failures; |
int failures; |
|
int needbanner = 1; |
char term[64], *hostname, *tty; |
char term[64], *hostname, *tty; |
|
char *style; |
char *username = NULL, *rusername = NULL; |
char *username = NULL, *rusername = NULL; |
|
|
int |
int |
|
|
char *argv[]; |
char *argv[]; |
{ |
{ |
extern char **environ; |
extern char **environ; |
|
struct addrinfo *ai, hints; |
struct group *gr; |
struct group *gr; |
|
struct rlimit cds, scds; |
struct stat st; |
struct stat st; |
struct timeval tp; |
|
struct utmp utmp; |
struct utmp utmp; |
int ask, ch, cnt, fflag, hflag, pflag, uflag, quietlog, rootlogin, rval; |
quad_t expire, warning; |
uid_t uid; |
uid_t uid; |
char *domain, *p, *salt, *ttyn, *shell; |
int ask, ch, cnt, fflag, pflag, quietlog, rootlogin, lastchance; |
|
int error, homeless, needto, authok; |
|
char *domain, *p, *ttyn, *shell, *fullname, *instance; |
|
char *lipaddr, *script, *ripaddr, *style, *type, *fqdn, *copyright; |
char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; |
char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; |
char localhost[MAXHOSTNAMELEN]; |
char localhost[MAXHOSTNAMELEN]; |
|
|
(void)signal(SIGALRM, timedout); |
(void)signal(SIGALRM, timedout); |
(void)alarm(timeout); |
if (argc > 1) { |
|
needto = 0; |
|
(void)alarm(timeout); |
|
} else |
|
needto = 1; |
(void)signal(SIGQUIT, SIG_IGN); |
(void)signal(SIGQUIT, SIG_IGN); |
(void)signal(SIGINT, SIG_IGN); |
(void)signal(SIGINT, SIG_IGN); |
(void)signal(SIGHUP, sighup); |
(void)signal(SIGHUP, SIG_IGN); |
(void)setpriority(PRIO_PROCESS, 0, 0); |
(void)setpriority(PRIO_PROCESS, 0, 0); |
|
|
openlog("login", LOG_ODELAY, LOG_AUTH); |
openlog("login", LOG_ODELAY, LOG_AUTH); |
|
|
|
fqdn = lipaddr = ripaddr = fullname = NULL; |
|
|
/* |
/* |
|
* Since login deals with sensitive information, turn off coredumps. |
|
*/ |
|
if (getrlimit(RLIMIT_CORE, &scds) < 0) { |
|
syslog(LOG_ERR, "couldn't get core dump size: %m"); |
|
scds.rlim_cur = scds.rlim_max = QUAD_MIN; |
|
} |
|
cds.rlim_cur = cds.rlim_max = 0; |
|
if (setrlimit(RLIMIT_CORE, &cds) < 0) { |
|
syslog(LOG_ERR, "couldn't set core dump size to 0: %m"); |
|
scds.rlim_cur = scds.rlim_max = QUAD_MIN; |
|
} |
|
|
|
/* |
* -p is used by getty to tell login not to destroy the environment |
* -p is used by getty to tell login not to destroy the environment |
* -f is used to skip a second login authentication |
* -f is used to skip a second login authentication |
* -h is used by other servers to pass the name of the remote |
* -h is used by other servers to pass the name of the remote |
|
|
domain = localhost; |
domain = localhost; |
} |
} |
|
|
fflag = hflag = pflag = 0; |
if ((as = auth_open()) == NULL) { |
|
syslog(LOG_ERR, "%m"); |
|
err(1, NULL); |
|
} |
|
|
|
fflag = pflag = 0; |
uid = getuid(); |
uid = getuid(); |
while ((ch = getopt(argc, argv, "fh:u:p")) != -1) |
while ((ch = getopt(argc, argv, "fh:pu:L:R:")) != -1) |
switch (ch) { |
switch (ch) { |
case 'f': |
case 'f': |
fflag = 1; |
fflag = 1; |
break; |
break; |
case 'h': |
case 'h': |
if (uid) |
if (uid) { |
errx(1, "-h option: %s", strerror(EPERM)); |
warn("-h option: %s", strerror(EPERM)); |
hflag = 1; |
quickexit(1); |
|
} |
|
if ((fqdn = strdup(optarg)) == NULL) { |
|
warn(NULL); |
|
quickexit(1); |
|
} |
|
auth_setoption(as, "fqdn", fqdn); |
if (domain && (p = strchr(optarg, '.')) && |
if (domain && (p = strchr(optarg, '.')) && |
strcasecmp(p+1, domain) == 0) |
strcasecmp(p+1, domain) == 0) |
*p = 0; |
*p = 0; |
hostname = optarg; |
hostname = optarg; |
|
auth_setoption(as, "hostname", hostname); |
break; |
break; |
|
case 'L': |
|
if (uid) { |
|
warnx("-L option: %s", strerror(EPERM)); |
|
quickexit(1); |
|
} |
|
if (lipaddr) { |
|
warnx("duplicate -L option"); |
|
quickexit(1); |
|
} |
|
lipaddr = optarg; |
|
memset(&hints, 0, sizeof(hints)); |
|
hints.ai_family = PF_UNSPEC; |
|
hints.ai_flags = AI_CANONNAME; |
|
error = getaddrinfo(lipaddr, NULL, &hints, &ai); |
|
if (!error) { |
|
strlcpy(localhost, ai->ai_canonname, |
|
sizeof(localhost)); |
|
freeaddrinfo(ai); |
|
} else |
|
strlcpy(localhost, lipaddr, sizeof(localhost)); |
|
auth_setoption(as, "local_addr", lipaddr); |
|
break; |
case 'p': |
case 'p': |
pflag = 1; |
pflag = 1; |
break; |
break; |
|
case 'R': |
|
if (uid) { |
|
warnx("-R option: %s", strerror(EPERM)); |
|
quickexit(1); |
|
} |
|
if (ripaddr) { |
|
warnx("duplicate -R option"); |
|
quickexit(1); |
|
} |
|
ripaddr = optarg; |
|
auth_setoption(as, "remote_addr", ripaddr); |
|
break; |
case 'u': |
case 'u': |
if (uid) |
if (uid) { |
errx(1, "-u option: %s", strerror(EPERM)); |
warnx("-u option: %s", strerror(EPERM)); |
uflag = 1; |
quickexit(1); |
|
} |
rusername = optarg; |
rusername = optarg; |
break; |
break; |
case '?': |
|
default: |
default: |
if (!uid) |
if (!uid) |
syslog(LOG_ERR, "invalid flag %c", ch); |
syslog(LOG_ERR, "invalid flag %c", ch); |
(void)fprintf(stderr, |
(void)fprintf(stderr, |
"usage: login [-fp] [-h hostname] [username]\n"); |
"usage: login [-fp] [-h hostname] [-L lipaddr] [-R ripaddr] [username]\n"); |
exit(1); |
quickexit(1); |
} |
} |
argc -= optind; |
argc -= optind; |
argv += optind; |
argv += optind; |
|
|
else |
else |
tty = ttyn; |
tty = ttyn; |
|
|
for (cnt = 0;; ask = 1) { |
#ifdef notyet |
#if defined(KERBEROS) || defined(KERBEROS5) |
/* XXX - we don't (yet) support per-tty auth stuff */ |
kdestroy(); |
/* BSDi uses a ttys.conf file but we could just overload /etc/ttys */ |
authok = 0; |
/* |
|
* Classify the attempt. |
|
* By default we use the value in the ttys file. |
|
* If there is a classify script we run that as |
|
* |
|
* classify [-f] [username] |
|
*/ |
|
if (type = getttyauth(tty)) |
|
auth_setoption(as, "auth_type", type); |
#endif |
#endif |
|
|
|
/* get the default login class */ |
|
if ((lc = login_getclass(0)) == NULL) { /* get the default class */ |
|
warnx("Failure to retrieve default class"); |
|
quickexit(1); |
|
} |
|
if ((script = login_getcapstr(lc, "classify", NULL, NULL)) != NULL) { |
|
unsetenv("AUTH_TYPE"); |
|
unsetenv("REMOTE_NAME"); |
|
if (script[0] != '/') { |
|
syslog(LOG_ERR, "Invalid classify script: %s", script); |
|
warnx("Classification failure"); |
|
quickexit(1); |
|
} |
|
shell = strrchr(script, '/') + 1; |
|
auth_setstate(as, AUTH_OKAY); |
|
auth_call(as, script, shell, |
|
fflag ? "-f" : username, fflag ? username : 0, 0); |
|
if (!(auth_getstate(as) & AUTH_ALLOW)) |
|
quickexit(1); |
|
auth_setenv(as); |
|
if ((p = getenv("AUTH_TYPE")) != NULL && |
|
strncmp(p, "auth-", 5) == 0) |
|
type = p; |
|
if ((p = getenv("REMOTE_NAME")) != NULL) |
|
hostname = p; |
|
/* |
|
* we may have changed some values, reset them |
|
*/ |
|
auth_clroptions(as); |
|
if (type) |
|
auth_setoption(as, "auth_type", type); |
|
if (fqdn) |
|
auth_setoption(as, "fqdn", fqdn); |
|
if (hostname) |
|
auth_setoption(as, "hostname", hostname); |
|
if (lipaddr) |
|
auth_setoption(as, "local_addr", lipaddr); |
|
if (ripaddr) |
|
auth_setoption(as, "remote_addr", ripaddr); |
|
} |
|
|
|
/* |
|
* Request the things like the approval script print things |
|
* to stdout (in particular, the nologins files) |
|
*/ |
|
auth_setitem(as, AUTHV_INTERACTIVE, "True"); |
|
|
|
for (cnt = 0;; ask = 1) { |
|
/* |
|
* Clean up our current authentication session. |
|
* Options are not cleared so we need to clear any |
|
* we might set below. |
|
*/ |
|
auth_clean(as); |
|
auth_clroption(as, "style"); |
|
auth_clroption(as, "lastchance"); |
|
|
|
lastchance = 0; |
|
|
if (ask) { |
if (ask) { |
fflag = 0; |
fflag = 0; |
getloginname(); |
getloginname(); |
} |
} |
|
if (needto) { |
|
needto = 0; |
|
alarm(timeout); |
|
} |
|
if ((style = strchr(username, ':')) != NULL) |
|
*style++ = '\0'; |
|
if (fullname) |
|
free(fullname); |
|
if (auth_setitem(as, AUTHV_NAME, username) < 0 || |
|
(fullname = strdup(username)) == NULL) { |
|
syslog(LOG_ERR, "%m"); |
|
warn(NULL); |
|
quickexit(1); |
|
} |
rootlogin = 0; |
rootlogin = 0; |
|
/* XXX - kerb5 uses a '/' not a '.' ??? */ |
#if defined(KERBEROS) || defined(KERBEROS5) |
|
/* |
|
* Why should anyone with a root instance be able |
|
* to be root here? |
|
*/ |
|
instance = ""; |
|
#endif |
|
#ifdef KERBEROS |
|
if ((instance = strchr(username, '.')) != NULL) { |
if ((instance = strchr(username, '.')) != NULL) { |
if (strncmp(instance, ".root", 5) == 0) |
if (strncmp(instance, ".root", 5) == 0) |
rootlogin = 1; |
rootlogin = 1; |
*instance++ = '\0'; |
*instance++ = '\0'; |
} else |
} else |
instance = ""; |
instance = ""; |
#endif |
|
#ifdef KERBEROS5 |
|
if ((instance = strchr(username, '/')) != NULL) { |
|
if (strncmp(instance, "/root", 5) == 0) |
|
rootlogin = 1; |
|
*instance++ = '\0'; |
|
} else |
|
instance = ""; |
|
#endif |
|
if (strlen(username) > UT_NAMESIZE) |
if (strlen(username) > UT_NAMESIZE) |
username[UT_NAMESIZE] = '\0'; |
username[UT_NAMESIZE] = '\0'; |
|
|
|
|
badlogin(tbuf); |
badlogin(tbuf); |
failures = 0; |
failures = 0; |
} |
} |
(void)strlcpy(tbuf, username, sizeof tbuf); |
(void)strlcpy(tbuf, username, sizeof(tbuf)); |
|
|
if ((pwd = getpwnam(username))) |
if ((pwd = getpwnam(username)) != NULL && |
salt = pwd->pw_passwd; |
auth_setpwd(as, pwd) < 0) { |
else |
syslog(LOG_ERR, "%m"); |
salt = "xx"; |
warn(NULL); |
lc = login_getclass(pwd ? pwd->pw_class : LOGIN_DEFCLASS); |
quickexit(1); |
if (!lc) |
} |
err(1, "unable to get login class"); |
|
|
|
/* |
lc = login_getclass(pwd ? pwd->pw_class : NULL); |
* If we have a valid account name, and it doesn't have a |
|
* password, or the -f option was specified and the caller |
|
* is root or the caller isn't changing their uid, don't |
|
* authenticate. |
|
*/ |
|
if (pwd) { |
|
if (pwd->pw_uid == 0) |
|
rootlogin = 1; |
|
|
|
if (fflag && (uid == 0 || uid == pwd->pw_uid)) { |
if (!lc) |
/* already authenticated */ |
goto failed; |
break; |
|
} else if (pwd->pw_passwd[0] == '\0') { |
|
/* pretend password okay */ |
|
rval = 0; |
|
#if defined(KERBEROS) || defined(KERBEROS5) |
|
authok = 1; |
|
#endif |
|
goto ttycheck; |
|
} |
|
} |
|
|
|
fflag = 0; |
style = login_getstyle(lc, style, type); |
|
|
(void)setpriority(PRIO_PROCESS, 0, -4); |
if (!style) |
|
goto failed; |
|
|
p = getpass("Password:"); |
/* |
|
* Turn off the fflag if we have an an invalid user |
|
* or we are not root and we are trying to change uids. |
|
*/ |
|
if (!pwd || (uid && uid != pwd->pw_uid)) |
|
fflag = 0; |
|
|
if (pwd) { |
if (pwd && pwd->pw_uid == 0) |
#if defined(KERBEROS) || defined(KERBEROS5) |
rootlogin = 1; |
rval = klogin(pwd, instance, localhost, p); |
|
if (rval != 0 && rootlogin && pwd->pw_uid != 0) |
|
rootlogin = 0; |
|
if (rval == 1) { |
|
/* Fall back on password file. */ |
|
if (pwd->pw_uid != 0) |
|
rootlogin = 0; |
|
rval = pwcheck(username, p, salt, pwd->pw_passwd); |
|
} |
|
if (rval == 0) |
|
authok = 1; |
|
#else |
|
rval = pwcheck(username, p, salt, pwd->pw_passwd); |
|
#endif |
|
} else { |
|
#ifdef SKEY |
|
if (strcasecmp(p, "s/key") == 0) |
|
(void)skey_authenticate(username); |
|
else |
|
#endif |
|
{ |
|
useconds_t us; |
|
|
|
/* |
/* |
* Sleep between 1 and 3 seconds |
* If we do not have the force flag authenticate the user |
* to emulate a crypt. |
*/ |
*/ |
if (fflag) |
us = arc4random() % 3000000; |
authok = AUTH_SECURE; |
usleep(us); |
else { |
|
lastchance = |
|
login_getcaptime(lc, "password-dead", 0, 0) != 0; |
|
if (lastchance) |
|
auth_setoption(as, "lastchance", "yes"); |
|
/* |
|
* Once we start asking for a password |
|
* we want to log a failure on a hup. |
|
*/ |
|
signal(SIGHUP, sighup); |
|
auth_verify(as, style, NULL, lc->lc_class, NULL); |
|
authok = auth_getstate(as); |
|
/* |
|
* If their password expired and it has not been |
|
* too long since then, give the user one last |
|
* chance to change their password |
|
*/ |
|
if ((authok & AUTH_PWEXPIRED) && lastchance) { |
|
authok = AUTH_OKAY; |
|
} else |
|
lastchance = 0; |
|
if ((authok & AUTH_ALLOW) == 0) |
|
goto failed; |
|
if (auth_setoption(as, "style", style) < 0) { |
|
syslog(LOG_ERR, "%m"); |
|
warn(NULL); |
|
quickexit(1); |
} |
} |
rval = 1; |
|
} |
} |
memset(p, 0, strlen(p)); |
|
|
|
(void)setpriority(PRIO_PROCESS, 0, 0); |
|
|
|
ttycheck: |
|
/* |
/* |
* If trying to log in as root without Kerberos, |
* explicitly reject users without password file entries |
* but with insecure terminal, refuse the login attempt. |
|
*/ |
*/ |
#if defined(KERBEROS) || defined(KERBEROS5) |
if (pwd == 0) |
if (authok == 1) |
goto failed; |
#endif |
|
/* if logging in as root, user must be on a secure tty */ |
|
if (pwd && rval == 0 && (!rootlogin || rootterm(tty))) |
|
break; |
|
|
|
|
authok &= AUTH_SECURE; |
|
|
/* |
/* |
* We don't want to give out info to an attacker trying |
* If trying to log in as root on an insecure terminal, |
* to guess root's password so we always say "login refused" |
* refuse the login attempt unless the authentication |
* in that case, not "Login incorrect". |
* style explicitly says a root login is okay. |
*/ |
*/ |
|
if (authok == 0 && pwd && rootlogin && !rootterm(tty)) |
|
goto failed; |
|
|
|
if (fflag) { |
|
type = 0; |
|
style = "forced"; |
|
} |
|
break; |
|
|
|
failed: |
|
if (authok & AUTH_SILENT) |
|
quickexit(0); |
if (rootlogin && !rootterm(tty)) { |
if (rootlogin && !rootterm(tty)) { |
(void)fprintf(stderr, |
warnx("%s login refused on this terminal.", |
"%s login refused on this terminal.\n", |
fullname); |
pwd ? pwd->pw_name : "root"); |
|
if (hostname) |
if (hostname) |
syslog(LOG_NOTICE, |
syslog(LOG_NOTICE, |
"LOGIN %s REFUSED FROM %s%s%s ON TTY %s", |
"LOGIN %s REFUSED FROM %s%s%s ON TTY %s", |
pwd ? pwd->pw_name : "root", |
fullname, rusername ? rusername : "", |
rusername ? rusername : "", |
|
rusername ? "@" : "", hostname, tty); |
rusername ? "@" : "", hostname, tty); |
else |
else |
syslog(LOG_NOTICE, |
syslog(LOG_NOTICE, |
"LOGIN %s REFUSED ON TTY %s", |
"LOGIN %s REFUSED ON TTY %s", |
pwd ? pwd->pw_name : "root", tty); |
fullname, tty); |
} else |
} else { |
(void)printf("Login incorrect\n"); |
if (!as || (p = auth_getvalue(as, "errormsg")) == NULL) |
|
p = "Login incorrect"; |
|
(void)printf("%s\n", p); |
|
} |
failures++; |
failures++; |
if (pwd) |
if (pwd) |
log_failedlogin(pwd->pw_uid, hostname, rusername, tty); |
log_failedlogin(pwd->pw_uid, hostname, rusername, tty); |
/* we allow 10 tries, but after 3 we start backing off */ |
/* we allow 10 tries, but after 3 we start backing off */ |
|
/* XXX - should be configurable */ |
if (++cnt > 3) { |
if (++cnt > 3) { |
if (cnt >= 10) { |
if (cnt >= 10) { |
badlogin(username); |
badlogin(username); |
|
|
} |
} |
|
|
/* committed to login -- turn off timeout */ |
/* committed to login -- turn off timeout */ |
(void)alarm((u_int)0); |
(void)alarm(0); |
|
|
endpwent(); |
endpwent(); |
|
|
/* if user not super-user, check for disabled logins */ |
|
if (!rootlogin) |
|
checknologin(); |
|
|
|
setegid(pwd->pw_gid); |
|
seteuid(pwd->pw_uid); |
|
|
|
if (chdir(pwd->pw_dir) < 0) { |
|
(void)printf("No home directory %s!\n", pwd->pw_dir); |
|
if (login_getcapbool(lc, "requirehome", 0)) |
|
exit(1); |
|
if (chdir("/")) |
|
exit(0); |
|
pwd->pw_dir = "/"; |
|
(void)printf("Logging in with home = \"/\".\n"); |
|
} |
|
|
|
shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell); |
shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell); |
if (*shell == '\0') |
if (*shell == '\0') |
shell = _PATH_BSHELL; |
shell = _PATH_BSHELL; |
else if (strlen(shell) >= MAXPATHLEN) { |
else if (strlen(shell) >= MAXPATHLEN) { |
syslog(LOG_ERR, "shell path too long: %s", shell); |
syslog(LOG_ERR, "shell path too long: %s", shell); |
warnx("invalid shell"); |
warnx("invalid shell"); |
sleepexit(1); |
quickexit(1); |
} |
} |
|
|
quietlog = ((strcmp(pwd->pw_shell, "/sbin/nologin") == 0) || |
|
login_getcapbool(lc, "hushlogin", 0) || |
|
(access(_PATH_HUSHLOGIN, F_OK) == 0)); |
|
|
|
seteuid(0); |
|
setegid(0); /* XXX use a saved gid instead? */ |
|
|
|
if (pwd->pw_change || pwd->pw_expire) |
|
(void)gettimeofday(&tp, (struct timezone *)NULL); |
|
if (pwd->pw_expire) { |
|
if (tp.tv_sec >= pwd->pw_expire) { |
|
(void)printf("Sorry -- your account has expired.\n"); |
|
sleepexit(1); |
|
} else if (!quietlog &&pwd->pw_expire - tp.tv_sec < |
|
login_getcaptime(lc, "expire-warn", |
|
2 * DAYSPERWEEK * SECSPERDAY, 2 * DAYSPERWEEK * SECSPERDAY)) |
|
(void)printf("Warning: your account expires on %s", |
|
ctime(&pwd->pw_expire)); |
|
} |
|
if (pwd->pw_change) { |
|
if (tp.tv_sec >= pwd->pw_change) { |
|
(void)printf("Sorry -- your password has expired.\n"); |
|
sleepexit(1); |
|
} else if (!quietlog && pwd->pw_change - tp.tv_sec < |
|
login_getcaptime(lc, "password-warn", |
|
2 * DAYSPERWEEK * SECSPERDAY, 2 * DAYSPERWEEK * SECSPERDAY)) |
|
(void)printf("Warning: your password expires on %s", |
|
ctime(&pwd->pw_change)); |
|
} |
|
|
|
/* Nothing else left to fail -- really log in. */ |
|
(void)signal(SIGHUP, SIG_DFL); |
|
memset((void *)&utmp, 0, sizeof(utmp)); |
|
(void)time(&utmp.ut_time); |
|
(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); |
|
if (hostname) |
|
(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); |
|
(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); |
|
login(&utmp); |
|
|
|
if (!quietlog) |
|
(void)check_failedlogin(pwd->pw_uid); |
|
dolastlog(quietlog); |
|
|
|
login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); |
|
|
|
(void)chown(ttyn, pwd->pw_uid, |
|
(gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); |
|
#if defined(KERBEROS) || defined(KERBEROS5) |
|
/* Fork so that we can call kdestroy */ |
|
if (krbtkfile_env) |
|
dofork(); |
|
#endif |
|
|
|
/* Destroy environment unless user has requested its preservation. */ |
/* Destroy environment unless user has requested its preservation. */ |
if (!pflag) { |
if (!pflag) { |
if ((environ = calloc(1, sizeof (char *))) == NULL) |
if ((environ = calloc(1, sizeof (char *))) == NULL) |
|
|
if (setenv("HOME", pwd->pw_dir, 1) == -1 || |
if (setenv("HOME", pwd->pw_dir, 1) == -1 || |
setenv("SHELL", shell, 1) == -1) { |
setenv("SHELL", shell, 1) == -1) { |
warn("unable to setenv()"); |
warn("unable to setenv()"); |
exit(1); |
quickexit(1); |
} |
} |
if (term[0] == '\0') |
if (term[0] == '\0') |
(void)strlcpy(term, stypeof(tty), sizeof(term)); |
(void)strlcpy(term, stypeof(tty), sizeof(term)); |
|
|
setenv("LOGNAME", pwd->pw_name, 1) == -1 || |
setenv("LOGNAME", pwd->pw_name, 1) == -1 || |
setenv("USER", pwd->pw_name, 1) == -1) { |
setenv("USER", pwd->pw_name, 1) == -1) { |
warn("unable to setenv()"); |
warn("unable to setenv()"); |
exit(1); |
quickexit(1); |
} |
} |
if (hostname) { |
if (hostname) { |
if (setenv("REMOTEHOST", hostname, 1) == -1) { |
if (setenv("REMOTEHOST", hostname, 1) == -1) { |
warn("unable to setenv()"); |
warn("unable to setenv()"); |
exit(1); |
quickexit(1); |
} |
} |
} |
} |
if (rusername) { |
if (rusername) { |
if (setenv("REMOTEUSER", rusername, 1) == -1) { |
if (setenv("REMOTEUSER", rusername, 1) == -1) { |
warn("unable to setenv()"); |
warn("unable to setenv()"); |
exit(1); |
quickexit(1); |
} |
} |
} |
} |
#ifdef KERBEROS |
|
if (krbtkfile_env) { |
if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH)) { |
if (setenv("KRBTKFILE", krbtkfile_env, 1) == -1) { |
warn("unable to set user context"); |
warn("unable to setenv()"); |
quickexit(1); |
exit(1); |
|
} |
|
} |
} |
#endif |
auth_setenv(as); |
#ifdef KERBEROS5 |
|
if (krbtkfile_env) { |
/* if user not super-user, check for disabled logins */ |
if (setenv("KRB5CCNAME", krbtkfile_env, 1) == -1) { |
if (!rootlogin) |
warn("unable to setenv()"); |
auth_checknologin(lc); |
exit(1); |
|
|
setegid(pwd->pw_gid); |
|
seteuid(pwd->pw_uid); |
|
|
|
homeless = chdir(pwd->pw_dir); |
|
if (homeless) { |
|
if (login_getcapbool(lc, "requirehome", 0)) { |
|
(void)printf("No home directory %s!\n", pwd->pw_dir); |
|
quickexit(1); |
} |
} |
|
if (chdir("/")) |
|
quickexit(0); |
} |
} |
#endif |
|
|
quietlog = ((strcmp(pwd->pw_shell, "/sbin/nologin") == 0) || |
|
login_getcapbool(lc, "hushlogin", 0) || |
|
(access(_PATH_HUSHLOGIN, F_OK) == 0)); |
|
|
|
seteuid(0); |
|
setegid(0); /* XXX use a saved gid instead? */ |
|
|
|
if ((p = auth_getvalue(as, "warnmsg")) != NULL) |
|
(void)printf("WARNING: %s\n\n", p); |
|
|
|
expire = auth_check_expire(as); |
|
if (expire < 0) { |
|
(void)printf("Sorry -- your account has expired.\n"); |
|
quickexit(1); |
|
} else if (expire > 0 && !quietlog) { |
|
warning = login_getcaptime(lc, "expire-warn", |
|
2 * DAYSPERWEEK * SECSPERDAY, 2 * DAYSPERWEEK * SECSPERDAY); |
|
if (expire < warning) |
|
(void)printf("Warning: your account expires on %s", |
|
ctime(&pwd->pw_expire)); |
|
} |
|
|
|
/* Nothing else left to fail -- really log in. */ |
|
(void)signal(SIGHUP, SIG_DFL); |
|
memset(&utmp, 0, sizeof(utmp)); |
|
(void)time(&utmp.ut_time); |
|
(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); |
|
if (hostname) |
|
(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); |
|
(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); |
|
login(&utmp); |
|
|
|
if (!quietlog) |
|
(void)check_failedlogin(pwd->pw_uid); |
|
dolastlog(quietlog); |
|
|
|
login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); |
|
|
|
(void)chown(ttyn, pwd->pw_uid, |
|
(gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); |
|
|
/* If fflag is on, assume caller/authenticator has logged root login. */ |
/* If fflag is on, assume caller/authenticator has logged root login. */ |
if (rootlogin && fflag == 0) { |
if (rootlogin && fflag == 0) { |
if (hostname) |
if (hostname) |
|
|
syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty); |
syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty); |
} |
} |
|
|
#if defined(KERBEROS) || defined(KERBEROS5) |
|
if (!quietlog && notickets == 1) |
|
(void)printf("Warning: no Kerberos tickets issued.\n"); |
|
#endif |
|
|
|
if (!quietlog) { |
if (!quietlog) { |
#if 0 |
#if 0 |
(void)printf("%s\n\t%s %s\n\n", |
(void)printf("%s\n\t%s %s\n\n", |
|
|
"The Regents of the University of California. ", |
"The Regents of the University of California. ", |
"All rights reserved."); |
"All rights reserved."); |
#endif |
#endif |
|
if ((copyright = |
|
login_getcapstr(lc, "copyright", NULL, NULL)) != NULL) |
|
auth_cat(copyright); |
motd(); |
motd(); |
(void)snprintf(tbuf, |
(void)snprintf(tbuf, |
sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name); |
sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name); |
|
|
|
|
(void)signal(SIGALRM, SIG_DFL); |
(void)signal(SIGALRM, SIG_DFL); |
(void)signal(SIGQUIT, SIG_DFL); |
(void)signal(SIGQUIT, SIG_DFL); |
|
(void)signal(SIGHUP, SIG_DFL); |
(void)signal(SIGINT, SIG_DFL); |
(void)signal(SIGINT, SIG_DFL); |
(void)signal(SIGTSTP, SIG_IGN); |
(void)signal(SIGTSTP, SIG_IGN); |
|
|
tbuf[0] = '-'; |
tbuf[0] = '-'; |
(void)strlcpy(tbuf + 1, (p = strrchr(shell, '/')) ? |
(void)strlcpy(tbuf + 1, (p = strrchr(shell, '/')) ? |
p + 1 : shell, sizeof tbuf - 1); |
p + 1 : shell, sizeof(tbuf) - 1); |
|
|
/* Discard permissions last so can't get killed and drop core. */ |
if ((scds.rlim_cur != QUAD_MIN || scds.rlim_max != QUAD_MIN) && |
if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL)) { |
setrlimit(RLIMIT_CORE, &scds) < 0) |
|
syslog(LOG_ERR, "couldn't reset core dump size: %m"); |
|
|
|
if (lastchance) |
|
(void)printf("WARNING: Your password has expired. You must change your password, now!\n"); |
|
|
|
if (setusercontext(lc, pwd, pwd->pw_uid, |
|
LOGIN_SETALL & ~LOGIN_SETPATH) < 0) { |
warn("unable to set user context"); |
warn("unable to set user context"); |
exit(1); |
quickexit(1); |
} |
} |
|
|
|
if (homeless) { |
|
(void)printf("No home directory %s!\n", pwd->pw_dir); |
|
(void)printf("Logging in with home = \"/\".\n"); |
|
(void)setenv("HOME", "/", 1); |
|
} |
|
|
|
if (auth_approval(as, lc, NULL, "login") == 0) { |
|
if (auth_getstate(as) & AUTH_EXPIRED) |
|
(void)printf("Sorry -- your account has expired.\n"); |
|
else |
|
(void)printf("approval failure\n"); |
|
quickexit(1); |
|
} |
|
|
|
/* |
|
* The last thing we do is discard all of the open file descriptors. |
|
* Last because the C library may have some open. |
|
* |
|
* XXX |
|
* Assume that stdin, stdout and stderr are 0, 1 and 2, and that |
|
* STDERR_FILENO is 2. |
|
*/ |
|
for (cnt = getdtablesize(); cnt > STDERR_FILENO; cnt--) |
|
(void)close(cnt); |
|
|
|
/* |
|
* Close the authentication session, make sure it is marked |
|
* as okay so no files are removed. |
|
*/ |
|
auth_setstate(as, AUTH_OKAY); |
|
auth_close(as); |
|
|
#ifdef KERBEROS |
#ifdef KERBEROS |
kgettokens(pwd->pw_dir); |
kgettokens(pwd->pw_dir); |
#endif |
#endif |
|
|
execlp(shell, tbuf, 0); |
execlp(shell, tbuf, 0); |
err(1, "%s", shell); |
err(1, "%s", shell); |
} |
} |
|
|
int |
|
pwcheck(user, p, salt, passwd) |
|
char *user, *p, *salt, *passwd; |
|
{ |
|
#ifdef SKEY |
|
if (strcasecmp(p, "s/key") == 0) |
|
return skey_authenticate(user); |
|
#endif |
|
return strcmp(crypt(p, salt), passwd); |
|
} |
|
|
|
#if defined(KERBEROS) || defined(KERBEROS5) |
|
#define NBUFSIZ (UT_NAMESIZE + 1 + 5) /* .root suffix */ |
|
#else |
|
#define NBUFSIZ (UT_NAMESIZE + 1) |
|
#endif |
|
|
|
#if defined(KERBEROS) || defined(KERBEROS5) |
|
/* |
/* |
* This routine handles cleanup stuff, and the like. |
* Allow for a '.' and 16 characters for any instance as well as |
* It exists only in the child process. |
* space for a ':' and 16 charcters defining the authentication type. |
*/ |
*/ |
#include <sys/wait.h> |
#define NBUFSIZ (UT_NAMESIZE + 1 + 16 + 1 + 16) |
void |
|
dofork() |
|
{ |
|
int child; |
|
|
|
if (!(child = fork())) |
|
return; /* Child process */ |
|
|
|
/* Setup stuff? This would be things we could do in parallel with login */ |
|
(void) chdir("/"); /* Let's not keep the fs busy... */ |
|
|
|
/* If we're the parent, watch the child until it dies */ |
|
while (wait(0) != child) |
|
; |
|
|
|
/* Cleanup stuff */ |
|
/* Run kdestroy to destroy tickets */ |
|
kdestroy(); |
|
|
|
/* Leave */ |
|
exit(0); |
|
} |
|
#endif |
|
|
|
void |
void |
getloginname() |
getloginname() |
{ |
{ |
|
|
for (p = nbuf; (ch = getchar()) != '\n'; ) { |
for (p = nbuf; (ch = getchar()) != '\n'; ) { |
if (ch == EOF) { |
if (ch == EOF) { |
badlogin(username); |
badlogin(username); |
exit(0); |
quickexit(0); |
} |
} |
if (p < nbuf + (NBUFSIZ - 1)) |
if (p < nbuf + (NBUFSIZ - 1)) |
*p++ = ch; |
*p++ = ch; |
|
|
{ |
{ |
struct ttyent *t; |
struct ttyent *t; |
|
|
|
/* XXX - stash output of getttynam() elsewhere */ |
return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE); |
return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE); |
} |
} |
|
|
|
|
} |
} |
|
|
void |
void |
checknologin() |
|
{ |
|
int fd, nchars; |
|
char *nologin; |
|
char tbuf[8192]; |
|
|
|
if (!login_getcapbool(lc, "ignorenologin", 0)) { |
|
nologin = login_getcapstr(lc, "nologin", _PATH_NOLOGIN, |
|
_PATH_NOLOGIN); |
|
if ((fd = open(nologin, O_RDONLY, 0)) >= 0) { |
|
while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) |
|
(void)write(fileno(stdout), tbuf, nchars); |
|
sleepexit(0); |
|
} |
|
} |
|
} |
|
|
|
void |
|
dolastlog(quiet) |
dolastlog(quiet) |
int quiet; |
int quiet; |
{ |
{ |
|
|
sleepexit(eval) |
sleepexit(eval) |
int eval; |
int eval; |
{ |
{ |
|
auth_close(as); |
(void)sleep(5); |
(void)sleep(5); |
exit(eval); |
exit(eval); |
} |
} |
|
|
void |
void |
|
quickexit(eval) |
|
int eval; |
|
{ |
|
if (as) |
|
auth_close(as); |
|
exit(eval); |
|
} |
|
|
|
|
|
void |
sighup(signum) |
sighup(signum) |
int signum; |
int signum; |
{ |
{ |
|
|
badlogin(username); |
badlogin(username); |
exit(0); |
exit(0); |
} |
} |
|
|
|
#ifdef KERBEROS |
|
void |
|
kgettokens(homedir) |
|
char *homedir; |
|
{ |
|
|
|
/* buy AFS-tokens for homedir */ |
|
if (k_hasafs()) { |
|
char cell[128]; |
|
k_setpag(); |
|
if (k_afs_cell_of_file(homedir, |
|
cell, sizeof(cell)) == 0) |
|
krb_afslog(cell, 0); |
|
krb_afslog(0, 0); |
|
} |
|
} |
|
#endif |