aboutsummaryrefslogtreecommitdiffstats
path: root/usr.bin/login
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/login')
-rw-r--r--usr.bin/login/Makefile22
-rw-r--r--usr.bin/login/README10
-rw-r--r--usr.bin/login/klogin.c2
-rw-r--r--usr.bin/login/login.access.550
-rw-r--r--usr.bin/login/login.c49
-rw-r--r--usr.bin/login/login_access.c236
-rw-r--r--usr.bin/login/login_skey.c105
-rw-r--r--usr.bin/login/pathnames.h1
8 files changed, 467 insertions, 8 deletions
diff --git a/usr.bin/login/Makefile b/usr.bin/login/Makefile
index 9b496a38fb10..f181a7ae8442 100644
--- a/usr.bin/login/Makefile
+++ b/usr.bin/login/Makefile
@@ -1,16 +1,24 @@
# @(#)Makefile 5.6 (Berkeley) 6/24/90
PROG= login
-SRCS= klogin.c login.c
-DPADD= ${LIBUTIL}
-LDADD= -lutil
+MAN5= login.access.5
+MAN1= login.1
+SRCS= klogin.c login.c login_access.c login_skey.c
+DPADD= ${LIBUTIL} ${LIBSKEY}
+LDADD= -lutil -lskey
BINOWN= root
BINMODE=4555
+#CFLAGS+=-DLOGIN_ACCESS -DSKEY -DLOGALL -I${.CURDIR}/../../lib
+CFLAGS+=-DLOGIN_ACCESS -DSKEY
-.if exists(/usr/lib/libcrypt.a)
-#CFLAGS+=-DKERBEROS
-DPADD+= ${LIBCRYPT} #${LIBKRB}
-LDADD+= -lcrypt #-lkrb
+.if exists(${DESTDIR}/usr/lib/libcrypt.a)
+DPADD+= ${LIBCRYPT}
+LDADD+= -lcrypt
+.endif
+.if exists(${DESTDIR}/usr/lib/libkrb.a)
+DPADD+= ${LIBKRB} ${LIBDES}
+LDADD+= -lkrb -ldes
+CFLAGS+= -DKERBEROS
.endif
.include <bsd.prog.mk>
diff --git a/usr.bin/login/README b/usr.bin/login/README
new file mode 100644
index 000000000000..6ad7a10d4796
--- /dev/null
+++ b/usr.bin/login/README
@@ -0,0 +1,10 @@
+This login has additional functionalities. They are all based on (part of)
+Wietse Venema's logdaemon package.
+
+
+The following defines can be used:
+1) LOGIN_ACCESS to allow access control on a per tty/user combination
+2) SKEY to allow the use of s/key one time passwords
+3) LOGALL to log all logins
+
+-Guido
diff --git a/usr.bin/login/klogin.c b/usr.bin/login/klogin.c
index faf5aa63adfb..4063611520fe 100644
--- a/usr.bin/login/klogin.c
+++ b/usr.bin/login/klogin.c
@@ -171,7 +171,7 @@ klogin(pw, instance, localhost, password)
}
/* undecipherable: probably didn't have a srvtab on the local host */
- if (kerror = RD_AP_UNDEC) {
+ if (kerror == RD_AP_UNDEC) {
syslog(LOG_NOTICE, "krb_rd_req: (%s)\n", krb_err_txt[kerror]);
dest_tkt();
return(1);
diff --git a/usr.bin/login/login.access.5 b/usr.bin/login/login.access.5
new file mode 100644
index 000000000000..28d423c9156c
--- /dev/null
+++ b/usr.bin/login/login.access.5
@@ -0,0 +1,50 @@
+.\" this is comment
+.Dd April 30, 1994
+.Dt SKEY.ACCESS 5
+.Os FreeBSD 1.2
+.Sh NAME
+.Nm login.access
+.Nd Login access control table
+.Sh DESCRIPTION
+The
+.Nm login.access
+file specifies (user, host) combinations and/or (user, tty)
+combinations for which a login will be either accepted or refused.
+.Pp
+When someone logs in, the
+.Nm login.access
+is scanned for the first entry that
+matches the (user, host) combination, or, in case of non-networked
+logins, the first entry that matches the (user, tty) combination. The
+permissions field of that table entry determines whether the login will
+be accepted or refused.
+.Pp
+Each line of the login access control table has three fields separated by a
+":" character: permission : users : origins
+
+The first field should be a "+" (access granted) or "-" (access denied)
+character. The second field should be a list of one or more login names,
+group names, or ALL (always matches). The third field should be a list
+of one or more tty names (for non-networked logins), host names, domain
+names (begin with "."), host addresses, internet network numbers (end
+with "."), ALL (always matches) or LOCAL (matches any string that does
+not contain a "." character). If you run NIS you can use @netgroupname
+in host or user patterns.
+
+The EXCEPT operator makes it possible to write very compact rules.
+
+The group file is searched only when a name does not match that of the
+logged-in user. Only groups are matched in which users are explicitly
+listed: the program does not look at a user's primary group id value.
+.Sh FILES
+.Bl -tag -width /etc/login.access -compact
+.It Pa /etc/login.access
+The
+.Nm login.access
+file resides in
+.Pa /etc .
+.El
+.Sh SEE ALSO
+.Xr login 1
+.Sh AUTHOR
+Guido van Rooij
diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c
index ad1d4d60ad57..ab0adf8280dc 100644
--- a/usr.bin/login/login.c
+++ b/usr.bin/login/login.c
@@ -117,6 +117,10 @@ main(argc, argv)
char *domain, *salt, *ttyn;
char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
char localhost[MAXHOSTNAMELEN];
+#ifdef SKEY
+ int permit_passwd = 0;
+ char *skey_getpass(), *skey_crypt();
+#endif /* SKEY */
(void)signal(SIGALRM, timedout);
(void)alarm((u_int)timeout);
@@ -152,9 +156,11 @@ main(argc, argv)
exit(1);
}
hflag = 1;
+#ifndef SKEY
if (domain && (p = index(optarg, '.')) &&
strcasecmp(p, domain) == 0)
*p = 0;
+#endif /* SKEY */
hostname = optarg;
break;
case 'p':
@@ -190,6 +196,10 @@ main(argc, argv)
else
tty = ttyn;
+#ifdef SKEY
+ permit_passwd = (hostname == 0 || authfile(hostname) != 0);
+#endif
+
for (cnt = 0;; ask = 1) {
if (ask) {
fflag = 0;
@@ -248,7 +258,11 @@ main(argc, argv)
(void)setpriority(PRIO_PROCESS, 0, -4);
+#ifdef SKEY
+ p = skey_getpass("Password:", pwd, permit_passwd);
+#else
p = getpass("Password:");
+#endif /* SKEY */
if (pwd) {
#ifdef KERBEROS
@@ -256,9 +270,19 @@ main(argc, argv)
if (rval == 0)
authok = 1;
else if (rval == 1)
+#ifdef SKEY
+ rval = strcmp(skey_crypt(p, salt, pwd, permit_passwd),
+ pwd->pw_passwd);
+#else
rval = strcmp(crypt(p, salt), pwd->pw_passwd);
+#endif /* SKEY */
+#else
+#ifdef SKEY
+ rval = strcmp(skey_crypt(p, salt, pwd, permit_passwd),
+ pwd->pw_passwd);
#else
rval = strcmp(crypt(p, salt), pwd->pw_passwd);
+#endif /* SKEY */
#endif
}
bzero(p, strlen(p));
@@ -393,6 +417,18 @@ main(argc, argv)
(void)printf("Warning: no Kerberos tickets issued.\n");
#endif
+#ifdef LOGALL
+ /*
+ * Syslog each successful login, so we don't have to watch hundreds
+ * of wtmp or lastlogin files.
+ */
+ if (hostname) {
+ syslog(LOG_INFO, "login from %s as %s", hostname, pwd->pw_name);
+ } else {
+ syslog(LOG_INFO, "login on %s as %s", tty, pwd->pw_name);
+ }
+#endif
+
if (!quietlog) {
(void)printf(
"Copyright (c) 1980,1983,1986,1988,1990,1991 The Regents of the University\n%s",
@@ -405,6 +441,19 @@ main(argc, argv)
(st.st_mtime > st.st_atime) ? "new " : "");
}
+#ifdef LOGIN_ACCESS
+ if (login_access(pwd->pw_name, hostname ? hostname : tty) == 0) {
+ printf("Permission denied\n");
+ if (hostname)
+ syslog(LOG_NOTICE, "%s LOGIN REFUSED FROM %s",
+ pwd->pw_name, hostname);
+ else
+ syslog(LOG_NOTICE, "%s LOGIN REFUSED ON %s",
+ pwd->pw_name, tty);
+ sleepexit(1);
+ }
+#endif
+
(void)signal(SIGALRM, SIG_DFL);
(void)signal(SIGQUIT, SIG_DFL);
(void)signal(SIGINT, SIG_DFL);
diff --git a/usr.bin/login/login_access.c b/usr.bin/login/login_access.c
new file mode 100644
index 000000000000..90de8e0ae368
--- /dev/null
+++ b/usr.bin/login/login_access.c
@@ -0,0 +1,236 @@
+ /*
+ * This module implements a simple but effective form of login access
+ * control based on login names and on host (or domain) names, internet
+ * addresses (or network numbers), or on terminal line names in case of
+ * non-networked logins. Diagnostics are reported through syslog(3).
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+#ifdef LOGIN_ACCESS
+#ifndef lint
+static char sccsid[] = "%Z% %M% %I% %E% %U%";
+#endif
+
+#include <stdio.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <grp.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "pathnames.h"
+
+ /* Delimiters for fields and for lists of users, ttys or hosts. */
+
+static char fs[] = ":"; /* field separator */
+static char sep[] = ", \t"; /* list-element separator */
+
+ /* Constants to be used in assignments only, not in comparisons... */
+
+#define YES 1
+#define NO 0
+
+static int list_match();
+static int user_match();
+static int from_match();
+static int string_match();
+
+/* login_access - match username/group and host/tty with access control file */
+
+login_access(user, from)
+char *user;
+char *from;
+{
+ FILE *fp;
+ char line[BUFSIZ];
+ char *perm; /* becomes permission field */
+ char *users; /* becomes list of login names */
+ char *froms; /* becomes list of terminals or hosts */
+ int match = NO;
+ int end;
+ int lineno = 0; /* for diagnostics */
+
+ /*
+ * Process the table one line at a time and stop at the first match.
+ * Blank lines and lines that begin with a '#' character are ignored.
+ * Non-comment lines are broken at the ':' character. All fields are
+ * mandatory. The first field should be a "+" or "-" character. A
+ * non-existing table means no access control.
+ */
+
+ if (fp = fopen(_PATH_LOGACCESS, "r")) {
+ while (!match && fgets(line, sizeof(line), fp)) {
+ lineno++;
+ if (line[end = strlen(line) - 1] != '\n') {
+ syslog(LOG_ERR, "%s: line %d: missing newline or line too long",
+ _PATH_LOGACCESS, lineno);
+ continue;
+ }
+ if (line[0] == '#')
+ continue; /* comment line */
+ while (end > 0 && isspace(line[end - 1]))
+ end--;
+ line[end] = 0; /* strip trailing whitespace */
+ if (line[0] == 0) /* skip blank lines */
+ continue;
+ if (!(perm = strtok(line, fs))
+ || !(users = strtok((char *) 0, fs))
+ || !(froms = strtok((char *) 0, fs))
+ || strtok((char *) 0, fs)) {
+ syslog(LOG_ERR, "%s: line %d: bad field count", _PATH_LOGACCESS,
+ lineno);
+ continue;
+ }
+ if (perm[0] != '+' && perm[0] != '-') {
+ syslog(LOG_ERR, "%s: line %d: bad first field", _PATH_LOGACCESS,
+ lineno);
+ continue;
+ }
+ match = (list_match(froms, from, from_match)
+ && list_match(users, user, user_match));
+ }
+ (void) fclose(fp);
+ } else if (errno != ENOENT) {
+ syslog(LOG_ERR, "cannot open %s: %m", _PATH_LOGACCESS);
+ }
+ return (match == 0 || (line[0] == '+'));
+}
+
+/* list_match - match an item against a list of tokens with exceptions */
+
+static int list_match(list, item, match_fn)
+char *list;
+char *item;
+int (*match_fn) ();
+{
+ char *tok;
+ int match = NO;
+
+ /*
+ * Process tokens one at a time. We have exhausted all possible matches
+ * when we reach an "EXCEPT" token or the end of the list. If we do find
+ * a match, look for an "EXCEPT" list and recurse to determine whether
+ * the match is affected by any exceptions.
+ */
+
+ for (tok = strtok(list, sep); tok != 0; tok = strtok((char *) 0, sep)) {
+ if (strcasecmp(tok, "EXCEPT") == 0) /* EXCEPT: give up */
+ break;
+ if (match = (*match_fn) (tok, item)) /* YES */
+ break;
+ }
+ /* Process exceptions to matches. */
+
+ if (match != NO) {
+ while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT"))
+ /* VOID */ ;
+ if (tok == 0 || list_match((char *) 0, item, match_fn) == NO)
+ return (match);
+ }
+ return (NO);
+}
+
+/* netgroup_match - match group against machine or user */
+
+static int netgroup_match(group, machine, user)
+char *machine;
+char *user;
+{
+#ifdef NIS
+ static char *mydomain = 0;
+
+ if (mydomain == 0)
+ yp_get_default_domain(&mydomain);
+ return (innetgr(group, machine, user, mydomain));
+#else
+ syslog(LOG_ERR, "NIS netgroup support not configured");
+#endif
+}
+
+/* user_match - match a username against one token */
+
+static int user_match(tok, string)
+char *tok;
+char *string;
+{
+ struct group *group;
+ int i;
+
+ /*
+ * If a token has the magic value "ALL" the match always succeeds.
+ * Otherwise, return YES if the token fully matches the username, or if
+ * the token is a group that contains the username.
+ */
+
+ if (tok[0] == '@') { /* netgroup */
+ return (netgroup_match(tok + 1, (char *) 0, string));
+ } else if (string_match(tok, string)) { /* ALL or exact match */
+ return (YES);
+ } else if (group = getgrnam(tok)) { /* try group membership */
+ for (i = 0; group->gr_mem[i]; i++)
+ if (strcasecmp(string, group->gr_mem[i]) == 0)
+ return (YES);
+ }
+ return (NO);
+}
+
+/* from_match - match a host or tty against a list of tokens */
+
+static int from_match(tok, string)
+char *tok;
+char *string;
+{
+ int tok_len;
+ int str_len;
+
+ /*
+ * If a token has the magic value "ALL" the match always succeeds. Return
+ * YES if the token fully matches the string. If the token is a domain
+ * name, return YES if it matches the last fields of the string. If the
+ * token has the magic value "LOCAL", return YES if the string does not
+ * contain a "." character. If the token is a network number, return YES
+ * if it matches the head of the string.
+ */
+
+ if (tok[0] == '@') { /* netgroup */
+ return (netgroup_match(tok + 1, string, (char *) 0));
+ } else if (string_match(tok, string)) { /* ALL or exact match */
+ return (YES);
+ } else if (tok[0] == '.') { /* domain: match last fields */
+ if ((str_len = strlen(string)) > (tok_len = strlen(tok))
+ && strcasecmp(tok, string + str_len - tok_len) == 0)
+ return (YES);
+ } else if (strcasecmp(tok, "LOCAL") == 0) { /* local: no dots */
+ if (strchr(string, '.') == 0)
+ return (YES);
+ } else if (tok[(tok_len = strlen(tok)) - 1] == '.' /* network */
+ && strncmp(tok, string, tok_len) == 0) {
+ return (YES);
+ }
+ return (NO);
+}
+
+/* string_match - match a string against one token */
+
+static int string_match(tok, string)
+char *tok;
+char *string;
+{
+
+ /*
+ * If the token has the magic value "ALL" the match always succeeds.
+ * Otherwise, return YES if the token fully matches the string.
+ */
+
+ if (strcasecmp(tok, "ALL") == 0) { /* all: always matches */
+ return (YES);
+ } else if (strcasecmp(tok, string) == 0) { /* try exact match */
+ return (YES);
+ }
+ return (NO);
+}
+#endif /* LOGIN_ACCES */
diff --git a/usr.bin/login/login_skey.c b/usr.bin/login/login_skey.c
new file mode 100644
index 000000000000..b94bd28ad9cb
--- /dev/null
+++ b/usr.bin/login/login_skey.c
@@ -0,0 +1,105 @@
+ /* Portions taken from the skey distribution on Oct 21 1993 */
+#ifdef SKEY
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <termios.h>
+#include <pwd.h>
+#include <syslog.h>
+
+#include <skey.h>
+
+/* skey_getpass - read regular or s/key password */
+
+char *skey_getpass(prompt, pwd, pwok)
+char *prompt;
+struct passwd *pwd;
+int pwok;
+{
+ static char buf[128];
+ struct skey skey;
+ char *cp;
+ void rip();
+ struct termios saved_ttymode;
+ struct termios noecho_ttymode;
+ char *username = pwd ? pwd->pw_name : "nope";
+ int sflag;
+
+ /* Attempt an s/key challenge. */
+
+ if ((sflag = skeychallenge(&skey, username, buf)) == 0) {
+ printf("%s\n", buf);
+ }
+ if (!pwok) {
+ printf("(s/key required)\n");
+ }
+ fputs(prompt, stdout);
+ fflush(stdout);
+
+ /* Save current input modes and turn echo off. */
+
+ tcgetattr(0, &saved_ttymode);
+ tcgetattr(0, &noecho_ttymode);
+ noecho_ttymode.c_lflag &= ~ECHO;
+ tcsetattr(0, TCSANOW, &noecho_ttymode);
+
+ /* Read password. */
+
+ buf[0] = 0;
+ fgets(buf, sizeof(buf), stdin);
+ rip(buf);
+
+ /* Restore previous input modes. */
+
+ tcsetattr(0, TCSANOW, &saved_ttymode);
+
+ /* Give S/Key users a chance to do it with echo on. */
+
+ if (sflag == 0 && feof(stdin) == 0 && buf[0] == 0) {
+ fputs(" (turning echo on)\n", stdout);
+ fputs(prompt, stdout);
+ fflush(stdout);
+ fgets(buf, sizeof(buf), stdin);
+ rip(buf);
+ } else {
+ putchar('\n');
+ }
+ return (buf);
+}
+
+/* skey_crypt - return encrypted UNIX passwd if s/key or regular password ok */
+
+char *skey_crypt(pp, salt, pwd, pwok)
+char *pp;
+char *salt;
+struct passwd *pwd;
+int pwok;
+{
+ struct skey skey;
+ char *p;
+ char *crypt();
+
+ /* Try s/key authentication even when the UNIX password is permitted. */
+
+ if (pwd != 0 && skeylookup(&skey, pwd->pw_name) == 0
+ && skeyverify(&skey, pp) == 0) {
+ /* s/key authentication succeeded */
+ if (skey.n < 5)
+ printf("Warning! Change s/key password soon\n");
+ return (pwd->pw_passwd);
+ }
+
+ /* When s/key authentication does not work, always invoke crypt(). */
+
+ p = crypt(pp, salt);
+ if (pwok && pwd != 0 && strcmp(p, pwd->pw_passwd) == 0)
+ return (pwd->pw_passwd);
+
+ /* The user does not exist or entered bad input. */
+
+ return (":");
+}
+#endif SKEY
diff --git a/usr.bin/login/pathnames.h b/usr.bin/login/pathnames.h
index 1c8ff9dd07b3..22e579cf7ef0 100644
--- a/usr.bin/login/pathnames.h
+++ b/usr.bin/login/pathnames.h
@@ -37,3 +37,4 @@
#define _PATH_HUSHLOGIN ".hushlogin"
#define _PATH_MOTDFILE "/etc/motd"
+#define _PATH_LOGACCESS "/etc/login.access"