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@domain.com", "foo", LDAP_AUTH_SIMPLE); err = ldap_simple_bind_s(ldp, "user@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@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@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?