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