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