]> git.ipfire.org Git - thirdparty/squid.git/blob - helpers/digest_auth/LDAP/ldap_backend.cc
Merged Postfix-Prefix branch: refactor inc/decrement operators from postfix to prefix...
[thirdparty/squid.git] / helpers / digest_auth / LDAP / ldap_backend.cc
1 /*
2 *
3 *
4 *
5 * ldap_backend.c
6 * AUTHOR: Flavio Pescuma, MARA Systems AB <flavio@marasystems.com>
7 */
8 #include "squid.h"
9 #include "util.h"
10
11 #define LDAP_DEPRECATED 1
12
13 #include "ldap_backend.h"
14
15 #if _SQUID_MSWIN_ /* Native Windows port and MinGW */
16
17 #define snprintf _snprintf
18 #include <windows.h>
19 #include <winldap.h>
20 #ifndef LDAPAPI
21 #define LDAPAPI __cdecl
22 #endif
23 #ifdef LDAP_VERSION3
24 #ifndef LDAP_OPT_X_TLS
25 #define LDAP_OPT_X_TLS 0x6000
26 #endif
27 /* Some tricks to allow dynamic bind with ldap_start_tls_s entry point at
28 * run time.
29 */
30 #undef ldap_start_tls_s
31 #if LDAP_UNICODE
32 #define LDAP_START_TLS_S "ldap_start_tls_sW"
33 typedef WINLDAPAPI ULONG(LDAPAPI * PFldap_start_tls_s) (IN PLDAP, OUT PULONG, OUT LDAPMessage **, IN PLDAPControlW *, IN PLDAPControlW *);
34 #else
35 #define LDAP_START_TLS_S "ldap_start_tls_sA"
36 typedef WINLDAPAPI ULONG(LDAPAPI * PFldap_start_tls_s) (IN PLDAP, OUT PULONG, OUT LDAPMessage **, IN PLDAPControlA *, IN PLDAPControlA *);
37 #endif /* LDAP_UNICODE */
38 PFldap_start_tls_s Win32_ldap_start_tls_s;
39 #define ldap_start_tls_s(l,s,c) Win32_ldap_start_tls_s(l,NULL,NULL,s,c)
40 #endif /* LDAP_VERSION3 */
41
42 #else
43
44 #include <lber.h>
45 #include <ldap.h>
46
47 #endif
48 #define PROGRAM_NAME "digest_pw_auth(LDAP_backend)"
49
50 /* Globals */
51
52 static LDAP *ld = NULL;
53 static const char *passattr = NULL;
54 static char *ldapServer = NULL;
55 static const char *userbasedn = NULL;
56 static const char *userdnattr = NULL;
57 static const char *usersearchfilter = NULL;
58 static const char *binddn = NULL;
59 static const char *bindpasswd = NULL;
60 static const char *delimiter = ":";
61 static int encrpass = 0;
62 static int searchscope = LDAP_SCOPE_SUBTREE;
63 static int persistent = 0;
64 static int noreferrals = 0;
65 static int port = LDAP_PORT;
66 static int strip_nt_domain = 0;
67 static int aliasderef = LDAP_DEREF_NEVER;
68 #if defined(NETSCAPE_SSL)
69 static char *sslpath = NULL;
70 static int sslinit = 0;
71 #endif
72 static int connect_timeout = 0;
73 static int timelimit = LDAP_NO_LIMIT;
74
75 #ifdef LDAP_VERSION3
76 /* Added for TLS support and version 3 */
77 static int use_tls = 0;
78 static int version = -1;
79 #endif
80
81 static void ldapconnect(void);
82 static int readSecret(const char *filename);
83
84 /* Yuck.. we need to glue to different versions of the API */
85
86 #if defined(LDAP_API_VERSION) && LDAP_API_VERSION > 1823
87 static void
88 squid_ldap_set_aliasderef(int deref)
89 {
90 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
91 }
92 static void
93 squid_ldap_set_referrals(int referrals)
94 {
95 int *value = static_cast<int*>(referrals ? LDAP_OPT_ON :LDAP_OPT_OFF);
96 ldap_set_option(ld, LDAP_OPT_REFERRALS, value);
97 }
98 static void
99 squid_ldap_set_timelimit(int aTimeLimit)
100 {
101 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &aTimeLimit);
102 }
103 static void
104 squid_ldap_set_connect_timeout(int aTimeLimit)
105 {
106 #if defined(LDAP_OPT_NETWORK_TIMEOUT)
107 struct timeval tv;
108 tv.tv_sec = aTimeLimit;
109 tv.tv_usec = 0;
110 ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
111 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
112 aTimeLimit *= 1000;
113 ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, &aTimeLimit);
114 #endif
115 }
116
117 #else
118 static int
119 squid_ldap_errno(LDAP * ld)
120 {
121 return ld->ld_errno;
122 }
123 static void
124 squid_ldap_set_aliasderef(int deref)
125 {
126 ld->ld_deref = deref;
127 }
128 static void
129 squid_ldap_set_referrals(int referrals)
130 {
131 if (referrals)
132 ld->ld_options |= ~LDAP_OPT_REFERRALS;
133 else
134 ld->ld_options &= ~LDAP_OPT_REFERRALS;
135 }
136 static void
137 squid_ldap_set_timelimit(int aTimeLimit)
138 {
139 ld->ld_timelimit = aTimeLimit;
140 }
141 static void
142 squid_ldap_set_connect_timeout(int aTimeLimit)
143 {
144 fprintf(stderr, "Connect timeouts not supported in your LDAP library\n");
145 }
146 static void
147 squid_ldap_memfree(char *p)
148 {
149 free(p);
150 }
151
152 #endif
153
154 #ifdef LDAP_API_FEATURE_X_OPENLDAP
155 #if LDAP_VENDOR_VERSION > 194
156 #define HAS_URI_SUPPORT 1
157 #endif
158 #endif
159
160 static int
161 ldap_escape_value(char *escaped, int size, const char *src)
162 {
163 int n = 0;
164 while (size > 4 && *src) {
165 switch (*src) {
166 case '*':
167 case '(':
168 case ')':
169 case '\\':
170 n += 3;
171 size -= 3;
172 if (size > 0) {
173 *escaped = '\\';
174 ++escaped;
175 snprintf(escaped, 3, "%02x", (int) *src);
176 ++src;
177 escaped += 2;
178 }
179 break;
180 default:
181 *escaped = *src;
182 ++escaped;
183 ++src;
184 ++n;
185 --size;
186 }
187 }
188 *escaped = '\0';
189 return n;
190 }
191
192 static char *
193 getpassword(char *login, char *realm)
194 {
195 LDAPMessage *res = NULL;
196 LDAPMessage *entry;
197 char **values = NULL;
198 char **value = NULL;
199 char *password = NULL;
200 int retry = 0;
201 char filter[8192];
202 char searchbase[8192];
203 int rc = -1;
204 if (ld) {
205 if (usersearchfilter) {
206 char escaped_login[1024];
207 snprintf(searchbase, sizeof(searchbase), "%s", userbasedn);
208 ldap_escape_value(escaped_login, sizeof(escaped_login), login);
209 snprintf(filter, sizeof(filter), usersearchfilter, escaped_login, escaped_login, escaped_login, escaped_login, escaped_login, escaped_login, escaped_login, escaped_login, escaped_login, escaped_login, escaped_login, escaped_login, escaped_login, escaped_login, escaped_login, escaped_login);
210
211 retrysrch:
212 debug("user filter '%s', searchbase '%s'\n", filter, searchbase);
213
214 rc = ldap_search_s(ld, searchbase, searchscope, filter, NULL, 0, &res);
215 if (rc != LDAP_SUCCESS) {
216 if (noreferrals && rc == LDAP_PARTIAL_RESULTS) {
217 /* Everything is fine. This is expected when referrals
218 * are disabled.
219 */
220 rc = LDAP_SUCCESS;
221 } else {
222 fprintf(stderr, PROGRAM_NAME " WARNING, LDAP search error '%s'\n", ldap_err2string(rc));
223 #if defined(NETSCAPE_SSL)
224 if (sslpath && ((rc == LDAP_SERVER_DOWN) || (rc == LDAP_CONNECT_ERROR))) {
225 int sslerr = PORT_GetError();
226 fprintf(stderr, PROGRAM_NAME ": WARNING, SSL error %d (%s)\n", sslerr, ldapssl_err2string(sslerr));
227 }
228 #endif
229 fprintf(stderr, PROGRAM_NAME " WARNING, LDAP search error, trying to recover'%s'\n", ldap_err2string(rc));
230 ldap_msgfree(res);
231 /* try to connect to the LDAP server agin, maybe my persisten conexion failed. */
232 if (!retry) {
233 ++retry;
234 ldap_unbind(ld);
235 ld = NULL;
236 ldapconnect();
237 goto retrysrch;
238 }
239 return NULL;
240
241 }
242 }
243 } else if (userdnattr) {
244 snprintf(filter,8192,"%s=%s",userdnattr,login);
245
246 retrydnattr:
247 debug("searchbase '%s'\n", userbasedn);
248 rc = ldap_search_s(ld, userbasedn, searchscope, filter, NULL, 0, &res);
249 }
250 if (rc == LDAP_SUCCESS) {
251 entry = ldap_first_entry(ld, res);
252 if (entry)
253 values = ldap_get_values(ld, entry, passattr);
254 else {
255 ldap_msgfree(res);
256 return NULL;
257 }
258 if (!values) {
259 debug("No attribute value found\n");
260 ldap_msgfree(res);
261 return NULL;
262 }
263 value = values;
264 while (*value) {
265 if (encrpass) {
266 if (strcmp(strtok(*value, delimiter), realm) == 0) {
267 password = strtok(NULL, delimiter);
268 break;
269 }
270 } else {
271 password = *value;
272 break;
273 }
274 ++value;
275 }
276 debug("password: %s\n", password);
277 if (password)
278 password = xstrdup(password);
279 ldap_value_free(values);
280 ldap_msgfree(res);
281 return password;
282 } else {
283 fprintf(stderr, PROGRAM_NAME " WARNING, LDAP error '%s'\n", ldap_err2string(rc));
284 /* try to connect to the LDAP server agin, maybe my persisten conexion failed. */
285 if (!retry) {
286 ++retry;
287 ldap_unbind(ld);
288 ld = NULL;
289 ldapconnect();
290 goto retrydnattr;
291 }
292 return NULL;
293 }
294 }
295 return NULL;
296 }
297
298
299
300 static void
301 ldapconnect(void)
302 {
303 int rc;
304
305 /* On Windows ldap_start_tls_s is available starting from Windows XP,
306 * so we need to bind at run-time with the function entry point
307 */
308 #if _SQUID_MSWIN_
309 if (use_tls) {
310
311 HMODULE WLDAP32Handle;
312
313 WLDAP32Handle = GetModuleHandle("wldap32");
314 if ((Win32_ldap_start_tls_s = (PFldap_start_tls_s) GetProcAddress(WLDAP32Handle, LDAP_START_TLS_S)) == NULL) {
315 fprintf(stderr, PROGRAM_NAME ": ERROR: TLS (-Z) not supported on this platform.\n");
316 exit(1);
317 }
318 }
319 #endif
320
321 if (ld == NULL) {
322 #if HAS_URI_SUPPORT
323 if (strstr(ldapServer, "://") != NULL) {
324 rc = ldap_initialize(&ld, ldapServer);
325 if (rc != LDAP_SUCCESS) {
326 fprintf(stderr, "\nUnable to connect to LDAPURI:%s\n", ldapServer);
327 }
328 } else
329 #endif
330 #if NETSCAPE_SSL
331 if (sslpath) {
332 if (!sslinit && (ldapssl_client_init(sslpath, NULL) != LDAP_SUCCESS)) {
333 fprintf(stderr, "\nUnable to initialise SSL with cert path %s\n",
334 sslpath);
335 exit(1);
336 } else {
337 ++sslinit;
338 }
339 if ((ld = ldapssl_init(ldapServer, port, 1)) == NULL) {
340 fprintf(stderr, "\nUnable to connect to SSL LDAP server: %s port:%d\n",
341 ldapServer, port);
342 exit(1);
343 }
344 } else
345 #endif
346 if ((ld = ldap_init(ldapServer, port)) == NULL) {
347 fprintf(stderr, "\nUnable to connect to LDAP server:%s port:%d\n", ldapServer, port);
348 }
349 if (connect_timeout)
350 squid_ldap_set_connect_timeout(connect_timeout);
351
352 #ifdef LDAP_VERSION3
353 if (version == -1) {
354 version = LDAP_VERSION2;
355 }
356 if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version)
357 != LDAP_SUCCESS) {
358 fprintf(stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n",
359 version);
360 ldap_unbind(ld);
361 ld = NULL;
362 }
363 if (use_tls) {
364 #ifdef LDAP_OPT_X_TLS
365 if (version != LDAP_VERSION3) {
366 fprintf(stderr, "TLS requires LDAP version 3\n");
367 exit(1);
368 } else if (ldap_start_tls_s(ld, NULL, NULL) != LDAP_SUCCESS) {
369 fprintf(stderr, "Could not Activate TLS connection\n");
370 exit(1);
371 }
372 #else
373 fprintf(stderr, "TLS not supported with your LDAP library\n");
374 ldap_unbind(ld);
375 ld = NULL;
376 #endif
377 }
378 #endif
379 squid_ldap_set_timelimit(timelimit);
380 squid_ldap_set_referrals(!noreferrals);
381 squid_ldap_set_aliasderef(aliasderef);
382 if (binddn && bindpasswd && *binddn && *bindpasswd) {
383 rc = ldap_simple_bind_s(ld, binddn, bindpasswd);
384 if (rc != LDAP_SUCCESS) {
385 fprintf(stderr, PROGRAM_NAME " WARNING, could not bind to binddn '%s'\n", ldap_err2string(rc));
386 ldap_unbind(ld);
387 ld = NULL;
388 }
389 }
390 debug("Connected OK\n");
391 }
392 }
393 int
394 LDAPArguments(int argc, char **argv)
395 {
396 setbuf(stdout, NULL);
397
398 while (argc > 1 && argv[1][0] == '-') {
399 const char *value = "";
400 char option = argv[1][1];
401 switch (option) {
402 case 'P':
403 case 'R':
404 case 'z':
405 case 'Z':
406 case 'g':
407 case 'e':
408 case 'S':
409 break;
410 default:
411 if (strlen(argv[1]) > 2) {
412 value = argv[1] + 2;
413 } else if (argc > 2) {
414 value = argv[2];
415 ++argv;
416 --argc;
417 } else
418 value = "";
419 break;
420 }
421 ++argv;
422 --argc;
423 switch (option) {
424 case 'H':
425 #if !HAS_URI_SUPPORT
426 fprintf(stderr, "ERROR: Your LDAP library does not have URI support\n");
427 return 1;
428 #endif
429 /* Fall thru to -h */
430 case 'h':
431 if (ldapServer) {
432 int len = strlen(ldapServer) + 1 + strlen(value) + 1;
433 char *newhost = (char*)malloc(len);
434 snprintf(newhost, len, "%s %s", ldapServer, value);
435 free(ldapServer);
436 ldapServer = newhost;
437 } else {
438 ldapServer = xstrdup(value);
439 }
440 break;
441 case 'A':
442 passattr = value;
443 break;
444 case 'e':
445 encrpass = 1;
446 break;
447 case 'l':
448 delimiter = value;
449 break;
450 case 'b':
451 userbasedn = value;
452 break;
453 case 'F':
454 usersearchfilter = value;
455 break;
456 case 'u':
457 userdnattr = value;
458 break;
459 case 's':
460 if (strcmp(value, "base") == 0)
461 searchscope = LDAP_SCOPE_BASE;
462 else if (strcmp(value, "one") == 0)
463 searchscope = LDAP_SCOPE_ONELEVEL;
464 else if (strcmp(value, "sub") == 0)
465 searchscope = LDAP_SCOPE_SUBTREE;
466 else {
467 fprintf(stderr, PROGRAM_NAME " ERROR: Unknown search scope '%s'\n", value);
468 return 1;
469 }
470 break;
471 case 'S':
472 #if defined(NETSCAPE_SSL)
473 sslpath = value;
474 if (port == LDAP_PORT)
475 port = LDAPS_PORT;
476 #else
477 fprintf(stderr, PROGRAM_NAME " ERROR: -E unsupported with this LDAP library\n");
478 return 1;
479 #endif
480 break;
481 case 'c':
482 connect_timeout = atoi(value);
483 break;
484 case 't':
485 timelimit = atoi(value);
486 break;
487 case 'a':
488 if (strcmp(value, "never") == 0)
489 aliasderef = LDAP_DEREF_NEVER;
490 else if (strcmp(value, "always") == 0)
491 aliasderef = LDAP_DEREF_ALWAYS;
492 else if (strcmp(value, "search") == 0)
493 aliasderef = LDAP_DEREF_SEARCHING;
494 else if (strcmp(value, "find") == 0)
495 aliasderef = LDAP_DEREF_FINDING;
496 else {
497 fprintf(stderr, PROGRAM_NAME " ERROR: Unknown alias dereference method '%s'\n", value);
498 return 1;
499 }
500 break;
501 case 'D':
502 binddn = value;
503 break;
504 case 'w':
505 bindpasswd = value;
506 break;
507 case 'W':
508 readSecret(value);
509 break;
510 case 'P':
511 persistent = !persistent;
512 break;
513 case 'p':
514 port = atoi(value);
515 break;
516 case 'R':
517 noreferrals = !noreferrals;
518 break;
519 #ifdef LDAP_VERSION3
520 case 'v':
521 switch (atoi(value)) {
522 case 2:
523 version = LDAP_VERSION2;
524 break;
525 case 3:
526 version = LDAP_VERSION3;
527 break;
528 default:
529 fprintf(stderr, "Protocol version should be 2 or 3\n");
530 return 1;
531 }
532 break;
533 case 'Z':
534 if (version == LDAP_VERSION2) {
535 fprintf(stderr, "TLS (-Z) is incompatible with version %d\n",
536 version);
537 return 1;
538 }
539 version = LDAP_VERSION3;
540 use_tls = 1;
541 break;
542 #endif
543 case 'd':
544 debug_enabled = 1;
545 break;
546 case 'E':
547 strip_nt_domain = 1;
548 break;
549 default:
550 fprintf(stderr, PROGRAM_NAME " ERROR: Unknown command line option '%c'\n", option);
551 return 1;
552 }
553 }
554
555 while (argc > 1) {
556 char *value = argv[1];
557 if (ldapServer) {
558 int len = strlen(ldapServer) + 1 + strlen(value) + 1;
559 char *newhost = (char*)malloc(len);
560 snprintf(newhost, len, "%s %s", ldapServer, value);
561 free(ldapServer);
562 ldapServer = newhost;
563 } else {
564 ldapServer = xstrdup(value);
565 }
566 --argc;
567 ++argv;
568 }
569
570 if (!ldapServer)
571 ldapServer = (char *) "localhost";
572
573 if (!userbasedn || !passattr) {
574 fprintf(stderr, "Usage: " PROGRAM_NAME " -b basedn -f filter [options] ldap_server_name\n\n");
575 fprintf(stderr, "\t-A password attribute(REQUIRED)\t\tUser attribute that contains the password\n");
576 fprintf(stderr, "\t-l password realm delimiter(REQUIRED)\tCharater(s) that devides the password attribute\n\t\t\t\t\t\tin realm and password tokens, default ':' realm:password\n");
577 fprintf(stderr, "\t-b basedn (REQUIRED)\t\t\tbase dn under where to search for users\n");
578 fprintf(stderr, "\t-e Encrypted passwords(REQUIRED)\tPassword are stored encrypted using HHA1\n");
579 fprintf(stderr, "\t-F filter\t\t\t\tuser search filter pattern. %%s = login\n");
580 fprintf(stderr, "\t-u attribute\t\t\t\tattribute to use in combination with the basedn to create the user DN\n");
581 fprintf(stderr, "\t-s base|one|sub\t\t\t\tsearch scope\n");
582 fprintf(stderr, "\t-D binddn\t\t\t\tDN to bind as to perform searches\n");
583 fprintf(stderr, "\t-w bindpasswd\t\t\t\tpassword for binddn\n");
584 fprintf(stderr, "\t-W secretfile\t\t\t\tread password for binddn from file secretfile\n");
585 #if HAS_URI_SUPPORT
586 fprintf(stderr, "\t-H URI\t\t\t\t\tLDAPURI (defaults to ldap://localhost)\n");
587 #endif
588 fprintf(stderr, "\t-h server\t\t\t\tLDAP server (defaults to localhost)\n");
589 fprintf(stderr, "\t-p port\t\t\t\t\tLDAP server port (defaults to %d)\n", LDAP_PORT);
590 fprintf(stderr, "\t-P\t\t\t\t\tpersistent LDAP connection\n");
591 #if defined(NETSCAPE_SSL)
592 fprintf(stderr, "\t-E sslcertpath\t\t\t\tenable LDAP over SSL\n");
593 #endif
594 fprintf(stderr, "\t-c timeout\t\t\t\tconnect timeout\n");
595 fprintf(stderr, "\t-t timelimit\t\t\t\tsearch time limit\n");
596 fprintf(stderr, "\t-R\t\t\t\t\tdo not follow referrals\n");
597 fprintf(stderr, "\t-a never|always|search|find\t\twhen to dereference aliases\n");
598 #ifdef LDAP_VERSION3
599 fprintf(stderr, "\t-v 2|3\t\t\t\t\tLDAP version\n");
600 fprintf(stderr, "\t-Z\t\t\t\t\tTLS encrypt the LDAP connection, requires\n\t\t\t\tLDAP version 3\n");
601 #endif
602 fprintf(stderr, "\t-S\t\t\t\t\tStrip NT domain from usernames\n");
603 fprintf(stderr, "\n");
604 fprintf(stderr, "\tIf you need to bind as a user to perform searches then use the\n\t-D binddn -w bindpasswd or -D binddn -W secretfile options\n\n");
605 return -1;
606 }
607 return 0;
608 }
609 static int
610 readSecret(const char *filename)
611 {
612 char buf[BUFSIZ];
613 char *e = 0;
614 FILE *f;
615
616 if (!(f = fopen(filename, "r"))) {
617 fprintf(stderr, PROGRAM_NAME " ERROR: Can not read secret file %s\n", filename);
618 return 1;
619 }
620 if (!fgets(buf, sizeof(buf) - 1, f)) {
621 fprintf(stderr, PROGRAM_NAME " ERROR: Secret file %s is empty\n", filename);
622 fclose(f);
623 return 1;
624 }
625 /* strip whitespaces on end */
626 if ((e = strrchr(buf, '\n')))
627 *e = 0;
628 if ((e = strrchr(buf, '\r')))
629 *e = 0;
630
631 bindpasswd = xstrdup(buf);
632 if (!bindpasswd) {
633 fprintf(stderr, PROGRAM_NAME " ERROR: can not allocate memory\n");
634 }
635 fclose(f);
636
637 return 0;
638 }
639
640 void
641 LDAPHHA1(RequestData * requestData)
642 {
643 char *password;
644 ldapconnect();
645 password = getpassword(requestData->user, requestData->realm);
646 if (password != NULL) {
647 if (encrpass)
648 xstrncpy(requestData->HHA1, password, sizeof(requestData->HHA1));
649 else {
650 HASH HA1;
651 DigestCalcHA1("md5", requestData->user, requestData->realm, password, NULL, NULL, HA1, requestData->HHA1);
652 }
653 free(password);
654 } else {
655 requestData->error = -1;
656 }
657
658 }