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