ldapconnection.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. /*##############################################################################
  2. Copyright (C) 2011 HPCC Systems.
  3. All rights reserved. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ############################################################################## */
  14. #ifndef __LDAPCONNECTION_HPP
  15. #define __LDAPCONNECTION_HPP
  16. #include <stdlib.h>
  17. #include "thirdparty.h"
  18. #include "jiface.hpp"
  19. #include "jliball.hpp"
  20. #include "seclib.hpp"
  21. #ifdef _WIN32
  22. #include <windows.h>
  23. #include <winldap.h>
  24. #include <winber.h>
  25. #include <rpc.h>
  26. #include <rpcdce.h>
  27. #include "dsgetdc.h"
  28. #include <lm.h>
  29. #else
  30. #define LDAP_DEPRECATED 1
  31. #include <stdio.h>
  32. #include <string.h>
  33. #include <ldap_cdefs.h>
  34. #include <ldap.h>
  35. #endif
  36. #ifdef _WIN32
  37. typedef struct l_timeval TIMEVAL;
  38. #else
  39. typedef struct timeval TIMEVAL;
  40. #endif
  41. #define LDAPTIMEOUT 60 //20 second connection/search timeout
  42. #define DEFAULT_LDAP_POOL_SIZE 10
  43. // 1 for ActiveDirectory, 2 for iPlanet, 3 for openLdap
  44. enum LdapServerType
  45. {
  46. LDAPSERVER_UNKNOWN = 0,
  47. ACTIVE_DIRECTORY = 1,
  48. IPLANET = 2,
  49. OPEN_LDAP = 3
  50. };
  51. enum ACT_TYPE
  52. {
  53. USER_ACT = 0,
  54. GROUP_ACT = 1
  55. };
  56. interface IPermissionProcessor;
  57. interface ILdapConnection : extends IInterface
  58. {
  59. virtual LDAP* getLd() = 0;
  60. };
  61. interface ILdapConnectionPool : extends IInterface
  62. {
  63. virtual ILdapConnection* getConnection() = 0;
  64. virtual ILdapConnection* getSSLConnection() = 0;
  65. };
  66. interface ILdapConfig : extends IInterface
  67. {
  68. virtual LdapServerType getServerType() = 0;
  69. virtual StringBuffer& getLdapHost(StringBuffer& hostbuf) = 0;
  70. virtual void markDown(const char* ldaphost) = 0;
  71. virtual int getLdapPort() = 0;
  72. virtual int getLdapSecurePort() = 0;
  73. virtual const char* getProtocol() = 0;
  74. virtual const char* getBasedn() = 0;
  75. virtual const char* getDomain() = 0;
  76. virtual const char* getAuthMethod() = 0;
  77. virtual const char* getUserBasedn() = 0;
  78. virtual const char* getGroupBasedn() = 0;
  79. virtual const char* getResourceBasedn(SecResourceType rtype) = 0;
  80. virtual const char* getTemplateName() = 0;
  81. virtual const char* getSysUser() = 0;
  82. virtual const char* getSysUserDn() = 0;
  83. virtual const char* getSysUserCommonName() = 0;
  84. virtual const char* getSysUserPassword() = 0;
  85. virtual const char* getSysUserBasedn() = 0;
  86. virtual bool sysuserSpecified() = 0;
  87. virtual int getMaxConnections() = 0;
  88. virtual void setResourceBasedn(const char* rbasedn, SecResourceType rtype = RT_DEFAULT) = 0;
  89. };
  90. class CPermission : public CInterface, implements IInterface
  91. {
  92. StringBuffer m_account_name;
  93. ACT_TYPE m_account_type;
  94. int m_allows;
  95. int m_denies;
  96. public:
  97. IMPLEMENT_IINTERFACE
  98. CPermission(const char* account_name, ACT_TYPE account_type, int allows, int denies)
  99. {
  100. m_account_name.append(account_name);
  101. m_account_type = account_type;
  102. m_allows = allows;
  103. m_denies = denies;
  104. }
  105. const char* getAccount_name() {return m_account_name.str();}
  106. ACT_TYPE getAccount_type() {return m_account_type;}
  107. int getAllows() {return m_allows;}
  108. int getDenies() {return m_denies;}
  109. void setAllows(int allows) { m_allows = allows;}
  110. void setDenies(int denies) { m_denies = denies;}
  111. };
  112. class CPermissionAction : public CInterface, implements IInterface
  113. {
  114. public:
  115. StringBuffer m_action;
  116. StringBuffer m_basedn;
  117. SecResourceType m_rtype;
  118. StringBuffer m_rname;
  119. StringBuffer m_account_name;
  120. ACT_TYPE m_account_type;
  121. int m_allows;
  122. int m_denies;
  123. IMPLEMENT_IINTERFACE
  124. };
  125. interface ILdapClient : extends IInterface
  126. {
  127. virtual void init(IPermissionProcessor* pp) = 0;
  128. virtual LdapServerType getServerType() = 0;
  129. virtual bool authenticate(ISecUser& user) = 0;
  130. virtual bool authorize(SecResourceType rtype, ISecUser&, IArrayOf<ISecResource>& resources) = 0;
  131. virtual bool addResources(SecResourceType rtype, ISecUser& user, IArrayOf<ISecResource>& resources, SecPermissionType ptype, const char* basedn) = 0;
  132. virtual bool addUser(ISecUser& user) = 0;
  133. virtual void getGroups(const char *user, StringArray& groups) = 0;
  134. virtual bool getUserInfo(ISecUser& user, const char* infotype = NULL) = 0;
  135. virtual ISecUser* lookupUser(unsigned uid) = 0;
  136. virtual bool lookupAccount(MemoryBuffer& sidbuf, StringBuffer& account_name, ACT_TYPE& act_type) = 0;
  137. virtual void lookupSid(const char* act_name, MemoryBuffer& act_sid, ACT_TYPE act_type) = 0;
  138. virtual void setPermissionProcessor(IPermissionProcessor* pp) = 0;
  139. virtual bool retrieveUsers(IUserArray& users) = 0;
  140. virtual bool retrieveUsers(const char* searchstr, IUserArray& users) = 0;
  141. virtual void getAllGroups(StringArray & groups) = 0;
  142. virtual void setResourceBasedn(const char* rbasedn, SecResourceType rtype = RT_DEFAULT) = 0;
  143. virtual ILdapConfig* getLdapConfig() = 0;
  144. virtual bool userInGroup(const char* userdn, const char* groupdn) = 0;
  145. virtual bool updateUser(ISecUser& user, const char* newPassword) = 0;
  146. virtual bool updateUser(const char* type, ISecUser& user) = 0;
  147. virtual bool updateUser(const char* username, const char* newPassword) = 0;
  148. virtual bool getResources(SecResourceType rtype, const char * basedn, const char* prefix, IArrayOf<ISecResource>& resources) = 0;
  149. virtual bool getResourcesEx(SecResourceType rtype, const char * basedn, const char* prefix, const char* searchstr, IArrayOf<ISecResource>& resources) = 0;
  150. virtual bool getPermissionsArray(const char* basedn, SecResourceType rtype, const char* name, IArrayOf<CPermission>& permissions) = 0;
  151. virtual bool changePermission(CPermissionAction& action) = 0;
  152. virtual void changeUserGroup(const char* action, const char* username, const char* groupname) = 0;
  153. virtual bool deleteUser(ISecUser* user) = 0;
  154. virtual void addGroup(const char* groupname) = 0;
  155. virtual void deleteGroup(const char* groupname) = 0;
  156. virtual void getGroupMembers(const char* groupname, StringArray & users) = 0;
  157. virtual void deleteResource(SecResourceType rtype, const char* name, const char* basedn) = 0;
  158. virtual void renameResource(SecResourceType rtype, const char* oldname, const char* newname, const char* basedn) = 0;
  159. virtual void copyResource(SecResourceType rtype, const char* oldname, const char* newname, const char* basedn) = 0;
  160. virtual void normalizeDn(const char* dn, StringBuffer& ndn) = 0;
  161. virtual bool isSuperUser(ISecUser* user) = 0;
  162. virtual int countEntries(const char* basedn, const char* objectClass, int limit) = 0;
  163. virtual int countUsers(const char* searchstr, int limit) = 0;
  164. virtual int countResources(const char* basedn, const char* searchstr, int limit) = 0;
  165. virtual ILdapConfig* queryConfig() = 0;
  166. virtual const char* getPasswordStorageScheme() = 0;
  167. };
  168. ILdapClient* createLdapClient(IPropertyTree* cfg);
  169. #ifdef _WIN32
  170. bool verifyServerCert(LDAP* ld, PCCERT_CONTEXT pServerCert);
  171. #endif
  172. class LdapUtils
  173. {
  174. public:
  175. static LDAP* LdapInit(const char* protocol, const char* host, int port, int secure_port)
  176. {
  177. LDAP* ld = NULL;
  178. if(stricmp(protocol, "ldaps") == 0)
  179. {
  180. #ifdef _WIN32
  181. ld = ldap_sslinit((char*)host, secure_port, 1);
  182. if (ld == NULL )
  183. throw MakeStringException(-1, "ldap_sslinit error" );
  184. int rc = 0;
  185. unsigned long version = LDAP_VERSION3;
  186. long lv = 0;
  187. rc = ldap_set_option(ld,
  188. LDAP_OPT_PROTOCOL_VERSION,
  189. (void*)&version);
  190. if (rc != LDAP_SUCCESS)
  191. throw MakeStringException(-1, "ldap_set_option error - %s", ldap_err2string(rc));
  192. rc = ldap_get_option(ld,LDAP_OPT_SSL,(void*)&lv);
  193. if (rc != LDAP_SUCCESS)
  194. throw MakeStringException(-1, "ldap_get_option error - %s", ldap_err2string(rc));
  195. // If SSL is not enabled, enable it.
  196. if ((void*)lv != LDAP_OPT_ON)
  197. {
  198. rc = ldap_set_option(ld, LDAP_OPT_SSL, LDAP_OPT_ON);
  199. if (rc != LDAP_SUCCESS)
  200. throw MakeStringException(-1, "ldap_set_option error - %s", ldap_err2string(rc));
  201. }
  202. ldap_set_option(ld, LDAP_OPT_SERVER_CERTIFICATE, verifyServerCert);
  203. #else
  204. // Initialize an LDAP session for TLS/SSL
  205. #ifndef HAVE_TLS
  206. //throw MakeStringException(-1, "openldap client library libldap not compiled with TLS support");
  207. #endif
  208. StringBuffer uri("ldaps://");
  209. uri.appendf("%s:%d", host, secure_port);
  210. DBGLOG("connecting to %s", uri.str());
  211. int rc = ldap_initialize(&ld, uri.str());
  212. if(rc != LDAP_SUCCESS)
  213. {
  214. DBGLOG("ldap_initialize error %s", ldap_err2string(rc));
  215. throw MakeStringException(-1, "ldap_initialize error %s", ldap_err2string(rc));
  216. }
  217. int reqcert = LDAP_OPT_X_TLS_NEVER;
  218. ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &reqcert);
  219. #endif
  220. }
  221. else
  222. {
  223. // Initialize an LDAP session
  224. if ((ld = ldap_init( (char*)host, port )) == NULL)
  225. {
  226. throw MakeStringException(-1, "ldap_init error");
  227. }
  228. }
  229. return ld;
  230. }
  231. static int LdapSimpleBind(LDAP* ld, char* userdn, char* password)
  232. {
  233. #ifndef _WIN32
  234. TIMEVAL timeout = {LDAPTIMEOUT, 0};
  235. ldap_set_option(ld, LDAP_OPT_TIMEOUT, &timeout);
  236. ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &timeout);
  237. #endif
  238. return ldap_simple_bind_s(ld, userdn, password);
  239. /*
  240. //TODO: bugs need to be fixed: (1) in ldap_result, is "1" actually meant for LDAP_MESSAGE_ONE? (2) should call ldap_msgfree on result
  241. int final_rc = LDAP_SUCCESS;
  242. int msgid = ldap_simple_bind(ld, userdn, password);
  243. if(msgid < 0)
  244. {
  245. #ifndef _WIN32
  246. final_rc = ldap_get_lderrno(ld, NULL, NULL);
  247. #else
  248. final_rc = LDAP_OTHER;
  249. #endif
  250. }
  251. else
  252. {
  253. LDAPMessage* result = NULL;
  254. TIMEVAL timeOut = {LDAPTIMEOUT,0};
  255. int rc = ldap_result(ld, msgid, 1, &timeOut, &result);
  256. if(rc < 0)
  257. {
  258. #ifndef _WIN32
  259. final_rc = ldap_get_lderrno(ld, NULL, NULL);
  260. #else
  261. final_rc = LDAP_OTHER;
  262. #endif
  263. }
  264. else if(rc == 0)
  265. {
  266. final_rc = LDAP_TIMEOUT;
  267. }
  268. else
  269. {
  270. final_rc = ldap_result2error(ld, result, 1);
  271. }
  272. }
  273. return final_rc;
  274. */
  275. }
  276. // userdn is required for ldap_simple_bind_s, not really necessary for ldap_bind_s.
  277. static int LdapBind(LDAP* ld, const char* domain, const char* username, const char* password, const char* userdn, LdapServerType server_type, const char* method="kerboros")
  278. {
  279. bool binddone = false;
  280. int rc = LDAP_SUCCESS;
  281. // By default, use kerberos authentication
  282. if((method == NULL) || (strlen(method) == 0) || (stricmp(method, "kerberos") == 0))
  283. {
  284. #ifdef _WIN32
  285. if(server_type == ACTIVE_DIRECTORY)
  286. {
  287. if(username != NULL)
  288. {
  289. SEC_WINNT_AUTH_IDENTITY secIdent;
  290. secIdent.User = (unsigned char*)username;
  291. secIdent.UserLength = strlen(username);
  292. secIdent.Password = (unsigned char*)password;
  293. secIdent.PasswordLength = strlen(password);
  294. // Somehow, setting the domain makes it slower
  295. secIdent.Domain = (unsigned char*)domain;
  296. secIdent.DomainLength = strlen(domain);
  297. secIdent.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
  298. int rc = ldap_bind_s(ld, (char*)userdn, (char*)&secIdent, LDAP_AUTH_NEGOTIATE);
  299. if(rc != LDAP_SUCCESS)
  300. {
  301. DBGLOG("ldap_bind_s for user %s failed with %d - %s.", username, rc, ldap_err2string(rc));
  302. return rc;
  303. }
  304. }
  305. else
  306. {
  307. int rc = ldap_bind_s(ld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  308. if(rc != LDAP_SUCCESS)
  309. {
  310. DBGLOG("User Authentication Failed - ldap_bind_s for current user failed with %d - %s.", rc, ldap_err2string(rc));
  311. return rc;
  312. }
  313. }
  314. binddone = true;
  315. }
  316. #endif
  317. }
  318. if(!binddone)
  319. {
  320. if(userdn == NULL)
  321. {
  322. DBGLOG("userdn can't be NULL in order to bind to ldap server.");
  323. return LDAP_INVALID_CREDENTIALS;
  324. }
  325. int rc = LdapSimpleBind(ld, (char*)userdn, (char*)password);
  326. if (rc != LDAP_SUCCESS )
  327. {
  328. // For Active Directory, try binding with NT format username
  329. if(server_type == ACTIVE_DIRECTORY)
  330. {
  331. StringBuffer logonname;
  332. logonname.append(domain).append("\\").append(username);
  333. rc = LdapSimpleBind(ld, (char*)logonname.str(), (char*)password);
  334. if(rc != LDAP_SUCCESS)
  335. {
  336. #ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
  337. char *msg=NULL;
  338. ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&msg);
  339. DBGLOG("LDAP bind error for user %s with %d - %s. %s", logonname.str(), rc, ldap_err2string(rc), msg&&*msg?msg:"");
  340. ldap_memfree(msg);
  341. #else
  342. DBGLOG("LDAP bind error for user %s with 0x%"I64F"x - %s", username, (unsigned __int64) rc, ldap_err2string(rc));
  343. #endif
  344. return rc;
  345. }
  346. }
  347. else
  348. {
  349. DBGLOG("LDAP bind error for user %s with 0x%"I64F"x - %s", username, (unsigned __int64) rc, ldap_err2string(rc));
  350. return rc;
  351. }
  352. }
  353. }
  354. return rc;
  355. }
  356. static void bin2str(MemoryBuffer& from, StringBuffer& to)
  357. {
  358. const char* frombuf = from.toByteArray();
  359. char tmp[3];
  360. for(unsigned i = 0; i < from.length(); i++)
  361. {
  362. unsigned char c = frombuf[i];
  363. sprintf(tmp, "%02X", c);
  364. tmp[2] = 0;
  365. to.append("\\").append(tmp);
  366. }
  367. }
  368. static int getServerInfo(const char* ldapserver, int ldapport, StringBuffer& domainDN, LdapServerType& stype, const char* domainname);
  369. static void normalizeDn(const char* dn, const char* basedn, StringBuffer& dnbuf)
  370. {
  371. dnbuf.clear();
  372. cleanupDn(dn, dnbuf);
  373. if(!containsBasedn(dnbuf.str()))
  374. dnbuf.append(",").append(basedn);
  375. }
  376. static bool containsBasedn(const char* str)
  377. {
  378. if(str == NULL || str[0] == '\0')
  379. return false;
  380. else
  381. return (strstr(str, "dc=") != NULL);
  382. }
  383. static void cleanupDn(const char* dn, StringBuffer& dnbuf)
  384. {
  385. if(dn == NULL || dn[0] == '\0')
  386. return;
  387. const char* ptr = dn;
  388. while(ptr && *ptr != '\0')
  389. {
  390. char c = *ptr;
  391. if(!isspace(c))
  392. {
  393. c = tolower(c);
  394. dnbuf.append(c);
  395. }
  396. ptr++;
  397. }
  398. }
  399. static bool getDcName(const char* domain, StringBuffer& dc)
  400. {
  401. bool ret = false;
  402. #ifdef _WIN32
  403. PDOMAIN_CONTROLLER_INFO psInfo = NULL;
  404. DWORD dwErr = DsGetDcName(NULL, domain, NULL, NULL, DS_FORCE_REDISCOVERY | DS_DIRECTORY_SERVICE_REQUIRED, &psInfo);
  405. if( dwErr == NO_ERROR)
  406. {
  407. const char* dcname = psInfo->DomainControllerName;
  408. if(dcname != NULL)
  409. {
  410. while(*dcname == '\\')
  411. dcname++;
  412. dc.append(dcname);
  413. ret = true;
  414. }
  415. NetApiBufferFree(psInfo);
  416. }
  417. else
  418. {
  419. DBGLOG("Error getting domain controller, error = %d", dwErr);
  420. ret = false;
  421. }
  422. #endif
  423. return ret;
  424. }
  425. static void getName(const char* dn, StringBuffer& name)
  426. {
  427. const char* bptr = dn;
  428. while(*bptr != '\0' && *bptr != '=')
  429. bptr++;
  430. if(*bptr == '\0')
  431. {
  432. name.append(dn);
  433. return;
  434. }
  435. else
  436. bptr++;
  437. const char* colon = strstr(bptr, ",");
  438. if(colon == NULL)
  439. name.append(bptr);
  440. else
  441. name.append(colon - bptr, bptr);
  442. }
  443. };
  444. #endif