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