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