]> granicus.if.org Git - shadow/blob - src/login_nopam.c
8f124fb20c2ee0d33f9b569de07615f4b1effd7b
[shadow] / src / login_nopam.c
1 /* Taken from logdaemon-5.0, only minimal changes.  --marekm */
2
3 /************************************************************************
4 * Copyright 1995 by Wietse Venema.  All rights reserved. Individual files
5 * may be covered by other copyrights (as noted in the file itself.)
6 *
7 * This material was originally written and compiled by Wietse Venema at
8 * Eindhoven University of Technology, The Netherlands, in 1990, 1991,
9 * 1992, 1993, 1994 and 1995.
10 *
11 * Redistribution and use in source and binary forms are permitted
12 * provided that this entire copyright notice is duplicated in all such
13 * copies.  
14 *
15 * This software is provided "as is" and without any expressed or implied
16 * warranties, including, without limitation, the implied warranties of
17 * merchantibility and fitness for any particular purpose.
18 ************************************************************************/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #ifndef USE_PAM
25 #include "rcsid.h"
26 RCSID ("$Id: login_nopam.c,v 1.3 2005/04/17 15:38:56 kloczek Exp $")
27 #include "prototypes.h"
28     /*
29      * This module implements a simple but effective form of login access
30      * control based on login names and on host (or domain) names, internet
31      * addresses (or network numbers), or on terminal line names in case of
32      * non-networked logins. Diagnostics are reported through syslog(3).
33      * 
34      * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
35      */
36 #include <sys/types.h>
37 #include <stdio.h>
38 #include <syslog.h>
39 #include <ctype.h>
40 #include <netdb.h>
41 #include <grp.h>
42 #ifdef PRIMARY_GROUP_MATCH
43 #include <pwd.h>
44 #endif
45 #include <errno.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <sys/socket.h>
50 #include <netinet/in.h>
51 #include <arpa/inet.h>          /* for inet_ntoa() */
52 extern struct group *getgrnam ();
53 extern int innetgr ();
54
55 #if !defined(MAXHOSTNAMELEN) || (MAXHOSTNAMELEN < 64)
56 #undef MAXHOSTNAMELEN
57 #define MAXHOSTNAMELEN 256
58 #endif
59
60  /* Path name of the access control file. */
61
62 #ifndef TABLE
63 #define TABLE   "/etc/login.access"
64 #endif
65
66  /* Delimiters for fields and for lists of users, ttys or hosts. */
67
68 static char fs[] = ":";         /* field separator */
69 static char sep[] = ", \t";     /* list-element separator */
70
71  /* Constants to be used in assignments only, not in comparisons... */
72
73 #define YES             1
74 #define NO              0
75
76 static int list_match ();
77 static int user_match ();
78 static int from_match ();
79 static int string_match ();
80
81 /* login_access - match username/group and host/tty with access control file */
82
83 int login_access (const char *user, const char *from)
84 {
85         FILE *fp;
86         char line[BUFSIZ];
87         char *perm;             /* becomes permission field */
88         char *users;            /* becomes list of login names */
89         char *froms;            /* becomes list of terminals or hosts */
90         int match = NO;
91         int end;
92         int lineno = 0;         /* for diagnostics */
93
94         /*
95          * Process the table one line at a time and stop at the first match.
96          * Blank lines and lines that begin with a '#' character are ignored.
97          * Non-comment lines are broken at the ':' character. All fields are
98          * mandatory. The first field should be a "+" or "-" character. A
99          * non-existing table means no access control.
100          */
101
102         if ((fp = fopen (TABLE, "r"))) {
103                 while (!match && fgets (line, sizeof (line), fp)) {
104                         lineno++;
105                         if (line[end = strlen (line) - 1] != '\n') {
106                                 syslog (LOG_ERR,
107                                         "%s: line %d: missing newline or line too long",
108                                         TABLE, lineno);
109                                 continue;
110                         }
111                         if (line[0] == '#')
112                                 continue;       /* comment line */
113                         while (end > 0 && isspace (line[end - 1]))
114                                 end--;
115                         line[end] = 0;  /* strip trailing whitespace */
116                         if (line[0] == 0)       /* skip blank lines */
117                                 continue;
118                         if (!(perm = strtok (line, fs))
119                             || !(users = strtok ((char *) 0, fs))
120                             || !(froms = strtok ((char *) 0, fs))
121                             || strtok ((char *) 0, fs)) {
122                                 syslog (LOG_ERR,
123                                         "%s: line %d: bad field count",
124                                         TABLE, lineno);
125                                 continue;
126                         }
127                         if (perm[0] != '+' && perm[0] != '-') {
128                                 syslog (LOG_ERR,
129                                         "%s: line %d: bad first field",
130                                         TABLE, lineno);
131                                 continue;
132                         }
133                         match = (list_match (froms, from, from_match)
134                                  && list_match (users, user, user_match));
135                 }
136                 (void) fclose (fp);
137         } else if (errno != ENOENT) {
138                 syslog (LOG_ERR, "cannot open %s: %m", TABLE);
139         }
140         return (match == 0 || (line[0] == '+'));
141 }
142
143 /* list_match - match an item against a list of tokens with exceptions */
144
145 static int list_match (char *list, const char *item, int (*match_fn) ())
146 {
147         char *tok;
148         int match = NO;
149
150         /*
151          * Process tokens one at a time. We have exhausted all possible matches
152          * when we reach an "EXCEPT" token or the end of the list. If we do find
153          * a match, look for an "EXCEPT" list and recurse to determine whether
154          * the match is affected by any exceptions.
155          */
156
157         for (tok = strtok (list, sep); tok != 0; tok = strtok ((char *) 0, sep)) {
158                 if (strcasecmp (tok, "EXCEPT") == 0)    /* EXCEPT: give up */
159                         break;
160                 if ((match = (*match_fn) (tok, item)))  /* YES */
161                         break;
162         }
163         /* Process exceptions to matches. */
164
165         if (match != NO) {
166                 while ((tok = strtok ((char *) 0, sep))
167                        && strcasecmp (tok, "EXCEPT"))
168                         /* VOID */ ;
169                 if (tok == 0 || list_match ((char *) 0, item, match_fn) == NO)
170                         return (match);
171         }
172         return (NO);
173 }
174
175 /* myhostname - figure out local machine name */
176
177 static char *myhostname (void)
178 {
179         static char name[MAXHOSTNAMELEN + 1] = "";
180
181         if (name[0] == 0) {
182                 gethostname (name, sizeof (name));
183                 name[MAXHOSTNAMELEN] = 0;
184         }
185         return (name);
186 }
187
188 /* netgroup_match - match group against machine or user */
189
190 static int
191 netgroup_match (const char *group, const char *machine, const char *user)
192 {
193         static char *mydomain = 0;
194
195         if (mydomain == 0) {
196                 static char domain[MAXHOSTNAMELEN + 1];
197
198                 getdomainname (domain, MAXHOSTNAMELEN);
199                 mydomain = domain;
200         }
201
202         return innetgr (group, machine, user, mydomain);
203 }
204
205 /* user_match - match a username against one token */
206
207 static int user_match (const char *tok, const char *string)
208 {
209         struct group *group;
210
211 #ifdef PRIMARY_GROUP_MATCH
212         struct passwd *userinf;
213 #endif
214         int i;
215         char *at;
216
217         /*
218          * If a token has the magic value "ALL" the match always succeeds.
219          * Otherwise, return YES if the token fully matches the username, or if
220          * the token is a group that contains the username.
221          */
222
223         if ((at = strchr (tok + 1, '@')) != 0) {        /* split user@host pattern */
224                 *at = 0;
225                 return (user_match (tok, string)
226                         && from_match (at + 1, myhostname ()));
227         } else if (tok[0] == '@') {     /* netgroup */
228                 return (netgroup_match (tok + 1, (char *) 0, string));
229         } else if (string_match (tok, string)) {        /* ALL or exact match */
230                 return (YES);
231         } else if ((group = getgrnam (tok))) {  /* try group membership */
232                 for (i = 0; group->gr_mem[i]; i++)
233                         if (strcasecmp (string, group->gr_mem[i]) == 0)
234                                 return (YES);
235 #ifdef PRIMARY_GROUP_MATCH
236                 /*
237                  * If the sting is an user whose initial GID matches the token,
238                  * accept it. May avoid excessively long lines in /etc/group.
239                  * Radu-Adrian Feurdean <raf@licj.soroscj.ro>
240                  *
241                  * XXX - disabled by default for now.  Need to verify that
242                  * getpwnam() doesn't have some nasty side effects.  --marekm
243                  */
244                 if ((userinf = getpwnam (string)))
245                         if (userinf->pw_gid == group->gr_gid)
246                                 return (YES);
247 #endif
248         }
249         return (NO);
250 }
251
252 static char *resolve_hostname (string)
253 char *string;
254 {
255         /*
256          * Resolve hostname to numeric IP address, as suggested
257          * by Dave Hagewood <admin@arrowweb.com>.  --marekm
258          */
259         struct hostent *hp;
260
261         hp = gethostbyname (string);
262         if (hp)
263                 return inet_ntoa (*((struct in_addr *) *(hp->h_addr_list)));
264
265         syslog (LOG_ERR, "%s - unknown host", string);
266         return string;
267 }
268
269 /* from_match - match a host or tty against a list of tokens */
270
271 static int from_match (const char *tok, const char *string)
272 {
273         int tok_len;
274         int str_len;
275
276         /*
277          * If a token has the magic value "ALL" the match always succeeds. Return
278          * YES if the token fully matches the string. If the token is a domain
279          * name, return YES if it matches the last fields of the string. If the
280          * token has the magic value "LOCAL", return YES if the string does not
281          * contain a "." character. If the token is a network number, return YES
282          * if it matches the head of the string.
283          */
284
285         if (tok[0] == '@') {    /* netgroup */
286                 return (netgroup_match (tok + 1, string, (char *) 0));
287         } else if (string_match (tok, string)) {        /* ALL or exact match */
288                 return (YES);
289         } else if (tok[0] == '.') {     /* domain: match last fields */
290                 if ((str_len = strlen (string)) > (tok_len = strlen (tok))
291                     && strcasecmp (tok, string + str_len - tok_len) == 0)
292                         return (YES);
293         } else if (strcasecmp (tok, "LOCAL") == 0) {    /* local: no dots */
294                 if (strchr (string, '.') == 0)
295                         return (YES);
296         } else if (tok[(tok_len = strlen (tok)) - 1] == '.'     /* network */
297                    && strncmp (tok, resolve_hostname (string), tok_len) == 0) {
298                 return (YES);
299         }
300         return (NO);
301 }
302
303 /* string_match - match a string against one token */
304
305 static int string_match (const char *tok, const char *string)
306 {
307
308         /*
309          * If the token has the magic value "ALL" the match always succeeds.
310          * Otherwise, return YES if the token fully matches the string.
311          */
312
313         if (strcasecmp (tok, "ALL") == 0) {     /* all: always matches */
314                 return (YES);
315         } else if (strcasecmp (tok, string) == 0) {     /* try exact match */
316                 return (YES);
317         }
318         return (NO);
319 }
320
321 #endif                          /* !USE_PAM */