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