2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
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.
10 * AUTHOR: Flavio Pescuma, MARA Systems AB <flavio@marasystems.com>
16 #define LDAP_DEPRECATED 1
18 #include "auth/digest/eDirectory/ldap_backend.h"
20 #if _SQUID_WINDOWS_ && !_SQUID_CYGWIN_
25 #define LDAPAPI __cdecl
28 #ifndef LDAP_OPT_X_TLS
29 #define LDAP_OPT_X_TLS 0x6000
31 /* Some tricks to allow dynamic bind with ldap_start_tls_s entry point at
34 #undef ldap_start_tls_s
36 #define LDAP_START_TLS_S "ldap_start_tls_sW"
37 typedef WINLDAPAPI
ULONG(LDAPAPI
* PFldap_start_tls_s
) (IN PLDAP
, OUT PULONG
, OUT LDAPMessage
**, IN PLDAPControlW
*, IN PLDAPControlW
*);
39 #define LDAP_START_TLS_S "ldap_start_tls_sA"
40 typedef WINLDAPAPI
ULONG(LDAPAPI
* PFldap_start_tls_s
) (IN PLDAP
, OUT PULONG
, OUT LDAPMessage
**, IN PLDAPControlA
*, IN PLDAPControlA
*);
41 #endif /* LDAP_UNICODE */
42 PFldap_start_tls_s Win32_ldap_start_tls_s
;
43 #define ldap_start_tls_s(l,s,c) Win32_ldap_start_tls_s(l,NULL,NULL,s,c)
44 #endif /* LDAP_VERSION3 */
52 #include "auth/digest/eDirectory/edir_ldapext.h"
53 #define PROGRAM_NAME "digest_pw_auth(LDAP_backend)"
57 static LDAP
*ld
= nullptr;
58 static const char *passattr
= nullptr;
59 static char *ldapServer
= nullptr;
60 static const char *userbasedn
= nullptr;
61 static const char *userdnattr
= nullptr;
62 static const char *usersearchfilter
= nullptr;
63 static const char *binddn
= nullptr;
64 static const char *bindpasswd
= nullptr;
65 static const char *delimiter
= ":";
66 static int encrpass
= 0;
67 static int searchscope
= LDAP_SCOPE_SUBTREE
;
68 static int persistent
= 0;
69 static int noreferrals
= 0;
70 static int port
= LDAP_PORT
;
71 static int strip_nt_domain
= 0;
72 static int edir_universal_passwd
= 0;
73 static int aliasderef
= LDAP_DEREF_NEVER
;
74 #if defined(NETSCAPE_SSL)
75 static char *sslpath
= NULL
;
76 static int sslinit
= 0;
78 static int connect_timeout
= 0;
79 static int timelimit
= LDAP_NO_LIMIT
;
82 /* Added for TLS support and version 3 */
83 static int use_tls
= 0;
84 static int version
= -1;
87 static void ldapconnect(void);
88 static int readSecret(const char *filename
);
90 /* Yuck.. we need to glue to different versions of the API */
92 #if defined(LDAP_API_VERSION) && LDAP_API_VERSION > 1823
94 squid_ldap_set_aliasderef(int deref
)
96 ldap_set_option(ld
, LDAP_OPT_DEREF
, &deref
);
99 squid_ldap_set_referrals(int referrals
)
101 int *value
= static_cast<int*>(referrals
? LDAP_OPT_ON
:LDAP_OPT_OFF
);
102 ldap_set_option(ld
, LDAP_OPT_REFERRALS
, value
);
105 squid_ldap_set_timelimit(int aTimeLimit
)
107 ldap_set_option(ld
, LDAP_OPT_TIMELIMIT
, &aTimeLimit
);
110 squid_ldap_set_connect_timeout(int aTimeLimit
)
112 #if defined(LDAP_OPT_NETWORK_TIMEOUT)
114 tv
.tv_sec
= aTimeLimit
;
116 ldap_set_option(ld
, LDAP_OPT_NETWORK_TIMEOUT
, &tv
);
117 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
119 ldap_set_option(ld
, LDAP_X_OPT_CONNECT_TIMEOUT
, &aTimeLimit
);
125 squid_ldap_errno(LDAP
* ld
)
130 squid_ldap_set_aliasderef(int deref
)
132 ld
->ld_deref
= deref
;
135 squid_ldap_set_referrals(int referrals
)
138 ld
->ld_options
|= ~LDAP_OPT_REFERRALS
;
140 ld
->ld_options
&= ~LDAP_OPT_REFERRALS
;
143 squid_ldap_set_timelimit(int aTimeLimit
)
145 ld
->ld_timelimit
= aTimeLimit
;
148 squid_ldap_set_connect_timeout(int aTimeLimit
)
150 fprintf(stderr
, "ERROR: Connect timeouts not supported in your LDAP library\n");
153 squid_ldap_memfree(char *p
)
160 #ifdef LDAP_API_FEATURE_X_OPENLDAP
161 #if LDAP_VENDOR_VERSION > 194
162 #define HAS_URI_SUPPORT 1
167 ldap_escape_value(char *escaped
, int size
, const char *src
)
170 while (size
> 4 && *src
) {
181 std::snprintf(escaped
, 3, "%02x", (int) *src
);
199 getpassword(char *login
, char *realm
)
201 LDAPMessage
*res
= nullptr;
203 char **values
= nullptr;
204 char **value
= nullptr;
205 char *password
= nullptr;
209 char searchbase
[8192];
211 char *universal_password
= nullptr;
212 size_t universal_password_len
= 256;
216 if (usersearchfilter
) {
217 char escaped_login
[1024];
218 std::snprintf(searchbase
, sizeof(searchbase
), "%s", userbasedn
);
219 ldap_escape_value(escaped_login
, sizeof(escaped_login
), login
);
220 std::snprintf(filter
, sizeof(filter
), usersearchfilter
, escaped_login
, escaped_login
, escaped_login
, escaped_login
, escaped_login
, escaped_login
, escaped_login
, escaped_login
, escaped_login
, escaped_login
, escaped_login
, escaped_login
, escaped_login
, escaped_login
, escaped_login
, escaped_login
);
223 debug("user filter '%s', searchbase '%s'\n", filter
, searchbase
);
225 rc
= ldap_search_s(ld
, searchbase
, searchscope
, filter
, nullptr, 0, &res
);
226 if (rc
!= LDAP_SUCCESS
) {
227 if (noreferrals
&& rc
== LDAP_PARTIAL_RESULTS
) {
228 /* Everything is fine. This is expected when referrals
233 fprintf(stderr
, PROGRAM_NAME
" WARNING, LDAP search error '%s'\n", ldap_err2string(rc
));
234 #if defined(NETSCAPE_SSL)
235 if (sslpath
&& ((rc
== LDAP_SERVER_DOWN
) || (rc
== LDAP_CONNECT_ERROR
))) {
236 int sslerr
= PORT_GetError();
237 fprintf(stderr
, PROGRAM_NAME
": WARNING, SSL error %d (%s)\n", sslerr
, ldapssl_err2string(sslerr
));
240 fprintf(stderr
, PROGRAM_NAME
" WARNING, LDAP search error, trying to recover'%s'\n", ldap_err2string(rc
));
242 /* try to connect to the LDAP server again, maybe my persistent conexion failed. */
254 } else if (userdnattr
) {
255 std::snprintf(searchbase
, 8192, "%s=%s, %s", userdnattr
, login
, userbasedn
);
258 debug("searchbase '%s'\n", searchbase
);
259 rc
= ldap_search_s(ld
, searchbase
, searchscope
, nullptr, nullptr, 0, &res
);
261 if (rc
== LDAP_SUCCESS
) {
262 entry
= ldap_first_entry(ld
, res
);
264 debug("ldap dn: %s\n", ldap_get_dn(ld
, entry
));
265 if (edir_universal_passwd
) {
267 /* allocate some memory for the universal password returned by NMAS */
268 universal_password
= (char*)calloc(1, universal_password_len
);
269 values
= (char**)calloc(1, sizeof(char *));
271 /* actually talk to NMAS to get a password */
272 nmas_res
= nds_get_password(ld
, ldap_get_dn(ld
, entry
), &universal_password_len
, universal_password
);
273 if (nmas_res
== LDAP_SUCCESS
&& universal_password
) {
274 debug("NMAS returned value %s\n", universal_password
);
275 values
[0] = universal_password
;
277 debug("Error reading Universal Password: %d = %s\n", nmas_res
, ldap_err2string(nmas_res
));
280 values
= ldap_get_values(ld
, entry
, passattr
);
287 debug("No attribute value found\n");
288 if (edir_universal_passwd
)
289 free(universal_password
);
296 const char *t
= strtok(*value
, delimiter
);
297 if (t
&& strcmp(t
, realm
) == 0) {
298 password
= strtok(nullptr, delimiter
);
307 debug("password: %s\n", password
);
309 password
= xstrdup(password
);
310 if (edir_universal_passwd
) {
312 free(universal_password
);
314 ldap_value_free(values
);
319 fprintf(stderr
, PROGRAM_NAME
" WARNING, LDAP error '%s'\n", ldap_err2string(rc
));
320 /* try to connect to the LDAP server again, maybe my persistent conexion failed. */
339 /* On Windows ldap_start_tls_s is available starting from Windows XP,
340 * so we need to bind at run-time with the function entry point
345 HMODULE WLDAP32Handle
;
347 WLDAP32Handle
= GetModuleHandle("wldap32");
348 if ((Win32_ldap_start_tls_s
= (PFldap_start_tls_s
) GetProcAddress(WLDAP32Handle
, LDAP_START_TLS_S
)) == NULL
) {
349 fprintf(stderr
, PROGRAM_NAME
": ERROR: TLS (-Z) not supported on this platform.\n");
357 if (strstr(ldapServer
, "://") != nullptr) {
358 rc
= ldap_initialize(&ld
, ldapServer
);
359 if (rc
!= LDAP_SUCCESS
) {
360 fprintf(stderr
, "\nUnable to connect to LDAPURI:%s\n", ldapServer
);
366 if (!sslinit
&& (ldapssl_client_init(sslpath
, NULL
) != LDAP_SUCCESS
)) {
367 fprintf(stderr
, "\nUnable to initialise SSL with cert path %s\n",
373 if ((ld
= ldapssl_init(ldapServer
, port
, 1)) == NULL
) {
374 fprintf(stderr
, "\nUnable to connect to SSL LDAP server: %s port:%d\n",
380 if ((ld
= ldap_init(ldapServer
, port
)) == nullptr) {
381 fprintf(stderr
, "\nUnable to connect to LDAP server:%s port:%d\n", ldapServer
, port
);
384 squid_ldap_set_connect_timeout(connect_timeout
);
388 version
= LDAP_VERSION2
;
390 if (ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
)
392 fprintf(stderr
, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n",
398 #ifdef LDAP_OPT_X_TLS
399 if ((version
== LDAP_VERSION3
) && (ldap_start_tls_s(ld
, nullptr, nullptr) == LDAP_SUCCESS
)) {
400 fprintf(stderr
, "Could not Activate TLS connection\n");
405 fprintf(stderr
, "TLS not supported with your LDAP library\n");
411 squid_ldap_set_timelimit(timelimit
);
412 squid_ldap_set_referrals(!noreferrals
);
413 squid_ldap_set_aliasderef(aliasderef
);
414 if (binddn
&& bindpasswd
&& *binddn
&& *bindpasswd
) {
415 rc
= ldap_simple_bind_s(ld
, binddn
, bindpasswd
);
416 if (rc
!= LDAP_SUCCESS
) {
417 fprintf(stderr
, PROGRAM_NAME
" WARNING, could not bind to binddn '%s'\n", ldap_err2string(rc
));
422 debug("Connected OK\n");
426 LDAPArguments(int argc
, char **argv
)
428 setbuf(stdout
, nullptr);
430 while (argc
> 1 && argv
[1][0] == '-') {
431 const char *value
= "";
432 char option
= argv
[1][1];
445 if (strlen(argv
[1]) > 2) {
447 } else if (argc
> 2) {
460 fprintf(stderr
, "ERROR: Your LDAP library does not have URI support\n");
463 /* Fall thru to -h */
466 int len
= strlen(ldapServer
) + 1 + strlen(value
) + 1;
467 char *newhost
= static_cast<char*>(xmalloc(len
));
468 std::snprintf(newhost
, len
, "%s %s", ldapServer
, value
);
470 ldapServer
= newhost
;
472 ldapServer
= xstrdup(value
);
488 usersearchfilter
= value
;
494 if (strcmp(value
, "base") == 0)
495 searchscope
= LDAP_SCOPE_BASE
;
496 else if (strcmp(value
, "one") == 0)
497 searchscope
= LDAP_SCOPE_ONELEVEL
;
498 else if (strcmp(value
, "sub") == 0)
499 searchscope
= LDAP_SCOPE_SUBTREE
;
501 fprintf(stderr
, PROGRAM_NAME
" ERROR: Unknown search scope '%s'\n", value
);
506 #if defined(NETSCAPE_SSL)
508 if (port
== LDAP_PORT
)
511 fprintf(stderr
, PROGRAM_NAME
" ERROR: -E unsupported with this LDAP library\n");
516 connect_timeout
= atoi(value
);
519 timelimit
= atoi(value
);
522 if (strcmp(value
, "never") == 0)
523 aliasderef
= LDAP_DEREF_NEVER
;
524 else if (strcmp(value
, "always") == 0)
525 aliasderef
= LDAP_DEREF_ALWAYS
;
526 else if (strcmp(value
, "search") == 0)
527 aliasderef
= LDAP_DEREF_SEARCHING
;
528 else if (strcmp(value
, "find") == 0)
529 aliasderef
= LDAP_DEREF_FINDING
;
531 fprintf(stderr
, PROGRAM_NAME
" ERROR: Unknown alias dereference method '%s'\n", value
);
545 persistent
= !persistent
;
551 noreferrals
= !noreferrals
;
555 switch (atoi(value
)) {
557 version
= LDAP_VERSION2
;
560 version
= LDAP_VERSION3
;
563 fprintf(stderr
, "Protocol version should be 2 or 3\n");
568 if (version
== LDAP_VERSION2
) {
569 fprintf(stderr
, "TLS (-Z) is incompatible with version %d\n",
573 version
= LDAP_VERSION3
;
584 edir_universal_passwd
= 1;
587 fprintf(stderr
, PROGRAM_NAME
" ERROR: Unknown command line option '%c'\n", option
);
593 char *value
= argv
[1];
595 int len
= strlen(ldapServer
) + 1 + strlen(value
) + 1;
596 char *newhost
= static_cast<char*>(xmalloc(len
));
597 std::snprintf(newhost
, len
, "%s %s", ldapServer
, value
);
599 ldapServer
= newhost
;
601 ldapServer
= xstrdup(value
);
608 ldapServer
= (char *) "localhost";
610 if (!userbasedn
|| !((passattr
!= nullptr) || (edir_universal_passwd
&& usersearchfilter
&& version
== LDAP_VERSION3
&& use_tls
))) {
611 fprintf(stderr
, "Usage: " PROGRAM_NAME
" -b basedn -f filter [options] ldap_server_name\n\n");
612 fprintf(stderr
, "\t-A password attribute(REQUIRED)\t\tUser attribute that contains the password\n");
613 fprintf(stderr
, "\t-l password realm delimiter(REQUIRED)\tCharater(s) that divides the password attribute\n\t\t\t\t\t\tin realm and password tokens, default ':' realm:password\n");
614 fprintf(stderr
, "\t-b basedn (REQUIRED)\t\t\tbase dn under where to search for users\n");
615 fprintf(stderr
, "\t-e Encrypted passwords(REQUIRED)\tPassword are stored encrypted using HHA1\n");
616 fprintf(stderr
, "\t-F filter\t\t\t\tuser search filter pattern. %%s = login\n");
617 fprintf(stderr
, "\t-u attribute\t\t\t\tattribute to use in combination with the basedn to create the user DN\n");
618 fprintf(stderr
, "\t-s base|one|sub\t\t\t\tsearch scope\n");
619 fprintf(stderr
, "\t-D binddn\t\t\t\tDN to bind as to perform searches\n");
620 fprintf(stderr
, "\t-w bindpasswd\t\t\t\tpassword for binddn\n");
621 fprintf(stderr
, "\t-W secretfile\t\t\t\tread password for binddn from file secretfile\n");
623 fprintf(stderr
, "\t-H URI\t\t\t\t\tLDAPURI (defaults to ldap://localhost)\n");
625 fprintf(stderr
, "\t-h server\t\t\t\tLDAP server (defaults to localhost)\n");
626 fprintf(stderr
, "\t-p port\t\t\t\t\tLDAP server port (defaults to %d)\n", LDAP_PORT
);
627 fprintf(stderr
, "\t-P\t\t\t\t\tpersistent LDAP connection\n");
628 #if defined(NETSCAPE_SSL)
629 fprintf(stderr
, "\t-E sslcertpath\t\t\t\tenable LDAP over SSL\n");
631 fprintf(stderr
, "\t-c timeout\t\t\t\tconnect timeout\n");
632 fprintf(stderr
, "\t-t timelimit\t\t\t\tsearch time limit\n");
633 fprintf(stderr
, "\t-R\t\t\t\t\tdo not follow referrals\n");
634 fprintf(stderr
, "\t-a never|always|search|find\t\twhen to dereference aliases\n");
636 fprintf(stderr
, "\t-v 2|3\t\t\t\t\tLDAP version\n");
637 fprintf(stderr
, "\t-Z\t\t\t\t\tTLS encrypt the LDAP connection, requires\n\t\t\t\tLDAP version 3\n");
639 fprintf(stderr
, "\t-S\t\t\t\t\tStrip NT domain from usernames\n");
640 fprintf(stderr
, "\t-n\t\t\t\t\tGet an eDirectory Universal Password from Novell NMAS\n\t\t\t\t\t\t(requires bind credentials, version 3, TLS, and a search filter)\n");
641 fprintf(stderr
, "\n");
642 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");
648 readSecret(const char *filename
)
654 if (!(f
= fopen(filename
, "r"))) {
655 fprintf(stderr
, PROGRAM_NAME
" ERROR: Can not read secret file %s\n", filename
);
658 if (!fgets(buf
, sizeof(buf
) - 1, f
)) {
659 fprintf(stderr
, PROGRAM_NAME
" ERROR: Secret file %s is empty\n", filename
);
663 /* strip whitespaces on end */
664 if ((e
= strrchr(buf
, '\n')))
666 if ((e
= strrchr(buf
, '\r')))
669 bindpasswd
= xstrdup(buf
);
671 fprintf(stderr
, PROGRAM_NAME
" ERROR: can not allocate memory\n");
679 LDAPHHA1(RequestData
* requestData
)
683 password
= getpassword(requestData
->user
, requestData
->realm
);
684 if (password
!= nullptr) {
686 xstrncpy(requestData
->HHA1
, password
, sizeof(requestData
->HHA1
));
689 DigestCalcHA1("md5", requestData
->user
, requestData
->realm
, password
, nullptr, nullptr, HA1
, requestData
->HHA1
);
693 requestData
->error
= -1;