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