1491 lines
38 KiB
C
1491 lines
38 KiB
C
/* This program is derived from 4.3 BSD software and is
|
|
subject to the copyright notice below.
|
|
|
|
The port to HP-UX has been motivated by the incapability
|
|
of 'rlogin'/'rlogind' as per HP-UX 6.5 (and 7.0) to transfer window sizes.
|
|
|
|
Changes:
|
|
|
|
- General HP-UX portation. Use of facilities not available
|
|
in HP-UX (e.g. setpriority) has been eliminated.
|
|
Utmp/wtmp handling has been ported.
|
|
|
|
- The program uses BSD command line options to be used
|
|
in connection with e.g. 'rlogind' i.e. 'new login'.
|
|
|
|
- HP features left out: password expiry
|
|
'*' as login shell, add it if you need it
|
|
|
|
- BSD features left out: quota checks
|
|
password expiry
|
|
analysis of terminal type (tset feature)
|
|
|
|
- BSD features thrown in: Security logging to syslogd.
|
|
This requires you to have a (ported) syslog
|
|
system -- 7.0 comes with syslog
|
|
|
|
'Lastlog' feature.
|
|
|
|
- A lot of nitty gritty details have been adjusted in favour of
|
|
HP-UX, e.g. /etc/securetty, default paths and the environment
|
|
variables assigned by 'login'.
|
|
|
|
- We do *nothing* to setup/alter tty state, under HP-UX this is
|
|
to be done by getty/rlogind/telnetd/some one else.
|
|
|
|
Michael Glad (glad@daimi.dk)
|
|
Computer Science Department
|
|
Aarhus University
|
|
Denmark
|
|
|
|
1990-07-04
|
|
|
|
1991-09-24 glad@daimi.aau.dk: HP-UX 8.0 port:
|
|
- now explictly sets non-blocking mode on descriptors
|
|
- strcasecmp is now part of HP-UX
|
|
|
|
1992-02-05 poe@daimi.aau.dk: Ported the stuff to Linux 0.12
|
|
From 1992 till now (1997) this code for Linux has been maintained at
|
|
ftp.daimi.aau.dk:/pub/linux/poe/
|
|
|
|
1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
|
|
- added Native Language Support
|
|
Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
|
|
- fixed strerr(errno) in gettext calls
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms are permitted
|
|
* provided that the above copyright notice and this paragraph are
|
|
* duplicated in all such forms and that any documentation,
|
|
* advertising materials, and other materials related to such
|
|
* distribution and use acknowledge that the software was developed
|
|
* by the University of California, Berkeley. The name of the
|
|
* University may not be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
*/
|
|
|
|
/*
|
|
* login [ name ]
|
|
* login -h hostname (for telnetd, etc.)
|
|
* login -f name (for pre-authenticated login: datakit, xterm, etc.)
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <getopt.h>
|
|
#include <memory.h>
|
|
#include <time.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/file.h>
|
|
#include <termios.h>
|
|
#include <string.h>
|
|
#define index strchr
|
|
#define rindex strrchr
|
|
#include <sys/ioctl.h>
|
|
#include <sys/wait.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <grp.h>
|
|
#include <pwd.h>
|
|
#include <utmp.h>
|
|
#include <setjmp.h>
|
|
#include <stdlib.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <linux/major.h>
|
|
#include <netdb.h>
|
|
#ifdef HAVE_LIBAUDIT
|
|
# include <libaudit.h>
|
|
#endif
|
|
|
|
#include "pathnames.h"
|
|
#include "my_crypt.h"
|
|
#include "login.h"
|
|
#include "xstrncpy.h"
|
|
#include "nls.h"
|
|
|
|
|
|
#ifdef HAVE_SECURITY_PAM_MISC_H
|
|
# include <security/pam_appl.h>
|
|
# include <security/pam_misc.h>
|
|
# define PAM_MAX_LOGIN_TRIES 3
|
|
# define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \
|
|
fprintf(stderr,"\n%s\n",pam_strerror(pamh, retcode)); \
|
|
syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \
|
|
pam_end(pamh, retcode); exit(1); \
|
|
}
|
|
# define PAM_END { \
|
|
pam_setcred(pamh, PAM_DELETE_CRED); \
|
|
retcode = pam_close_session(pamh,0); \
|
|
pam_end(pamh,retcode); \
|
|
}
|
|
#endif
|
|
|
|
#include <lastlog.h>
|
|
|
|
#define SLEEP_EXIT_TIMEOUT 5
|
|
|
|
#include "setproctitle.h"
|
|
|
|
#ifndef HAVE_SECURITY_PAM_MISC_H
|
|
static void getloginname (void);
|
|
static void checknologin (void);
|
|
static int rootterm (char *ttyn);
|
|
#endif
|
|
static void timedout (int);
|
|
static void sigint (int);
|
|
static void motd (void);
|
|
static void dolastlog (int quiet);
|
|
|
|
#ifdef USE_TTY_GROUP
|
|
# define TTY_MODE 0620
|
|
#else
|
|
# define TTY_MODE 0600
|
|
#endif
|
|
|
|
#define TTYGRPNAME "tty" /* name of group to own ttys */
|
|
|
|
#ifndef MAXPATHLEN
|
|
# define MAXPATHLEN 1024
|
|
#endif
|
|
|
|
/*
|
|
* This bounds the time given to login. Not a define so it can
|
|
* be patched on machines where it's too small.
|
|
*/
|
|
int timeout = 60;
|
|
|
|
struct passwd *pwd;
|
|
|
|
#ifdef HAVE_SECURITY_PAM_MISC_H
|
|
static struct passwd pwdcopy;
|
|
#endif
|
|
char hostaddress[16]; /* used in checktty.c */
|
|
sa_family_t hostfamily; /* used in checktty.c */
|
|
char *hostname; /* idem */
|
|
static char *username, *tty_name, *tty_number;
|
|
static char thishost[100];
|
|
static int failures = 1;
|
|
static pid_t pid;
|
|
|
|
/* Nice and simple code provided by Linus Torvalds 16-Feb-93 */
|
|
/* Nonblocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
|
|
He writes: "Login performs open() on a tty in a blocking mode.
|
|
In some cases it may make login wait in open() for carrier infinitely,
|
|
for example if the line is a simplistic case of a three-wire serial
|
|
connection. I believe login should open the line in the non-blocking mode
|
|
leaving the decision to make a connection to getty (where it actually
|
|
belongs). */
|
|
static void
|
|
opentty(const char * tty) {
|
|
int i, fd, flags;
|
|
|
|
fd = open(tty, O_RDWR | O_NONBLOCK);
|
|
if (fd == -1) {
|
|
syslog(LOG_ERR, _("FATAL: can't reopen tty: %s"),
|
|
strerror(errno));
|
|
sleep(1);
|
|
exit(1);
|
|
}
|
|
|
|
flags = fcntl(fd, F_GETFL);
|
|
flags &= ~O_NONBLOCK;
|
|
fcntl(fd, F_SETFL, flags);
|
|
|
|
for (i = 0; i < fd; i++)
|
|
close(i);
|
|
for (i = 0; i < 3; i++)
|
|
if (fd != i)
|
|
dup2(fd, i);
|
|
if (fd >= 3)
|
|
close(fd);
|
|
}
|
|
|
|
/* In case login is suid it was possible to use a hardlink as stdin
|
|
and exploit races for a local root exploit. (Wojciech Purczynski). */
|
|
/* More precisely, the problem is ttyn := ttyname(0); ...; chown(ttyn);
|
|
here ttyname() might return "/tmp/x", a hardlink to a pseudotty. */
|
|
/* All of this is a problem only when login is suid, which it isnt. */
|
|
static void
|
|
check_ttyname(char *ttyn) {
|
|
struct stat statbuf;
|
|
|
|
if (lstat(ttyn, &statbuf)
|
|
|| !S_ISCHR(statbuf.st_mode)
|
|
|| (statbuf.st_nlink > 1 && strncmp(ttyn, "/dev/", 5))
|
|
|| (access(ttyn, R_OK | W_OK) != 0)) {
|
|
syslog(LOG_ERR, _("FATAL: bad tty"));
|
|
sleep(1);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
#ifdef LOGIN_CHOWN_VCS
|
|
/* true if the filedescriptor fd is a console tty, very Linux specific */
|
|
static int
|
|
consoletty(int fd) {
|
|
struct stat stb;
|
|
|
|
if ((fstat(fd, &stb) >= 0)
|
|
&& (major(stb.st_rdev) == TTY_MAJOR)
|
|
&& (minor(stb.st_rdev) < 64)) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_SECURITY_PAM_MISC_H
|
|
/*
|
|
* Log failed login attempts in _PATH_BTMP if that exists.
|
|
* Must be called only with username the name of an actual user.
|
|
* The most common login failure is to give password instead of username.
|
|
*/
|
|
#define _PATH_BTMP "/var/log/btmp"
|
|
static void
|
|
logbtmp(const char *line, const char *username, const char *hostname) {
|
|
struct utmp ut;
|
|
struct timeval tv;
|
|
|
|
memset(&ut, 0, sizeof(ut));
|
|
|
|
strncpy(ut.ut_user, username ? username : "(unknown)",
|
|
sizeof(ut.ut_user));
|
|
|
|
strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
|
|
xstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
|
|
|
|
#if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */
|
|
gettimeofday(&tv, NULL);
|
|
ut.ut_tv.tv_sec = tv.tv_sec;
|
|
ut.ut_tv.tv_usec = tv.tv_usec;
|
|
#else
|
|
{
|
|
time_t t;
|
|
time(&t);
|
|
ut.ut_time = t; /* ut_time is not always a time_t */
|
|
}
|
|
#endif
|
|
|
|
ut.ut_type = LOGIN_PROCESS; /* XXX doesn't matter */
|
|
ut.ut_pid = pid;
|
|
if (hostname) {
|
|
xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
|
|
if (hostaddress[0])
|
|
memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6));
|
|
}
|
|
#if HAVE_UPDWTMP /* bad luck for ancient systems */
|
|
updwtmp(_PATH_BTMP, &ut);
|
|
#endif
|
|
}
|
|
|
|
|
|
static int child_pid = 0;
|
|
static volatile int got_sig = 0;
|
|
|
|
/*
|
|
* This handler allows to inform a shell about signals to login. If you have
|
|
* (root) permissions you can kill all login childrent by one signal to login
|
|
* process.
|
|
*
|
|
* Also, parent who is session leader is able (before setsid() in child) to
|
|
* inform child when controlling tty goes away (e.g. modem hangup, SIGHUP).
|
|
*/
|
|
static void
|
|
sig_handler(int signal)
|
|
{
|
|
if(child_pid)
|
|
kill(-child_pid, signal);
|
|
else
|
|
got_sig = 1;
|
|
if(signal == SIGTERM)
|
|
kill(-child_pid, SIGHUP); /* because the shell often ignores SIGTERM */
|
|
}
|
|
|
|
#endif /* HAVE_SECURITY_PAM_MISC_H */
|
|
|
|
#ifdef HAVE_LIBAUDIT
|
|
static void
|
|
logaudit(const char *tty, const char *username, const char *hostname,
|
|
struct passwd *pwd, int status)
|
|
{
|
|
int audit_fd;
|
|
|
|
audit_fd = audit_open();
|
|
if (audit_fd == -1)
|
|
return;
|
|
if (!pwd && username)
|
|
pwd = getpwnam(username);
|
|
|
|
audit_log_acct_message(audit_fd, AUDIT_USER_LOGIN,
|
|
NULL, "login", username ? username : "(unknown)",
|
|
pwd ? pwd->pw_uid : -1, hostname, NULL, tty, status);
|
|
|
|
close(audit_fd);
|
|
}
|
|
#else /* ! HAVE_LIBAUDIT */
|
|
# define logaudit(tty, username, hostname, pwd, status)
|
|
#endif /* HAVE_LIBAUDIT */
|
|
|
|
#ifdef HAVE_SECURITY_PAM_MISC_H
|
|
/* encapsulate stupid "void **" pam_get_item() API */
|
|
int
|
|
get_pam_username(pam_handle_t *pamh, char **name)
|
|
{
|
|
const void *item = (void *) *name;
|
|
int rc;
|
|
rc = pam_get_item(pamh, PAM_USER, &item);
|
|
*name = (char *) item;
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* We need to check effective UID/GID. For example $HOME could be on root
|
|
* squashed NFS or on NFS with UID mapping and access(2) uses real UID/GID.
|
|
* The open(2) seems as the surest solution.
|
|
* -- kzak@redhat.com (10-Apr-2009)
|
|
*/
|
|
int
|
|
effective_access(const char *path, int mode)
|
|
{
|
|
int fd = open(path, mode);
|
|
if (fd != -1)
|
|
close(fd);
|
|
return fd == -1 ? -1 : 0;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
extern int optind;
|
|
extern char *optarg, **environ;
|
|
struct group *gr;
|
|
register int ch;
|
|
register char *p;
|
|
int ask, fflag, hflag, pflag, cnt, errsv;
|
|
int quietlog, passwd_req;
|
|
char *domain, *ttyn;
|
|
char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_DEV_TTY) + 10];
|
|
char *termenv;
|
|
char *childArgv[10];
|
|
char *buff;
|
|
int childArgc = 0;
|
|
#ifdef HAVE_SECURITY_PAM_MISC_H
|
|
int retcode;
|
|
pam_handle_t *pamh = NULL;
|
|
struct pam_conv conv = { misc_conv, NULL };
|
|
struct sigaction sa, oldsa_hup, oldsa_term;
|
|
#else
|
|
char *salt, *pp;
|
|
#endif
|
|
#ifdef LOGIN_CHOWN_VCS
|
|
char vcsn[20], vcsan[20];
|
|
#endif
|
|
|
|
pid = getpid();
|
|
|
|
signal(SIGALRM, timedout);
|
|
siginterrupt(SIGALRM,1); /* we have to interrupt syscalls like ioclt() */
|
|
alarm((unsigned int)timeout);
|
|
signal(SIGQUIT, SIG_IGN);
|
|
signal(SIGINT, SIG_IGN);
|
|
|
|
setlocale(LC_ALL, "");
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
textdomain(PACKAGE);
|
|
|
|
setpriority(PRIO_PROCESS, 0, 0);
|
|
initproctitle(argc, argv);
|
|
|
|
/*
|
|
* -p is used by getty to tell login not to destroy the environment
|
|
* -f is used to skip a second login authentication
|
|
* -h is used by other servers to pass the name of the remote
|
|
* host to login so that it may be placed in utmp and wtmp
|
|
*/
|
|
gethostname(tbuf, sizeof(tbuf));
|
|
xstrncpy(thishost, tbuf, sizeof(thishost));
|
|
domain = strchr(tbuf, '.');
|
|
|
|
username = tty_name = hostname = NULL;
|
|
fflag = hflag = pflag = 0;
|
|
passwd_req = 1;
|
|
|
|
while ((ch = getopt(argc, argv, "fh:p")) != -1)
|
|
switch (ch) {
|
|
case 'f':
|
|
fflag = 1;
|
|
break;
|
|
|
|
case 'h':
|
|
if (getuid()) {
|
|
fprintf(stderr,
|
|
_("login: -h for super-user only.\n"));
|
|
exit(1);
|
|
}
|
|
hflag = 1;
|
|
if (domain && (p = strchr(optarg, '.')) &&
|
|
strcasecmp(p, domain) == 0)
|
|
*p = 0;
|
|
|
|
hostname = strdup(optarg); /* strdup: Ambrose C. Li */
|
|
{
|
|
struct addrinfo hints, *info = NULL;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_flags = AI_ADDRCONFIG;
|
|
|
|
hostaddress[0] = 0;
|
|
|
|
if (getaddrinfo(hostname, NULL, &hints, &info)==0 && info) {
|
|
if (info->ai_family == AF_INET) {
|
|
struct sockaddr_in *sa =
|
|
(struct sockaddr_in *) info->ai_addr;
|
|
memcpy(hostaddress, &(sa->sin_addr),
|
|
sizeof(sa->sin_addr));
|
|
}
|
|
else if (info->ai_family == AF_INET6) {
|
|
struct sockaddr_in6 *sa =
|
|
(struct sockaddr_in6 *) info->ai_addr;
|
|
memcpy(hostaddress, &(sa->sin6_addr),
|
|
sizeof(sa->sin6_addr));
|
|
}
|
|
hostfamily = info->ai_family;
|
|
freeaddrinfo(info);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'p':
|
|
pflag = 1;
|
|
break;
|
|
|
|
case '?':
|
|
default:
|
|
fprintf(stderr,
|
|
_("usage: login [-fp] [username]\n"));
|
|
exit(1);
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
if (*argv) {
|
|
char *p = *argv;
|
|
username = strdup(p);
|
|
ask = 0;
|
|
/* wipe name - some people mistype their password here */
|
|
/* (of course we are too late, but perhaps this helps a little ..) */
|
|
while(*p)
|
|
*p++ = ' ';
|
|
} else
|
|
ask = 1;
|
|
|
|
for (cnt = getdtablesize(); cnt > 2; cnt--)
|
|
close(cnt);
|
|
|
|
ttyn = ttyname(0);
|
|
|
|
if (ttyn == NULL || *ttyn == '\0') {
|
|
/* no snprintf required - see definition of tname */
|
|
snprintf(tname, sizeof(tname), "%s??", _PATH_DEV_TTY);
|
|
ttyn = tname;
|
|
}
|
|
|
|
check_ttyname(ttyn);
|
|
|
|
if (strncmp(ttyn, "/dev/", 5) == 0)
|
|
tty_name = ttyn+5;
|
|
else
|
|
tty_name = ttyn;
|
|
|
|
if (strncmp(ttyn, "/dev/tty", 8) == 0)
|
|
tty_number = ttyn+8;
|
|
else {
|
|
char *p = ttyn;
|
|
while (*p && !isdigit(*p)) p++;
|
|
tty_number = p;
|
|
}
|
|
|
|
#ifdef LOGIN_CHOWN_VCS
|
|
/* find names of Virtual Console devices, for later mode change */
|
|
snprintf(vcsn, sizeof(vcsn), "/dev/vcs%s", tty_number);
|
|
snprintf(vcsan, sizeof(vcsan), "/dev/vcsa%s", tty_number);
|
|
#endif
|
|
|
|
/* set pgid to pid */
|
|
setpgrp();
|
|
/* this means that setsid() will fail */
|
|
|
|
{
|
|
struct termios tt, ttt;
|
|
|
|
tcgetattr(0, &tt);
|
|
ttt = tt;
|
|
ttt.c_cflag &= ~HUPCL;
|
|
|
|
/* These can fail, e.g. with ttyn on a read-only filesystem */
|
|
chown(ttyn, 0, 0);
|
|
chmod(ttyn, TTY_MODE);
|
|
|
|
/* Kill processes left on this tty */
|
|
tcsetattr(0,TCSAFLUSH,&ttt);
|
|
signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */
|
|
vhangup();
|
|
signal(SIGHUP, SIG_DFL);
|
|
|
|
/* open stdin,stdout,stderr to the tty */
|
|
opentty(ttyn);
|
|
|
|
/* restore tty modes */
|
|
tcsetattr(0,TCSAFLUSH,&tt);
|
|
}
|
|
|
|
openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
|
|
|
|
#if 0
|
|
/* other than iso-8859-1 */
|
|
printf("\033(K");
|
|
fprintf(stderr,"\033(K");
|
|
#endif
|
|
|
|
#ifdef HAVE_SECURITY_PAM_MISC_H
|
|
/*
|
|
* username is initialized to NULL
|
|
* and if specified on the command line it is set.
|
|
* Therefore, we are safe not setting it to anything
|
|
*/
|
|
|
|
retcode = pam_start(hflag?"remote":"login",username, &conv, &pamh);
|
|
if(retcode != PAM_SUCCESS) {
|
|
fprintf(stderr, _("%s: PAM failure, aborting: %s\n"),
|
|
"login", pam_strerror(pamh, retcode));
|
|
syslog(LOG_ERR, _("Couldn't initialize PAM: %s"),
|
|
pam_strerror(pamh, retcode));
|
|
exit(99);
|
|
}
|
|
/* hostname & tty are either set to NULL or their correct values,
|
|
depending on how much we know */
|
|
retcode = pam_set_item(pamh, PAM_RHOST, hostname);
|
|
PAM_FAIL_CHECK;
|
|
retcode = pam_set_item(pamh, PAM_TTY, tty_name);
|
|
PAM_FAIL_CHECK;
|
|
|
|
/*
|
|
* Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM
|
|
* so that the "login: " prompt gets localized. Unfortunately,
|
|
* PAM doesn't have an interface to specify the "Password: " string
|
|
* (yet).
|
|
*/
|
|
retcode = pam_set_item(pamh, PAM_USER_PROMPT, _("login: "));
|
|
PAM_FAIL_CHECK;
|
|
|
|
#if 0
|
|
/*
|
|
* other than iso-8859-1
|
|
* one more time due to reset tty by PAM
|
|
*/
|
|
printf("\033(K");
|
|
fprintf(stderr,"\033(K");
|
|
#endif
|
|
|
|
if (username) {
|
|
/* we need't the original username. We have to follow PAM. */
|
|
free(username);
|
|
username = NULL;
|
|
}
|
|
|
|
/* if fflag == 1, then the user has already been authenticated */
|
|
if (fflag && (getuid() == 0))
|
|
passwd_req = 0;
|
|
else
|
|
passwd_req = 1;
|
|
|
|
if(passwd_req == 1) {
|
|
int failcount=0;
|
|
|
|
/* if we didn't get a user on the command line, set it to NULL */
|
|
get_pam_username(pamh, &username);
|
|
|
|
/* there may be better ways to deal with some of these
|
|
conditions, but at least this way I don't think we'll
|
|
be giving away information... */
|
|
/* Perhaps someday we can trust that all PAM modules will
|
|
pay attention to failure count and get rid of MAX_LOGIN_TRIES? */
|
|
|
|
retcode = pam_authenticate(pamh, 0);
|
|
while((failcount++ < PAM_MAX_LOGIN_TRIES) &&
|
|
((retcode == PAM_AUTH_ERR) ||
|
|
(retcode == PAM_USER_UNKNOWN) ||
|
|
(retcode == PAM_CRED_INSUFFICIENT) ||
|
|
(retcode == PAM_AUTHINFO_UNAVAIL))) {
|
|
get_pam_username(pamh, &username);
|
|
|
|
syslog(LOG_NOTICE,_("FAILED LOGIN %d FROM %s FOR %s, %s"),
|
|
failcount, hostname, username, pam_strerror(pamh, retcode));
|
|
logbtmp(tty_name, username, hostname);
|
|
logaudit(tty_name, username, hostname, NULL, 0);
|
|
|
|
fprintf(stderr,_("Login incorrect\n\n"));
|
|
pam_set_item(pamh,PAM_USER,NULL);
|
|
retcode = pam_authenticate(pamh, 0);
|
|
}
|
|
|
|
if (retcode != PAM_SUCCESS) {
|
|
get_pam_username(pamh, &username);
|
|
|
|
if (retcode == PAM_MAXTRIES)
|
|
syslog(LOG_NOTICE,_("TOO MANY LOGIN TRIES (%d) FROM %s FOR "
|
|
"%s, %s"), failcount, hostname, username,
|
|
pam_strerror(pamh, retcode));
|
|
else
|
|
syslog(LOG_NOTICE,_("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
|
|
hostname, username, pam_strerror(pamh, retcode));
|
|
logbtmp(tty_name, username, hostname);
|
|
logaudit(tty_name, username, hostname, NULL, 0);
|
|
|
|
fprintf(stderr,_("\nLogin incorrect\n"));
|
|
pam_end(pamh, retcode);
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Authentication may be skipped (for example, during krlogin, rlogin, etc...),
|
|
* but it doesn't mean that we can skip other account checks. The account
|
|
* could be disabled or password expired (althought kerberos ticket is valid).
|
|
* -- kzak@redhat.com (22-Feb-2006)
|
|
*/
|
|
retcode = pam_acct_mgmt(pamh, 0);
|
|
|
|
if(retcode == PAM_NEW_AUTHTOK_REQD) {
|
|
retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
|
|
}
|
|
|
|
PAM_FAIL_CHECK;
|
|
|
|
/*
|
|
* Grab the user information out of the password file for future usage
|
|
* First get the username that we are actually using, though.
|
|
*/
|
|
retcode = get_pam_username(pamh, &username);
|
|
PAM_FAIL_CHECK;
|
|
|
|
if (!username || !*username) {
|
|
fprintf(stderr, _("\nSession setup problem, abort.\n"));
|
|
syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."),
|
|
__FUNCTION__, __LINE__);
|
|
pam_end(pamh, PAM_SYSTEM_ERR);
|
|
exit(1);
|
|
}
|
|
if (!(pwd = getpwnam(username))) {
|
|
fprintf(stderr, _("\nSession setup problem, abort.\n"));
|
|
syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."),
|
|
username, __FUNCTION__, __LINE__);
|
|
pam_end(pamh, PAM_SYSTEM_ERR);
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Create a copy of the pwd struct - otherwise it may get
|
|
* clobbered by PAM
|
|
*/
|
|
memcpy(&pwdcopy, pwd, sizeof(*pwd));
|
|
pwd = &pwdcopy;
|
|
pwd->pw_name = strdup(pwd->pw_name);
|
|
pwd->pw_passwd = strdup(pwd->pw_passwd);
|
|
pwd->pw_gecos = strdup(pwd->pw_gecos);
|
|
pwd->pw_dir = strdup(pwd->pw_dir);
|
|
pwd->pw_shell = strdup(pwd->pw_shell);
|
|
if (!pwd->pw_name || !pwd->pw_passwd || !pwd->pw_gecos ||
|
|
!pwd->pw_dir || !pwd->pw_shell) {
|
|
fprintf(stderr, _("login: Out of memory\n"));
|
|
syslog(LOG_ERR, "Out of memory");
|
|
pam_end(pamh, PAM_SYSTEM_ERR);
|
|
exit(1);
|
|
}
|
|
username = pwd->pw_name;
|
|
|
|
/*
|
|
* Initialize the supplementary group list.
|
|
* This should be done before pam_setcred because
|
|
* the PAM modules might add groups during pam_setcred.
|
|
*/
|
|
if (initgroups(username, pwd->pw_gid) < 0) {
|
|
syslog(LOG_ERR, "initgroups: %m");
|
|
fprintf(stderr, _("\nSession setup problem, abort.\n"));
|
|
pam_end(pamh, PAM_SYSTEM_ERR);
|
|
exit(1);
|
|
}
|
|
|
|
retcode = pam_open_session(pamh, 0);
|
|
PAM_FAIL_CHECK;
|
|
|
|
retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
|
|
if (retcode != PAM_SUCCESS)
|
|
pam_close_session(pamh, 0);
|
|
PAM_FAIL_CHECK;
|
|
|
|
#else /* ! HAVE_SECURITY_PAM_MISC_H */
|
|
|
|
for (cnt = 0;; ask = 1) {
|
|
|
|
if (ask) {
|
|
fflag = 0;
|
|
getloginname();
|
|
}
|
|
|
|
/* Dirty patch to fix a gigantic security hole when using
|
|
yellow pages. This problem should be solved by the
|
|
libraries, and not by programs, but this must be fixed
|
|
urgently! If the first char of the username is '+', we
|
|
avoid login success.
|
|
Feb 95 <alvaro@etsit.upm.es> */
|
|
|
|
if (username[0] == '+') {
|
|
puts(_("Illegal username"));
|
|
badlogin(username);
|
|
sleepexit(1);
|
|
}
|
|
|
|
/* (void)strcpy(tbuf, username); why was this here? */
|
|
if ((pwd = getpwnam(username))) {
|
|
# ifdef SHADOW_PWD
|
|
struct spwd *sp;
|
|
|
|
if ((sp = getspnam(username)))
|
|
pwd->pw_passwd = sp->sp_pwdp;
|
|
# endif
|
|
salt = pwd->pw_passwd;
|
|
} else
|
|
salt = "xx";
|
|
|
|
if (pwd) {
|
|
initgroups(username, pwd->pw_gid);
|
|
checktty(username, tty_name, pwd); /* in checktty.c */
|
|
}
|
|
|
|
/* if user not super-user, check for disabled logins */
|
|
if (pwd == NULL || pwd->pw_uid)
|
|
checknologin();
|
|
|
|
/*
|
|
* Disallow automatic login to root; if not invoked by
|
|
* root, disallow if the uid's differ.
|
|
*/
|
|
if (fflag && pwd) {
|
|
int uid = getuid();
|
|
|
|
passwd_req = pwd->pw_uid == 0 ||
|
|
(uid && uid != pwd->pw_uid);
|
|
}
|
|
|
|
/*
|
|
* If trying to log in as root, but with insecure terminal,
|
|
* refuse the login attempt.
|
|
*/
|
|
if (pwd && pwd->pw_uid == 0 && !rootterm(tty_name)) {
|
|
fprintf(stderr,
|
|
_("%s login refused on this terminal.\n"),
|
|
pwd->pw_name);
|
|
|
|
if (hostname)
|
|
syslog(LOG_NOTICE,
|
|
_("LOGIN %s REFUSED FROM %s ON TTY %s"),
|
|
pwd->pw_name, hostname, tty_name);
|
|
else
|
|
syslog(LOG_NOTICE,
|
|
_("LOGIN %s REFUSED ON TTY %s"),
|
|
pwd->pw_name, tty_name);
|
|
logaudit(tty_name, pwd->pw_name, hostname, pwd, 0);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If no pre-authentication and a password exists
|
|
* for this user, prompt for one and verify it.
|
|
*/
|
|
if (!passwd_req || (pwd && !*pwd->pw_passwd))
|
|
break;
|
|
|
|
setpriority(PRIO_PROCESS, 0, -4);
|
|
pp = getpass(_("Password: "));
|
|
|
|
# ifdef CRYPTOCARD
|
|
if (strncmp(pp, "CRYPTO", 6) == 0) {
|
|
if (pwd && cryptocard()) break;
|
|
}
|
|
# endif /* CRYPTOCARD */
|
|
|
|
p = crypt(pp, salt);
|
|
setpriority(PRIO_PROCESS, 0, 0);
|
|
|
|
# ifdef KERBEROS
|
|
/*
|
|
* If not present in pw file, act as we normally would.
|
|
* If we aren't Kerberos-authenticated, try the normal
|
|
* pw file for a password. If that's ok, log the user
|
|
* in without issueing any tickets.
|
|
*/
|
|
|
|
if (pwd && !krb_get_lrealm(realm,1)) {
|
|
/*
|
|
* get TGT for local realm; be careful about uid's
|
|
* here for ticket file ownership
|
|
*/
|
|
setreuid(geteuid(),pwd->pw_uid);
|
|
kerror = krb_get_pw_in_tkt(pwd->pw_name, "", realm,
|
|
"krbtgt", realm, DEFAULT_TKT_LIFE, pp);
|
|
setuid(0);
|
|
if (kerror == INTK_OK) {
|
|
memset(pp, 0, strlen(pp));
|
|
notickets = 0; /* user got ticket */
|
|
break;
|
|
}
|
|
}
|
|
# endif /* KERBEROS */
|
|
memset(pp, 0, strlen(pp));
|
|
|
|
if (pwd && !strcmp(p, pwd->pw_passwd))
|
|
break;
|
|
|
|
printf(_("Login incorrect\n"));
|
|
badlogin(username); /* log ALL bad logins */
|
|
failures++;
|
|
|
|
/* we allow 10 tries, but after 3 we start backing off */
|
|
if (++cnt > 3) {
|
|
if (cnt >= 10) {
|
|
sleepexit(1);
|
|
}
|
|
sleep((unsigned int)((cnt - 3) * 5));
|
|
}
|
|
}
|
|
#endif /* !HAVE_SECURITY_PAM_MISC_H */
|
|
|
|
/* committed to login -- turn off timeout */
|
|
alarm((unsigned int)0);
|
|
|
|
endpwent();
|
|
|
|
/* This requires some explanation: As root we may not be able to
|
|
read the directory of the user if it is on an NFS mounted
|
|
filesystem. We temporarily set our effective uid to the user-uid
|
|
making sure that we keep root privs. in the real uid.
|
|
|
|
A portable solution would require a fork(), but we rely on Linux
|
|
having the BSD setreuid() */
|
|
|
|
{
|
|
char tmpstr[MAXPATHLEN];
|
|
uid_t ruid = getuid();
|
|
gid_t egid = getegid();
|
|
|
|
/* avoid snprintf - old systems do not have it, or worse,
|
|
have a libc in which snprintf is the same as sprintf */
|
|
if (strlen(pwd->pw_dir) + sizeof(_PATH_HUSHLOGIN) + 2 > MAXPATHLEN)
|
|
quietlog = 0;
|
|
else {
|
|
sprintf(tmpstr, "%s/%s", pwd->pw_dir, _PATH_HUSHLOGIN);
|
|
setregid(-1, pwd->pw_gid);
|
|
setreuid(0, pwd->pw_uid);
|
|
quietlog = (effective_access(tmpstr, O_RDONLY) == 0);
|
|
setuid(0); /* setreuid doesn't do it alone! */
|
|
setreuid(ruid, 0);
|
|
setregid(-1, egid);
|
|
}
|
|
}
|
|
|
|
/* for linux, write entries in utmp and wtmp */
|
|
{
|
|
struct utmp ut;
|
|
struct utmp *utp;
|
|
struct timeval tv;
|
|
|
|
utmpname(_PATH_UTMP);
|
|
setutent();
|
|
|
|
/* Find pid in utmp.
|
|
login sometimes overwrites the runlevel entry in /var/run/utmp,
|
|
confusing sysvinit. I added a test for the entry type, and the problem
|
|
was gone. (In a runlevel entry, st_pid is not really a pid but some number
|
|
calculated from the previous and current runlevel).
|
|
Michael Riepe <michael@stud.uni-hannover.de>
|
|
*/
|
|
while ((utp = getutent()))
|
|
if (utp->ut_pid == pid
|
|
&& utp->ut_type >= INIT_PROCESS
|
|
&& utp->ut_type <= DEAD_PROCESS)
|
|
break;
|
|
|
|
/* If we can't find a pre-existing entry by pid, try by line.
|
|
BSD network daemons may rely on this. (anonymous) */
|
|
if (utp == NULL) {
|
|
setutent();
|
|
ut.ut_type = LOGIN_PROCESS;
|
|
strncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
|
|
utp = getutline(&ut);
|
|
}
|
|
|
|
if (utp) {
|
|
memcpy(&ut, utp, sizeof(ut));
|
|
} else {
|
|
/* some gettys/telnetds don't initialize utmp... */
|
|
memset(&ut, 0, sizeof(ut));
|
|
}
|
|
|
|
if (ut.ut_id[0] == 0)
|
|
strncpy(ut.ut_id, tty_number, sizeof(ut.ut_id));
|
|
|
|
strncpy(ut.ut_user, username, sizeof(ut.ut_user));
|
|
xstrncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
|
|
#ifdef _HAVE_UT_TV /* in <utmpbits.h> included by <utmp.h> */
|
|
gettimeofday(&tv, NULL);
|
|
ut.ut_tv.tv_sec = tv.tv_sec;
|
|
ut.ut_tv.tv_usec = tv.tv_usec;
|
|
#else
|
|
{
|
|
time_t t;
|
|
time(&t);
|
|
ut.ut_time = t; /* ut_time is not always a time_t */
|
|
/* glibc2 #defines it as ut_tv.tv_sec */
|
|
}
|
|
#endif
|
|
ut.ut_type = USER_PROCESS;
|
|
ut.ut_pid = pid;
|
|
if (hostname) {
|
|
xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
|
|
if (hostaddress[0])
|
|
memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6));
|
|
}
|
|
|
|
pututline(&ut);
|
|
endutent();
|
|
|
|
#if HAVE_UPDWTMP
|
|
updwtmp(_PATH_WTMP, &ut);
|
|
#else
|
|
#if 0
|
|
/* The O_APPEND open() flag should be enough to guarantee
|
|
atomic writes at end of file. */
|
|
{
|
|
int wtmp;
|
|
|
|
if((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
|
|
write(wtmp, (char *)&ut, sizeof(ut));
|
|
close(wtmp);
|
|
}
|
|
}
|
|
#else
|
|
/* Probably all this locking below is just nonsense,
|
|
and the short version is OK as well. */
|
|
{
|
|
int lf, wtmp;
|
|
if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
|
|
flock(lf, LOCK_EX);
|
|
if ((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
|
|
write(wtmp, (char *)&ut, sizeof(ut));
|
|
close(wtmp);
|
|
}
|
|
flock(lf, LOCK_UN);
|
|
close(lf);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
logaudit(tty_name, username, hostname, pwd, 1);
|
|
dolastlog(quietlog);
|
|
|
|
chown(ttyn, pwd->pw_uid,
|
|
(gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
|
|
chmod(ttyn, TTY_MODE);
|
|
|
|
#ifdef LOGIN_CHOWN_VCS
|
|
/* if tty is one of the VC's then change owner and mode of the
|
|
special /dev/vcs devices as well */
|
|
if (consoletty(0)) {
|
|
chown(vcsn, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid));
|
|
chown(vcsan, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid));
|
|
chmod(vcsn, TTY_MODE);
|
|
chmod(vcsan, TTY_MODE);
|
|
}
|
|
#endif
|
|
|
|
setgid(pwd->pw_gid);
|
|
|
|
if (*pwd->pw_shell == '\0')
|
|
pwd->pw_shell = _PATH_BSHELL;
|
|
|
|
/* preserve TERM even without -p flag */
|
|
{
|
|
char *ep;
|
|
|
|
if(!((ep = getenv("TERM")) && (termenv = strdup(ep))))
|
|
termenv = "dumb";
|
|
}
|
|
|
|
/* destroy environment unless user has requested preservation */
|
|
if (!pflag)
|
|
{
|
|
environ = (char**)malloc(sizeof(char*));
|
|
memset(environ, 0, sizeof(char*));
|
|
}
|
|
|
|
setenv("HOME", pwd->pw_dir, 0); /* legal to override */
|
|
if(pwd->pw_uid)
|
|
setenv("PATH", _PATH_DEFPATH, 1);
|
|
else
|
|
setenv("PATH", _PATH_DEFPATH_ROOT, 1);
|
|
|
|
setenv("SHELL", pwd->pw_shell, 1);
|
|
setenv("TERM", termenv, 1);
|
|
|
|
/* mailx will give a funny error msg if you forget this one */
|
|
{
|
|
char tmp[MAXPATHLEN];
|
|
/* avoid snprintf */
|
|
if (sizeof(_PATH_MAILDIR) + strlen(pwd->pw_name) + 1 < MAXPATHLEN) {
|
|
sprintf(tmp, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
|
|
setenv("MAIL",tmp,0);
|
|
}
|
|
}
|
|
|
|
/* LOGNAME is not documented in login(1) but
|
|
HP-UX 6.5 does it. We'll not allow modifying it.
|
|
*/
|
|
setenv("LOGNAME", pwd->pw_name, 1);
|
|
|
|
#ifdef HAVE_SECURITY_PAM_MISC_H
|
|
{
|
|
int i;
|
|
char ** env = pam_getenvlist(pamh);
|
|
|
|
if (env != NULL) {
|
|
for (i=0; env[i]; i++) {
|
|
putenv(env[i]);
|
|
/* D(("env[%d] = %s", i,env[i])); */
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
setproctitle("login", username);
|
|
|
|
if (!strncmp(tty_name, "ttyS", 4))
|
|
syslog(LOG_INFO, _("DIALUP AT %s BY %s"), tty_name, pwd->pw_name);
|
|
|
|
/* allow tracking of good logins.
|
|
-steve philp (sphilp@mail.alliance.net) */
|
|
|
|
if (pwd->pw_uid == 0) {
|
|
if (hostname)
|
|
syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"),
|
|
tty_name, hostname);
|
|
else
|
|
syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), tty_name);
|
|
} else {
|
|
if (hostname)
|
|
syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"), tty_name,
|
|
pwd->pw_name, hostname);
|
|
else
|
|
syslog(LOG_INFO, _("LOGIN ON %s BY %s"), tty_name,
|
|
pwd->pw_name);
|
|
}
|
|
|
|
if (!quietlog) {
|
|
motd();
|
|
|
|
#ifdef LOGIN_STAT_MAIL
|
|
/*
|
|
* This turns out to be a bad idea: when the mail spool
|
|
* is NFS mounted, and the NFS connection hangs, the
|
|
* login hangs, even root cannot login.
|
|
* Checking for mail should be done from the shell.
|
|
*/
|
|
{
|
|
struct stat st;
|
|
char *mail;
|
|
|
|
mail = getenv("MAIL");
|
|
if (mail && stat(mail, &st) == 0 && st.st_size != 0) {
|
|
if (st.st_mtime > st.st_atime)
|
|
printf(_("You have new mail.\n"));
|
|
else
|
|
printf(_("You have mail.\n"));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
signal(SIGALRM, SIG_DFL);
|
|
signal(SIGQUIT, SIG_DFL);
|
|
signal(SIGTSTP, SIG_IGN);
|
|
|
|
#ifdef HAVE_SECURITY_PAM_MISC_H
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.sa_handler = SIG_IGN;
|
|
sigaction(SIGINT, &sa, NULL);
|
|
|
|
sigaction(SIGHUP, &sa, &oldsa_hup); /* ignore when TIOCNOTTY */
|
|
|
|
/*
|
|
* detach the controlling tty
|
|
* -- we needn't the tty in parent who waits for child only.
|
|
* The child calls setsid() that detach from the tty as well.
|
|
*/
|
|
ioctl(0, TIOCNOTTY, NULL);
|
|
|
|
/*
|
|
* We have care about SIGTERM, because leave PAM session without
|
|
* pam_close_session() is pretty bad thing.
|
|
*/
|
|
sa.sa_handler = sig_handler;
|
|
sigaction(SIGHUP, &sa, NULL);
|
|
sigaction(SIGTERM, &sa, &oldsa_term);
|
|
|
|
closelog();
|
|
|
|
/*
|
|
* We must fork before setuid() because we need to call
|
|
* pam_close_session() as root.
|
|
*/
|
|
|
|
child_pid = fork();
|
|
if (child_pid < 0) {
|
|
int errsv = errno;
|
|
/* error in fork() */
|
|
fprintf(stderr, _("login: failure forking: %s"), strerror(errsv));
|
|
PAM_END;
|
|
exit(0);
|
|
}
|
|
|
|
if (child_pid) {
|
|
/* parent - wait for child to finish, then cleanup session */
|
|
close(0);
|
|
close(1);
|
|
close(2);
|
|
sa.sa_handler = SIG_IGN;
|
|
sigaction(SIGQUIT, &sa, NULL);
|
|
sigaction(SIGINT, &sa, NULL);
|
|
|
|
/* wait as long as any child is there */
|
|
while(wait(NULL) == -1 && errno == EINTR)
|
|
;
|
|
openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
|
|
PAM_END;
|
|
exit(0);
|
|
}
|
|
|
|
/* child */
|
|
|
|
/* restore to old state */
|
|
sigaction(SIGHUP, &oldsa_hup, NULL);
|
|
sigaction(SIGTERM, &oldsa_term, NULL);
|
|
if(got_sig)
|
|
exit(1);
|
|
|
|
/*
|
|
* Problem: if the user's shell is a shell like ash that doesnt do
|
|
* setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
|
|
* process in the pgrp, will kill us.
|
|
*/
|
|
|
|
/* start new session */
|
|
setsid();
|
|
|
|
/* make sure we have a controlling tty */
|
|
opentty(ttyn);
|
|
openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */
|
|
|
|
/*
|
|
* TIOCSCTTY: steal tty from other process group.
|
|
*/
|
|
if (ioctl(0, TIOCSCTTY, 1))
|
|
syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
|
|
#endif
|
|
signal(SIGINT, SIG_DFL);
|
|
|
|
/* discard permissions last so can't get killed and drop core */
|
|
if(setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
|
|
syslog(LOG_ALERT, _("setuid() failed"));
|
|
exit(1);
|
|
}
|
|
|
|
/* wait until here to change directory! */
|
|
if (chdir(pwd->pw_dir) < 0) {
|
|
printf(_("No directory %s!\n"), pwd->pw_dir);
|
|
if (chdir("/"))
|
|
exit(0);
|
|
pwd->pw_dir = "/";
|
|
printf(_("Logging in with home = \"/\".\n"));
|
|
}
|
|
|
|
/* if the shell field has a space: treat it like a shell script */
|
|
if (strchr(pwd->pw_shell, ' ')) {
|
|
buff = malloc(strlen(pwd->pw_shell) + 6);
|
|
|
|
if (!buff) {
|
|
fprintf(stderr, _("login: no memory for shell script.\n"));
|
|
exit(0);
|
|
}
|
|
|
|
strcpy(buff, "exec ");
|
|
strcat(buff, pwd->pw_shell);
|
|
childArgv[childArgc++] = "/bin/sh";
|
|
childArgv[childArgc++] = "-sh";
|
|
childArgv[childArgc++] = "-c";
|
|
childArgv[childArgc++] = buff;
|
|
} else {
|
|
tbuf[0] = '-';
|
|
xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ?
|
|
p + 1 : pwd->pw_shell),
|
|
sizeof(tbuf)-1);
|
|
|
|
childArgv[childArgc++] = pwd->pw_shell;
|
|
childArgv[childArgc++] = tbuf;
|
|
}
|
|
|
|
childArgv[childArgc++] = NULL;
|
|
|
|
execvp(childArgv[0], childArgv + 1);
|
|
|
|
errsv = errno;
|
|
|
|
if (!strcmp(childArgv[0], "/bin/sh"))
|
|
fprintf(stderr, _("login: couldn't exec shell script: %s.\n"),
|
|
strerror(errsv));
|
|
else
|
|
fprintf(stderr, _("login: no shell: %s.\n"), strerror(errsv));
|
|
|
|
exit(0);
|
|
}
|
|
|
|
#ifndef HAVE_SECURITY_PAM_MISC_H
|
|
static void
|
|
getloginname(void) {
|
|
int ch, cnt, cnt2;
|
|
char *p;
|
|
static char nbuf[UT_NAMESIZE + 1];
|
|
|
|
cnt2 = 0;
|
|
for (;;) {
|
|
cnt = 0;
|
|
printf(_("\n%s login: "), thishost); fflush(stdout);
|
|
for (p = nbuf; (ch = getchar()) != '\n'; ) {
|
|
if (ch == EOF) {
|
|
badlogin("EOF");
|
|
exit(0);
|
|
}
|
|
if (p < nbuf + UT_NAMESIZE)
|
|
*p++ = ch;
|
|
|
|
cnt++;
|
|
if (cnt > UT_NAMESIZE + 20) {
|
|
fprintf(stderr, _("login name much too long.\n"));
|
|
badlogin(_("NAME too long"));
|
|
exit(0);
|
|
}
|
|
}
|
|
if (p > nbuf) {
|
|
if (nbuf[0] == '-')
|
|
fprintf(stderr,
|
|
_("login names may not start with '-'.\n"));
|
|
else {
|
|
*p = '\0';
|
|
username = nbuf;
|
|
break;
|
|
}
|
|
}
|
|
|
|
cnt2++;
|
|
if (cnt2 > 50) {
|
|
fprintf(stderr, _("too many bare linefeeds.\n"));
|
|
badlogin(_("EXCESSIVE linefeeds"));
|
|
exit(0);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Robert Ambrose writes:
|
|
* A couple of my users have a problem with login processes hanging around
|
|
* soaking up pts's. What they seem to hung up on is trying to write out the
|
|
* message 'Login timed out after %d seconds' when the connection has already
|
|
* been dropped.
|
|
* What I did was add a second timeout while trying to write the message so
|
|
* the process just exits if the second timeout expires.
|
|
*/
|
|
|
|
static void
|
|
timedout2(int sig) {
|
|
struct termios ti;
|
|
|
|
/* reset echo */
|
|
tcgetattr(0, &ti);
|
|
ti.c_lflag |= ECHO;
|
|
tcsetattr(0, TCSANOW, &ti);
|
|
exit(0); /* %% */
|
|
}
|
|
|
|
static void
|
|
timedout(int sig) {
|
|
signal(SIGALRM, timedout2);
|
|
alarm(10);
|
|
fprintf(stderr, _("Login timed out after %d seconds\n"), timeout);
|
|
signal(SIGALRM, SIG_IGN);
|
|
alarm(0);
|
|
timedout2(0);
|
|
}
|
|
|
|
#ifndef HAVE_SECURITY_PAM_MISC_H
|
|
int
|
|
rootterm(char * ttyn)
|
|
{
|
|
int fd;
|
|
char buf[100],*p;
|
|
int cnt, more = 0;
|
|
|
|
fd = open(_PATH_SECURETTY, O_RDONLY);
|
|
if(fd < 0) return 1;
|
|
|
|
/* read each line in /etc/securetty, if a line matches our ttyline
|
|
then root is allowed to login on this tty, and we should return
|
|
true. */
|
|
for(;;) {
|
|
p = buf; cnt = 100;
|
|
while(--cnt >= 0 && (more = read(fd, p, 1)) == 1 && *p != '\n') p++;
|
|
if(more && *p == '\n') {
|
|
*p = '\0';
|
|
if(!strcmp(buf, ttyn)) {
|
|
close(fd);
|
|
return 1;
|
|
} else
|
|
continue;
|
|
} else {
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
#endif /* !HAVE_SECURITY_PAM_MISC_H */
|
|
|
|
jmp_buf motdinterrupt;
|
|
|
|
void
|
|
motd(void) {
|
|
int fd, nchars;
|
|
void (*oldint)(int);
|
|
char tbuf[8192];
|
|
|
|
if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
|
|
return;
|
|
oldint = signal(SIGINT, sigint);
|
|
if (setjmp(motdinterrupt) == 0)
|
|
while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
|
|
write(fileno(stdout), tbuf, nchars);
|
|
signal(SIGINT, oldint);
|
|
close(fd);
|
|
}
|
|
|
|
void
|
|
sigint(int sig) {
|
|
longjmp(motdinterrupt, 1);
|
|
}
|
|
|
|
#ifndef HAVE_SECURITY_PAM_MISC_H /* PAM takes care of this */
|
|
void
|
|
checknologin(void) {
|
|
int fd, nchars;
|
|
char tbuf[8192];
|
|
|
|
if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
|
|
while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
|
|
write(fileno(stdout), tbuf, nchars);
|
|
close(fd);
|
|
sleepexit(0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void
|
|
dolastlog(int quiet) {
|
|
struct lastlog ll;
|
|
int fd;
|
|
|
|
if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
|
|
lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
|
|
if (!quiet) {
|
|
if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
|
|
ll.ll_time != 0) {
|
|
time_t ll_time = (time_t) ll.ll_time;
|
|
|
|
printf(_("Last login: %.*s "),
|
|
24-5, ctime(&ll_time));
|
|
|
|
if (*ll.ll_host != '\0')
|
|
printf(_("from %.*s\n"),
|
|
(int)sizeof(ll.ll_host), ll.ll_host);
|
|
else
|
|
printf(_("on %.*s\n"),
|
|
(int)sizeof(ll.ll_line), ll.ll_line);
|
|
}
|
|
lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
|
|
}
|
|
memset((char *)&ll, 0, sizeof(ll));
|
|
|
|
{
|
|
time_t t;
|
|
time(&t);
|
|
ll.ll_time = t; /* ll_time is always 32bit */
|
|
}
|
|
|
|
xstrncpy(ll.ll_line, tty_name, sizeof(ll.ll_line));
|
|
if (hostname)
|
|
xstrncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
|
|
|
|
write(fd, (char *)&ll, sizeof(ll));
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
void
|
|
badlogin(const char *name) {
|
|
if (failures == 1) {
|
|
if (hostname)
|
|
syslog(LOG_NOTICE, _("LOGIN FAILURE FROM %s, %s"),
|
|
hostname, name);
|
|
else
|
|
syslog(LOG_NOTICE, _("LOGIN FAILURE ON %s, %s"),
|
|
tty_name, name);
|
|
} else {
|
|
if (hostname)
|
|
syslog(LOG_NOTICE, _("%d LOGIN FAILURES FROM %s, %s"),
|
|
failures, hostname, name);
|
|
else
|
|
syslog(LOG_NOTICE, _("%d LOGIN FAILURES ON %s, %s"),
|
|
failures, tty_name, name);
|
|
}
|
|
}
|
|
|
|
/* Should not be called from PAM code... */
|
|
void
|
|
sleepexit(int eval) {
|
|
sleep(SLEEP_EXIT_TIMEOUT);
|
|
exit(eval);
|
|
}
|