]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/checktty.c
Imported from util-linux-2.5 tarball.
[thirdparty/util-linux.git] / login-utils / checktty.c
CommitLineData
726f69e2
KZ
1/* checktty.c - linked into login, checks user against /etc/usertty
2 Created 25-Aug-95 by Peter Orbaek <poe@daimi.aau.dk>
3*/
4
5#include <pwd.h>
6#include <grp.h>
7#include <string.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <unistd.h>
11#include <time.h>
12#include <sys/stat.h>
13#include <malloc.h>
14#include <netdb.h>
15#include <sys/syslog.h>
16
17#ifdef linux
18# include <sys/sysmacros.h>
19# include <linux/major.h>
20#endif
21
22#include "pathnames.h"
23
24/* functions in login.c */
25void badlogin(char *s);
26void sleepexit(int);
27extern struct hostent hostaddress;
28extern char *hostname;
29
30#ifdef TESTING
31struct hostent hostaddress;
32char *hostname;
33
34void
35badlogin(char *s)
36{
37 printf("badlogin: %s\n", s);
38}
39
40void
41sleepexit(int x)
42{
43 printf("sleepexit %d\n", x);
44 exit(1);
45}
46#endif
47
48#define NAMELEN 128
49
50/* linked list of names */
51struct grplist {
52 struct grplist *next;
53 char name[NAMELEN];
54};
55
56struct grplist *mygroups = NULL;
57
58enum State { StateUsers, StateGroups, StateClasses };
59
60#define CLASSNAMELEN 32
61
62struct ttyclass {
63 struct grplist *first;
64 struct ttyclass *next;
65 char classname[CLASSNAMELEN];
66};
67
68struct ttyclass *ttyclasses = NULL;
69
70static void
71add_group(char *group)
72{
73 struct grplist *ge;
74
75 ge = (struct grplist *)malloc(sizeof(struct grplist));
76
77 /* we can't just bail out at this stage! */
78 if (!ge) {
79 printf("login: memory low, login may fail\n");
80 syslog(LOG_WARNING, "can't malloc grplist");
81 return;
82 }
83
84 ge->next = mygroups;
85 strncpy(ge->name, group, NAMELEN);
86 mygroups = ge;
87}
88
89static int
90am_in_group(char *group)
91{
92 struct grplist *ge;
93
94 for (ge = mygroups; ge; ge = ge->next) {
95 if (strcmp(ge->name, group) == 0) return 1;
96 }
97 return 0;
98}
99
100static void
101find_groups(gid_t defgrp, char *user)
102{
103 struct group *gp;
104 char **p;
105
106 setgrent();
107 while ((gp = getgrent())) {
108 if (gp->gr_gid == defgrp) {
109 add_group(gp->gr_name);
110 } else {
111 for(p = gp->gr_mem; *p; p++) {
112 if (strcmp(user, *p) == 0) {
113 add_group(gp->gr_name);
114 break;
115 }
116 }
117 }
118
119 }
120 endgrent();
121}
122
123static struct ttyclass *
124new_class(char *class)
125{
126 struct ttyclass *tc;
127
128 tc = (struct ttyclass *)malloc(sizeof(struct ttyclass));
129 if (tc == NULL) {
130 printf("login: memory low, login may fail\n");
131 syslog(LOG_WARNING, "can't malloc for ttyclass");
132 return NULL;
133 }
134
135 tc->next = ttyclasses;
136 tc->first = NULL;
137 strncpy(tc->classname, class, CLASSNAMELEN);
138 ttyclasses = tc;
139 return tc;
140}
141
142static void
143add_to_class(struct ttyclass *tc, char *tty)
144{
145 struct grplist *ge;
146
147 if (tc == NULL) return;
148
149 ge = (struct grplist *)malloc(sizeof(struct grplist));
150 if (ge == NULL) {
151 printf("login: memory low, login may fail\n");
152 syslog(LOG_WARNING, "can't malloc for grplist");
153 return;
154 }
155
156 ge->next = tc->first;
157 strncpy(ge->name, tty, NAMELEN);
158 tc->first = ge;
159}
160
161
162/* return true if tty is a pty. Very linux dependent */
163static int
164isapty(tty)
165 char *tty;
166{
167 char devname[100];
168 struct stat stb;
169
170#ifdef linux
171 strcpy(devname, "/dev/");
172 strncat(devname, tty, 80);
173 if((stat(devname, &stb) >= 0)
174 && major(stb.st_rdev) == TTY_MAJOR
175 && minor(stb.st_rdev) >= 192) {
176 return 1;
177 }
178#endif
179 return 0;
180}
181
182/* match the hostname hn against the pattern pat */
183static int
184hnmatch(hn, pat)
185 char *hn;
186 char *pat;
187{
188 int x1, x2, x3, x4, y1, y2, y3, y4;
189 unsigned long p, mask, a;
190 unsigned char *ha;
191 int n, m;
192
193 if ((hn == NULL) && (strcmp(pat, "localhost") == 0)) return 1;
194 if ((hn == NULL) || hn[0] == 0) return 0;
195
196 if (pat[0] >= '0' && pat[0] <= '9') {
197 /* pattern is an IP QUAD address and a mask x.x.x.x/y.y.y.y */
198 sscanf(pat, "%d.%d.%d.%d/%d.%d.%d.%d", &x1, &x2, &x3, &x4,
199 &y1, &y2, &y3, &y4);
200 p = (((unsigned long)x1<<24)+((unsigned long)x2<<16)
201 +((unsigned long)x3<<8)+((unsigned long)x4));
202 mask = (((unsigned long)y1<<24)+((unsigned long)y2<<16)
203 +((unsigned long)y3<<8)+((unsigned long)y4));
204
205 if (!hostaddress.h_addr_list || !hostaddress.h_addr_list[0])
206 return 0;
207
208 ha = (unsigned char *)hostaddress.h_addr_list[0];
209 a = (((unsigned long)ha[0]<<24)+((unsigned long)ha[1]<<16)
210 +((unsigned long)ha[2]<<8)+((unsigned long)ha[3]));
211 return ((p & mask) == (a & mask));
212 } else {
213 /* pattern is a suffix of a FQDN */
214 n = strlen(pat);
215 m = strlen(hn);
216 if (n > m) return 0;
217 return (strcasecmp(pat, hn + m - n) == 0);
218 }
219}
220
221static char *wdays[] = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" };
222
223/* example timespecs:
224
225 mon:tue:wed:8-17
226
227 meaning monday, tuesday or wednesday between 8:00 and 17:59
228
229 4:5:13:fri
230
231 meaning fridays from 4:00 to 5:59 and from 13:00 to 13:59
232*/
233static int
234timeok(struct tm *t, char *spec)
235{
236 char *p, *q;
237 int dayok = 0;
238 int hourok = 0;
239 int h, h2;
240 char *sp;
241
242 sp = spec;
243 while ((p = strsep(&sp, ":"))) {
244 if (*p >= '0' && *p <= '9') {
245 h = atoi(p);
246 if (h == t->tm_hour) hourok = 1;
247 if ((q = strchr(p, '-')) && (q[1] >= '0' && q[1] <= '9')) {
248 h2 = atoi(q+1);
249 if (h <= t->tm_hour && t->tm_hour <= h2) hourok = 1;
250 }
251 } else if (strcasecmp(wdays[t->tm_wday], p) == 0) {
252 dayok = 1;
253 }
254 }
255
256 return (dayok && hourok);
257}
258
259/* return true if tty equals class or is in the class defined by class.
260 Also return true if hostname matches the hostname pattern, class
261 or a pattern in the class named by class. */
262static int
263in_class(char *tty, char *class)
264{
265 struct ttyclass *tc;
266 struct grplist *ge;
267 time_t t;
268 char *p;
269 char timespec[256];
270 struct tm *tm;
271 char *n;
272
273 time(&t);
274 tm = localtime(&t);
275
276 if (class[0] == '[') {
277 if ((p = strchr(class, ']'))) {
278 *p = 0;
279 strcpy(timespec, class+1);
280 *p = ']';
281 if(!timeok(tm, timespec)) return 0;
282 class = p+1;
283 }
284 /* really ought to warn about syntax error */
285 }
286
287 if (strcmp(tty, class) == 0) return 1;
288
289 if ((class[0] == '@') && isapty(tty)
290 && hnmatch(hostname, class+1)) return 1;
291
292 for (tc = ttyclasses; tc; tc = tc->next) {
293 if (strcmp(tc->classname, class) == 0) {
294 for (ge = tc->first; ge; ge = ge->next) {
295
296 n = ge->name;
297 if (n[0] == '[') {
298 if ((p = strchr(n, ']'))) {
299 *p = 0;
300 strcpy(timespec, n+1);
301 *p = ']';
302 if(!timeok(tm, timespec)) continue;
303 n = p+1;
304 }
305 /* really ought to warn about syntax error */
306 }
307
308 if (strcmp(n, tty) == 0) return 1;
309
310 if ((n[0] == '@') && isapty(tty)
311 && hnmatch(hostname, n+1)) return 1;
312 }
313 return 0;
314 }
315 }
316 return 0;
317}
318
319void
320checktty(user, tty, pwd)
321 char *user;
322 char *tty;
323 struct passwd *pwd;
324{
325 FILE *f;
326 char buf[256], defaultbuf[256];
327 char *ptr;
328 enum State state = StateUsers;
329 int found_match = 0;
330
331 /* no /etc/usertty, default to allow access */
332#ifdef TESTING
333 if (!(f = fopen("usertty", "r"))) return;
334#else
335 if (!(f = fopen(_PATH_USERTTY, "r"))) return;
336#endif
337
338 if (pwd == NULL) return; /* misspelled username handled elsewhere */
339
340 find_groups(pwd->pw_gid, user);
341
342 defaultbuf[0] = 0;
343 while(fgets(buf, 255, f)) {
344
345 /* strip comments */
346 for(ptr = buf; ptr < buf + 256; ptr++)
347 if(*ptr == '#') *ptr = 0;
348
349 if (buf[0] == '*') {
350 strncpy(defaultbuf, buf, 256);
351 continue;
352 }
353
354 if (strncmp("GROUPS", buf, 6) == 0) {
355 state = StateGroups;
356 continue;
357 } else if (strncmp("USERS", buf, 5) == 0) {
358 state = StateUsers;
359 continue;
360 } else if (strncmp("CLASSES", buf, 7) == 0) {
361 state = StateClasses;
362 continue;
363 }
364
365 strtok(buf, " \t");
366 if((state == StateUsers && (strncmp(user, buf, 8) == 0))
367 || (state == StateGroups && am_in_group(buf))) {
368 found_match = 1; /* we found a line matching the user */
369 while((ptr = strtok(NULL, "\t\n "))) {
370 if (in_class(tty, ptr)) {
371 fclose(f);
372 return;
373 }
374 }
375 } else if (state == StateClasses) {
376 /* define a new tty/host class */
377 struct ttyclass *tc = new_class(buf);
378
379 while ((ptr = strtok(NULL, "\t\n "))) {
380 add_to_class(tc, ptr);
381 }
382 }
383 }
384 fclose(f);
385
386 /* user is not explicitly mentioned in /etc/usertty, if there was
387 a default rule, use that */
388 if (defaultbuf[0]) {
389 strtok(defaultbuf, " \t");
390 while((ptr = strtok(NULL, "\t\n "))) {
391 if (in_class(tty, ptr)) return;
392 }
393
394 /* there was a default rule, but user didn't match, reject! */
395 printf("Login on %s from %s denied by default.\n", tty, hostname);
396 badlogin(user);
397 sleepexit(1);
398 }
399
400 if (found_match) {
401 /* if we get here, /etc/usertty exists, there's a line
402 matching our username, but it doesn't contain the
403 name of the tty where the user is trying to log in.
404 So deny access! */
405
406 printf("Login on %s from %s denied.\n", tty, hostname);
407 badlogin(user);
408 sleepexit(1);
409 }
410
411 /* users not matched in /etc/usertty are by default allowed access
412 on all tty's */
413}
414
415#ifdef TESTING
416main(int argc, char *argv[])
417{
418 struct passwd *pw;
419
420 pw = getpwnam(argv[1]);
421 checktty(argv[1], argv[2], pw);
422}
423#endif