Commit | Line | Data |
---|---|---|
bca32771 JH |
1 | /* |
2 | * auth.c - PPP authentication and phase control. | |
3 | * | |
4 | * Copyright (c) 1993 The Australian National University. | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms are permitted | |
8 | * provided that the above copyright notice and this paragraph are | |
9 | * duplicated in all such forms and that any documentation, | |
10 | * advertising materials, and other materials related to such | |
11 | * distribution and use acknowledge that the software was developed | |
12 | * by the Australian National University. The name of the University | |
13 | * may not be used to endorse or promote products derived from this | |
14 | * software without specific prior written permission. | |
15 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
16 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
17 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
18 | * | |
19 | * Copyright (c) 1989 Carnegie Mellon University. | |
20 | * All rights reserved. | |
21 | * | |
22 | * Redistribution and use in source and binary forms are permitted | |
23 | * provided that the above copyright notice and this paragraph are | |
24 | * duplicated in all such forms and that any documentation, | |
25 | * advertising materials, and other materials related to such | |
26 | * distribution and use acknowledge that the software was developed | |
27 | * by Carnegie Mellon University. The name of the | |
28 | * University may not be used to endorse or promote products derived | |
29 | * from this software without specific prior written permission. | |
30 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
31 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
32 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
33 | */ | |
34 | ||
35 | #ifndef lint | |
36 | static char rcsid[] = "$Id: auth.c,v 1.2 1993/12/14 05:40:50 paulus Exp $"; | |
37 | #endif | |
38 | ||
39 | #include <stdio.h> | |
40 | #include <stddef.h> | |
41 | #include <syslog.h> | |
42 | #include <pwd.h> | |
43 | #include <string.h> | |
44 | #include <sys/types.h> | |
45 | #include <sys/stat.h> | |
46 | ||
47 | #include <netdb.h> | |
48 | #include <netinet/in.h> | |
49 | #include <arpa/inet.h> | |
50 | ||
51 | #include "ppp.h" | |
52 | #include "pppd.h" | |
53 | #include "fsm.h" | |
54 | #include "lcp.h" | |
55 | #include "upap.h" | |
56 | #include "chap.h" | |
57 | #include "ipcp.h" | |
58 | #include "pathnames.h" | |
59 | ||
60 | #ifdef sparc | |
61 | #include <alloca.h> | |
62 | #ifndef __GNUC__ | |
63 | /* why alloca.h doesn't define what alloca() returns is a mystery */ | |
64 | /* char *alloca __ARGS((int)); */ | |
65 | #endif /*__GNUC__*/ | |
66 | #endif /*sparc*/ | |
67 | ||
68 | /* Used for storing a sequence of words. Usually malloced. */ | |
69 | struct wordlist { | |
70 | struct wordlist *next; | |
71 | char word[1]; | |
72 | }; | |
73 | ||
74 | /* Bits in scan_authfile return value */ | |
75 | #define NONWILD_SERVER 1 | |
76 | #define NONWILD_CLIENT 2 | |
77 | ||
78 | #define ISWILD(word) (word[0] == '*' && word[1] == 0) | |
79 | ||
80 | #define FALSE 0 | |
81 | #define TRUE 1 | |
82 | ||
83 | extern char user[]; | |
84 | extern char passwd[]; | |
85 | extern char devname[]; | |
86 | extern char our_name[]; | |
87 | extern char remote_name[]; | |
88 | extern char hostname[]; | |
89 | extern int uselogin; | |
90 | extern int usehostname; | |
91 | extern int auth_required; | |
92 | ||
93 | /* Records which authentication operations haven't completed yet. */ | |
94 | static int auth_pending[_NPPP]; | |
95 | static int logged_in; | |
96 | static struct wordlist *addresses[_NPPP]; | |
97 | ||
98 | /* Bits in auth_pending[] */ | |
99 | #define UPAP_WITHPEER 1 | |
100 | #define UPAP_PEER 2 | |
101 | #define CHAP_WITHPEER 4 | |
102 | #define CHAP_PEER 8 | |
103 | ||
104 | /* Prototypes */ | |
105 | void check_access __ARGS((FILE *, char *)); | |
106 | ||
107 | static int login __ARGS((char *, char *, char **, int *)); | |
108 | static void logout __ARGS((void)); | |
109 | static int get_upap_passwd __ARGS((void)); | |
110 | static int have_upap_secret __ARGS((void)); | |
111 | static int have_chap_secret __ARGS((char *, char *)); | |
112 | static int scan_authfile __ARGS((FILE *, char *, char *, char *, | |
113 | struct wordlist **, char *)); | |
114 | static void free_wordlist __ARGS((struct wordlist *)); | |
115 | ||
116 | extern char *crypt __ARGS((char *, char *)); | |
117 | ||
118 | /* | |
119 | * An Open on LCP has requested a change from Dead to Establish phase. | |
120 | * Do what's necessary to bring the physical layer up. | |
121 | */ | |
122 | void | |
123 | link_required(unit) | |
124 | int unit; | |
125 | { | |
126 | } | |
127 | ||
128 | /* | |
129 | * LCP has terminated the link; go to the Dead phase and take the | |
130 | * physical layer down. | |
131 | */ | |
132 | void | |
133 | link_terminated(unit) | |
134 | int unit; | |
135 | { | |
136 | if (logged_in) | |
137 | logout(); | |
138 | if (lcp_wantoptions[unit].restart) { | |
139 | lcp_lowerdown(unit); | |
140 | lcp_lowerup(unit); | |
141 | } else | |
142 | EXIT(unit); | |
143 | } | |
144 | ||
145 | /* | |
146 | * The link is established. | |
147 | * Proceed to the Dead, Authenticate or Network phase as appropriate. | |
148 | */ | |
149 | void | |
150 | link_established(unit) | |
151 | int unit; | |
152 | { | |
153 | int auth; | |
154 | lcp_options *wo = &lcp_wantoptions[unit]; | |
155 | lcp_options *go = &lcp_gotoptions[unit]; | |
156 | lcp_options *ho = &lcp_hisoptions[unit]; | |
157 | ||
158 | if (auth_required && !(go->neg_chap || go->neg_upap)) { | |
159 | /* | |
160 | * We wanted the peer to authenticate himself, and he refused: | |
161 | * tell him to go away. | |
162 | */ | |
163 | syslog(LOG_WARNING, "peer refused to authenticate"); | |
164 | lcp_close(unit); | |
165 | return; | |
166 | } | |
167 | ||
168 | auth = 0; | |
169 | if (go->neg_chap) { | |
170 | ChapAuthPeer(unit, our_name, go->chap_mdtype); | |
171 | auth |= CHAP_PEER; | |
172 | } else if (go->neg_upap) { | |
173 | upap_authpeer(unit); | |
174 | auth |= UPAP_PEER; | |
175 | } | |
176 | if (ho->neg_chap) { | |
177 | ChapAuthWithPeer(unit, our_name, ho->chap_mdtype); | |
178 | auth |= CHAP_WITHPEER; | |
179 | } else if (ho->neg_upap) { | |
180 | upap_authwithpeer(unit, user, passwd); | |
181 | auth |= UPAP_WITHPEER; | |
182 | } | |
183 | auth_pending[unit] = auth; | |
184 | ||
185 | if (!auth) | |
186 | ipcp_open(unit); | |
187 | } | |
188 | ||
189 | /* | |
190 | * The peer has failed to authenticate himself using `protocol'. | |
191 | */ | |
192 | void | |
193 | auth_peer_fail(unit, protocol) | |
194 | int unit, protocol; | |
195 | { | |
196 | /* | |
197 | * Authentication failure: take the link down | |
198 | */ | |
199 | lcp_close(unit); | |
200 | } | |
201 | ||
202 | /* | |
203 | * The peer has been successfully authenticated using `protocol'. | |
204 | */ | |
205 | void | |
206 | auth_peer_success(unit, protocol) | |
207 | int unit, protocol; | |
208 | { | |
209 | int bit; | |
210 | ||
211 | switch (protocol) { | |
212 | case CHAP: | |
213 | bit = CHAP_PEER; | |
214 | break; | |
215 | case UPAP: | |
216 | bit = UPAP_PEER; | |
217 | break; | |
218 | default: | |
219 | syslog(LOG_WARNING, "auth_peer_success: unknown protocol %x", | |
220 | protocol); | |
221 | return; | |
222 | } | |
223 | ||
224 | /* | |
225 | * If there is no more authentication still to be done, | |
226 | * proceed to the network phase. | |
227 | */ | |
228 | if ((auth_pending[unit] &= ~bit) == 0) | |
229 | ipcp_open(unit); | |
230 | } | |
231 | ||
232 | /* | |
233 | * We have failed to authenticate ourselves to the peer using `protocol'. | |
234 | */ | |
235 | void | |
236 | auth_withpeer_fail(unit, protocol) | |
237 | int unit, protocol; | |
238 | { | |
239 | /* | |
240 | * We've failed to authenticate ourselves to our peer. | |
241 | * He'll probably take the link down, and there's not much | |
242 | * we can do except wait for that. | |
243 | */ | |
244 | } | |
245 | ||
246 | /* | |
247 | * We have successfully authenticated ourselves with the peer using `protocol'. | |
248 | */ | |
249 | void | |
250 | auth_withpeer_success(unit, protocol) | |
251 | int unit, protocol; | |
252 | { | |
253 | int bit; | |
254 | ||
255 | switch (protocol) { | |
256 | case CHAP: | |
257 | bit = CHAP_WITHPEER; | |
258 | break; | |
259 | case UPAP: | |
260 | bit = UPAP_WITHPEER; | |
261 | break; | |
262 | default: | |
263 | syslog(LOG_WARNING, "auth_peer_success: unknown protocol %x", | |
264 | protocol); | |
265 | } | |
266 | ||
267 | /* | |
268 | * If there is no more authentication still being done, | |
269 | * proceed to the network phase. | |
270 | */ | |
271 | if ((auth_pending[unit] &= ~bit) == 0) | |
272 | ipcp_open(unit); | |
273 | } | |
274 | ||
275 | ||
276 | /* | |
277 | * check_auth_options - called to check authentication options. | |
278 | */ | |
279 | void | |
280 | check_auth_options() | |
281 | { | |
282 | lcp_options *wo = &lcp_wantoptions[0]; | |
283 | lcp_options *ao = &lcp_allowoptions[0]; | |
284 | ||
285 | /* Default our_name to hostname, and user to our_name */ | |
286 | if (our_name[0] == 0 || usehostname) | |
287 | strcpy(our_name, hostname); | |
288 | if (user[0] == 0) | |
289 | strcpy(user, our_name); | |
290 | ||
291 | /* If authentication is required, ask peer for CHAP or PAP. */ | |
292 | if (auth_required && !wo->neg_chap && !wo->neg_upap) { | |
293 | wo->neg_chap = 1; | |
294 | wo->neg_upap = 1; | |
295 | } | |
296 | ||
297 | /* | |
298 | * Check whether we have appropriate secrets to use | |
299 | * to authenticate ourselves and/or the peer. | |
300 | */ | |
301 | if (ao->neg_upap && passwd[0] == 0 && !get_upap_passwd()) | |
302 | ao->neg_upap = 0; | |
303 | if (wo->neg_upap && !uselogin && !have_upap_secret()) | |
304 | wo->neg_upap = 0; | |
305 | if (ao->neg_chap && !have_chap_secret(our_name, remote_name)) | |
306 | ao->neg_chap = 0; | |
307 | if (wo->neg_chap && !have_chap_secret(remote_name, our_name)) | |
308 | wo->neg_chap = 0; | |
309 | ||
310 | if (auth_required && !wo->neg_chap && !wo->neg_upap) { | |
311 | fprintf(stderr, "\ | |
312 | pppd: peer authentication required but no authentication files accessible\n"); | |
313 | exit(1); | |
314 | } | |
315 | ||
316 | } | |
317 | ||
318 | ||
319 | /* | |
320 | * check_passwd - Check the user name and passwd against the PAP secrets | |
321 | * file. If requested, also check against the system password database, | |
322 | * and login the user if OK. | |
323 | * | |
324 | * returns: | |
325 | * UPAP_AUTHNAK: Authentication failed. | |
326 | * UPAP_AUTHACK: Authentication succeeded. | |
327 | * In either case, msg points to an appropriate message. | |
328 | */ | |
329 | int | |
330 | check_passwd(unit, auser, userlen, apasswd, passwdlen, msg, msglen) | |
331 | int unit; | |
332 | char *auser; | |
333 | int userlen; | |
334 | char *apasswd; | |
335 | int passwdlen; | |
336 | char **msg; | |
337 | int *msglen; | |
338 | { | |
339 | int ret; | |
340 | char *filename; | |
341 | FILE *f; | |
342 | struct wordlist *addrs; | |
343 | char passwd[256], user[256]; | |
344 | char secret[MAXWORDLEN]; | |
345 | static int attempts = 0; | |
346 | ||
347 | /* | |
348 | * Make copies of apasswd and auser, then null-terminate them. | |
349 | */ | |
350 | BCOPY(apasswd, passwd, passwdlen); | |
351 | passwd[passwdlen] = '\0'; | |
352 | BCOPY(auser, user, userlen); | |
353 | user[userlen] = '\0'; | |
354 | ||
355 | /* | |
356 | * Open the file of upap secrets and scan for a suitable secret | |
357 | * for authenticating this user. | |
358 | */ | |
359 | filename = _PATH_UPAPFILE; | |
360 | addrs = NULL; | |
361 | ret = UPAP_AUTHACK; | |
362 | f = fopen(filename, "r"); | |
363 | if (f == NULL) { | |
364 | if (!uselogin) { | |
365 | syslog(LOG_ERR, "Can't open upap password file %s: %m", filename); | |
366 | ret = UPAP_AUTHNAK; | |
367 | } | |
368 | ||
369 | } else { | |
370 | check_access(f, filename); | |
371 | if (scan_authfile(f, user, our_name, secret, &addrs, filename) < 0 | |
372 | || (secret[0] != 0 && strcmp(passwd, secret) != 0 | |
373 | && strcmp(crypt(passwd, secret), secret) != 0)) { | |
374 | syslog(LOG_WARNING, "upap authentication failure for %s", user); | |
375 | ret = UPAP_AUTHNAK; | |
376 | } | |
377 | fclose(f); | |
378 | } | |
379 | ||
380 | if (uselogin && ret == UPAP_AUTHACK) { | |
381 | ret = login(user, passwd, msg, msglen); | |
382 | if (ret == UPAP_AUTHNAK) { | |
383 | syslog(LOG_WARNING, "upap login failure for %s", user); | |
384 | } | |
385 | } | |
386 | ||
387 | if (ret == UPAP_AUTHNAK) { | |
388 | *msg = "Login incorrect"; | |
389 | *msglen = strlen(*msg); | |
390 | /* | |
391 | * Frustrate passwd stealer programs. | |
392 | * Allow 10 tries, but start backing off after 3 (stolen from login). | |
393 | * On 10'th, drop the connection. | |
394 | */ | |
395 | if (attempts++ >= 10) { | |
396 | syslog(LOG_WARNING, "%d LOGIN FAILURES ON %s, %s", | |
397 | attempts, devname, user); | |
398 | quit(); | |
399 | } | |
400 | if (attempts > 3) | |
401 | sleep((u_int) (attempts - 3) * 5); | |
402 | if (addrs != NULL) | |
403 | free_wordlist(addrs); | |
404 | ||
405 | } else { | |
406 | attempts = 0; /* Reset count */ | |
407 | *msg = "Login ok"; | |
408 | *msglen = strlen(*msg); | |
409 | if (addresses[unit] != NULL) | |
410 | free_wordlist(addresses[unit]); | |
411 | addresses[unit] = addrs; | |
412 | } | |
413 | ||
414 | return ret; | |
415 | } | |
416 | ||
417 | ||
418 | /* | |
419 | * login - Check the user name and password against the system | |
420 | * password database, and login the user if OK. | |
421 | * | |
422 | * returns: | |
423 | * UPAP_AUTHNAK: Login failed. | |
424 | * UPAP_AUTHACK: Login succeeded. | |
425 | * In either case, msg points to an appropriate message. | |
426 | */ | |
427 | static int | |
428 | login(user, passwd, msg, msglen) | |
429 | char *user; | |
430 | char *passwd; | |
431 | char **msg; | |
432 | int *msglen; | |
433 | { | |
434 | struct passwd *pw; | |
435 | char *epasswd; | |
436 | char *tty; | |
437 | ||
438 | if ((pw = getpwnam(user)) == NULL) { | |
439 | return (UPAP_AUTHNAK); | |
440 | } | |
441 | ||
442 | /* | |
443 | * XXX If no passwd, let them login without one. | |
444 | */ | |
445 | if (pw->pw_passwd == '\0') { | |
446 | return (UPAP_AUTHACK); | |
447 | } | |
448 | ||
449 | epasswd = crypt(passwd, pw->pw_passwd); | |
450 | if (strcmp(epasswd, pw->pw_passwd)) { | |
451 | return (UPAP_AUTHNAK); | |
452 | } | |
453 | ||
454 | syslog(LOG_INFO, "user %s logged in", user); | |
455 | ||
456 | /* | |
457 | * Write a wtmp entry for this user. | |
458 | */ | |
459 | tty = strrchr(devname, '/'); | |
460 | if (tty == NULL) | |
461 | tty = devname; | |
462 | else | |
463 | tty++; | |
464 | logwtmp(tty, user, ""); /* Add wtmp login entry */ | |
465 | logged_in = TRUE; | |
466 | ||
467 | return (UPAP_AUTHACK); | |
468 | } | |
469 | ||
470 | /* | |
471 | * logout - Logout the user. | |
472 | */ | |
473 | static void | |
474 | logout() | |
475 | { | |
476 | char *tty; | |
477 | ||
478 | tty = strrchr(devname, '/'); | |
479 | if (tty == NULL) | |
480 | tty = devname; | |
481 | else | |
482 | tty++; | |
483 | logwtmp(tty, "", ""); /* Wipe out wtmp logout entry */ | |
484 | logged_in = FALSE; | |
485 | } | |
486 | ||
487 | ||
488 | /* | |
489 | * get_upap_passwd - get a password for authenticating ourselves with | |
490 | * our peer using PAP. Returns 1 on success, 0 if no suitable password | |
491 | * could be found. | |
492 | */ | |
493 | static int | |
494 | get_upap_passwd() | |
495 | { | |
496 | char *filename; | |
497 | FILE *f; | |
498 | struct wordlist *addrs; | |
499 | char secret[MAXWORDLEN]; | |
500 | ||
501 | filename = _PATH_UPAPFILE; | |
502 | addrs = NULL; | |
503 | f = fopen(filename, "r"); | |
504 | if (f == NULL) | |
505 | return 0; | |
506 | check_access(f, filename); | |
507 | if (scan_authfile(f, user, remote_name, secret, NULL, filename) < 0) | |
508 | return 0; | |
509 | strncpy(passwd, secret, MAXSECRETLEN); | |
510 | passwd[MAXSECRETLEN-1] = 0; | |
511 | return 1; | |
512 | } | |
513 | ||
514 | ||
515 | /* | |
516 | * have_upap_secret - check whether we have a PAP file with any | |
517 | * secrets that we could possibly use for authenticating the peer. | |
518 | */ | |
519 | static int | |
520 | have_upap_secret() | |
521 | { | |
522 | FILE *f; | |
523 | int ret; | |
524 | char *filename; | |
525 | ||
526 | filename = _PATH_UPAPFILE; | |
527 | f = fopen(filename, "r"); | |
528 | if (f == NULL) | |
529 | return 0; | |
530 | ||
531 | ret = scan_authfile(f, NULL, our_name, NULL, NULL, filename); | |
532 | fclose(f); | |
533 | if (ret < 0) | |
534 | return 0; | |
535 | ||
536 | return 1; | |
537 | } | |
538 | ||
539 | ||
540 | /* | |
541 | * have_chap_secret - check whether we have a CHAP file with a | |
542 | * secret that we could possibly use for authenticating `client' | |
543 | * on `server'. Either can be the null string, meaning we don't | |
544 | * know the identity yet. | |
545 | */ | |
546 | static int | |
547 | have_chap_secret(client, server) | |
548 | char *client; | |
549 | char *server; | |
550 | { | |
551 | FILE *f; | |
552 | int ret; | |
553 | char *filename; | |
554 | ||
555 | filename = _PATH_CHAPFILE; | |
556 | f = fopen(filename, "r"); | |
557 | if (f == NULL) | |
558 | return 0; | |
559 | ||
560 | if (client[0] == 0) | |
561 | client = NULL; | |
562 | else if (server[0] == 0) | |
563 | server = NULL; | |
564 | ||
565 | ret = scan_authfile(f, client, server, NULL, NULL, filename); | |
566 | fclose(f); | |
567 | if (ret < 0) | |
568 | return 0; | |
569 | ||
570 | return 1; | |
571 | } | |
572 | ||
573 | ||
574 | /* | |
575 | * get_secret - open the CHAP secret file and return the secret | |
576 | * for authenticating the given client on the given server. | |
577 | * (We could be either client or server). | |
578 | */ | |
579 | int | |
580 | get_secret(unit, client, server, secret, secret_len, save_addrs) | |
581 | int unit; | |
582 | char *client; | |
583 | char *server; | |
584 | char *secret; | |
585 | int *secret_len; | |
586 | { | |
587 | FILE *f; | |
588 | int ret, len; | |
589 | char *filename; | |
590 | struct wordlist *addrs; | |
591 | char secbuf[MAXWORDLEN]; | |
592 | ||
593 | filename = _PATH_CHAPFILE; | |
594 | addrs = NULL; | |
595 | secbuf[0] = 0; | |
596 | ||
597 | f = fopen(filename, "r"); | |
598 | if (f == NULL) { | |
599 | syslog(LOG_ERR, "Can't open chap secret file %s: %m", filename); | |
600 | return 0; | |
601 | } | |
602 | check_access(f, filename); | |
603 | ||
604 | ret = scan_authfile(f, client, server, secbuf, &addrs, filename); | |
605 | fclose(f); | |
606 | if (ret < 0) | |
607 | return 0; | |
608 | ||
609 | if (save_addrs) { | |
610 | if (addresses[unit] != NULL) | |
611 | free_wordlist(addresses[unit]); | |
612 | addresses[unit] = addrs; | |
613 | } | |
614 | ||
615 | len = strlen(secbuf); | |
616 | if (len > MAXSECRETLEN) { | |
617 | syslog(LOG_ERR, "Secret for %s on %s is too long", client, server); | |
618 | len = MAXSECRETLEN; | |
619 | } | |
620 | BCOPY(secbuf, secret, len); | |
621 | *secret_len = len; | |
622 | ||
623 | return 1; | |
624 | } | |
625 | ||
626 | /* | |
627 | * auth_ip_addr - check whether the peer is authorized to use | |
628 | * a given IP address. Returns 1 if authorized, 0 otherwise. | |
629 | */ | |
630 | int | |
631 | auth_ip_addr(unit, addr) | |
632 | int unit; | |
633 | u_long addr; | |
634 | { | |
635 | u_long a; | |
636 | struct hostent *hp; | |
637 | struct wordlist *addrs; | |
638 | ||
639 | if ((addrs = addresses[unit]) == NULL) | |
640 | return 1; /* no restriction */ | |
641 | ||
642 | for (; addrs != NULL; addrs = addrs->next) { | |
643 | /* "-" means no addresses authorized */ | |
644 | if (strcmp(addrs->word, "-") == 0) | |
645 | break; | |
646 | if ((a = inet_addr(addrs->word)) == -1) { | |
647 | if ((hp = gethostbyname(addrs->word)) == NULL) { | |
648 | syslog(LOG_WARNING, "unknown host %s in auth. address list", | |
649 | addrs->word); | |
650 | continue; | |
651 | } else | |
652 | a = *(u_long *)hp->h_addr; | |
653 | } | |
654 | if (addr == a) | |
655 | return 1; | |
656 | } | |
657 | return 0; /* not in list => can't have it */ | |
658 | } | |
659 | ||
660 | /* | |
661 | * check_access - complain if a secret file has too-liberal permissions. | |
662 | */ | |
663 | void | |
664 | check_access(f, filename) | |
665 | FILE *f; | |
666 | char *filename; | |
667 | { | |
668 | struct stat sbuf; | |
669 | ||
670 | if (fstat(fileno(f), &sbuf) < 0) { | |
671 | syslog(LOG_WARNING, "cannot stat secret file %s: %m", filename); | |
672 | } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) { | |
673 | syslog(LOG_WARNING, "Warning - secret file %s has world and/or group access", filename); | |
674 | } | |
675 | } | |
676 | ||
677 | ||
678 | /* | |
679 | * scan_authfile - Scan an authorization file for a secret suitable | |
680 | * for authenticating `client' on `server'. The return value is -1 | |
681 | * if no secret is found, otherwise >= 0. The return value has | |
682 | * NONWILD_CLIENT set if the secret didn't have "*" for the client, and | |
683 | * NONWILD_SERVER set if the secret didn't have "*" for the server. | |
684 | * Any following words on the line (i.e. address authorization | |
685 | * info) are placed in a wordlist and returned in *addrs. | |
686 | */ | |
687 | static int | |
688 | scan_authfile(f, client, server, secret, addrs, filename) | |
689 | FILE *f; | |
690 | char *client; | |
691 | char *server; | |
692 | char *secret; | |
693 | struct wordlist **addrs; | |
694 | char *filename; | |
695 | { | |
696 | int newline, xxx; | |
697 | int got_flag, best_flag; | |
698 | FILE *sf; | |
699 | struct wordlist *ap, *addr_list, *addr_last; | |
700 | char word[MAXWORDLEN]; | |
701 | char atfile[MAXWORDLEN]; | |
702 | ||
703 | if (addrs != NULL) | |
704 | *addrs = NULL; | |
705 | addr_list = NULL; | |
706 | if (!getword(f, word, &newline, filename)) | |
707 | return -1; /* file is empty??? */ | |
708 | newline = 1; | |
709 | best_flag = -1; | |
710 | for (;;) { | |
711 | /* | |
712 | * Skip until we find a word at the start of a line. | |
713 | */ | |
714 | while (!newline && getword(f, word, &newline, filename)) | |
715 | ; | |
716 | if (!newline) | |
717 | break; /* got to end of file */ | |
718 | ||
719 | /* | |
720 | * Got a client - check if it's a match or a wildcard. | |
721 | */ | |
722 | got_flag = 0; | |
723 | if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) { | |
724 | newline = 0; | |
725 | continue; | |
726 | } | |
727 | if (!ISWILD(word)) | |
728 | got_flag = NONWILD_CLIENT; | |
729 | ||
730 | /* | |
731 | * Now get a server and check if it matches. | |
732 | */ | |
733 | if (!getword(f, word, &newline, filename)) | |
734 | break; | |
735 | if (newline) | |
736 | continue; | |
737 | if (server != NULL && strcmp(word, server) != 0 && !ISWILD(word)) | |
738 | continue; | |
739 | if (!ISWILD(word)) | |
740 | got_flag |= NONWILD_SERVER; | |
741 | ||
742 | /* | |
743 | * Got some sort of a match - see if it's better than what | |
744 | * we have already. | |
745 | */ | |
746 | if (got_flag <= best_flag) | |
747 | continue; | |
748 | ||
749 | /* | |
750 | * Get the secret. | |
751 | */ | |
752 | if (!getword(f, word, &newline, filename)) | |
753 | break; | |
754 | if (newline) | |
755 | continue; | |
756 | ||
757 | /* | |
758 | * Special syntax: @filename means read secret from file. | |
759 | */ | |
760 | if (word[0] == '@') { | |
761 | strcpy(atfile, word+1); | |
762 | if ((sf = fopen(atfile, "r")) == NULL) { | |
763 | syslog(LOG_WARNING, "can't open indirect secret file %s", | |
764 | atfile); | |
765 | continue; | |
766 | } | |
767 | check_access(sf, atfile); | |
768 | if (!getword(sf, word, &xxx, atfile)) { | |
769 | syslog(LOG_WARNING, "no secret in indirect secret file %s", | |
770 | atfile); | |
771 | fclose(sf); | |
772 | continue; | |
773 | } | |
774 | fclose(sf); | |
775 | } | |
776 | if (secret != NULL) | |
777 | strcpy(secret, word); | |
778 | ||
779 | best_flag = got_flag; | |
780 | ||
781 | /* | |
782 | * Now read address authorization info and make a wordlist. | |
783 | */ | |
784 | if (addr_list) | |
785 | free_wordlist(addr_list); | |
786 | addr_list = NULL; | |
787 | for (;;) { | |
788 | if (!getword(f, word, &newline, filename) || newline) | |
789 | break; | |
790 | ap = (struct wordlist *) malloc(sizeof(struct wordlist) | |
791 | + strlen(word)); | |
792 | if (ap == NULL) | |
793 | novm("authorized addresses"); | |
794 | ap->next = NULL; | |
795 | strcpy(ap->word, word); | |
796 | if (addr_list == NULL) | |
797 | addr_list = ap; | |
798 | else | |
799 | addr_last->next = ap; | |
800 | addr_last = ap; | |
801 | } | |
802 | if (!newline) | |
803 | break; | |
804 | } | |
805 | ||
806 | if (addrs != NULL) | |
807 | *addrs = addr_list; | |
808 | else if (addr_list != NULL) | |
809 | free_wordlist(addr_list); | |
810 | ||
811 | return best_flag; | |
812 | } | |
813 | ||
814 | /* | |
815 | * free_wordlist - release memory allocated for a wordlist. | |
816 | */ | |
817 | static void | |
818 | free_wordlist(wp) | |
819 | struct wordlist *wp; | |
820 | { | |
821 | struct wordlist *next; | |
822 | ||
823 | while (wp != NULL) { | |
824 | next = wp->next; | |
825 | free(wp); | |
826 | wp = next; | |
827 | } | |
828 | } |