Add skey supprot

Reviewed by:
Submitted by:	guido
This commit is contained in:
Guido van Rooij 1994-08-21 19:26:22 +00:00
parent d9e9aec7ad
commit 7c4c6e58ba
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=2198
8 changed files with 695 additions and 3 deletions

View File

@ -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

View File

@ -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 */

View File

@ -2,14 +2,18 @@
#CFLAGS+=-DKERBEROS
PROG= login
SRCS= login.c
MAN1= login.1
MAN5= login.access.5
SRCS= login.c login_access.c login_skey.c
#klogin.c
DPADD= ${LIBUTIL}
DPADD= ${LIBUTIL} ${LIBSKEY}
#${LIBKRB} ${LIBDES}
LDADD= -lutil -lcrypt
LDADD= -lutil -lcrypt -lskey
#-lkrb -ldes
BINOWN= root
BINMODE=4555
CFLAGS+= -DLOGIN_ACCESS -DSKEY -DLOGALL
INSTALLFLAGS=-fschg
.include <bsd.prog.mk>

10
usr.bin/login/README Normal file
View File

@ -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

View File

@ -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

View File

@ -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 */

105
usr.bin/login/login_skey.c Normal file
View File

@ -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

View File

@ -37,3 +37,4 @@
#define _PATH_HUSHLOGIN ".hushlogin"
#define _PATH_MOTDFILE "/etc/motd"
#define _PATH_LOGACCESS "/etc/login.access"