Merge remote-tracking branch 'origin/debian'
[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"
b0322a85 7#include "courierauthstaticlist.h"
d9898ee8 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
d9898ee8 34
35static 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
117static 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
167static int writeauth(int fd, const char *p, unsigned pl)
168{
169fd_set fds;
170struct 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
190static void readauth(int fd, char *p, unsigned pl, const char *term)
191{
192time_t end_time, curtime;
193unsigned 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
232int _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
247int 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
259int _authdaemondo(int wrfd, int rdfd, const char *authreq,
260 int (*func)(struct authinfo *, void *), void *arg)
261{
262char buf[BUFSIZ];
263char *p, *q, *r;
264struct authinfo a;
265uid_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
350int authdaemondo(const char *authreq,
351 int (*func)(struct authinfo *, void *), void *arg)
352{
353int s=opensock();
354int 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
365void auth_daemon_cleanup()
366{
367}
368
369struct 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
379static 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
416static 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
438int _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
525void 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}