login_access.c revision 22230
1139804Simp /*
21541Srgrimes  * This module implements a simple but effective form of login access
31541Srgrimes  * control based on login names and on host (or domain) names, internet
41541Srgrimes  * addresses (or network numbers), or on terminal line names in case of
51541Srgrimes  * non-networked logins. Diagnostics are reported through syslog(3).
61541Srgrimes  *
71541Srgrimes  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
81541Srgrimes  */
91541Srgrimes
101541Srgrimes#ifdef LOGIN_ACCESS
111541Srgrimes#ifndef lint
121541Srgrimesstatic char sccsid[] = "%Z% %M% %I% %E% %U%";
131541Srgrimes#endif
141541Srgrimes
151541Srgrimes#include <stdio.h>
161541Srgrimes#include <syslog.h>
171541Srgrimes#include <ctype.h>
181541Srgrimes#include <sys/types.h>
191541Srgrimes#include <grp.h>
201541Srgrimes#include <errno.h>
211541Srgrimes#include <string.h>
221541Srgrimes#include <unistd.h>
231541Srgrimes#include <stdlib.h>
241541Srgrimes
251541Srgrimes#include "pathnames.h"
261541Srgrimes
271541Srgrimes /* Delimiters for fields and for lists of users, ttys or hosts. */
281541Srgrimes
291541Srgrimesstatic char fs[] = ":";			/* field separator */
301541Srgrimesstatic char sep[] = ", \t";		/* list-element separator */
311541Srgrimes
321541Srgrimes /* Constants to be used in assignments only, not in comparisons... */
331541Srgrimes
3414526Shsu#define YES             1
351541Srgrimes#define NO              0
361541Srgrimes
37116182Sobrienstatic int list_match();
38116182Sobrienstatic int user_match();
39116182Sobrienstatic int from_match();
4014328Speterstatic int string_match();
41217688Spluknet
4280418Speter/* login_access - match username/group and host/tty with access control file */
4313226Swollman
44202143Sbrooksint
451541Srgrimeslogin_access(user, from)
4680418Speterchar   *user;
4780418Speterchar   *from;
48172696Salfred{
49217688Spluknet    FILE   *fp;
501541Srgrimes    char    line[BUFSIZ];
51137393Sdes    char   *perm;			/* becomes permission field */
5284783Sps    char   *users;			/* becomes list of login names */
531541Srgrimes    char   *froms;			/* becomes list of terminals or hosts */
541541Srgrimes    int     match = NO;
551541Srgrimes    int     end;
561541Srgrimes    int     lineno = 0;			/* for diagnostics */
571541Srgrimes
58209492Snwhitehorn    /*
59209492Snwhitehorn     * Process the table one line at a time and stop at the first match.
60209492Snwhitehorn     * Blank lines and lines that begin with a '#' character are ignored.
61137307Sphk     * Non-comment lines are broken at the ':' character. All fields are
62138214Sbms     * mandatory. The first field should be a "+" or "-" character. A
63184323Ssobomax     * non-existing table means no access control.
64195430Ssilby     */
65184323Ssobomax
66184323Ssobomax    if ((fp = fopen(_PATH_LOGACCESS, "r")) != NULL) {
67184323Ssobomax	while (!match && fgets(line, sizeof(line), fp)) {
68184323Ssobomax	    lineno++;
69184323Ssobomax	    if (line[end = strlen(line) - 1] != '\n') {
701541Srgrimes		syslog(LOG_ERR, "%s: line %d: missing newline or line too long",
7180418Speter		       _PATH_LOGACCESS, lineno);
7280418Speter		continue;
7380418Speter	    }
7480418Speter	    if (line[0] == '#')
7545515Sdes		continue;			/* comment line */
7680418Speter	    while (end > 0 && isspace(line[end - 1]))
7745515Sdes		end--;
788747Sdg	    line[end] = 0;			/* strip trailing whitespace */
79186286Sivoras	    if (line[0] == 0)			/* skip blank lines */
80186286Sivoras		continue;
8180418Speter	    if (!(perm = strtok(line, fs))
8280418Speter		|| !(users = strtok((char *) 0, fs))
8380418Speter		|| !(froms = strtok((char *) 0, fs))
8480418Speter		|| strtok((char *) 0, fs)) {
8580418Speter		syslog(LOG_ERR, "%s: line %d: bad field count", _PATH_LOGACCESS,
8680418Speter		       lineno);
8780418Speter		continue;
88217688Spluknet	    }
8980418Speter	    if (perm[0] != '+' && perm[0] != '-') {
9080418Speter		syslog(LOG_ERR, "%s: line %d: bad first field", _PATH_LOGACCESS,
91202143Sbrooks		       lineno);
9280418Speter		continue;
93189595Sjhb	    }
94189595Sjhb	    match = (list_match(froms, from, from_match)
95189649Sjhb		     && list_match(users, user, user_match));
96186286Sivoras	}
97137393Sdes	(void) fclose(fp);
98137393Sdes    } else if (errno != ENOENT) {
99137393Sdes	syslog(LOG_ERR, "cannot open %s: %m", _PATH_LOGACCESS);
100137393Sdes    }
101137393Sdes    return (match == 0 || (line[0] == '+'));
102137393Sdes}
1031541Srgrimes
104190331Sjhb/* list_match - match an item against a list of tokens with exceptions */
105190331Sjhb
106189744Sjhbstatic int list_match(list, item, match_fn)
107189744Sjhbchar   *list;
108189744Sjhbchar   *item;
109190331Sjhbint   (*match_fn) ();
110189744Sjhb{
111189744Sjhb    char   *tok;
112217688Spluknet    int     match = NO;
113217688Spluknet
114189745Sjhb    /*
115190331Sjhb     * Process tokens one at a time. We have exhausted all possible matches
116189745Sjhb     * when we reach an "EXCEPT" token or the end of the list. If we do find
117190331Sjhb     * a match, look for an "EXCEPT" list and recurse to determine whether
118178872Spjd     * the match is affected by any exceptions.
119190331Sjhb     */
120178872Spjd
121190331Sjhb    for (tok = strtok(list, sep); tok != 0; tok = strtok((char *) 0, sep)) {
122178872Spjd	if (strcasecmp(tok, "EXCEPT") == 0)	/* EXCEPT: give up */
123190331Sjhb	    break;
124178872Spjd	if ((match = (*match_fn)(tok, item)) != NULL)	/* YES */
125190331Sjhb	    break;
126178872Spjd    }
127190331Sjhb    /* Process exceptions to matches. */
128178872Spjd
129190331Sjhb    if (match != NO) {
130186286Sivoras	while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT"))
131186286Sivoras	     /* VOID */ ;
132204611Sivoras	if (tok == 0 || list_match((char *) 0, item, match_fn) == NO)
133172696Salfred	    return (match);
1341541Srgrimes    }
1351541Srgrimes    return (NO);
1361541Srgrimes}
1371541Srgrimes
1381541Srgrimes/* netgroup_match - match group against machine or user */
1399759Sbde
14067046Sjasonestatic int netgroup_match(group, machine, user)
141204420Salcgid_t   group;
142204420Salcchar   *machine;
143204420Salcchar   *user;
144204420Salc{
145186522Sbz#ifdef NIS
146186522Sbz    static char *mydomain = 0;
147186522Sbz
148186522Sbz    if (mydomain == 0)
149186522Sbz	yp_get_default_domain(&mydomain);
150186522Sbz    return (innetgr(group, machine, user, mydomain));
151186522Sbz#else
152186522Sbz    syslog(LOG_ERR, "NIS netgroup support not configured");
153185772Sjkim    return 0;
154185772Sjkim#endif
155185772Sjkim}
156185772Sjkim
157210935Scsjp/* user_match - match a username against one token */
158185772Sjkim
159185772Sjkimstatic int user_match(tok, string)
160185772Sjkimchar   *tok;
161184326Ssobomaxchar   *string;
162184323Ssobomax{
163184323Ssobomax    struct group *group;
164184323Ssobomax    int     i;
165184323Ssobomax
166184323Ssobomax    /*
167184323Ssobomax     * If a token has the magic value "ALL" the match always succeeds.
168184323Ssobomax     * Otherwise, return YES if the token fully matches the username, or if
169186286Sivoras     * the token is a group that contains the username.
170186286Sivoras     */
171186286Sivoras
172186286Sivoras    if (tok[0] == '@') {			/* netgroup */
173186252Sivoras	return (netgroup_match(tok + 1, (char *) 0, string));
174184323Ssobomax    } else if (string_match(tok, string)) {	/* ALL or exact match */
175184323Ssobomax	return (YES);
176184323Ssobomax    } else if ((group = getgrnam(tok)) != NULL) {/* try group membership */
177184323Ssobomax	for (i = 0; group->gr_mem[i]; i++)
178184323Ssobomax	    if (strcasecmp(string, group->gr_mem[i]) == 0)
179185772Sjkim		return (YES);
180185772Sjkim    }
181185772Sjkim    return (NO);
182185772Sjkim}
183185772Sjkim
184186252Sivoras/* from_match - match a host or tty against a list of tokens */
185185772Sjkim
186185772Sjkimstatic int from_match(tok, string)
187185772Sjkimchar   *tok;
188184323Ssobomaxchar   *string;
189184323Ssobomax{
190185772Sjkim    int     tok_len;
191185772Sjkim    int     str_len;
192185772Sjkim
193186252Sivoras    /*
194185772Sjkim     * If a token has the magic value "ALL" the match always succeeds. Return
195185772Sjkim     * YES if the token fully matches the string. If the token is a domain
196184323Ssobomax     * name, return YES if it matches the last fields of the string. If the
197186252Sivoras     * token has the magic value "LOCAL", return YES if the string does not
198184323Ssobomax     * contain a "." character. If the token is a network number, return YES
199186522Sbz     * if it matches the head of the string.
200184323Ssobomax     */
20167046Sjasone
20287546Sdillon    if (tok[0] == '@') {			/* netgroup */
20380418Speter	return (netgroup_match(tok + 1, string, (char *) 0));
20480418Speter    } else if (string_match(tok, string)) {	/* ALL or exact match */
20587546Sdillon	return (YES);
20680418Speter    } else if (tok[0] == '.') {			/* domain: match last fields */
207186252Sivoras	if ((str_len = strlen(string)) > (tok_len = strlen(tok))
208186252Sivoras	    && strcasecmp(tok, string + str_len - tok_len) == 0)
209186252Sivoras	    return (YES);
210186252Sivoras    } else if (strcasecmp(tok, "LOCAL") == 0) {	/* local: no dots */
211186252Sivoras	if (strchr(string, '.') == 0)
212184323Ssobomax	    return (YES);
21380418Speter    } else if (tok[(tok_len = strlen(tok)) - 1] == '.'	/* network */
214185772Sjkim	       && strncmp(tok, string, tok_len) == 0) {
215186252Sivoras	return (YES);
21680418Speter    }
21780418Speter    return (NO);
21881986Sdillon}
21981933Sdillon
22081986Sdillon/* string_match - match a string against one token */
221189595Sjhb
22281986Sdillonstatic int string_match(tok, string)
22381933Sdillonchar   *tok;
22481986Sdillonchar   *string;
225189595Sjhb{
226217688Spluknet
227217688Spluknet    /*
22884783Sps     * If the token has the magic value "ALL" the match always succeeds.
22984783Sps     * Otherwise, return YES if the token fully matches the string.
230137393Sdes     */
23184783Sps
232137393Sdes    if (strcasecmp(tok, "ALL") == 0) {		/* all: always matches */
23384783Sps	return (YES);
234137393Sdes    } else if (strcasecmp(tok, string) == 0) {	/* try exact match */
23584783Sps	return (YES);
236137393Sdes    }
23784783Sps    return (NO);
238137393Sdes}
23984783Sps#endif /* LOGIN_ACCES */
240137393Sdes