d9898ee8 |
1 | /* |
ac40fd9e |
2 | ** Copyright 2000-2008 Double Precision, Inc. See COPYING for |
d9898ee8 |
3 | ** distribution information. |
4 | */ |
5 | |
6 | #include "courier_auth_config.h" |
7 | #include <sys/types.h> |
8 | #include <sys/stat.h> |
9 | #include <sys/socket.h> |
10 | #include <sys/un.h> |
11 | #include <sys/time.h> |
12 | #include <sys/wait.h> |
13 | #include <unistd.h> |
14 | #include <stdlib.h> |
15 | #include <stdio.h> |
16 | #include <errno.h> |
17 | #include <signal.h> |
18 | #include <fcntl.h> |
19 | #include <ctype.h> |
20 | #include "numlib/numlib.h" |
21 | #include "liblock/config.h" |
22 | #include "liblock/liblock.h" |
23 | #include "auth.h" |
24 | #include "authdaemonrc.h" |
25 | #include "courierauthdebug.h" |
26 | #include "pkglibdir.h" |
27 | #include "authstaticlist.h" |
ac40fd9e |
28 | #include "libhmac/hmac.h" |
29 | #include "cramlib.h" |
d9898ee8 |
30 | #include <ltdl.h> |
31 | |
ac40fd9e |
32 | static const char rcsid[]="$Id: authdaemond.c,v 1.34 2008/07/10 02:43:55 mrsam Exp $"; |
d9898ee8 |
33 | |
34 | #ifndef SOMAXCONN |
35 | #define SOMAXCONN 5 |
36 | #endif |
37 | |
d9898ee8 |
38 | #include "authstaticlist.h" |
39 | |
40 | static unsigned ndaemons; |
41 | |
42 | struct authstaticinfolist { |
43 | struct authstaticinfolist *next; |
44 | struct authstaticinfo *info; |
45 | lt_dlhandle h; |
46 | }; |
47 | |
48 | static struct authstaticinfolist *modulelist=NULL; |
49 | |
50 | static int mksocket() |
51 | { |
52 | int fd=socket(PF_UNIX, SOCK_STREAM, 0); |
53 | struct sockaddr_un skun; |
54 | |
55 | if (fd < 0) return (-1); |
56 | skun.sun_family=AF_UNIX; |
57 | strcpy(skun.sun_path, AUTHDAEMONSOCK); |
58 | strcat(skun.sun_path, ".tmp"); |
59 | unlink(skun.sun_path); |
60 | if (bind(fd, (const struct sockaddr *)&skun, sizeof(skun)) || |
61 | listen(fd, SOMAXCONN) || |
62 | chmod(skun.sun_path, 0777) || |
63 | rename(skun.sun_path, AUTHDAEMONSOCK) || |
64 | fcntl(fd, F_SETFL, O_NONBLOCK) < 0) |
65 | { |
66 | perror(AUTHDAEMONSOCK); |
67 | close(fd); |
68 | return (-1); |
69 | } |
70 | return (fd); |
71 | } |
72 | |
73 | |
74 | static int initmodules(const char *p) |
75 | { |
76 | char buf[100]; |
77 | char buf2[100]; |
78 | struct authstaticinfolist **modptr= &modulelist; |
79 | struct authstaticinfolist *m; |
80 | |
81 | if (ndaemons <= 0) |
82 | { |
83 | ndaemons=1; |
84 | |
85 | fprintf(stderr, "ERR: Configuration error - missing 'daemons' setting, using %u\n", |
86 | ndaemons); |
87 | } |
88 | |
89 | while ((m=modulelist) != NULL) |
90 | { |
91 | modulelist=m->next; |
92 | fprintf(stderr, "INFO: Uninstalling %s\n", |
93 | m->info->auth_name); |
94 | lt_dlclose(m->h); |
95 | free(m); |
96 | } |
97 | |
98 | while (p && *p) |
99 | { |
100 | size_t i; |
101 | lt_dlhandle h; |
102 | lt_ptr pt; |
103 | struct authstaticinfo *a; |
104 | |
105 | if (isspace((int)(unsigned char)*p)) |
106 | { |
107 | ++p; |
108 | continue; |
109 | } |
110 | |
111 | for (i=0; p[i] && !isspace((int)(unsigned char)p[i]); ++i) |
112 | ; |
113 | |
114 | strcpy(buf, "lib"); |
115 | strncat(buf, p, i>40 ? 40:i); |
116 | |
117 | fprintf(stderr, "INFO: Installing %s\n", buf); |
118 | p += i; |
119 | h=lt_dlopenext(buf); |
120 | |
121 | if (h == NULL) |
122 | { |
123 | fprintf(stderr, "INFO: %s\n", lt_dlerror()); |
124 | continue; |
125 | } |
126 | |
127 | sprintf(buf2, "courier_%s_init", buf+3); |
128 | |
129 | pt=lt_dlsym(h, buf2); |
130 | if (pt == NULL) |
131 | { |
132 | fprintf(stderr, |
133 | "ERR: Can't locate init function %s.\n", |
134 | buf2); |
135 | fprintf(stderr, "ERR: %s\n", lt_dlerror()); |
136 | continue; |
137 | } |
138 | |
139 | a= (*(struct authstaticinfo *(*)(void))pt)(); |
140 | |
141 | if ((m=malloc(sizeof(*modulelist))) == NULL) |
142 | { |
143 | perror("ERR"); |
144 | lt_dlclose(h); |
145 | continue; |
146 | } |
147 | *modptr=m; |
148 | m->next=NULL; |
149 | m->info=a; |
150 | m->h=h; |
151 | modptr= &m->next; |
152 | fprintf(stderr, "INFO: Installation complete: %s\n", |
153 | a->auth_name); |
154 | } |
155 | return (0); |
156 | } |
157 | |
158 | static int readconfig() |
159 | { |
160 | char buf[BUFSIZ]; |
161 | FILE *fp; |
162 | char *modlist=0; |
163 | unsigned daemons=0; |
164 | |
165 | if ((fp=fopen(AUTHDAEMONRC, "r")) == NULL) |
166 | { |
167 | perror(AUTHDAEMONRC); |
168 | return (-1); |
169 | } |
170 | |
171 | while (fgets(buf, sizeof(buf), fp)) |
172 | { |
173 | char *p=strchr(buf, '\n'), *q; |
174 | |
175 | if (!p) |
176 | { |
177 | int c; |
178 | |
179 | while ((c=getc(fp)) >= 0 && c != '\n') |
180 | ; |
181 | } |
182 | else *p=0; |
183 | if ((p=strchr(buf, '#')) != 0) *p=0; |
184 | |
185 | for (p=buf; *p; p++) |
186 | if (!isspace((int)(unsigned char)*p)) |
187 | break; |
188 | if (*p == 0) continue; |
189 | |
190 | if ((p=strchr(buf, '=')) == 0) |
191 | { |
192 | fprintf(stderr, "ERR: Bad line in %s: %s\n", |
193 | AUTHDAEMONRC, buf); |
194 | fclose(fp); |
195 | if (modlist) |
196 | free(modlist); |
197 | return (-1); |
198 | } |
199 | *p++=0; |
200 | while (*p && isspace((int)(unsigned char)*p)) |
201 | ++p; |
202 | if (*p == '"') |
203 | { |
204 | ++p; |
205 | q=strchr(p, '"'); |
206 | if (q) *q=0; |
207 | } |
208 | if (strcmp(buf, "authmodulelist") == 0) |
209 | { |
210 | if (modlist) |
211 | free(modlist); |
212 | modlist=strdup(p); |
213 | if (!modlist) |
214 | { |
215 | perror("malloc"); |
216 | fclose(fp); |
217 | return (-1); |
218 | } |
219 | continue; |
220 | } |
221 | if (strcmp(buf, "daemons") == 0) |
222 | { |
223 | daemons=atoi(p); |
224 | continue; |
225 | } |
226 | } |
227 | fclose(fp); |
228 | |
229 | fprintf(stderr, "INFO: modules=\"%s\", daemons=%u\n", |
230 | modlist ? modlist:"(none)", |
231 | daemons); |
232 | ndaemons=daemons; |
233 | return (initmodules(modlist)); |
234 | } |
235 | |
236 | static char buf[BUFSIZ]; |
237 | static char *readptr; |
238 | static int readleft; |
239 | static char *writeptr; |
240 | static int writeleft; |
241 | |
242 | static int getauthc(int fd) |
243 | { |
244 | fd_set fds; |
245 | struct timeval tv; |
246 | |
247 | if (readleft--) |
248 | return ( (int)(unsigned char)*readptr++ ); |
249 | |
250 | readleft=0; |
251 | FD_ZERO(&fds); |
252 | FD_SET(fd, &fds); |
253 | tv.tv_sec=10; |
254 | tv.tv_usec=0; |
255 | if (select(fd+1, &fds, 0, 0, &tv) <= 0 || |
256 | !FD_ISSET(fd, &fds)) |
257 | return (EOF); |
258 | readleft=read(fd, buf, sizeof(buf)); |
259 | readptr=buf; |
260 | if (readleft <= 0) |
261 | { |
262 | readleft=0; |
263 | return (EOF); |
264 | } |
265 | --readleft; |
266 | return ( (int)(unsigned char)*readptr++ ); |
267 | } |
268 | |
269 | static int writeauth(int fd, const char *p, unsigned pl) |
270 | { |
271 | fd_set fds; |
272 | struct timeval tv; |
273 | |
274 | while (pl) |
275 | { |
276 | int n; |
277 | |
278 | FD_ZERO(&fds); |
279 | FD_SET(fd, &fds); |
280 | tv.tv_sec=30; |
281 | tv.tv_usec=0; |
282 | if (select(fd+1, 0, &fds, 0, &tv) <= 0 || |
283 | !FD_ISSET(fd, &fds)) |
284 | return (-1); |
285 | n=write(fd, p, pl); |
286 | if (n <= 0) return (-1); |
287 | p += n; |
288 | pl -= n; |
289 | } |
290 | return (0); |
291 | } |
292 | |
293 | static int writeauthflush(int fd) |
294 | { |
295 | if (writeptr > buf) |
296 | { |
297 | if (writeauth(fd, buf, writeptr - buf)) |
298 | return (-1); |
299 | } |
300 | writeptr=buf; |
301 | writeleft=sizeof(buf); |
302 | return (0); |
303 | } |
304 | |
305 | static int writeauthbuf(int fd, const char *p, unsigned pl) |
306 | { |
307 | unsigned n; |
308 | |
309 | while (pl) |
310 | { |
311 | if (pl < writeleft) |
312 | { |
313 | memcpy(writeptr, p, pl); |
314 | writeptr += pl; |
315 | writeleft -= pl; |
316 | return (0); |
317 | } |
318 | |
319 | if (writeauthflush(fd)) return (-1); |
320 | |
321 | n=pl; |
322 | if (n > writeleft) n=writeleft; |
323 | memcpy(writeptr, p, n); |
324 | p += n; |
325 | writeptr += n; |
326 | writeleft -= n; |
327 | pl -= n; |
328 | } |
329 | return (0); |
330 | } |
331 | |
332 | static int writeenvval(int fd, const char *env, const char *val) |
333 | { |
334 | if (writeauthbuf(fd, env, strlen(env)) || |
335 | writeauthbuf(fd, "=", 1) || |
336 | writeauthbuf(fd, val, strlen(val)) || |
337 | writeauthbuf(fd, "\n", 1)) |
338 | return (-1); |
339 | return (0); |
340 | } |
341 | |
342 | static const char *findopt(const char *options, const char *keyword) |
343 | { |
344 | size_t keyword_l=strlen(keyword); |
345 | |
346 | while (options) |
347 | { |
348 | if (strncmp(options, keyword, keyword_l) == 0) |
349 | { |
350 | switch (options[keyword_l]) |
351 | { |
352 | case '=': |
353 | return options + keyword_l + 1; |
354 | case ',': case '\0': |
355 | return options + keyword_l; |
356 | } |
357 | } |
358 | options=strchr(options, ','); |
359 | if (options) |
360 | ++options; |
361 | } |
362 | return NULL; |
363 | } |
364 | |
365 | /* Returns a malloc'd string containing the merge of the options string |
366 | and any default options which apply, or NULL if no options at all */ |
367 | static char *mergeoptions(const char *options) |
368 | { |
369 | char *defoptions = getenv("DEFAULTOPTIONS"); |
370 | char *p; |
371 | |
372 | if (options && *options && defoptions && *defoptions) |
373 | { |
374 | char *result = malloc(strlen(options) + |
375 | strlen(defoptions) + 2); |
376 | if (!result) |
377 | { |
378 | perror("malloc"); |
379 | return NULL; |
380 | } |
381 | strcpy(result, options); |
382 | |
383 | defoptions = strdup(defoptions); |
384 | if (!defoptions) |
385 | { |
386 | perror("malloc"); |
387 | free(result); |
388 | return NULL; |
389 | } |
390 | |
391 | for (p = strtok(defoptions, ","); p; p = strtok(0, ",")) |
392 | { |
393 | char *q = strchr(p, '='); |
394 | if (q) *q = '\0'; |
395 | if (findopt(result, p)) continue; |
396 | if (q) *q = '='; |
397 | strcat(result, ","); |
398 | strcat(result, p); |
399 | } |
400 | free(defoptions); |
401 | return result; |
402 | } |
403 | else if (options && *options) |
404 | { |
405 | return strdup(options); |
406 | } |
407 | else if (defoptions && *defoptions) |
408 | { |
409 | return strdup(defoptions); |
410 | } |
411 | else |
412 | return 0; |
413 | } |
414 | |
415 | static int printauth(struct authinfo *authinfo, void *vp) |
416 | { |
417 | int fd= *(int *)vp; |
418 | char buf2[NUMBUFSIZE]; |
419 | char *fullopt; |
420 | |
421 | writeptr=buf; |
422 | writeleft=sizeof(buf); |
423 | |
424 | courier_authdebug_authinfo("Authenticated: ", authinfo, |
425 | authinfo->clearpasswd, |
426 | authinfo->passwd); |
427 | |
428 | if (authinfo->sysusername) |
429 | if (writeenvval(fd, "USERNAME", authinfo->sysusername)) |
430 | return (1); |
431 | if (authinfo->sysuserid) |
432 | if (writeenvval(fd, "UID", libmail_str_uid_t(*authinfo->sysuserid, |
433 | buf2))) |
434 | return (1); |
435 | if (writeenvval(fd, "GID", libmail_str_uid_t(authinfo->sysgroupid, buf2))) |
436 | return (1); |
437 | |
438 | if (writeenvval(fd, "HOME", authinfo->homedir)) |
439 | return (1); |
440 | if (authinfo->address) |
441 | if (writeenvval(fd, "ADDRESS", authinfo->address)) |
442 | return (1); |
443 | if (authinfo->fullname) |
444 | { |
445 | /* |
446 | * Only the first field of the comma-seperated GECOS field is the |
447 | * full username. |
448 | */ |
449 | char *fullname; |
450 | char *p; |
451 | int retval; |
452 | |
453 | fullname=strdup(authinfo->fullname); |
454 | if(fullname == NULL) |
455 | { |
456 | perror("strdup"); |
457 | return (1); |
458 | } |
459 | |
460 | p = fullname; |
461 | while (*p != ',' && *p != '\0') |
462 | p++; |
463 | *p=0; |
464 | retval = writeenvval(fd, "NAME", fullname); |
465 | free(fullname); |
466 | if(retval) |
467 | return (1); |
468 | } |
469 | if (authinfo->maildir) |
470 | if (writeenvval(fd, "MAILDIR", authinfo->maildir)) |
471 | return (1); |
472 | if (authinfo->quota) |
473 | if (writeenvval(fd, "QUOTA", authinfo->quota)) |
474 | return (1); |
475 | if (authinfo->passwd) |
476 | if (writeenvval(fd, "PASSWD", authinfo->passwd)) |
477 | return (1); |
478 | if (authinfo->clearpasswd) |
479 | if (writeenvval(fd, "PASSWD2", authinfo->clearpasswd)) |
480 | return (1); |
481 | fullopt = mergeoptions(authinfo->options); |
482 | if (fullopt) |
483 | { |
484 | int rc = writeenvval(fd, "OPTIONS", fullopt); |
485 | free(fullopt); |
486 | if (rc) |
487 | return (1); |
488 | } |
489 | if (writeauthbuf(fd, ".\n", 2) || writeauthflush(fd)) |
490 | return (1); |
491 | return (0); |
492 | } |
493 | |
494 | static void pre(int fd, char *prebuf) |
495 | { |
496 | char *p=strchr(prebuf, ' '); |
497 | char *service; |
498 | struct authstaticinfolist *l; |
499 | |
500 | if (!p) return; |
501 | *p++=0; |
502 | while (*p == ' ') ++p; |
503 | service=p; |
504 | p=strchr(p, ' '); |
505 | if (!p) return; |
506 | *p++=0; |
507 | while (*p == ' ') ++p; |
508 | |
509 | DPRINTF("received userid lookup request: %s", p); |
510 | |
511 | for (l=modulelist; l; l=l->next) |
512 | { |
513 | struct authstaticinfo *auth=l->info; |
514 | const char *modname = auth->auth_name; |
515 | int rc; |
516 | |
517 | if (strcmp(prebuf, ".") && strcmp(prebuf, modname)) |
518 | continue; |
519 | |
520 | DPRINTF("%s: trying this module", modname); |
521 | |
522 | |
523 | rc=(*auth->auth_prefunc)(p, service, |
524 | &printauth, &fd); |
525 | |
526 | if (rc == 0) |
527 | return; |
528 | |
529 | if (rc > 0) |
530 | { |
531 | DPRINTF("%s: TEMPFAIL - no more modules will be tried", |
532 | modname); |
533 | return; /* Temporary error */ |
534 | } |
535 | DPRINTF("%s: REJECT - try next module", modname); |
536 | } |
537 | writeauth(fd, "FAIL\n", 5); |
538 | DPRINTF("FAIL, all modules rejected"); |
539 | } |
540 | |
541 | struct enumerate_info { |
542 | int fd; |
543 | char *buf_ptr; |
544 | size_t buf_left; |
545 | char buffer[BUFSIZ]; |
546 | int enumerate_ok; |
547 | }; |
548 | |
549 | static void enumflush(struct enumerate_info *ei) |
550 | { |
551 | if (ei->buf_ptr > ei->buffer) |
552 | writeauth(ei->fd, ei->buffer, ei->buf_ptr - ei->buffer); |
553 | ei->buf_ptr=ei->buffer; |
554 | ei->buf_left=sizeof(ei->buffer); |
555 | } |
556 | |
557 | static void enumwrite(struct enumerate_info *ei, const char *s) |
558 | { |
559 | while (s && *s) |
560 | { |
561 | size_t l; |
562 | |
563 | if (ei->buf_left == 0) |
564 | enumflush(ei); |
565 | |
566 | l=strlen(s); |
567 | if (l > ei->buf_left) |
568 | l=ei->buf_left; |
569 | memcpy(ei->buf_ptr, s, l); |
570 | ei->buf_ptr += l; |
571 | ei->buf_left -= l; |
572 | s += l; |
573 | } |
574 | } |
575 | |
576 | static void enum_cb(const char *name, |
577 | uid_t uid, |
578 | gid_t gid, |
579 | const char *homedir, |
580 | const char *maildir, |
581 | const char *options, |
582 | void *void_arg) |
583 | { |
584 | struct enumerate_info *ei=(struct enumerate_info *)void_arg; |
585 | char buf[NUMBUFSIZE]; |
586 | char *fullopt; |
587 | |
588 | if (name == NULL) |
589 | { |
590 | ei->enumerate_ok=1; |
591 | return; |
592 | } |
593 | |
594 | enumwrite(ei, name); |
595 | enumwrite(ei, "\t"); |
596 | enumwrite(ei, libmail_str_uid_t(uid, buf)); |
597 | enumwrite(ei, "\t"); |
598 | enumwrite(ei, libmail_str_gid_t(gid, buf)); |
599 | enumwrite(ei, "\t"); |
600 | enumwrite(ei, homedir); |
601 | enumwrite(ei, "\t"); |
602 | enumwrite(ei, maildir ? maildir : ""); |
603 | enumwrite(ei, "\t"); |
604 | fullopt = mergeoptions(options); |
605 | if (fullopt) |
606 | { |
607 | enumwrite(ei, fullopt); |
608 | free (fullopt); |
609 | } |
610 | enumwrite(ei, "\n"); |
611 | } |
612 | |
613 | static void enumerate(int fd) |
614 | { |
615 | struct enumerate_info ei; |
616 | struct authstaticinfolist *l; |
617 | |
618 | ei.fd=fd; |
619 | ei.buf_left=0; |
620 | |
621 | for (l=modulelist; l; l=l->next) |
622 | { |
623 | struct authstaticinfo *auth=l->info; |
624 | |
625 | if (auth->auth_enumerate == NULL) |
626 | continue; |
627 | |
628 | enumwrite(&ei, "# "); |
629 | enumwrite(&ei, auth->auth_name); |
630 | enumwrite(&ei, "\n\n"); |
631 | ei.enumerate_ok=0; |
632 | (*auth->auth_enumerate)(enum_cb, &ei); |
633 | if (!ei.enumerate_ok) |
634 | { |
635 | enumflush(&ei); |
636 | DPRINTF("enumeration terminated prematurely in module %s", |
637 | auth->auth_name); |
638 | return; |
639 | } |
640 | } |
641 | enumwrite(&ei, ".\n"); |
642 | enumflush(&ei); |
643 | } |
644 | |
645 | static void dopasswd(int, const char *, const char *, const char *, |
646 | const char *); |
647 | |
648 | static void passwd(int fd, char *prebuf) |
649 | { |
650 | char *p; |
651 | const char *service; |
652 | const char *userid; |
653 | const char *opwd; |
654 | const char *npwd; |
655 | |
656 | for (p=prebuf; *p; p++) |
657 | { |
658 | if (*p == '\t') |
659 | continue; |
660 | if ((int)(unsigned char)*p < ' ') |
661 | { |
662 | writeauth(fd, "FAIL\n", 5); |
663 | return; |
664 | } |
665 | } |
666 | |
667 | service=prebuf; |
668 | |
669 | if ((p=strchr(service, '\t')) != 0) |
670 | { |
671 | *p++=0; |
672 | userid=p; |
673 | if ((p=strchr(p, '\t')) != 0) |
674 | { |
675 | *p++=0; |
676 | opwd=p; |
677 | if ((p=strchr(p, '\t')) != 0) |
678 | { |
679 | *p++=0; |
680 | npwd=p; |
681 | if ((p=strchr(p, '\t')) != 0) |
682 | *p=0; |
683 | dopasswd(fd, service, userid, opwd, npwd); |
684 | return; |
685 | } |
686 | } |
687 | } |
688 | } |
689 | |
690 | static void dopasswd(int fd, |
691 | const char *service, |
692 | const char *userid, |
693 | const char *opwd, |
694 | const char *npwd) |
695 | { |
696 | struct authstaticinfolist *l; |
697 | |
698 | |
699 | for (l=modulelist; l; l=l->next) |
700 | { |
701 | struct authstaticinfo *auth=l->info; |
702 | int rc; |
703 | |
704 | int (*f)(const char *, const char *, const char *, |
705 | const char *)= |
706 | auth->auth_changepwd; |
707 | |
708 | if (!f) |
709 | continue; |
710 | |
711 | rc= (*f)(service, userid, opwd, npwd); |
712 | |
713 | if (rc == 0) |
714 | { |
715 | writeauth(fd, "OK\n", 3); |
716 | return; |
717 | } |
718 | if (rc > 0) |
719 | break; |
720 | } |
721 | writeauth(fd, "FAIL\n", 5); |
722 | } |
723 | |
724 | static void auth(int fd, char *p) |
725 | { |
726 | char *service; |
727 | char *authtype; |
728 | char *pp; |
729 | struct authstaticinfolist *l; |
730 | |
731 | service=p; |
732 | if ((p=strchr(p, '\n')) == 0) return; |
733 | *p++=0; |
734 | authtype=p; |
735 | if ((p=strchr(p, '\n')) == 0) return; |
736 | *p++=0; |
737 | |
738 | pp=malloc(strlen(p)+1); |
739 | if (!pp) |
740 | { |
741 | perror("CRIT: malloc() failed"); |
742 | return; |
743 | } |
744 | |
745 | DPRINTF("received auth request, service=%s, authtype=%s", service, authtype); |
746 | for (l=modulelist; l; l=l->next) |
747 | { |
748 | struct authstaticinfo *auth=l->info; |
749 | const char *modname = auth->auth_name; |
750 | int rc; |
751 | |
752 | DPRINTF("%s: trying this module", modname); |
753 | |
754 | rc=(*auth->auth_func)(service, authtype, |
755 | strcpy(pp, p), |
756 | &printauth, &fd); |
757 | |
758 | if (rc == 0) |
759 | { |
760 | free(pp); |
761 | return; |
762 | } |
763 | |
764 | if (rc > 0) |
765 | { |
766 | DPRINTF("%s: TEMPFAIL - no more modules will be tried", modname); |
767 | free(pp); |
768 | return; /* Temporary error */ |
769 | } |
770 | DPRINTF("%s: REJECT - try next module", modname); |
771 | } |
772 | DPRINTF("FAIL, all modules rejected"); |
773 | writeauth(fd, "FAIL\n", 5); |
774 | free(pp); |
775 | } |
776 | |
777 | static void idlefunc() |
778 | { |
779 | struct authstaticinfolist *l; |
780 | |
781 | for (l=modulelist; l; l=l->next) |
782 | { |
783 | struct authstaticinfo *auth=l->info; |
784 | |
785 | if (auth->auth_idle) |
786 | (*auth->auth_idle)(); |
787 | } |
788 | } |
789 | |
790 | static void doauth(int fd) |
791 | { |
792 | char buf[BUFSIZ]; |
793 | int i, ch; |
794 | char *p; |
795 | |
796 | readleft=0; |
797 | for (i=0; (ch=getauthc(fd)) != '\n'; i++) |
798 | { |
799 | if (ch < 0 || i >= sizeof(buf)-2) |
800 | return; |
801 | buf[i]=ch; |
802 | } |
803 | buf[i]=0; |
804 | |
805 | for (p=buf; *p; p++) |
806 | { |
807 | if (*p == ' ') |
808 | { |
809 | *p++=0; |
810 | while (*p == ' ') ++p; |
811 | break; |
812 | } |
813 | } |
814 | |
815 | if (strcmp(buf, "PRE") == 0) |
816 | { |
817 | pre(fd, p); |
818 | return; |
819 | } |
820 | |
821 | if (strcmp(buf, "PASSWD") == 0) |
822 | { |
823 | passwd(fd, p); |
824 | return; |
825 | } |
826 | |
827 | if (strcmp(buf, "AUTH") == 0) |
828 | { |
829 | int j; |
830 | |
831 | i=atoi(p); |
832 | if (i < 0 || i >= sizeof(buf)) return; |
833 | for (j=0; j<i; j++) |
834 | { |
835 | ch=getauthc(fd); |
836 | if (ch < 0) return; |
837 | buf[j]=ch; |
838 | } |
839 | buf[j]=0; |
840 | auth(fd, buf); |
841 | } |
842 | |
843 | if (strcmp(buf, "ENUMERATE") == 0) |
844 | { |
845 | enumerate(fd); |
846 | } |
847 | } |
848 | |
849 | static int sighup_pipe= -1; |
850 | |
851 | static RETSIGTYPE sighup(int n) |
852 | { |
853 | if (sighup_pipe >= 0) |
854 | { |
855 | close(sighup_pipe); |
856 | sighup_pipe= -1; |
857 | } |
858 | signal(SIGHUP, sighup); |
859 | #if RETSIGTYPE != void |
860 | return (1); |
861 | #endif |
862 | } |
863 | |
864 | static int sigterm_received=0; |
865 | |
866 | static RETSIGTYPE sigterm(int n) |
867 | { |
868 | sigterm_received=1; |
869 | if (sighup_pipe >= 0) |
870 | { |
871 | close(sighup_pipe); |
872 | sighup_pipe= -1; |
873 | } |
874 | else |
875 | { |
876 | kill(0, SIGTERM); |
877 | _exit(0); |
878 | } |
879 | |
880 | #if RETSIGTYPE != void |
881 | return (0); |
882 | #endif |
883 | } |
884 | |
885 | static int startchildren(int *pipefd) |
886 | { |
887 | unsigned i; |
888 | pid_t p; |
889 | |
890 | signal(SIGCHLD, sighup); |
891 | for (i=0; i<ndaemons; i++) |
892 | { |
893 | p=fork(); |
894 | while (p == -1) |
895 | { |
896 | perror("CRIT: fork() failed"); |
897 | sleep(5); |
898 | p=fork(); |
899 | } |
900 | if (p == 0) |
901 | { |
902 | sighup_pipe= -1; |
903 | close(pipefd[1]); |
904 | signal(SIGHUP, SIG_DFL); |
905 | signal(SIGTERM, SIG_DFL); |
906 | signal(SIGCHLD, SIG_DFL); |
907 | return (1); |
908 | } |
909 | } |
910 | return (0); |
911 | } |
912 | |
913 | static int killchildren(int *pipefd) |
914 | { |
915 | int waitstat; |
916 | |
917 | while (wait(&waitstat) >= 0 || errno != ECHILD) |
918 | ; |
919 | |
920 | if (pipe(pipefd)) |
921 | { |
922 | perror("CRIT: pipe() failed"); |
923 | return (-1); |
924 | } |
925 | |
926 | return (0); |
927 | } |
928 | |
929 | int start() |
930 | { |
931 | int s; |
932 | int fd; |
933 | int pipefd[2]; |
934 | int do_child; |
935 | |
936 | for (fd=3; fd<256; fd++) |
937 | close(fd); |
938 | |
939 | if (pipe(pipefd)) |
940 | { |
941 | perror("pipe"); |
942 | return (1); |
943 | } |
944 | |
945 | if (lt_dlinit()) |
946 | { |
947 | fprintf(stderr, "ERR: lt_dlinit() failed: %s\n", |
948 | lt_dlerror()); |
949 | exit(1); |
950 | } |
951 | |
952 | if (lt_dlsetsearchpath(PKGLIBDIR)) |
953 | { |
954 | fprintf(stderr, "ERR: lt_dlsetsearchpath() failed: %s\n", |
955 | lt_dlerror()); |
956 | exit(1); |
957 | } |
958 | |
959 | if (readconfig()) |
960 | { |
961 | close(pipefd[0]); |
962 | close(pipefd[1]); |
963 | return (1); |
964 | } |
965 | |
966 | s=mksocket(); |
967 | if (s < 0) |
968 | { |
969 | perror(AUTHDAEMONSOCK); |
970 | close(pipefd[0]); |
971 | close(pipefd[1]); |
972 | return (1); |
973 | } |
974 | |
975 | signal(SIGPIPE, SIG_IGN); |
976 | signal(SIGHUP, sighup); |
977 | signal(SIGTERM, sigterm); |
978 | |
979 | close(0); |
980 | if (open("/dev/null", O_RDWR) != 0) |
981 | { |
982 | perror("open"); |
983 | exit(1); |
984 | } |
985 | dup2(0, 1); |
986 | sighup_pipe= pipefd[1]; |
987 | |
988 | do_child=startchildren(pipefd); |
989 | |
990 | for (;;) |
991 | { |
992 | struct sockaddr saddr; |
993 | socklen_t saddr_len; |
994 | fd_set fds; |
995 | struct timeval tv; |
996 | |
997 | FD_ZERO(&fds); |
998 | FD_SET(pipefd[0], &fds); |
999 | |
1000 | if (do_child) |
1001 | FD_SET(s, &fds); |
1002 | |
1003 | tv.tv_sec=300; |
1004 | tv.tv_usec=0; |
1005 | |
1006 | if (select( (s > pipefd[0] ? s:pipefd[0])+1, |
1007 | &fds, 0, 0, &tv) < 0) |
1008 | continue; |
1009 | if (FD_ISSET(pipefd[0], &fds)) |
1010 | { |
1011 | close(pipefd[0]); |
1012 | if (do_child) |
1013 | return (0); /* Parent died */ |
1014 | fprintf(stderr, "INFO: stopping authdaemond children\n"); |
1015 | while (killchildren(pipefd)) |
1016 | sleep(5); |
1017 | if (sigterm_received) |
1018 | return (0); |
1019 | fprintf(stderr, "INFO: restarting authdaemond children\n"); |
1020 | readconfig(); |
1021 | sighup_pipe=pipefd[1]; |
1022 | do_child=startchildren(pipefd); |
1023 | continue; |
1024 | } |
1025 | |
1026 | if (!FD_ISSET(s, &fds)) |
1027 | { |
1028 | idlefunc(); |
1029 | continue; |
1030 | } |
1031 | |
1032 | saddr_len=sizeof(saddr); |
1033 | if ((fd=accept(s, &saddr, &saddr_len)) < 0) |
1034 | continue; |
1035 | if (fcntl(fd, F_SETFL, 0) < 0) |
1036 | { |
1037 | perror("CRIT: fcntl() failed"); |
1038 | } |
1039 | else |
1040 | doauth(fd); |
1041 | close(fd); |
1042 | } |
1043 | } |
1044 | |
1045 | int main(int argc, char **argv) |
1046 | { |
1047 | courier_authdebug_login_init(); |
1048 | |
1049 | if (argc > 1) |
1050 | { |
1051 | fprintf(stderr, "Error: authdaemond no longer handles its own daemonizing.\n" |
1052 | "Use new startup script.\n"); |
1053 | exit(1); |
1054 | } |
1055 | |
1056 | start(); |
1057 | return (0); |
1058 | } |