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