I'm trying to use the OpenLDAP C APIs to execute a search query against an AD.
#include <ldap.h>
#include <iostream>
int main(void)
{
LDAP *ldp;
auto ldap_version = LDAP_VERSION3;
auto tls_cert_strat = LDAP_OPT_X_TLS_NEVER; //Never require identity cert from peer
int err = ldap_initialize(&ldp,"ldap://1.1.1.1");
if (err != LDAP_SUCCESS)
{
std::cerr << "unable to connect" << std::endl;
return -1;
}
std::cout << "connected, err == " << ldap_err2string(err) << "\n";
ldap_set_option(ldp, LDAP_OPT_PROTOCOL_VERSION, &ldap_version); //Set version to LDAPv3
ldap_set_option(ldp, LDAP_OPT_X_TLS_REQUIRE_CERT, &tls_cert_strat);
err = ldap_start_tls_s(ldp, nullptr, nullptr);
if (err != LDAP_SUCCESS)
{
char* msg;
ldap_get_option(ldp, LDAP_OPT_DIAGNOSTIC_MESSAGE, &msg);
std::cerr << "unable to upgrade to TLS! err: " << ldap_err2string(err) << std::endl;
std::cerr << "diagnostic msg: " << msg << "\n";
ldap_memfree(msg);
return -1;
}
std::cout << "start_tls request sent successfully, err == " << ldap_err2string(err) << "\n";
err = ldap_tls_inplace(ldp);
if (err != 1)
{
char* msg;
ldap_get_option(ldp, LDAP_OPT_DIAGNOSTIC_MESSAGE, &msg);
std::cerr << "TLS handlers not installed! err: " << ldap_err2string(err) << std::endl;
std::cerr << "diagnostic msg: " << msg << "\n";
ldap_memfree(msg);
return -1;
}
std::cout << "TLS handlers have been installed sucessfully" << "\n";
//err = ldap_bind_s(ldp, "user(a)domain.com", "foo", LDAP_AUTH_SIMPLE);
err = ldap_simple_bind_s(ldp, "user(a)domain.com", "foo");
if (err != LDAP_SUCCESS)
{
char* msg;
ldap_get_option(ldp, LDAP_OPT_DIAGNOSTIC_MESSAGE, &msg);
std::cerr << "Unable to bind to domain! err: " << ldap_err2string(err) << std::endl;
std::cerr << "diagnostic msg: " << msg << "\n";
ldap_memfree(msg);
return -1;
}
std::cout << "Successfully bound to DC\n";
char* base_dn = const_cast<char*>("dc=domain,dc=com");
char* filters = const_cast<char*>("userPrincipalName=user");
char* attr[4];
attr[0] = const_cast<char*>("samaccountname");
attr[1] = const_cast<char*>("cn");
attr[2] = const_cast<char*>("mail");
attr[3] = nullptr;
LDAPMessage* ldp_msg;
std::cout << "Executing LDAP search query\n";
err = ldap_search_ext_s(ldp, base_dn, LDAP_SCOPE_SUBTREE, filters, attr, 0, nullptr, nullptr, nullptr, 0, &ldp_msg);
if (err != LDAP_SUCCESS)
{
char* msg;
ldap_get_option(ldp, LDAP_OPT_DIAGNOSTIC_MESSAGE, &msg);
std::cerr << "Unable to search domain! err: " << ldap_err2string(err) << std::endl;
std::cerr << "diagnostic msg: " << msg << "\n";
ldap_memfree(msg);
return -1;
}
std::cout << "Search executed successfully\n";
return 0;
}
I build the code as follows:
g++8 -D LDAP_DEPRECATED -g -std=c++17 my_ldap.cpp -o my_ldap -l ldap
But when I execute it, I get the following error:
connected, err == Success
start_tls request sent successfully, err == Success
TLS handlers have been installed sucessfully
Successfully bound to DC
Executing LDAP search query
Unable to search domain! err: Operations error
diagnostic msg: 000004DC: LdapErr: DSID-0C0907E1, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v2580
This is where I'm confused: The diagnostic message says that I didn't bind, but given that ldap_simple_search_s returned LDAP_SUCCESS, that would mean that I did have a successful bind.
I would understand if the search fails with a message citing lack of authorization, filter not matching, etc.
The only other thing that I could think of is that I may have bound to one DN and executed a search against a different DN, but I checked for any typos several times, and nothing popped up.
Just FYI, ldap_bind_s(ldp, "user(a)domain.com", "foo", LDAP_AUTH_SIMPLE) has the same behavior. Now, if the DC didn't like my bind/authentication method, I would imagine that would've caused an error during the bind call, but nothing of the sort happened.
I thought about using the built-in debugging of the library as documented in the man pages for ldap_set_option, i.e. the LDAP_OPT_DEBUG_LEVEL option, but the code doesn't compile when I try to use one of the options as shown in the man page (incidentally none of those options are defined in ldap.h)
I've tried a few other random (I'm saying random coz I doubt this is the cause) things too:
a) Tried binding to the secure port directly instead of upgrading to TLS.
b) Tried disabling LDAP_OPT_REFERRALS
One strange thing that I observed though was that if the changed the syntax of the bind dn to "uid=user,dc=domain,dc=com" that would result in an "Invalid credentials" error from ldap_bind_s, but the "user(a)domain.com" format is accepted without any errors.
I'm unable to check the contents of LDAP* in gdb because I cannot build the openldap port in freeBSD with non-stripped symbols (even though I built the port with debug support).
What other angle should I be looking at to understand/resolve this problem?