xref: /freebsd/crypto/heimdal/appl/login/login_access.c (revision 6a068746777241722b2b32c5d0bc443a2a64d80b)
14137ff4cSJacques Vidrine /************************************************************************
24137ff4cSJacques Vidrine * Copyright 1995 by Wietse Venema.  All rights reserved.  Some individual
34137ff4cSJacques Vidrine * files may be covered by other copyrights.
44137ff4cSJacques Vidrine *
54137ff4cSJacques Vidrine * This material was originally written and compiled by Wietse Venema at
64137ff4cSJacques Vidrine * Eindhoven University of Technology, The Netherlands, in 1990, 1991,
74137ff4cSJacques Vidrine * 1992, 1993, 1994 and 1995.
84137ff4cSJacques Vidrine *
94137ff4cSJacques Vidrine * Redistribution and use in source and binary forms, with or without
104137ff4cSJacques Vidrine * modification, are permitted provided that this entire copyright notice
114137ff4cSJacques Vidrine * is duplicated in all such copies.
124137ff4cSJacques Vidrine *
134137ff4cSJacques Vidrine * This software is provided "as is" and without any expressed or implied
144137ff4cSJacques Vidrine * warranties, including, without limitation, the implied warranties of
154137ff4cSJacques Vidrine * merchantibility and fitness for any particular purpose.
164137ff4cSJacques Vidrine ************************************************************************/
17b528cefcSMark Murray  /*
18b528cefcSMark Murray   * This module implements a simple but effective form of login access
19b528cefcSMark Murray   * control based on login names and on host (or domain) names, internet
20b528cefcSMark Murray   * addresses (or network numbers), or on terminal line names in case of
21b528cefcSMark Murray   * non-networked logins. Diagnostics are reported through syslog(3).
22b528cefcSMark Murray   *
23b528cefcSMark Murray   * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
24b528cefcSMark Murray   */
25b528cefcSMark Murray 
26b528cefcSMark Murray #include "login_locl.h"
27b528cefcSMark Murray 
28*ae771770SStanislav Sedov RCSID("$Id$");
29b528cefcSMark Murray 
30b528cefcSMark Murray  /* Delimiters for fields and for lists of users, ttys or hosts. */
31b528cefcSMark Murray 
32b528cefcSMark Murray static char fs[] = ":";			/* field separator */
33b528cefcSMark Murray static char sep[] = ", \t";		/* list-element separator */
34b528cefcSMark Murray 
35b528cefcSMark Murray  /* Constants to be used in assignments only, not in comparisons... */
36b528cefcSMark Murray 
37b528cefcSMark Murray #define YES             1
38b528cefcSMark Murray #define NO              0
39b528cefcSMark Murray 
40b528cefcSMark Murray  /*
41b528cefcSMark Murray   * A structure to bundle up all login-related information to keep the
42b528cefcSMark Murray   * functional interfaces as generic as possible.
43b528cefcSMark Murray   */
44b528cefcSMark Murray struct login_info {
45b528cefcSMark Murray     struct passwd *user;
46b528cefcSMark Murray     char   *from;
47b528cefcSMark Murray };
48b528cefcSMark Murray 
49b528cefcSMark Murray static int list_match(char *list, struct login_info *item,
50b528cefcSMark Murray 		      int (*match_fn)(char *, struct login_info *));
51b528cefcSMark Murray static int user_match(char *tok, struct login_info *item);
52b528cefcSMark Murray static int from_match(char *tok, struct login_info *item);
53b528cefcSMark Murray static int string_match(char *tok, char *string);
54b528cefcSMark Murray 
55b528cefcSMark Murray /* login_access - match username/group and host/tty with access control file */
56b528cefcSMark Murray 
login_access(struct passwd * user,char * from)57b528cefcSMark Murray int login_access(struct passwd *user, char *from)
58b528cefcSMark Murray {
59b528cefcSMark Murray     struct login_info item;
60b528cefcSMark Murray     FILE   *fp;
61b528cefcSMark Murray     char    line[BUFSIZ];
62b528cefcSMark Murray     char   *perm;			/* becomes permission field */
63b528cefcSMark Murray     char   *users;			/* becomes list of login names */
64b528cefcSMark Murray     char   *froms;			/* becomes list of terminals or hosts */
65b528cefcSMark Murray     int     match = NO;
66b528cefcSMark Murray     int     end;
67b528cefcSMark Murray     int     lineno = 0;			/* for diagnostics */
68b528cefcSMark Murray     char   *foo;
69b528cefcSMark Murray 
70b528cefcSMark Murray     /*
71b528cefcSMark Murray      * Bundle up the arguments to avoid unnecessary clumsiness lateron.
72b528cefcSMark Murray      */
73b528cefcSMark Murray     item.user = user;
74b528cefcSMark Murray     item.from = from;
75b528cefcSMark Murray 
76b528cefcSMark Murray     /*
77b528cefcSMark Murray      * Process the table one line at a time and stop at the first match.
78b528cefcSMark Murray      * Blank lines and lines that begin with a '#' character are ignored.
79b528cefcSMark Murray      * Non-comment lines are broken at the ':' character. All fields are
80b528cefcSMark Murray      * mandatory. The first field should be a "+" or "-" character. A
81b528cefcSMark Murray      * non-existing table means no access control.
82b528cefcSMark Murray      */
83b528cefcSMark Murray 
84b528cefcSMark Murray     if ((fp = fopen(_PATH_LOGACCESS, "r")) != 0) {
85b528cefcSMark Murray 	while (!match && fgets(line, sizeof(line), fp)) {
86b528cefcSMark Murray 	    lineno++;
87b528cefcSMark Murray 	    if (line[end = strlen(line) - 1] != '\n') {
88b528cefcSMark Murray 		syslog(LOG_ERR, "%s: line %d: missing newline or line too long",
89b528cefcSMark Murray 		       _PATH_LOGACCESS, lineno);
90b528cefcSMark Murray 		continue;
91b528cefcSMark Murray 	    }
92b528cefcSMark Murray 	    if (line[0] == '#')
93b528cefcSMark Murray 		continue;			/* comment line */
94b528cefcSMark Murray 	    while (end > 0 && isspace((unsigned char)line[end - 1]))
95b528cefcSMark Murray 		end--;
96b528cefcSMark Murray 	    line[end] = 0;			/* strip trailing whitespace */
97b528cefcSMark Murray 	    if (line[0] == 0)			/* skip blank lines */
98b528cefcSMark Murray 		continue;
99b528cefcSMark Murray 	    foo = NULL;
100b528cefcSMark Murray 	    if (!(perm = strtok_r(line, fs, &foo))
101b528cefcSMark Murray 		|| !(users = strtok_r(NULL, fs, &foo))
102b528cefcSMark Murray 		|| !(froms = strtok_r(NULL, fs, &foo))
103b528cefcSMark Murray 		|| strtok_r(NULL, fs, &foo)) {
104b528cefcSMark Murray 		syslog(LOG_ERR, "%s: line %d: bad field count",
105b528cefcSMark Murray 		       _PATH_LOGACCESS,
106b528cefcSMark Murray 		       lineno);
107b528cefcSMark Murray 		continue;
108b528cefcSMark Murray 	    }
109b528cefcSMark Murray 	    if (perm[0] != '+' && perm[0] != '-') {
110b528cefcSMark Murray 		syslog(LOG_ERR, "%s: line %d: bad first field",
111b528cefcSMark Murray 		       _PATH_LOGACCESS,
112b528cefcSMark Murray 		       lineno);
113b528cefcSMark Murray 		continue;
114b528cefcSMark Murray 	    }
115b528cefcSMark Murray 	    match = (list_match(froms, &item, from_match)
116b528cefcSMark Murray 		     && list_match(users, &item, user_match));
117b528cefcSMark Murray 	}
118b528cefcSMark Murray 	fclose(fp);
119b528cefcSMark Murray     } else if (errno != ENOENT) {
120b528cefcSMark Murray 	syslog(LOG_ERR, "cannot open %s: %m", _PATH_LOGACCESS);
121b528cefcSMark Murray     }
122b528cefcSMark Murray     return (match == 0 || (line[0] == '+'));
123b528cefcSMark Murray }
124b528cefcSMark Murray 
125b528cefcSMark Murray /* list_match - match an item against a list of tokens with exceptions */
126b528cefcSMark Murray 
127b528cefcSMark Murray static int
list_match(char * list,struct login_info * item,int (* match_fn)(char *,struct login_info *))128b528cefcSMark Murray list_match(char *list,
129b528cefcSMark Murray 	   struct login_info *item,
130b528cefcSMark Murray 	   int (*match_fn)(char *, struct login_info *))
131b528cefcSMark Murray {
132b528cefcSMark Murray     char   *tok;
133b528cefcSMark Murray     int     match = NO;
134b528cefcSMark Murray     char   *foo = NULL;
135b528cefcSMark Murray 
136b528cefcSMark Murray     /*
137b528cefcSMark Murray      * Process tokens one at a time. We have exhausted all possible matches
138b528cefcSMark Murray      * when we reach an "EXCEPT" token or the end of the list. If we do find
139b528cefcSMark Murray      * a match, look for an "EXCEPT" list and recurse to determine whether
140b528cefcSMark Murray      * the match is affected by any exceptions.
141b528cefcSMark Murray      */
142b528cefcSMark Murray 
143b528cefcSMark Murray     for (tok = strtok_r(list, sep, &foo);
144b528cefcSMark Murray 	 tok != NULL;
145b528cefcSMark Murray 	 tok = strtok_r(NULL, sep, &foo)) {
146b528cefcSMark Murray 	if (strcasecmp(tok, "EXCEPT") == 0)	/* EXCEPT: give up */
147b528cefcSMark Murray 	    break;
148b528cefcSMark Murray 	if ((match = (*match_fn) (tok, item)) != 0)	/* YES */
149b528cefcSMark Murray 	    break;
150b528cefcSMark Murray     }
151b528cefcSMark Murray     /* Process exceptions to matches. */
152b528cefcSMark Murray 
153b528cefcSMark Murray     if (match != NO) {
154b528cefcSMark Murray 	while ((tok = strtok_r(NULL, sep, &foo)) && strcasecmp(tok, "EXCEPT"))
155b528cefcSMark Murray 	     /* VOID */ ;
156b528cefcSMark Murray 	if (tok == 0 || list_match(NULL, item, match_fn) == NO)
157b528cefcSMark Murray 	    return (match);
158b528cefcSMark Murray     }
159b528cefcSMark Murray     return (NO);
160b528cefcSMark Murray }
161b528cefcSMark Murray 
162b528cefcSMark Murray /* myhostname - figure out local machine name */
163b528cefcSMark Murray 
myhostname(void)164b528cefcSMark Murray static char *myhostname(void)
165b528cefcSMark Murray {
166b528cefcSMark Murray     static char name[MAXHOSTNAMELEN + 1] = "";
167b528cefcSMark Murray 
168b528cefcSMark Murray     if (name[0] == 0) {
169b528cefcSMark Murray 	gethostname(name, sizeof(name));
170b528cefcSMark Murray 	name[MAXHOSTNAMELEN] = 0;
171b528cefcSMark Murray     }
172b528cefcSMark Murray     return (name);
173b528cefcSMark Murray }
174b528cefcSMark Murray 
175b528cefcSMark Murray /* netgroup_match - match group against machine or user */
176b528cefcSMark Murray 
netgroup_match(char * group,char * machine,char * user)177b528cefcSMark Murray static int netgroup_match(char *group, char *machine, char *user)
178b528cefcSMark Murray {
179b528cefcSMark Murray #ifdef HAVE_YP_GET_DEFAULT_DOMAIN
180b528cefcSMark Murray     static char *mydomain = 0;
181b528cefcSMark Murray 
182b528cefcSMark Murray     if (mydomain == 0)
183b528cefcSMark Murray 	yp_get_default_domain(&mydomain);
184b528cefcSMark Murray     return (innetgr(group, machine, user, mydomain));
185b528cefcSMark Murray #else
186b528cefcSMark Murray     syslog(LOG_ERR, "NIS netgroup support not configured");
187b528cefcSMark Murray     return 0;
188b528cefcSMark Murray #endif
189b528cefcSMark Murray }
190b528cefcSMark Murray 
191b528cefcSMark Murray /* user_match - match a username against one token */
192b528cefcSMark Murray 
user_match(char * tok,struct login_info * item)193b528cefcSMark Murray static int user_match(char *tok, struct login_info *item)
194b528cefcSMark Murray {
195b528cefcSMark Murray     char   *string = item->user->pw_name;
196b528cefcSMark Murray     struct login_info fake_item;
197b528cefcSMark Murray     struct group *group;
198b528cefcSMark Murray     int     i;
199b528cefcSMark Murray     char   *at;
200b528cefcSMark Murray 
201b528cefcSMark Murray     /*
202b528cefcSMark Murray      * If a token has the magic value "ALL" the match always succeeds.
203b528cefcSMark Murray      * Otherwise, return YES if the token fully matches the username, if the
204b528cefcSMark Murray      * token is a group that contains the username, or if the token is the
205b528cefcSMark Murray      * name of the user's primary group.
206b528cefcSMark Murray      */
207b528cefcSMark Murray 
208b528cefcSMark Murray     if ((at = strchr(tok + 1, '@')) != 0) {	/* split user@host pattern */
209b528cefcSMark Murray 	*at = 0;
210b528cefcSMark Murray 	fake_item.from = myhostname();
211b528cefcSMark Murray 	return (user_match(tok, item) && from_match(at + 1, &fake_item));
212b528cefcSMark Murray     } else if (tok[0] == '@') {			/* netgroup */
213b528cefcSMark Murray 	return (netgroup_match(tok + 1, (char *) 0, string));
214b528cefcSMark Murray     } else if (string_match(tok, string)) {	/* ALL or exact match */
215b528cefcSMark Murray 	return (YES);
216b528cefcSMark Murray     } else if ((group = getgrnam(tok)) != 0) { /* try group membership */
217b528cefcSMark Murray 	if (item->user->pw_gid == group->gr_gid)
218b528cefcSMark Murray 	    return (YES);
219b528cefcSMark Murray 	for (i = 0; group->gr_mem[i]; i++)
220b528cefcSMark Murray 	    if (strcasecmp(string, group->gr_mem[i]) == 0)
221b528cefcSMark Murray 		return (YES);
222b528cefcSMark Murray     }
223b528cefcSMark Murray     return (NO);
224b528cefcSMark Murray }
225b528cefcSMark Murray 
226b528cefcSMark Murray /* from_match - match a host or tty against a list of tokens */
227b528cefcSMark Murray 
from_match(char * tok,struct login_info * item)228b528cefcSMark Murray static int from_match(char *tok, struct login_info *item)
229b528cefcSMark Murray {
230b528cefcSMark Murray     char   *string = item->from;
231b528cefcSMark Murray     int     tok_len;
232b528cefcSMark Murray     int     str_len;
233b528cefcSMark Murray 
234b528cefcSMark Murray     /*
235b528cefcSMark Murray      * If a token has the magic value "ALL" the match always succeeds. Return
236b528cefcSMark Murray      * YES if the token fully matches the string. If the token is a domain
237b528cefcSMark Murray      * name, return YES if it matches the last fields of the string. If the
238b528cefcSMark Murray      * token has the magic value "LOCAL", return YES if the string does not
239b528cefcSMark Murray      * contain a "." character. If the token is a network number, return YES
240b528cefcSMark Murray      * if it matches the head of the string.
241b528cefcSMark Murray      */
242b528cefcSMark Murray 
243b528cefcSMark Murray     if (tok[0] == '@') {			/* netgroup */
244b528cefcSMark Murray 	return (netgroup_match(tok + 1, string, (char *) 0));
245b528cefcSMark Murray     } else if (string_match(tok, string)) {	/* ALL or exact match */
246b528cefcSMark Murray 	return (YES);
247b528cefcSMark Murray     } else if (tok[0] == '.') {			/* domain: match last fields */
248b528cefcSMark Murray 	if ((str_len = strlen(string)) > (tok_len = strlen(tok))
249b528cefcSMark Murray 	    && strcasecmp(tok, string + str_len - tok_len) == 0)
250b528cefcSMark Murray 	    return (YES);
251b528cefcSMark Murray     } else if (strcasecmp(tok, "LOCAL") == 0) {	/* local: no dots */
252b528cefcSMark Murray 	if (strchr(string, '.') == 0)
253b528cefcSMark Murray 	    return (YES);
254b528cefcSMark Murray     } else if (tok[(tok_len = strlen(tok)) - 1] == '.'	/* network */
255b528cefcSMark Murray 	       && strncmp(tok, string, tok_len) == 0) {
256b528cefcSMark Murray 	return (YES);
257b528cefcSMark Murray     }
258b528cefcSMark Murray     return (NO);
259b528cefcSMark Murray }
260b528cefcSMark Murray 
261b528cefcSMark Murray /* string_match - match a string against one token */
262b528cefcSMark Murray 
string_match(char * tok,char * string)263b528cefcSMark Murray static int string_match(char *tok, char *string)
264b528cefcSMark Murray {
265b528cefcSMark Murray 
266b528cefcSMark Murray     /*
267b528cefcSMark Murray      * If the token has the magic value "ALL" the match always succeeds.
268b528cefcSMark Murray      * Otherwise, return YES if the token fully matches the string.
269b528cefcSMark Murray      */
270b528cefcSMark Murray 
271b528cefcSMark Murray     if (strcasecmp(tok, "ALL") == 0) {		/* all: always matches */
272b528cefcSMark Murray 	return (YES);
273b528cefcSMark Murray     } else if (strcasecmp(tok, string) == 0) {	/* try exact match */
274b528cefcSMark Murray 	return (YES);
275b528cefcSMark Murray     }
276b528cefcSMark Murray     return (NO);
277b528cefcSMark Murray }
278