Import NetBSD usr.bin/login

This commit is contained in:
Ben Gras 2012-04-07 03:41:28 +02:00
parent 928d76dbe8
commit a2d1372680
18 changed files with 2706 additions and 558 deletions

View file

@ -15,7 +15,7 @@ SUBDIR= add_route arp ash at awk \
ftp101 gcore gcov-pull getty grep head hexdump host \
hostaddr id ifconfig ifdef install \
intr ipcrm ipcs irdpd isoread join kill last \
less lex loadkeys loadramdisk logger login look lp \
less lex loadkeys loadramdisk logger look lp \
lpd ls lspci mail make MAKEDEV \
mdb mesg mined mkfifo mkfs.mfs mknod \
mkproto mount mt netconf newroot nice acknm nohup \

View file

@ -1,7 +0,0 @@
PROG= login
MAN=
.if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no")
LDADD+= -lcrypt
.endif
.include <bsd.prog.mk>

View file

@ -1,509 +0,0 @@
/* login - log into the system Author: Patrick van Kleef */
/* Original version by Patrick van Kleef. History of modifications:
*
* Peter S. Housel Jan. 1988
* - Set up $USER, $HOME and $TERM.
* - Set signals to SIG_DFL.
*
* Terrence W. Holm June 1988
* - Allow a username as an optional argument.
* - Time out if a password is not typed within 60 seconds.
* - Perform a dummy delay after a bad username is entered.
* - Don't allow a login if "/etc/nologin" exists.
* - Cause a failure on bad "pw_shell" fields.
* - Record the login in "/usr/adm/wtmp".
*
* Peter S. Housel Dec. 1988
* - Record the login in "/etc/utmp" also.
*
* F. van Kempen June 1989
* - various patches for Minix V1.4a.
*
* F. van Kempen September 1989
* - added login-failure administration (new utmp.h needed!).
* - support arguments in pw_shell field
* - adapted source text to MINIX Style Sheet
*
* F. van Kempen October 1989
* - adapted to new utmp database.
* F. van Kempen, December 1989
* - fixed 'slot' assumption in wtmp()
* - fixed all MSS-stuff
* - adapted to POSIX (MINIX 1.5)
* F. van Kempen, January 1990
* - made all 'bad login accounting' optional by "#ifdef BADLOG".
* F. van Kempen, Februari 1990
* - fixed 'first argument' bug and added some casts.
*
* Andy Tanenbaum April 1990
* - if /bin/sh cannot be located, try /usr/bin/sh
*
* Michael A. Temari October 1990
* - handle more than single digit tty devices
*
* Philip Homburg - Feb 28 1992
* - use ttyname to get the name of a tty.
*
* Kees J. Bot - Feb 13 1993
* - putting out garbage.
* - added lastlog.
*
* Kees J. Bot - Feb 13 1993
* - supplementary groups.
*
* Kees J. Bot - Jan 3 1996
* - ported back to standard Minix.
*/
#define _MINIX_SOURCE
#define _POSIX_C_SOURCE 2
#include <sys/types.h>
#include <ttyent.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utmp.h>
#include <time.h>
#include <sys/utsname.h>
#include <minix/minlib.h>
#include <paths.h>
char PATH_UTMP[] = _PATH_UTMP; /* current logins */
char PATH_WTMP[] = _PATH_WTMP; /* login/logout history */
char PATH_LASTLOG[] = _PATH_LASTLOG; /* last login history */
char PATH_MOTD[] = _PATH_MOTD; /* message of the day */
#define TTY_GID 4 /* group ID of ttys */
#define EXTRA_ENV 6
/* Crude indication of a tty being physically secure: */
#define securetty(dev) ((unsigned) ((dev) - 0x0400) < (unsigned) 8)
int time_out;
char *hostname;
char user[32];
char logname[35];
char home[128];
char shell[128];
char term[128];
char **env;
extern char **environ;
int main(int argc, char **argv);
void wtmp(char *user, int uid);
void show_file(char *nam);
void Time_out(int dummy);
void usage(void);
void add2env(char **env, char *entry, int replace);
void wtmp(user, uid)
char *user; /* user name */
int uid; /* user id */
{
/* Make entries in /usr/adm/wtmp and /etc/utmp. */
struct utmp entry;
register int fd= -1;
int lineno;
int err = 0;
char *what;
/* First, read the current UTMP entry. we need some of its
* parameters! (like PID, ID etc...).
*/
what= "ttyslot()";
lineno= ttyslot();
if (lineno == 0) err= errno; /* ttyslot failed */
if (err == 0 && (fd = open(what = PATH_UTMP, O_RDONLY)) < 0) {
if (errno == ENOENT) return;
err= errno;
}
if (err == 0 && lseek(fd, (off_t) lineno * sizeof(entry), SEEK_SET) < 0)
err= errno;
if (err == 0 && read(fd, (char *) &entry, sizeof(entry)) != sizeof(entry))
err= errno;
if (fd >= 0) close(fd);
/* Enter new fields. */
#ifdef __NBSD_LIBC
strncpy(entry.ut_name, user, sizeof(entry.ut_name));
#else
strncpy(entry.ut_user, user, sizeof(entry.ut_user));
#endif
if (hostname) strncpy(entry.ut_host, hostname, sizeof(entry.ut_host));
if (entry.ut_pid == 0) entry.ut_pid = getpid();
entry.ut_type = USER_PROCESS; /* we are past login... */
time(&entry.ut_time);
/* Write a WTMP record. */
if (err == 0) {
if ((fd = open(what = PATH_WTMP, O_WRONLY|O_APPEND)) < 0) {
if (errno != ENOENT) err= errno;
} else {
if (write(fd, (char *) &entry, sizeof(entry)) < 0) err= errno;
close(fd);
}
}
/* Rewrite the UTMP entry. */
if (err == 0 && (fd = open(what = PATH_UTMP, O_WRONLY)) < 0)
err= errno;
if (err == 0 && lseek(fd, (off_t) lineno * sizeof(entry), SEEK_SET) < 0)
err= errno;
if (err == 0 && write(fd, (char *) &entry, sizeof(entry)) < 0)
err= errno;
if (fd >= 0) close(fd);
/* Write the LASTLOG entry. */
if (err == 0 && (fd = open(what = PATH_LASTLOG, O_WRONLY)) < 0) {
if (errno == ENOENT) return;
err= errno;
}
if (err == 0 && lseek(fd, (off_t) uid * sizeof(entry), SEEK_SET) < 0)
err= errno;
if (err == 0 && write(fd, (char *) &entry, sizeof(entry)) < 0)
err= errno;
if (fd >= 0) close(fd);
if (err != 0) {
fprintf(stderr, "login: %s: %s\n", what, strerror(err));
return;
}
}
void show_file(nam)
char *nam;
{
/* Read a textfile and show it on the desired terminal. */
register int fd, len;
char buf[80];
if ((fd = open(nam, O_RDONLY)) > 0) {
len = 1;
while (len > 0) {
len = read(fd, buf, 80);
write(1, buf, len);
}
close(fd);
}
}
int main(argc, argv)
int argc;
char *argv[];
{
char name[30];
char *password, *cryptedpwd;
char *tty_name, *p;
int n, ap, check_pw, bad, secure, i, envsiz, do_banner;
struct passwd *pwd;
char *bp, *argx[8], **ep; /* pw_shell arguments */
char argx0[64]; /* argv[0] of the shell */
char *sh = "/bin/sh"; /* sh/pw_shell field value */
char *initialname;
int c, b_flag, f_flag, p_flag;
char *h_arg;
int authorized, preserv_env;
struct ttyent *ttyp;
struct stat ttystat;
struct sigaction sa;
struct utsname uts;
/* Don't let QUIT dump core. */
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = exit;
sigaction(SIGQUIT, &sa, NULL);
/* Parse options. */
b_flag= 0;
f_flag= 0;
p_flag= 0;
h_arg= NULL;
while ((c= getopt(argc, argv, "?bfh:p")) != -1)
{
switch(c)
{
case 'b': b_flag= 1; break;
case 'f': f_flag= 1; break;
case 'h':
if (h_arg)
usage();
if (getuid() == 0)
h_arg= optarg;
break;
case 'p': p_flag= 1; break;
case '?':
usage();
default:
fprintf(stderr, "login: getopt failed: '%c'\n", c);
exit(1);
}
}
if (optind < argc)
initialname= argv[optind++];
else
initialname= NULL;
if (optind != argc)
usage();
authorized= f_flag;
hostname= h_arg;
preserv_env= p_flag;
do_banner= b_flag;
/* Look up /dev/tty number. */
tty_name= ttyname(0);
if (tty_name == NULL)
{
write(1, "Unable to lookup tty name\n", 26);
exit(1);
}
if (do_banner)
{
uname(&uts);
write(1, "\n", 1);
write(1, uts.sysname, strlen(uts.sysname));
write(1, "/", 1);
write(1, uts.machine, strlen(uts.machine));
write(1, " Release ", 9);
write(1, uts.release, strlen(uts.release));
write(1, " Version ", 9);
write(1, uts.version, strlen(uts.version));
write(1, " (", 2);
p= strrchr(tty_name, '/');
if (!p)
p= tty_name;
else
p++;
write(1, p, strlen(p));
write(1, ")\n\n", 3);
write(1, uts.nodename, strlen(uts.nodename));
write(1, " ", 1);
}
/* Get login name and passwd. */
for (;;initialname= NULL) {
if (initialname)
strcpy(name, initialname);
else {
do {
write(1, "login: ", 7);
n = read(0, name, 30);
if (n == 0) exit(1);
if (n < 0)
{
if (errno != EINTR)
fprintf(stderr,
"login: read failed: %s\n",
strerror(errno));
exit(1);
}
} while (n < 2);
name[n - 1] = 0;
}
/* Start timer running. */
time_out = 0;
sa.sa_handler = Time_out;
sigaction(SIGALRM, &sa, NULL);
alarm(60);
/* Look up login/passwd. */
pwd = getpwnam(name);
check_pw = 1; /* default is check password. */
/* For now, only console is secure. */
secure = fstat(0, &ttystat) == 0 && securetty(ttystat.st_rdev);
if (pwd && authorized && initialname
&& (pwd->pw_uid == getuid() || getuid() == 0)) {
check_pw= 0; /* Don't ask a password for
* pre-authorized users.
*/
} else
if (pwd && secure && (pwd->pw_passwd[0] == '\0')) {
check_pw= 0; /* empty password, pretend password okay */
}
if (check_pw) {
password = getpass("Password:");
if (time_out) exit(1);
bad = 0;
if (!pwd) bad = 1;
if (!password) { password = ""; bad = 1; }
if (!secure && pwd && strcmp(crypt("", pwd->pw_passwd),
pwd->pw_passwd) == 0) bad = 1;
cryptedpwd = bad ? "*" : pwd->pw_passwd;
if (strcmp(crypt(password, cryptedpwd), cryptedpwd) != 0) {
write(1, "Login incorrect\n", 16);
continue;
}
}
/* Check if the system is going down */
if (access("/etc/nologin", 0) == 0 && strcmp(name, "root") != 0) {
write(1, "System going down\n\n", 19);
continue;
}
/* Stop timer. */
alarm(0);
/* Write login record to /usr/adm/wtmp and /etc/utmp */
wtmp(name, pwd->pw_uid);
/* Create the argv[] array from the pw_shell field. */
ap = 0;
argx[ap++] = argx0; /* "-sh" most likely */
if (pwd->pw_shell[0]) {
sh = pwd->pw_shell;
bp = sh;
while (*bp) {
while (*bp && *bp != ' ' && *bp != '\t') bp++;
if (*bp == ' ' || *bp == '\t') {
*bp++ = '\0'; /* mark end of string */
argx[ap++] = bp;
}
}
} else
argx[ap] = NULL;
strcpy(argx0, "-"); /* most shells need it for their .profile */
if ((bp= strrchr(sh, '/')) == NULL) bp = sh; else bp++;
strncat(argx0, bp, sizeof(argx0) - 2);
/* Set the environment */
if (p_flag)
{
for (ep= environ; *ep; ep++)
;
}
else
ep= environ;
envsiz= ep-environ;
env= calloc(envsiz + EXTRA_ENV, sizeof(*env));
if (env == NULL)
{
fprintf(stderr, "login: out of memory\n");
exit(1);
}
for (i= 0; i<envsiz; i++)
env[i]= environ[i];
strcpy(user, "USER=");
strcat(user, name);
add2env(env, user, 1);
strcpy(logname, "LOGNAME=");
strcat(logname, name);
add2env(env, logname, 1);
strcpy(home, "HOME=");
strcat(home, pwd->pw_dir);
add2env(env, home, 1);
strcpy(shell, "SHELL=");
strcat(shell, sh);
add2env(env, shell, 1);
if ((ttyp = getttynam(tty_name + 5)) != NULL) {
strcpy(term, "TERM=");
strcat(term, ttyp->ty_type);
add2env(env, term, 0);
}
/* Show the message-of-the-day. */
show_file(PATH_MOTD);
/* Assign the terminal to this user. */
chown(tty_name, pwd->pw_uid, TTY_GID);
chmod(tty_name, 0620);
/* Change id. */
initgroups(pwd->pw_name, pwd->pw_gid);
setgid(pwd->pw_gid);
setuid(pwd->pw_uid);
/* cd $HOME */
chdir(pwd->pw_dir);
/* Reset signals to default values. */
sa.sa_handler = SIG_DFL;
for (n = 1; n < _NSIG; ++n) sigaction(n, &sa, NULL);
/* Execute the user's shell. */
execve(sh, argx, env);
if (pwd->pw_gid == 0) {
/* Privileged user gets /bin/sh in times of crisis. */
sh= "/bin/sh";
argx[0]= "-sh";
strcpy(shell, "SHELL=");
strcat(shell, sh);
execve(sh, argx, env);
}
fprintf(stderr, "login: can't execute %s: %s\n", sh, strerror(errno));
exit(1);
}
return(0);
}
void Time_out(dummy)
int dummy; /* to keep the compiler happy */
{
write(2, "\r\nLogin timed out after 60 seconds\r\n", 36);
time_out = 1;
}
void usage()
{
fprintf(stderr,
"Usage: login [-h hostname] [-b] [-f] [-p] [username]\n");
exit(1);
}
void add2env(env, entry, replace)
char **env;
char *entry;
int replace;
{
/* Replace an environment variable with entry or add entry if the environment
* variable doesn't exit yet.
*/
char *cp;
int keylen;
cp= strchr(entry, '=');
keylen= cp-entry+1;
for(; *env; env++)
{
if (strncmp(*env, entry, keylen) == 0) {
if (!replace) return; /* Don't replace */
break;
}
}
*env= entry;
}
/*
* $PchId: login.c,v 1.6 2001/07/31 14:23:28 philip Exp $
*/

View file

@ -19,7 +19,7 @@ SRCS+= efun.c getbootfile.c getlabelsector.c getmaxpartitions.c \
logwtmp.c logwtmpx.c opendisk.c parsedate.y \
passwd.c pw_scan.c pidfile.c pidlock.c pty.c \
raise_default_signal.c \
secure_path.c sockaddr_snprintf.c stat_flags.c \
secure_path.c stat_flags.c \
ttyaction.c ttymsg.c \
MAN= efun.3 getbootfile.3 getlabelsector.3 getmaxpartitions.3 \
@ -35,7 +35,7 @@ MAN= efun.3 getbootfile.3 getlabelsector.3 getmaxpartitions.3 \
.else
SRCS+= efun.c getbootfile.c \
getmntopts.c \
getmntopts.c sockaddr_snprintf.c\
login.c loginx.c login_cap.c login_tty.c logout.c logoutx.c \
logwtmp.c logwtmpx.c opendisk.c \
passwd.c pw_scan.c pidfile.c pidlock.c pty.c \

View file

@ -38,8 +38,6 @@ __RCSID("$NetBSD: sockaddr_snprintf.c,v 1.9 2008/04/28 20:23:03 martin Exp $");
#include <sys/un.h>
#include <netinet/in.h>
#include <netatalk/at.h>
#include <net/if_dl.h>
#include <stdio.h>
#include <string.h>
@ -57,11 +55,13 @@ sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt,
char *ebuf = &sbuf[len - 1], *buf = sbuf;
const char *ptr, *s;
int p = -1;
const struct sockaddr_at *sat = NULL;
const struct sockaddr_in *sin4 = NULL;
#ifndef __minix
const struct sockaddr_at *sat = NULL;
const struct sockaddr_in6 *sin6 = NULL;
const struct sockaddr_un *sun = NULL;
const struct sockaddr_dl *sdl = NULL;
#endif
int na = 1;
#define ADDC(c) do { if (buf < ebuf) *buf++ = c; else buf++; } \
@ -74,6 +74,7 @@ sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt,
switch (sa->sa_family) {
case AF_UNSPEC:
goto done;
#ifndef __minix
case AF_APPLETALK:
sat = ((const struct sockaddr_at *)(const void *)sa);
p = ntohs(sat->sat_port);
@ -85,11 +86,13 @@ sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt,
sun = ((const struct sockaddr_un *)(const void *)sa);
(void)strlcpy(addr = abuf, sun->sun_path, SUN_LEN(sun));
break;
#endif
case AF_INET:
sin4 = ((const struct sockaddr_in *)(const void *)sa);
p = ntohs(sin4->sin_port);
a = &sin4->sin_addr;
break;
#ifndef __minix
case AF_INET6:
sin6 = ((const struct sockaddr_in6 *)(const void *)sa);
p = ntohs(sin6->sin6_port);
@ -103,6 +106,7 @@ sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt,
addr = w;
}
break;
#endif
default:
errno = EAFNOSUPPORT;
return -1;
@ -111,7 +115,7 @@ sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt,
if (addr == abuf)
name = addr;
if (a && getnameinfo(sa, (socklen_t)sa->sa_len, addr = abuf,
if (a && getnameinfo(sa, (socklen_t)len, addr = abuf,
(unsigned int)sizeof(abuf), NULL, 0,
NI_NUMERICHOST|NI_NUMERICSERV) != 0)
return -1;
@ -141,7 +145,7 @@ sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt,
ADDS(nbuf);
break;
case 'l':
(void)snprintf(nbuf, sizeof(nbuf), "%d", sa->sa_len);
(void)snprintf(nbuf, sizeof(nbuf), "%d", len);
ADDS(nbuf);
break;
case 'A':
@ -150,7 +154,7 @@ sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt,
else if (!a)
ADDNA();
else {
getnameinfo(sa, (socklen_t)sa->sa_len,
getnameinfo(sa, (socklen_t)len,
name = Abuf,
(unsigned int)sizeof(nbuf), NULL, 0, 0);
ADDS(name);
@ -162,12 +166,13 @@ sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt,
else if (p == -1)
ADDNA();
else {
getnameinfo(sa, (socklen_t)sa->sa_len, NULL, 0,
getnameinfo(sa, (socklen_t)len, NULL, 0,
port = pbuf,
(unsigned int)sizeof(pbuf), 0);
ADDS(port);
}
break;
#ifndef __minix
case 'I':
if (sdl && addr != abuf) {
ADDS(abuf);
@ -207,6 +212,7 @@ sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt,
ADDNA();
}
break;
#endif
default:
ADDC('%');
if (na == 0)

View file

@ -8,7 +8,7 @@ MAN= ash.1 at.1 banner.1 basename.1 \
finger.1 flexdoc.1 fmt.1 fold.1 format.1 fortune.1 \
fsck.mfs.1 head.1 host.1 hostaddr.1 ifdef.1 \
install.1 isodir.1 isoinfo.1 isoread.1 join.1 kill.1 \
last.1 loadfont.1 loadkeys.1 logger.1 login.1 \
last.1 loadfont.1 loadkeys.1 logger.1 \
look.1 lp.1 ls.1 lspci.1 mail.1 \
mesg.1 mixer.1 mkfs.1 \
mkproto.1 mount.1 mt.1 nice.1 nm.1 nohup.1 od.1 \

View file

@ -1,30 +0,0 @@
.TH LOGIN 1
.SH NAME
login \- log into the computer
.SH SYNOPSIS
\fBlogin\fR [\fIuser\fR]\fR
.br
.de FL
.TP
\\fB\\$1\\fR
\\$2
..
.de EX
.TP 20
\\fB\\$1\\fR
# \\$2
..
.SH EXAMPLES
.TP 20
.B login ast
# Login as ast
.SH DESCRIPTION
.PP
\fILogin\fR allows a logged in user to login as someone else without first
logging out.
If a password is needed, \fIlogin\fR will prompt for it.
.SH "SEE ALSO"
.BR su (1),
.BR init (8),
.BR getty (8),
.BR rlogin (1).

1
sys/conf/copyright Normal file
View file

@ -0,0 +1 @@
Copyright (c) 2012, Vrije Universiteit, Amsterdam, The Netherlands

View file

@ -59,3 +59,4 @@
2012/01/16 18:47:57,sys/lib/libsa
2011/10/30 00:28:57,sys/lib/libz
2012/02/10 16:16:12,external/bsd/file
2012/02/10 16:16:12,usr.bin/login

View file

@ -3,7 +3,7 @@
.include <bsd.own.mk>
# NetBSD imports
SUBDIR= indent m4 stat tic sed mkdep uniq seq du man \
SUBDIR= login indent m4 stat tic sed mkdep uniq seq du man \
apropos chpass newgrp passwd bzip2 bzip2recover gzip su genassym
# Non-NetBSD imports

57
usr.bin/login/Makefile Normal file
View file

@ -0,0 +1,57 @@
# $NetBSD: Makefile,v 1.52 2011/04/24 21:42:06 elric Exp $
# @(#)Makefile 8.1 (Berkeley) 7/19/93
WARNS?= 2 # XXX -Wcast-qual issues
USE_PAM?= no
USE_KERBEROS?= no
.include <bsd.own.mk>
USE_FORT?= no # setuid
PROG= login
SRCS= copyrightstr.c
DPADD+= ${LIBUTIL} ${LIBCRYPT}
LDADD+= -lutil -lcrypt
BINOWN= root
BINMODE=4555
SRCS+= common.c
.if (${USE_PAM} != "no")
SRCS+= login_pam.c
LDADD+= -lpam ${PAM_STATIC_LDADD}
DPADD+= ${LIBPAM} ${PAM_STATIC_DPADD}
.else # USE_PAM == no
SRCS+= login.c
CPPFLAGS+= -DSUPPORT_UTMP -DSUPPORT_UTMPX #-DLOGIN_CAP
.if (${USE_KERBEROS} != "no")
SRCS+= k5login.c
CPPFLAGS+=-DKERBEROS5
DPADD+= ${LIBKRB5} ${LIBASN1}
LDADD+= -lkrb5 -lasn1
DPADD+= ${LIBCRYPTO} ${LIBROKEN} ${LIBCOM_ERR}
LDADD+= -lcrypto -lroken -lcom_err
.endif
.if (${USE_SKEY} != "no")
CPPFLAGS+=-DSKEY
DPADD+= ${LIBSKEY}
LDADD+= -lskey
.endif
.endif # USE_PAM == no
CLEANFILES+= copyrightstr.c
copyrightstr.c: ${NETBSDSRCDIR}/sys/conf/copyright
${_MKTARGET_CREATE}
rm -f ${.TARGET}
${TOOL_AWK} '\
BEGIN { print "const char copyrightstr[] =" }\
{ print "\""$$0"\\n\""}\
END { print "\"\\n\";" }\
' ${.ALLSRC} > ${.TARGET}
.include <bsd.prog.mk>

393
usr.bin/login/common.c Normal file
View file

@ -0,0 +1,393 @@
/* $NetBSD: common.c,v 1.3 2009/12/29 20:15:15 christos Exp $ */
/*-
* Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
* The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE REGENTS OR CONTRIBUTORS 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.
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: common.c,v 1.3 2009/12/29 20:15:15 christos Exp $");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <syslog.h>
#include <fcntl.h>
#include <ttyent.h>
#include <setjmp.h>
#include <time.h>
#include <pwd.h>
#include <err.h>
#include <vis.h>
#include <util.h>
#include "pathnames.h"
#include "common.h"
#if defined(KERBEROS5)
#define NBUFSIZ (MAXLOGNAME + 1 + 5) /* .root suffix */
#else
#define NBUFSIZ (MAXLOGNAME + 1)
#endif
#ifdef SUPPORT_UTMP
#include <utmp.h>
static void doutmp(void);
static void dolastlog(int);
#endif
#ifdef SUPPORT_UTMPX
#include <utmpx.h>
static void doutmpx(void);
static void dolastlogx(int);
#endif
/*
* This bounds the time given to login. Not a define so it can
* be patched on machines where it's too small.
*/
u_int timeout = 300;
void decode_ss(const char *);
struct passwd *pwd;
int failures, have_ss;
char term[64], *envinit[1], *hostname, *username, *tty, *nested;
struct timeval now;
struct sockaddr_storage ss;
void
getloginname(void)
{
int ch;
char *p;
static char nbuf[NBUFSIZ];
for (;;) {
(void)printf("login: ");
for (p = nbuf; (ch = getchar()) != '\n'; ) {
if (ch == EOF) {
badlogin(username);
exit(EXIT_FAILURE);
}
if (p < nbuf + (NBUFSIZ - 1))
*p++ = ch;
}
if (p > nbuf) {
if (nbuf[0] == '-')
(void)fprintf(stderr,
"login names may not start with '-'.\n");
else {
*p = '\0';
username = nbuf;
break;
}
}
}
}
int
rootterm(char *ttyn)
{
struct ttyent *t;
return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
}
static jmp_buf motdinterrupt;
void
motd(char *fname)
{
int fd, nchars;
sig_t oldint;
char tbuf[8192];
if ((fd = open(fname ? fname : _PATH_MOTDFILE, O_RDONLY, 0)) < 0)
return;
oldint = signal(SIGINT, sigint);
if (setjmp(motdinterrupt) == 0)
while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
(void)write(fileno(stdout), tbuf, nchars);
(void)signal(SIGINT, oldint);
(void)close(fd);
}
/* ARGSUSED */
void
sigint(int signo)
{
longjmp(motdinterrupt, 1);
}
/* ARGSUSED */
void
timedout(int signo)
{
(void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
exit(EXIT_FAILURE);
}
void
update_db(int quietlog, int rootlogin, int fflag)
{
struct sockaddr_storage ass;
char assbuf[1024];
socklen_t alen;
const char *hname;
int remote;
hname = (hostname == NULL) ? "?" : hostname;
if (getpeername(STDIN_FILENO, (struct sockaddr *)&ass, &alen) != -1) {
(void)sockaddr_snprintf(assbuf,
sizeof(assbuf), "%A (%a)", (void *)&ass);
if (have_ss) {
char ssbuf[1024];
(void)sockaddr_snprintf(ssbuf,
sizeof(ssbuf), "%A(%a)", (void *)&ss);
if (memcmp(&ass, &ss, alen) != 0)
syslog(LOG_NOTICE,
"login %s on tty %s address mismatch "
"passed %s != actual %s", username, tty,
ssbuf, assbuf);
} else
ss = ass;
remote = 1;
} else if (have_ss) {
(void)sockaddr_snprintf(assbuf,
sizeof(assbuf), "%A(%a)", (void *)&ss);
remote = 1;
} else if (hostname) {
(void)snprintf(assbuf, sizeof(assbuf), "? ?");
remote = 1;
} else
remote = 0;
/* If fflag is on, assume caller/authenticator has logged root login. */
if (rootlogin && fflag == 0) {
if (remote)
syslog(LOG_NOTICE, "ROOT LOGIN (%s) on tty %s from %s /"
" %s", username, tty, hname, assbuf);
else
syslog(LOG_NOTICE, "ROOT LOGIN (%s) on tty %s",
username, tty);
} else if (nested != NULL) {
if (remote)
syslog(LOG_NOTICE, "%s to %s on tty %s from %s / "
"%s", nested, pwd->pw_name, tty, hname, assbuf);
else
syslog(LOG_NOTICE, "%s to %s on tty %s", nested,
pwd->pw_name, tty);
} else {
if (remote)
syslog(LOG_NOTICE, "%s on tty %s from %s / %s",
pwd->pw_name, tty, hname, assbuf);
else
syslog(LOG_NOTICE, "%s on tty %s",
pwd->pw_name, tty);
}
(void)gettimeofday(&now, NULL);
#ifdef SUPPORT_UTMPX
doutmpx();
dolastlogx(quietlog);
quietlog = 1;
#endif
#ifdef SUPPORT_UTMP
doutmp();
dolastlog(quietlog);
#endif
}
#ifdef SUPPORT_UTMPX
static void
doutmpx(void)
{
struct utmpx utmpx;
char *t;
memset((void *)&utmpx, 0, sizeof(utmpx));
utmpx.ut_tv = now;
(void)strncpy(utmpx.ut_name, username, sizeof(utmpx.ut_name));
if (hostname) {
(void)strncpy(utmpx.ut_host, hostname, sizeof(utmpx.ut_host));
utmpx.ut_ss = ss;
}
(void)strncpy(utmpx.ut_line, tty, sizeof(utmpx.ut_line));
utmpx.ut_type = USER_PROCESS;
utmpx.ut_pid = getpid();
t = tty + strlen(tty);
if (t - tty >= sizeof(utmpx.ut_id)) {
(void)strncpy(utmpx.ut_id, t - sizeof(utmpx.ut_id),
sizeof(utmpx.ut_id));
} else {
(void)strncpy(utmpx.ut_id, tty, sizeof(utmpx.ut_id));
}
if (pututxline(&utmpx) == NULL)
syslog(LOG_NOTICE, "Cannot update utmpx: %m");
endutxent();
if (updwtmpx(_PATH_WTMPX, &utmpx) != 0)
syslog(LOG_NOTICE, "Cannot update wtmpx: %m");
}
static void
dolastlogx(int quiet)
{
struct lastlogx ll;
if (!quiet && getlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != NULL) {
time_t t = (time_t)ll.ll_tv.tv_sec;
(void)printf("Last login: %.24s ", ctime(&t));
if (*ll.ll_host != '\0')
(void)printf("from %.*s ",
(int)sizeof(ll.ll_host),
ll.ll_host);
(void)printf("on %.*s\n",
(int)sizeof(ll.ll_line),
ll.ll_line);
}
ll.ll_tv = now;
(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
if (hostname)
(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
else
(void)memset(ll.ll_host, '\0', sizeof(ll.ll_host));
if (have_ss)
ll.ll_ss = ss;
else
(void)memset(&ll.ll_ss, 0, sizeof(ll.ll_ss));
if (updlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != 0)
syslog(LOG_NOTICE, "Cannot update lastlogx: %m");
}
#endif
#ifdef SUPPORT_UTMP
static void
doutmp(void)
{
struct utmp utmp;
(void)memset((void *)&utmp, 0, sizeof(utmp));
utmp.ut_time = now.tv_sec;
(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);
}
static void
dolastlog(int quiet)
{
struct lastlog ll;
int fd;
if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
(void)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) {
(void)printf("Last login: %.24s ",
ctime(&ll.ll_time));
if (*ll.ll_host != '\0')
(void)printf("from %.*s ",
(int)sizeof(ll.ll_host),
ll.ll_host);
(void)printf("on %.*s\n",
(int)sizeof(ll.ll_line), ll.ll_line);
}
(void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)),
SEEK_SET);
}
memset((void *)&ll, 0, sizeof(ll));
ll.ll_time = now.tv_sec;
(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
if (hostname)
(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
(void)write(fd, (char *)&ll, sizeof(ll));
(void)close(fd);
}
}
#endif
void
badlogin(const char *name)
{
if (failures == 0)
return;
if (hostname) {
syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
failures, failures > 1 ? "S" : "", hostname);
syslog(LOG_AUTHPRIV|LOG_NOTICE,
"%d LOGIN FAILURE%s FROM %s, %s",
failures, failures > 1 ? "S" : "", hostname, name);
} else {
syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
failures, failures > 1 ? "S" : "", tty);
syslog(LOG_AUTHPRIV|LOG_NOTICE,
"%d LOGIN FAILURE%s ON %s, %s",
failures, failures > 1 ? "S" : "", tty, name);
}
}
const char *
stypeof(const char *ttyid)
{
struct ttyent *t;
return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : NULL);
}
void
sleepexit(int eval)
{
(void)sleep(5);
exit(eval);
}
void
decode_ss(const char *arg)
{
struct sockaddr_storage *ssp;
size_t len = strlen(arg);
if (len > sizeof(*ssp) * 4 + 1 || len < sizeof(*ssp))
errx(EXIT_FAILURE, "Bad argument");
if ((ssp = malloc(len)) == NULL)
err(EXIT_FAILURE, NULL);
if (strunvis((char *)ssp, arg) != sizeof(*ssp))
errx(EXIT_FAILURE, "Decoding error");
(void)memcpy(&ss, ssp, sizeof(ss));
free(ssp);
have_ss = 1;
}

53
usr.bin/login/common.h Normal file
View file

@ -0,0 +1,53 @@
/* $NetBSD: common.h,v 1.1 2009/12/29 19:26:13 christos Exp $ */
/*-
* Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
* The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE REGENTS OR CONTRIBUTORS 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.
*/
__BEGIN_DECLS
void badlogin(const char *);
void update_db(int, int, int);
void getloginname(void);
void motd(char *);
int rootterm(char *);
void sigint(int);
void sleepexit(int);
const char *stypeof(const char *);
void timedout(int);
void decode_ss(const char *);
extern u_int timeout;
extern struct passwd *pwd;
extern int failures, have_ss;
extern char term[64], *envinit[1], *hostname, *username, *tty, *nested;
extern struct timeval now;
extern struct sockaddr_storage ss;
extern const char copyrightstr[];
__END_DECLS

478
usr.bin/login/k5login.c Normal file
View file

@ -0,0 +1,478 @@
/* $NetBSD: k5login.c,v 1.27 2006/03/23 23:33:28 wiz Exp $ */
/*-
* Copyright (c) 1990 The Regents of the University of California.
* 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. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE REGENTS OR CONTRIBUTORS 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.
*/
/*
* 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.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)klogin.c 5.11 (Berkeley) 7/12/92";
#endif
__RCSID("$NetBSD: k5login.c,v 1.27 2006/03/23 23:33:28 wiz Exp $");
#endif /* not lint */
#ifdef KERBEROS5
#include <sys/param.h>
#include <sys/syslog.h>
#include <krb5/krb5.h>
#include <pwd.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#define KRB5_DEFAULT_OPTIONS 0
#define KRB5_DEFAULT_LIFE 60*60*10 /* 10 hours */
krb5_context kcontext;
int notickets;
int krb5_configured;
char *krb5tkfile_env;
extern char *tty;
extern int login_krb5_forwardable_tgt;
extern int has_ccache;
static char tkt_location[MAXPATHLEN];
static krb5_creds forw_creds;
int have_forward;
static krb5_principal me, server;
int k5_read_creds(char *);
int k5_write_creds(void);
int k5_verify_creds(krb5_context, krb5_ccache);
int k5login(struct passwd *, char *, char *, char *);
void k5destroy(void);
#ifndef krb5_realm_length
#define krb5_realm_length(r) ((r).length)
#endif
#ifndef krb5_realm_data
#define krb5_realm_data(r) ((r).data)
#endif
/*
* Verify the Kerberos ticket-granting ticket just retrieved for the
* user. If the Kerberos server doesn't respond, assume the user is
* trying to fake us out (since we DID just get a TGT from what is
* supposedly our KDC). If the host/<host> service is unknown (i.e.,
* the local keytab doesn't have it), let her in.
*
* Returns 1 for confirmation, -1 for failure, 0 for uncertainty.
*/
int
k5_verify_creds(krb5_context c, krb5_ccache ccache)
{
char phost[MAXHOSTNAMELEN];
int retval, have_keys;
krb5_principal princ;
krb5_keyblock *kb = 0;
krb5_error_code kerror;
krb5_data packet;
krb5_auth_context auth_context = NULL;
krb5_ticket *ticket = NULL;
kerror = krb5_sname_to_principal(c, 0, 0, KRB5_NT_SRV_HST, &princ);
if (kerror) {
krb5_warn(kcontext, kerror, "constructing local service name");
return (-1);
}
/* Do we have host/<host> keys? */
/* (use default keytab, kvno IGNORE_VNO to get the first match,
* and default enctype.) */
kerror = krb5_kt_read_service_key(c, NULL, princ, 0, 0, &kb);
if (kb)
krb5_free_keyblock(c, kb);
/* any failure means we don't have keys at all. */
have_keys = kerror ? 0 : 1;
/* XXX there should be a krb5 function like mk_req, but taking a full
* principal, instead of a service/hostname. (Did I miss one?) */
gethostname(phost, sizeof(phost));
phost[sizeof(phost) - 1] = '\0';
/* talk to the kdc and construct the ticket */
kerror = krb5_mk_req(c, &auth_context, 0, "host", phost,
0, ccache, &packet);
/* wipe the auth context for rd_req */
if (auth_context) {
krb5_auth_con_free(c, auth_context);
auth_context = NULL;
}
if (kerror == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
/* we have a service key, so something should be
* in the database, therefore this error packet could
* have come from an attacker. */
if (have_keys) {
retval = -1;
goto EGRESS;
}
/* but if it is unknown and we've got no key, we don't
* have any security anyhow, so it is ok. */
else {
retval = 0;
goto EGRESS;
}
}
else if (kerror) {
krb5_warn(kcontext, kerror,
"Unable to verify Kerberos V5 TGT: %s", phost);
syslog(LOG_NOTICE, "Kerberos V5 TGT bad: %s",
krb5_get_err_text(kcontext, kerror));
retval = -1;
goto EGRESS;
}
/* got ticket, try to use it */
kerror = krb5_rd_req(c, &auth_context, &packet,
princ, NULL, NULL, &ticket);
if (kerror) {
if (!have_keys) {
/* The krb5 errors aren't specified well, but I think
* these values cover the cases we expect. */
switch (kerror) {
case ENOENT: /* no keytab */
case KRB5_KT_NOTFOUND:
retval = 0;
break;
default:
/* unexpected error: fail */
retval = -1;
break;
}
}
else {
/* we have keys, so if we got any error, we could be
* under attack. */
retval = -1;
}
krb5_warn(kcontext, kerror, "Unable to verify host ticket");
syslog(LOG_NOTICE, "can't verify v5 ticket: %s; %s\n",
krb5_get_err_text(kcontext, kerror),
retval
? "keytab found, assuming failure"
: "no keytab found, assuming success");
goto EGRESS;
}
/*
* The host/<host> ticket has been received _and_ verified.
*/
retval = 1;
/* do cleanup and return */
EGRESS:
if (auth_context)
krb5_auth_con_free(c, auth_context);
krb5_free_principal(c, princ);
/* possibly ticket and packet need freeing here as well */
return (retval);
}
/*
* Attempt to read forwarded kerberos creds
*
* return 0 on success (forwarded creds in memory)
* 1 if no forwarded creds.
*/
int
k5_read_creds(char *username)
{
krb5_error_code kerror;
krb5_creds mcreds;
krb5_ccache ccache;
have_forward = 0;
memset((char*) &mcreds, 0, sizeof(forw_creds));
memset((char*) &forw_creds, 0, sizeof(forw_creds));
kerror = krb5_cc_default(kcontext, &ccache);
if (kerror) {
krb5_warn(kcontext, kerror, "while getting default ccache");
return(1);
}
kerror = krb5_parse_name(kcontext, username, &me);
if (kerror) {
krb5_warn(kcontext, kerror, "when parsing name %s", username);
return(1);
}
mcreds.client = me;
kerror = krb5_build_principal_ext(kcontext, &mcreds.server,
krb5_realm_length(*krb5_princ_realm(kcontext, me)),
krb5_realm_data(*krb5_princ_realm(kcontext, me)),
KRB5_TGS_NAME_SIZE,
KRB5_TGS_NAME,
krb5_realm_length(*krb5_princ_realm(kcontext, me)),
krb5_realm_data(*krb5_princ_realm(kcontext, me)),
0);
if (kerror) {
krb5_warn(kcontext, kerror, "while building server name");
goto nuke_ccache;
}
kerror = krb5_cc_retrieve_cred(kcontext, ccache, 0,
&mcreds, &forw_creds);
if (kerror) {
krb5_warn(kcontext, kerror,
"while retrieving V5 initial ticket for copy");
goto nuke_ccache;
}
have_forward = 1;
strlcpy(tkt_location, getenv("KRB5CCNAME"), sizeof(tkt_location));
krb5tkfile_env = tkt_location;
has_ccache = 1;
notickets = 0;
nuke_ccache:
krb5_cc_destroy(kcontext, ccache);
return(!have_forward);
}
int
k5_write_creds(void)
{
krb5_error_code kerror;
krb5_ccache ccache;
if (!have_forward)
return (1);
kerror = krb5_cc_default(kcontext, &ccache);
if (kerror) {
krb5_warn(kcontext, kerror, "while getting default ccache");
return (1);
}
kerror = krb5_cc_initialize(kcontext, ccache, me);
if (kerror) {
krb5_warn(kcontext, kerror,
"while re-initializing V5 ccache as user");
goto nuke_ccache_contents;
}
kerror = krb5_cc_store_cred(kcontext, ccache, &forw_creds);
if (kerror) {
krb5_warn(kcontext, kerror,
"while re-storing V5 ccache as user");
goto nuke_ccache_contents;
}
nuke_ccache_contents:
krb5_free_cred_contents(kcontext, &forw_creds);
return (kerror != 0);
}
/*
* Attempt to log the user in using Kerberos authentication
*
* return 0 on success (will be logged in)
* 1 if Kerberos failed (try local password in login)
*/
int
k5login(struct passwd *pw, char *instance, char *localhost, char *password)
{
krb5_error_code kerror;
krb5_creds my_creds;
krb5_timestamp now;
krb5_ccache ccache = NULL;
long lifetime = KRB5_DEFAULT_LIFE;
int options = KRB5_DEFAULT_OPTIONS;
char *realm, *client_name;
char *principal;
krb5_configured = 1;
if (login_krb5_forwardable_tgt)
options |= KDC_OPT_FORWARDABLE;
/*
* Root logins don't use Kerberos.
* If we have a realm, try getting a ticket-granting ticket
* and using it to authenticate. Otherwise, return
* failure so that we can try the normal passwd file
* for a password. If that's ok, log the user in
* without issuing any tickets.
*/
if (strcmp(pw->pw_name, "root") == 0 ||
krb5_get_default_realm(kcontext, &realm)) {
krb5_configured = 0;
return (1);
}
/*
* get TGT for local realm
* tickets are stored in a file named TKT_ROOT plus uid
* except for user.root tickets.
*/
if (strcmp(instance, "root") != 0)
(void)snprintf(tkt_location, sizeof tkt_location,
"FILE:/tmp/krb5cc_%d.%s", pw->pw_uid, tty);
else
(void)snprintf(tkt_location, sizeof tkt_location,
"FILE:/tmp/krb5cc_root_%d.%s", pw->pw_uid, tty);
krb5tkfile_env = tkt_location;
has_ccache = 1;
if (strlen(instance))
asprintf(&principal, "%s/%s", pw->pw_name, instance);
else
principal = strdup(pw->pw_name);
if (!principal) {
syslog(LOG_NOTICE, "fatal: %s", strerror(errno));
return (1);
}
if ((kerror = krb5_cc_resolve(kcontext, tkt_location, &ccache)) != 0) {
syslog(LOG_NOTICE, "warning: %s while getting default ccache",
krb5_get_err_text(kcontext, kerror));
return (1);
}
if ((kerror = krb5_parse_name(kcontext, principal, &me)) != 0) {
syslog(LOG_NOTICE, "warning: %s when parsing name %s",
krb5_get_err_text(kcontext, kerror), principal);
return (1);
}
if ((kerror = krb5_unparse_name(kcontext, me, &client_name)) != 0) {
syslog(LOG_NOTICE, "warning: %s when unparsing name %s",
krb5_get_err_text(kcontext, kerror), principal);
return (1);
}
kerror = krb5_cc_initialize(kcontext, ccache, me);
if (kerror != 0) {
syslog(LOG_NOTICE, "%s when initializing cache %s",
krb5_get_err_text(kcontext, kerror), tkt_location);
return (1);
}
memset((char *)&my_creds, 0, sizeof(my_creds));
my_creds.client = me;
if ((kerror = krb5_build_principal_ext(kcontext,
&server,
krb5_realm_length(*krb5_princ_realm(kcontext, me)),
krb5_realm_data(*krb5_princ_realm(kcontext, me)),
KRB5_TGS_NAME_SIZE,
KRB5_TGS_NAME,
krb5_realm_length(*krb5_princ_realm(kcontext, me)),
krb5_realm_data(*krb5_princ_realm(kcontext, me)),
0)) != 0) {
syslog(LOG_NOTICE, "%s while building server name",
krb5_get_err_text(kcontext, kerror));
return (1);
}
my_creds.server = server;
if ((kerror = krb5_timeofday(kcontext, &now)) != 0) {
syslog(LOG_NOTICE, "%s while getting time of day",
krb5_get_err_text(kcontext, kerror));
return (1);
}
my_creds.times.starttime = 0; /* start timer when request
gets to KDC */
my_creds.times.endtime = now + lifetime;
my_creds.times.renew_till = 0;
kerror = krb5_get_in_tkt_with_password(kcontext, options,
NULL,
NULL,
NULL,
password,
ccache,
&my_creds, 0);
if (my_creds.server != NULL)
krb5_free_principal(kcontext, my_creds.server);
if (chown(&tkt_location[5], pw->pw_uid, pw->pw_gid) < 0)
syslog(LOG_ERR, "chown tkfile (%s): %m", &tkt_location[5]);
if (kerror) {
if (kerror == KRB5KRB_AP_ERR_BAD_INTEGRITY)
printf("%s: Kerberos Password incorrect\n", principal);
else
krb5_warn(kcontext, kerror,
"while getting initial credentials");
return (1);
}
if (k5_verify_creds(kcontext, ccache) < 0)
return (1);
/* Success */
notickets = 0;
return (0);
}
/*
* Remove any credentials
*/
void
k5destroy(void)
{
krb5_error_code kerror;
krb5_ccache ccache = NULL;
if (krb5tkfile_env == NULL)
return;
kerror = krb5_cc_resolve(kcontext, krb5tkfile_env, &ccache);
if (kerror == 0)
(void)krb5_cc_destroy(kcontext, ccache);
}
#endif /* KERBEROS5 */

206
usr.bin/login/login.1 Normal file
View file

@ -0,0 +1,206 @@
.\" $NetBSD: login.1,v 1.30 2008/11/19 17:56:53 ginsbach Exp $
.\"
.\" Copyright (c) 1980, 1990, 1993
.\" The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE REGENTS OR CONTRIBUTORS 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.
.\"
.\" @(#)login.1 8.2 (Berkeley) 5/5/94
.\"
.Dd November 19, 2008
.Dt LOGIN 1
.Os
.Sh NAME
.Nm login
.Nd authenticate users and set up their session environment
.Sh SYNOPSIS
.Nm
.Op Fl Ffps
.Op Fl a Ar address
.Op Fl h Ar hostname
.Op Ar user
.Sh DESCRIPTION
The
.Nm
utility logs users (and pseudo-users) into the computer system.
.Pp
If no user is specified, or if a user is specified and authentication
of the user fails,
.Nm
prompts for a user name.
Authentication of users is done via passwords.
If the user can be authenticated via
.Tn S/Key ,
then the
.Tn S/Key
challenge is incorporated in the password prompt.
The user then has the option of entering their Kerberos or normal
password or the
.Tn S/Key
response.
Neither will be echoed.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl a
The
.Fl a
option specifies the address of the host from which the connection was received.
It is used by various daemons such as
.Xr telnetd 8 .
This option may only be used by the super-user.
.It Fl F
The
.Fl F
option acts like the
.Fl f
option, but also indicates to
.Nm
that it should attempt to rewrite an existing Kerberos 5 credentials cache
(specified by the KRB5CCNAME environment variable) after dropping
permissions to the user logging in.
This flag is not supported under
.Xr pam 8 .
.It Fl f
The
.Fl f
option is used when a user name is specified to indicate that proper
authentication has already been done and that no password need be
requested.
This option may only be used by the super-user or when an already
logged in user is logging in as themselves.
.It Fl h
The
.Fl h
option specifies the host from which the connection was received.
It is used by various daemons such as
.Xr telnetd 8 .
This option may only be used by the super-user.
.It Fl p
By default,
.Nm
discards any previous environment.
The
.Fl p
option disables this behavior.
.It Fl s
Require a secure authentication mechanism like
.Tn Kerberos
or
.Tn S/Key
to be used.
This flag is not supported under
.Xr pam 8 .
.El
.Pp
If a user other than the superuser attempts to login while the file
.Pa /etc/nologin
exists,
.Nm
displays its contents to the user and exits.
This is used by
.Xr shutdown 8
to prevent normal users from logging in when the system is about to go down.
.Pp
Immediately after logging a user in,
.Nm
displays the system copyright notice, the date and time the user last
logged in, the message of the day as well as other information.
If the file
.Dq Pa .hushlogin
exists in the user's home directory, all of these messages are suppressed.
This is to simplify logins for non-human users.
.Nm
then records an entry in the
.Xr wtmp 5
and
.Xr utmp 5
files, executes site-specific login commands via the
.Xr ttyaction 3
facility with an action of "login", and executes the user's command
interpreter.
.Pp
.Nm
enters information into the environment (see
.Xr environ 7 )
specifying the user's home directory (HOME), command interpreter (SHELL),
search path (PATH), terminal type (TERM) and user name (both LOGNAME and
USER).
.Pp
The user's login experience can be customized using
login class capabilities as configured in
.Pa /etc/login.conf
and documented in
.Xr login.conf 5 .
.Pp
The standard shells,
.Xr csh 1
and
.Xr sh 1 ,
do not fork before executing the
.Nm
utility.
.Sh FILES
.Bl -tag -width /var/mail/userXXX -compact
.It Pa /etc/login.conf
login class capability database
.It Pa /etc/motd
message-of-the-day
.It Pa /etc/nologin
disallows non-superuser logins
.It Pa /var/run/utmp
list of current logins
.It Pa /var/log/lastlog
last login account records
.It Pa /var/log/wtmp
login account records
.It Pa /var/mail/user
system mailboxes
.It Pa \&.hushlogin
makes login quieter
.El
.Sh SEE ALSO
.Xr chpass 1 ,
.Xr newgrp 1 ,
.Xr passwd 1 ,
.Xr rlogin 1 ,
.Xr skey 1 ,
.Xr getpass 3 ,
.Xr ttyaction 3 ,
.Xr login.conf 5 ,
.Xr passwd.conf 5 ,
.Xr utmp 5 ,
.Xr environ 7 ,
.Xr kerberos 8 ,
.Xr pam 8
.Sh HISTORY
A
.Nm
appeared in
.At v6 .
.Sh TRADEMARKS AND PATENTS
.Tn S/Key
is a trademark of
.Tn Bellcore .

786
usr.bin/login/login.c Normal file
View file

@ -0,0 +1,786 @@
/* $NetBSD: login.c,v 1.97 2009/12/29 19:26:13 christos Exp $ */
/*-
* Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
* The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE REGENTS OR CONTRIBUTORS 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.
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\
The Regents of the University of California. All rights reserved.");
#endif /* not lint */
#ifndef lint
#if 0
static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
#endif
__RCSID("$NetBSD: login.c,v 1.97 2009/12/29 19:26:13 christos Exp $");
#endif /* not lint */
/*
* login [ name ]
* login -h hostname (for telnetd, etc.)
* login -f name (for pre-authenticated login: datakit, xterm, etc.)
*/
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <err.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <ttyent.h>
#include <tzfile.h>
#include <unistd.h>
#include <sysexits.h>
#ifdef SUPPORT_UTMP
#include <utmp.h>
#endif
#ifdef SUPPORT_UTMPX
#include <utmpx.h>
#endif
#include <util.h>
#ifdef SKEY
#include <skey.h>
#endif
#ifdef KERBEROS5
#include <krb5/krb5.h>
#include <com_err.h>
#endif
#ifdef LOGIN_CAP
#include <login_cap.h>
#endif
#include <vis.h>
#include "pathnames.h"
#include "common.h"
#ifdef KERBEROS5
int login_krb5_forwardable_tgt = 0;
static int login_krb5_get_tickets = 1;
static int login_krb5_retain_ccache = 0;
#endif
static void checknologin(char *);
#ifdef KERBEROS5
int k5login(struct passwd *, char *, char *, char *);
void k5destroy(void);
int k5_read_creds(char*);
int k5_write_creds(void);
#endif
#if defined(KERBEROS5)
static void dofork(void);
#endif
static void usage(void);
#define TTYGRPNAME "tty" /* name of group to own ttys */
#define DEFAULT_BACKOFF 3
#define DEFAULT_RETRIES 10
#if defined(KERBEROS5)
int has_ccache = 0;
static int notickets = 1;
static char *instance;
extern krb5_context kcontext;
extern int have_forward;
extern char *krb5tkfile_env;
extern int krb5_configured;
#endif
#if defined(KERBEROS5)
#define KERBEROS_CONFIGURED krb5_configured
#endif
extern char **environ;
int
main(int argc, char *argv[])
{
struct group *gr;
struct stat st;
int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin, rval;
int Fflag;
uid_t uid, saved_uid;
gid_t saved_gid, saved_gids[NGROUPS_MAX];
int nsaved_gids;
char *domain, *p, *ttyn, *pwprompt;
char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
char localhost[MAXHOSTNAMELEN + 1];
int need_chpass, require_chpass;
int login_retries = DEFAULT_RETRIES,
login_backoff = DEFAULT_BACKOFF;
time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY;
#ifdef KERBEROS5
krb5_error_code kerror;
#endif
#if defined(KERBEROS5)
int got_tickets = 0;
#endif
#ifdef LOGIN_CAP
char *shell = NULL;
login_cap_t *lc = NULL;
#endif
tbuf[0] = '\0';
rval = 0;
pwprompt = NULL;
nested = NULL;
need_chpass = require_chpass = 0;
(void)signal(SIGALRM, timedout);
(void)alarm(timeout);
(void)signal(SIGQUIT, SIG_IGN);
(void)signal(SIGINT, SIG_IGN);
(void)setpriority(PRIO_PROCESS, 0, 0);
openlog("login", 0, LOG_AUTH);
/*
* -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/utmpx and wtmp/wtmpx
* -a in addition to -h, a server may supply -a to pass the actual
* server address.
* -s is used to force use of S/Key or equivalent.
*/
domain = NULL;
if (gethostname(localhost, sizeof(localhost)) < 0)
syslog(LOG_ERR, "couldn't get local hostname: %m");
else
domain = strchr(localhost, '.');
localhost[sizeof(localhost) - 1] = '\0';
Fflag = fflag = hflag = pflag = sflag = 0;
have_ss = 0;
#ifdef KERBEROS5
have_forward = 0;
#endif
uid = getuid();
while ((ch = getopt(argc, argv, "a:Ffh:ps")) != -1)
switch (ch) {
case 'a':
if (uid)
errx(EXIT_FAILURE, "-a option: %s", strerror(EPERM));
decode_ss(optarg);
#ifdef notdef
(void)sockaddr_snprintf(optarg,
sizeof(struct sockaddr_storage), "%a", (void *)&ss);
#endif
break;
case 'F':
Fflag = 1;
/* FALLTHROUGH */
case 'f':
fflag = 1;
break;
case 'h':
if (uid)
errx(EXIT_FAILURE, "-h option: %s", strerror(EPERM));
hflag = 1;
#ifdef notdef
if (domain && (p = strchr(optarg, '.')) != NULL &&
strcasecmp(p, domain) == 0)
*p = '\0';
#endif
hostname = optarg;
break;
case 'p':
pflag = 1;
break;
case 's':
sflag = 1;
break;
default:
case '?':
usage();
break;
}
#ifndef __minix
setproctitle(NULL);
#endif
argc -= optind;
argv += optind;
if (*argv) {
username = *argv;
ask = 0;
} else
ask = 1;
#ifdef F_CLOSEM
(void)fcntl(3, F_CLOSEM, 0);
#else
for (cnt = getdtablesize(); cnt > 2; cnt--)
(void)close(cnt);
#endif
ttyn = ttyname(STDIN_FILENO);
if (ttyn == NULL || *ttyn == '\0') {
(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
ttyn = tname;
}
if ((tty = strstr(ttyn, "/pts/")) != NULL)
++tty;
else if ((tty = strrchr(ttyn, '/')) != NULL)
++tty;
else
tty = ttyn;
if (issetugid()) {
nested = strdup(user_from_uid(getuid(), 0));
if (nested == NULL) {
syslog(LOG_ERR, "strdup: %m");
sleepexit(EXIT_FAILURE);
}
}
#ifdef LOGIN_CAP
/* Get "login-retries" and "login-backoff" from default class */
if ((lc = login_getclass(NULL)) != NULL) {
login_retries = (int)login_getcapnum(lc, "login-retries",
DEFAULT_RETRIES, DEFAULT_RETRIES);
login_backoff = (int)login_getcapnum(lc, "login-backoff",
DEFAULT_BACKOFF, DEFAULT_BACKOFF);
login_close(lc);
lc = NULL;
}
#endif
#ifdef KERBEROS5
kerror = krb5_init_context(&kcontext);
if (kerror) {
/*
* If Kerberos is not configured, that is, we are
* not using Kerberos, do not log the error message.
* However, if Kerberos is configured, and the
* context init fails for some other reason, we need
* to issue a no tickets warning to the user when the
* login succeeds.
*/
if (kerror != ENXIO) { /* XXX NetBSD-local Heimdal hack */
syslog(LOG_NOTICE,
"%s when initializing Kerberos context",
error_message(kerror));
krb5_configured = 1;
}
login_krb5_get_tickets = 0;
}
#endif /* KERBEROS5 */
for (cnt = 0;; ask = 1) {
#if defined(KERBEROS5)
if (login_krb5_get_tickets)
k5destroy();
#endif
if (ask) {
fflag = 0;
getloginname();
}
rootlogin = 0;
#ifdef KERBEROS5
if ((instance = strchr(username, '/')) != NULL)
*instance++ = '\0';
else
instance = "";
#endif
if (strlen(username) > MAXLOGNAME)
username[MAXLOGNAME] = '\0';
/*
* Note if trying multiple user names; log failures for
* previous user name, but don't bother logging one failure
* for nonexistent name (mistyped username).
*/
if (failures && strcmp(tbuf, username)) {
if (failures > (pwd ? 0 : 1))
badlogin(tbuf);
failures = 0;
}
(void)strlcpy(tbuf, username, sizeof(tbuf));
pwd = getpwnam(username);
#ifdef LOGIN_CAP
/*
* Establish the class now, before we might goto
* within the next block. pwd can be NULL since it
* falls back to the "default" class if it is.
*/
lc = login_getclass(pwd ? pwd->pw_class : NULL);
#endif
/*
* 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)) {
/* already authenticated */
#ifdef KERBEROS5
if (login_krb5_get_tickets && Fflag)
k5_read_creds(username);
#endif
break;
} else if (pwd->pw_passwd[0] == '\0') {
/* pretend password okay */
rval = 0;
goto ttycheck;
}
}
fflag = 0;
(void)setpriority(PRIO_PROCESS, 0, -4);
#ifdef SKEY
if (skey_haskey(username) == 0) {
static char skprompt[80];
const char *skinfo = skey_keyinfo(username);
(void)snprintf(skprompt, sizeof(skprompt),
"Password [ %s ]:",
skinfo ? skinfo : "error getting challenge");
pwprompt = skprompt;
} else
#endif
pwprompt = "Password:";
p = getpass(pwprompt);
if (pwd == NULL) {
rval = 1;
goto skip;
}
#ifdef KERBEROS5
if (login_krb5_get_tickets &&
k5login(pwd, instance, localhost, p) == 0) {
rval = 0;
got_tickets = 1;
}
#endif
#if defined(KERBEROS5)
if (got_tickets)
goto skip;
#endif
#ifdef SKEY
if (skey_haskey(username) == 0 &&
skey_passcheck(username, p) != -1) {
rval = 0;
goto skip;
}
#endif
if (!sflag && *pwd->pw_passwd != '\0' &&
!strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd)) {
rval = 0;
require_chpass = 1;
goto skip;
}
rval = 1;
skip:
memset(p, 0, strlen(p));
(void)setpriority(PRIO_PROCESS, 0, 0);
ttycheck:
/*
* If trying to log in as root without Kerberos,
* but with insecure terminal, refuse the login attempt.
*/
if (pwd && !rval && rootlogin && !rootterm(tty)) {
(void)printf("Login incorrect or refused on this "
"terminal.\n");
if (hostname)
syslog(LOG_NOTICE,
"LOGIN %s REFUSED FROM %s ON TTY %s",
pwd->pw_name, hostname, tty);
else
syslog(LOG_NOTICE,
"LOGIN %s REFUSED ON TTY %s",
pwd->pw_name, tty);
continue;
}
if (pwd && !rval)
break;
(void)printf("Login incorrect or refused on this "
"terminal.\n");
failures++;
cnt++;
/*
* We allow login_retries tries, but after login_backoff
* we start backing off. These default to 10 and 3
* respectively.
*/
if (cnt > login_backoff) {
if (cnt >= login_retries) {
badlogin(username);
sleepexit(EXIT_FAILURE);
}
sleep((u_int)((cnt - login_backoff) * 5));
}
}
/* committed to login -- turn off timeout */
(void)alarm((u_int)0);
endpwent();
/* if user not super-user, check for disabled logins */
#ifdef LOGIN_CAP
if (!login_getcapbool(lc, "ignorenologin", rootlogin))
checknologin(login_getcapstr(lc, "nologin", NULL, NULL));
#else
if (!rootlogin)
checknologin(NULL);
#endif
#ifdef LOGIN_CAP
quietlog = login_getcapbool(lc, "hushlogin", 0);
#else
quietlog = 0;
#endif
/* Temporarily give up special privileges so we can change */
/* into NFS-mounted homes that are exported for non-root */
/* access and have mode 7x0 */
saved_uid = geteuid();
saved_gid = getegid();
nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
(void)setegid(pwd->pw_gid);
initgroups(username, pwd->pw_gid);
(void)seteuid(pwd->pw_uid);
if (chdir(pwd->pw_dir) < 0) {
#ifdef LOGIN_CAP
if (login_getcapbool(lc, "requirehome", 0)) {
(void)printf("Home directory %s required\n",
pwd->pw_dir);
sleepexit(EXIT_FAILURE);
}
#endif
(void)printf("No home directory %s!\n", pwd->pw_dir);
if (chdir("/") == -1)
exit(EXIT_FAILURE);
pwd->pw_dir = "/";
(void)printf("Logging in with home = \"/\".\n");
}
if (!quietlog)
quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
/* regain special privileges */
(void)seteuid(saved_uid);
setgroups(nsaved_gids, saved_gids);
(void)setegid(saved_gid);
#ifdef LOGIN_CAP
pw_warntime = login_getcaptime(lc, "password-warn",
_PASSWORD_WARNDAYS * SECSPERDAY,
_PASSWORD_WARNDAYS * SECSPERDAY);
#endif
(void)gettimeofday(&now, (struct timezone *)NULL);
if (pwd->pw_expire) {
if (now.tv_sec >= pwd->pw_expire) {
(void)printf("Sorry -- your account has expired.\n");
sleepexit(EXIT_FAILURE);
} else if (pwd->pw_expire - now.tv_sec < pw_warntime &&
!quietlog)
(void)printf("Warning: your account expires on %s",
ctime(&pwd->pw_expire));
}
if (pwd->pw_change) {
if (pwd->pw_change == _PASSWORD_CHGNOW)
need_chpass = 1;
else if (now.tv_sec >= pwd->pw_change) {
(void)printf("Sorry -- your password has expired.\n");
sleepexit(EXIT_FAILURE);
} else if (pwd->pw_change - now.tv_sec < pw_warntime &&
!quietlog)
(void)printf("Warning: your password expires on %s",
ctime(&pwd->pw_change));
}
/* Nothing else left to fail -- really log in. */
update_db(quietlog, rootlogin, fflag);
(void)chown(ttyn, pwd->pw_uid,
(gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
if (ttyaction(ttyn, "login", pwd->pw_name))
(void)printf("Warning: ttyaction failed.\n");
#if defined(KERBEROS5)
/* Fork so that we can call kdestroy */
if (! login_krb5_retain_ccache && has_ccache)
dofork();
#endif
/* Destroy environment unless user has requested its preservation. */
if (!pflag)
environ = envinit;
#ifdef LOGIN_CAP
if (nested == NULL && setusercontext(lc, pwd, pwd->pw_uid,
LOGIN_SETLOGIN) != 0) {
syslog(LOG_ERR, "setusercontext failed");
exit(EXIT_FAILURE);
}
if (setusercontext(lc, pwd, pwd->pw_uid,
(LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETLOGIN))) != 0) {
syslog(LOG_ERR, "setusercontext failed");
exit(EXIT_FAILURE);
}
#else
(void)setgid(pwd->pw_gid);
initgroups(username, pwd->pw_gid);
#ifndef __minix
if (nested == NULL && setlogin(pwd->pw_name) < 0)
syslog(LOG_ERR, "setlogin() failure: %m");
#endif
/* Discard permissions last so can't get killed and drop core. */
if (rootlogin)
(void)setuid(0);
else
(void)setuid(pwd->pw_uid);
#endif
if (*pwd->pw_shell == '\0')
pwd->pw_shell = _PATH_BSHELL;
#ifdef LOGIN_CAP
if ((shell = login_getcapstr(lc, "shell", NULL, NULL)) != NULL) {
if ((shell = strdup(shell)) == NULL) {
syslog(LOG_ERR, "Cannot alloc mem");
sleepexit(EXIT_FAILURE);
}
pwd->pw_shell = shell;
}
#endif
(void)setenv("HOME", pwd->pw_dir, 1);
(void)setenv("SHELL", pwd->pw_shell, 1);
if (term[0] == '\0') {
char *tt = (char *)stypeof(tty);
#ifdef LOGIN_CAP
if (tt == NULL)
tt = login_getcapstr(lc, "term", NULL, NULL);
#endif
/* unknown term -> "su" */
(void)strlcpy(term, tt != NULL ? tt : "su", sizeof(term));
}
(void)setenv("TERM", term, 0);
(void)setenv("LOGNAME", pwd->pw_name, 1);
(void)setenv("USER", pwd->pw_name, 1);
#ifdef LOGIN_CAP
setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH);
#else
(void)setenv("PATH", _PATH_DEFPATH, 0);
#endif
#ifdef KERBEROS5
if (krb5tkfile_env)
(void)setenv("KRB5CCNAME", krb5tkfile_env, 1);
#endif
if (tty[sizeof("tty")-1] == 'd')
syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
/* If fflag is on, assume caller/authenticator has logged root login. */
if (rootlogin && fflag == 0) {
if (hostname)
syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
username, tty, hostname);
else
syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
username, tty);
}
#if defined(KERBEROS5)
if (KERBEROS_CONFIGURED && !quietlog && notickets == 1)
(void)printf("Warning: no Kerberos tickets issued.\n");
#endif
if (!quietlog) {
char *fname;
#ifdef LOGIN_CAP
fname = login_getcapstr(lc, "copyright", NULL, NULL);
if (fname != NULL && access(fname, F_OK) == 0)
motd(fname);
else
#endif
(void)printf("%s", copyrightstr);
#ifdef LOGIN_CAP
fname = login_getcapstr(lc, "welcome", NULL, NULL);
if (fname == NULL || access(fname, F_OK) != 0)
#endif
fname = _PATH_MOTDFILE;
motd(fname);
(void)snprintf(tbuf,
sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
if (stat(tbuf, &st) == 0 && st.st_size != 0)
(void)printf("You have %smail.\n",
(st.st_mtime > st.st_atime) ? "new " : "");
}
#ifdef LOGIN_CAP
login_close(lc);
#endif
(void)signal(SIGALRM, SIG_DFL);
(void)signal(SIGQUIT, SIG_DFL);
(void)signal(SIGINT, SIG_DFL);
(void)signal(SIGTSTP, SIG_IGN);
tbuf[0] = '-';
(void)strlcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
p + 1 : pwd->pw_shell, sizeof(tbuf) - 1);
/* Wait to change password until we're unprivileged */
if (need_chpass) {
if (!require_chpass)
(void)printf(
"Warning: your password has expired. Please change it as soon as possible.\n");
else {
int status;
(void)printf(
"Your password has expired. Please choose a new one.\n");
switch (fork()) {
case -1:
warn("fork");
sleepexit(EXIT_FAILURE);
case 0:
execl(_PATH_BINPASSWD, "passwd", NULL);
_exit(EXIT_FAILURE);
default:
if (wait(&status) == -1 ||
WEXITSTATUS(status))
sleepexit(EXIT_FAILURE);
}
}
}
#ifdef KERBEROS5
if (login_krb5_get_tickets)
k5_write_creds();
#endif
execlp(pwd->pw_shell, tbuf, NULL);
err(EXIT_FAILURE, "%s", pwd->pw_shell);
}
#if defined(KERBEROS5)
/*
* This routine handles cleanup stuff, and the like.
* It exists only in the child process.
*/
static void
dofork(void)
{
pid_t child, wchild;
switch (child = fork()) {
case 0:
return; /* Child process */
case -1:
err(EXIT_FAILURE, "Can't fork");
/*NOTREACHED*/
default:
break;
}
/*
* Setup stuff? This would be things we could do in parallel
* with login
*/
if (chdir("/") == -1) /* Let's not keep the fs busy... */
err(EXIT_FAILURE, "Can't chdir to `/'");
/* If we're the parent, watch the child until it dies */
while ((wchild = wait(NULL)) != child)
if (wchild == -1)
err(EXIT_FAILURE, "Can't wait");
/* Cleanup stuff */
/* Run kdestroy to destroy tickets */
if (login_krb5_get_tickets)
k5destroy();
/* Leave */
exit(EXIT_SUCCESS);
}
#endif
static void
checknologin(char *fname)
{
int fd, nchars;
char tbuf[8192];
if ((fd = open(fname ? fname : _PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
(void)write(fileno(stdout), tbuf, nchars);
sleepexit(EXIT_SUCCESS);
}
}
static void
usage(void)
{
(void)fprintf(stderr,
"Usage: %s [-Ffps] [-a address] [-h hostname] [username]\n",
getprogname());
exit(EXIT_FAILURE);
}

675
usr.bin/login/login_pam.c Normal file
View file

@ -0,0 +1,675 @@
/* $NetBSD: login_pam.c,v 1.20 2009/12/29 19:26:13 christos Exp $ */
/*-
* Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
* The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE REGENTS OR CONTRIBUTORS 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.
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\
The Regents of the University of California. All rights reserved.");
#endif /* not lint */
#ifndef lint
#if 0
static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
#endif
__RCSID("$NetBSD: login_pam.c,v 1.20 2009/12/29 19:26:13 christos Exp $");
#endif /* not lint */
/*
* login [ name ]
* login -h hostname (for telnetd, etc.)
* login -f name (for pre-authenticated login: datakit, xterm, etc.)
*/
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <err.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <ttyent.h>
#include <tzfile.h>
#include <unistd.h>
#include <util.h>
#include <login_cap.h>
#include <vis.h>
#include <security/pam_appl.h>
#include <security/openpam.h>
#include "pathnames.h"
#include "common.h"
#if 0
static int rootterm(char *);
#endif
static void usage(void) __attribute__((__noreturn__));
static struct pam_conv pamc = { openpam_ttyconv, NULL };
#define TTYGRPNAME "tty" /* name of group to own ttys */
#define DEFAULT_BACKOFF 3
#define DEFAULT_RETRIES 10
static struct passwd pwres;
static char pwbuf[1024];
static struct group grs, *grp;
static char grbuf[1024];
extern char **environ;
int
main(int argc, char *argv[])
{
struct stat st;
int ask, ch, cnt, fflag, pflag, quietlog, rootlogin;
int auth_passed;
uid_t uid, saved_uid;
gid_t saved_gid, saved_gids[NGROUPS_MAX];
int nsaved_gids;
char *domain, *p, *ttyn, *pwprompt;
char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
char localhost[MAXHOSTNAMELEN + 1];
int need_chpass, require_chpass;
int login_retries = DEFAULT_RETRIES,
login_backoff = DEFAULT_BACKOFF;
char *shell = NULL;
login_cap_t *lc = NULL;
pam_handle_t *pamh = NULL;
int pam_err;
sig_t oint, oabrt, oquit, oalrm;
const void *newuser;
int pam_silent = PAM_SILENT;
pid_t xpid, pid;
int status;
char *saved_term;
char **pamenv;
tbuf[0] = '\0';
pwprompt = NULL;
nested = NULL;
need_chpass = require_chpass = 0;
oabrt = signal(SIGABRT, SIG_IGN);
oalrm = signal(SIGALRM, timedout);
oint = signal(SIGINT, SIG_IGN);
oquit = signal(SIGQUIT, SIG_IGN);
(void)alarm(timeout);
(void)setpriority(PRIO_PROCESS, 0, 0);
openlog("login", 0, LOG_AUTH);
/*
* -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/utmpx and wtmp/wtmpx
* -a in addition to -h, a server my supply -a to pass the actual
* server address.
*/
domain = NULL;
if (gethostname(localhost, sizeof(localhost)) < 0)
syslog(LOG_ERR, "couldn't get local hostname: %m");
else
domain = strchr(localhost, '.');
localhost[sizeof(localhost) - 1] = '\0';
fflag = pflag = 0;
have_ss = 0;
uid = getuid();
while ((ch = getopt(argc, argv, "a:fh:p")) != -1)
switch (ch) {
case 'a':
if (uid) {
errno = EPERM;
err(EXIT_FAILURE, "-a option");
}
decode_ss(optarg);
break;
case 'f':
fflag = 1;
break;
case 'h':
if (uid) {
errno = EPERM;
err(EXIT_FAILURE, "-h option");
}
if (domain && (p = strchr(optarg, '.')) != NULL &&
strcasecmp(p, domain) == 0)
*p = '\0';
hostname = optarg;
break;
case 'p':
pflag = 1;
break;
default:
case '?':
usage();
break;
}
setproctitle(NULL);
argc -= optind;
argv += optind;
if (*argv) {
username = *argv;
ask = 0;
} else
ask = 1;
#ifdef F_CLOSEM
(void)fcntl(3, F_CLOSEM, 0);
#else
for (cnt = getdtablesize(); cnt > 2; cnt--)
(void)close(cnt);
#endif
ttyn = ttyname(STDIN_FILENO);
if (ttyn == NULL || *ttyn == '\0') {
(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
ttyn = tname;
}
if ((tty = strstr(ttyn, "/pts/")) != NULL)
++tty;
else if ((tty = strrchr(ttyn, '/')) != NULL)
++tty;
else
tty = ttyn;
if (issetugid()) {
nested = strdup(user_from_uid(getuid(), 0));
if (nested == NULL) {
syslog(LOG_ERR, "strdup: %m");
sleepexit(EXIT_FAILURE);
}
}
/* Get "login-retries" and "login-backoff" from default class */
if ((lc = login_getclass(NULL)) != NULL) {
login_retries = (int)login_getcapnum(lc, "login-retries",
DEFAULT_RETRIES, DEFAULT_RETRIES);
login_backoff = (int)login_getcapnum(lc, "login-backoff",
DEFAULT_BACKOFF, DEFAULT_BACKOFF);
login_close(lc);
lc = NULL;
}
for (cnt = 0;; ask = 1) {
if (ask) {
fflag = 0;
getloginname();
}
rootlogin = 0;
auth_passed = 0;
if (strlen(username) > MAXLOGNAME)
username[MAXLOGNAME] = '\0';
/*
* Note if trying multiple user names; log failures for
* previous user name, but don't bother logging one failure
* for nonexistent name (mistyped username).
*/
if (failures && strcmp(tbuf, username)) {
if (failures > (pwd ? 0 : 1))
badlogin(tbuf);
failures = 0;
}
#define PAM_END(msg) do { \
syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err)); \
warnx("%s: %s", msg, pam_strerror(pamh, pam_err)); \
pam_end(pamh, pam_err); \
sleepexit(EXIT_FAILURE); \
} while (/*CONSTCOND*/0)
pam_err = pam_start("login", username, &pamc, &pamh);
if (pam_err != PAM_SUCCESS) {
if (pamh != NULL)
PAM_END("pam_start");
/* Things went really bad... */
syslog(LOG_ERR, "pam_start failed: %s",
pam_strerror(pamh, pam_err));
errx(EXIT_FAILURE, "pam_start failed");
}
#define PAM_SET_ITEM(item, var) do { \
pam_err = pam_set_item(pamh, (item), (var)); \
if (pam_err != PAM_SUCCESS) \
PAM_END("pam_set_item(" # item ")"); \
} while (/*CONSTCOND*/0)
/*
* Fill hostname tty, and nested user
*/
PAM_SET_ITEM(PAM_RHOST, hostname);
PAM_SET_ITEM(PAM_TTY, tty);
if (nested)
PAM_SET_ITEM(PAM_NUSER, nested);
if (have_ss)
PAM_SET_ITEM(PAM_SOCKADDR, &ss);
/*
* Don't check for errors, because we don't want to give
* out any information.
*/
pwd = NULL;
(void)getpwnam_r(username, &pwres, pwbuf, sizeof(pwbuf), &pwd);
/*
* Establish the class now, before we might goto
* within the next block. pwd can be NULL since it
* falls back to the "default" class if it is.
*/
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)) {
/* already authenticated */
auth_passed = 1;
goto skip_auth;
}
}
(void)setpriority(PRIO_PROCESS, 0, -4);
switch(pam_err = pam_authenticate(pamh, pam_silent)) {
case PAM_SUCCESS:
/*
* PAM can change the user, refresh
* username, pwd, and lc.
*/
pam_err = pam_get_item(pamh, PAM_USER, &newuser);
if (pam_err != PAM_SUCCESS)
PAM_END("pam_get_item(PAM_USER)");
username = (char *)newuser;
/*
* Don't check for errors, because we don't want to give
* out any information.
*/
pwd = NULL;
(void)getpwnam_r(username, &pwres, pwbuf, sizeof(pwbuf),
&pwd);
lc = login_getpwclass(pwd);
auth_passed = 1;
switch (pam_err = pam_acct_mgmt(pamh, pam_silent)) {
case PAM_SUCCESS:
break;
case PAM_NEW_AUTHTOK_REQD:
pam_err = pam_chauthtok(pamh,
pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK);
if (pam_err != PAM_SUCCESS)
PAM_END("pam_chauthtok");
break;
case PAM_AUTH_ERR:
case PAM_USER_UNKNOWN:
case PAM_MAXTRIES:
auth_passed = 0;
break;
default:
PAM_END("pam_acct_mgmt");
break;
}
break;
case PAM_AUTH_ERR:
case PAM_USER_UNKNOWN:
case PAM_MAXTRIES:
auth_passed = 0;
break;
default:
PAM_END("pam_authenticate");
break;
}
(void)setpriority(PRIO_PROCESS, 0, 0);
skip_auth:
/*
* If the user exists and authentication passed,
* get out of the loop and login the user.
*/
if (pwd && auth_passed)
break;
(void)printf("Login incorrect or refused on this terminal.\n");
failures++;
cnt++;
/*
* We allow login_retries tries, but after login_backoff
* we start backing off. These default to 10 and 3
* respectively.
*/
if (cnt > login_backoff) {
if (cnt >= login_retries) {
badlogin(username);
pam_end(pamh, PAM_SUCCESS);
sleepexit(EXIT_FAILURE);
}
sleep((u_int)((cnt - login_backoff) * 5));
}
}
/* committed to login -- turn off timeout */
(void)alarm((u_int)0);
endpwent();
quietlog = login_getcapbool(lc, "hushlogin", 0);
/*
* Temporarily give up special privileges so we can change
* into NFS-mounted homes that are exported for non-root
* access and have mode 7x0
*/
saved_uid = geteuid();
saved_gid = getegid();
nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
(void)setegid(pwd->pw_gid);
initgroups(username, pwd->pw_gid);
(void)seteuid(pwd->pw_uid);
if (chdir(pwd->pw_dir) != 0) {
if (login_getcapbool(lc, "requirehome", 0)) {
(void)printf("Home directory %s required\n",
pwd->pw_dir);
pam_end(pamh, PAM_SUCCESS);
exit(EXIT_FAILURE);
}
(void)printf("No home directory %s!\n", pwd->pw_dir);
if (chdir("/") == -1) {
pam_end(pamh, PAM_SUCCESS);
exit(EXIT_FAILURE);
}
pwd->pw_dir = "/";
(void)printf("Logging in with home = \"/\".\n");
}
if (!quietlog) {
quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
pam_silent = quietlog ? PAM_SILENT : 0;
}
/* regain special privileges */
setegid(saved_gid);
setgroups(nsaved_gids, saved_gids);
seteuid(saved_uid);
(void)getgrnam_r(TTYGRPNAME, &grs, grbuf, sizeof(grbuf), &grp);
(void)chown(ttyn, pwd->pw_uid,
(grp != NULL) ? grp->gr_gid : pwd->pw_gid);
if (ttyaction(ttyn, "login", pwd->pw_name))
(void)printf("Warning: ttyaction failed.\n");
/* Nothing else left to fail -- really log in. */
update_db(quietlog, rootlogin, fflag);
if (nested == NULL && setusercontext(lc, pwd, pwd->pw_uid,
LOGIN_SETLOGIN) != 0) {
syslog(LOG_ERR, "setusercontext failed");
pam_end(pamh, PAM_SUCCESS);
exit(EXIT_FAILURE);
}
if (tty[sizeof("tty")-1] == 'd')
syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
/*
* Establish groups
*/
if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
syslog(LOG_ERR, "setusercontext failed");
pam_end(pamh, PAM_SUCCESS);
exit(EXIT_FAILURE);
}
pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED);
if (pam_err != PAM_SUCCESS)
PAM_END("pam_setcred");
pam_err = pam_open_session(pamh, pam_silent);
if (pam_err != PAM_SUCCESS)
PAM_END("pam_open_session");
/*
* Fork because we need to call pam_closesession as root.
* Make sure signals cannot kill the parent.
* This has been handled in the begining of main.
*/
switch(pid = fork()) {
case -1:
pam_err = pam_close_session(pamh, 0);
if (pam_err != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_close_session: %s",
pam_strerror(pamh, pam_err));
warnx("pam_close_session: %s",
pam_strerror(pamh, pam_err));
}
syslog(LOG_ERR, "fork failed: %m");
warn("fork failed");
pam_end(pamh, pam_err);
exit(EXIT_FAILURE);
break;
case 0: /* Child */
break;
default:
/*
* Parent: wait for the child to terminate
* and call pam_close_session.
*/
if ((xpid = waitpid(pid, &status, 0)) != pid) {
pam_err = pam_close_session(pamh, 0);
if (pam_err != PAM_SUCCESS) {
syslog(LOG_ERR,
"pam_close_session: %s",
pam_strerror(pamh, pam_err));
warnx("pam_close_session: %s",
pam_strerror(pamh, pam_err));
}
pam_end(pamh, pam_err);
if (xpid != -1)
warnx("wrong PID: %d != %d", pid, xpid);
else
warn("wait for pid %d failed", pid);
exit(EXIT_FAILURE);
}
(void)signal(SIGABRT, oabrt);
(void)signal(SIGALRM, oalrm);
(void)signal(SIGINT, oint);
(void)signal(SIGQUIT, oquit);
if ((pam_err = pam_close_session(pamh, 0)) != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_close_session: %s",
pam_strerror(pamh, pam_err));
warnx("pam_close_session: %s",
pam_strerror(pamh, pam_err));
}
pam_end(pamh, PAM_SUCCESS);
exit(EXIT_SUCCESS);
break;
}
/*
* The child: starting here, we don't have to care about
* handling PAM issues if we exit, the parent will do the
* job when we exit.
*
* Destroy environment unless user has requested its preservation.
* Try to preserve TERM anyway.
*/
saved_term = getenv("TERM");
if (!pflag) {
environ = envinit;
if (saved_term)
setenv("TERM", saved_term, 0);
}
if (*pwd->pw_shell == '\0')
pwd->pw_shell = _PATH_BSHELL;
shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
if (*shell == '\0')
shell = pwd->pw_shell;
if ((pwd->pw_shell = strdup(shell)) == NULL) {
syslog(LOG_ERR, "Cannot alloc mem");
exit(EXIT_FAILURE);
}
(void)setenv("HOME", pwd->pw_dir, 1);
(void)setenv("SHELL", pwd->pw_shell, 1);
if (term[0] == '\0') {
char *tt = (char *)stypeof(tty);
if (tt == NULL)
tt = login_getcapstr(lc, "term", NULL, NULL);
/* unknown term -> "su" */
(void)strlcpy(term, tt != NULL ? tt : "su", sizeof(term));
}
(void)setenv("TERM", term, 0);
(void)setenv("LOGNAME", pwd->pw_name, 1);
(void)setenv("USER", pwd->pw_name, 1);
/*
* Add PAM environement
*/
if ((pamenv = pam_getenvlist(pamh)) != NULL) {
char **envitem;
for (envitem = pamenv; *envitem; envitem++) {
putenv(*envitem);
free(*envitem);
}
free(pamenv);
}
/* This drops root privs */
if (setusercontext(lc, pwd, pwd->pw_uid,
(LOGIN_SETALL & ~LOGIN_SETLOGIN)) != 0) {
syslog(LOG_ERR, "setusercontext failed");
exit(EXIT_FAILURE);
}
if (!quietlog) {
char *fname;
fname = login_getcapstr(lc, "copyright", NULL, NULL);
if (fname != NULL && access(fname, F_OK) == 0)
motd(fname);
else
(void)printf("%s", copyrightstr);
fname = login_getcapstr(lc, "welcome", NULL, NULL);
if (fname == NULL || access(fname, F_OK) != 0)
fname = _PATH_MOTDFILE;
motd(fname);
(void)snprintf(tbuf,
sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
if (stat(tbuf, &st) == 0 && st.st_size != 0)
(void)printf("You have %smail.\n",
(st.st_mtime > st.st_atime) ? "new " : "");
}
login_close(lc);
tbuf[0] = '-';
(void)strlcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
p + 1 : pwd->pw_shell, sizeof(tbuf) - 1);
(void)signal(SIGABRT, oabrt);
(void)signal(SIGALRM, oalrm);
(void)signal(SIGINT, oint);
(void)signal(SIGQUIT, oquit);
(void)signal(SIGTSTP, SIG_IGN);
execlp(pwd->pw_shell, tbuf, NULL);
err(EXIT_FAILURE, "%s", pwd->pw_shell);
}
static void
usage(void)
{
(void)fprintf(stderr,
"Usage: %s [-fp] [-a address] [-h hostname] [username]\n",
getprogname());
exit(EXIT_FAILURE);
}
#if 0
static int
rootterm(char *ttyn)
{
struct ttyent *t;
return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
}
#endif

38
usr.bin/login/pathnames.h Normal file
View file

@ -0,0 +1,38 @@
/* $NetBSD: pathnames.h,v 1.6 2003/08/07 11:14:26 agc Exp $ */
/*-
* Copyright (c) 1989, 1993
* The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE REGENTS OR CONTRIBUTORS 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.
*
* @(#)pathnames.h 8.1 (Berkeley) 6/9/93
*/
#include <paths.h>
#define _PATH_HUSHLOGIN ".hushlogin"
#define _PATH_MOTDFILE "/etc/motd"
#define _PATH_BINPASSWD "/usr/bin/passwd"