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