DKIM ACL Documentation
[exim.git] / src / src / lookups / ldap.c
CommitLineData
0a49a7a4 1/* $Cambridge: exim/src/src/lookups/ldap.c,v 1.15 2009/11/16 19:50:38 nm4 Exp $ */
0756eb3c
PH
2
3/*************************************************
4* Exim - an Internet mail transport agent *
5*************************************************/
6
0a49a7a4 7/* Copyright (c) University of Cambridge 1995 - 2009 */
0756eb3c
PH
8/* See the file NOTICE for conditions of use and distribution. */
9
10/* Many thanks to Stuart Lynne for contributing the original code for this
11driver. Further contibutions from Michael Haardt, Brian Candler, Barry
12Pederson, Peter Savitch and Christian Kellner. Particular thanks to Brian for
13researching how to handle the different kinds of error. */
14
15
16#include "../exim.h"
17#include "lf_functions.h"
18#include "ldap.h"
19
20
21/* We can't just compile this code and allow the library mechanism to omit the
22functions if they are not wanted, because we need to have the LDAP headers
23available for compiling. Therefore, compile these functions only if LOOKUP_LDAP
24is defined. However, some compilers don't like compiling empty modules, so keep
25them happy with a dummy when skipping the rest. Make it reference itself to
26stop picky compilers complaining that it is unused, and put in a dummy argument
27to stop even pickier compilers complaining about infinite loops. */
28
29#ifndef LOOKUP_LDAP
30static void dummy(int x) { dummy(x-1); }
31#else
32
33
765b530f
PH
34/* Include LDAP headers. The code below uses some "old" LDAP interfaces that
35are deprecated in OpenLDAP. I don't know their status in other LDAP
36implementations. LDAP_DEPRECATED causes their prototypes to be defined in
37ldap.h. */
38
39#define LDAP_DEPRECATED 1
0756eb3c
PH
40
41#include <lber.h>
42#include <ldap.h>
43
44
45/* Annoyingly, the different LDAP libraries handle errors in different ways,
46and some other things too. There doesn't seem to be an automatic way of
47distinguishing between them. Local/Makefile should contain a setting of
48LDAP_LIB_TYPE, which in turn causes appropriate macros to be defined for the
49different kinds. Those that matter are:
50
51LDAP_LIB_NETSCAPE
52LDAP_LIB_SOLARIS with synonym LDAP_LIB_SOLARIS7
53LDAP_LIB_OPENLDAP2
54
55These others may be defined, but are in fact the default, so are not tested:
56
57LDAP_LIB_UMICHIGAN
58LDAP_LIB_OPENLDAP1
59*/
60
61#if defined(LDAP_LIB_SOLARIS7) && ! defined(LDAP_LIB_SOLARIS)
62#define LDAP_LIB_SOLARIS
63#endif
64
65
66/* Just in case LDAP_NO_LIMIT is not defined by some of these libraries. */
67
68#ifndef LDAP_NO_LIMIT
69#define LDAP_NO_LIMIT 0
70#endif
71
72
73/* Just in case LDAP_DEREF_NEVER is not defined */
74
75#ifndef LDAP_DEREF_NEVER
76#define LDAP_DEREF_NEVER 0
77#endif
78
79
0756eb3c
PH
80/* Four types of LDAP search are implemented */
81
82#define SEARCH_LDAP_MULTIPLE 0 /* Get attributes from multiple entries */
83#define SEARCH_LDAP_SINGLE 1 /* Get attributes from one entry only */
84#define SEARCH_LDAP_DN 2 /* Get just the DN from one entry */
85#define SEARCH_LDAP_AUTH 3 /* Just checking for authentication */
86
87/* In all 4 cases, the DN is left in $ldap_dn (which post-dates the
88SEARCH_LDAP_DN lookup). */
89
90
91/* Structure and anchor for caching connections. */
92
93typedef struct ldap_connection {
94 struct ldap_connection *next;
95 uschar *host;
96 uschar *user;
97 uschar *password;
98 BOOL bound;
99 int port;
100 LDAP *ld;
101} LDAP_CONNECTION;
102
103static LDAP_CONNECTION *ldap_connections = NULL;
104
105
106
107/*************************************************
108* Internal search function *
109*************************************************/
110
111/* This is the function that actually does the work. It is called (indirectly
112via control_ldap_search) from eldap_find(), eldapauth_find(), eldapdn_find(),
113and eldapm_find(), with a difference in the "search_type" argument.
114
115The case of eldapauth_find() is special in that all it does is do
116authentication, returning OK or FAIL as appropriate. This isn't used as a
117lookup. Instead, it is called from expand.c as an expansion condition test.
118
119The DN from a successful lookup is placed in $ldap_dn. This feature postdates
120the provision of the SEARCH_LDAP_DN facility for returning just the DN as the
121data.
122
123Arguments:
124 ldap_url the URL to be looked up
125 server server host name, when URL contains none
126 s_port server port, used when URL contains no name
127 search_type SEARCH_LDAP_MULTIPLE allows values from multiple entries
128 SEARCH_LDAP_SINGLE allows values from one entry only
129 SEARCH_LDAP_DN gets the DN from one entry
130 res set to point at the result (not used for ldapauth)
131 errmsg set to point a message if result is not OK
132 defer_break set TRUE if no more servers to be tried after a DEFER
133 user user name for authentication, or NULL
134 password password for authentication, or NULL
135 sizelimit max number of entries returned, or 0 for no limit
136 timelimit max time to wait, or 0 for no limit
d00328e2 137 tcplimit max time for network activity, e.g. connect, or 0 for OS default
0756eb3c
PH
138 deference the dereference option, which is one of
139 LDAP_DEREF_{NEVER,SEARCHING,FINDING,ALWAYS}
6ec97b1b 140 referrals the referral option, which is LDAP_OPT_ON or LDAP_OPT_OFF
0756eb3c
PH
141
142Returns: OK or FAIL or DEFER
143 FAIL is given only if a lookup was performed successfully, but
144 returned no data.
145*/
146
147static int
148perform_ldap_search(uschar *ldap_url, uschar *server, int s_port, int search_type,
149 uschar **res, uschar **errmsg, BOOL *defer_break, uschar *user, uschar *password,
6ec97b1b 150 int sizelimit, int timelimit, int tcplimit, int dereference, void *referrals)
0756eb3c
PH
151{
152LDAPURLDesc *ludp = NULL;
153LDAPMessage *result = NULL;
154BerElement *ber;
155LDAP_CONNECTION *lcp;
156
157struct timeval timeout;
158struct timeval *timeoutptr = NULL;
159
160uschar *attr;
161uschar **attrp;
162uschar *data = NULL;
163uschar *dn = NULL;
164uschar *host;
165uschar **values;
166uschar **firstval;
167uschar porttext[16];
168
169uschar *error1 = NULL; /* string representation of errcode (static) */
170uschar *error2 = NULL; /* error message from the server */
171uschar *matched = NULL; /* partially matched DN */
172
173int attr_count = 0;
174int error_yield = DEFER;
175int msgid;
d38f8232 176int rc, ldap_rc, ldap_parse_rc;
0756eb3c
PH
177int port;
178int ptr = 0;
179int rescount = 0;
180int size = 0;
181BOOL attribute_found = FALSE;
182BOOL ldapi = FALSE;
183
184DEBUG(D_lookup)
185 debug_printf("perform_ldap_search: ldap%s URL = \"%s\" server=%s port=%d "
186 "sizelimit=%d timelimit=%d tcplimit=%d\n",
187 (search_type == SEARCH_LDAP_MULTIPLE)? "m" :
188 (search_type == SEARCH_LDAP_DN)? "dn" :
189 (search_type == SEARCH_LDAP_AUTH)? "auth" : "",
190 ldap_url, server, s_port, sizelimit, timelimit, tcplimit);
191
192/* Check if LDAP thinks the URL is a valid LDAP URL. We assume that if the LDAP
193library that is in use doesn't recognize, say, "ldapi", it will barf here. */
194
195if (!ldap_is_ldap_url(CS ldap_url))
196 {
197 *errmsg = string_sprintf("ldap_is_ldap_url: not an LDAP url \"%s\"\n",
198 ldap_url);
199 goto RETURN_ERROR_BREAK;
200 }
201
202/* Parse the URL */
203
204if ((rc = ldap_url_parse(CS ldap_url, &ludp)) != 0)
205 {
206 *errmsg = string_sprintf("ldap_url_parse: (error %d) parsing \"%s\"\n", rc,
207 ldap_url);
208 goto RETURN_ERROR_BREAK;
209 }
210
211/* If the host name is empty, take it from the separate argument, if one is
212given. OpenLDAP 2.0.6 sets an unset hostname to "" rather than empty, but
213expects NULL later in ldap_init() to mean "default", annoyingly. In OpenLDAP
2142.0.11 this has changed (it uses NULL). */
215
216if ((ludp->lud_host == NULL || ludp->lud_host[0] == 0) && server != NULL)
217 {
218 host = server;
219 port = s_port;
220 }
221else
222 {
223 host = US ludp->lud_host;
224 if (host != NULL && host[0] == 0) host = NULL;
225 port = ludp->lud_port;
226 }
227
228DEBUG(D_lookup) debug_printf("after ldap_url_parse: host=%s port=%d\n",
229 host, port);
230
231if (port == 0) port = LDAP_PORT; /* Default if none given */
232sprintf(CS porttext, ":%d", port); /* For messages */
233
234/* If the "host name" is actually a path, we are going to connect using a Unix
235socket, regardless of whether "ldapi" was actually specified or not. This means
236that a Unix socket can be declared in eldap_default_servers, and "traditional"
237LDAP queries using just "ldap" can be used ("ldaps" is similarly overridden).
238The path may start with "/" or it may already be escaped as "%2F" if it was
239actually declared that way in eldap_default_servers. (I did it that way the
240first time.) If the host name is not a path, the use of "ldapi" causes an
241error, except in the default case. (But lud_scheme doesn't seem to exist in
242older libraries.) */
243
244if (host != NULL)
245 {
246 if ((host[0] == '/' || Ustrncmp(host, "%2F", 3) == 0))
247 {
248 ldapi = TRUE;
249 porttext[0] = 0; /* Remove port from messages */
250 }
251
252 #if defined LDAP_LIB_OPENLDAP2
253 else if (strncmp(ludp->lud_scheme, "ldapi", 5) == 0)
254 {
255 *errmsg = string_sprintf("ldapi requires an absolute path (\"%s\" given)",
256 host);
257 goto RETURN_ERROR;
258 }
259 #endif
260 }
261
262/* Count the attributes; we need this later to tell us how to format results */
263
264for (attrp = USS ludp->lud_attrs; attrp != NULL && *attrp != NULL; attrp++)
265 attr_count++;
266
267/* See if we can find a cached connection to this host. The port is not
268relevant for ldapi. The host name pointer is set to NULL if no host was given
269(implying the library default), rather than to the empty string. Note that in
270this case, there is no difference between ldap and ldapi. */
271
272for (lcp = ldap_connections; lcp != NULL; lcp = lcp->next)
273 {
274 if ((host == NULL) != (lcp->host == NULL) ||
275 (host != NULL && strcmpic(lcp->host, host) != 0))
276 continue;
277 if (ldapi || port == lcp->port) break;
278 }
279
d00328e2
PH
280/* Use this network timeout in any requests. */
281
282if (tcplimit > 0)
283 {
284 timeout.tv_sec = tcplimit;
285 timeout.tv_usec = 0;
286 timeoutptr = &timeout;
287 }
288
0756eb3c
PH
289/* If no cached connection found, we must open a connection to the server. If
290the server name is actually an absolute path, we set ldapi=TRUE above. This
291requests connection via a Unix socket. However, as far as I know, only OpenLDAP
292supports the use of sockets, and the use of ldap_initialize(). */
293
294if (lcp == NULL)
295 {
296 LDAP *ld;
297
298
299 /* --------------------------- OpenLDAP ------------------------ */
300
301 /* There seems to be a preference under OpenLDAP for ldap_initialize()
302 instead of ldap_init(), though I have as yet been unable to find
303 documentation that says this. (OpenLDAP documentation is sparse to
304 non-existent). So we handle OpenLDAP differently here. Also, support for
305 ldapi seems to be OpenLDAP-only at present. */
306
307 #ifdef LDAP_LIB_OPENLDAP2
308
309 /* We now need an empty string for the default host. Get some store in which
310 to build a URL for ldap_initialize(). In the ldapi case, it can't be bigger
311 than (9 + 3*Ustrlen(shost)), whereas in the other cases it can't be bigger
312 than the host name + "ldaps:///" plus : and a port number, say 20 + the
313 length of the host name. What we get should accommodate both, easily. */
314
315 uschar *shost = (host == NULL)? US"" : host;
316 uschar *init_url = store_get(20 + 3 * Ustrlen(shost));
317 uschar *init_ptr;
318
319 /* Handle connection via Unix socket ("ldapi"). We build a basic LDAP URI to
320 contain the path name, with slashes escaped as %2F. */
321
322 if (ldapi)
323 {
324 int ch;
325 init_ptr = init_url + 8;
326 Ustrcpy(init_url, "ldapi://");
327 while ((ch = *shost++) != 0)
328 {
329 if (ch == '/')
330 {
331 Ustrncpy(init_ptr, "%2F", 3);
332 init_ptr += 3;
333 }
334 else *init_ptr++ = ch;
335 }
336 *init_ptr = 0;
337 }
338
339 /* This is not an ldapi call. Just build a URI with the protocol type, host
340 name, and port. */
341
342 else
343 {
344 init_ptr = Ustrchr(ldap_url, '/');
345 Ustrncpy(init_url, ldap_url, init_ptr - ldap_url);
346 init_ptr = init_url + (init_ptr - ldap_url);
347 sprintf(CS init_ptr, "//%s:%d/", shost, port);
348 }
349
350 /* Call ldap_initialize() and check the result */
351
352 DEBUG(D_lookup) debug_printf("ldap_initialize with URL %s\n", init_url);
353 rc = ldap_initialize(&ld, CS init_url);
354 if (rc != LDAP_SUCCESS)
355 {
356 *errmsg = string_sprintf("ldap_initialize: (error %d) URL \"%s\"\n",
357 rc, init_url);
358 goto RETURN_ERROR;
359 }
360 store_reset(init_url); /* Might as well save memory when we can */
361
362
363 /* ------------------------- Not OpenLDAP ---------------------- */
364
365 /* For libraries other than OpenLDAP, use ldap_init(). */
366
367 #else /* LDAP_LIB_OPENLDAP2 */
368 ld = ldap_init(CS host, port);
369 #endif /* LDAP_LIB_OPENLDAP2 */
370
371 /* -------------------------------------------------------------- */
372
373
374 /* Handle failure to initialize */
375
376 if (ld == NULL)
377 {
378 *errmsg = string_sprintf("failed to initialize for LDAP server %s%s - %s",
379 host, porttext, strerror(errno));
380 goto RETURN_ERROR;
381 }
382
383 /* Set the TCP connect time limit if available. This is something that is
384 in Netscape SDK v4.1; I don't know about other libraries. */
385
386 #ifdef LDAP_X_OPT_CONNECT_TIMEOUT
7c7ad977
PH
387 if (tcplimit > 0)
388 {
994a09e9 389 int timeout1000 = tcplimit*1000;
7c7ad977
PH
390 ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, (void *)&timeout1000);
391 }
994a09e9
PH
392 else
393 {
394 int notimeout = LDAP_X_IO_TIMEOUT_NO_TIMEOUT;
395 ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, (void *)&notimeout);
396 }
0756eb3c
PH
397 #endif
398
7c7ad977
PH
399 /* Set the TCP connect timeout. This works with OpenLDAP 2.2.14. */
400
401 #ifdef LDAP_OPT_NETWORK_TIMEOUT
402 if (tcplimit > 0)
403 ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, (void *)timeoutptr);
8e669ac1 404 #endif
7c7ad977 405
0756eb3c
PH
406 /* I could not get TLS to work until I set the version to 3. That version
407 seems to be the default nowadays. The RFC is dated 1997, so I would hope
408 that all the LDAP libraries support it. Therefore, if eldap_version hasn't
409 been set, go for v3 if we can. */
410
411 if (eldap_version < 0)
412 {
413 #ifdef LDAP_VERSION3
414 eldap_version = LDAP_VERSION3;
415 #else
416 eldap_version = 2;
417 #endif
418 }
419
420 #ifdef LDAP_OPT_PROTOCOL_VERSION
421 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, (void *)&eldap_version);
422 #endif
423
424 DEBUG(D_lookup) debug_printf("initialized for LDAP (v%d) server %s%s\n",
425 eldap_version, host, porttext);
426
427 /* If not using ldapi and TLS is available, set appropriate TLS options: hard
428 for "ldaps" and soft otherwise. */
429
430 #ifdef LDAP_OPT_X_TLS
431 if (!ldapi)
432 {
433 int tls_option;
434 if (strncmp(ludp->lud_scheme, "ldaps", 5) == 0)
435 {
436 tls_option = LDAP_OPT_X_TLS_HARD;
437 DEBUG(D_lookup) debug_printf("LDAP_OPT_X_TLS_HARD set\n");
438 }
439 else
440 {
441 tls_option = LDAP_OPT_X_TLS_TRY;
442 DEBUG(D_lookup) debug_printf("LDAP_OPT_X_TLS_TRY set\n");
443 }
444 ldap_set_option(ld, LDAP_OPT_X_TLS, (void *)&tls_option);
445 }
446 #endif /* LDAP_OPT_X_TLS */
447
448 /* Now add this connection to the chain of cached connections */
449
450 lcp = store_get(sizeof(LDAP_CONNECTION));
451 lcp->host = (host == NULL)? NULL : string_copy(host);
452 lcp->bound = FALSE;
453 lcp->user = NULL;
454 lcp->password = NULL;
455 lcp->port = port;
456 lcp->ld = ld;
457 lcp->next = ldap_connections;
458 ldap_connections = lcp;
459 }
460
461/* Found cached connection */
462
463else
464 {
465 DEBUG(D_lookup)
466 debug_printf("re-using cached connection to LDAP server %s%s\n",
467 host, porttext);
468 }
469
470/* Bind with the user/password supplied, or an anonymous bind if these values
471are NULL, unless a cached connection is already bound with the same values. */
472
473if (!lcp->bound ||
474 (lcp->user == NULL && user != NULL) ||
475 (lcp->user != NULL && user == NULL) ||
476 (lcp->user != NULL && user != NULL && Ustrcmp(lcp->user, user) != 0) ||
477 (lcp->password == NULL && password != NULL) ||
478 (lcp->password != NULL && password == NULL) ||
479 (lcp->password != NULL && password != NULL &&
480 Ustrcmp(lcp->password, password) != 0))
481 {
482 DEBUG(D_lookup) debug_printf("%sbinding with user=%s password=%s\n",
483 (lcp->bound)? "re-" : "", user, password);
7c7ad977
PH
484 if ((msgid = ldap_bind(lcp->ld, CS user, CS password, LDAP_AUTH_SIMPLE))
485 == -1)
0756eb3c 486 {
7c7ad977 487 *errmsg = string_sprintf("failed to bind the LDAP connection to server "
d00328e2 488 "%s%s - ldap_bind() returned -1", host, porttext);
7c7ad977
PH
489 goto RETURN_ERROR;
490 }
0756eb3c 491
7c7ad977
PH
492 if ((rc = ldap_result( lcp->ld, msgid, 1, timeoutptr, &result )) <= 0)
493 {
494 *errmsg = string_sprintf("failed to bind the LDAP connection to server "
8e669ac1 495 "%s%s - LDAP error: %s", host, porttext,
7c7ad977
PH
496 rc == -1 ? "result retrieval failed" : "timeout" );
497 result = NULL;
498 goto RETURN_ERROR;
499 }
500
501 rc = ldap_result2error( lcp->ld, result, 0 );
502
503 /* Invalid credentials when just checking credentials returns FAIL. This
504 stops any further servers being tried. */
0756eb3c 505
7c7ad977
PH
506 if (search_type == SEARCH_LDAP_AUTH && rc == LDAP_INVALID_CREDENTIALS)
507 {
508 DEBUG(D_lookup)
509 debug_printf("Invalid credentials: ldapauth returns FAIL\n");
510 error_yield = FAIL;
511 goto RETURN_ERROR_NOMSG;
512 }
0756eb3c 513
7c7ad977
PH
514 /* Otherwise we have a problem that doesn't stop further servers from being
515 tried. */
516
517 if (rc != LDAP_SUCCESS)
518 {
0756eb3c
PH
519 *errmsg = string_sprintf("failed to bind the LDAP connection to server "
520 "%s%s - LDAP error %d: %s", host, porttext, rc, ldap_err2string(rc));
521 goto RETURN_ERROR;
522 }
523
524 /* Successful bind */
525
526 lcp->bound = TRUE;
527 lcp->user = (user == NULL)? NULL : string_copy(user);
528 lcp->password = (password == NULL)? NULL : string_copy(password);
7c7ad977
PH
529
530 ldap_msgfree(result);
531 result = NULL;
0756eb3c
PH
532 }
533
534/* If we are just checking credentials, return OK. */
535
536if (search_type == SEARCH_LDAP_AUTH)
537 {
538 DEBUG(D_lookup) debug_printf("Bind succeeded: ldapauth returns OK\n");
539 goto RETURN_OK;
540 }
541
542/* Before doing the search, set the time and size limits (if given). Here again
543the different implementations of LDAP have chosen to do things differently. */
544
545#if defined(LDAP_OPT_SIZELIMIT)
546ldap_set_option(lcp->ld, LDAP_OPT_SIZELIMIT, (void *)&sizelimit);
547ldap_set_option(lcp->ld, LDAP_OPT_TIMELIMIT, (void *)&timelimit);
548#else
549lcp->ld->ld_sizelimit = sizelimit;
550lcp->ld->ld_timelimit = timelimit;
551#endif
552
553/* Similarly for dereferencing aliases. Don't know if this is possible on
554an LDAP library without LDAP_OPT_DEREF. */
555
556#if defined(LDAP_OPT_DEREF)
557ldap_set_option(lcp->ld, LDAP_OPT_DEREF, (void *)&dereference);
558#endif
559
6ec97b1b
PH
560/* Similarly for the referral setting; should the library follow referrals that
561the LDAP server returns? The conditional is just in case someone uses a library
562without it. */
563
564#if defined(LDAP_OPT_REFERRALS)
565ldap_set_option(lcp->ld, LDAP_OPT_REFERRALS, referrals);
566#endif
567
0756eb3c
PH
568/* Start the search on the server. */
569
570DEBUG(D_lookup) debug_printf("Start search\n");
571
572msgid = ldap_search(lcp->ld, ludp->lud_dn, ludp->lud_scope, ludp->lud_filter,
573 ludp->lud_attrs, 0);
574
575if (msgid == -1)
576 {
3ca0ba97
PH
577 #if defined LDAP_LIB_SOLARIS || defined LDAP_LIB_OPENLDAP2
578 int err;
579 ldap_get_option(lcp->ld, LDAP_OPT_ERROR_NUMBER, &err);
8e669ac1 580 *errmsg = string_sprintf("ldap_search failed: %d, %s", err,
3ca0ba97 581 ldap_err2string(err));
8e669ac1
PH
582
583 #else
3ca0ba97
PH
584 *errmsg = string_sprintf("ldap_search failed");
585 #endif
8e669ac1 586
0756eb3c
PH
587 goto RETURN_ERROR;
588 }
589
590/* Loop to pick up results as they come in, setting a timeout if one was
591given. */
592
0756eb3c
PH
593while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
594 LDAP_RES_SEARCH_ENTRY)
595 {
596 LDAPMessage *e;
597
598 DEBUG(D_lookup) debug_printf("ldap_result loop\n");
599
600 for(e = ldap_first_entry(lcp->ld, result);
601 e != NULL;
602 e = ldap_next_entry(lcp->ld, e))
603 {
604 uschar *new_dn;
605 BOOL insert_space = FALSE;
606
607 DEBUG(D_lookup) debug_printf("LDAP entry loop\n");
608
609 rescount++; /* Count results */
610
611 /* Results for multiple entries values are separated by newlines. */
612
613 if (data != NULL) data = string_cat(data, &size, &ptr, US"\n", 1);
614
615 /* Get the DN from the last result. */
616
617 new_dn = US ldap_get_dn(lcp->ld, e);
618 if (new_dn != NULL)
619 {
620 if (dn != NULL)
621 {
622 #if defined LDAP_LIB_NETSCAPE || defined LDAP_LIB_OPENLDAP2
623 ldap_memfree(dn);
624 #else /* OPENLDAP 1, UMich, Solaris */
625 free(dn);
626 #endif
627 }
628 /* Save for later */
629 dn = new_dn;
630 }
631
632 /* If the data we want is actually the DN rather than any attribute values,
633 (an "ldapdn" search) add it to the data string. If there are multiple
634 entries, the DNs will be concatenated, but we test for this case below, as
635 for SEARCH_LDAP_SINGLE, and give an error. */
636
637 if (search_type == SEARCH_LDAP_DN) /* Do not amalgamate these into one */
638 { /* condition, because of the else */
639 if (new_dn != NULL) /* below, that's for the first only */
640 {
641 data = string_cat(data, &size, &ptr, new_dn, Ustrlen(new_dn));
642 data[ptr] = 0;
643 attribute_found = TRUE;
644 }
645 }
646
647 /* Otherwise, loop through the entry, grabbing attribute values. If there's
648 only one attribute being retrieved, no attribute name is given, and the
649 result is not quoted. Multiple values are separated by (comma, space).
650 If more than one attribute is being retrieved, the data is given as a
651 sequence of name=value pairs, with the value always in quotes. If there are
652 multiple values, they are given within the quotes, comma separated. */
653
654 else for (attr = US ldap_first_attribute(lcp->ld, e, &ber);
655 attr != NULL;
656 attr = US ldap_next_attribute(lcp->ld, e, ber))
657 {
658 if (attr[0] != 0)
659 {
660 /* Get array of values for this attribute. */
661
662 if ((firstval = values = USS ldap_get_values(lcp->ld, e, CS attr))
663 != NULL)
664 {
665 if (attr_count != 1)
666 {
667 if (insert_space)
668 data = string_cat(data, &size, &ptr, US" ", 1);
669 else
670 insert_space = TRUE;
671 data = string_cat(data, &size, &ptr, attr, Ustrlen(attr));
672 data = string_cat(data, &size, &ptr, US"=\"", 2);
673 }
674
675 while (*values != NULL)
676 {
677 uschar *value = *values;
678 int len = Ustrlen(value);
679
680 DEBUG(D_lookup) debug_printf("LDAP attr loop %s:%s\n", attr, value);
681
682 if (values != firstval)
683 data = string_cat(data, &size, &ptr, US", ", 2);
684
685 /* For multiple attributes, the data is in quotes. We must escape
686 internal quotes, backslashes, newlines. */
687
688 if (attr_count != 1)
689 {
690 int j;
691 for (j = 0; j < len; j++)
692 {
693 if (value[j] == '\n')
694 data = string_cat(data, &size, &ptr, US"\\n", 2);
695 else
696 {
697 if (value[j] == '\"' || value[j] == '\\')
698 data = string_cat(data, &size, &ptr, US"\\", 1);
699 data = string_cat(data, &size, &ptr, value+j, 1);
700 }
701 }
702 }
703
704 /* For single attributes, copy the value verbatim */
705
706 else data = string_cat(data, &size, &ptr, value, len);
707
708 /* Move on to the next value */
709
710 values++;
711 attribute_found = TRUE;
712 }
713
714 /* Closing quote at the end of the data for a named attribute. */
715
716 if (attr_count != 1)
717 data = string_cat(data, &size, &ptr, US"\"", 1);
718
719 /* Free the values */
720
721 ldap_value_free(CSS firstval);
722 }
723 }
724
725 #if defined LDAP_LIB_NETSCAPE || defined LDAP_LIB_OPENLDAP2
726
727 /* Netscape and OpenLDAP2 LDAP's attrs are dynamically allocated and need
728 to be freed. UMich LDAP stores them in static storage and does not require
729 this. */
730
731 ldap_memfree(attr);
732 #endif
733 } /* End "for" loop for extracting attributes from an entry */
734 } /* End "for" loop for extracting entries from a result */
735
736 /* Free the result */
737
738 ldap_msgfree(result);
739 result = NULL;
740 } /* End "while" loop for multiple results */
741
742/* Terminate the dynamic string that we have built and reclaim unused store */
743
744if (data != NULL)
745 {
746 data[ptr] = 0;
747 store_reset(data + ptr + 1);
748 }
749
750/* Copy the last dn into eldap_dn */
751
752if (dn != NULL)
753 {
754 eldap_dn = string_copy(dn);
755 #if defined LDAP_LIB_NETSCAPE || defined LDAP_LIB_OPENLDAP2
756 ldap_memfree(dn);
757 #else /* OPENLDAP 1, UMich, Solaris */
758 free(dn);
759 #endif
760 }
761
762DEBUG(D_lookup) debug_printf("search ended by ldap_result yielding %d\n",rc);
763
764if (rc == 0)
765 {
766 *errmsg = US"ldap_result timed out";
767 goto RETURN_ERROR;
768 }
769
770/* A return code of -1 seems to mean "ldap_result failed internally or couldn't
771provide you with a message". Other error states seem to exist where
772ldap_result() didn't give us any message from the server at all, leaving result
773set to NULL. Apparently, "the error parameters of the LDAP session handle will
774be set accordingly". That's the best we can do to retrieve an error status; we
775can't use functions like ldap_result2error because they parse a message from
776the server, which we didn't get.
777
778Annoyingly, the different implementations of LDAP have gone for different
779methods of handling error codes and generating error messages. */
780
781if (rc == -1 || result == NULL)
782 {
783 int err;
784 DEBUG(D_lookup) debug_printf("ldap_result failed\n");
785
786 #if defined LDAP_LIB_SOLARIS || defined LDAP_LIB_OPENLDAP2
787 ldap_get_option(lcp->ld, LDAP_OPT_ERROR_NUMBER, &err);
788 *errmsg = string_sprintf("ldap_result failed: %d, %s",
789 err, ldap_err2string(err));
790
791 #elif defined LDAP_LIB_NETSCAPE
792 /* Dubious (surely 'matched' is spurious here?) */
793 (void)ldap_get_lderrno(lcp->ld, &matched, &error1);
794 *errmsg = string_sprintf("ldap_result failed: %s (%s)", error1, matched);
795
796 #else /* UMich LDAP aka OpenLDAP 1.x */
797 *errmsg = string_sprintf("ldap_result failed: %d, %s",
798 lcp->ld->ld_errno, ldap_err2string(lcp->ld->ld_errno));
799 #endif
800
801 goto RETURN_ERROR;
802 }
803
804/* A return code that isn't -1 doesn't necessarily mean there were no problems
8e669ac1
PH
805with the search. The message must be an LDAP_RES_SEARCH_RESULT or
806LDAP_RES_SEARCH_REFERENCE or else it's something we can't handle. Some versions
807of LDAP do not define LDAP_RES_SEARCH_REFERENCE (LDAP v1 is one, it seems). So
3295e65b
PH
808we don't provide that functionality when we can't. :-) */
809
8e669ac1 810if (rc != LDAP_RES_SEARCH_RESULT
3295e65b
PH
811#ifdef LDAP_RES_SEARCH_REFERENCE
812 && rc != LDAP_RES_SEARCH_REFERENCE
8e669ac1 813#endif
3295e65b 814 )
0756eb3c
PH
815 {
816 *errmsg = string_sprintf("ldap_result returned unexpected code %d", rc);
817 goto RETURN_ERROR;
818 }
819
820/* We have a result message from the server. This doesn't yet mean all is well.
821We need to parse the message to find out exactly what's happened. */
822
d38f8232
PH
823#if defined LDAP_LIB_SOLARIS || defined LDAP_LIB_OPENLDAP2
824 ldap_rc = rc;
8e669ac1 825 ldap_parse_rc = ldap_parse_result(lcp->ld, result, &rc, CSS &matched,
d38f8232
PH
826 CSS &error2, NULL, NULL, 0);
827 DEBUG(D_lookup) debug_printf("ldap_parse_result: %d\n", ldap_parse_rc);
8e669ac1 828 if (ldap_parse_rc < 0 &&
3295e65b 829 (ldap_parse_rc != LDAP_NO_RESULTS_RETURNED
8e669ac1 830 #ifdef LDAP_RES_SEARCH_REFERENCE
3295e65b 831 || ldap_rc != LDAP_RES_SEARCH_REFERENCE
8e669ac1 832 #endif
3295e65b 833 ))
0756eb3c 834 {
d38f8232 835 *errmsg = string_sprintf("ldap_parse_result failed %d", ldap_parse_rc);
0756eb3c
PH
836 goto RETURN_ERROR;
837 }
838 error1 = US ldap_err2string(rc);
839
840#elif defined LDAP_LIB_NETSCAPE
841 /* Dubious (it doesn't reference 'result' at all!) */
842 rc = ldap_get_lderrno(lcp->ld, &matched, &error1);
843
844#else /* UMich LDAP aka OpenLDAP 1.x */
845 rc = ldap_result2error(lcp->ld, result, 0);
846 error1 = ldap_err2string(rc);
847 error2 = lcp->ld->ld_error;
848 matched = lcp->ld->ld_matched;
849#endif
850
851/* Process the status as follows:
852
853 (1) If we get LDAP_SIZELIMIT_EXCEEDED, just carry on, to return the
854 truncated result list.
855
21eb6e72
PH
856 (2) If we get LDAP_RES_SEARCH_REFERENCE, also just carry on. This was a
857 submitted patch that is reported to "do the right thing" with Solaris
858 LDAP libraries. (The problem it addresses apparently does not occur with
859 Open LDAP.)
860
861 (3) The range of errors defined by LDAP_NAME_ERROR generally mean "that
0756eb3c
PH
862 object does not, or cannot, exist in the database". For those cases we
863 fail the lookup.
864
21eb6e72 865 (4) All other non-successes here are treated as some kind of problem with
0756eb3c
PH
866 the lookup, so return DEFER (which is the default in error_yield).
867*/
868
869DEBUG(D_lookup) debug_printf("ldap_parse_result yielded %d: %s\n",
870 rc, ldap_err2string(rc));
871
21eb6e72
PH
872if (rc != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED
873 #ifdef LDAP_RES_SEARCH_REFERENCE
874 && rc != LDAP_RES_SEARCH_REFERENCE
875 #endif
876 )
0756eb3c
PH
877 {
878 *errmsg = string_sprintf("LDAP search failed - error %d: %s%s%s%s%s",
879 rc,
880 (error1 != NULL)? error1 : US"",
881 (error2 != NULL && error2[0] != 0)? US"/" : US"",
882 (error2 != NULL)? error2 : US"",
883 (matched != NULL && matched[0] != 0)? US"/" : US"",
884 (matched != NULL)? matched : US"");
885
886 #if defined LDAP_NAME_ERROR
887 if (LDAP_NAME_ERROR(rc))
888 #elif defined NAME_ERROR /* OPENLDAP1 calls it this */
889 if (NAME_ERROR(rc))
890 #else
891 if (rc == LDAP_NO_SUCH_OBJECT)
892 #endif
893
894 {
895 DEBUG(D_lookup) debug_printf("lookup failure forced\n");
896 error_yield = FAIL;
897 }
898 goto RETURN_ERROR;
899 }
900
901/* The search succeeded. Check if we have too many results */
902
903if (search_type != SEARCH_LDAP_MULTIPLE && rescount > 1)
904 {
905 *errmsg = string_sprintf("LDAP search: more than one entry (%d) was returned "
906 "(filter not specific enough?)", rescount);
907 goto RETURN_ERROR_BREAK;
908 }
909
910/* Check if we have too few (zero) entries */
911
912if (rescount < 1)
913 {
914 *errmsg = string_sprintf("LDAP search: no results");
915 error_yield = FAIL;
916 goto RETURN_ERROR_BREAK;
917 }
918
919/* If an entry was found, but it had no attributes, we behave as if no entries
920were found, that is, the lookup failed. */
921
922if (!attribute_found)
923 {
924 *errmsg = US"LDAP search: found no attributes";
925 error_yield = FAIL;
926 goto RETURN_ERROR;
927 }
928
929/* Otherwise, it's all worked */
930
931DEBUG(D_lookup) debug_printf("LDAP search: returning: %s\n", data);
932*res = data;
933
934RETURN_OK:
935if (result != NULL) ldap_msgfree(result);
936ldap_free_urldesc(ludp);
937return OK;
938
939/* Error returns */
940
941RETURN_ERROR_BREAK:
942*defer_break = TRUE;
943
944RETURN_ERROR:
945DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
946
947RETURN_ERROR_NOMSG:
948if (result != NULL) ldap_msgfree(result);
949if (ludp != NULL) ldap_free_urldesc(ludp);
950
951#if defined LDAP_LIB_OPENLDAP2
952 if (error2 != NULL) ldap_memfree(error2);
953 if (matched != NULL) ldap_memfree(matched);
954#endif
955
956return error_yield;
957}
958
959
960
961/*************************************************
962* Internal search control function *
963*************************************************/
964
965/* This function is called from eldap_find(), eldapauth_find(), eldapdn_find(),
966and eldapm_find() with a difference in the "search_type" argument. It controls
967calls to perform_ldap_search() which actually does the work. We call that
968repeatedly for certain types of defer in the case when the URL contains no host
969name and eldap_default_servers is set to a list of servers to try. This gives
970more control than just passing over a list of hosts to ldap_open() because it
971handles other kinds of defer as well as just a failure to open. Note that the
972URL is defined to contain either zero or one "hostport" only.
973
974Parameter data in addition to the URL can be passed as preceding text in the
975string, as items of the form XXX=yyy. The URL itself can be detected because it
976must begin "ldapx://", where x is empty, s, or i.
977
978Arguments:
979 ldap_url the URL to be looked up, optionally preceded by other parameter
980 settings
981 search_type SEARCH_LDAP_MULTIPLE allows values from multiple entries
982 SEARCH_LDAP_SINGLE allows values from one entry only
983 SEARCH_LDAP_DN gets the DN from one entry
984 res set to point at the result
985 errmsg set to point a message if result is not OK
986
987Returns: OK or FAIL or DEFER
988*/
989
990static int
991control_ldap_search(uschar *ldap_url, int search_type, uschar **res,
992 uschar **errmsg)
993{
994BOOL defer_break = FALSE;
995int timelimit = LDAP_NO_LIMIT;
996int sizelimit = LDAP_NO_LIMIT;
7c7ad977 997int tcplimit = 0;
0756eb3c 998int sep = 0;
6ec97b1b
PH
999int dereference = LDAP_DEREF_NEVER;
1000void* referrals = LDAP_OPT_ON;
0756eb3c
PH
1001uschar *url = ldap_url;
1002uschar *p;
1003uschar *user = NULL;
1004uschar *password = NULL;
1005uschar *server, *list;
1006uschar buffer[512];
1007
1008while (isspace(*url)) url++;
1009
1010/* Until the string begins "ldap", search for the other parameter settings that
1011are recognized. They are of the form NAME=VALUE, with the value being
1012optionally double-quoted. There must still be a space after it, however. No
1013NAME has the value "ldap". */
1014
1015while (strncmpic(url, US"ldap", 4) != 0)
1016 {
1017 uschar *name = url;
1018 while (*url != 0 && *url != '=') url++;
1019 if (*url == '=')
1020 {
1021 int namelen;
1022 uschar *value;
1023 namelen = ++url - name;
1024 value = string_dequote(&url);
1025 if (isspace(*url))
1026 {
1027 if (strncmpic(name, US"USER=", namelen) == 0) user = value;
1028 else if (strncmpic(name, US"PASS=", namelen) == 0) password = value;
1029 else if (strncmpic(name, US"SIZE=", namelen) == 0) sizelimit = Uatoi(value);
1030 else if (strncmpic(name, US"TIME=", namelen) == 0) timelimit = Uatoi(value);
7c7ad977
PH
1031 else if (strncmpic(name, US"CONNECT=", namelen) == 0) tcplimit = Uatoi(value);
1032 else if (strncmpic(name, US"NETTIME=", namelen) == 0) tcplimit = Uatoi(value);
0756eb3c
PH
1033
1034 /* Don't know if all LDAP libraries have LDAP_OPT_DEREF */
1035
1036 #ifdef LDAP_OPT_DEREF
1037 else if (strncmpic(name, US"DEREFERENCE=", namelen) == 0)
1038 {
1039 if (strcmpic(value, US"never") == 0) dereference = LDAP_DEREF_NEVER;
1040 else if (strcmpic(value, US"searching") == 0)
1041 dereference = LDAP_DEREF_SEARCHING;
1042 else if (strcmpic(value, US"finding") == 0)
1043 dereference = LDAP_DEREF_FINDING;
1044 if (strcmpic(value, US"always") == 0) dereference = LDAP_DEREF_ALWAYS;
1045 }
1046 #else
1047 else if (strncmpic(name, US"DEREFERENCE=", namelen) == 0)
1048 {
1049 *errmsg = string_sprintf("LDAP_OP_DEREF not defined in this LDAP "
1050 "library - cannot use \"dereference\"");
1051 DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
1052 return DEFER;
1053 }
6ec97b1b 1054 #endif
0756eb3c 1055
6ec97b1b
PH
1056 #ifdef LDAP_OPT_REFERRALS
1057 else if (strncmpic(name, US"REFERRALS=", namelen) == 0)
1058 {
1059 if (strcmpic(value, US"follow") == 0) referrals = LDAP_OPT_ON;
1060 else if (strcmpic(value, US"nofollow") == 0) referrals = LDAP_OPT_OFF;
1061 else
1062 {
1063 *errmsg = string_sprintf("LDAP option REFERRALS is not \"follow\" "
1064 "or \"nofollow\"");
1065 DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
1066 return DEFER;
1067 }
1068 }
1069 #else
1070 else if (strncmpic(name, US"REFERRALS=", namelen) == 0)
1071 {
1072 *errmsg = string_sprintf("LDAP_OP_REFERRALS not defined in this LDAP "
1073 "library - cannot use \"referrals\"");
1074 DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
1075 return DEFER;
1076 }
0756eb3c
PH
1077 #endif
1078
1079 else
1080 {
1081 *errmsg =
1082 string_sprintf("unknown parameter \"%.*s\" precedes LDAP URL",
1083 namelen, name);
1084 DEBUG(D_lookup) debug_printf("LDAP query error: %s\n", *errmsg);
1085 return DEFER;
1086 }
1087 while (isspace(*url)) url++;
1088 continue;
1089 }
1090 }
1091 *errmsg = US"malformed parameter setting precedes LDAP URL";
1092 DEBUG(D_lookup) debug_printf("LDAP query error: %s\n", *errmsg);
1093 return DEFER;
1094 }
1095
1096/* If user is set, de-URL-quote it. Some LDAP libraries do this for themselves,
1097but it seems that not all behave like this. The DN for the user is often the
1098result of ${quote_ldap_dn:...} quoting, which does apply URL quoting, because
1099that is needed when the DN is used as a base DN in a query. Sigh. This is all
1100far too complicated. */
1101
1102if (user != NULL)
1103 {
1104 uschar *s;
1105 uschar *t = user;
1106 for (s = user; *s != 0; s++)
1107 {
1108 int c, d;
1109 if (*s == '%' && isxdigit(c=s[1]) && isxdigit(d=s[2]))
1110 {
1111 c = tolower(c);
1112 d = tolower(d);
1113 *t++ =
1114 (((c >= 'a')? (10 + c - 'a') : c - '0') << 4) |
1115 ((d >= 'a')? (10 + d - 'a') : d - '0');
1116 s += 2;
1117 }
1118 else *t++ = *s;
1119 }
1120 *t = 0;
1121 }
1122
1123DEBUG(D_lookup)
1124 debug_printf("LDAP parameters: user=%s pass=%s size=%d time=%d connect=%d "
6ec97b1b
PH
1125 "dereference=%d referrals=%s\n", user, password, sizelimit, timelimit,
1126 tcplimit, dereference, (referrals == LDAP_OPT_ON)? "on" : "off");
0756eb3c
PH
1127
1128/* If the request is just to check authentication, some credentials must
1129be given. The password must not be empty because LDAP binds with an empty
1130password are considered anonymous, and will succeed on most installations. */
1131
1132if (search_type == SEARCH_LDAP_AUTH)
1133 {
1134 if (user == NULL || password == NULL)
1135 {
1136 *errmsg = US"ldapauth lookups must specify the username and password";
1137 return DEFER;
1138 }
1139 if (password[0] == 0)
1140 {
1141 DEBUG(D_lookup) debug_printf("Empty password: ldapauth returns FAIL\n");
1142 return FAIL;
1143 }
1144 }
1145
1146/* Check for valid ldap url starters */
1147
1148p = url + 4;
1149if (tolower(*p) == 's' || tolower(*p) == 'i') p++;
1150if (Ustrncmp(p, "://", 3) != 0)
1151 {
1152 *errmsg = string_sprintf("LDAP URL does not start with \"ldap://\", "
1153 "\"ldaps://\", or \"ldapi://\" (it starts with \"%.16s...\")", url);
1154 DEBUG(D_lookup) debug_printf("LDAP query error: %s\n", *errmsg);
1155 return DEFER;
1156 }
1157
1158/* No default servers, or URL contains a server name: just one attempt */
1159
1160if (eldap_default_servers == NULL || p[3] != '/')
1161 {
1162 return perform_ldap_search(url, NULL, 0, search_type, res, errmsg,
6ec97b1b
PH
1163 &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference,
1164 referrals);
0756eb3c
PH
1165 }
1166
1167/* Loop through the default servers until OK or FAIL */
1168
1169list = eldap_default_servers;
1170while ((server = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
1171 {
1172 int rc;
1173 int port = 0;
1174 uschar *colon = Ustrchr(server, ':');
1175 if (colon != NULL)
1176 {
1177 *colon = 0;
1178 port = Uatoi(colon+1);
1179 }
1180 rc = perform_ldap_search(url, server, port, search_type, res, errmsg,
6ec97b1b
PH
1181 &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference,
1182 referrals);
0756eb3c
PH
1183 if (rc != DEFER || defer_break) return rc;
1184 }
1185
1186return DEFER;
1187}
1188
1189
1190
1191/*************************************************
1192* Find entry point *
1193*************************************************/
1194
1195/* See local README for interface description. The different kinds of search
1196are handled by a common function, with a flag to differentiate between them.
1197The handle and filename arguments are not used. */
1198
1199int
1200eldap_find(void *handle, uschar *filename, uschar *ldap_url, int length,
1201 uschar **result, uschar **errmsg, BOOL *do_cache)
1202{
1203/* Keep picky compilers happy */
1204do_cache = do_cache;
1205return(control_ldap_search(ldap_url, SEARCH_LDAP_SINGLE, result, errmsg));
1206}
1207
1208int
1209eldapm_find(void *handle, uschar *filename, uschar *ldap_url, int length,
1210 uschar **result, uschar **errmsg, BOOL *do_cache)
1211{
1212/* Keep picky compilers happy */
1213do_cache = do_cache;
1214return(control_ldap_search(ldap_url, SEARCH_LDAP_MULTIPLE, result, errmsg));
1215}
1216
1217int
1218eldapdn_find(void *handle, uschar *filename, uschar *ldap_url, int length,
1219 uschar **result, uschar **errmsg, BOOL *do_cache)
1220{
1221/* Keep picky compilers happy */
1222do_cache = do_cache;
1223return(control_ldap_search(ldap_url, SEARCH_LDAP_DN, result, errmsg));
1224}
1225
1226int
1227eldapauth_find(void *handle, uschar *filename, uschar *ldap_url, int length,
1228 uschar **result, uschar **errmsg, BOOL *do_cache)
1229{
1230/* Keep picky compilers happy */
1231do_cache = do_cache;
1232return(control_ldap_search(ldap_url, SEARCH_LDAP_AUTH, result, errmsg));
1233}
1234
1235
1236
1237/*************************************************
1238* Open entry point *
1239*************************************************/
1240
1241/* See local README for interface description. */
1242
1243void *
1244eldap_open(uschar *filename, uschar **errmsg)
1245{
1246return (void *)(1); /* Just return something non-null */
1247}
1248
1249
1250
1251/*************************************************
1252* Tidy entry point *
1253*************************************************/
1254
1255/* See local README for interface description.
1256Make sure that eldap_dn does not refer to reclaimed or worse, freed store */
1257
1258void
1259eldap_tidy(void)
1260{
1261LDAP_CONNECTION *lcp = NULL;
1262eldap_dn = NULL;
1263
1264while ((lcp = ldap_connections) != NULL)
1265 {
1266 DEBUG(D_lookup) debug_printf("unbind LDAP connection to %s:%d\n", lcp->host,
1267 lcp->port);
1268 ldap_unbind(lcp->ld);
1269 ldap_connections = lcp->next;
1270 }
1271}
1272
1273
1274
1275/*************************************************
1276* Quote entry point *
1277*************************************************/
1278
1279/* LDAP quoting is unbelievably messy. For a start, two different levels of
1280quoting have to be done: LDAP quoting, and URL quoting. The current
1281specification is the result of a suggestion by Brian Candler. It recognizes
1282two separate cases:
1283
1284(1) For text that appears in a search filter, the following escapes are
1285 required (see RFC 2254):
1286
1287 * -> \2A
1288 ( -> \28
1289 ) -> \29
1290 \ -> \5C
1291 NULL -> \00
1292
1293 Then the entire filter text must be URL-escaped. This kind of quoting is
1294 implemented by ${quote_ldap:....}. Note that we can never have a NULL
1295 in the input string, because that's a terminator.
1296
1297(2) For a DN that is part of a URL (i.e. the base DN), the characters
1298
1299 , + " \ < > ;
1300
1301 must be quoted by backslashing. See RFC 2253. Leading and trailing spaces
1302 must be escaped, as must a leading #. Then the string must be URL-quoted.
1303 This type of quoting is implemented by ${quote_ldap_dn:....}.
1304
1305For URL quoting, the only characters that need not be quoted are the
1306alphamerics and
1307
1308 ! $ ' ( ) * + - . _
1309
1310All the others must be hexified and preceded by %. This includes the
1311backslashes used for LDAP quoting.
1312
1313For a DN that is given in the USER parameter for authentication, we need the
1314same initial quoting as (2) but in this case, the result must NOT be
1315URL-escaped, because it isn't a URL. The way this is handled is by
1316de-URL-quoting the text when processing the USER parameter in
1317control_ldap_search() above. That means that the same quote operator can be
1318used. This has the additional advantage that spaces in the DN won't cause
1319parsing problems. For example:
1320
1321 USER=cn=${quote_ldap_dn:$1},%20dc=example,%20dc=com
1322
1323should be safe if there are spaces in $1.
1324
1325
1326Arguments:
1327 s the string to be quoted
1328 opt additional option text or NULL if none
1329 only "dn" is recognized
1330
1331Returns: the processed string or NULL for a bad option
1332*/
1333
1334
1335
1336/* The characters in this string, together with alphanumerics, never need
1337quoting in any way. */
1338
1339#define ALWAYS_LITERAL "!$'-._"
1340
1341/* The special characters in this string do not need to be URL-quoted. The set
1342is a bit larger than the general literals. */
1343
1344#define URL_NONQUOTE ALWAYS_LITERAL "()*+"
1345
1346/* The following macros define the characters that are quoted by quote_ldap and
1347quote_ldap_dn, respectively. */
1348
1349#define LDAP_QUOTE "*()\\"
1350#define LDAP_DN_QUOTE ",+\"\\<>;"
1351
1352
1353
1354uschar *
1355eldap_quote(uschar *s, uschar *opt)
1356{
1357register int c;
1358int count = 0;
1359int len = 0;
1360BOOL dn = FALSE;
1361uschar *t = s;
1362uschar *quoted;
1363
1364/* Test for a DN quotation. */
1365
1366if (opt != NULL)
1367 {
1368 if (Ustrcmp(opt, "dn") != 0) return NULL; /* No others recognized */
1369 dn = TRUE;
1370 }
1371
1372/* Compute how much extra store we need for the string. This doesn't have to be
1373exact as long as it isn't an underestimate. The worst case is the addition of 5
1374extra bytes for a single character. This occurs for certain characters in DNs,
1375where, for example, < turns into %5C%3C. For simplicity, we just add 5 for each
1376possibly escaped character. The really fast way would be just to test for
1377non-alphanumerics, but it is probably better to spot a few others that are
1378never escaped, because if there are no specials at all, we can avoid copying
1379the string. */
1380
1381while ((c = *t++) != 0)
1382 {
1383 len++;
1384 if (!isalnum(c) && Ustrchr(ALWAYS_LITERAL, c) == NULL) count += 5;
1385 }
1386if (count == 0) return s;
1387
1388/* Get sufficient store to hold the quoted string */
1389
1390t = quoted = store_get(len + count + 1);
1391
1392/* Handle plain quote_ldap */
1393
1394if (!dn)
1395 {
1396 while ((c = *s++) != 0)
1397 {
1398 if (!isalnum(c))
1399 {
1400 if (Ustrchr(LDAP_QUOTE, c) != NULL)
1401 {
1402 sprintf(CS t, "%%5C%02X", c); /* e.g. * => %5C2A */
1403 t += 5;
1404 continue;
1405 }
1406 if (Ustrchr(URL_NONQUOTE, c) == NULL) /* e.g. ] => %5D */
1407 {
1408 sprintf(CS t, "%%%02X", c);
1409 t += 3;
1410 continue;
1411 }
1412 }
1413 *t++ = c; /* unquoted character */
1414 }
1415 }
1416
1417/* Handle quote_ldap_dn */
1418
1419else
1420 {
1421 uschar *ss = s + len;
1422
1423 /* Find the last char before any trailing spaces */
1424
1425 while (ss > s && ss[-1] == ' ') ss--;
1426
1427 /* Quote leading spaces and sharps */
1428
1429 for (; s < ss; s++)
1430 {
1431 if (*s != ' ' && *s != '#') break;
1432 sprintf(CS t, "%%5C%%%02X", *s);
1433 t += 6;
1434 }
1435
1436 /* Handle the rest of the string, up to the trailing spaces */
1437
1438 while (s < ss)
1439 {
1440 c = *s++;
1441 if (!isalnum(c))
1442 {
1443 if (Ustrchr(LDAP_DN_QUOTE, c) != NULL)
1444 {
1445 Ustrncpy(t, "%5C", 3); /* insert \ where needed */
1446 t += 3; /* fall through to check URL */
1447 }
1448 if (Ustrchr(URL_NONQUOTE, c) == NULL) /* e.g. ] => %5D */
1449 {
1450 sprintf(CS t, "%%%02X", c);
1451 t += 3;
1452 continue;
1453 }
1454 }
1455 *t++ = c; /* unquoted character, or non-URL quoted after %5C */
1456 }
1457
1458 /* Handle the trailing spaces */
1459
1460 while (*ss++ != 0)
1461 {
1462 Ustrncpy(t, "%5C%20", 6);
1463 t += 6;
1464 }
1465 }
1466
1467/* Terminate the new string and return */
1468
1469*t = 0;
1470return quoted;
1471}
1472
1473#endif /* LOOKUP_LDAP */
1474
1475/* End of lookups/ldap.c */