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