1#define LDAP_DEPRECATED 1
40#define WHO "qmail-ldapam"
75unsigned long scope = LDAP_SCOPE_SUBTREE;
78static void log_error(
const char *
in,
const char *src,
const char *type)
81 buffer_put(&
bl,
": ",2);
82 if (type) { buffer_puts(&
bl,type); buffer_put(&
bl,
" ",1); }
84 if (src) { buffer_puts(&
bl,src); buffer_put(&
bl,
" ",1); }
85 buffer_put(&
bl,
"\n",1);
90static void pexit(
int fail)
99 buffer_puts(&
bl,
": FATAL: out of memory\n");
104static int get_password(stralloc *
in)
109 fd = open_read(
in->s);
110 if (
fd == -1)
return 0;
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); }
117 { log_error(
"unable to read: ",
in->s,
"FATAL"); pexit(111); }
120 if (!stralloc_0(
in))
return -1;
125static void read_passwd(
void)
132 { log_error(
"unable to read password",0,
"FATAL"); pexit(111); }
143static int dn2domain(stralloc *domain,
char *dn,
int flagdn)
151 int dnlen = str_len(dn);
154 if (flagdn == 1)
c =
',';
155 if (flagdn == 2)
c =
'/';
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;
165 if (!stralloc_cats(domain,
"."))
return -1;
170 if (!stralloc_0(domain))
return -1;
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;
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;
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;
192 if (org && *org && country && *country) {
194 if (!stralloc_catb(domain,ou,
j - 3))
return -1;
195 if (!stralloc_cats(domain,
"."))
return -1;
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;
202 if (
domain->len > 2)
return 2;
207static stralloc
cafile = {0};
208static stralloc
cadir = {0};
222 { log_error(
"unable to initialize LDAP bind",0,
"FATAL"); pexit(111); }
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); }
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); }
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); }
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); }
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); }
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); }
260static int ldap_testbind(
int flag,
char *
host,
unsigned long port)
267 if (r == LDAP_SUCCESS) ldap_unbind(ld);
280static int ldap_mailaddr(
int flag,
char *
host,
unsigned long port,
char *mailaddr,
char *
filter)
283 LDAPMessage *result, *entry;
284 char *search =
"mail";
289 r = str_len(mailaddr);
290 if (r == 0 || str_chr(mailaddr,
'@') == r)
291 { log_error(
"invalid mail address : ",mailaddr,
"ERROR"); pexit(2); }
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); }
310 { log_error(
"search failed: ",ldap_err2string(r),
"ERROR"); pexit(110); }
312 entry = ldap_first_entry(ld,result);
313 if (!entry) { ldap_unbind(ld); pexit(1); }
315 dn = ldap_get_dn(ld,result);
316 mail = ldap_get_values(ld,entry,search);
317 addrok = ldap_count_values(mail);
319 ldap_msgfree(result);
322 if (!addrok) { log_error(
"mail address not found: ",mailaddr,
"INFO"); pexit(1); }
326 if (!env_put(
"AUTHUSER",dn)) pexit(111);
340static int ldap_authuser(
int flag,
char *
host,
unsigned long port,
char *domain,
char *
user,
char *pwd,
char *
id,
char *
filter)
343 LDAPMessage *result, *entry;
344 unsigned long scope = LDAP_SCOPE_BASE;
345 char *search =
"mail";
353 { log_error(
"Missing Mailfrom:<> for user: ",
user,
"FATAL"); pexit(2); }
359 if (domain && *domain) {
368 for (
int i = 0; i < str_len(domain); ++i) {
369 if (domain[i] ==
'.') {
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); }
391 { log_error(
"search failed: ",ldap_err2string(r),
"ERROR"); pexit(110); }
393 entry = ldap_first_entry(ld,result);
394 if (!entry) { ldap_msgfree(result); ldap_unbind(ld); pexit(1); }
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++;
402 ldap_msgfree(result);
406 if (!addrok) { log_error(
"mail address not found: ",
mailfrom,
"INFO"); pexit(1); }
410 if (!env_put(
"AUTHUSER",
binddn.s)) pexit(111);
431static int ldap_mboxuser(
int flag,
char *
host,
unsigned long port,
char *domain,
char *
user,
char *pwd,
char *
id,
char *
filter)
434 LDAPMessage *result, *entry;
435 unsigned long scope = LDAP_SCOPE_BASE;
436 char *search =
"homeDirectory";
444 if (domain && *domain) {
453 for (
int i = 0; i < str_len(domain); ++i) {
454 if (domain[i] ==
'.') {
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); }
476 { log_error(
"search failed: ",ldap_err2string(r),
"ERROR"); pexit(110); }
478 entry = ldap_first_entry(ld,result);
479 if (!entry) { ldap_msgfree(result); ldap_unbind(ld); pexit(1); }
480 homedir = ldap_get_values(ld,entry,search);
482 for (
int i = 0; i < ldap_count_values(
homedir); ++i) {
484 if (
fd == -1)
continue;
486 if (!env_put(
"HOME",
homedir[i]) pexit(111);
491 ldap_memfree(search);
496 { log_error(
"can't change to the home directory for DN:",
binddn.s,
"ERROR"); pexit(1); }
510static int ldap_proxyauth(
int flag,
char *
host,
unsigned long port,
char *mailaddr,
char *pwd,
char *
id,
char *
home)
514 stralloc binduser = {0};
516 int scope = LDAP_SCOPE_BASE;
525 i = str_chr(
binddn.s,
',');
526 if (case_starts(
binddn.s + i + 1,
"dc=") &&
id && *
id) {
528 if (!stralloc_cats(&binduser,
",cn="))
temp_nomem();
529 if (!stralloc_cats(&binduser,
id))
temp_nomem();
534 { log_error(
"can't setup proxy auth user for DN:",
binddn.s,
"ERROR"); pexit(110); }
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); }
546 r = ldap_search_s(ld,binduser.s,
scope,
filters.s,NULL,0,&result);
548 { log_error(
"UserPrincipalName not found in LDAP ",mailaddr,
"INFO"); pexit(1); }
554 j = str_chr(mailaddr,
'@');
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();
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); }
570 r = ldap_search_s(ld,binduser.s,
scope,
filters.s,NULL,0,&result);
572 { log_error(
"UserPincipalName rotten in LDAP",ldap_err2string(r),
"ERROR"); ldap_unbind(ld); pexit(110); }
574 if (!env_put(
"USER",mailaddr)) pexit(111);
584 if (
fd != -1 && chdir(
homedir.s) != -1) {
585 if (!env_put(
"HOME",
homedir.s) pexit(111);
593 { log_error(
"can't change to the home directory of user:",mailaddr,
"ERROR"); pexit(1) ; }
595 if (!env_put(
"AUTHUSER",binduser.s)) pexit(111);
599int main(
int argc,
char *
const argv[])
619 while ((opt = getoptb(argc,(
char **)argv,
"ahps")) !=opteof)
622 case 'h': flaghome = 1;
break;
623 case 'p': flagprox = 1;
break;
624 case 's': flagstls = 1;
break;
636 }
while ((rc == -1) && (errno == EINTR));
637 if (rc == -1) pexit(111);
640 if (buflen >=
sizeof(
authbuf)) pexit(2);
645 if (i == buflen) pexit(2);
647 if (i == buflen) pexit(2);
649 if (i == buflen) pexit(2);
651 authlen = str_len(authuser);
654 if ((i = byte_rchr(authuser,authlen,
'@')))
655 if (i < authlen && authuser[i] ==
'@') {
656 domain = authuser + i + 1;
658 case_lowerb(
domain,domlen);
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;
664 if (!env_put(
"USER",authuser)) pexit(111);
680 dn2domain(&
domaindn,authuser,dnseen);
696 if (!ldaparam && domlen)
702 if (!ldaparam) pexit(1);
710 int paramlen = str_len(ldaparam);
713 i = str_chr(ldaparam,
'|');
715 if (ldaparam[i] ==
'|' || len == paramlen) {
717 p = str_chr(ldaparam,
';');
718 if (ldaparam[
p] ==
';') {
721 if (
p < i) scan_ulong(ldaparam +
p + 1,&
port);
728 if (len < paramlen) {
730 i = str_chr(ldaparam,
'|');
732 if (ldaparam[i] ==
'|' || len == paramlen) {
734 s = str_chr(ldaparam,
':');
735 if (ldaparam[s] ==
':') {
737 if (s < i) scan_ulong(ldaparam + s + 1,&
scope);
744 if (len < paramlen) {
746 i = str_chr(ldaparam,
'|');
748 if (ldaparam[i] ==
'|' || len == paramlen) {
755 if (len < paramlen) {
757 i = str_chr(ldaparam,
'|');
759 if (ldaparam[i] ==
'|' || len == paramlen) {
763 switch (get_password(&
bindpw)) {
765 case -1: log_error(
"can't copy pwd",0,
"FATAL"); pexit(111);
771 if (len < paramlen) {
773 i = str_chr(ldaparam,
'|');
775 if (ldaparam[i] ==
'|' || len == paramlen) {
777 u = str_chr(ldaparam,
':');
778 if (ldaparam[u] ==
':') {
788 if (len < paramlen) {
790 i = str_chr(ldaparam,
'|');
792 if (ldaparam[i] ==
'|' || len == paramlen) {
794 if (ldaparam[i - 1] ==
'/') {
804 if (len < paramlen) {
806 i = str_chr(ldaparam,
'|');
808 if (ldaparam[i] ==
'|' || len == paramlen) {
810 k = str_chr(ldaparam,
':');
811 if (ldaparam[
k] ==
':') {
825 for (i = 0; i <
bindpwds.len; i++) {
829 if (ldap_testbind(flagstls,
bindhost.s,
port) == LDAP_SUCCESS)
break;
int constmap_init(struct constmap *cm, char *s, int len, int flagcolon)
int control_readfile(stralloc *sa, char *fn, int flagme)
int stralloc_copys(stralloc *, char const *)
void c(char *, char *, char *, int, int, int)
void p(char *, char *, int, int, int)
char authbuf[BUFSIZE_AUTH]
struct constmap mapldapauth
LDAP * ldap_setup(int flagtls, char *host, int port)
char logbuf[BUFSIZE_SMALL]