s/qmail 4.4.13
Next generation secure email transport
Loading...
Searching...
No Matches
qmail-ldapam.c
Go to the documentation of this file.
1#define LDAP_DEPRECATED 1
2/* OpenLDAP still needs some time to stabilize the new code base.
3 In the new regime, there are dark edges where dragons live ;-)
4*/
5//#include <stdio.h> // to be removed
6#include <sys/types.h>
7#include <sys/stat.h>
8#include <unistd.h>
9#include <string.h>
10#include <grp.h>
11#include <pwd.h>
12#include <ldap.h>
13#include "auto_qmail.h"
14#include "qmail.h"
15#include "case.h"
16#include "control.h"
17#include "constmap.h"
18#include "readwrite.h"
19#include "buffer.h"
20#include "fd.h"
21#include "byte.h"
22#include "case.h"
23#include "str.h"
24#include "stralloc.h"
25#include "exit.h"
26#include "logmsg.h"
27#include "pathexec.h"
28#include "getln.h"
29#include "scan.h"
30#include "getoptb.h"
31#include "open.h"
32#include "readclose.h"
33#include "env.h"
34#include "base64.h"
35#include "md5.h"
36#include "hmac_md5.h"
37#include "sha1.h"
38#include "sha256.h"
39
40#define WHO "qmail-ldapam"
41
42#define FDAUTH 3
43#define FDLOG 4
44#define FDPWD 5
45#define PORT_LDAP 389
46#define PORT_LDAPS 636
47
49buffer ba = BUFFER_INIT(buffer_unixwrite,FDAUTH,authbuf,sizeof(authbuf));
50char bspace[512];
51buffer bp;
52
54buffer bl = BUFFER_INIT(buffer_unixwrite,FDLOG,logbuf,sizeof(logbuf));
55
57stralloc ldapcntl = {0};
58stralloc disabled = {0};
59
60/* LDAP binding params */
61
62stralloc binddn = {0};
63stralloc bindpw = {0};
64stralloc bindpwds = {0};
65stralloc bindbase = {0};
66stralloc bindhost = {0};
67stralloc filter = {0};
68
69stralloc user = {0}; // user w/o domain appended
70stralloc domaindn = {0};
71stralloc password = {0};
72stralloc filters = {0};
73stralloc userattr = {0};
74
75unsigned long scope = LDAP_SCOPE_SUBTREE; // (2), BASE (0), ONELEVEL (1)
76int version = LDAP_VERSION3;
77
78static void log_error(const char *in,const char *src,const char *type)
79{
80 buffer_puts(&bl,WHO);
81 buffer_put(&bl,": ",2);
82 if (type) { buffer_puts(&bl,type); buffer_put(&bl," ",1); }
83 buffer_puts(&bl,in);
84 if (src) { buffer_puts(&bl,src); buffer_put(&bl," ",1); }
85 buffer_put(&bl,"\n",1);
86 buffer_flush(&bl);
87}
88
89
90static void pexit(int fail)
91{
92 for (int i = 0; i < sizeof(authbuf); ++i) authbuf[i] = 0;
93 _exit(fail);
94}
95
96static void temp_nomem(void)
97{
98 buffer_puts(&bl,WHO);
99 buffer_puts(&bl,": FATAL: out of memory\n");
100 buffer_flush(&bl);
101 pexit(111);
102}
103
104static int get_password(stralloc *in)
105{
106 int fd;
107 struct stat st;
108
109 fd = open_read(in->s);
110 if (fd == -1) return 0;
111
112 if (fstat(fd,&st) == -1)
113 { log_error("unable to read: ",in->s,"FATAL"); pexit(111); }
114 if ((st.st_mode & 0477) != 0400)
115 { log_error("wrong file permissions for: ",in->s,"FATAL"); pexit(111); }
116 if (readclose_append(fd,&password,BUFSIZE_LINE) == -1)
117 { log_error("unable to read: ",in->s,"FATAL"); pexit(111); }
118
119 if (!stralloc_copyb(in,password.s,password.len - 1)) return -1;
120 if (!stralloc_0(in)) return -1;
121
122 return password.len;
123}
124
125static void read_passwd(void)
126{
127 int match = 0;
128
129 if (!bindpwds.len) {
130 buffer_init(&bp,buffer_unixread,FDPWD,bspace,sizeof(bspace));
131 if (getln(&bp,&bindpwds,&match,'\0') == -1)
132 { log_error("unable to read password",0,"FATAL"); pexit(111); }
133 close(FDPWD);
134 if (match) --bindpwds.len;
135 }
136 if (!stralloc_0(&bindpwds)) temp_nomem();
137}
138
139/* This is a clumsy way to convert DNs into three-level Internet domain names.
140 DNs can be given as dc=,dc= (RFC 4514); or as legacy O=/OU=/C= and mapped in correct order
141 Though domain names can be given in UTF8, standard ASCII is supported only and not converted to Punycode */
142
143static int dn2domain(stralloc *domain,char *dn,int flagdn)
144{
145 char c;
146 char *ou = 0;
147 char *org = 0;
148 char *country = 0;
149 int j, k, l = 0;
150
151 int dnlen = str_len(dn);
152 if (!stralloc_copys(domain,"")) return -1;
153
154 if (flagdn == 1) c = ',';
155 if (flagdn == 2) c = '/';
156
157 /* dc= case */
158
159 for (int i = 0; i < dnlen; ++i) {
160 if (case_startb(dn + i,3,"dc=")) {
161 j = str_chr(dn + i,c);
162 if (dn[i + j] == c || dnlen == i + j) {
163 if (!stralloc_catb(domain,dn + i + 3,j - 3)) return -1;
164 if (dnlen != i + j)
165 if (!stralloc_cats(domain,".")) return -1;
166 }
167 }
168 }
169 if (domain->len > 2) {
170 if (!stralloc_0(domain)) return -1;
171 return 1;
172 }
173
174 /* o= case */
175
176 for (int i = 0; i < dnlen; ++i) {
177 if (case_startb(dn + i,3,"ou=")) {
178 j = str_chr(dn + i,c);
179 if (dn[i + j] == c || dnlen == i + j) ou = dn + i + 3;
180 }
181 if (case_startb(dn + i,2,"o=")) {
182 k = str_chr(dn + i,c);
183 if (dn[i + k] == c || dnlen == i + k) org = dn + i + 2;
184 }
185 if (case_startb(dn + i,2,"c=")) {
186 l = str_chr(dn + i,c);
187 if (dn[i + l] == c || dnlen == i + l) country = dn + i + 2;
188 }
189 }
190 /* put everything together in correct order */
191
192 if (org && *org && country && *country) {
193 if (ou && *ou) {
194 if (!stralloc_catb(domain,ou,j - 3)) return -1;
195 if (!stralloc_cats(domain,".")) return -1;
196 }
197 if (!stralloc_catb(domain,org,k - 2)) return -1;
198 if (!stralloc_cats(domain,".")) return -1;
199 if (!stralloc_catb(domain,country,l)) return -1;
200 if (!stralloc_0(domain)) return -1;
201 }
202 if (domain->len > 2) return 2;
203
204 return 0;
205}
206
207static stralloc cafile = {0};
208static stralloc cadir = {0};
209static stralloc certfile = {0};
210static stralloc keyfile = {0};
211
212/* LDAP *ldap_setup() is used to prepare an (1) unencrypted, (2) StartTLS,
213 or (3) strong bind connection to a LDAP server, given flagtls on input */
214
215LDAP *ldap_setup(int flagtls,char *host,int port)
216{
217 LDAP *ld;
218
219/* Simple bind */
220
221 if (!(ld = ldap_init(host,port)))
222 { log_error("unable to initialize LDAP bind",0,"FATAL"); pexit(111); }
223
224// Specify version 3; the default is version 2; required for TLS
225
226 if (ldap_set_option(ld,LDAP_OPT_PROTOCOL_VERSION,(void *)&version) != LDAP_OPT_SUCCESS)
227 { log_error("unable to initialize LDAP vers 3 bind",0,"FATAL"); pexit(111); }
228
229/* StartTLS bind */
230
231 if (flagtls == 1) {
232 if (ldap_start_tls_s(ld,NULL,NULL) != LDAP_SUCCESS)
233 { log_error("unable to connect via StartTLS to the LDAP server",0,"FATAL"); pexit(111); }
234
235 if (cafile.len > 1)
236 if (ldap_set_option(ld,LDAP_OPT_X_TLS_CACERTFILE,(void *)cafile.s) != LDAP_OPT_SUCCESS)
237 { log_error("unable to set LDAP CA file: ",cafile.s,"FATAL"); pexit(111); }
238
239 if (cadir.len > 1)
240 if (ldap_set_option(ld,LDAP_OPT_X_TLS_CACERTDIR,(void *)cadir.s) != LDAP_OPT_SUCCESS)
241 { log_error("unable to set LDAP CA directory: ",cadir.s,"FATAL"); pexit(111); }
242 }
243
244/* Strong bind */
245
246 if (flagtls == 2) { // setup TLS context -- options first
247 if (certfile.len && keyfile.len) {
248 if (ldap_set_option(ld,LDAP_OPT_X_TLS_CERTFILE,certfile.s) != LDAP_OPT_SUCCESS)
249 { log_error("unable to initialize LDAP vers 3 bind",0,"FATAL"); pexit(111); }
250 if (ldap_set_option(ld,LDAP_OPT_X_TLS_KEYFILE,keyfile.s) != LDAP_OPT_SUCCESS)
251 { log_error("unable to initialize LDAP vers 3 bind",0,"FATAL"); pexit(111); }
252 }
253 if (ldap_install_tls(ld) != LDAP_SUCCESS)
254 { log_error("unable to connect via TLS for strong bind to the LDAP server",0,"FATAL"); pexit(111); }
255 }
256
257 return ld;
258}
259
260static int ldap_testbind(int flag,char *host,unsigned long port)
261{
262 LDAP *ld;
263 int r;
264
265 ld = ldap_setup(flag,host,port);
266 r = ldap_simple_bind_s(ld,binddn.s,bindpw.s);
267 if (r == LDAP_SUCCESS) ldap_unbind(ld);
268
269 return r;
270}
271
272/* The ldap_mailaddr() recipient Mailbox given by the RECIPIENT interface
273 uses the checkpassword API:
274 a) We start from existing DN in the LDAP (a 'proxy' from 'ldapbind') allowing
275 b) to search for the MAIL attribute in the subtree given by the 'bindbase' from 'ldapbind'.
276 We return the DN as AUTHUSER.
277 The search attribute is customizable via 'ldapbind' but defaults to MAIL.
278*/
279
280static int ldap_mailaddr(int flag,char *host,unsigned long port,char *mailaddr,char *filter)
281{
282 LDAP *ld;
283 LDAPMessage *result, *entry;
284 char *search = "mail";
285 char *dn, **mail;
286 int r;
287 int addrok = 0;
288
289 r = str_len(mailaddr);
290 if (r == 0 || str_chr(mailaddr,'@') == r)
291 { log_error("invalid mail address : ",mailaddr,"ERROR"); pexit(2); }
292
293 ld = ldap_setup(flag,host,port);
294
295 if (ldap_simple_bind_s(ld,binddn.s,bindpw.s) != LDAP_SUCCESS)
296 { log_error("can't bind with LDAP server for mailaddr. DN:",binddn.s,"ERROR"); pexit(110); }
297
298 // Construct filter and search with limited scope
299
300 if (filter && *filter) search = filter;
301 if (!stralloc_copys(&filters,"(")) temp_nomem();
302 if (!stralloc_cats(&filters,search)) temp_nomem();
303 if (!stralloc_cats(&filters,"=")) temp_nomem();
304 if (!stralloc_cats(&filters,mailaddr)) temp_nomem();
305 if (!stralloc_cats(&filters,")")) temp_nomem();
306 if (!stralloc_0(&filters)) temp_nomem();
307
308 r = ldap_search_s(ld,bindbase.s,scope,filters.s,NULL,0,&result);
309 if (r)
310 { log_error("search failed: ",ldap_err2string(r),"ERROR"); pexit(110); }
311
312 entry = ldap_first_entry(ld,result);
313 if (!entry) { ldap_unbind(ld); pexit(1); } // Not found
314
315 dn = ldap_get_dn(ld,result); // DN = Authuser
316 mail = ldap_get_values(ld,entry,search);
317 addrok = ldap_count_values(mail);
318
319 ldap_msgfree(result);
320 ldap_unbind(ld);
321
322 if (!addrok) { log_error("mail address not found: ",mailaddr,"INFO"); pexit(1); }
323
324 /* Done; set environment variables */
325
326 if (!env_put("AUTHUSER",dn)) pexit(111);
327 return 0;
328}
329
330/* The ldap_authuser() needs to be authorized by some information over the checkpassword API:
331 a) The DN + password in the LDAP (LDAP aware).
332 b) Username + password, where the username includes the userid + domain: userid@domain.
333 From here, we construct the DN for the LDAP bind: CN = userid (default)
334 UserAttr := userid, eg. UID: UID = userid; UserAttr defined in 'ldapbind'.
335 c) We also check, whether the DN has a (MAIL) attribute value matching the SMTP Mailfrom:<>.
336 Here, the default attribute is MAIL, but can be customized as well in 'ldapbind'.
337 For the retrieved DN entry, we iterate for that attribute over of all values.
338*/
339
340static int ldap_authuser(int flag,char *host,unsigned long port,char *domain,char *user,char *pwd,char *id,char *filter)
341{
342 LDAP *ld;
343 LDAPMessage *result, *entry;
344 unsigned long scope = LDAP_SCOPE_BASE;
345 char *search = "mail";
346 char **mail;
347 char *mailfrom;
348 int addrok = 0;
349 int r;
350
351 mailfrom = env_get("MAILFROM");
352 if (!mailfrom || !*mailfrom)
353 { log_error("Missing Mailfrom:<> for user: ",user,"FATAL"); pexit(2); }
354
355 ld = ldap_setup(flag,host,port);
356
357 /* Fabricate DN to use for binding; if not given directly */
358
359 if (domain && *domain) {
360 if (id && *id) { // custom DN identifier
361 if (!stralloc_copys(&binddn,id)) temp_nomem();
362 } else
363 if (!stralloc_copys(&binddn,"cn")) temp_nomem(); // default DN cn=,dc=,...
364 if (!stralloc_cats(&binddn,"=")) temp_nomem();
365 if (!stralloc_cats(&binddn,user)) temp_nomem();
366
367 if (!stralloc_cats(&binddn,",dc=")) temp_nomem();
368 for (int i = 0; i < str_len(domain); ++i) {
369 if (domain[i] == '.') {
370 if (!stralloc_cats(&binddn,",dc=")) temp_nomem();
371 } else
372 if (!stralloc_catb(&binddn,domain + i,1)) temp_nomem();
373 }
374 if (!stralloc_0(&binddn)) temp_nomem();
375 } else
376 if (!stralloc_copys(&binddn,user)) temp_nomem(); // User DN given
377
378 if (ldap_simple_bind_s(ld,binddn.s,pwd) != LDAP_SUCCESS)
379 { log_error("can't bind with LDAP server for auth. DN:",binddn.s,"INFO"); pexit(111); }
380
381 // Construct filter and search with limited scope
382
383 if (filter && *filter) search = filter;
384 if (!stralloc_copys(&filters,"(")) temp_nomem();
385 if (!stralloc_cats(&filters,search)) temp_nomem();
386 if (!stralloc_cats(&filters,"=*)")) temp_nomem();
387 if (!stralloc_0(&filters)) temp_nomem();
388
389 r = ldap_search_s(ld,binddn.s,scope,filters.s,NULL,0,&result);
390 if (r)
391 { log_error("search failed: ",ldap_err2string(r),"ERROR"); pexit(110); }
392
393 entry = ldap_first_entry(ld,result);
394 if (!entry) { ldap_msgfree(result); ldap_unbind(ld); pexit(1); }
395
396 /* Look for a corresponding mailaddr matching $MAILFROM */
397
398 mail = ldap_get_values(ld,entry,search);
399 for (int i = 0; i < ldap_count_values(mail); ++i)
400 if (case_equals(mailfrom,mail[i])) addrok++;
401
402 ldap_msgfree(result);
403 ldap_msgfree(entry);
404 ldap_unbind(ld);
405
406 if (!addrok) { log_error("mail address not found: ",mailfrom,"INFO"); pexit(1); }
407
408 /* Done, set environment variables */
409
410 if (!env_put("AUTHUSER",binddn.s)) pexit(111);
411 return 0;
412}
413
414/* The ldap_mboxuser() needs to be authorized by some information over the checkpassword API:
415 a) The DN + password in the LDAP (LDAP aware).
416 b) Username + password, where the username includes the userid + domain: userid@domain.
417 From here, we construct the DN for the LDAP bind:
418 CN = userid (default)
419 UserAttr := userid, eg. UID: UID = userid; UserAttr defined in 'ldapbind'.
420 c) We require, that for the retrieved DN a HOMEDIRECTORY attribute exists (eg. PosixAccount schema).
421 Here, the default attribute is 'homeDirectory', but can be customized as well in 'ldapbind'.
422 d) For the given DN entry, we iterate for that attribute over of all values and check
423 its existance while opening it. Thus read access is required.
424 e) We apply a chdir() to the HOMEDIRECTORY; if possible.
425 f) The existance of a subsequent 'mbox' or 'maildir' is subject of the called program.
426
427 Note: While for usual Unix users HOMEDIRECTORY, it is typically '/home/user',
428 for a virtual mail manager (VMM) it is the VMM's home directory.
429*/
430
431static int ldap_mboxuser(int flag,char *host,unsigned long port,char *domain,char *user,char *pwd,char *id,char *filter)
432{
433 LDAP *ld;
434 LDAPMessage *result, *entry;
435 unsigned long scope = LDAP_SCOPE_BASE;
436 char *search = "homeDirectory";
437 char **homedir;
438 int fd;
439 int homeok = 0;
440 int r;
441
442 ld = ldap_setup(flag,host,port);
443
444 if (domain && *domain) {
445 if (id && *id) { // custom DN identifier
446 if (!stralloc_copys(&binddn,id)) temp_nomem();
447 } else
448 if (!stralloc_copys(&binddn,"cn")) temp_nomem(); // default DN cn=,dc=,...
449 if (!stralloc_cats(&binddn,"=")) temp_nomem();
450 if (!stralloc_cats(&binddn,user)) temp_nomem();
451
452 if (!stralloc_cats(&binddn,",dc=")) temp_nomem();
453 for (int i = 0; i < str_len(domain); ++i) {
454 if (domain[i] == '.') {
455 if (!stralloc_cats(&binddn,",dc=")) temp_nomem();
456 } else
457 if (!stralloc_catb(&binddn,domain + i,1)) temp_nomem();
458 }
459 if (!stralloc_0(&binddn)) temp_nomem();
460 } else
461 if (!stralloc_copys(&binddn,user)) temp_nomem(); // User DN given
462
463 if (ldap_simple_bind_s(ld,binddn.s,pwd) != LDAP_SUCCESS)
464 { log_error("can't bind with LDAP server for home. DN:",binddn.s,"INFO"); pexit(111); }
465
466 // Construct filter and search with limited scope
467
468 if (filter && *filter) search = filter;
469 if (!stralloc_copys(&filters,"(")) temp_nomem();
470 if (!stralloc_cats(&filters,search)) temp_nomem();
471 if (!stralloc_cats(&filters,"=*)")) temp_nomem();
472 if (!stralloc_0(&filters)) temp_nomem();
473
474 r = ldap_search_s(ld,binddn.s,scope,filters.s,NULL,0,&result);
475 if (r)
476 { log_error("search failed: ",ldap_err2string(r),"ERROR"); pexit(110); }
477
478 entry = ldap_first_entry(ld,result);
479 if (!entry) { ldap_msgfree(result); ldap_unbind(ld); pexit(1); } // Not found
480 homedir = ldap_get_values(ld,entry,search);
481
482 for (int i = 0; i < ldap_count_values(homedir); ++i) {
483 fd = open_read(homedir[i]);
484 if (fd == -1) continue;
485 if (chdir(homedir[i]) != -1) {
486 if (!env_put("HOME",homedir[i]) pexit(111);
487 homeok++;
488 }
489 }
490
491 ldap_memfree(search);
492 ldap_msgfree(entry);
493 ldap_unbind(ld);
494
495 if (!homeok)
496 { log_error("can't change to the home directory for DN:",binddn.s,"ERROR"); pexit(1); }
497
498 return 0;
499}
500
501/* ldap_proxyauth():
502 1. Do an initial bind using BindDN and BindPW from ldapbind; considering a hierarchy given by a UserAttr extension.
503 2. Perform a search for the 'userPersonalName' in that branch resulting in a ProxyAuth user (UPN).
504 3. Take the UPN's local part and perform a second bind with the given password of the user.
505 4. If the 'home' variable is provided (taken from the filter value of ldapbind),
506 construct the HOMEDIR of that user and chdir to that.
507 Note: The mapping of BindDN <=> UserAttr <=> UPN + user password needs to realized in the LDAP.
508*/
509
510static int ldap_proxyauth(int flag,char *host,unsigned long port,char *mailaddr,char *pwd,char *id,char *home)
511{
512 LDAP *ld;
513 LDAPMessage *result;
514 stralloc binduser = {0};
515 stralloc homedir = {0};
516 int scope = LDAP_SCOPE_BASE;
517 int fd;
518 int i, j, r;
519 int homeok = 0;
520
521 ld = ldap_setup(flag,host,port);
522
523 /* First BindDN with subordinate 'id' as context: DN:cn=bindCN,cn=id,dc=x,dc=y,dc=z */
524
525 i = str_chr(binddn.s,',');
526 if (case_starts(binddn.s + i + 1,"dc=") && id && *id) {
527 if (!stralloc_copyb(&binduser,binddn.s,i)) temp_nomem();
528 if (!stralloc_cats(&binduser,",cn=")) temp_nomem();
529 if (!stralloc_cats(&binduser,id)) temp_nomem();
530 if (!stralloc_cats(&binduser,binddn.s + i)) temp_nomem();
531 if (!stralloc_0(&binduser)) temp_nomem();
532 }
533 else
534 { log_error("can't setup proxy auth user for DN:",binddn.s,"ERROR"); pexit(110); }
535
536 if (ldap_simple_bind_s(ld,binduser.s,bindpw.s) != LDAP_SUCCESS)
537 { log_error("can't bind with LDAP server for proxy. DN:",binduser.s,"INFO"); pexit(111); }
538
539 /* Do we have a 'UserPrincipalName' with that 'mailaddr' in that scope? */
540
541 if (!stralloc_copys(&filters,"(UserPrincipalName=")) temp_nomem();
542 if (!stralloc_cats(&filters,mailaddr)) temp_nomem();
543 if (!stralloc_cats(&filters,")")) temp_nomem();
544 if (!stralloc_0(&filters)) temp_nomem();
545
546 r = ldap_search_s(ld,binduser.s,scope,filters.s,NULL,0,&result);
547 if (r)
548 { log_error("UserPrincipalName not found in LDAP ",mailaddr,"INFO"); pexit(1); }
549
550 /* Second bind: If found, we try to bind with the localpart of 'mailaddr':
551 DN:cn=local,cn=id,dc=x,dc=y,dc=z
552 */
553
554 j = str_chr(mailaddr,'@');
555 if (!stralloc_copys(&binduser,"cn=")) temp_nomem();
556 if (!stralloc_catb(&binduser,mailaddr,j)) temp_nomem();
557 if (!stralloc_cats(&binduser,",cn=")) temp_nomem();
558 if (!stralloc_cats(&binduser,id)) temp_nomem();
559 if (!stralloc_cats(&binduser,binddn.s + i)) temp_nomem(); // domainpart of binddn
560 if (!stralloc_0(&binduser)) temp_nomem();
561
562 if (ldap_simple_bind_s(ld,binduser.s,pwd) != LDAP_SUCCESS)
563 { log_error("can't bind with LDAP server. DN:",binduser.s,"INFO"); pexit(1); }
564
565 /* Verify it! */
566
567 if (!stralloc_copys(&filters,"(objectclass=*)")) temp_nomem();
568 if (!stralloc_0(&filters)) temp_nomem();
569
570 r = ldap_search_s(ld,binduser.s,scope,filters.s,NULL,0,&result);
571 if (r)
572 { log_error("UserPincipalName rotten in LDAP",ldap_err2string(r),"ERROR"); ldap_unbind(ld); pexit(110); }
573
574 if (!env_put("USER",mailaddr)) pexit(111);
575
576 /* Look for a mbox/maildir path for eg. vpopmail/vmailmgr */
577
578 if (home && *home) {
580 if (!stralloc_cats(&homedir,"/")) temp_nomem();
581 if (!stralloc_catb(&homedir,mailaddr,j)) temp_nomem();
582 if (!stralloc_0(&homedir)) temp_nomem();
583 fd = open_read(homedir.s);
584 if (fd != -1 && chdir(homedir.s) != -1) {
585 if (!env_put("HOME",homedir.s) pexit(111);
586 homeok++;
587 }
588 }
589
590 ldap_unbind(ld);
591
592 if (home && !homeok)
593 { log_error("can't change to the home directory of user:",mailaddr,"ERROR"); pexit(1) ; }
594
595 if (!env_put("AUTHUSER",binduser.s)) pexit(111);
596 return 0;
597}
598
599int main(int argc,char * const argv[])
600{
601 char *authuser = 0;
602 char *ldaparam = 0;
603 char *domain = 0;
604 char *pwgiven = 0;
605 unsigned long port = PORT_LDAP;
606 int authlen = 0;
607 int buflen = 0;
608 int domlen = 0;
609 int flagauth = 0;
610 int flaghome = 0;
611 int flagprox = 0;
612 int flagstls = 0;
613 int dnseen = 0;
614 int opt;
615 int i = 0;
616 int s, p, u, k;
617 int rc;
618
619 while ((opt = getoptb(argc,(char **)argv,"ahps")) !=opteof)
620 switch (opt) {
621 case 'a': flagauth = 1; break;
622 case 'h': flaghome = 1; break;
623 case 'p': flagprox = 1; break;
624 case 's': flagstls = 1; break;
625 }
626 argv += optind;
627 argc -= optind;
628
629 env_unset("USER");
630
631 /* Read input from FDAUTH */
632
633 for (;;) {
634 do {
635 rc = read(FDAUTH,authbuf + buflen,sizeof(authbuf) - buflen);
636 } while ((rc == -1) && (errno == EINTR));
637 if (rc == -1) pexit(111);
638 if (rc == 0) break;
639 buflen += rc;
640 if (buflen >= sizeof(authbuf)) pexit(2);
641 }
642 close(FDAUTH);
643
644 authuser = authbuf + i; /* username or DN */
645 if (i == buflen) pexit(2);
646 while (authbuf[i++]) /* password */
647 if (i == buflen) pexit(2);
648 pwgiven = authbuf + i;
649 if (i == buflen) pexit(2);
650
651 authlen = str_len(authuser);
652 if (!stralloc_copyb(&user,authuser,authlen)) temp_nomem();
653
654 if ((i = byte_rchr(authuser,authlen,'@'))) /* domain */
655 if (i < authlen && authuser[i] == '@') {
656 domain = authuser + i + 1;
657 domlen = str_len(domain);
658 case_lowerb(domain,domlen);
659 if (!stralloc_copyb(&user,authuser,i)) temp_nomem();
660 }
661 if (!stralloc_0(&user)) pexit(111);
662 if ((i = str_chr(authuser,',')) && authuser[i] == ',') dnseen = 1;
663 if ((i = str_chr(authuser,'/')) && authuser[i] == '/') dnseen = 2; // old style
664 if (!env_put("USER",authuser)) pexit(111);
665
666 /* Read control file users/ldapauth and go for checks */
667
668 if (chdir(auto_qmail) == -1) pexit(110);
669
670 switch (control_readfile(&ldapcntl,"control/ldapbind",0)) {
671 case -1: pexit(110);
672 case 0: if (!constmap_init(&mapldapauth,"",0,1)) temp_nomem();
673 case 1: if (!constmap_init(&mapldapauth,ldapcntl.s,ldapcntl.len,1)) temp_nomem();
674 }
675
676 /* For a propper lookup we need to convert X.500 DNs to domain names;
677 dc= type style and old styles are supported */
678
679 if (dnseen) {
680 dn2domain(&domaindn,authuser,dnseen);
681 domain = domaindn.s;
682 }
683
684 /* Check for disabled authuser/domains - only working if user@domain given*/
685
686 if (!stralloc_copys(&disabled,"!")) temp_nomem();
687 if (!stralloc_catb(&disabled,authuser,authlen)) temp_nomem();
689
690 if (domlen) {
691 if (!stralloc_copys(&disabled,"!")) temp_nomem();
692 if (!stralloc_catb(&disabled,domain,domlen)) temp_nomem();
694 }
695
696 if (!ldaparam && domlen)
697 ldaparam = constmap(&mapldapauth,domain,domlen); // 1. ldap server by domain
698 if (!ldaparam)
699 ldaparam = constmap(&mapldapauth,"*",1); // 2. one ldap for all
700 if (dnseen) domain = 0; // only used for lookup in ldapbind
701
702 if (!ldaparam) pexit(1);
703
704 /* Evaluate LDAP lookup params:
705 p i s i i i u i i k
706 domain:Host;[s]port|Base:Scope|BindDN|BindPwd|Filter:UserAttr|CA|Cert:keyfile*/
707
708 i = 0;
709 int len = 0;
710 int paramlen = str_len(ldaparam);
711 if (!stralloc_copys(&bindhost,"localhost")) temp_nomem(); /* Default LDAP host */
712
713 i = str_chr(ldaparam,'|'); /* Host;Port */
714 len += i;
715 if (ldaparam[i] == '|' || len == paramlen) {
716 ldaparam[i] = 0;
717 p = str_chr(ldaparam,';'); /* Port */
718 if (ldaparam[p] == ';') {
719 ldaparam[p] = 0;
720 if (ldaparam[p + 1] == 's') { flagstls = 2; port = PORT_LDAPS; ++p; }
721 if (p < i) scan_ulong(ldaparam + p + 1,&port);
722 }
723 if (i) /* can be omitted */
724 if (!stralloc_copys(&bindhost,ldaparam)) temp_nomem();
725 }
726 if (!stralloc_0(&bindhost)) temp_nomem();
727
728 if (len < paramlen) {
729 ldaparam += i + 1;
730 i = str_chr(ldaparam,'|'); /* Base:Scope */
731 len += i + 1;
732 if (ldaparam[i] == '|' || len == paramlen) {
733 ldaparam[i] = 0;
734 s = str_chr(ldaparam,':'); /* Scope */
735 if (ldaparam[s] == ':') {
736 ldaparam[s] = 0;
737 if (s < i) scan_ulong(ldaparam + s + 1,&scope);
738 }
739 if (!stralloc_copys(&bindbase,ldaparam)) temp_nomem();
740 if (!stralloc_0(&bindbase)) temp_nomem();
741 }
742 }
743
744 if (len < paramlen) {
745 ldaparam += i + 1;
746 i = str_chr(ldaparam,'|'); /* Bind DN */
747 len += i + 1;
748 if (ldaparam[i] == '|' || len == paramlen) {
749 ldaparam[i] = 0;
750 if (!stralloc_copys(&binddn,ldaparam)) temp_nomem();
751 if (!stralloc_0(&binddn)) temp_nomem();
752 }
753 }
754
755 if (len < paramlen) {
756 ldaparam += i + 1;
757 i = str_chr(ldaparam,'|'); /* Bind PWD */
758 len += i + 1;
759 if (ldaparam[i] == '|' || len == paramlen) {
760 ldaparam[i] = 0;
761 if (!stralloc_copys(&bindpw,ldaparam)) temp_nomem();
762 if (!stralloc_0(&bindpw)) temp_nomem();
763 switch (get_password(&bindpw)) {
764 // case 0: log_error("pwd taken from ldapbind",0,"INFO"); break;
765 case -1: log_error("can't copy pwd",0,"FATAL"); pexit(111);
766 // default: log_error("pwd read from file",0,"INFO");
767 }
768 }
769 }
770
771 if (len < paramlen) {
772 ldaparam += i + 1; /* Filter */
773 i = str_chr(ldaparam,'|');
774 len += i + 1;
775 if (ldaparam[i] == '|' || len == paramlen) {
776 ldaparam[i] = 0;
777 u = str_chr(ldaparam,':'); /* User Attribute */
778 if (ldaparam[u] == ':') {
779 ldaparam[u] = 0;
780 if (!stralloc_copys(&userattr,ldaparam + u + 1)) temp_nomem();
781 if (!stralloc_0(&userattr)) temp_nomem();
782 }
783 if (!stralloc_copys(&filter,ldaparam)) temp_nomem();
784 if (!stralloc_0(&filter)) temp_nomem();
785 }
786 }
787
788 if (len < paramlen) {
789 ldaparam += i + 1;
790 i = str_chr(ldaparam,'|'); /* CA cert */
791 len += i + 1;
792 if (ldaparam[i] == '|' || len == paramlen) {
793 ldaparam[i] = 0;
794 if (ldaparam[i - 1] == '/') { /* CA dir/ */
795 if (!stralloc_copys(&cadir,ldaparam)) temp_nomem();
796 if (!stralloc_0(&cadir)) temp_nomem();
797 } else {
798 if (!stralloc_copys(&cafile,ldaparam)) temp_nomem();
799 if (!stralloc_0(&cafile)) temp_nomem();
800 }
801 }
802 }
803
804 if (len < paramlen) {
805 ldaparam += i + 1;
806 i = str_chr(ldaparam,'|'); /* Client cert */
807 len += i + 1;
808 if (ldaparam[i] == '|' || len == paramlen) {
809 ldaparam[i] = 0;
810 k = str_chr(ldaparam,':'); /* keyfile */
811 if (ldaparam[k] == ':') {
812 ldaparam[k] = 0;
813 if (!stralloc_copys(&keyfile,ldaparam + k + 1)) temp_nomem();
814 if (!stralloc_0(&keyfile)) temp_nomem();
815 }
816 if (!stralloc_copys(&certfile,ldaparam)) temp_nomem();
817 if (!stralloc_0(&certfile)) temp_nomem();
818 }
819 }
820
821 /* Get and read passwords from file or FDAUTH */
822
823 if (bindpw.s[0] == '*') {
824 read_passwd();
825 for (i = 0; i < bindpwds.len; i++) {
826 if (bindpwds.s[i] == '\0' || bindpwds.s[i] == ' ' ) {
827 if (!stralloc_copyb(&bindpw,bindpwds.s,i - 1)) temp_nomem();
828 if (!stralloc_0(&bindpw)) temp_nomem();
829 if (ldap_testbind(flagstls,bindhost.s,port) == LDAP_SUCCESS) break; // gotcha
830 bindpwds.s = bindpwds.s + i;
831 }
832 }
833 }
834
835 if (!stralloc_copys(&password,pwgiven)) temp_nomem();
836 if (!stralloc_0(&password)) temp_nomem();
837
838 if (flagauth)
839 ldap_authuser(flagstls,bindhost.s,port,domain,user.s,password.s,userattr.s,filter.s);
840 else if (flagprox)
841 ldap_proxyauth(flagstls,bindhost.s,port,authuser,password.s,userattr.s,filter.s);
842 else if (flaghome)
843 ldap_mboxuser(flagstls,bindhost.s,port,domain,user.s,password.s,userattr.s,filter.s);
844 else
845 ldap_mailaddr(flagstls,bindhost.s,port,authuser,filter.s);
846
847 for (i = 0; i < sizeof(authbuf); ++i) authbuf[i] = 0;
848
849 pathexec(argv);
850 pexit(111);
851}
char auto_qmail[]
int main()
Definition: chkshsgr.c:6
int constmap_init(struct constmap *cm, char *s, int len, int flagcolon)
Definition: constmap.c:35
int control_readfile(stralloc *sa, char *fn, int flagme)
Definition: control.c:87
int stralloc_copys(stralloc *, char const *)
void _exit(int)
void c(char *, char *, char *, int, int, int)
Definition: install.c:70
void p(char *, char *, int, int, int)
Definition: install.c:52
char host[256]
Definition: hostname.c:5
int match
Definition: matchup.c:196
stralloc homedir
int fd
stralloc filters
Definition: qmail-ldapam.c:72
#define FDPWD
Definition: qmail-ldapam.c:44
stralloc password
Definition: qmail-ldapam.c:71
unsigned long scope
Definition: qmail-ldapam.c:75
buffer ba
Definition: qmail-ldapam.c:49
stralloc disabled
Definition: qmail-ldapam.c:58
char authbuf[BUFSIZE_AUTH]
Definition: qmail-ldapam.c:48
#define PORT_LDAP
Definition: qmail-ldapam.c:45
stralloc domaindn
Definition: qmail-ldapam.c:70
stralloc ldapcntl
Definition: qmail-ldapam.c:57
stralloc bindpw
Definition: qmail-ldapam.c:63
stralloc user
Definition: qmail-ldapam.c:69
buffer bl
Definition: qmail-ldapam.c:54
stralloc bindpwds
Definition: qmail-ldapam.c:64
stralloc bindhost
Definition: qmail-ldapam.c:66
#define FDAUTH
Definition: qmail-ldapam.c:42
struct constmap mapldapauth
Definition: qmail-ldapam.c:56
char bspace[512]
Definition: qmail-ldapam.c:50
buffer bp
Definition: qmail-ldapam.c:51
int version
Definition: qmail-ldapam.c:76
stralloc userattr
Definition: qmail-ldapam.c:73
LDAP * ldap_setup(int flagtls, char *host, int port)
Definition: qmail-ldapam.c:215
char logbuf[BUFSIZE_SMALL]
Definition: qmail-ldapam.c:53
#define PORT_LDAPS
Definition: qmail-ldapam.c:46
#define FDLOG
Definition: qmail-ldapam.c:43
stralloc binddn
Definition: qmail-ldapam.c:62
#define WHO
Definition: qmail-ldapam.c:40
stralloc bindbase
Definition: qmail-ldapam.c:65
stralloc filter
Definition: qmail-ldapam.c:67
unsigned int port
stralloc home
Definition: qmail-pw2u.c:99
buffer in
Definition: qmail-pw2u.c:240
int flagauth
Definition: qmail-remote.c:60
int flagtls
Definition: qmail-remote.c:63
int j
Definition: qmail-send.c:926
stralloc mailfrom
Definition: qmail-smtpd.c:532
#define BUFSIZE_LINE
Definition: qmail.h:8
#define BUFSIZE_SMALL
Definition: qmail.h:12
#define BUFSIZE_AUTH
Definition: qmail.h:9
uint32_t k[64]
Definition: sha256.c:27
stralloc domain
Definition: spf.c:34
stralloc certfile
Definition: qmail-remote.c:430
stralloc keyfile
Definition: qmail-remote.c:431
stralloc cadir
Definition: qmail-remote.c:429
stralloc cafile
Definition: qmail-remote.c:428
void temp_nomem(void)
Definition: qmail-remote.c:149