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