]>
Commit | Line | Data |
---|---|---|
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 | 72 | typedef 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 | 75 | typedef WINLDAPAPI ULONG(LDAPAPI * PFldap_start_tls_s) (IN PLDAP, OUT PULONG, OUT LDAPMessage **, IN PLDAPControlA *, IN PLDAPControlA *); |
e39ea462 | 76 | #endif /* LDAP_UNICODE */ |
77 | PFldap_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 | 97 | static const char *basedn = NULL; |
98 | static const char *searchfilter = NULL; | |
99 | static const char *userbasedn = NULL; | |
100 | static const char *userdnattr = NULL; | |
101 | static const char *usersearchfilter = NULL; | |
102 | static const char *binddn = NULL; | |
103 | static const char *bindpasswd = NULL; | |
8f7b71f7 | 104 | static int searchscope = LDAP_SCOPE_SUBTREE; |
105 | static int persistent = 0; | |
106 | static int noreferrals = 0; | |
8f7b71f7 | 107 | static int aliasderef = LDAP_DEREF_NEVER; |
653b264e | 108 | #if defined(NETSCAPE_SSL) |
109 | static char *sslpath = NULL; | |
110 | static int sslinit = 0; | |
111 | #endif | |
112 | static int connect_timeout = 0; | |
113 | static int timelimit = LDAP_NO_LIMIT; | |
8f7b71f7 | 114 | |
251c9916 | 115 | #ifdef LDAP_VERSION3 |
387b9f71 | 116 | /* Added for TLS support and version 3 */ |
117 | static int use_tls = 0; | |
118 | static int version = -1; | |
251c9916 | 119 | #endif |
387b9f71 | 120 | |
6708c52c | 121 | static int searchLDAP(LDAP * ld, char *group, char *user, char *extension_dn); |
8f7b71f7 | 122 | |
e9505fad | 123 | static 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 | 132 | static int |
d440f560 | 133 | squid_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 | 139 | static void |
d440f560 | 140 | squid_ldap_set_aliasderef(LDAP * ld, int deref) |
8f7b71f7 | 141 | { |
142 | ldap_set_option(ld, LDAP_OPT_DEREF, &deref); | |
143 | } | |
ae9f801d | 144 | static void |
d440f560 | 145 | squid_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 | 150 | static void |
d5f8d05f | 151 | squid_ldap_set_timelimit(LDAP * ld, int aTimeLimit) |
653b264e | 152 | { |
d5f8d05f | 153 | ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &aTimeLimit); |
653b264e | 154 | } |
155 | static void | |
d5f8d05f | 156 | squid_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 | 168 | static void |
d440f560 | 169 | squid_ldap_memfree(char *p) |
387b9f71 | 170 | { |
171 | ldap_memfree(p); | |
172 | } | |
ae9f801d | 173 | |
8f7b71f7 | 174 | #else |
ae9f801d | 175 | static int |
d440f560 | 176 | squid_ldap_errno(LDAP * ld) |
8f7b71f7 | 177 | { |
178 | return ld->ld_errno; | |
179 | } | |
ae9f801d | 180 | static void |
d440f560 | 181 | squid_ldap_set_aliasderef(LDAP * ld, int deref) |
8f7b71f7 | 182 | { |
183 | ld->ld_deref = deref; | |
184 | } | |
ae9f801d | 185 | static void |
d440f560 | 186 | squid_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 | 193 | static void |
ae9f801d | 194 | squid_ldap_set_timelimit(LDAP * ld, int timelimit) |
653b264e | 195 | { |
196 | ld->ld_timelimit = timelimit; | |
197 | } | |
198 | static void | |
ae9f801d | 199 | squid_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 | 203 | static void |
d440f560 | 204 | squid_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 | 217 | int |
218 | main(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 | ||
510 | recover: | |
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 |
617 | static std::string |
618 | ldap_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 |
636 | static bool |
637 | build_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 | ||
678 | static std::string | |
679 | build_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 | ||
688 | static 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 |
710 | typedef const std::unique_ptr<LDAPMessage, decltype(&ldap_msgfree)> LdapResult; |
711 | ||
6708c52c | 712 | static int |
37c7518a | 713 | searchLDAPGroup(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 | ||
736 | static void | |
737 | formatWithString(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 | |
746 | static int | |
ae9f801d | 747 | searchLDAP(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 | 789 | int |
ae9f801d | 790 | readSecret(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 |