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