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