]> git.ipfire.org Git - thirdparty/squid.git/blame - helpers/basic_auth/LDAP/basic_ldap_auth.cc
SourceFormat Enforcement
[thirdparty/squid.git] / helpers / basic_auth / LDAP / basic_ldap_auth.cc
CommitLineData
5b95b903 1/*
bde978a6 2 * Copyright (C) 1996-2015 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 120typedef 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 123typedef WINLDAPAPI ULONG(LDAPAPI * PFldap_start_tls_s) (IN PLDAP, OUT PULONG, OUT LDAPMessage **, IN PLDAPControlA *, IN PLDAPControlA *);
e39ea462 124#endif /* LDAP_UNICODE */
125PFldap_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 139static const char *basedn;
140static const char *searchfilter = NULL;
141static const char *binddn = NULL;
142static const char *bindpasswd = NULL;
143static const char *userattr = "uid";
b627c18a 144static const char *passwdattr = NULL;
331ba756 145static int searchscope = LDAP_SCOPE_SUBTREE;
c4c1f30c 146static int persistent = 0;
b627c18a 147static int bind_once = 0;
50f87883 148static int noreferrals = 0;
149static int aliasderef = LDAP_DEREF_NEVER;
653b264e 150#if defined(NETSCAPE_SSL)
a0fbb6a7 151static const char *sslpath = NULL;
653b264e 152static int sslinit = 0;
153#endif
154static int connect_timeout = 0;
155static int timelimit = LDAP_NO_LIMIT;
94439e4e 156
8370dcf2 157/* Added for TLS support and version 3 */
158static int use_tls = 0;
159static int version = -1;
160
b627c18a 161static int checkLDAP(LDAP * ld, const char *userid, const char *password, const char *server, int port);
a0fbb6a7 162static 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 171static int
b671cc68 172squid_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 178static void
b671cc68 179squid_ldap_set_aliasderef(LDAP * ld, int deref)
87f6d1e1 180{
181 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
182}
9bea1d5b 183static void
b671cc68 184squid_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 189static void
d5f8d05f 190squid_ldap_set_timelimit(LDAP * ld, int aTimeLimit)
653b264e 191{
d5f8d05f 192 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &aTimeLimit);
653b264e 193}
194static void
d5f8d05f 195squid_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}
207static void
a9ce6538 208squid_ldap_memfree(char *p)
209{
210 ldap_memfree(p);
211}
b627c18a 212
87f6d1e1 213#else
9bea1d5b 214static int
b671cc68 215squid_ldap_errno(LDAP * ld)
87f6d1e1 216{
217 return ld->ld_errno;
218}
9bea1d5b 219static void
b671cc68 220squid_ldap_set_aliasderef(LDAP * ld, int deref)
87f6d1e1 221{
222 ld->ld_deref = deref;
223}
9bea1d5b 224static void
b671cc68 225squid_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 232static void
b627c18a 233squid_ldap_set_timelimit(LDAP * ld, int timelimit)
653b264e 234{
235 ld->ld_timelimit = timelimit;
236}
237static void
b627c18a 238squid_ldap_set_connect_timeout(LDAP * ld, int timelimit)
653b264e 239{
240 fprintf(stderr, "Connect timeouts not supported in your LDAP library\n");
241}
a9ce6538 242static void
243squid_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 256static LDAP *
257open_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 */
324static int
325validUsername(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 349int
350main(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;
8ea6914d 395 char *newhost = static_cast<char*>(malloc(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;
8ea6914d 515 char *newhost = static_cast<char*>(malloc(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);
595recover:
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 619static int
620ldap_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 654static int
b627c18a 655checkLDAP(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 }
731search_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 764int
b627c18a 765readSecret(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