123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- /*##############################################################################
- HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ############################################################################## */
- // LDAP prototypes use char* where they should be using const char *, resulting in lots of spurious warnings
- #pragma warning( disable : 4786 )
- #ifdef __GNUC__
- #pragma GCC diagnostic ignored "-Wwrite-strings"
- #endif
- #include "ldaputils.hpp"
- #ifndef _WIN32
- # include <signal.h>
- #endif
- //------------------------------------
- // LdapUtils implementation
- //------------------------------------
- LDAP* LdapUtils::LdapInit(const char* protocol, const char* host, int port, int secure_port)
- {
- LDAP* ld = NULL;
- if(stricmp(protocol, "ldaps") == 0)
- {
- #ifdef _WIN32
- ld = ldap_sslinit((char*)host, secure_port, 1);
- if (ld == NULL )
- throw MakeStringException(-1, "ldap_sslinit error" );
- int rc = 0;
- unsigned long version = LDAP_VERSION3;
- long lv = 0;
- rc = ldap_set_option(ld,
- LDAP_OPT_PROTOCOL_VERSION,
- (void*)&version);
- if (rc != LDAP_SUCCESS)
- throw MakeStringException(-1, "ldap_set_option error - %s", ldap_err2string(rc));
- rc = ldap_get_option(ld,LDAP_OPT_SSL,(void*)&lv);
- if (rc != LDAP_SUCCESS)
- throw MakeStringException(-1, "ldap_get_option error - %s", ldap_err2string(rc));
- // If SSL is not enabled, enable it.
- if ((void*)lv != LDAP_OPT_ON)
- {
- rc = ldap_set_option(ld, LDAP_OPT_SSL, LDAP_OPT_ON);
- if (rc != LDAP_SUCCESS)
- throw MakeStringException(-1, "ldap_set_option error - %s", ldap_err2string(rc));
- }
- ldap_set_option(ld, LDAP_OPT_SERVER_CERTIFICATE, verifyServerCert);
- #else
- // Initialize an LDAP session for TLS/SSL
- #ifndef HAVE_TLS
- //throw MakeStringException(-1, "openldap client library libldap not compiled with TLS support");
- #endif
- StringBuffer uri("ldaps://");
- uri.appendf("%s:%d", host, secure_port);
- DBGLOG("connecting to %s", uri.str());
- int rc = LDAP_INIT(&ld, uri.str());
- if(rc != LDAP_SUCCESS)
- {
- throw MakeStringException(-1, "ldap_initialize error %s", ldap_err2string(rc));
- }
- int reqcert = LDAP_OPT_X_TLS_NEVER;
- ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &reqcert);
- #endif
- }
- else
- {
- // Initialize an LDAP session
- DBGLOG("connecting to ldap://%s:%d", host, port);
- #ifdef _WIN32
- ld = LDAP_INIT(host, port);
- if(NULL == ld)
- {
- throw MakeStringException(-1, "ldap_init(%s,%d) error %s", host, port, ldap_err2string(LdapGetLastError()));
- }
- #else
- StringBuffer uri("ldap://");
- uri.appendf("%s:%d", host, port);
- int rc = LDAP_INIT(&ld, uri.str());
- if(rc != LDAP_SUCCESS)
- {
- throw MakeStringException(-1, "ldap_initialize(%s,%d) error %s", host, port, ldap_err2string(rc));
- }
- #endif
- }
- return ld;
- }
- int LdapUtils::LdapSimpleBind(LDAP* ld, char* userdn, char* password)
- {
- #ifndef _WIN32
- TIMEVAL timeout = {LDAPTIMEOUT, 0};
- ldap_set_option(ld, LDAP_OPT_TIMEOUT, &timeout);
- ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &timeout);
- #endif
- int srtn = ldap_bind_s(ld, userdn, password, LDAP_AUTH_SIMPLE);
- #ifndef _WIN32
- // secure ldap tls might overwrite SIGPIPE handler
- signal(SIGPIPE, SIG_IGN);
- #endif
- return srtn;
- }
- // userdn is required for ldap_simple_bind_s, not really necessary for ldap_bind_s.
- int LdapUtils::LdapBind(LDAP* ld, const char* domain, const char* username, const char* password, const char* userdn, LdapServerType server_type, const char* method)
- {
- bool binddone = false;
- int rc = LDAP_SUCCESS;
- // By default, use kerberos authentication
- if((method == NULL) || (strlen(method) == 0) || (stricmp(method, "kerberos") == 0))
- {
- #ifdef _WIN32
- if(server_type == ACTIVE_DIRECTORY)
- {
- if(username != NULL)
- {
- SEC_WINNT_AUTH_IDENTITY secIdent;
- secIdent.User = (unsigned char*)username;
- secIdent.UserLength = strlen(username);
- secIdent.Password = (unsigned char*)password;
- secIdent.PasswordLength = strlen(password);
- // Somehow, setting the domain makes it slower
- secIdent.Domain = (unsigned char*)domain;
- secIdent.DomainLength = strlen(domain);
- secIdent.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
- int rc = ldap_bind_s(ld, (char*)userdn, (char*)&secIdent, LDAP_AUTH_NEGOTIATE);
- if(rc != LDAP_SUCCESS)
- {
- DBGLOG("ldap_bind_s for user %s failed with %d - %s.", username, rc, ldap_err2string(rc));
- return rc;
- }
- }
- else
- {
- int rc = ldap_bind_s(ld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
- if(rc != LDAP_SUCCESS)
- {
- DBGLOG("User Authentication Failed - ldap_bind_s for current user failed with %d - %s.", rc, ldap_err2string(rc));
- return rc;
- }
- }
- binddone = true;
- }
- #endif
- }
- if(!binddone)
- {
- if(userdn == NULL)
- {
- DBGLOG("userdn can't be NULL in order to bind to ldap server.");
- return LDAP_INVALID_CREDENTIALS;
- }
- int rc = LdapSimpleBind(ld, (char*)userdn, (char*)password);
- if (rc != LDAP_SUCCESS && server_type == OPEN_LDAP && strchr(userdn,','))
- { //Fedora389 is happier without the domain component specified
- StringBuffer cn(userdn);
- cn.replace(',',(char)NULL);
- if (cn.length())//disallow call if no cn
- rc = LdapSimpleBind(ld, (char*)cn.str(), (char*)password);
- }
- if (rc != LDAP_SUCCESS )
- {
- // For Active Directory, try binding with NT format username
- if(server_type == ACTIVE_DIRECTORY)
- {
- StringBuffer logonname;
- logonname.append(domain).append("\\").append(username);
- rc = LdapSimpleBind(ld, (char*)logonname.str(), (char*)password);
- if(rc != LDAP_SUCCESS)
- {
- #ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
- char *msg=NULL;
- ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&msg);
- DBGLOG("LDAP bind error for user %s with %d - %s. %s", logonname.str(), rc, ldap_err2string(rc), msg&&*msg?msg:"");
- ldap_memfree(msg);
- #else
- DBGLOG("LDAP bind error for user %s with 0x%" I64F "x - %s", username, (unsigned __int64) rc, ldap_err2string(rc));
- #endif
- return rc;
- }
- }
- else
- {
- DBGLOG("LDAP bind error for user %s with 0x%" I64F "x - %s", username, (unsigned __int64) rc, ldap_err2string(rc));
- return rc;
- }
- }
- }
- return rc;
- }
- int LdapUtils::getServerInfo(const char* ldapserver, int ldapport, StringBuffer& domainDN, LdapServerType& stype, const char* domainname)
- {
- LdapServerType deducedSType = LDAPSERVER_UNKNOWN;
- LDAP* ld = LdapInit("ldap", ldapserver, ldapport, 636);
- if(ld == NULL)
- {
- ERRLOG("ldap init error");
- return false;
- }
- int err = LdapSimpleBind(ld, NULL, NULL);
- if(err != LDAP_SUCCESS)
- {
- DBGLOG("ldap anonymous bind error (%d) - %s", err, ldap_err2string(err));
- // for new versions of openldap, version 2.2.*
- if(err == LDAP_PROTOCOL_ERROR)
- DBGLOG("If you're trying to connect to an OpenLdap server, make sure you have \"allow bind_v2\" enabled in slapd.conf");
- return err;
- }
- LDAPMessage* msg = NULL;
- char* attrs[] = {"namingContexts", NULL};
- TIMEVAL timeOut = {LDAPTIMEOUT,0};
- err = ldap_search_ext_s(ld, NULL, LDAP_SCOPE_BASE, "objectClass=*", attrs, false, NULL, NULL, &timeOut, LDAP_NO_LIMIT, &msg);
- if(err != LDAP_SUCCESS)
- {
- DBGLOG("ldap_search_ext_s error: %s", ldap_err2string( err ));
- if (msg)
- ldap_msgfree(msg);
- return err;
- }
- LDAPMessage* entry = ldap_first_entry(ld, msg);
- if(entry != NULL)
- {
- CLDAPGetValuesLenWrapper vals(ld, entry, "namingContexts");
- if(vals.hasValues())
- {
- int i = 0;
- const char* curdn;
- StringBuffer onedn;
- while((curdn = vals.queryCharValue(i)) != NULL)
- {
- if(*curdn != '\0' && (strncmp(curdn, "dc=", 3) == 0 || strncmp(curdn, "DC=", 3) == 0) && strstr(curdn,"DC=ForestDnsZones")==0 && strstr(curdn,"DC=DomainDnsZones")==0 )
- {
- if(domainDN.length() == 0)
- {
- StringBuffer curdomain;
- getName(curdn, curdomain);
- if(onedn.length() == 0)
- {
- DBGLOG("Queried '%s', selected basedn '%s'",curdn, curdomain.str());
- onedn.append(curdomain.str());
- }
- else
- DBGLOG("Ignoring %s", curdn);
- if(!domainname || !*domainname || stricmp(curdomain.str(), domainname) == 0)
- domainDN.append(curdn);
- }
- }
- else if(*curdn != '\0' && strcmp(curdn, "o=NetscapeRoot") == 0)
- {
- PROGLOG("Deduced LDAP Server Type 'iPlanet'");
- deducedSType = IPLANET;
- }
- i++;
- }
- if(domainDN.length() == 0)
- domainDN.append(onedn.str());
- if (deducedSType == LDAPSERVER_UNKNOWN)
- {
- if(i <= 1)
- {
- PROGLOG("Deduced LDAP Server Type 'OpenLDAP'");
- deducedSType = OPEN_LDAP;
- }
- else
- {
- PROGLOG("Deduced LDAP Server Type 'Active Directory'");
- deducedSType = ACTIVE_DIRECTORY;
- }
- }
- }
- }
- ldap_msgfree(msg);
- LDAP_UNBIND(ld);
- if (stype == LDAPSERVER_UNKNOWN)
- stype = deducedSType;
- else if (deducedSType != stype)
- WARNLOG("Ignoring deduced LDAP Server Type, does not match config LDAPServerType");
- return err;
- }
- void LdapUtils::bin2str(MemoryBuffer& from, StringBuffer& to)
- {
- const char* frombuf = from.toByteArray();
- char tmp[3];
- for(unsigned i = 0; i < from.length(); i++)
- {
- unsigned char c = frombuf[i];
- sprintf(tmp, "%02X", c);
- tmp[2] = 0;
- to.append("\\").append(tmp);
- }
- }
- void LdapUtils::normalizeDn(const char* dn, const char* basedn, StringBuffer& dnbuf)
- {
- dnbuf.clear();
- cleanupDn(dn, dnbuf);
- if(!containsBasedn(dnbuf.str()))
- dnbuf.append(",").append(basedn);
- }
- bool LdapUtils::containsBasedn(const char* str)
- {
- if(str == NULL || str[0] == '\0')
- return false;
- else
- return (strstr(str, "dc=") != NULL);
- }
- void LdapUtils::cleanupDn(const char* dn, StringBuffer& dnbuf)
- {
- if(dn == NULL || dn[0] == '\0')
- return;
- dnbuf.append(dn);
- dnbuf.toLowerCase();
- }
- bool LdapUtils::getDcName(const char* domain, StringBuffer& dc)
- {
- bool ret = false;
- #ifdef _WIN32
- PDOMAIN_CONTROLLER_INFO psInfo = NULL;
- DWORD dwErr = DsGetDcName(NULL, domain, NULL, NULL, DS_FORCE_REDISCOVERY | DS_DIRECTORY_SERVICE_REQUIRED, &psInfo);
- if( dwErr == NO_ERROR)
- {
- const char* dcname = psInfo->DomainControllerName;
- if(dcname != NULL)
- {
- while(*dcname == '\\')
- dcname++;
- dc.append(dcname);
- ret = true;
- }
- NetApiBufferFree(psInfo);
- }
- else
- {
- DBGLOG("Error getting domain controller, error = %d", dwErr);
- ret = false;
- }
- #endif
- return ret;
- }
- void LdapUtils::getName(const char* dn, StringBuffer& name)
- {
- const char* bptr = dn;
- while(*bptr != '\0' && *bptr != '=')
- bptr++;
- if(*bptr == '\0')
- {
- name.append(dn);
- return;
- }
- else
- bptr++;
- const char* colon = strstr(bptr, ",");
- if(colon == NULL)
- name.append(bptr);
- else
- name.append(colon - bptr, bptr);
- }
|