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

 

Introduction

Documentation

Support

Miscellaneous