From a2d137268034a818ca7dc98e995c78f543b150bd Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Sat, 7 Apr 2012 03:41:28 +0200 Subject: [PATCH] Import NetBSD usr.bin/login --- commands/Makefile | 2 +- commands/login/Makefile | 7 - commands/login/login.c | 509 --------------------- lib/libutil/Makefile | 4 +- lib/libutil/sockaddr_snprintf.c | 20 +- man/man1/Makefile | 2 +- man/man1/login.1 | 30 -- sys/conf/copyright | 1 + tools/nbsd_ports | 1 + usr.bin/Makefile | 2 +- usr.bin/login/Makefile | 57 +++ usr.bin/login/common.c | 393 ++++++++++++++++ usr.bin/login/common.h | 53 +++ usr.bin/login/k5login.c | 478 +++++++++++++++++++ usr.bin/login/login.1 | 206 +++++++++ usr.bin/login/login.c | 786 ++++++++++++++++++++++++++++++++ usr.bin/login/login_pam.c | 675 +++++++++++++++++++++++++++ usr.bin/login/pathnames.h | 38 ++ 18 files changed, 2706 insertions(+), 558 deletions(-) delete mode 100644 commands/login/Makefile delete mode 100644 commands/login/login.c delete mode 100644 man/man1/login.1 create mode 100644 sys/conf/copyright create mode 100644 usr.bin/login/Makefile create mode 100644 usr.bin/login/common.c create mode 100644 usr.bin/login/common.h create mode 100644 usr.bin/login/k5login.c create mode 100644 usr.bin/login/login.1 create mode 100644 usr.bin/login/login.c create mode 100644 usr.bin/login/login_pam.c create mode 100644 usr.bin/login/pathnames.h diff --git a/commands/Makefile b/commands/Makefile index 2fe0a4d5c..ac0be14f1 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -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 \ diff --git a/commands/login/Makefile b/commands/login/Makefile deleted file mode 100644 index 6dd5f37a3..000000000 --- a/commands/login/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -PROG= login -MAN= -.if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no") -LDADD+= -lcrypt -.endif - -.include diff --git a/commands/login/login.c b/commands/login/login.c deleted file mode 100644 index 53b80303f..000000000 --- a/commands/login/login.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 #include -#include -#include #include #include @@ -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) diff --git a/man/man1/Makefile b/man/man1/Makefile index b9cb10e9e..30c14fb8d 100644 --- a/man/man1/Makefile +++ b/man/man1/Makefile @@ -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 \ diff --git a/man/man1/login.1 b/man/man1/login.1 deleted file mode 100644 index ae3d24f73..000000000 --- a/man/man1/login.1 +++ /dev/null @@ -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). diff --git a/sys/conf/copyright b/sys/conf/copyright new file mode 100644 index 000000000..07e2b736a --- /dev/null +++ b/sys/conf/copyright @@ -0,0 +1 @@ +Copyright (c) 2012, Vrije Universiteit, Amsterdam, The Netherlands diff --git a/tools/nbsd_ports b/tools/nbsd_ports index 66a85b555..bfc950cfb 100644 --- a/tools/nbsd_ports +++ b/tools/nbsd_ports @@ -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 diff --git a/usr.bin/Makefile b/usr.bin/Makefile index a7a987e19..fac8c6136 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -3,7 +3,7 @@ .include # 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 diff --git a/usr.bin/login/Makefile b/usr.bin/login/Makefile new file mode 100644 index 000000000..088b50447 --- /dev/null +++ b/usr.bin/login/Makefile @@ -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 + +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 diff --git a/usr.bin/login/common.c b/usr.bin/login/common.c new file mode 100644 index 000000000..8a6252e16 --- /dev/null +++ b/usr.bin/login/common.c @@ -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 +__RCSID("$NetBSD: common.c,v 1.3 2009/12/29 20:15:15 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +static void doutmp(void); +static void dolastlog(int); +#endif +#ifdef SUPPORT_UTMPX +#include +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; +} diff --git a/usr.bin/login/common.h b/usr.bin/login/common.h new file mode 100644 index 000000000..2ce50e6ae --- /dev/null +++ b/usr.bin/login/common.h @@ -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 diff --git a/usr.bin/login/k5login.c b/usr.bin/login/k5login.c new file mode 100644 index 000000000..5d2f6a12f --- /dev/null +++ b/usr.bin/login/k5login.c @@ -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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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/ 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/ 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/ 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 */ diff --git a/usr.bin/login/login.1 b/usr.bin/login/login.1 new file mode 100644 index 000000000..ec64d0fb9 --- /dev/null +++ b/usr.bin/login/login.1 @@ -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 . diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c new file mode 100644 index 000000000..0023ab5a5 --- /dev/null +++ b/usr.bin/login/login.c @@ -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 +#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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SUPPORT_UTMP +#include +#endif +#ifdef SUPPORT_UTMPX +#include +#endif +#include +#ifdef SKEY +#include +#endif +#ifdef KERBEROS5 +#include +#include +#endif +#ifdef LOGIN_CAP +#include +#endif +#include + +#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); +} diff --git a/usr.bin/login/login_pam.c b/usr.bin/login/login_pam.c new file mode 100644 index 000000000..39e0e8247 --- /dev/null +++ b/usr.bin/login/login_pam.c @@ -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 +#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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 diff --git a/usr.bin/login/pathnames.h b/usr.bin/login/pathnames.h new file mode 100644 index 000000000..e95e5cbe0 --- /dev/null +++ b/usr.bin/login/pathnames.h @@ -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 + +#define _PATH_HUSHLOGIN ".hushlogin" +#define _PATH_MOTDFILE "/etc/motd" +#define _PATH_BINPASSWD "/usr/bin/passwd"