Merge branch 'upstream' into debian
[hcoop/debian/courier-authlib.git] / authldaplib.c
CommitLineData
d9898ee8 1/*
2 * authldap.c -
3 *
4 * courier-imap -
5 *
6 * Copyright 1999 Luc Saillard <luc.saillard@alcove.fr>.
7 *
8 * This module use a server LDAP to authenticate user.
9 * See the README.ldap
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; see the file COPYING. If not, write to
24 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 * Boston, MA 02111-1307, USA.
26 */
27
28/*
29 * Modified 28/11/2001 Iustin Pop <iusty@intensit.de>
30 * There was a bug regarding the LDAP_TLS option: if both LDAP_TLS
31 * and was LDAP_AUTHBIND were enabled, the ldap_start_tls function
32 * was called only for the first connection, resulting in the fact
33 * that the bind for checking the password was done without TLS,
34 * sending the password in clear text over the network. Detected
35 * when using OpenLDAP with "security ssf=128" (which disalows any
36 * clear-text communication).
37*/
38
39/*
40 Modified 01/21/2000 James Golovich <james@wwnet.net>
41
421. If LDAP_AUTHBIND is set in the config file, then the ldap server will
43handle passwords via authenticated binds, instead of checking these
44internally.
452. Changed paramaters for authldap_get to include pass.
46
47*/
48
49/*
50 Modified 12/31/99 Sam Varshavchik:
51
521. read_env reads from a configuration file, instead of the environment
532. read_config appropriately modified.
543. If 'user' contains the @ character, domain from config is NOT appended.
554. added 'homeDir' attribute. Use 'homeDir' instead of mailDir, and put
56 mailDir into MAILDIR=
575. read_config renamed to authldap_read_config
586. get_user_info renamed to authldap_get
597. Added authldap_free_config, to clean up all the allocated memory
60 (required for preauthldap).
618. Output LDAP attributes are defined in the configuration file as well.
629. Allow both plaintext and crypted passwords to be read from LDAP.
6310. Added GLOB_UID GLOB_GID, as well as UID and GID params.
64
652/19/2000 Sam.
66
67Rewrite to allow this code to be used in a long-running authentication daemon
68(for Courier). authldap_get renamed to authldapcommon, will initialize and
69maintain a persistent connection. Password checking moved entirely to
70authldap.c. authldapclose() will unbind and close the connection.
71
72connection gets closed and reopened automatically after a protocol error.
73
74error return from authldapcommon will indicate whether this is a transient,
75or a permanent failure.
76
77authldap_free_config removed - no longer required.
78
79*/
80
81#if HAVE_CONFIG_H
82#include "courier_auth_config.h"
83#endif
84#include <stdio.h>
85#include <stdlib.h>
86#include <ctype.h>
87#include <string.h>
88#include <errno.h>
89#include <pwd.h>
90#include <grp.h>
91#include <time.h>
92#if HAVE_UNISTD_H
93#include <unistd.h>
94#endif
95#if HAVE_LBER_H
96#include <lber.h>
97#endif
98#if HAVE_LDAP_H
99#include <ldap.h>
100#if LDAP_VENDOR_VERSION > 20000
101#define OPENLDAPV2
102#endif
103#endif
104#if HAVE_SYS_TIME_H
105#include <sys/time.h>
106#endif
107#if HAVE_SYS_STAT_H
108#include <sys/stat.h>
109#endif
110
111#include "authldap.h"
112#include "auth.h"
113#include "authldaprc.h"
114#include "courierauthdebug.h"
115
116#define err courier_auth_err
117
118#ifndef DEBUG_LDAP
119#define DEBUG_LDAP 0
120#endif
121
122#ifndef LDAP_OPT_SUCCESS
123#define LDAP_OPT_SUCCESS LDAP_SUCCESS
124#endif
125
126static char **l_get_values(LDAP *ld, LDAPMessage *entry, const char *attribut)
127{
128 struct berval **p=ldap_get_values_len(ld, entry, attribut);
0fde1ce3 129 int i, n;
d9898ee8 130 char **a;
131
132 if (!p)
133 return NULL;
134
135 n=ldap_count_values_len(p);
136
137
138 a=malloc((n + 1) * sizeof(char *));
139
140 if (!a)
141 {
142 DPRINTF("malloc failed");
143 ldap_value_free_len(p);
144 return NULL;
145 }
146
d9898ee8 147 for (i=0; i<n; i++)
148 {
149 if ((a[i]=malloc(p[i]->bv_len+1)) == NULL)
150 {
151 DPRINTF("malloc failed");
152 while (i--)
153 {
154 free(a[i]);
155 }
156 free(a);
157 ldap_value_free_len(p);
158 return NULL;
159 }
160
161 memcpy(a[i], p[i]->bv_val, p[i]->bv_len);
162 a[i][p[i]->bv_len]=0;
163 }
164
165 ldap_value_free_len(p);
166 a[i]=NULL;
167 return a;
168}
169
170static void l_value_free(char **p)
171{
172 int i;
173
174 for (i=0; p[i]; ++i)
175 free(p[i]);
176 free(p);
177}
178
179static int l_count_values(char **p)
180{
181 int i;
182
183 for (i=0; p[i]; ++i)
184 ;
185 return i;
186}
187
188static int l_unbind(LDAP *ld)
189{
190 return ldap_unbind_ext(ld, NULL, NULL);
191}
192
193static int l_simple_bind_s(LDAP *ld,
194 const char *who,
195 const char *passwd)
196{
197 struct berval cred;
198
199 cred.bv_len=passwd ? strlen(passwd):0;
200 cred.bv_val=(char *)passwd;
201
202 return ldap_sasl_bind_s(ld, who, NULL, &cred, NULL, NULL, NULL);
203}
204
205static int l_search_st(LDAP *ld,
206 const char *base,
207 int scope,
208 const char *filter,
209 char **attrs,
210 int attrsonly,
211 struct timeval *timeout,
212 LDAPMessage **res)
213{
214 return ldap_search_ext_s(ld, base, scope, filter, attrs,
215 attrsonly,
216 NULL, NULL,
217 timeout,
218 100,
219 res);
220}
221
222static int l_modify_s(LDAP *ld,
223 const char *dn,
224 LDAPMod **mods)
225{
226 return ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
227}
228
229static int l_search(LDAP *ld,
230 const char *base,
231 int scope,
232 const char *filter,
233 char **attrs,
234 int attrsonly)
235{
236 struct timeval tv;
237 int msgid;
238
239 tv.tv_sec=60*60;
240 tv.tv_usec=0;
241
242 if (ldap_search_ext(ld, base, scope, filter, attrs, attrsonly,
243 NULL, NULL, &tv, 1000000, &msgid) !=
244 LDAP_SUCCESS)
245 return -1;
246
247 return msgid;
248}
249
250struct ldap_info
251{
252 const char *uri;
253 const char *binddn;
254 const char *bindpw;
255 const char *basedn;
256 const char *mail;
257 const char *filter;
258 const char *enumerate_filter;
259 const char *domain;
260
261 uid_t uid;
262 gid_t gid;
263 int timeout;
264 int authbind;
265 int deref;
266 int protocol_version;
267 int tls;
268
269 const char *mailroot;
270
271 char **auxoptions;
272 char **auxnames;
273 const char **attrlist;
274
275 /* Optional emailmap to handle */
276
277 const char *emailmap;
278 const char *emailmap_basedn;
279 const char *emailmap_handle;
280 const char *emailmap_handle_lookup;
281};
282
283/*
284** There's a memory leak in OpenLDAP 1.2.11, presumably in earlier versions
285** too. See http://www.OpenLDAP.org/its/index.cgi?findid=864 for more
286** information. To work around the bug, the first time a connection fails
287** we stop trying for 60 seconds. After 60 seconds we kill the process,
288** and let the parent process restart it.
289**
290** We'll control this behavior via LDAP_MEMORY_LEAK. Set it to ZERO to turn
291** off this behavior (whenever OpenLDAP gets fixed).
292*/
293
294static time_t ldapfailflag=0;
295
296static void ldapconnfailure()
297{
298 const char *p=getenv("LDAP_MEMORY_LEAK");
299
300 if (!p)
301 {
302#ifdef LDAP_VENDOR_NAME
303#ifdef LDAP_VENDOR_VERSION
304#define DO_OPENLDAP_CHECK
305#endif
306#endif
307
308#ifdef DO_OPENLDAP_CHECK
309
310 /* It's supposed to be fixed in 20019 */
311
312 if (strcmp(LDAP_VENDOR_NAME, "OpenLDAP") == 0 &&
313 LDAP_VENDOR_VERSION < 20019)
314 p="1";
315 else
316 p="0";
317#else
318 p="0";
319#endif
320 }
321
322 if (atoi(p) && !ldapfailflag)
323 {
324 time(&ldapfailflag);
325 ldapfailflag += 60;
326 }
327}
328
329static int ldapconncheck()
330{
331 time_t t;
332
333 if (!ldapfailflag)
334 return (0);
335
336 time(&t);
337
338 if (t >= ldapfailflag)
339 exit(0);
340 return (1);
341}
342
343static int read_env(const char *env, const char **copy,
344 const char *errstr, int needit, const char *value_default);
345static void copy_value(LDAP *ld, LDAPMessage *entry, const char *attribut,
346 char **copy, const char *username);
347
348/*
349 * Function: read_env
350 * Copy the environnement $env and copy to $copy if not null
351 * if needit is false, and env doesn't exist, copy $value_default to $copy
352 * INPUT:
353 * $env: pointer to the environnement name
354 * $copy: where the value go
355 * $err: print a nice message when $env not_found and $needit is true
356 * $needit: if $needit is true and $value not found, return a error
357 * $value_default: the default value when $needit is false and $env doesn't exists
358 * OUTPUT:
359 * boolean
360 */
361static int read_env(const char *env, const char **copy,
362 const char *errstr, int needit, const char *value_default)
363{
364static char *ldapauth=0;
365static size_t ldapauth_size=0;
366size_t i;
367char *p=0;
368int l=strlen(env);
369
370 if (!ldapauth)
371 {
372 FILE *f=fopen(AUTHLDAPRC, "r");
373 struct stat buf;
374
375 if (!f) return (0);
376 if (fstat(fileno(f), &buf) ||
377 (ldapauth=malloc(buf.st_size+2)) == 0)
378 {
379 fclose(f);
380 return (0);
381 }
382 if (fread(ldapauth, buf.st_size, 1, f) != 1)
383 {
384 free(ldapauth);
385 ldapauth=0;
386 fclose(f);
387 return (0);
388 }
389 ldapauth[ldapauth_size=buf.st_size]=0;
390
391 for (i=0; i<ldapauth_size; i++)
392 if (ldapauth[i] == '\n')
393 ldapauth[i]=0;
394 }
395
396 for (i=0; i<ldapauth_size; )
397 {
398 p=ldapauth+i;
399 if (memcmp(p, env, l) == 0 &&
400 isspace((int)(unsigned char)p[l]))
401 {
402 p += l;
403 while (*p && *p != '\n' &&
404 isspace((int)(unsigned char)*p))
405 ++p;
406 break;
407 }
408
409 while (i < ldapauth_size)
410 if (ldapauth[i++] == 0) break;
411 }
412
413 if (i < ldapauth_size)
414 {
415 *copy= p;
416 return (1);
417 }
418
419 if (needit)
420 {
421 err("%s", errstr);
422 return 0;
423 }
424
425 *copy=0;
426 if (value_default)
427 *copy=value_default;
428
429 return 1;
430}
431
432/*
433 * Function: authldap_read_config
434 * Read Configuration from the environnement table
435 * INPUT:
436 * $ldap: a structure where we place information
437 * OUTPUT:
438 * boolean
439 */
440static int authldap_read_config(struct ldap_info *ldap)
441{
442 struct passwd *pwent;
443 struct group *grent;
444 const char *p;
445 size_t i, pass;
446
447 for (i=0; ldap->auxoptions && ldap->auxoptions[i]; i++)
448 free(ldap->auxoptions[i]);
449 for (i=0; ldap->auxnames && ldap->auxnames[i]; i++)
450 free(ldap->auxnames[i]);
451
452 if (ldap->attrlist)
453 free(ldap->attrlist);
454 if (ldap->auxnames)
455 free(ldap->auxnames);
456 if (ldap->auxoptions)
457 free(ldap->auxoptions);
458
459 memset(ldap,0,sizeof(struct ldap_info));
460
461 if (!read_env("LDAP_URI",&ldap->uri,
462 "You need to specify LDAP_URI in config file",1,NULL))
463 return 0;
464
465 if (!read_env("LDAP_AUTHBIND", &p, "", 0, ""))
466 return (0);
467
468 if (p)
469 sscanf(p,"%d",&ldap->authbind);
470
471 if (!read_env("LDAP_BASEDN",&ldap->basedn,
472 "You need to specify a basedn in config file",1,NULL))
473 return 0;
474 if (!read_env("LDAP_BINDDN",&ldap->binddn,
475 "You need to specify a BINDDN in config file",0,NULL))
476 return 0;
477 if (!read_env("LDAP_BINDPW",&ldap->bindpw,
478 "You need to specify a password for the BINDDN in config file",0,NULL))
479 return 0;
480 if (!read_env("LDAP_MAIL",&ldap->mail,
481 "You need to specify a attribute for mail in config file",0,"mail"))
482 return 0;
483 if (!read_env("LDAP_DOMAIN",&ldap->domain,
484 "You need to specify a domain for mail in config file",0,""))
485 return 0;
486
487 p=0;
488 ldap->uid=0;
489 if (!read_env("LDAP_GLOB_UID", &p, "", 0, ""))
490 return (0);
491
492 if (p && *p)
493 {
494 unsigned long n;
495
496 if (sscanf(p, "%lu", &n) == 1)
497 ldap->uid=(uid_t)n;
498 else
499 {
500 pwent=getpwnam(p);
501 if (!pwent)
502 {
503 err("authldap: INVALID LDAP_GLOB_UID");
504 return (0);
505 }
506 ldap->uid=pwent->pw_uid;
507 }
508 }
509
510 ldap->gid=0;
511 p=0;
512 if (!read_env("LDAP_GLOB_GID", &p, "", 0, ""))
513 return (0);
514
515 if (p && *p)
516 {
517 unsigned long n;
518
519 if (sscanf(p, "%lu", &n) == 1)
520 ldap->gid=(gid_t)n;
521 else
522 {
523 grent=getgrnam(p);
524 if (!grent)
525 {
526 err("authldap: INVALID LDAP_GLOB_GID");
527 return (0);
528 }
529 ldap->gid=grent->gr_gid;
530 }
531 }
532
533 ldap->timeout=5;
534 p=0;
535 if (read_env("LDAP_TIMEOUT", &p, "", 0, "") && p)
536 {
537 sscanf(p,"%d",&ldap->timeout);
538 }
539
540 ldap->tls=0;
541 p=0;
542 if (read_env("LDAP_TLS", &p, "", 0, "") && p)
543 {
544 ldap->tls=atoi(p);
545 }
546
547 ldap->filter=0;
548 p=0;
549 if (read_env("LDAP_FILTER", &p, "", 0, "") && p && strlen (p))
550 {
551 ldap->filter=p;
552 }
553
554 ldap->enumerate_filter=0;
555 p=0;
556 if (read_env("LDAP_ENUMERATE_FILTER", &p, "", 0, "") && p && strlen (p))
557 {
558 ldap->enumerate_filter=p;
559 }
560 else if (ldap->filter)
561 {
562 ldap->enumerate_filter=ldap->filter;
563 }
564 else
565 {
566 ldap->enumerate_filter = malloc(strlen(ldap->mail)+3);
567 if (!ldap->enumerate_filter)
568 {
569 perror("CRIT: authldap: malloc failed");
570 return 0;
571 }
572 sprintf((char *)ldap->enumerate_filter, "%s=*", ldap->mail);
573 }
574
575 ldap->deref = LDAP_DEREF_NEVER;
576 ldap->protocol_version = 0; /* use API default */
577 p=0;
578 if (!read_env("LDAP_DEREF", &p, "", 0, ""))
579 return (0);
580 if (p)
581 {
582#ifndef LDAP_OPT_DEREF
583 err("authldaplib: LDAP_OPT_DEREF not available, ignored");
584#endif
585 if (!strcasecmp (p, "never"))
586 ldap->deref = LDAP_DEREF_NEVER;
587 else if (!strcasecmp (p, "searching"))
588 ldap->deref = LDAP_DEREF_SEARCHING;
589 else if (!strcasecmp (p, "finding"))
590 ldap->deref = LDAP_DEREF_FINDING;
591 else if (!strcasecmp (p, "always"))
592 ldap->deref = LDAP_DEREF_ALWAYS;
593 }
594
595 if (!read_env("LDAP_PROTOCOL_VERSION", &p, 0, 0, 0))
596 return (0);
597 if (p)
598 {
599 int lpv = atoi(p);
600#ifndef LDAP_OPT_PROTOCOL_VERSION
601 err("authldaplib: LDAP_OPT_PROTOCOL_VERSION not available, ignored");
602#endif
603 if (lpv == 0
604#ifdef LDAP_VERSION_MIN
605 || lpv < LDAP_VERSION_MIN
606#endif
607#ifdef LDAP_VERSION_MAX
608 || lpv > LDAP_VERSION_MAX
609#endif
610 )
611 err("authldaplib: illegal protocol version ignored");
612 else
613 ldap->protocol_version = lpv;
614 }
615
616 if (!read_env("LDAP_MAILROOT",&ldap->mailroot,"",0,NULL)
617 || ldap->mailroot == NULL || ldap->mailroot[0] == 0)
618 ldap->mailroot=NULL;
619
620 if (!read_env("LDAP_EMAILMAP", &ldap->emailmap, "", 0, "") ||
621 !read_env("LDAP_EMAILMAP_BASEDN", &ldap->emailmap_basedn, "", 0, "") ||
622 !read_env("LDAP_EMAILMAP_ATTRIBUTE", &ldap->emailmap_handle, "", 0, "")||
623 !read_env("LDAP_EMAILMAP_MAIL",
624 &ldap->emailmap_handle_lookup, "", 0, ""))
625 return (0);
626
627
628 for (pass=0; pass<2; pass++)
629 {
630 if (pass)
631 {
632 if ((ldap->auxnames=malloc((i+1)*sizeof(char *)))
633 == NULL ||
634 (ldap->auxoptions=malloc((i+1)*sizeof(char *)))
635 == NULL)
636 {
637 perror("CRIT: authldap: malloc failed");
638 if (ldap->auxnames)
639 ldap->auxnames[0]=0;
640 return 0;
641 }
642 }
643 i=0;
644
645 if (pass)
646 {
647 ldap->auxnames[0]=NULL;
648 ldap->auxoptions[0]=NULL;
649 }
650
651 if (!read_env("LDAP_AUXOPTIONS", &p, "", 0, NULL)
652 || p == NULL || *p == 0)
653 p=NULL;
654
655 while (p && *p)
656 {
657 size_t n;
658
659 if (*p == ',')
660 {
661 ++p;
662 continue;
663 }
664
665 for (n=0; p[n] && p[n] != ',' && p[n] != '='; n++)
666 ;
667
668 if (pass)
669 {
670 if ((ldap->auxoptions[i]=malloc(n+1)) == NULL)
671 {
672 perror("CRIT: authldap: malloc failed");
673 return 0;
674 }
675
676 memcpy(ldap->auxoptions[i], p, n);
677 ldap->auxoptions[i][n]=0;
678 ldap->auxoptions[i+1]=NULL;
679 }
680
681 p += n;
682
683 if (*p == '=') ++p;
684
685 for (n=0; p[n] && p[n] != ','; n++)
686 ;
687
688 if (pass)
689 {
690 if (n == 0)
691 {
692 if ((ldap->auxnames[i]=
693 strdup(ldap->auxoptions[i]))
694 == NULL)
695 {
696 perror("CRIT: authldap: malloc failed");
697 return 0;
698 }
699 }
700 else if ((ldap->auxnames[i]=malloc(n+1)) == NULL)
701 {
702 perror("CRIT: authldap: malloc failed");
703 return 0;
704 }
705 else
706 {
707 memcpy(ldap->auxnames[i], p, n);
708 ldap->auxnames[i][n]=0;
709 ldap->auxnames[i+1]=NULL;
710 }
711 }
712 p += n;
713 ++i;
714 }
715 }
716
717 if ((ldap->attrlist=malloc((i+20)*sizeof(const char *))) == NULL)
718 {
719 perror("CRIT: authldap: malloc failed");
720 return 0;
721 }
722
723 return 1;
724}
725
726static void get_error(LDAP *ld, LDAPMessage *entry,
727 const char *func,
728 const char *attribut)
729{
730#if HAVE_LDAP_PARSE_RESULT
731
732 int errcode;
733 char *nmatched;
734 char *errmsg;
735
736
737 if (ldap_parse_result(ld, entry, &errcode, &nmatched,
738 &errmsg, NULL, NULL, 0)
739 != LDAP_SUCCESS)
740 {
741 DPRINTF("ldap_parseresult failed");
742 }
743 else
744 {
745 if (errcode && errcode != LDAP_DECODING_ERROR &&
746 errcode != LDAP_NO_RESULTS_RETURNED)
747 {
748 DPRINTF("get_values attribute %s: %s",
749 attribut,
750 errmsg ? errmsg:"unknown error");
751 }
752
753 if (errmsg)
754 ldap_memfree(errmsg);
755 if (nmatched)
756 ldap_memfree(nmatched);
757 }
758#else
759#if HAVE_LDAP_RESULT2ERROR
760 int ld_errno = ldap_result2error(ld,entry,0);
761 if (ld_errno && ld_errno != LDAP_DECODING_ERROR
762 && ld_errno != LDAP_NO_RESULTS_RETURNED)
763 {
764 DPRINTF("get_values attribute %s: %s", attribut,
765 ldap_err2string(ld_errno));
766 }
767#else
768 if (ld->ld_errno != LDAP_DECODING_ERROR
769 && ld->ld_errno != LDAP_NO_RESULTS_RETURNED)
770 {
771 DPRINTF("get_values attribute %s: %s", attribut,
772 ldap_err2string(ld->ld_errno));
773 }
774#endif
775#endif
776}
777
778static char **get_values(LDAP *ld, LDAPMessage *entry, const char *attribut)
779{
780 char ** values;
781 values=l_get_values(ld,entry, (char *)attribut);
782
783 if (values==NULL)
784 {
785 get_error(ld, entry, "get_values", attribut);
786 }
787
788 return values;
789}
790
791
792
793/*
794 * Function: copy_value
795 * Copy value from a LDAP attribute to $copy
796 * INPUT:
797 * $ld: the connection with the LDAP server
798 * $entry: the entry who contains attributes
799 * $attribut: this attribut
800 * $copy: where data can go
801 * OUTPUT:
802 * void
803 */
804static void copy_value(LDAP *ld, LDAPMessage *entry, const char *attribut,
805 char **copy, const char *username)
806{
807 char ** values;
808 values=l_get_values(ld,entry, attribut);
809
810 if (values==NULL)
811 {
812 get_error(ld, entry, "copy_value ", attribut);
813 *copy=NULL;
814 return;
815 }
816 /* We accept only attribute with one value */
817 else if (l_count_values(values)>1)
818 {
819 *copy=strdup(values[0]);
820 fprintf(stderr, "WARN: authldaplib: duplicate attribute %s for %s\n",
821 attribut,
822 username);
823 *copy=NULL;
824 }
825 /* We accept only attribute with one value */
826 else if (l_count_values(values)!=1)
827 {
828 *copy=NULL;
829 }
830 else
831 {
832 *copy=strdup(values[0]);
833 }
834#if DEBUG_LDAP
835 DPRINTF("copy_value %s: %s",attribut,values[0]);
836#endif
837 l_value_free(values);
838}
839
840static struct ldap_info my_ldap;
841static LDAP *my_ldap_fp=0;
842static LDAP *bindp=0; /* for checking passwords with AUTHBIND */
843
844void authldapclose()
845{
846 if (my_ldap_fp)
847 {
848 l_unbind(my_ldap_fp);
849 my_ldap_fp=0;
850 }
851 if (bindp)
852 {
853 l_unbind(bindp);
854 bindp=0;
855 }
856}
857
858static int ldaperror(int rc)
859{
860#ifdef OPENLDAPV2
861 if (rc && !LDAP_NAME_ERROR(rc))
862#else
863 if (rc && !NAME_ERROR(rc))
864#endif
865 {
866 /* If there was a protocol error, close the connection */
867 authldapclose();
868 ldapconnfailure();
869 }
870 return (rc);
871}
872
873/* This function takes a ldap connection and
874 * tries to enable TLS on it.
875*/
876static int enable_tls_on(LDAP *conn) {
877#if HAVE_LDAP_TLS
878 int version;
879 int ldrc;
880
881 if (ldaperror(ldrc=ldap_get_option (conn,
882 LDAP_OPT_PROTOCOL_VERSION,
883 &version))
884 != LDAP_SUCCESS)
885 {
886 const char *s=ldap_err2string(ldrc);
887
888 err("ldap_get_option failed: %s", s);
889 return (-1);
890 }
891
892 if (version < LDAP_VERSION3)
893 {
894 version = LDAP_VERSION3;
895 (void)ldap_set_option (conn,
896 LDAP_OPT_PROTOCOL_VERSION,
897 &version);
898 }
899
900 if (ldaperror(ldrc=ldap_start_tls_s(conn, NULL, NULL))
901 != LDAP_SUCCESS)
902 {
903 const char *s=ldap_err2string(ldrc);
904
905 err("ldap_start_tls_s failed: %s", s);
906 return (-1);
907 }
908 return 0;
909#else
910 err("authldaplib: TLS not available");
911 return (-1);
912#endif
913}
914
915static LDAP *ldapconnect()
916{
917LDAP *p=NULL;
918
919#if DEBUG_LDAP
920 DPRINTF("URI: %s",my_ldap.uri);
921 DPRINTF("UID: %d",my_ldap.uid);
922 DPRINTF("GID: %d",my_ldap.gid);
923#endif
924
925 if (ldapconncheck())
926 {
927 DPRINTF("authldaplib: timing out after failed connection");
928 return (NULL);
929 }
930
931 ldap_initialize(&p, my_ldap.uri);
932
933 if (p==NULL)
934 {
935 err("cannot connect to LDAP server (%s): %s",
936 my_ldap.uri, strerror(errno));
937 ldapconnfailure();
938 }
939#ifdef LDAP_OPT_NETWORK_TIMEOUT
940 if (my_ldap.timeout > 0)
941 ldap_set_option (p, LDAP_OPT_NETWORK_TIMEOUT, &my_ldap.timeout);
942#endif
943#if DEBUG_LDAP
944 DPRINTF("ldapconnect end");
945#endif
946 return (p);
947}
948
949static int ldapopen()
950{
951int ldrc;
952
953 if (my_ldap_fp) return (0);
954
955 if (authldap_read_config(&my_ldap) == 0)
956 {
957 err("authldaplib: error in LDAP configuration file, aborting");
958 return (1);
959 }
960
961 my_ldap_fp=ldapconnect();
962
963 if (!my_ldap_fp)
964 {
965 return (1);
966 }
967
968#ifdef LDAP_OPT_PROTOCOL_VERSION
969
970 /* Set protocol version if selected */
971 if (my_ldap.protocol_version &&
972 ldaperror(ldrc = ldap_set_option(my_ldap_fp, LDAP_OPT_PROTOCOL_VERSION,
973 (void *) & my_ldap.protocol_version)) != LDAP_SUCCESS)
974 {
975 const char *s=ldap_err2string(ldrc);
976
977 err("ldap_set_option(PROTOCOL_VERSION %d) failed: %s",
978 my_ldap.protocol_version, s);
979 authldapclose();
980 ldapconnfailure();
981 return (-1);
982 }
983 if (my_ldap.protocol_version)
984 {
985 DPRINTF("selected ldap protocol version %d", my_ldap.protocol_version);
986 }
987#endif
988
989 if (my_ldap.tls && enable_tls_on(my_ldap_fp))
990 {
991 authldapclose();
992 ldapconnfailure();
993 return (-1);
994 }
995
996#ifdef LDAP_OPT_DEREF
997
998 /* Set dereferencing mode */
999 if (ldaperror(ldrc = ldap_set_option(my_ldap_fp, LDAP_OPT_DEREF,
1000 (void *) & my_ldap.deref)) != LDAP_SUCCESS)
1001 {
1002 const char *s=ldap_err2string(ldrc);
1003
1004 err("ldap_set_option(DEREF) failed: %s", s);
1005 authldapclose();
1006 ldapconnfailure();
1007 return (-1);
1008 }
1009#endif
1010
1011 /* Bind to server */
1012 if (courier_authdebug_login_level >= 2)
1013 {
1014 DPRINTF("binding to LDAP server as DN '%s', password '%s'",
1015 my_ldap.binddn ? my_ldap.binddn : "<null>",
1016 my_ldap.bindpw ? my_ldap.bindpw : "<null>");
1017 }
1018 else
1019 {
1020 DPRINTF("binding to LDAP server as DN '%s'",
1021 my_ldap.binddn ? my_ldap.binddn : "<null>");
1022 }
1023
1024 if (ldaperror(ldrc = l_simple_bind_s(my_ldap_fp,
1025 my_ldap.binddn,
1026 my_ldap.bindpw)) != LDAP_SUCCESS)
1027 {
1028 const char *s=ldap_err2string(ldrc);
1029
1030 err("ldap_simple_bind_s failed: %s", s);
1031 authldapclose();
1032 ldapconnfailure();
1033 return (-1);
1034 }
1035 return (0);
1036}
1037
1038static int auth_ldap_do(const char *, const char *, const char *,
1039 int (*)(struct authinfo *, void *),
1040 void *arg, const char *);
1041
1042int auth_ldap_changepw(const char *dummy, const char *user,
1043 const char *pass,
1044 const char *newpass)
1045{
1046 return auth_ldap_do("authlib", user, pass, NULL, NULL, newpass);
1047}
1048
1049/*
1050 * Function: authldapcommon
1051 * Get information from the LDAP server ($ldap) for this $user
1052 * INPUT:
1053 * $user: the login name
1054 * $pass: the login password (NULL if we don't want to check the pw)
1055 * callback - callback function with filled in authentication info
1056 * arg - extra argument for the callback function.
1057 * OUTPUT:
1058 * < 0 - authentication failure
1059 * > 0 - temporary failure
1060 * else return code from the callback function.
1061 */
1062
1063int authldapcommon(const char *service,
1064 const char *user, const char *pass,
1065 int (*callback)(struct authinfo *, void *),
1066 void *arg)
1067{
1068 return (auth_ldap_do(service, user, pass, callback, arg, NULL));
1069}
1070
1071static int auth_ldap_do2(const char *service,
1072 const char *user, const char *pass,
1073 int (*callback)(struct authinfo *, void *),
1074 void *arg, const char *newpass);
1075
1076static char *escape_str(const char *);
1077
1078static int auth_ldap_retry(const char *service,
1079 const char *user, const char *pass,
1080 int (*callback)(struct authinfo *, void *),
1081 void *arg, const char *newpass);
1082
1083static int auth_ldap_do(const char *service,
1084 const char *user, const char *pass,
1085 int (*callback)(struct authinfo *, void *),
1086 void *arg, const char *newpass)
1087{
1088 int rc=auth_ldap_retry(service, user, pass, callback, arg, newpass);
1089
1090 if (rc > 0)
1091 rc=auth_ldap_retry(service, user, pass, callback, arg,
1092 newpass);
1093
1094 return rc;
1095}
1096
1097static int auth_ldap_retry(const char *service,
1098 const char *user, const char *pass,
1099 int (*callback)(struct authinfo *, void *),
1100 void *arg, const char *newpass)
1101{
1102 char *q;
1103 int i;
1104
1105 q=escape_str(user);
1106
1107 if (!q)
1108 return (auth_ldap_do2(service,
1109 user, pass, callback, arg, newpass));
1110
1111 i=auth_ldap_do2(service, q, pass, callback, arg, newpass);
1112 free(q);
1113 return (i);
1114}
1115
1116
1117static int auth_ldap_do3(const char *service,
1118 const char *attrname,
1119 const char *user, const char *pass,
1120 int (*callback)(struct authinfo *, void *),
1121 void *arg, const char *newpass, const char *authaddr);
1122
1123static char *emailmap_get_search_string(const char *str, const char *email);
1124
1125static int auth_ldap_do2(const char *service,
1126 const char *user, const char *pass,
1127 int (*callback)(struct authinfo *, void *),
1128 void *arg, const char *newpass)
1129{
1130 char *srch;
1131 struct timeval tv;
1132 const char *attributes[2];
1133 LDAPMessage *result, *entry;
1134 int cnt;
1135 char *v;
1136 const char *aname;
1137
1138 if (ldapopen()) return (1);
1139
1140 if (my_ldap.emailmap[0] == 0 || strchr(user, '@') == NULL)
1141 return (auth_ldap_do3(service, my_ldap.mail,
1142 user, pass, callback, arg, newpass,
1143 user));
1144 /* Mapping is not enabled */
1145
1146 srch=emailmap_get_search_string(my_ldap.emailmap, user);
1147
1148 if (!srch)
1149 {
1150 perror("CRIT: authldaplib: malloc");
1151 exit(1);
1152 }
1153 DPRINTF("using emailmap search: %s", srch);
1154
1155 tv.tv_sec=my_ldap.timeout;
1156 tv.tv_usec=0;
1157
1158 attributes[0]=my_ldap.emailmap_handle;
1159
1160 if (!attributes[0][0])
1161 attributes[0]="handle";
1162 attributes[1]=NULL;
1163
1164 if (ldaperror(l_search_st(my_ldap_fp,
1165 (char *)(my_ldap.emailmap_basedn[0] ?
1166 my_ldap.emailmap_basedn
1167 :my_ldap.basedn),
1168 LDAP_SCOPE_SUBTREE,
1169 srch, (char **)attributes, 0,
1170 &tv, &result))
1171 != LDAP_SUCCESS)
1172 {
1173 free(srch);
1174
1175 DPRINTF("ldap_search_st failed");
1176 if (my_ldap_fp) return (-1);
1177 return (1);
1178 }
1179
1180 if ((cnt=ldap_count_entries(my_ldap_fp, result)) != 1)
1181 {
1182 free(srch);
1183
1184 if (cnt)
1185 err("emailmap: %d entries returned from search %s (but we need exactly 1)",
1186 cnt, srch);
1187 ldap_msgfree(result);
1188 return -1;
1189 }
1190 free(srch);
1191
1192 entry=ldap_first_entry(my_ldap_fp, result);
1193
1194 if (!entry)
1195 {
1196 ldap_msgfree(result);
1197
1198 err("authldap: unexpected NULL from ldap_first_entry");
1199 return -1;
1200 }
1201
1202 copy_value(my_ldap_fp, entry, attributes[0], &v, user);
1203
1204 if (!v)
1205 {
1206 DPRINTF("emailmap: empty attribute");
1207 ldap_msgfree(result);
1208 return (-1);
1209 }
1210
1211 aname=my_ldap.emailmap_handle_lookup;
1212 if (aname[0] == 0)
1213 aname=my_ldap.mail;
1214
1215 DPRINTF("emailmap results: aname=%s, handle=%s", aname, v);
1216
1217 cnt=auth_ldap_do3(service,
1218 aname, v, pass, callback, arg, newpass, user);
1219
1220 ldap_msgfree(result);
1221 free(v);
1222 return (cnt);
1223}
1224
1225static int auth_ldap_do3(const char *service,
1226 const char *attrname,
1227 const char *user, const char *pass,
1228 int (*callback)(struct authinfo *, void *),
1229 void *arg, const char *newpass,
1230 const char *authaddr)
1231{
1232 char *newpass_crypt=0;
1233 const char *attributes[10];
1234 struct timeval timeout;
1235
1236 LDAPMessage *result;
1237 LDAPMessage *entry;
1238 char *filter, *dn;
1239 int i, j;
1240
1241 struct authinfo auth;
1242 char *homeDir=0;
1243 char *mailDir=0;
1244 char *userPassword=0;
1245 char *cryptPassword=0;
1246 char *options=0;
1247 char *cn=0;
1248 uid_t au;
1249 gid_t ag;
1250 int rc;
1251 char *quota=0;
1252 int additionalFilter = 0;
1253 int hasAdditionalFilter = 0;
1254
1255 hasAdditionalFilter = my_ldap.filter != 0;
1256
1257 memset(&auth, 0, sizeof(auth));
1258
1259 if (hasAdditionalFilter)
1260 {
1261 /* To add the additional filter, we need to add on the
1262 * additional size for "(&)" and the other filter. So
1263 * filter+3
1264 */
1265 additionalFilter = strlen(my_ldap.filter) + 3;
1266 }
1267
1268 if ((filter=malloc(additionalFilter+strlen(attrname)+strlen(user)+
1269 (my_ldap.domain ? strlen(my_ldap.domain):0)+
1270 sizeof ("(=@)"))) == 0)
1271 {
1272 perror("malloc");
1273 return 1;
1274 }
1275 strcpy(filter, "\0");
1276
1277 if (hasAdditionalFilter)
1278 {
1279 strcat(filter, "(&");
1280 strcat(filter, my_ldap.filter);
1281 }
1282
1283 strcat(strcat(strcat(strcat(filter, "("), attrname), "="), user);
1284 if ( my_ldap.domain && my_ldap.domain[0] && strchr(user, '@') == 0 )
1285 strcat(strcat(filter, "@"), my_ldap.domain);
1286 strcat(filter, ")");
1287
1288 if (hasAdditionalFilter)
1289 {
1290 strcat(filter, ")");
1291 }
1292
1293 DPRINTF("using search filter: %s", filter);
1294
1295 timeout.tv_sec=my_ldap.timeout;
1296 timeout.tv_usec=0;
1297
1298 read_env("LDAP_HOMEDIR", &attributes[0], "", 0, "homeDir");
1299 read_env(service && strcmp(service, "courier") == 0
1300 ? "LDAP_DEFAULTDELIVERY":"LDAP_MAILDIR",
1301 &attributes[1], "", 0, 0);
1302 read_env("LDAP_FULLNAME", &attributes[2], "", 0, "cn");
1303 read_env("LDAP_CLEARPW", &attributes[3], "", 0, 0);
1304 read_env("LDAP_CRYPTPW", &attributes[4], "", 0, 0);
1305 read_env("LDAP_UID", &attributes[5], "", 0, 0);
1306 read_env("LDAP_GID", &attributes[6], "", 0, 0);
1307 attributes[7]=my_ldap.mail;
1308 read_env("LDAP_MAILDIRQUOTA", &attributes[8], "", 0, 0);
1309
1310 j=0;
1311 for (i=0; i<9; i++)
1312 {
1313 if (attributes[i])
1314 my_ldap.attrlist[j++]=attributes[i];
1315 }
1316
1317 for (i=0; my_ldap.auxoptions[i]; i++)
1318 my_ldap.attrlist[j++]=my_ldap.auxoptions[i];
1319
1320 my_ldap.attrlist[j]=0;
1321
1322 if (ldaperror(l_search_st(my_ldap_fp,
1323 (char *)my_ldap.basedn,LDAP_SCOPE_SUBTREE,
1324 filter, (char **)my_ldap.attrlist, 0,
1325 &timeout, &result))
1326 != LDAP_SUCCESS)
1327 {
1328 DPRINTF("ldap_search_st() failed");
1329 free(filter);
1330
1331 if (my_ldap_fp) return (-1);
1332 return (1);
1333 }
1334
1335 free(filter);
1336
1337 /* If we are more than one result, reject */
1338 if (ldap_count_entries(my_ldap_fp,result)!=1)
1339 {
1340 DPRINTF("number of entries returned: %d (but we need exactly 1)",
1341 ldap_count_entries(my_ldap_fp,result));
1342 ldap_msgfree(result);
1343 return -1;
1344 }
1345
1346 dn = ldap_get_dn(my_ldap_fp, result);
1347
1348 DPRINTF("one entry returned, DN: %s", dn ? dn : "<null>");
1349
1350 if (dn == NULL)
1351 {
1352 DPRINTF("ldap_get_dn failed");
1353 return -1;
1354 }
1355
1356 /* Get the pointer on this result */
1357 entry=ldap_first_entry(my_ldap_fp,result);
1358 if (entry==NULL)
1359 {
1360 DPRINTF("ldap_first_entry failed");
1361 free(dn);
1362 return -1;
1363 }
1364
1365#if DEBUG_LDAP
1366 DPRINTF("after ldap_first_entry");
1367#endif
1368
1369 /* print all the raw attributes */
1370 if (courier_authdebug_login_level >= 2)
1371 {
1372 char *attr;
1373 BerElement *berptr = 0;
1374
1375 attr = ldap_first_attribute(my_ldap_fp, entry, &berptr);
1376 if (attr)
1377 {
1378 DPRINTF("raw ldap entry returned:");
1379 }
1380
1381 while (attr)
1382 {
1383 char **av, **ap;
1384
1385 av = l_get_values(my_ldap_fp, entry, attr);
1386 ap = av;
1387 if (av)
1388 {
1389 while(*ap)
1390 {
1391 DPRINTF("| %s: %s", attr, *ap);
1392 ap++;
1393 }
1394 l_value_free(av);
1395 }
1396 ldap_memfree(attr);
1397 attr = ldap_next_attribute(my_ldap_fp, entry, berptr);
1398 }
1399
1400 ber_free(berptr, 0);
1401 }
1402
1403 /* Copy the directory and the password into struct */
1404 copy_value(my_ldap_fp,entry,attributes[0],&homeDir, user);
1405 if (attributes[1])
1406 copy_value(my_ldap_fp,entry,attributes[1],&mailDir, user);
1407 copy_value(my_ldap_fp,entry,attributes[2],&cn, user);
1408 if (attributes[3])
1409 copy_value(my_ldap_fp,entry,attributes[3],&userPassword, user);
1410 if (attributes[4])
1411 copy_value(my_ldap_fp,entry,attributes[4],&cryptPassword, user);
1412
1413 au=my_ldap.uid;
1414 ag=my_ldap.gid;
1415 if (attributes[5])
1416 {
1417 char *p=0;
1418 unsigned long n;
1419
1420 copy_value(my_ldap_fp, entry, attributes[5], &p, user);
1421 if (p) {
1422 if (sscanf(p, "%lu", &n) > 0)
1423 au= (uid_t)n;
1424 free(p);
1425 }
1426#if DEBUG_LDAP
1427 DPRINTF("au= %d",au);
1428#endif
1429 }
1430
1431 if (attributes[6])
1432 {
1433 char *p=0;
1434 unsigned long n;
1435
1436 copy_value(my_ldap_fp, entry, attributes[6], &p, user);
1437 if (p) {
1438 if (sscanf(p, "%lu", &n) > 0)
1439 ag= (gid_t)n;
1440 free(p);
1441 }
1442#if DEBUG_LDAP
1443 DPRINTF("ag= %d",ag);
1444#endif
1445 }
1446
1447 if (attributes[8])
1448 copy_value(my_ldap_fp,entry,attributes[8],&quota, user);
1449
1450 if (homeDir != 0 && my_ldap.mailroot != 0 && *my_ldap.mailroot)
1451 {
1452 char *new_mailroot=malloc(strlen(homeDir)+
1453 strlen(my_ldap.mailroot)+2);
1454
1455 if (!new_mailroot)
1456 {
1457 perror("CRIT: authldap: malloc failed");
1458 rc= -1;
1459 }
1460 else
1461 {
1462 strcat(strcat(strcpy(new_mailroot, my_ldap.mailroot),
1463 "/"), homeDir);
1464 free(homeDir);
1465 homeDir=new_mailroot;
1466 }
1467 }
1468
1469 j=1;
1470
1471 for (i=0; my_ldap.auxoptions[i]; i++)
1472 {
1473 char *val;
1474
1475 copy_value(my_ldap_fp, entry, my_ldap.auxoptions[i], &val,
1476 user);
1477
1478 if (!val)
1479 continue;
1480
1481 j += 2 + strlen(my_ldap.auxnames[i]) +
1482 strlen(val);
1483 free(val);
1484 }
1485
1486 options=malloc(j);
1487
1488 if (!options)
1489 {
1490 perror("CRIT: authldap: malloc failed");
1491 rc= -1;
1492 }
1493 else
1494 {
1495 *options=0;
1496
1497 for (i=0; my_ldap.auxoptions[i]; i++)
1498 {
1499 char *val;
1500
1501 copy_value(my_ldap_fp, entry, my_ldap.auxoptions[i],
1502 &val,
1503 user);
1504
1505 if (!val)
1506 continue;
1507
1508 if (*options)
1509 strcat(options, ",");
1510 strcat(options, my_ldap.auxnames[i]);
1511 strcat(options, "=");
1512 strcat(options, val);
1513 free(val);
1514 }
1515 }
1516
1517
1518 auth.sysuserid= &au;
1519 auth.sysgroupid= ag;
1520 auth.homedir=homeDir;
1521 auth.address=authaddr;
1522 auth.fullname=cn;
1523 auth.maildir=mailDir;
1524 auth.clearpasswd=userPassword;
1525 auth.passwd=cryptPassword;
1526 auth.quota=quota;
1527 auth.options=options && *options ? options:NULL;
1528
1529 if (homeDir == 0)
1530 auth.homedir="";
1531
1532 rc=0;
1533
1534 if (au == 0 || ag == 0)
1535 {
1536 err("authldaplib: refuse to authenticate %s: uid=%d, gid=%d (zero uid or gid not permitted)",
1537 user, au, ag);
1538 rc= 1;
1539 }
1540
1541 courier_authdebug_authinfo("DEBUG: authldaplib: ", &auth,
1542 userPassword, cryptPassword);
1543
1544 if (pass)
1545 {
1546 if (my_ldap.authbind)
1547 {
1548 if (!bindp)
1549 {
1550 bindp=ldapconnect();
1551
1552 if (!bindp)
1553 {
1554 DPRINTF("ldapconnect failed");
1555 rc=1;
1556 }
1557 else
1558#ifdef LDAP_OPT_PROTOCOL_VERSION
1559 /* Set protocol version */
1560 if (my_ldap.protocol_version &&
1561 ldap_set_option(bindp, LDAP_OPT_PROTOCOL_VERSION,
1562 (void *) & my_ldap.protocol_version) != LDAP_OPT_SUCCESS)
1563 {
1564 err("ldap_set_option(PROTOCOL_VERSION %d) failed",
1565 my_ldap.protocol_version);
1566 rc = 1;
1567 }
1568 else
1569#endif
1570 if(my_ldap.tls && enable_tls_on(bindp)) {
1571 err("authldaplib: LDAP_TLS enabled but I'm unable to start tls, check your config");
1572 rc = 1;
1573 }
1574 }
1575
1576 if (bindp)
1577 {
1578 if (rc == 0)
1579 {
1580 int ldrc;
1581 DPRINTF("rebinding with DN '%s' to validate password", dn);
1582 ldrc = l_simple_bind_s(bindp, dn, pass);
1583 switch (ldrc)
1584 {
1585 case LDAP_SUCCESS:
1586 DPRINTF("authentication bind successful");
1587 break;
1588 case LDAP_INVALID_CREDENTIALS:
1589 DPRINTF("authentication bind failed, invalid credentials");
1590 rc = -1;
1591 break;
1592 default:
1593 DPRINTF("authentication bind failed, some other problem: %s",
1594 ldap_err2string(ldrc));
1595 rc = 1;
1596 break;
1597 }
1598 }
1599 /* Drop the connection if there was a fatal
1600 error or if we are using historic LDAP v2,
1601 which didn't support rebinding on same conn */
1602 if (rc > 0 || my_ldap.protocol_version == 2)
1603 {
1604 l_unbind(bindp);
1605 bindp=0;
1606 }
1607 }
1608
1609 if (rc == 0 && newpass)
1610 {
1611 if ((newpass_crypt=authcryptpasswd(newpass,
1612 auth.passwd)
1613 ) == 0)
1614 rc= -1;
1615 }
1616 }
1617 else
1618 {
1619 if (auth.clearpasswd)
1620 {
1621 if (strcmp(pass,auth.clearpasswd))
1622 {
1623 if (courier_authdebug_login_level >= 2)
1624 {
1625 DPRINTF("supplied password '%s' does not match clearpasswd '%s'",
1626 pass, auth.clearpasswd);
1627 }
1628 else
1629 {
1630 DPRINTF("supplied password does not match clearpasswd");
1631 }
1632 rc= -1;
1633 }
1634 }
1635 else
1636 {
1637 const char *p=auth.passwd;
1638
1639 if (!p)
1640 {
1641 DPRINTF("no password to compare against!");
1642 rc= -1;
1643 }
1644 else if (authcheckpassword(pass, p))
1645 rc= -1;
1646 }
1647
1648 if (rc == 0 && newpass && auth.passwd)
1649 {
1650 if ((newpass_crypt=authcryptpasswd(newpass,
1651 auth.passwd)
1652 ) == 0)
1653 rc= -1;
1654 }
1655 }
1656 }
1657
1658 if (rc == 0 && newpass)
1659 {
1660 LDAPMod *mods[3];
1661 int mod_index=0;
1662
1663 LDAPMod mod_clear, mod_crypt;
1664 char *mod_clear_vals[2], *mod_crypt_vals[2];
1665
1666 if (attributes[3])
1667 {
1668 mods[mod_index]= &mod_clear;
1669 mod_clear.mod_op=LDAP_MOD_REPLACE;
1670 mod_clear.mod_type=(char *)attributes[3];
1671 mod_clear.mod_values=mod_clear_vals;
1672
1673 mod_clear_vals[0]=(char *)newpass;
1674 mod_clear_vals[1]=NULL;
1675 ++mod_index;
1676 }
1677
1678 if (attributes[4] && newpass_crypt)
1679 {
1680 mods[mod_index]= &mod_crypt;
1681 mod_crypt.mod_op=LDAP_MOD_REPLACE;
1682 mod_crypt.mod_type=(char *)attributes[4];
1683 mod_crypt.mod_values=mod_crypt_vals;
1684
1685 mod_crypt_vals[0]=newpass_crypt;
1686 mod_crypt_vals[1]=NULL;
1687 ++mod_index;
1688 }
1689 if (mod_index == 0)
1690 rc= -1;
1691 else
1692 {
1693 int ld_errno;
1694 mods[mod_index]=0;
1695
1696 /* On a system which uses LDAP_AUTHBIND, we probably
1697 want to use the user's credentials (bindp) rather
1698 than the search credentials (my_ldap_fp) for
1699 performing the password update. (May not always be
1700 true, ideally it would be configurable) */
1701 ld_errno = l_modify_s(bindp? bindp:my_ldap_fp, dn, mods);
1702 if (ld_errno != LDAP_SUCCESS)
1703 {
1704 rc= -1;
1705 DPRINTF("LDAP modify failed: %s",
1706 ldap_err2string(ld_errno));
1707 }
1708 }
1709 }
1710
1711 if (newpass_crypt)
1712 free(newpass_crypt);
1713 free (dn);
1714#if DEBUG_LDAP
1715 DPRINTF("before callback rc=%d",rc);
1716#endif
1717
1718 if (rc == 0 && callback)
1719 {
1720 if (!auth.clearpasswd)
1721 auth.clearpasswd=pass;
1722 rc= (*callback)(&auth, arg);
1723#if DEBUG_LDAP
1724 DPRINTF("after callback rc=%d",rc);
1725#endif
1726 }
1727
1728 ldap_msgfree(result);
1729 if (options) free(options);
1730 if (homeDir) free(homeDir);
1731 if (mailDir) free(mailDir);
1732 if (userPassword) free(userPassword);
1733 if (cryptPassword) free(cryptPassword);
1734 if (cn) free(cn);
1735 if (quota) free(quota);
1736 return (rc);
1737}
1738
1739/*
1740** Escape a string with special LDAP characters. Returns NULL if the original
1741** string does not have any special LDAP characters (so we don't allocate
1742** memory unless absolutely necessary).
1743*/
1744
1745static char *escape_str(const char *user)
1746{
1747 int i;
1748 const char *p;
1749 char *q, *r;
1750
1751 for (i=0, p=user; *p; p++)
1752 if (strchr("*()\\", *p))
1753 ++i;
1754
1755 if (i == 0)
1756 return NULL; /* No need to escape anything */
1757
1758 q=malloc(strlen(user)+i+1);
1759
1760 if (!q)
1761 {
1762 perror("malloc");
1763 exit(1);
1764 }
1765
1766 for (r=q, p=user; *p; p++)
1767 {
1768 if (strchr("*()\\", *p))
1769 *r++= '\\';
1770 *r++ = *p;
1771 }
1772 *r=0;
1773 return (q);
1774}
1775
1776/**
1777 ** Create an emailmap search string. I'm going to wrap this into an external
1778 ** variable, so I'll use generic coding here.
1779 */
1780
1781struct varlist {
1782 const char *varname;
1783 int varname_len;
1784 const char *varvalue;
1785 int varvalue_len;
1786} ;
1787
1788static char *var_expand(const char *, const struct varlist *);
1789
1790static char *emailmap_get_search_string(const char *str, const char *email)
1791{
1792 struct varlist vl[3];
1793 const char *realmptr=strrchr(email, '@');/* Guaranteed nonNULL */
1794
1795 static const char userid[]="user";
1796 static const char realm[]="realm";
1797
1798 vl[0].varname=userid;
1799 vl[0].varname_len=sizeof(userid)-1;
1800 vl[0].varvalue=email;
1801 vl[0].varvalue_len=realmptr - email;
1802 vl[1].varname=realm;
1803 vl[1].varname_len=sizeof(realm)-1;
1804 vl[1].varvalue=realmptr+1;
1805 vl[1].varvalue_len=strlen(vl[1].varvalue);
1806 vl[2].varname=NULL;
1807
1808 return (var_expand(str, vl));
1809}
1810
1811static char *var_expand(const char *str, const struct varlist *vl)
1812{
1813 const char *p;
1814 int cnt;
1815 int pass;
1816 char *q, *r;
1817
1818 cnt=0;
1819 q=NULL;
1820
1821 /*
1822 ** Pass 1 - count expanded string length, allocate buffer,
1823 ** Pass 2 - generate the string.
1824 */
1825
1826 for (pass=0; pass<2; pass++)
1827 {
1828 if (pass)
1829 {
1830 if ((q=malloc(cnt)) == NULL)
1831 return (NULL);
1832 }
1833 r=q;
1834 cnt=1;
1835
1836 p=str;
1837
1838 while (*p)
1839 {
1840 if (*p == '@')
1841 {
1842 int j;
1843
1844 for (j=0; vl[j].varname; j++)
1845 {
1846 if (memcmp(vl[j].varname, p+1,
1847 vl[j].varname_len) == 0
1848 && p[vl[j].varname_len+1] == '@')
1849 break;
1850 }
1851
1852 if (vl[j].varname)
1853 {
1854 p += vl[j].varname_len+2;
1855
1856 if (r)
1857 {
1858 memcpy(r, vl[j].varvalue,
1859 vl[j].varvalue_len);
1860 r += vl[j].varvalue_len;
1861 }
1862 cnt += vl[j].varvalue_len;
1863 continue;
1864 }
1865 }
1866
1867 if (r)
1868 *r++ = *p;
1869 ++p;
1870 ++cnt;
1871 }
1872 if (r)
1873 *r=0;
1874 }
1875
1876 return (q);
1877}
1878
1879void auth_ldap_enumerate( void(*cb_func)(const char *name,
1880 uid_t uid,
1881 gid_t gid,
1882 const char *homedir,
1883 const char *maildir,
1884 const char *options,
1885 void *void_arg),
1886 void *void_arg)
1887{
1888 const char *attributes[5];
1889
1890 int i, j;
1891 int msgid;
1892
1893 if (ldapopen()) return;
1894
1895 read_env("LDAP_MAIL", &attributes[0], "", 0, "mail");
1896 read_env("LDAP_UID", &attributes[1], "", 0, 0);
1897 read_env("LDAP_GID", &attributes[2], "", 0, 0);
1898 read_env("LDAP_HOMEDIR", &attributes[3], "", 0, "homeDir");
1899 read_env("LDAP_MAILDIR", &attributes[4], "", 0, 0);
1900
1901 j=0;
1902 for (i=0; i<5; i++)
1903 {
1904 if (attributes[i])
1905 my_ldap.attrlist[j++]=attributes[i];
1906 }
1907
1908 for (i=0; my_ldap.auxoptions[i]; i++)
1909 my_ldap.attrlist[j++]=my_ldap.auxoptions[i];
1910
1911 my_ldap.attrlist[j]=0;
1912
1913 DPRINTF("ldap_search: basedn='%s', filter='%s'",
1914 my_ldap.basedn, my_ldap.enumerate_filter);
1915 if ((msgid = l_search(my_ldap_fp, (char *)my_ldap.basedn,LDAP_SCOPE_SUBTREE,
1916 my_ldap.enumerate_filter,
1917 (char **)my_ldap.attrlist, 0)) < 0)
1918 {
1919 DPRINTF("ldap_search failed");
1920 return;
1921 }
1922
1923 while(1) /* process results as they come in */
1924 {
1925 struct timeval timeout;
1926 LDAPMessage *result;
1927 LDAPMessage *entry;
1928 int ldrc;
1929
1930 timeout.tv_sec=my_ldap.timeout;
1931 timeout.tv_usec=0;
1932 ldrc = ldap_result(my_ldap_fp, msgid, 0, &timeout, &result);
1933 switch (ldrc)
1934 {
1935 case -1:
1936 DPRINTF("error in ldap_result");
1937 ldap_msgfree(result);
1938 return;
1939 case 0:
1940 DPRINTF("timeout waiting for search result");
1941 ldap_msgfree(result);
1942 return;
1943 case LDAP_RES_SEARCH_ENTRY:
1944 break; /* deal with below */
1945 case LDAP_RES_SEARCH_RESULT:
1946 if (ldap_parse_result(my_ldap_fp, result, &ldrc,
1947 NULL, NULL, NULL, NULL, 0) != LDAP_SUCCESS)
1948 {
1949 DPRINTF("ldap_parse_result failed");
1950 ldap_msgfree(result);
1951 return;
1952 }
1953 ldap_msgfree(result);
1954 if (ldrc != LDAP_SUCCESS)
1955 {
1956 DPRINTF("ldap search failure result: %s",
1957 ldap_err2string(ldrc));
1958 return;
1959 }
1960 /* Search successfully completed */
1961 (*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg);
1962 return;
1963 default:
1964 DPRINTF("ldap result type 0x%02X ignored", ldrc);
1965 ldap_msgfree(result);
1966 continue;
1967 }
1968
1969 entry = ldap_first_entry(my_ldap_fp, result);
1970 while (entry)
1971 {
1972 char **names = get_values(my_ldap_fp, entry, attributes[0]);
1973 int n;
1974
1975 if (names == NULL)
1976 {
1977 entry = ldap_next_entry(my_ldap_fp, entry);
1978 continue;
1979 }
1980
1981 n=l_count_values(names);
1982 if (n > 0)
1983 {
1984 const char *name = names[0] ? names[0] : "<null>";
1985 int i,j;
1986 char *uid_s=NULL;
1987 char *gid_s=NULL;
1988 char *homedir;
1989 char *maildir;
1990 char *options;
1991 uid_t uid=my_ldap.uid;
1992 gid_t gid=my_ldap.gid;
1993
1994 if (attributes[1])
1995 {
1996 copy_value(my_ldap_fp, entry, attributes[1],
1997 &uid_s,
1998 name);
1999 }
2000
2001 if (attributes[2])
2002 {
2003 copy_value(my_ldap_fp, entry, attributes[2],
2004 &gid_s, name);
2005 }
2006
2007 copy_value(my_ldap_fp, entry, attributes[3],
2008 &homedir, name);
2009 copy_value(my_ldap_fp, entry, attributes[4],
2010 &maildir, name);
2011
2012 if (uid_s)
2013 uid=atol(uid_s);
2014 if (gid_s)
2015 gid=atol(gid_s);
2016
2017 j=1;
2018
2019 for (i=0; my_ldap.auxoptions[i]; i++)
2020 {
2021 char *val;
2022
2023 copy_value(my_ldap_fp, entry, my_ldap.auxoptions[i],
2024 &val, name);
2025
2026 if (!val)
2027 continue;
2028
2029 j += 2 + strlen(my_ldap.auxnames[i]) +
2030 strlen(val);
2031 free(val);
2032 }
2033
2034 options=malloc(j);
2035
2036 if (!options)
2037 {
2038 perror("CRIT: auth_ldap_enumerate: malloc failed");
2039 return;
2040 }
2041 *options=0;
2042
2043 for (i=0; my_ldap.auxoptions[i]; i++)
2044 {
2045 char *val;
2046
2047 copy_value(my_ldap_fp, entry, my_ldap.auxoptions[i],
2048 &val, name);
2049
2050 if (!val)
2051 continue;
2052
2053 if (*options)
2054 strcat(options, ",");
2055 strcat(options, my_ldap.auxnames[i]);
2056 strcat(options, "=");
2057 strcat(options, val);
2058 free(val);
2059 }
2060
2061 for (j = 0; j < n; j++)
2062 {
2063 name=names[j];
2064
2065 if (name && homedir)
2066 (*cb_func)(name, uid, gid, homedir,
2067 maildir, options, void_arg);
2068 }
2069
2070 if (uid_s)
2071 free(uid_s);
2072 if (gid_s)
2073 free(gid_s);
2074 if (homedir)
2075 free(homedir);
2076 if (maildir)
2077 free(maildir);
2078 if (options)
2079 free(options);
2080 }
2081 l_value_free(names);
2082
2083 entry = ldap_next_entry(my_ldap_fp, entry);
2084 }
2085
2086 ldap_msgfree(result);
2087 }
2088}