Imported Upstream version 0.66.1
[hcoop/debian/courier-authlib.git] / authdaemonlib.c
1 /*
2 ** Copyright 2000-2006 Double Precision, Inc. See COPYING for
3 ** distribution information.
4 */
5
6 #include "auth.h"
7 #include "courierauthstaticlist.h"
8 #include "courierauthsasl.h"
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #if HAVE_UNISTD_H
13 #include <unistd.h>
14 #endif
15 #include <fcntl.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <sys/un.h>
19 #include <sys/time.h>
20 #if HAVE_SYS_SELECT_H
21 #include <sys/select.h>
22 #endif
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <errno.h>
27 #include "authdaemonrc.h"
28 #include "numlib/numlib.h"
29
30 static int TIMEOUT_SOCK=10,
31 TIMEOUT_WRITE=10,
32 TIMEOUT_READ=30;
33
34
35 static int s_connect(int sockfd,
36 const struct sockaddr *addr,
37 size_t addr_s,
38 time_t connect_timeout)
39 {
40 fd_set fdr;
41 struct timeval tv;
42 int rc;
43
44 #ifdef SOL_KEEPALIVE
45 setsockopt(sockfd, SOL_SOCKET, SOL_KEEPALIVE,
46 (const char *)&dummy, sizeof(dummy));
47 #endif
48
49 #ifdef SOL_LINGER
50 {
51 struct linger l;
52
53 l.l_onoff=0;
54 l.l_linger=0;
55
56 setsockopt(sockfd, SOL_SOCKET, SOL_LINGER,
57 (const char *)&l, sizeof(l));
58 }
59 #endif
60
61 /*
62 ** If configuration says to use the kernel's timeout settings,
63 ** just call connect, and be done with it.
64 */
65
66 if (connect_timeout == 0)
67 return ( connect(sockfd, addr, addr_s));
68
69 /* Asynchronous connect with timeout. */
70
71 if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) return (-1);
72
73 if ( connect(sockfd, addr, addr_s) == 0)
74 {
75 /* That was easy, we're done. */
76
77 if (fcntl(sockfd, F_SETFL, 0) < 0) return (-1);
78 return (0);
79 }
80
81 if (errno != EINPROGRESS)
82 return -1;
83
84 /* Wait for the connection to go through, until the timeout expires */
85
86 FD_ZERO(&fdr);
87 FD_SET(sockfd, &fdr);
88 tv.tv_sec=connect_timeout;
89 tv.tv_usec=0;
90
91 rc=select(sockfd+1, 0, &fdr, 0, &tv);
92 if (rc < 0) return (-1);
93
94 if (!FD_ISSET(sockfd, &fdr))
95 {
96 errno=ETIMEDOUT;
97 return (-1);
98 }
99
100 {
101 int gserr;
102 socklen_t gslen = sizeof(gserr);
103
104 if (getsockopt(sockfd, SOL_SOCKET,
105 SO_ERROR,
106 (char *)&gserr, &gslen)==0)
107 {
108 if (gserr == 0)
109 return 0;
110
111 errno=gserr;
112 }
113 }
114 return (-1);
115 }
116
117 static int opensock()
118 {
119 int s=socket(PF_UNIX, SOCK_STREAM, 0);
120 struct sockaddr_un skun;
121
122 skun.sun_family=AF_UNIX;
123 strcpy(skun.sun_path, AUTHDAEMONSOCK);
124
125 if (s < 0)
126 {
127 perror("CRIT: authdaemon: socket() failed");
128 return (-1);
129 }
130
131 {
132 const char *p=getenv("TIMEOUT_SOCK");
133 int n=atoi(p ? p:"0");
134
135 if (n > 0)
136 TIMEOUT_SOCK=n;
137 }
138
139 {
140 const char *p=getenv("TIMEOUT_READ");
141 int n=atoi(p ? p:"0");
142
143 if (n > 0)
144 TIMEOUT_READ=n;
145 }
146
147 {
148 const char *p=getenv("TIMEOUT_WRITE");
149 int n=atoi(p ? p:"0");
150
151 if (n > 0)
152 TIMEOUT_WRITE=n;
153 }
154
155 if (s_connect(s, (const struct sockaddr *)&skun, sizeof(skun),
156 TIMEOUT_SOCK))
157 {
158 perror("ERR: authdaemon: s_connect() failed");
159 if (errno == ETIMEDOUT || errno == ECONNREFUSED)
160 fprintf(stderr, "ERR: [Hint: perhaps authdaemond is not running?]\n");
161 close(s);
162 return (-1);
163 }
164 return (s);
165 }
166
167 static int writeauth(int fd, const char *p, unsigned pl)
168 {
169 fd_set fds;
170 struct timeval tv;
171
172 while (pl)
173 {
174 int n;
175
176 FD_ZERO(&fds);
177 FD_SET(fd, &fds);
178 tv.tv_sec=TIMEOUT_WRITE;
179 tv.tv_usec=0;
180 if (select(fd+1, 0, &fds, 0, &tv) <= 0 || !FD_ISSET(fd, &fds))
181 return (-1);
182 n=write(fd, p, pl);
183 if (n <= 0) return (-1);
184 p += n;
185 pl -= n;
186 }
187 return (0);
188 }
189
190 static void readauth(int fd, char *p, unsigned pl, const char *term)
191 {
192 time_t end_time, curtime;
193 unsigned len = 0, tlen = strlen(term);
194
195 --pl;
196
197 time(&end_time);
198 end_time += TIMEOUT_READ;
199
200 while (pl)
201 {
202 int n;
203 fd_set fds;
204 struct timeval tv;
205
206 time(&curtime);
207 if (curtime >= end_time)
208 break;
209
210 FD_ZERO(&fds);
211 FD_SET(fd, &fds);
212 tv.tv_sec=end_time - curtime;
213 tv.tv_usec=0;
214 if (select(fd+1, &fds, 0, 0, &tv) <= 0 || !FD_ISSET(fd, &fds))
215 break;
216
217 n=read(fd, p, pl);
218 if (n <= 0)
219 break;
220 p += n;
221 pl -= n;
222 /* end-of-message detection for authpipe */
223 len += n;
224 if (len >= tlen && strncmp(p-tlen, term, tlen) == 0)
225 break;
226 else if (len == 5 && strncmp(p-5, "FAIL\n", 5) == 0)
227 break;
228 }
229 *p=0;
230 }
231
232 int _authdaemondopasswd(int wrfd, int rdfd, char *buffer, int bufsiz)
233 {
234 if (writeauth(wrfd, buffer, strlen(buffer)))
235 return 1;
236
237 readauth(rdfd, buffer, bufsiz, "\n");
238
239 if (strcmp(buffer, "OK\n"))
240 {
241 errno=EPERM;
242 return -1;
243 }
244 return 0;
245 }
246
247 int authdaemondopasswd(char *buffer, int bufsiz)
248 {
249 int s=opensock();
250 int rc;
251
252 if (s < 0)
253 return (1);
254 rc = _authdaemondopasswd(s, s, buffer, bufsiz);
255 close(s);
256 return rc;
257 }
258
259 int _authdaemondo(int wrfd, int rdfd, const char *authreq,
260 int (*func)(struct authinfo *, void *), void *arg)
261 {
262 char buf[BUFSIZ];
263 char *p, *q, *r;
264 struct authinfo a;
265 uid_t u;
266
267 if (writeauth(wrfd, authreq, strlen(authreq)))
268 {
269 return (1);
270 }
271
272 readauth(rdfd, buf, sizeof(buf), "\n.\n");
273 memset(&a, 0, sizeof(a));
274 a.homedir="";
275 p=buf;
276 while (*p)
277 {
278 for (q=p; *q; q++)
279 if (*q == '\n')
280 {
281 *q++=0;
282 break;
283 }
284 if (strcmp(p, ".") == 0)
285 {
286 return ( (*func)(&a, arg));
287 }
288 if (strcmp(p, "FAIL") == 0)
289 {
290 errno=EPERM;
291 return (-1);
292 }
293 r=strchr(p, '=');
294 if (!r)
295 {
296 p=q;
297 continue;
298 }
299 *r++=0;
300
301 if (strcmp(p, "USERNAME") == 0)
302 a.sysusername=r;
303 else if (strcmp(p, "UID") == 0)
304 {
305 u=atol(r);
306 a.sysuserid= &u;
307 }
308 else if (strcmp(p, "GID") == 0)
309 {
310 a.sysgroupid=atol(r);
311 }
312 else if (strcmp(p, "HOME") == 0)
313 {
314 a.homedir=r;
315 }
316 else if (strcmp(p, "ADDRESS") == 0)
317 {
318 a.address=r;
319 }
320 else if (strcmp(p, "NAME") == 0)
321 {
322 a.fullname=r;
323 }
324 else if (strcmp(p, "MAILDIR") == 0)
325 {
326 a.maildir=r;
327 }
328 else if (strcmp(p, "QUOTA") == 0)
329 {
330 a.quota=r;
331 }
332 else if (strcmp(p, "PASSWD") == 0)
333 {
334 a.passwd=r;
335 }
336 else if (strcmp(p, "PASSWD2") == 0)
337 {
338 a.clearpasswd=r;
339 }
340 else if (strcmp(p, "OPTIONS") == 0)
341 {
342 a.options=r;
343 }
344 p=q;
345 }
346 errno=EIO;
347 return (1);
348 }
349
350 int authdaemondo(const char *authreq,
351 int (*func)(struct authinfo *, void *), void *arg)
352 {
353 int s=opensock();
354 int rc;
355
356 if (s < 0)
357 {
358 return (1);
359 }
360 rc = _authdaemondo(s, s, authreq, func, arg);
361 close(s);
362 return rc;
363 }
364
365 void auth_daemon_cleanup()
366 {
367 }
368
369 struct enum_getch {
370 char buffer[BUFSIZ];
371 char *buf_ptr;
372 size_t buf_left;
373 };
374
375 #define getauthc(fd,eg) ((eg)->buf_left-- ? \
376 (unsigned char)*((eg)->buf_ptr)++:\
377 fillgetauthc((fd),(eg)))
378
379 static int fillgetauthc(int fd, struct enum_getch *eg)
380 {
381 time_t end_time, curtime;
382
383 time(&end_time);
384 end_time += 60;
385
386 for (;;)
387 {
388 int n;
389 fd_set fds;
390 struct timeval tv;
391
392 time(&curtime);
393 if (curtime >= end_time)
394 break;
395
396 FD_ZERO(&fds);
397 FD_SET(fd, &fds);
398 tv.tv_sec=end_time - curtime;
399 tv.tv_usec=0;
400 if (select(fd+1, &fds, 0, 0, &tv) <= 0 || !FD_ISSET(fd, &fds))
401 break;
402
403 n=read(fd, eg->buffer, sizeof(eg->buffer));
404 if (n <= 0)
405 break;
406
407 eg->buf_ptr=eg->buffer;
408 eg->buf_left=n;
409
410 --eg->buf_left;
411 return (unsigned char)*(eg->buf_ptr)++;
412 }
413 return EOF;
414 }
415
416 static int readline(int fd, struct enum_getch *eg,
417 char *buf,
418 size_t bufsize)
419 {
420 if (bufsize == 0)
421 return EOF;
422
423 while (--bufsize)
424 {
425 int ch=getauthc(fd, eg);
426
427 if (ch == EOF)
428 return -1;
429 if (ch == '\n')
430 break;
431
432 *buf++=ch;
433 }
434 *buf=0;
435 return 0;
436 }
437
438 int _auth_enumerate(int wrfd, int rdfd,
439 void(*cb_func)(const char *name,
440 uid_t uid,
441 gid_t gid,
442 const char *homedir,
443 const char *maildir,
444 const char *options,
445 void *void_arg),
446 void *void_arg)
447 {
448 static char cmd[]="ENUMERATE\n";
449 struct enum_getch eg;
450 char linebuf[BUFSIZ];
451
452 if (writeauth(wrfd, cmd, sizeof(cmd)-1))
453 {
454 return 1;
455 }
456
457 eg.buf_left=0;
458
459 while (readline(rdfd, &eg, linebuf, sizeof(linebuf)) == 0)
460 {
461 char *p;
462 const char *name;
463 uid_t uid;
464 gid_t gid;
465 const char *homedir;
466 const char *maildir;
467 const char *options;
468
469 if (strcmp(linebuf, ".") == 0)
470 {
471 (*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg);
472 return 0;
473 }
474
475 p=strchr(linebuf, '#');
476 if (p) *p=0;
477
478 p=strchr(linebuf, '\t');
479
480 if (p)
481 {
482 name=linebuf;
483 *p++=0;
484
485 uid=libmail_atouid_t(p);
486 p=strchr(p, '\t');
487 if (uid && p)
488 {
489 *p++=0;
490 gid=libmail_atogid_t(p);
491 p=strchr(p, '\t');
492 if (gid && p)
493 {
494 *p++=0;
495 homedir=p;
496 p=strchr(p, '\t');
497 maildir=NULL;
498 options=NULL;
499
500 if (p)
501 {
502 *p++=0;
503 maildir=p;
504 p=strchr(p, '\t');
505
506 if (p)
507 {
508 *p++=0;
509 options=p;
510 p=strchr(p, '\t');
511 if (p) *p=0;
512 }
513 }
514
515
516 (*cb_func)(name, uid, gid, homedir,
517 maildir, options, void_arg);
518 }
519 }
520 }
521 }
522 return 1;
523 }
524
525 void auth_enumerate( void(*cb_func)(const char *name,
526 uid_t uid,
527 gid_t gid,
528 const char *homedir,
529 const char *maildir,
530 const char *options,
531 void *void_arg),
532 void *void_arg)
533 {
534 int s=opensock();
535
536 if (s < 0)
537 return;
538
539 _auth_enumerate(s, s, cb_func, void_arg);
540
541 close(s);
542 }