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