1#define LDAP_DEPRECATED 1
36#define WHO "qmail-ldapam"
71unsigned long scope = LDAP_SCOPE_SUBTREE;
74static void log_error(
const char *
in,
const char *src,
const char *type)
77 buffer_put(&
bl,
": ",2);
78 if (type) { buffer_puts(&
bl,type); buffer_put(&
bl,
" ",1); }
80 if (src) { buffer_puts(&
bl,src); buffer_put(&
bl,
" ",1); }
81 buffer_put(&
bl,
"\n",1);
86static void pam_exit(
int fail)
95 buffer_puts(&
bl,
": FATAL: out of memory\n");
100static int get_password(stralloc *
in)
105 fd = open_read(
in->s);
106 if (
fd == -1)
return 0;
108 if (fstat(
fd,&st) == -1)
109 { log_error(
"unable to read: ",
in->s,
"FATAL"); pam_exit(111); }
110 if ((st.st_mode & 0477) != 0400)
111 { log_error(
"wrong file permissions for: ",
in->s,
"FATAL"); pam_exit(111); }
113 { log_error(
"unable to read: ",
in->s,
"FATAL"); pam_exit(111); }
116 if (!stralloc_0(
in))
return -1;
121static void read_passwd(
void)
128 { log_error(
"unable to read password",0,
"FATAL"); pam_exit(111); }
139static int dn2domain(stralloc *domain,
char *dn,
int flagdn)
147 int dnlen = str_len(dn);
150 if (flagdn == 1)
c =
',';
151 if (flagdn == 2)
c =
'/';
155 for (
int i = 0; i < dnlen; ++i) {
156 if (case_startb(dn + i,3,
"dc=")) {
157 j = str_chr(dn + i,c);
158 if (dn[i +
j] == c || dnlen == i +
j) {
159 if (!stralloc_catb(domain,dn + i + 3,
j - 3))
return -1;
161 if (!stralloc_cats(domain,
"."))
return -1;
166 if (!stralloc_0(domain))
return -1;
172 for (
int i = 0; i < dnlen; ++i) {
173 if (case_startb(dn + i,3,
"ou=")) {
174 j = str_chr(dn + i,c);
175 if (dn[i +
j] == c || dnlen == i +
j) ou = dn + i + 3;
177 if (case_startb(dn + i,2,
"o=")) {
178 k = str_chr(dn + i,c);
179 if (dn[i +
k] == c || dnlen == i +
k) org = dn + i + 2;
181 if (case_startb(dn + i,2,
"c=")) {
182 l = str_chr(dn + i,c);
183 if (dn[i + l] == c || dnlen == i + l) country = dn + i + 2;
188 if (org && *org && country && *country) {
190 if (!stralloc_catb(domain,ou,
j - 3))
return -1;
191 if (!stralloc_cats(domain,
"."))
return -1;
193 if (!stralloc_catb(domain,org,
k - 2))
return -1;
194 if (!stralloc_cats(domain,
"."))
return -1;
195 if (!stralloc_catb(domain,country,l))
return -1;
196 if (!stralloc_0(domain))
return -1;
198 if (
domain->len > 2)
return 2;
203static stralloc
cafile = {0};
204static stralloc
cadir = {0};
218 { log_error(
"unable to initialize LDAP bind",0,
"FATAL"); pam_exit(111); }
222 if (ldap_set_option(ld,LDAP_OPT_PROTOCOL_VERSION,(
void *)&
version) != LDAP_OPT_SUCCESS)
223 { log_error(
"unable to initialize LDAP vers 3 bind",0,
"FATAL"); pam_exit(111); }
228 if (ldap_start_tls_s(ld,NULL,NULL) != LDAP_SUCCESS)
229 { log_error(
"unable to connect via StartTLS to the LDAP server",0,
"FATAL"); pam_exit(111); }
232 if (ldap_set_option(ld,LDAP_OPT_X_TLS_CACERTFILE,(
void *)
cafile.s) != LDAP_OPT_SUCCESS)
233 { log_error(
"unable to set LDAP CA file: ",
cafile.s,
"FATAL"); pam_exit(111); }
236 if (ldap_set_option(ld,LDAP_OPT_X_TLS_CACERTDIR,(
void *)
cadir.s) != LDAP_OPT_SUCCESS)
237 { log_error(
"unable to set LDAP CA directory: ",
cadir.s,
"FATAL"); pam_exit(111); }
244 if (ldap_set_option(ld,LDAP_OPT_X_TLS_CERTFILE,
certfile.s) != LDAP_OPT_SUCCESS)
245 { log_error(
"unable to initialize LDAP vers 3 bind",0,
"FATAL"); pam_exit(111); }
246 if (ldap_set_option(ld,LDAP_OPT_X_TLS_KEYFILE,
keyfile.s) != LDAP_OPT_SUCCESS)
247 { log_error(
"unable to initialize LDAP vers 3 bind",0,
"FATAL"); pam_exit(111); }
249 if (ldap_install_tls(ld) != LDAP_SUCCESS)
250 { log_error(
"unable to connect via TLS for strong bind to the LDAP server",0,
"FATAL"); pam_exit(111); }
256static int ldap_testbind(
int flag,
char *
host,
unsigned long port)
263 if (r == LDAP_SUCCESS) ldap_unbind(ld);
276static int ldap_mailaddr(
int flag,
char *
host,
unsigned long port,
char *mailaddr,
char *
filter)
279 LDAPMessage *result, *entry;
280 char *search =
"mail";
285 r = str_len(mailaddr);
286 if (r == 0 || str_chr(mailaddr,
'@') == r)
287 { log_error(
"invalid mail address : ",mailaddr,
"ERROR"); pam_exit(2); }
291 if (ldap_simple_bind_s(ld,
binddn.s,
bindpw.s) != LDAP_SUCCESS)
292 { log_error(
"can't bind with LDAP server for mailaddr. DN:",
binddn.s,
"ERROR"); pam_exit(110); }
306 { log_error(
"search failed: ",ldap_err2string(r),
"ERROR"); pam_exit(110); }
308 entry = ldap_first_entry(ld,result);
309 if (!entry) { ldap_unbind(ld); pam_exit(1); }
311 dn = ldap_get_dn(ld,result);
312 mail = ldap_get_values(ld,entry,search);
313 addrok = ldap_count_values(mail);
315 ldap_msgfree(result);
318 if (!addrok) { log_error(
"mail address not found: ",mailaddr,
"INFO"); pam_exit(1); }
322 if (!env_put(
"AUTHUSER",dn)) pam_exit(111);
336static int ldap_authuser(
int flag,
char *
host,
unsigned long port,
char *domain,
char *
user,
char *pwd,
char *
id,
char *
filter)
339 LDAPMessage *result, *entry;
340 unsigned long scope = LDAP_SCOPE_BASE;
341 char *search =
"mail";
349 { log_error(
"Missing Mailfrom:<> for user: ",
user,
"FATAL"); pam_exit(2); }
355 if (domain && *domain) {
364 for (
int i = 0; i < str_len(domain); ++i) {
365 if (domain[i] ==
'.') {
374 if (ldap_simple_bind_s(ld,
binddn.s,pwd) != LDAP_SUCCESS)
375 { log_error(
"can't bind with LDAP server for auth. DN:",
binddn.s,
"INFO"); pam_exit(111); }
387 { log_error(
"search failed: ",ldap_err2string(r),
"ERROR"); pam_exit(110); }
389 entry = ldap_first_entry(ld,result);
390 if (!entry) { ldap_msgfree(result); ldap_unbind(ld); pam_exit(1); }
394 mail = ldap_get_values(ld,entry,search);
395 for (
int i = 0; i < ldap_count_values(mail); ++i)
396 if (case_equals(
mailfrom,mail[i])) addrok++;
398 ldap_msgfree(result);
402 if (!addrok) { log_error(
"mail address not found: ",
mailfrom,
"INFO"); pam_exit(1); }
406 if (!env_put(
"AUTHUSER",
binddn.s)) pam_exit(111);
427static int ldap_mboxuser(
int flag,
char *
host,
unsigned long port,
char *domain,
char *
user,
char *pwd,
char *
id,
char *
filter)
430 LDAPMessage *result, *entry;
431 unsigned long scope = LDAP_SCOPE_BASE;
432 char *search =
"homeDirectory";
440 if (domain && *domain) {
449 for (
int i = 0; i < str_len(domain); ++i) {
450 if (domain[i] ==
'.') {
459 if (ldap_simple_bind_s(ld,
binddn.s,pwd) != LDAP_SUCCESS)
460 { log_error(
"can't bind with LDAP server for home. DN:",
binddn.s,
"INFO"); pam_exit(111); }
472 { log_error(
"search failed: ",ldap_err2string(r),
"ERROR"); pam_exit(110); }
474 entry = ldap_first_entry(ld,result);
475 if (!entry) { ldap_msgfree(result); ldap_unbind(ld); pam_exit(1); }
476 homedir = ldap_get_values(ld,entry,search);
478 for (
int i = 0; i < ldap_count_values(
homedir); ++i) {
480 if (
fd == -1)
continue;
482 if (!env_put(
"HOME",
homedir[i])) pam_exit(111);
487 ldap_memfree(search);
492 { log_error(
"can't change to the home directory for DN:",
binddn.s,
"ERROR"); pam_exit(1); }
506static int ldap_proxyauth(
int flag,
char *
host,
unsigned long port,
char *mailaddr,
char *pwd,
char *
id,
char *
home)
510 stralloc binduser = {0};
512 int scope = LDAP_SCOPE_BASE;
521 i = str_chr(
binddn.s,
',');
522 if (case_starts(
binddn.s + i + 1,
"dc=") &&
id && *
id) {
524 if (!stralloc_cats(&binduser,
",cn="))
temp_nomem();
525 if (!stralloc_cats(&binduser,
id))
temp_nomem();
530 { log_error(
"can't setup proxy auth user for DN:",
binddn.s,
"ERROR"); pam_exit(110); }
532 if (ldap_simple_bind_s(ld,binduser.s,
bindpw.s) != LDAP_SUCCESS)
533 { log_error(
"can't bind with LDAP server for proxy. DN:",binduser.s,
"INFO"); pam_exit(111); }
542 r = ldap_search_s(ld,binduser.s,
scope,
filters.s,NULL,0,&result);
544 { log_error(
"UserPrincipalName not found in LDAP ",mailaddr,
"INFO"); pam_exit(1); }
550 j = str_chr(mailaddr,
'@');
552 if (!stralloc_catb(&binduser,mailaddr,
j))
temp_nomem();
553 if (!stralloc_cats(&binduser,
",cn="))
temp_nomem();
554 if (!stralloc_cats(&binduser,
id))
temp_nomem();
558 if (ldap_simple_bind_s(ld,binduser.s,pwd) != LDAP_SUCCESS)
559 { log_error(
"can't bind with LDAP server. DN:",binduser.s,
"INFO"); pam_exit(1); }
566 r = ldap_search_s(ld,binduser.s,
scope,
filters.s,NULL,0,&result);
568 { log_error(
"UserPincipalName rotten in LDAP",ldap_err2string(r),
"ERROR"); ldap_unbind(ld); pam_exit(110); }
570 if (!env_put(
"USER",mailaddr)) pam_exit(111);
580 if (
fd != -1 && chdir(
homedir.s) != -1) {
581 if (!env_put(
"HOME",
homedir.s)) pam_exit(111);
589 { log_error(
"can't change to the home directory of user:",mailaddr,
"ERROR"); pam_exit(1) ; }
591 if (!env_put(
"AUTHUSER",binduser.s)) pam_exit(111);
595int main(
int argc,
char *
const argv[])
615 while ((opt = getoptb(argc,(
char **)argv,
"ahps")) !=opteof)
618 case 'h': flaghome = 1;
break;
619 case 'p': flagprox = 1;
break;
620 case 's': flagstls = 1;
break;
632 }
while ((rc == -1) && (errno == EINTR));
633 if (rc == -1) pam_exit(111);
636 if (buflen >=
sizeof(
authbuf)) pam_exit(2);
641 if (i == buflen) pam_exit(2);
643 if (i == buflen) pam_exit(2);
645 if (i == buflen) pam_exit(2);
647 authlen = str_len(authuser);
650 if ((i = byte_rchr(authuser,authlen,
'@')))
651 if (i < authlen && authuser[i] ==
'@') {
652 domain = authuser + i + 1;
654 case_lowerb(
domain,domlen);
657 if (!stralloc_0(&
user)) pam_exit(111);
658 if ((i = str_chr(authuser,
',')) && authuser[i] ==
',') dnseen = 1;
659 if ((i = str_chr(authuser,
'/')) && authuser[i] ==
'/') dnseen = 2;
660 if (!env_put(
"USER",authuser)) pam_exit(111);
667 case -1: pam_exit(110);
676 dn2domain(&
domaindn,authuser,dnseen);
692 if (!ldaparam && domlen)
698 if (!ldaparam) pam_exit(1);
706 int paramlen = str_len(ldaparam);
709 i = str_chr(ldaparam,
'|');
711 if (ldaparam[i] ==
'|' || len == paramlen) {
713 p = str_chr(ldaparam,
';');
714 if (ldaparam[
p] ==
';') {
717 if (
p < i) scan_ulong(ldaparam +
p + 1,&
port);
724 if (len < paramlen) {
726 i = str_chr(ldaparam,
'|');
728 if (ldaparam[i] ==
'|' || len == paramlen) {
730 s = str_chr(ldaparam,
':');
731 if (ldaparam[s] ==
':') {
733 if (s < i) scan_ulong(ldaparam + s + 1,&
scope);
740 if (len < paramlen) {
742 i = str_chr(ldaparam,
'|');
744 if (ldaparam[i] ==
'|' || len == paramlen) {
751 if (len < paramlen) {
753 i = str_chr(ldaparam,
'|');
755 if (ldaparam[i] ==
'|' || len == paramlen) {
759 switch (get_password(&
bindpw)) {
761 case -1: log_error(
"can't copy pwd",0,
"FATAL"); pam_exit(111);
767 if (len < paramlen) {
769 i = str_chr(ldaparam,
'|');
771 if (ldaparam[i] ==
'|' || len == paramlen) {
773 u = str_chr(ldaparam,
':');
774 if (ldaparam[u] ==
':') {
784 if (len < paramlen) {
786 i = str_chr(ldaparam,
'|');
788 if (ldaparam[i] ==
'|' || len == paramlen) {
790 if (ldaparam[i - 1] ==
'/') {
800 if (len < paramlen) {
802 i = str_chr(ldaparam,
'|');
804 if (ldaparam[i] ==
'|' || len == paramlen) {
806 k = str_chr(ldaparam,
':');
807 if (ldaparam[
k] ==
':') {
821 for (i = 0; i <
bindpwds.len; i++) {
825 if (ldap_testbind(flagstls,
bindhost.s,
port) == LDAP_SUCCESS)
break;
int constmap_init(struct constmap *, char *, int, int)
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]