]> git.ipfire.org Git - thirdparty/squid.git/blame - helpers/external_acl/LDAP_group/ext_ldap_group_acl.cc
Boilerplate: update copyright blurbs on Squid helpers
[thirdparty/squid.git] / helpers / external_acl / LDAP_group / ext_ldap_group_acl.cc
CommitLineData
ca02e0ec
AJ
1/*
2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
8f7b71f7 9/*
c152a447
AJ
10 * ext_ldap_group_acl: lookup group membership in LDAP
11 *
12 * Version 2.17
6708c52c 13 *
251c9916 14 * (C)2002,2003 MARA Systems AB
6708c52c 15 *
16 * License: squid_ldap_group is free software; you can redistribute it
17 * and/or modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2,
19 * or (at your option) any later version.
26ac0430 20 *
6708c52c 21 * Authors:
22 * Flavio Pescuma <flavio@marasystems.com>
251c9916 23 * Henrik Nordstrom <hno@marasystems.com>
6708c52c 24 * MARA Systems AB, Sweden <http://www.marasystems.com>
387b9f71 25 *
5eecb267 26 * With contributions from others mentioned in the ChangeLog file
8f7b71f7 27 *
6708c52c 28 * In part based on squid_ldap_auth by Glen Newton and Henrik Nordstrom.
29 *
30 * Latest version of this program can always be found from MARA Systems
31 * at http://marasystems.com/download/LDAP_Group/
26ac0430 32 *
8f7b71f7 33 * Dependencies: You need to get the OpenLDAP libraries
6708c52c 34 * from http://www.openldap.org or use another compatible
35 * LDAP C-API library.
387b9f71 36 *
37 * If you want to make a TLS enabled connection you will also need the
38 * OpenSSL libraries linked into openldap. See http://www.openssl.org/
8f7b71f7 39 */
f7f3304a 40#include "squid.h"
c152a447 41#include "helpers/defines.h"
1fa9b1a7 42#include "rfc1738.h"
e39ea462 43#include "util.h"
44
c152a447
AJ
45#define LDAP_DEPRECATED 1
46
074d6a40
AJ
47#include <cctype>
48#include <cstring>
e39ea462 49
7aa9bb3e 50#if _SQUID_WINDOWS_ && !_SQUID_CYGWIN_
e39ea462 51
52#define snprintf _snprintf
53#include <windows.h>
54#include <winldap.h>
55#ifndef LDAPAPI
56#define LDAPAPI __cdecl
57#endif
58#ifdef LDAP_VERSION3
20e869bf 59#ifndef LDAP_OPT_X_TLS
60#define LDAP_OPT_X_TLS 0x6000
61#endif
e39ea462 62/* Some tricks to allow dynamic bind with ldap_start_tls_s entry point at
ae9f801d 63 * run time.
e39ea462 64 */
65#undef ldap_start_tls_s
66#if LDAP_UNICODE
67#define LDAP_START_TLS_S "ldap_start_tls_sW"
ae9f801d 68typedef WINLDAPAPI ULONG(LDAPAPI * PFldap_start_tls_s) (IN PLDAP, OUT PULONG, OUT LDAPMessage **, IN PLDAPControlW *, IN PLDAPControlW *);
e39ea462 69#else
70#define LDAP_START_TLS_S "ldap_start_tls_sA"
ae9f801d 71typedef WINLDAPAPI ULONG(LDAPAPI * PFldap_start_tls_s) (IN PLDAP, OUT PULONG, OUT LDAPMessage **, IN PLDAPControlA *, IN PLDAPControlA *);
e39ea462 72#endif /* LDAP_UNICODE */
73PFldap_start_tls_s Win32_ldap_start_tls_s;
74#define ldap_start_tls_s(l,s,c) Win32_ldap_start_tls_s(l,NULL,NULL,s,c)
75#endif /* LDAP_VERSION3 */
76
77#else
78
c152a447 79#if HAVE_LBER_H
8f7b71f7 80#include <lber.h>
c152a447
AJ
81#endif
82#if HAVE_LDAP_H
8f7b71f7 83#include <ldap.h>
c152a447 84#endif
e39ea462 85
86#endif
87
c152a447 88#define PROGRAM_NAME "ext_ldap_group_acl"
248109d2 89#define PROGRAM_VERSION "2.17"
6708c52c 90
91/* Globals */
92
e9505fad 93static const char *basedn = NULL;
94static const char *searchfilter = NULL;
95static const char *userbasedn = NULL;
96static const char *userdnattr = NULL;
97static const char *usersearchfilter = NULL;
98static const char *binddn = NULL;
99static const char *bindpasswd = NULL;
8f7b71f7 100static int searchscope = LDAP_SCOPE_SUBTREE;
101static int persistent = 0;
102static int noreferrals = 0;
8f7b71f7 103static int aliasderef = LDAP_DEREF_NEVER;
653b264e 104#if defined(NETSCAPE_SSL)
105static char *sslpath = NULL;
106static int sslinit = 0;
107#endif
108static int connect_timeout = 0;
109static int timelimit = LDAP_NO_LIMIT;
8f7b71f7 110
251c9916 111#ifdef LDAP_VERSION3
387b9f71 112/* Added for TLS support and version 3 */
113static int use_tls = 0;
114static int version = -1;
251c9916 115#endif
387b9f71 116
6708c52c 117static int searchLDAP(LDAP * ld, char *group, char *user, char *extension_dn);
8f7b71f7 118
e9505fad 119static int readSecret(const char *filename);
954a8513 120
8f7b71f7 121/* Yuck.. we need to glue to different versions of the API */
122
b10eaeab 123#ifndef LDAP_NO_ATTRS
124#define LDAP_NO_ATTRS "1.1"
125#endif
126
8f7b71f7 127#if defined(LDAP_API_VERSION) && LDAP_API_VERSION > 1823
ae9f801d 128static int
d440f560 129squid_ldap_errno(LDAP * ld)
8f7b71f7 130{
131 int err = 0;
132 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
133 return err;
134}
ae9f801d 135static void
d440f560 136squid_ldap_set_aliasderef(LDAP * ld, int deref)
8f7b71f7 137{
138 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
139}
ae9f801d 140static void
d440f560 141squid_ldap_set_referrals(LDAP * ld, int referrals)
8f7b71f7 142{
c152a447 143 int *value = static_cast<int*>(referrals ? LDAP_OPT_ON :LDAP_OPT_OFF);
8f7b71f7 144 ldap_set_option(ld, LDAP_OPT_REFERRALS, value);
145}
653b264e 146static void
d5f8d05f 147squid_ldap_set_timelimit(LDAP * ld, int aTimeLimit)
653b264e 148{
d5f8d05f 149 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &aTimeLimit);
653b264e 150}
151static void
d5f8d05f 152squid_ldap_set_connect_timeout(LDAP * ld, int aTimeLimit)
653b264e 153{
154#if defined(LDAP_OPT_NETWORK_TIMEOUT)
155 struct timeval tv;
d5f8d05f 156 tv.tv_sec = aTimeLimit;
653b264e 157 tv.tv_usec = 0;
158 ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
159#elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
d5f8d05f
FC
160 aTimeLimit *= 1000;
161 ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, &aTimeLimit);
653b264e 162#endif
163}
ae9f801d 164static void
d440f560 165squid_ldap_memfree(char *p)
387b9f71 166{
167 ldap_memfree(p);
168}
ae9f801d 169
8f7b71f7 170#else
ae9f801d 171static int
d440f560 172squid_ldap_errno(LDAP * ld)
8f7b71f7 173{
174 return ld->ld_errno;
175}
ae9f801d 176static void
d440f560 177squid_ldap_set_aliasderef(LDAP * ld, int deref)
8f7b71f7 178{
179 ld->ld_deref = deref;
180}
ae9f801d 181static void
d440f560 182squid_ldap_set_referrals(LDAP * ld, int referrals)
8f7b71f7 183{
184 if (referrals)
26ac0430 185 ld->ld_options |= ~LDAP_OPT_REFERRALS;
8f7b71f7 186 else
26ac0430 187 ld->ld_options &= ~LDAP_OPT_REFERRALS;
8f7b71f7 188}
653b264e 189static void
ae9f801d 190squid_ldap_set_timelimit(LDAP * ld, int timelimit)
653b264e 191{
192 ld->ld_timelimit = timelimit;
193}
194static void
ae9f801d 195squid_ldap_set_connect_timeout(LDAP * ld, int timelimit)
653b264e 196{
c152a447 197 fprintf(stderr, "WARNING: Connect timeouts not supported in your LDAP library\n");
653b264e 198}
ae9f801d 199static void
d440f560 200squid_ldap_memfree(char *p)
387b9f71 201{
202 free(p);
203}
ae9f801d 204
8f7b71f7 205#endif
206
5eecb267 207#ifdef LDAP_API_FEATURE_X_OPENLDAP
ae9f801d 208#if LDAP_VENDOR_VERSION > 194
209#define HAS_URI_SUPPORT 1
210#endif
5eecb267 211#endif
212
8f7b71f7 213int
214main(int argc, char **argv)
215{
c152a447 216 char buf[HELPER_INPUT_BUFFER];
6708c52c 217 char *user, *group, *extension_dn = NULL;
387b9f71 218 char *ldapServer = NULL;
8f7b71f7 219 LDAP *ld = NULL;
6708c52c 220 int tryagain = 0, rc;
387b9f71 221 int port = LDAP_PORT;
6708c52c 222 int use_extension_dn = 0;
223 int strip_nt_domain = 0;
0b9ea7bb 224 int strip_kerberos_realm = 0;
8f7b71f7 225
226 setbuf(stdout, NULL);
227
387b9f71 228 while (argc > 1 && argv[1][0] == '-') {
26ac0430
AJ
229 const char *value = "";
230 char option = argv[1][1];
231 switch (option) {
232 case 'P':
233 case 'R':
234 case 'z':
235 case 'Z':
236 case 'd':
237 case 'g':
238 case 'S':
f54f527e 239 case 'K':
26ac0430
AJ
240 break;
241 default:
242 if (strlen(argv[1]) > 2) {
243 value = argv[1] + 2;
244 } else if (argc > 2) {
245 value = argv[2];
755494da
FC
246 ++argv;
247 --argc;
26ac0430
AJ
248 } else
249 value = "";
250 break;
251 }
755494da
FC
252 ++argv;
253 --argc;
26ac0430
AJ
254 switch (option) {
255 case 'H':
5eecb267 256#if !HAS_URI_SUPPORT
c152a447 257 fprintf(stderr, "FATAL: Your LDAP library does not have URI support\n");
26ac0430 258 exit(1);
5eecb267 259#endif
26ac0430
AJ
260 /* Fall thru to -h */
261 case 'h':
262 if (ldapServer) {
263 int len = strlen(ldapServer) + 1 + strlen(value) + 1;
c152a447 264 char *newhost = (char*)malloc(len);
26ac0430
AJ
265 snprintf(newhost, len, "%s %s", ldapServer, value);
266 free(ldapServer);
267 ldapServer = newhost;
268 } else {
bb85e424 269 ldapServer = xstrdup(value);
26ac0430
AJ
270 }
271 break;
272 case 'b':
273 basedn = value;
274 break;
275 case 'f':
276 searchfilter = value;
277 break;
278 case 'B':
279 userbasedn = value;
280 break;
281 case 'F':
282 usersearchfilter = value;
283 break;
284 case 'u':
285 userdnattr = value;
286 break;
287 case 's':
288 if (strcmp(value, "base") == 0)
289 searchscope = LDAP_SCOPE_BASE;
290 else if (strcmp(value, "one") == 0)
291 searchscope = LDAP_SCOPE_ONELEVEL;
292 else if (strcmp(value, "sub") == 0)
293 searchscope = LDAP_SCOPE_SUBTREE;
294 else {
c152a447 295 fprintf(stderr, PROGRAM_NAME ": FATAL: Unknown search scope '%s'\n", value);
26ac0430
AJ
296 exit(1);
297 }
298 break;
299 case 'E':
653b264e 300#if defined(NETSCAPE_SSL)
26ac0430
AJ
301 sslpath = value;
302 if (port == LDAP_PORT)
303 port = LDAPS_PORT;
653b264e 304#else
c152a447 305 fprintf(stderr, PROGRAM_NAME ": FATAL: -E unsupported with this LDAP library\n");
26ac0430 306 exit(1);
653b264e 307#endif
26ac0430
AJ
308 break;
309 case 'c':
310 connect_timeout = atoi(value);
311 break;
312 case 't':
313 timelimit = atoi(value);
314 break;
315 case 'a':
316 if (strcmp(value, "never") == 0)
317 aliasderef = LDAP_DEREF_NEVER;
318 else if (strcmp(value, "always") == 0)
319 aliasderef = LDAP_DEREF_ALWAYS;
320 else if (strcmp(value, "search") == 0)
321 aliasderef = LDAP_DEREF_SEARCHING;
322 else if (strcmp(value, "find") == 0)
323 aliasderef = LDAP_DEREF_FINDING;
324 else {
c152a447 325 fprintf(stderr, PROGRAM_NAME ": FATAL: Unknown alias dereference method '%s'\n", value);
26ac0430
AJ
326 exit(1);
327 }
328 break;
329 case 'D':
330 binddn = value;
331 break;
332 case 'w':
333 bindpasswd = value;
334 break;
335 case 'W':
336 readSecret(value);
337 break;
338 case 'P':
339 persistent = !persistent;
340 break;
341 case 'p':
342 port = atoi(value);
343 break;
344 case 'R':
345 noreferrals = !noreferrals;
346 break;
251c9916 347#ifdef LDAP_VERSION3
26ac0430
AJ
348 case 'v':
349 switch (atoi(value)) {
350 case 2:
351 version = LDAP_VERSION2;
352 break;
353 case 3:
354 version = LDAP_VERSION3;
355 break;
356 default:
c152a447 357 fprintf(stderr, "FATAL: Protocol version should be 2 or 3\n");
26ac0430
AJ
358 exit(1);
359 }
360 break;
361 case 'Z':
362 if (version == LDAP_VERSION2) {
c152a447 363 fprintf(stderr, "FATAL: TLS (-Z) is incompatible with version %d\n",
26ac0430
AJ
364 version);
365 exit(1);
366 }
367 version = LDAP_VERSION3;
368 use_tls = 1;
369 break;
251c9916 370#endif
26ac0430 371 case 'd':
c152a447 372 debug_enabled = 1;
26ac0430
AJ
373 break;
374 case 'g':
375 use_extension_dn = 1;
376 break;
377 case 'S':
378 strip_nt_domain = 1;
379 break;
380 case 'K':
381 strip_kerberos_realm = 1;
382 break;
383 default:
c152a447 384 fprintf(stderr, PROGRAM_NAME ": FATAL: Unknown command line option '%c'\n", option);
26ac0430
AJ
385 exit(1);
386 }
8f7b71f7 387 }
388
387b9f71 389 while (argc > 1) {
26ac0430
AJ
390 char *value = argv[1];
391 if (ldapServer) {
392 int len = strlen(ldapServer) + 1 + strlen(value) + 1;
c152a447 393 char *newhost = (char*)malloc(len);
26ac0430
AJ
394 snprintf(newhost, len, "%s %s", ldapServer, value);
395 free(ldapServer);
396 ldapServer = newhost;
397 } else {
bb85e424 398 ldapServer = xstrdup(value);
26ac0430 399 }
755494da
FC
400 --argc;
401 ++argv;
387b9f71 402 }
403
404 if (!ldapServer)
26ac0430 405 ldapServer = (char *) "localhost";
387b9f71 406
407 if (!basedn || !searchfilter) {
26ac0430
AJ
408 fprintf(stderr, "\n" PROGRAM_NAME " version " PROGRAM_VERSION "\n\n");
409 fprintf(stderr, "Usage: " PROGRAM_NAME " -b basedn -f filter [options] ldap_server_name\n\n");
410 fprintf(stderr, "\t-b basedn (REQUIRED)\tbase dn under where to search for groups\n");
411 fprintf(stderr, "\t-f filter (REQUIRED)\tgroup search filter pattern. %%u = user,\n\t\t\t\t%%v = group\n");
412 fprintf(stderr, "\t-B basedn (REQUIRED)\tbase dn under where to search for users\n");
413 fprintf(stderr, "\t-F filter (REQUIRED)\tuser search filter pattern. %%s = login\n");
414 fprintf(stderr, "\t-s base|one|sub\t\tsearch scope\n");
415 fprintf(stderr, "\t-D binddn\t\tDN to bind as to perform searches\n");
416 fprintf(stderr, "\t-w bindpasswd\t\tpassword for binddn\n");
417 fprintf(stderr, "\t-W secretfile\t\tread password for binddn from file secretfile\n");
5eecb267 418#if HAS_URI_SUPPORT
26ac0430 419 fprintf(stderr, "\t-H URI\t\t\tLDAPURI (defaults to ldap://localhost)\n");
5eecb267 420#endif
26ac0430
AJ
421 fprintf(stderr, "\t-h server\t\tLDAP server (defaults to localhost)\n");
422 fprintf(stderr, "\t-p port\t\t\tLDAP server port (defaults to %d)\n", LDAP_PORT);
423 fprintf(stderr, "\t-P\t\t\tpersistent LDAP connection\n");
653b264e 424#if defined(NETSCAPE_SSL)
26ac0430 425 fprintf(stderr, "\t-E sslcertpath\t\tenable LDAP over SSL\n");
653b264e 426#endif
26ac0430
AJ
427 fprintf(stderr, "\t-c timeout\t\tconnect timeout\n");
428 fprintf(stderr, "\t-t timelimit\t\tsearch time limit\n");
429 fprintf(stderr, "\t-R\t\t\tdo not follow referrals\n");
430 fprintf(stderr, "\t-a never|always|search|find\n\t\t\t\twhen to dereference aliases\n");
5eecb267 431#ifdef LDAP_VERSION3
26ac0430
AJ
432 fprintf(stderr, "\t-v 2|3\t\t\tLDAP version\n");
433 fprintf(stderr, "\t-Z\t\t\tTLS encrypt the LDAP connection, requires\n\t\t\t\tLDAP version 3\n");
5eecb267 434#endif
26ac0430
AJ
435 fprintf(stderr, "\t-g\t\t\tfirst query parameter is base DN extension\n\t\t\t\tfor this query\n");
436 fprintf(stderr, "\t-S\t\t\tStrip NT domain from usernames\n");
437 fprintf(stderr, "\t-K\t\t\tStrip Kerberos realm from usernames\n");
af6a12ee 438 fprintf(stderr, "\t-d\t\t\tenable debug mode\n");
26ac0430
AJ
439 fprintf(stderr, "\n");
440 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");
441 exit(1);
8f7b71f7 442 }
26ac0430
AJ
443 /* On Windows ldap_start_tls_s is available starting from Windows XP,
444 * so we need to bind at run-time with the function entry point
445 */
7aa9bb3e 446#if _SQUID_WINDOWS_
e39ea462 447 if (use_tls) {
448
26ac0430 449 HMODULE WLDAP32Handle;
e39ea462 450
26ac0430
AJ
451 WLDAP32Handle = GetModuleHandle("wldap32");
452 if ((Win32_ldap_start_tls_s = (PFldap_start_tls_s) GetProcAddress(WLDAP32Handle, LDAP_START_TLS_S)) == NULL) {
c152a447 453 fprintf(stderr, PROGRAM_NAME ": FATAL: TLS (-Z) not supported on this platform.\n");
26ac0430
AJ
454 exit(1);
455 }
e39ea462 456 }
457#endif
458
c152a447 459 while (fgets(buf, HELPER_INPUT_BUFFER, stdin) != NULL) {
26ac0430
AJ
460 int found = 0;
461 if (!strchr(buf, '\n')) {
462 /* too large message received.. skip and deny */
c152a447 463 fprintf(stderr, "%s: ERROR: Input Too large: %s\n", argv[0], buf);
26ac0430 464 while (fgets(buf, sizeof(buf), stdin)) {
c152a447 465 fprintf(stderr, "%s: ERROR: Input Too large..: %s\n", argv[0], buf);
26ac0430
AJ
466 if (strchr(buf, '\n') != NULL)
467 break;
468 }
c152a447
AJ
469 SEND_ERR("");
470 continue;
26ac0430
AJ
471 }
472 user = strtok(buf, " \n");
473 if (!user) {
c152a447
AJ
474 debug("%s: Invalid request: No Username given\n", argv[0]);
475 SEND_ERR("Invalid request. No Username");
476 continue;
26ac0430
AJ
477 }
478 rfc1738_unescape(user);
479 if (strip_nt_domain) {
92333948 480 char *u = strrchr(user, '\\');
26ac0430 481 if (!u)
92333948 482 u = strrchr(user, '/');
d355ba6d
AJ
483 if (!u)
484 u = strrchr(user, '+');
26ac0430
AJ
485 if (u && u[1])
486 user = u + 1;
487 }
488 if (strip_kerberos_realm) {
489 char *u = strchr(user, '@');
490 if (u != NULL) {
491 *u = '\0';
492 }
493 }
494 if (use_extension_dn) {
495 extension_dn = strtok(NULL, " \n");
496 if (!extension_dn) {
c152a447
AJ
497 debug("%s: Invalid request: Extension DN configured, but none sent.\n", argv[0]);
498 SEND_ERR("Invalid Request. Extension DN required.");
499 continue;
26ac0430
AJ
500 }
501 rfc1738_unescape(extension_dn);
502 }
503 while (!found && user && (group = strtok(NULL, " \n")) != NULL) {
504 rfc1738_unescape(group);
505
506recover:
507 if (ld == NULL) {
5eecb267 508#if HAS_URI_SUPPORT
26ac0430
AJ
509 if (strstr(ldapServer, "://") != NULL) {
510 rc = ldap_initialize(&ld, ldapServer);
511 if (rc != LDAP_SUCCESS) {
c152a447 512 fprintf(stderr, "%s: ERROR: Unable to connect to LDAPURI:%s\n", argv[0], ldapServer);
26ac0430
AJ
513 break;
514 }
515 } else
653b264e 516#endif
517#if NETSCAPE_SSL
26ac0430
AJ
518 if (sslpath) {
519 if (!sslinit && (ldapssl_client_init(sslpath, NULL) != LDAP_SUCCESS)) {
c152a447 520 fprintf(stderr, "FATAL: Unable to initialise SSL with cert path %s\n", sslpath);
26ac0430
AJ
521 exit(1);
522 } else {
755494da 523 ++sslinit;
26ac0430
AJ
524 }
525 if ((ld = ldapssl_init(ldapServer, port, 1)) == NULL) {
c152a447 526 fprintf(stderr, "FATAL: Unable to connect to SSL LDAP server: %s port:%d\n",
26ac0430
AJ
527 ldapServer, port);
528 exit(1);
529 }
530 } else
5eecb267 531#endif
26ac0430 532 if ((ld = ldap_init(ldapServer, port)) == NULL) {
c152a447 533 fprintf(stderr, "ERROR: Unable to connect to LDAP server:%s port:%d\n", ldapServer, port);
26ac0430
AJ
534 break;
535 }
536 if (connect_timeout)
537 squid_ldap_set_connect_timeout(ld, connect_timeout);
653b264e 538
251c9916 539#ifdef LDAP_VERSION3
26ac0430 540 if (version == -1) {
8c33b163 541 version = LDAP_VERSION3;
26ac0430
AJ
542 }
543 if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) != LDAP_SUCCESS) {
c152a447 544 fprintf(stderr, "ERROR: Could not set LDAP_OPT_PROTOCOL_VERSION %d\n",
26ac0430
AJ
545 version);
546 ldap_unbind(ld);
547 ld = NULL;
548 break;
549 }
550 if (use_tls) {
20e869bf 551#ifdef LDAP_OPT_X_TLS
26ac0430 552 if (version != LDAP_VERSION3) {
c152a447 553 fprintf(stderr, "FATAL: TLS requires LDAP version 3\n");
26ac0430
AJ
554 exit(1);
555 } else if (ldap_start_tls_s(ld, NULL, NULL) != LDAP_SUCCESS) {
c152a447 556 fprintf(stderr, "ERROR: Could not Activate TLS connection\n");
26ac0430
AJ
557 ldap_unbind(ld);
558 ld = NULL;
559 break;
560 }
20e869bf 561#else
c152a447 562 fprintf(stderr, "FATAL: TLS not supported with your LDAP library\n");
26ac0430 563 exit(1);
20e869bf 564#endif
26ac0430 565 }
251c9916 566#endif
26ac0430
AJ
567 squid_ldap_set_timelimit(ld, timelimit);
568 squid_ldap_set_referrals(ld, !noreferrals);
569 squid_ldap_set_aliasderef(ld, aliasderef);
570 if (binddn && bindpasswd && *binddn && *bindpasswd) {
571 rc = ldap_simple_bind_s(ld, binddn, bindpasswd);
572 if (rc != LDAP_SUCCESS) {
c152a447 573 fprintf(stderr, PROGRAM_NAME ": WARNING: could not bind to binddn '%s'\n", ldap_err2string(rc));
26ac0430
AJ
574 ldap_unbind(ld);
575 ld = NULL;
576 break;
577 }
578 }
c152a447 579 debug("Connected OK\n");
26ac0430
AJ
580 }
581 if (searchLDAP(ld, group, user, extension_dn) == 0) {
582 found = 1;
583 break;
584 } else {
585 if (tryagain) {
586 tryagain = 0;
587 ldap_unbind(ld);
588 ld = NULL;
589 goto recover;
590 }
591 }
592 }
593 if (found)
c152a447 594 SEND_OK("");
26ac0430 595 else {
c152a447 596 SEND_ERR("");
26ac0430
AJ
597 }
598
599 if (ld != NULL) {
600 if (!persistent || (squid_ldap_errno(ld) != LDAP_SUCCESS && squid_ldap_errno(ld) != LDAP_INVALID_CREDENTIALS)) {
601 ldap_unbind(ld);
602 ld = NULL;
603 } else {
604 tryagain = 1;
605 }
606 }
8f7b71f7 607 }
608 if (ld)
26ac0430 609 ldap_unbind(ld);
8f7b71f7 610 return 0;
611}
612
613static int
251c9916 614ldap_escape_value(char *escaped, int size, const char *src)
6708c52c 615{
616 int n = 0;
251c9916 617 while (size > 4 && *src) {
26ac0430
AJ
618 switch (*src) {
619 case '*':
620 case '(':
621 case ')':
622 case '\\':
623 n += 3;
624 size -= 3;
625 if (size > 0) {
f207fe64
FC
626 *escaped = '\\';
627 ++escaped;
14942edd
FC
628 snprintf(escaped, 3, "%02x", (unsigned char) *src);
629 ++src;
26ac0430
AJ
630 escaped += 2;
631 }
632 break;
633 default:
14942edd
FC
634 *escaped = *src;
635 ++escaped;
636 ++src;
755494da
FC
637 ++n;
638 --size;
26ac0430 639 }
6708c52c 640 }
251c9916 641 *escaped = '\0';
6708c52c 642 return n;
643}
644
645static int
c152a447 646build_filter(char *filter, int size, const char *templ, const char *user, const char *group)
6708c52c 647{
648 int n;
c152a447
AJ
649 while (*templ && size > 0) {
650 switch (*templ) {
26ac0430 651 case '%':
755494da 652 ++templ;
c152a447 653 switch (*templ) {
26ac0430
AJ
654 case 'u':
655 case 'v':
755494da 656 ++templ;
26ac0430
AJ
657 n = ldap_escape_value(filter, size, user);
658 size -= n;
659 filter += n;
660 break;
661 case 'g':
662 case 'a':
755494da 663 ++templ;
26ac0430
AJ
664 n = ldap_escape_value(filter, size, group);
665 size -= n;
666 filter += n;
667 break;
668 default:
c152a447 669 fprintf(stderr, "ERROR: Unknown filter template string %%%c\n", *templ);
26ac0430
AJ
670 return 1;
671 break;
672 }
673 break;
674 case '\\':
755494da 675 ++templ;
c152a447 676 if (*templ) {
aec55359
FC
677 *filter = *templ;
678 ++filter;
679 ++templ;
755494da 680 --size;
26ac0430
AJ
681 }
682 break;
683 default:
aec55359
FC
684 *filter = *templ;
685 ++filter;
686 ++templ;
755494da 687 --size;
26ac0430
AJ
688 break;
689 }
6708c52c 690 }
691 if (size <= 0) {
26ac0430
AJ
692 fprintf(stderr, "ERROR: Filter too large\n");
693 return 1;
6708c52c 694 }
695 *filter = '\0';
696 return 0;
697}
698
699static int
700searchLDAPGroup(LDAP * ld, char *group, char *member, char *extension_dn)
8f7b71f7 701{
d440f560 702 char filter[256];
703 static char searchbase[256];
704 LDAPMessage *res = NULL;
705 LDAPMessage *entry;
6708c52c 706 int rc;
26ac0430 707 char *searchattr[] = {(char *) LDAP_NO_ATTRS, NULL};
8f7b71f7 708
6708c52c 709 if (extension_dn && *extension_dn)
26ac0430 710 snprintf(searchbase, sizeof(searchbase), "%s,%s", extension_dn, basedn);
d440f560 711 else
26ac0430 712 snprintf(searchbase, sizeof(searchbase), "%s", basedn);
8f7b71f7 713
6708c52c 714 if (build_filter(filter, sizeof(filter), searchfilter, member, group) != 0) {
c152a447 715 fprintf(stderr, PROGRAM_NAME ": ERROR: Failed to construct LDAP search filter. filter=\"%s\", user=\"%s\", group=\"%s\"\n", filter, member, group);
26ac0430 716 return 1;
6708c52c 717 }
c152a447 718 debug("group filter '%s', searchbase '%s'\n", filter, searchbase);
8f7b71f7 719
b10eaeab 720 rc = ldap_search_s(ld, searchbase, searchscope, filter, searchattr, 1, &res);
6708c52c 721 if (rc != LDAP_SUCCESS) {
26ac0430
AJ
722 if (noreferrals && rc == LDAP_PARTIAL_RESULTS) {
723 /* Everything is fine. This is expected when referrals
724 * are disabled.
725 */
726 } else {
c152a447 727 fprintf(stderr, PROGRAM_NAME ": WARNING: LDAP search error '%s'\n", ldap_err2string(rc));
653b264e 728#if defined(NETSCAPE_SSL)
26ac0430
AJ
729 if (sslpath && ((rc == LDAP_SERVER_DOWN) || (rc == LDAP_CONNECT_ERROR))) {
730 int sslerr = PORT_GetError();
c152a447 731 fprintf(stderr, PROGRAM_NAME ": WARNING: SSL error %d (%s)\n", sslerr, ldapssl_err2string(sslerr));
26ac0430 732 }
653b264e 733#endif
26ac0430
AJ
734 ldap_msgfree(res);
735 return 1;
736 }
d440f560 737 }
738 entry = ldap_first_entry(ld, res);
739 if (!entry) {
26ac0430
AJ
740 ldap_msgfree(res);
741 return 1;
d440f560 742 }
743 ldap_msgfree(res);
744 return 0;
8f7b71f7 745}
6708c52c 746
747static int
ae9f801d 748searchLDAP(LDAP * ld, char *group, char *login, char *extension_dn)
6708c52c 749{
750
751 if (usersearchfilter) {
26ac0430
AJ
752 char filter[8192];
753 char searchbase[8192];
754 char escaped_login[1024];
755 LDAPMessage *res = NULL;
756 LDAPMessage *entry;
757 int rc;
758 char *userdn;
759 char *searchattr[] = {(char *) LDAP_NO_ATTRS, NULL};
760 if (extension_dn && *extension_dn)
761 snprintf(searchbase, sizeof(searchbase), "%s,%s", extension_dn, userbasedn ? userbasedn : basedn);
762 else
763 snprintf(searchbase, sizeof(searchbase), "%s", userbasedn ? userbasedn : basedn);
764 ldap_escape_value(escaped_login, sizeof(escaped_login), login);
765 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);
c152a447 766 debug("user filter '%s', searchbase '%s'\n", filter, searchbase);
26ac0430
AJ
767 rc = ldap_search_s(ld, searchbase, searchscope, filter, searchattr, 1, &res);
768 if (rc != LDAP_SUCCESS) {
769 if (noreferrals && rc == LDAP_PARTIAL_RESULTS) {
770 /* Everything is fine. This is expected when referrals
771 * are disabled.
772 */
773 } else {
c152a447 774 fprintf(stderr, PROGRAM_NAME ": WARNING: LDAP search error '%s'\n", ldap_err2string(rc));
653b264e 775#if defined(NETSCAPE_SSL)
26ac0430
AJ
776 if (sslpath && ((rc == LDAP_SERVER_DOWN) || (rc == LDAP_CONNECT_ERROR))) {
777 int sslerr = PORT_GetError();
c152a447 778 fprintf(stderr, PROGRAM_NAME ": WARNING: SSL error %d (%s)\n", sslerr, ldapssl_err2string(sslerr));
26ac0430 779 }
653b264e 780#endif
26ac0430
AJ
781 ldap_msgfree(res);
782 return 1;
783 }
784 }
785 entry = ldap_first_entry(ld, res);
786 if (!entry) {
c152a447 787 fprintf(stderr, PROGRAM_NAME ": WARNING: User '%s' not found in '%s'\n", login, searchbase);
26ac0430
AJ
788 ldap_msgfree(res);
789 return 1;
790 }
791 userdn = ldap_get_dn(ld, entry);
792 rc = searchLDAPGroup(ld, group, userdn, extension_dn);
793 squid_ldap_memfree(userdn);
794 ldap_msgfree(res);
795 return rc;
6708c52c 796 } else if (userdnattr) {
26ac0430
AJ
797 char dn[8192];
798 if (extension_dn && *extension_dn)
bb85e424 799 snprintf(dn, 8192, "%s=%s, %s, %s", userdnattr, login, extension_dn, userbasedn ? userbasedn : basedn);
26ac0430 800 else
bb85e424 801 snprintf(dn, 8192, "%s=%s, %s", userdnattr, login, userbasedn ? userbasedn : basedn);
26ac0430 802 return searchLDAPGroup(ld, group, dn, extension_dn);
6708c52c 803 } else {
26ac0430 804 return searchLDAPGroup(ld, group, login, extension_dn);
6708c52c 805 }
806}
954a8513 807
0b9ea7bb 808int
ae9f801d 809readSecret(const char *filename)
954a8513 810{
ae9f801d 811 char buf[BUFSIZ];
812 char *e = 0;
813 FILE *f;
954a8513 814
ae9f801d 815 if (!(f = fopen(filename, "r"))) {
c152a447 816 fprintf(stderr, PROGRAM_NAME ": ERROR: Can not read secret file %s\n", filename);
26ac0430 817 return 1;
ae9f801d 818 }
819 if (!fgets(buf, sizeof(buf) - 1, f)) {
c152a447 820 fprintf(stderr, PROGRAM_NAME ": ERROR: Secret file %s is empty\n", filename);
26ac0430
AJ
821 fclose(f);
822 return 1;
ae9f801d 823 }
824 /* strip whitespaces on end */
825 if ((e = strrchr(buf, '\n')))
26ac0430 826 *e = 0;
ae9f801d 827 if ((e = strrchr(buf, '\r')))
26ac0430 828 *e = 0;
ae9f801d 829
bb85e424 830 bindpasswd = xstrdup(buf);
ae9f801d 831 if (!bindpasswd) {
c152a447 832 fprintf(stderr, PROGRAM_NAME ": ERROR: can not allocate memory\n");
ae9f801d 833 }
954a8513 834 fclose(f);
954a8513 835
ae9f801d 836 return 0;
954a8513 837}