]>
Commit | Line | Data |
---|---|---|
5b95b903 | 1 | /* |
ef57eb7b | 2 | * Copyright (C) 1996-2016 The Squid Software Foundation and contributors |
5b95b903 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 | ||
94439e4e | 9 | /* |
94439e4e | 10 | * squid_ldap_auth: authentication via ldap for squid proxy server |
26ac0430 | 11 | * |
d4ce3aef | 12 | * Authors: |
13 | * Henrik Nordstrom | |
14 | * hno@squid-cache.org | |
05b7b723 | 15 | * |
26ac0430 | 16 | * Glen Newton |
94439e4e | 17 | * glen.newton@nrc.ca |
26ac0430 | 18 | * Advanced Services |
94439e4e | 19 | * CISTI |
20 | * National Research Council | |
d4ce3aef | 21 | * |
22 | * with contributions from others mentioned in the Changes section below | |
26ac0430 | 23 | * |
7c2a6c51 | 24 | * Usage: squid_ldap_auth -b basedn [-s searchscope] |
b671cc68 | 25 | * [-f searchfilter] [-D binddn -w bindpasswd] |
20b6fc8e | 26 | * [-u attr] [-h host] [-p port] [-P] [-R] [ldap_server_name[:port]] ... |
26ac0430 | 27 | * |
94439e4e | 28 | * Dependencies: You need to get the OpenLDAP libraries |
d4ce3aef | 29 | * from http://www.openldap.org or another compatible LDAP C-API |
30 | * implementation. | |
8370dcf2 | 31 | * |
32 | * If you want to make a TLS enabled connection you will also need the | |
33 | * OpenSSL libraries linked into openldap. See http://www.openssl.org/ | |
26ac0430 AJ |
34 | * |
35 | * License: squid_ldap_auth is free software; you can redistribute it | |
36 | * and/or modify it under the terms of the GNU General Public License | |
37 | * as published by the Free Software Foundation; either version 2, | |
94439e4e | 38 | * or (at your option) any later version. |
efd2f308 | 39 | * |
40 | * Changes: | |
3cc8b8b6 | 41 | * 2005-01-07: Henrik Nordstrom <hno@squid-cache.org> |
42 | * - Added some sanity checks on login names to avoid | |
076d1037 | 43 | * users bypassing equality checks by exploring the |
44 | * overly helpful match capabilities of LDAP | |
b627c18a | 45 | * 2004-07-17: Henrik Nordstrom <hno@squid-cache.org> |
46 | * - Corrected non-persistent mode to only issue one | |
076d1037 | 47 | * ldap_bind per connection. |
b627c18a | 48 | * - -U option to compare the users password rather |
076d1037 | 49 | * than binding. |
307228f1 | 50 | * 2004-03-01: Henrik Nordstrom <hno@squid-cache.org> |
b627c18a | 51 | * - corrected building of search filters to escape |
52 | * unsafe input | |
53 | * - -d option for "debug" like squid_ldap_group | |
307228f1 | 54 | * 2004-01-05: Henrik Nordstrom <hno@squid-cache.org> |
b627c18a | 55 | * - Corrected TLS mode |
653b264e | 56 | * 2003-03-01: David J N Begley |
b627c18a | 57 | * - Support for Netscape API method of ldap over SSL |
58 | * connections | |
59 | * - Timeout option for better recovery when using | |
60 | * multiple LDAP servers | |
954a8513 | 61 | * 2003-03-01: Christoph Lechleitner <lech@ibcl.at> |
62 | * - Added -W option to read bindpasswd from file | |
7ba68818 | 63 | * 2003-03-01: Juerg Michel |
64 | * - Added support for ldap URI via the -H option | |
65 | * (requires OpenLDAP) | |
8370dcf2 | 66 | * 2001-12-12: Michael Cunningham <m.cunningham@xpedite.com> |
26ac0430 | 67 | * - Added TLS support and partial ldap version 3 support. |
49b97dc8 | 68 | * 2001-10-04: Henrik Nordstrom <hno@squid-cache.org> |
69 | * - Be consistent with the other helpers in how | |
20b6fc8e | 70 | * spaces are managed. If there is space characters |
71 | * then these are assumed to be part of the password | |
72 | * 2001-09-05: Henrik Nordstrom <hno@squid-cache.org> | |
73 | * - Added ability to specify another default LDAP port to | |
74 | * connect to. Persistent connections moved to -P | |
87f6d1e1 | 75 | * 2001-05-02: Henrik Nordstrom <hno@squid-cache.org> |
76 | * - Support newer OpenLDAP 2.x libraries using the | |
b671cc68 | 77 | * revised Internet Draft API which unfortunately |
87f6d1e1 | 78 | * is not backwards compatible with RFC1823.. |
efd2f308 | 79 | * 2001-04-15: Henrik Nordstrom <hno@squid-cache.org> |
80 | * - Added command line option for basedn | |
81 | * - Added the ability to search for the user DN | |
c4c1f30c | 82 | * 2001-04-16: Henrik Nordstrom <hno@squid-cache.org> |
83 | * - Added -D binddn -w bindpasswd. | |
50f87883 | 84 | * 2001-04-17: Henrik Nordstrom <hno@squid-cache.org> |
85 | * - Added -R to disable referrals | |
86 | * - Added -a to control alias dereferencing | |
7c2a6c51 | 87 | * 2001-04-17: Henrik Nordstrom <hno@squid-cache.org> |
88 | * - Added -u, DN username attribute name | |
c9acd551 | 89 | * 2001-04-18: Henrik Nordstrom <hno@squid-cache.org> |
90 | * - Allow full filter specifications in -f | |
94439e4e | 91 | */ |
92 | ||
f7f3304a | 93 | #include "squid.h" |
3314ecb0 | 94 | |
46962e36 | 95 | #define LDAP_DEPRECATED 1 |
96 | ||
1fa9b1a7 | 97 | #include "rfc1738.h" |
e39ea462 | 98 | #include "util.h" |
99 | ||
074d6a40 AJ |
100 | #include <cctype> |
101 | #include <cstring> | |
e39ea462 | 102 | |
7aa9bb3e | 103 | #if _SQUID_WINDOWS_ && !_SQUID_CYGWIN_ |
e39ea462 | 104 | #define snprintf _snprintf |
105 | #include <windows.h> | |
106 | #include <winldap.h> | |
107 | #ifndef LDAPAPI | |
108 | #define LDAPAPI __cdecl | |
109 | #endif | |
110 | #ifdef LDAP_VERSION3 | |
20e869bf | 111 | #ifndef LDAP_OPT_X_TLS |
112 | #define LDAP_OPT_X_TLS 0x6000 | |
113 | #endif | |
e39ea462 | 114 | /* Some tricks to allow dynamic bind with ldap_start_tls_s entry point at |
f3be8d13 | 115 | * run time. |
e39ea462 | 116 | */ |
117 | #undef ldap_start_tls_s | |
118 | #if LDAP_UNICODE | |
119 | #define LDAP_START_TLS_S "ldap_start_tls_sW" | |
f3be8d13 | 120 | typedef WINLDAPAPI ULONG(LDAPAPI * PFldap_start_tls_s) (IN PLDAP, OUT PULONG, OUT LDAPMessage **, IN PLDAPControlW *, IN PLDAPControlW *); |
e39ea462 | 121 | #else |
122 | #define LDAP_START_TLS_S "ldap_start_tls_sA" | |
f3be8d13 | 123 | typedef WINLDAPAPI ULONG(LDAPAPI * PFldap_start_tls_s) (IN PLDAP, OUT PULONG, OUT LDAPMessage **, IN PLDAPControlA *, IN PLDAPControlA *); |
e39ea462 | 124 | #endif /* LDAP_UNICODE */ |
125 | PFldap_start_tls_s Win32_ldap_start_tls_s; | |
126 | #define ldap_start_tls_s(l,s,c) Win32_ldap_start_tls_s(l,NULL,NULL,s,c) | |
127 | #endif /* LDAP_VERSION3 */ | |
128 | ||
129 | #else | |
130 | ||
94439e4e | 131 | #include <lber.h> |
94439e4e | 132 | #include <ldap.h> |
133 | ||
e39ea462 | 134 | #endif |
9bbd1655 | 135 | |
3314ecb0 | 136 | #define PROGRAM_NAME "basic_ldap_auth" |
653b264e | 137 | |
138 | /* Global options */ | |
a0fbb6a7 | 139 | static const char *basedn; |
140 | static const char *searchfilter = NULL; | |
141 | static const char *binddn = NULL; | |
142 | static const char *bindpasswd = NULL; | |
143 | static const char *userattr = "uid"; | |
b627c18a | 144 | static const char *passwdattr = NULL; |
331ba756 | 145 | static int searchscope = LDAP_SCOPE_SUBTREE; |
c4c1f30c | 146 | static int persistent = 0; |
b627c18a | 147 | static int bind_once = 0; |
50f87883 | 148 | static int noreferrals = 0; |
149 | static int aliasderef = LDAP_DEREF_NEVER; | |
653b264e | 150 | #if defined(NETSCAPE_SSL) |
a0fbb6a7 | 151 | static const char *sslpath = NULL; |
653b264e | 152 | static int sslinit = 0; |
153 | #endif | |
154 | static int connect_timeout = 0; | |
155 | static int timelimit = LDAP_NO_LIMIT; | |
94439e4e | 156 | |
8370dcf2 | 157 | /* Added for TLS support and version 3 */ |
158 | static int use_tls = 0; | |
159 | static int version = -1; | |
160 | ||
b627c18a | 161 | static int checkLDAP(LDAP * ld, const char *userid, const char *password, const char *server, int port); |
a0fbb6a7 | 162 | static int readSecret(const char *filename); |
94439e4e | 163 | |
87f6d1e1 | 164 | /* Yuck.. we need to glue to different versions of the API */ |
165 | ||
076d1037 | 166 | #ifndef LDAP_NO_ATTRS |
167 | #define LDAP_NO_ATTRS "1.1" | |
168 | #endif | |
169 | ||
87f6d1e1 | 170 | #if defined(LDAP_API_VERSION) && LDAP_API_VERSION > 1823 |
9bea1d5b | 171 | static int |
b671cc68 | 172 | squid_ldap_errno(LDAP * ld) |
87f6d1e1 | 173 | { |
174 | int err = 0; | |
175 | ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err); | |
176 | return err; | |
177 | } | |
9bea1d5b | 178 | static void |
b671cc68 | 179 | squid_ldap_set_aliasderef(LDAP * ld, int deref) |
87f6d1e1 | 180 | { |
181 | ldap_set_option(ld, LDAP_OPT_DEREF, &deref); | |
182 | } | |
9bea1d5b | 183 | static void |
b671cc68 | 184 | squid_ldap_set_referrals(LDAP * ld, int referrals) |
87f6d1e1 | 185 | { |
8ea6914d | 186 | int *value = static_cast<int*>(referrals ? LDAP_OPT_ON :LDAP_OPT_OFF); |
87f6d1e1 | 187 | ldap_set_option(ld, LDAP_OPT_REFERRALS, value); |
188 | } | |
a9ce6538 | 189 | static void |
d5f8d05f | 190 | squid_ldap_set_timelimit(LDAP * ld, int aTimeLimit) |
653b264e | 191 | { |
d5f8d05f | 192 | ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &aTimeLimit); |
653b264e | 193 | } |
194 | static void | |
d5f8d05f | 195 | squid_ldap_set_connect_timeout(LDAP * ld, int aTimeLimit) |
653b264e | 196 | { |
197 | #if defined(LDAP_OPT_NETWORK_TIMEOUT) | |
198 | struct timeval tv; | |
d5f8d05f | 199 | tv.tv_sec = aTimeLimit; |
653b264e | 200 | tv.tv_usec = 0; |
201 | ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv); | |
202 | #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT) | |
d5f8d05f FC |
203 | aTimeLimit *= 1000; |
204 | ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, &aTimeLimit); | |
653b264e | 205 | #endif |
206 | } | |
207 | static void | |
a9ce6538 | 208 | squid_ldap_memfree(char *p) |
209 | { | |
210 | ldap_memfree(p); | |
211 | } | |
b627c18a | 212 | |
87f6d1e1 | 213 | #else |
9bea1d5b | 214 | static int |
b671cc68 | 215 | squid_ldap_errno(LDAP * ld) |
87f6d1e1 | 216 | { |
217 | return ld->ld_errno; | |
218 | } | |
9bea1d5b | 219 | static void |
b671cc68 | 220 | squid_ldap_set_aliasderef(LDAP * ld, int deref) |
87f6d1e1 | 221 | { |
222 | ld->ld_deref = deref; | |
223 | } | |
9bea1d5b | 224 | static void |
b671cc68 | 225 | squid_ldap_set_referrals(LDAP * ld, int referrals) |
87f6d1e1 | 226 | { |
227 | if (referrals) | |
26ac0430 | 228 | ld->ld_options |= ~LDAP_OPT_REFERRALS; |
87f6d1e1 | 229 | else |
26ac0430 | 230 | ld->ld_options &= ~LDAP_OPT_REFERRALS; |
87f6d1e1 | 231 | } |
f3be8d13 | 232 | static void |
b627c18a | 233 | squid_ldap_set_timelimit(LDAP * ld, int timelimit) |
653b264e | 234 | { |
235 | ld->ld_timelimit = timelimit; | |
236 | } | |
237 | static void | |
b627c18a | 238 | squid_ldap_set_connect_timeout(LDAP * ld, int timelimit) |
653b264e | 239 | { |
240 | fprintf(stderr, "Connect timeouts not supported in your LDAP library\n"); | |
241 | } | |
a9ce6538 | 242 | static void |
243 | squid_ldap_memfree(char *p) | |
244 | { | |
245 | free(p); | |
246 | } | |
b627c18a | 247 | |
248 | #endif | |
249 | ||
7c211f58 | 250 | #ifdef LDAP_API_FEATURE_X_OPENLDAP |
251 | #if LDAP_VENDOR_VERSION > 194 | |
252 | #define HAS_URI_SUPPORT 1 | |
253 | #endif | |
254 | #endif | |
255 | ||
b627c18a | 256 | static LDAP * |
257 | open_ldap_connection(const char *ldapServer, int port) | |
258 | { | |
259 | LDAP *ld = NULL; | |
260 | #if HAS_URI_SUPPORT | |
261 | if (strstr(ldapServer, "://") != NULL) { | |
26ac0430 AJ |
262 | int rc = ldap_initialize(&ld, ldapServer); |
263 | if (rc != LDAP_SUCCESS) { | |
264 | fprintf(stderr, "\nUnable to connect to LDAPURI:%s\n", ldapServer); | |
265 | exit(1); | |
266 | } | |
b627c18a | 267 | } else |
268 | #endif | |
269 | #if NETSCAPE_SSL | |
26ac0430 AJ |
270 | if (sslpath) { |
271 | if (!sslinit && (ldapssl_client_init(sslpath, NULL) != LDAP_SUCCESS)) { | |
272 | fprintf(stderr, "\nUnable to initialise SSL with cert path %s\n", | |
273 | sslpath); | |
274 | exit(1); | |
275 | } else { | |
755494da | 276 | ++sslinit; |
26ac0430 AJ |
277 | } |
278 | if ((ld = ldapssl_init(ldapServer, port, 1)) == NULL) { | |
279 | fprintf(stderr, "\nUnable to connect to SSL LDAP server: %s port:%d\n", | |
280 | ldapServer, port); | |
281 | exit(1); | |
282 | } | |
283 | } else | |
b627c18a | 284 | #endif |
26ac0430 AJ |
285 | if ((ld = ldap_init(ldapServer, port)) == NULL) { |
286 | fprintf(stderr, "\nUnable to connect to LDAP server:%s port:%d\n", | |
287 | ldapServer, port); | |
288 | exit(1); | |
289 | } | |
b627c18a | 290 | if (connect_timeout) |
26ac0430 | 291 | squid_ldap_set_connect_timeout(ld, connect_timeout); |
b627c18a | 292 | |
293 | #ifdef LDAP_VERSION3 | |
294 | if (version == -1) { | |
8c33b163 | 295 | version = LDAP_VERSION3; |
b627c18a | 296 | } |
f3be8d13 | 297 | if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) != LDAP_SUCCESS) { |
26ac0430 AJ |
298 | fprintf(stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n", |
299 | version); | |
300 | exit(1); | |
b627c18a | 301 | } |
20e869bf | 302 | if (use_tls) { |
303 | #ifdef LDAP_OPT_X_TLS | |
26ac0430 AJ |
304 | if (version != LDAP_VERSION3) { |
305 | fprintf(stderr, "TLS requires LDAP version 3\n"); | |
306 | exit(1); | |
307 | } else if (ldap_start_tls_s(ld, NULL, NULL) != LDAP_SUCCESS) { | |
308 | fprintf(stderr, "Could not Activate TLS connection\n"); | |
309 | exit(1); | |
310 | } | |
20e869bf | 311 | #else |
26ac0430 AJ |
312 | fprintf(stderr, "TLS not supported with your LDAP library\n"); |
313 | exit(1); | |
20e869bf | 314 | #endif |
b627c18a | 315 | } |
87f6d1e1 | 316 | #endif |
b627c18a | 317 | squid_ldap_set_timelimit(ld, timelimit); |
318 | squid_ldap_set_referrals(ld, !noreferrals); | |
319 | squid_ldap_set_aliasderef(ld, aliasderef); | |
7c211f58 | 320 | return ld; |
b627c18a | 321 | } |
87f6d1e1 | 322 | |
3cc8b8b6 | 323 | /* Make a sanity check on the username to reject oddly typed names */ |
324 | static int | |
325 | validUsername(const char *user) | |
326 | { | |
f3be8d13 | 327 | const unsigned char *p = (const unsigned char *) user; |
3cc8b8b6 | 328 | |
329 | /* Leading whitespace? */ | |
e4755e29 | 330 | if (xisspace(p[0])) |
26ac0430 | 331 | return 0; |
f3be8d13 | 332 | while (p[0] && p[1]) { |
26ac0430 AJ |
333 | if (xisspace(p[0])) { |
334 | /* More than one consequitive space? */ | |
335 | if (xisspace(p[1])) | |
336 | return 0; | |
337 | /* or odd space type character used? */ | |
338 | if (p[0] != ' ') | |
339 | return 0; | |
340 | } | |
755494da | 341 | ++p; |
3cc8b8b6 | 342 | } |
343 | /* Trailing whitespace? */ | |
e4755e29 | 344 | if (xisspace(p[0])) |
26ac0430 | 345 | return 0; |
3cc8b8b6 | 346 | return 1; |
347 | } | |
348 | ||
94439e4e | 349 | int |
350 | main(int argc, char **argv) | |
351 | { | |
0af6cde9 | 352 | char buf[1024]; |
49b97dc8 | 353 | char *user, *passwd; |
20b6fc8e | 354 | char *ldapServer = NULL; |
c4c1f30c | 355 | LDAP *ld = NULL; |
c4c1f30c | 356 | int tryagain; |
70c46401 | 357 | int port = LDAP_PORT; |
94439e4e | 358 | |
359 | setbuf(stdout, NULL); | |
360 | ||
20b6fc8e | 361 | while (argc > 1 && argv[1][0] == '-') { |
26ac0430 AJ |
362 | const char *value = ""; |
363 | char option = argv[1][1]; | |
364 | switch (option) { | |
365 | case 'P': | |
366 | case 'R': | |
367 | case 'z': | |
368 | case 'Z': | |
369 | case 'd': | |
370 | case 'O': | |
371 | break; | |
372 | default: | |
373 | if (strlen(argv[1]) > 2) { | |
374 | value = argv[1] + 2; | |
375 | } else if (argc > 2) { | |
376 | value = argv[2]; | |
755494da FC |
377 | ++argv; |
378 | --argc; | |
26ac0430 AJ |
379 | } else |
380 | value = ""; | |
381 | break; | |
382 | } | |
755494da FC |
383 | ++argv; |
384 | --argc; | |
26ac0430 AJ |
385 | switch (option) { |
386 | case 'H': | |
7ba68818 | 387 | #if !HAS_URI_SUPPORT |
26ac0430 AJ |
388 | fprintf(stderr, "ERROR: Your LDAP library does not have URI support\n"); |
389 | exit(1); | |
7ba68818 | 390 | #endif |
f53969cc | 391 | /* Fall thru to -h */ |
26ac0430 AJ |
392 | case 'h': |
393 | if (ldapServer) { | |
394 | int len = strlen(ldapServer) + 1 + strlen(value) + 1; | |
c14fb378 | 395 | char *newhost = static_cast<char*>(xmalloc(len)); |
26ac0430 AJ |
396 | snprintf(newhost, len, "%s %s", ldapServer, value); |
397 | free(ldapServer); | |
398 | ldapServer = newhost; | |
399 | } else { | |
bb85e424 | 400 | ldapServer = xstrdup(value); |
26ac0430 AJ |
401 | } |
402 | break; | |
403 | case 'b': | |
404 | basedn = value; | |
405 | break; | |
406 | case 'f': | |
407 | searchfilter = value; | |
408 | break; | |
409 | case 'u': | |
410 | userattr = value; | |
411 | break; | |
412 | case 'U': | |
413 | passwdattr = value; | |
414 | break; | |
415 | case 's': | |
416 | if (strcmp(value, "base") == 0) | |
417 | searchscope = LDAP_SCOPE_BASE; | |
418 | else if (strcmp(value, "one") == 0) | |
419 | searchscope = LDAP_SCOPE_ONELEVEL; | |
420 | else if (strcmp(value, "sub") == 0) | |
421 | searchscope = LDAP_SCOPE_SUBTREE; | |
422 | else { | |
423 | fprintf(stderr, PROGRAM_NAME ": ERROR: Unknown search scope '%s'\n", value); | |
424 | exit(1); | |
425 | } | |
426 | break; | |
427 | case 'E': | |
653b264e | 428 | #if defined(NETSCAPE_SSL) |
26ac0430 AJ |
429 | sslpath = value; |
430 | if (port == LDAP_PORT) | |
431 | port = LDAPS_PORT; | |
653b264e | 432 | #else |
26ac0430 AJ |
433 | fprintf(stderr, PROGRAM_NAME " ERROR: -E unsupported with this LDAP library\n"); |
434 | exit(1); | |
653b264e | 435 | #endif |
26ac0430 AJ |
436 | break; |
437 | case 'c': | |
438 | connect_timeout = atoi(value); | |
439 | break; | |
440 | case 't': | |
441 | timelimit = atoi(value); | |
442 | break; | |
443 | case 'a': | |
444 | if (strcmp(value, "never") == 0) | |
445 | aliasderef = LDAP_DEREF_NEVER; | |
446 | else if (strcmp(value, "always") == 0) | |
447 | aliasderef = LDAP_DEREF_ALWAYS; | |
448 | else if (strcmp(value, "search") == 0) | |
449 | aliasderef = LDAP_DEREF_SEARCHING; | |
450 | else if (strcmp(value, "find") == 0) | |
451 | aliasderef = LDAP_DEREF_FINDING; | |
452 | else { | |
453 | fprintf(stderr, PROGRAM_NAME ": ERROR: Unknown alias dereference method '%s'\n", value); | |
454 | exit(1); | |
455 | } | |
456 | break; | |
457 | case 'D': | |
458 | binddn = value; | |
459 | break; | |
460 | case 'w': | |
461 | bindpasswd = value; | |
462 | break; | |
463 | case 'W': | |
464 | readSecret(value); | |
465 | break; | |
466 | case 'P': | |
467 | persistent = !persistent; | |
468 | break; | |
469 | case 'O': | |
470 | bind_once = !bind_once; | |
471 | break; | |
472 | case 'p': | |
473 | port = atoi(value); | |
474 | break; | |
475 | case 'R': | |
476 | noreferrals = !noreferrals; | |
477 | break; | |
653b264e | 478 | #ifdef LDAP_VERSION3 |
26ac0430 AJ |
479 | case 'v': |
480 | switch (atoi(value)) { | |
481 | case 2: | |
482 | version = LDAP_VERSION2; | |
483 | break; | |
484 | case 3: | |
485 | version = LDAP_VERSION3; | |
486 | break; | |
487 | default: | |
488 | fprintf(stderr, "Protocol version should be 2 or 3\n"); | |
489 | exit(1); | |
490 | } | |
491 | break; | |
492 | case 'Z': | |
493 | if (version == LDAP_VERSION2) { | |
494 | fprintf(stderr, "TLS (-Z) is incompatible with version %d\n", | |
495 | version); | |
496 | exit(1); | |
497 | } | |
498 | version = LDAP_VERSION3; | |
499 | use_tls = 1; | |
500 | break; | |
653b264e | 501 | #endif |
26ac0430 | 502 | case 'd': |
e673ba3a | 503 | debug_enabled = 1; |
26ac0430 AJ |
504 | break; |
505 | default: | |
506 | fprintf(stderr, PROGRAM_NAME ": ERROR: Unknown command line option '%c'\n", option); | |
507 | exit(1); | |
508 | } | |
331ba756 | 509 | } |
7c2a6c51 | 510 | |
2c10afe0 | 511 | while (argc > 1) { |
26ac0430 AJ |
512 | char *value = argv[1]; |
513 | if (ldapServer) { | |
514 | int len = strlen(ldapServer) + 1 + strlen(value) + 1; | |
c14fb378 | 515 | char *newhost = static_cast<char*>(xmalloc(len)); |
26ac0430 AJ |
516 | snprintf(newhost, len, "%s %s", ldapServer, value); |
517 | free(ldapServer); | |
518 | ldapServer = newhost; | |
519 | } else { | |
bb85e424 | 520 | ldapServer = xstrdup(value); |
26ac0430 | 521 | } |
755494da FC |
522 | --argc; |
523 | ++argv; | |
20b6fc8e | 524 | } |
525 | if (!ldapServer) | |
bb85e424 | 526 | ldapServer = xstrdup("localhost"); |
20b6fc8e | 527 | |
528 | if (!basedn) { | |
26ac0430 AJ |
529 | fprintf(stderr, "Usage: " PROGRAM_NAME " -b basedn [options] [ldap_server_name[:port]]...\n\n"); |
530 | fprintf(stderr, "\t-b basedn (REQUIRED)\tbase dn under which to search\n"); | |
531 | fprintf(stderr, "\t-f filter\t\tsearch filter to locate user DN\n"); | |
532 | fprintf(stderr, "\t-u userattr\t\tusername DN attribute\n"); | |
533 | fprintf(stderr, "\t-s base|one|sub\t\tsearch scope\n"); | |
534 | fprintf(stderr, "\t-D binddn\t\tDN to bind as to perform searches\n"); | |
535 | fprintf(stderr, "\t-w bindpasswd\t\tpassword for binddn\n"); | |
536 | fprintf(stderr, "\t-W secretfile\t\tread password for binddn from file secretfile\n"); | |
7ba68818 | 537 | #if HAS_URI_SUPPORT |
26ac0430 | 538 | fprintf(stderr, "\t-H URI\t\t\tLDAPURI (defaults to ldap://localhost)\n"); |
7ba68818 | 539 | #endif |
26ac0430 AJ |
540 | fprintf(stderr, "\t-h server\t\tLDAP server (defaults to localhost)\n"); |
541 | fprintf(stderr, "\t-p port\t\t\tLDAP server port\n"); | |
542 | fprintf(stderr, "\t-P\t\t\tpersistent LDAP connection\n"); | |
653b264e | 543 | #if defined(NETSCAPE_SSL) |
26ac0430 | 544 | fprintf(stderr, "\t-E sslcertpath\t\tenable LDAP over SSL\n"); |
653b264e | 545 | #endif |
26ac0430 AJ |
546 | fprintf(stderr, "\t-c timeout\t\tconnect timeout\n"); |
547 | fprintf(stderr, "\t-t timelimit\t\tsearch time limit\n"); | |
548 | fprintf(stderr, "\t-R\t\t\tdo not follow referrals\n"); | |
549 | fprintf(stderr, "\t-a never|always|search|find\n\t\t\t\twhen to dereference aliases\n"); | |
7ba68818 | 550 | #ifdef LDAP_VERSION3 |
26ac0430 AJ |
551 | fprintf(stderr, "\t-v 2|3\t\t\tLDAP version\n"); |
552 | fprintf(stderr, "\t-Z\t\t\tTLS encrypt the LDAP connection, requires LDAP version 3\n"); | |
7ba68818 | 553 | #endif |
af6a12ee | 554 | fprintf(stderr, "\t-d\t\t\tenable debug mode\n"); |
26ac0430 AJ |
555 | fprintf(stderr, "\n"); |
556 | fprintf(stderr, "\tIf no search filter is specified, then the dn <userattr>=user,basedn\n\twill be used (same as specifying a search filter of '<userattr>=',\n\tbut quicker as as there is no need to search for the user DN)\n\n"); | |
557 | 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"); | |
558 | exit(1); | |
94439e4e | 559 | } |
26ac0430 AJ |
560 | /* On Windows ldap_start_tls_s is available starting from Windows XP, |
561 | * so we need to bind at run-time with the function entry point | |
562 | */ | |
7aa9bb3e | 563 | #if _SQUID_WINDOWS_ |
e39ea462 | 564 | if (use_tls) { |
565 | ||
26ac0430 | 566 | HMODULE WLDAP32Handle; |
e39ea462 | 567 | |
26ac0430 AJ |
568 | WLDAP32Handle = GetModuleHandle("wldap32"); |
569 | if ((Win32_ldap_start_tls_s = (PFldap_start_tls_s) GetProcAddress(WLDAP32Handle, LDAP_START_TLS_S)) == NULL) { | |
570 | fprintf(stderr, PROGRAM_NAME ": ERROR: TLS (-Z) not supported on this platform.\n"); | |
571 | exit(1); | |
572 | } | |
e39ea462 | 573 | } |
574 | #endif | |
575 | ||
0af6cde9 | 576 | while (fgets(buf, sizeof(buf), stdin) != NULL) { |
26ac0430 AJ |
577 | user = strtok(buf, " \r\n"); |
578 | passwd = strtok(NULL, "\r\n"); | |
579 | ||
5cde6e7f AJ |
580 | if (!user) { |
581 | printf("ERR Missing username\n"); | |
582 | continue; | |
583 | } | |
584 | if (!passwd || !passwd[0]) { | |
585 | printf("ERR Missing password '%s'\n", user); | |
26ac0430 AJ |
586 | continue; |
587 | } | |
588 | rfc1738_unescape(user); | |
589 | rfc1738_unescape(passwd); | |
590 | if (!validUsername(user)) { | |
5cde6e7f | 591 | printf("ERR No such user '%s':'%s'\n",user, passwd); |
26ac0430 AJ |
592 | continue; |
593 | } | |
594 | tryagain = (ld != NULL); | |
595 | recover: | |
596 | if (ld == NULL && persistent) | |
597 | ld = open_ldap_connection(ldapServer, port); | |
598 | if (checkLDAP(ld, user, passwd, ldapServer, port) != 0) { | |
599 | if (tryagain && squid_ldap_errno(ld) != LDAP_INVALID_CREDENTIALS) { | |
600 | tryagain = 0; | |
601 | ldap_unbind(ld); | |
602 | ld = NULL; | |
603 | goto recover; | |
604 | } | |
605 | printf("ERR %s\n", ldap_err2string(squid_ldap_errno(ld))); | |
606 | } else { | |
607 | printf("OK\n"); | |
608 | } | |
609 | if (ld && (squid_ldap_errno(ld) != LDAP_SUCCESS && squid_ldap_errno(ld) != LDAP_INVALID_CREDENTIALS)) { | |
610 | ldap_unbind(ld); | |
611 | ld = NULL; | |
612 | } | |
94439e4e | 613 | } |
c4c1f30c | 614 | if (ld) |
26ac0430 | 615 | ldap_unbind(ld); |
331ba756 | 616 | return 0; |
94439e4e | 617 | } |
618 | ||
307228f1 | 619 | static int |
620 | ldap_escape_value(char *escaped, int size, const char *src) | |
621 | { | |
622 | int n = 0; | |
623 | while (size > 4 && *src) { | |
26ac0430 AJ |
624 | switch (*src) { |
625 | case '*': | |
626 | case '(': | |
627 | case ')': | |
628 | case '\\': | |
629 | n += 3; | |
630 | size -= 3; | |
631 | if (size > 0) { | |
14942edd FC |
632 | *escaped = '\\'; |
633 | ++escaped; | |
634 | snprintf(escaped, 3, "%02x", (unsigned char) *src); | |
635 | ++src; | |
26ac0430 AJ |
636 | escaped += 2; |
637 | } | |
638 | break; | |
639 | default: | |
14942edd FC |
640 | *escaped = *src; |
641 | ++escaped; | |
642 | ++src; | |
755494da FC |
643 | ++n; |
644 | --size; | |
26ac0430 | 645 | } |
307228f1 | 646 | } |
647 | *escaped = '\0'; | |
648 | return n; | |
649 | } | |
650 | ||
f3be8d13 | 651 | /* Check the userid & password. |
652 | * Return 0 on success, 1 on failure | |
653 | */ | |
c4c1f30c | 654 | static int |
b627c18a | 655 | checkLDAP(LDAP * persistent_ld, const char *userid, const char *password, const char *ldapServer, int port) |
94439e4e | 656 | { |
0af6cde9 | 657 | char dn[1024]; |
b627c18a | 658 | int ret = 0; |
659 | LDAP *bind_ld = NULL; | |
94439e4e | 660 | |
c4c1f30c | 661 | if (!*password) { |
26ac0430 AJ |
662 | /* LDAP can't bind with a blank password. Seen as "anonymous" |
663 | * and always granted access | |
664 | */ | |
e673ba3a | 665 | debug("Blank password given\n"); |
26ac0430 | 666 | return 1; |
c4c1f30c | 667 | } |
331ba756 | 668 | if (searchfilter) { |
26ac0430 AJ |
669 | char filter[16384]; |
670 | char escaped_login[1024]; | |
671 | LDAPMessage *res = NULL; | |
672 | LDAPMessage *entry; | |
673 | char *searchattr[] = {(char *)LDAP_NO_ATTRS, NULL}; | |
674 | char *userdn; | |
675 | int rc; | |
676 | LDAP *search_ld = persistent_ld; | |
677 | ||
678 | if (!search_ld) | |
679 | search_ld = open_ldap_connection(ldapServer, port); | |
680 | ||
681 | ldap_escape_value(escaped_login, sizeof(escaped_login), userid); | |
682 | if (binddn) { | |
683 | rc = ldap_simple_bind_s(search_ld, binddn, bindpasswd); | |
684 | if (rc != LDAP_SUCCESS) { | |
685 | fprintf(stderr, PROGRAM_NAME ": WARNING, could not bind to binddn '%s'\n", ldap_err2string(rc)); | |
686 | ret = 1; | |
687 | goto search_done; | |
688 | } | |
689 | } | |
690 | snprintf(filter, sizeof(filter), searchfilter, 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); | |
e673ba3a | 691 | debug("user filter '%s', searchbase '%s'\n", filter, basedn); |
26ac0430 AJ |
692 | rc = ldap_search_s(search_ld, basedn, searchscope, filter, searchattr, 1, &res); |
693 | if (rc != LDAP_SUCCESS) { | |
694 | if (noreferrals && rc == LDAP_PARTIAL_RESULTS) { | |
695 | /* Everything is fine. This is expected when referrals | |
696 | * are disabled. | |
697 | */ | |
e673ba3a | 698 | debug("noreferrals && rc == LDAP_PARTIAL_RESULTS\n"); |
26ac0430 AJ |
699 | } else { |
700 | fprintf(stderr, PROGRAM_NAME ": WARNING, LDAP search error '%s'\n", ldap_err2string(rc)); | |
653b264e | 701 | #if defined(NETSCAPE_SSL) |
26ac0430 AJ |
702 | if (sslpath && ((rc == LDAP_SERVER_DOWN) || (rc == LDAP_CONNECT_ERROR))) { |
703 | int sslerr = PORT_GetError(); | |
704 | fprintf(stderr, PROGRAM_NAME ": WARNING, SSL error %d (%s)\n", sslerr, ldapssl_err2string(sslerr)); | |
705 | } | |
653b264e | 706 | #endif |
26ac0430 AJ |
707 | ret = 1; |
708 | goto search_done; | |
709 | } | |
710 | } | |
711 | entry = ldap_first_entry(search_ld, res); | |
712 | if (!entry) { | |
e673ba3a | 713 | debug("Ldap search returned nothing\n"); |
26ac0430 AJ |
714 | ret = 1; |
715 | goto search_done; | |
716 | } | |
717 | userdn = ldap_get_dn(search_ld, entry); | |
718 | if (!userdn) { | |
719 | fprintf(stderr, PROGRAM_NAME ": ERROR, could not get user DN for '%s'\n", userid); | |
720 | ret = 1; | |
721 | goto search_done; | |
722 | } | |
723 | snprintf(dn, sizeof(dn), "%s", userdn); | |
724 | squid_ldap_memfree(userdn); | |
725 | ||
726 | if (ret == 0 && (!binddn || !bind_once || passwdattr)) { | |
727 | /* Reuse the search connection for comparing the user password attribute */ | |
728 | bind_ld = search_ld; | |
729 | search_ld = NULL; | |
730 | } | |
731 | search_done: | |
732 | if (res) { | |
733 | ldap_msgfree(res); | |
734 | res = NULL; | |
735 | } | |
736 | if (search_ld && search_ld != persistent_ld) { | |
737 | ldap_unbind(search_ld); | |
738 | search_ld = NULL; | |
739 | } | |
740 | if (ret != 0) | |
741 | return ret; | |
331ba756 | 742 | } else { |
26ac0430 | 743 | snprintf(dn, sizeof(dn), "%s=%s,%s", userattr, userid, basedn); |
94439e4e | 744 | } |
331ba756 | 745 | |
e673ba3a | 746 | debug("attempting to authenticate user '%s'\n", dn); |
b627c18a | 747 | if (!bind_ld && !bind_once) |
26ac0430 | 748 | bind_ld = persistent_ld; |
b627c18a | 749 | if (!bind_ld) |
26ac0430 | 750 | bind_ld = open_ldap_connection(ldapServer, port); |
f8c818e4 | 751 | if (passwdattr) { |
26ac0430 AJ |
752 | if (ldap_compare_s(bind_ld, dn, passwdattr, password) != LDAP_COMPARE_TRUE) { |
753 | ret = 1; | |
754 | } | |
f8c818e4 | 755 | } else if (ldap_simple_bind_s(bind_ld, dn, password) != LDAP_SUCCESS) |
26ac0430 | 756 | ret = 1; |
b627c18a | 757 | if (bind_ld != persistent_ld) { |
26ac0430 AJ |
758 | ldap_unbind(bind_ld); |
759 | bind_ld = NULL; | |
b627c18a | 760 | } |
7c211f58 | 761 | return ret; |
94439e4e | 762 | } |
954a8513 | 763 | |
f3be8d13 | 764 | int |
b627c18a | 765 | readSecret(const char *filename) |
954a8513 | 766 | { |
b627c18a | 767 | char buf[BUFSIZ]; |
768 | char *e = NULL; | |
769 | FILE *f; | |
770 | char *passwd = NULL; | |
954a8513 | 771 | |
b627c18a | 772 | if (!(f = fopen(filename, "r"))) { |
26ac0430 AJ |
773 | fprintf(stderr, PROGRAM_NAME " ERROR: Can not read secret file %s\n", filename); |
774 | return 1; | |
b627c18a | 775 | } |
776 | if (!fgets(buf, sizeof(buf) - 1, f)) { | |
26ac0430 AJ |
777 | fprintf(stderr, PROGRAM_NAME " ERROR: Secret file %s is empty\n", filename); |
778 | fclose(f); | |
779 | return 1; | |
b627c18a | 780 | } |
781 | /* strip whitespaces on end */ | |
782 | if ((e = strrchr(buf, '\n'))) | |
26ac0430 | 783 | *e = 0; |
b627c18a | 784 | if ((e = strrchr(buf, '\r'))) |
26ac0430 | 785 | *e = 0; |
b627c18a | 786 | |
787 | passwd = (char *) calloc(sizeof(char), strlen(buf) + 1); | |
788 | if (!passwd) { | |
26ac0430 AJ |
789 | fprintf(stderr, PROGRAM_NAME " ERROR: can not allocate memory\n"); |
790 | exit(1); | |
b627c18a | 791 | } |
792 | strcpy(passwd, buf); | |
793 | bindpasswd = passwd; | |
954a8513 | 794 | |
954a8513 | 795 | fclose(f); |
954a8513 | 796 | |
b627c18a | 797 | return 0; |
954a8513 | 798 | } |
f53969cc | 799 |