LDAPUserPlugin.cpp 112 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/zcdefs.h>
  18. #include <kopano/platform.h>
  19. #include <exception>
  20. #include <iostream>
  21. #include <memory>
  22. #include <string>
  23. #include <stdexcept>
  24. #include <algorithm>
  25. #include <map>
  26. #include <mutex>
  27. #include <set>
  28. #include <utility>
  29. #include <list>
  30. #include <cerrno>
  31. #include <cassert>
  32. #include <sys/time.h> /* gettimeofday */
  33. #include <kopano/EMSAbTag.h>
  34. #include <kopano/ECConfig.h>
  35. #include <kopano/ECLogger.h>
  36. #include <kopano/ECPluginSharedData.h>
  37. #include <kopano/lockhelper.hpp>
  38. #include <kopano/memory.hpp>
  39. #include "ECStatsCollector.h"
  40. #include <kopano/stringutil.h>
  41. using namespace std;
  42. using namespace KC;
  43. #include "LDAPUserPlugin.h"
  44. #include "ldappasswords.h"
  45. #include <kopano/ecversion.h>
  46. #ifndef PROP_ID
  47. // from mapidefs.h
  48. #define PROP_ID(ulPropTag) (((ULONG)(ulPropTag))>>16)
  49. #endif
  50. extern "C" {
  51. UserPlugin *getUserPluginInstance(std::mutex &pluginlock,
  52. ECPluginSharedData *shareddata)
  53. {
  54. return new LDAPUserPlugin(pluginlock, shareddata);
  55. }
  56. void deleteUserPluginInstance(UserPlugin *up) {
  57. delete up;
  58. }
  59. int getUserPluginVersion() {
  60. return PROJECT_VERSION_REVISION;
  61. }
  62. }
  63. namespace KC {
  64. class ldap_delete {
  65. public:
  66. void operator()(void *x) { ldap_memfree(x); }
  67. void operator()(BerElement *x) { ber_free(x, 0); }
  68. void operator()(LDAPMessage *x) { ldap_msgfree(x); }
  69. void operator()(LDAPControl *x) { ldap_control_free(x); }
  70. void operator()(LDAPControl **x) { ldap_controls_free(x); }
  71. void operator()(struct berval **x) { ldap_value_free_len(x); }
  72. };
  73. typedef KCHL::memory_ptr<char, ldap_delete> auto_free_ldap_attribute;
  74. typedef KCHL::memory_ptr<BerElement, ldap_delete> auto_free_ldap_berelement;
  75. typedef KCHL::memory_ptr<LDAPMessage, ldap_delete> auto_free_ldap_message;
  76. typedef KCHL::memory_ptr<LDAPControl, ldap_delete> auto_free_ldap_control;
  77. typedef KCHL::memory_ptr<LDAPControl *, ldap_delete> auto_free_ldap_controls;
  78. typedef KCHL::memory_ptr<struct berval *, ldap_delete> auto_free_ldap_berval;
  79. #define LDAP_DATA_TYPE_DN "dn" // data in attribute like cn=piet,cn=user,dc=localhost,dc=com
  80. #define LDAP_DATA_TYPE_BINARY "binary"
  81. #define FETCH_ATTR_VALS 0
  82. #define DONT_FETCH_ATTR_VALS 1
  83. #if HAVE_LDAP_CREATE_PAGE_CONTROL
  84. #define FOREACH_PAGING_SEARCH(basedn, scope, filter, attrs, flags, res) \
  85. { \
  86. bool morePages = false; \
  87. struct berval sCookie = {0, NULL}; \
  88. int ldap_page_size = strtoul(m_config->GetSetting("ldap_page_size"), NULL, 10); \
  89. LDAPControl *serverControls[2] = { NULL, NULL }; \
  90. auto_free_ldap_control pageControl; \
  91. auto_free_ldap_controls returnedControls; \
  92. int rc; \
  93. \
  94. ldap_page_size = (ldap_page_size == 0) ? 1000 : ldap_page_size; \
  95. do { \
  96. if (m_ldap == NULL) \
  97. /* this either returns a connection or throws an exception */ \
  98. m_ldap = ConnectLDAP(m_config->GetSetting("ldap_bind_user"), m_config->GetSetting("ldap_bind_passwd")); \
  99. /* set critical to 'F' to not force paging? @todo find an ldap server without support. */ \
  100. rc = ldap_create_page_control(m_ldap, ldap_page_size, &sCookie, 0, &~pageControl); \
  101. if (rc != LDAP_SUCCESS) { \
  102. /* 'F' ? */ \
  103. throw ldap_error(string("ldap_create_page_control: ") + ldap_err2string(rc), rc); \
  104. } \
  105. serverControls[0] = pageControl; \
  106. \
  107. /* search like normal, throws on error */ \
  108. my_ldap_search_s(basedn, scope, filter, attrs, flags, &~res, serverControls); \
  109. \
  110. /* get paged result */ \
  111. rc = ldap_parse_result(m_ldap, res, nullptr, nullptr, nullptr, nullptr, &~returnedControls, 0); \
  112. if (rc != LDAP_SUCCESS) { \
  113. /* @todo, whoops do we really need to unbind? */ \
  114. /* ldap_unbind(m_ldap); */ \
  115. /* m_ldap = NULL; */ \
  116. throw ldap_error(string("ldap_parse_result: ") + ldap_err2string(rc), rc); \
  117. } \
  118. \
  119. if (sCookie.bv_val != NULL) { \
  120. ber_memfree(sCookie.bv_val); \
  121. sCookie.bv_val = NULL; \
  122. sCookie.bv_len = 0; \
  123. } \
  124. if (!!returnedControls) { \
  125. rc = ldap_parse_pageresponse_control(m_ldap, returnedControls[0], NULL, &sCookie); \
  126. if (rc != LDAP_SUCCESS) { \
  127. throw ldap_error(string("ldap_parse_pageresponse_control: ") + ldap_err2string(rc), rc); \
  128. } \
  129. morePages = sCookie.bv_len > 0; \
  130. } else { \
  131. morePages = false; \
  132. }
  133. #define END_FOREACH_LDAP_PAGING \
  134. } \
  135. while (morePages == true); \
  136. \
  137. if (sCookie.bv_val != NULL) { \
  138. ber_memfree(sCookie.bv_val); \
  139. sCookie.bv_val = NULL; \
  140. sCookie.bv_len = 0; \
  141. } \
  142. }
  143. #else
  144. // non paged support, revert to normal search
  145. #define FOREACH_PAGING_SEARCH(basedn, scope, filter, attrs, flags, res) \
  146. { \
  147. my_ldap_search_s(basedn, scope, filter, attrs, flags, &~res);
  148. #define END_FOREACH_LDAP_PAGING \
  149. }
  150. #endif
  151. #define FOREACH_ENTRY(res) \
  152. { \
  153. LDAPMessage *entry = NULL; \
  154. for (entry = ldap_first_entry(m_ldap, res); \
  155. entry != NULL; \
  156. entry = ldap_next_entry(m_ldap, entry)) {
  157. #define END_FOREACH_ENTRY \
  158. } \
  159. }
  160. #define FOREACH_ATTR(entry) \
  161. { \
  162. auto_free_ldap_attribute att; \
  163. auto_free_ldap_berelement ber; \
  164. for (att.reset(ldap_first_attribute(m_ldap, entry, &~ber)); \
  165. att != NULL; \
  166. att.reset(ldap_next_attribute(m_ldap, entry, ber))) {
  167. #define END_FOREACH_ATTR \
  168. }\
  169. }
  170. class attrArray _kc_final {
  171. public:
  172. attrArray(unsigned int ulSize) :
  173. ulAttrs(0), ulMaxAttrs(ulSize),
  174. lpAttrs(new const char *[ulMaxAttrs+1])
  175. {
  176. /* +1 for NULL entry */
  177. assert(lpAttrs != NULL);
  178. /* Make sure all entries are NULL */
  179. memset(lpAttrs.get(), 0, sizeof(const char *) * ulSize);
  180. }
  181. void add(const char *lpAttr)
  182. {
  183. assert(ulAttrs < ulMaxAttrs);
  184. lpAttrs[ulAttrs++] = lpAttr;
  185. lpAttrs[ulAttrs] = NULL;
  186. }
  187. void add(const char **lppAttr)
  188. {
  189. unsigned int i = 0;
  190. while (lppAttr[i])
  191. add(lppAttr[i++]);
  192. }
  193. bool empty(void) const
  194. {
  195. return lpAttrs[0] == NULL;
  196. }
  197. const char **get(void) const
  198. {
  199. return lpAttrs.get();
  200. }
  201. private:
  202. unsigned int ulAttrs;
  203. unsigned int ulMaxAttrs;
  204. std::unique_ptr<const char *[]> lpAttrs;
  205. };
  206. /* Make life a bit less boring */
  207. #define CONFIG_TO_ATTR(__attrs, __var, __config) \
  208. const char *__var = m_config->GetSetting(__config, "", NULL); \
  209. if (__var) \
  210. (__attrs)->add(__var);
  211. std::unique_ptr<LDAPCache> LDAPUserPlugin::m_lpCache(new LDAPCache());
  212. LDAPUserPlugin::LDAPUserPlugin(std::mutex &pluginlock,
  213. ECPluginSharedData *shareddata) :
  214. UserPlugin(pluginlock, shareddata), m_ldap(NULL), m_iconv(NULL),
  215. m_iconvrev(NULL), ldapServerIndex(0)
  216. {
  217. // LDAP Defaults
  218. const configsetting_t lpDefaults[] = {
  219. { "ldap_user_sendas_relation_attribute", "ldap_sendas_relation_attribute", CONFIGSETTING_ALIAS },
  220. { "ldap_user_sendas_attribute_type", "ldap_sendas_attribute_type", CONFIGSETTING_ALIAS },
  221. { "ldap_user_sendas_attribute", "ldap_sendas_attribute", CONFIGSETTING_ALIAS },
  222. { "ldap_host","localhost" },
  223. { "ldap_port","389" },
  224. { "ldap_uri","" },
  225. { "ldap_protocol", "ldap" },
  226. { "ldap_server_charset", "UTF-8" },
  227. { "ldap_bind_user","" },
  228. { "ldap_bind_passwd","", CONFIGSETTING_EXACT | CONFIGSETTING_RELOADABLE },
  229. { "ldap_search_base","", CONFIGSETTING_RELOADABLE },
  230. { "ldap_object_type_attribute", "objectClass", CONFIGSETTING_RELOADABLE },
  231. { "ldap_user_type_attribute_value", "", CONFIGSETTING_NONEMPTY | CONFIGSETTING_RELOADABLE },
  232. { "ldap_group_type_attribute_value", "", CONFIGSETTING_NONEMPTY | CONFIGSETTING_RELOADABLE },
  233. { "ldap_contact_type_attribute_value", "", CONFIGSETTING_RELOADABLE },
  234. { "ldap_company_type_attribute_value", "", static_cast<unsigned short>((m_bHosted ? CONFIGSETTING_NONEMPTY : 0) | CONFIGSETTING_RELOADABLE)},
  235. { "ldap_addresslist_type_attribute_value", "", CONFIGSETTING_RELOADABLE },
  236. { "ldap_dynamicgroup_type_attribute_value", "", CONFIGSETTING_RELOADABLE },
  237. { "ldap_server_type_attribute_value", "", static_cast<unsigned short>((m_bDistributed ? CONFIGSETTING_NONEMPTY : 0) | CONFIGSETTING_RELOADABLE)},
  238. { "ldap_user_search_base","", CONFIGSETTING_UNUSED },
  239. { "ldap_user_search_filter","", CONFIGSETTING_RELOADABLE },
  240. { "ldap_user_unique_attribute","cn", CONFIGSETTING_RELOADABLE },
  241. { "ldap_user_unique_attribute_type","text", CONFIGSETTING_RELOADABLE },
  242. { "ldap_user_unique_attribute_name","objectClass", CONFIGSETTING_RELOADABLE },
  243. { "ldap_group_search_base","", CONFIGSETTING_UNUSED },
  244. { "ldap_group_search_filter","", CONFIGSETTING_RELOADABLE },
  245. { "ldap_group_unique_attribute","cn", CONFIGSETTING_RELOADABLE },
  246. { "ldap_group_unique_attribute_type","text", CONFIGSETTING_RELOADABLE },
  247. { "ldap_group_security_attribute","kopanoSecurityGroup", CONFIGSETTING_RELOADABLE },
  248. { "ldap_group_security_attribute_type","boolean", CONFIGSETTING_RELOADABLE },
  249. { "ldap_company_search_base","", CONFIGSETTING_UNUSED },
  250. { "ldap_company_search_filter","", CONFIGSETTING_RELOADABLE },
  251. { "ldap_company_unique_attribute","ou", CONFIGSETTING_RELOADABLE },
  252. { "ldap_company_unique_attribute_type","text", CONFIGSETTING_RELOADABLE },
  253. { "ldap_fullname_attribute","cn", CONFIGSETTING_RELOADABLE },
  254. { "ldap_loginname_attribute","uid", CONFIGSETTING_RELOADABLE },
  255. { "ldap_password_attribute","userPassword", CONFIGSETTING_RELOADABLE },
  256. { "ldap_nonactive_attribute","kopanoSharedStoreOnly", CONFIGSETTING_RELOADABLE },
  257. { "ldap_resource_type_attribute","kopanoResourceType", CONFIGSETTING_RELOADABLE },
  258. { "ldap_resource_capacity_attribute","kopanoResourceCapacity", CONFIGSETTING_RELOADABLE },
  259. { "ldap_user_certificate_attribute", "userCertificate", CONFIGSETTING_RELOADABLE },
  260. { "ldap_emailaddress_attribute","mail", CONFIGSETTING_RELOADABLE },
  261. { "ldap_emailaliases_attribute","kopanoAliases", CONFIGSETTING_RELOADABLE },
  262. { "ldap_groupname_attribute","cn", CONFIGSETTING_RELOADABLE },
  263. { "ldap_groupmembers_attribute","member", CONFIGSETTING_RELOADABLE },
  264. { "ldap_groupmembers_attribute_type","text", CONFIGSETTING_RELOADABLE },
  265. { "ldap_companyname_attribute","ou", CONFIGSETTING_RELOADABLE },
  266. { "ldap_isadmin_attribute","kopanoAdmin", CONFIGSETTING_RELOADABLE },
  267. { "ldap_sendas_attribute","kopanoSendAsPrivilege", CONFIGSETTING_RELOADABLE },
  268. { "ldap_sendas_attribute_type","text", CONFIGSETTING_RELOADABLE },
  269. { "ldap_sendas_relation_attribute","", CONFIGSETTING_RELOADABLE },
  270. { "ldap_user_exchange_dn_attribute", "0x6788001E", CONFIGSETTING_ALIAS },
  271. { "ldap_user_telephone_attribute", "0x3A08001E", CONFIGSETTING_ALIAS },
  272. { "ldap_user_department_attribute", "0x3A23001E", CONFIGSETTING_ALIAS },
  273. { "ldap_user_location_attribute", "0x3A18001E", CONFIGSETTING_ALIAS},
  274. { "ldap_user_fax_attribute", "0x3A19001E", CONFIGSETTING_ALIAS },
  275. { "ldap_company_view_attribute","kopanoViewPrivilege", CONFIGSETTING_RELOADABLE },
  276. { "ldap_company_view_attribute_type","text", CONFIGSETTING_RELOADABLE },
  277. { "ldap_company_view_relation_attribute","", CONFIGSETTING_RELOADABLE },
  278. { "ldap_company_admin_attribute","kopanoAdminPrivilege", CONFIGSETTING_RELOADABLE },
  279. { "ldap_company_admin_attribute_type","text", CONFIGSETTING_RELOADABLE },
  280. { "ldap_company_admin_relation_attribute","", CONFIGSETTING_RELOADABLE },
  281. { "ldap_company_system_admin_attribute","kopanoSystemAdmin", CONFIGSETTING_RELOADABLE },
  282. { "ldap_company_system_admin_attribute_type","text", CONFIGSETTING_RELOADABLE },
  283. { "ldap_company_system_admin_relation_attribute","", CONFIGSETTING_RELOADABLE },
  284. { "ldap_authentication_method","bind", CONFIGSETTING_RELOADABLE },
  285. { "ldap_quotaoverride_attribute","kopanoQuotaOverride", CONFIGSETTING_RELOADABLE },
  286. { "ldap_warnquota_attribute","kopanoQuotaWarn", CONFIGSETTING_RELOADABLE },
  287. { "ldap_softquota_attribute","kopanoQuotaSoft", CONFIGSETTING_RELOADABLE },
  288. { "ldap_hardquota_attribute","kopanoQuotaHard", CONFIGSETTING_RELOADABLE },
  289. { "ldap_userdefault_quotaoverride_attribute","kopanoUserDefaultQuotaOverride", CONFIGSETTING_RELOADABLE },
  290. { "ldap_userdefault_warnquota_attribute","kopanoUserDefaultQuotaWarn", CONFIGSETTING_RELOADABLE },
  291. { "ldap_userdefault_softquota_attribute","kopanoUserDefaultQuotaSoft", CONFIGSETTING_RELOADABLE },
  292. { "ldap_userdefault_hardquota_attribute","kopanoUserDefaultQuotaHard", CONFIGSETTING_RELOADABLE },
  293. { "ldap_quota_userwarning_recipients_attribute","kopanoQuotaUserWarningRecipients", CONFIGSETTING_RELOADABLE },
  294. { "ldap_quota_userwarning_recipients_attribute_type","text", CONFIGSETTING_RELOADABLE },
  295. { "ldap_quota_userwarning_recipients_relation_attribute","", CONFIGSETTING_RELOADABLE },
  296. { "ldap_quota_companywarning_recipients_attribute","kopanoQuotaCompanyWarningRecipients", CONFIGSETTING_RELOADABLE },
  297. { "ldap_quota_companywarning_recipients_attribute_type","text", CONFIGSETTING_RELOADABLE },
  298. { "ldap_quota_companywarning_recipients_relation_attribute","", CONFIGSETTING_RELOADABLE },
  299. { "ldap_quota_multiplier","1", CONFIGSETTING_RELOADABLE },
  300. { "ldap_user_scope","", CONFIGSETTING_UNUSED },
  301. { "ldap_group_scope","", CONFIGSETTING_UNUSED },
  302. { "ldap_company_scope","", CONFIGSETTING_UNUSED },
  303. { "ldap_groupmembers_relation_attribute", "", CONFIGSETTING_RELOADABLE },
  304. { "ldap_last_modification_attribute", "modifyTimestamp", CONFIGSETTING_RELOADABLE },
  305. { "ldap_user_server_attribute", "kopanoUserServer", CONFIGSETTING_RELOADABLE },
  306. { "ldap_company_server_attribute", "kopanoCompanyServer", CONFIGSETTING_RELOADABLE },
  307. { "ldap_server_address_attribute", "", CONFIGSETTING_RELOADABLE },
  308. { "ldap_server_http_port_attribute", "kopanoHttpPort", CONFIGSETTING_RELOADABLE },
  309. { "ldap_server_ssl_port_attribute", "kopanoSslPort", CONFIGSETTING_RELOADABLE },
  310. { "ldap_server_file_path_attribute", "kopanoFilePath", CONFIGSETTING_RELOADABLE },
  311. { "ldap_server_proxy_path_attribute", "kopanoProxyURL", CONFIGSETTING_RELOADABLE },
  312. { "ldap_server_contains_public_attribute", "kopanoContainsPublic", CONFIGSETTING_RELOADABLE },
  313. { "ldap_server_scope", "", CONFIGSETTING_UNUSED },
  314. { "ldap_server_search_base", "", CONFIGSETTING_UNUSED },
  315. { "ldap_server_search_filter", "", CONFIGSETTING_RELOADABLE },
  316. { "ldap_server_unique_attribute", "cn", CONFIGSETTING_RELOADABLE },
  317. { "ldap_addresslist_search_base","", CONFIGSETTING_UNUSED },
  318. { "ldap_addresslist_scope","", CONFIGSETTING_UNUSED },
  319. { "ldap_addresslist_search_filter","", CONFIGSETTING_RELOADABLE },
  320. { "ldap_addresslist_unique_attribute","cn", CONFIGSETTING_RELOADABLE },
  321. { "ldap_addresslist_unique_attribute_type","text", CONFIGSETTING_RELOADABLE },
  322. { "ldap_addresslist_filter_attribute","kopanoFilter", CONFIGSETTING_RELOADABLE },
  323. { "ldap_addresslist_search_base_attribute","kopanoBase", CONFIGSETTING_RELOADABLE },
  324. { "ldap_addresslist_name_attribute","cn", CONFIGSETTING_RELOADABLE },
  325. { "ldap_dynamicgroup_search_filter","", CONFIGSETTING_RELOADABLE },
  326. { "ldap_dynamicgroup_unique_attribute","cn", CONFIGSETTING_RELOADABLE },
  327. { "ldap_dynamicgroup_unique_attribute_type","text", CONFIGSETTING_RELOADABLE },
  328. { "ldap_dynamicgroup_filter_attribute","kopanoFilter", CONFIGSETTING_RELOADABLE },
  329. { "ldap_dynamicgroup_search_base_attribute","kopanoBase", CONFIGSETTING_RELOADABLE },
  330. { "ldap_dynamicgroup_name_attribute","cn", CONFIGSETTING_RELOADABLE },
  331. { "ldap_addressbook_hide_attribute","kopanoHidden", CONFIGSETTING_RELOADABLE },
  332. { "ldap_network_timeout", "30", CONFIGSETTING_RELOADABLE },
  333. { "ldap_object_search_filter", "", CONFIGSETTING_RELOADABLE },
  334. { "ldap_filter_cutoff_elements", "1000", CONFIGSETTING_RELOADABLE },
  335. { "ldap_page_size", "1000", CONFIGSETTING_RELOADABLE }, // MaxPageSize in ADS defaults to 1000
  336. /* Aliases, they should be loaded through the propmap directive */
  337. { "0x6788001E", "", 0, CONFIGGROUP_PROPMAP }, /* PR_EC_EXCHANGE_DN */
  338. { "0x3A08001E", "telephoneNumber", 0, CONFIGGROUP_PROPMAP }, /* PR_BUSINESS_TELEPHONE_NUMBER */
  339. { "0x3A23001E", "facsimileTelephoneNumber", 0, CONFIGGROUP_PROPMAP }, /* PR_PRIMARY_FAX_NUMBER */
  340. { "0x3A18001E", "department", 0, CONFIGGROUP_PROPMAP }, /* PR_DEPARTMENT_NAME */
  341. { "0x3A19001E", "physicalDeliveryOfficeName", 0, CONFIGGROUP_PROPMAP }, /* PR_OFFICE_LOCATION */
  342. { NULL, NULL },
  343. };
  344. static const char *const lpszAllowedDirectives[] = {
  345. "include",
  346. "propmap",
  347. NULL,
  348. };
  349. m_config = shareddata->CreateConfig(lpDefaults, lpszAllowedDirectives);
  350. if (!m_config)
  351. throw runtime_error(string("Not a valid configuration file."));
  352. // get the list of ldap urls and split them
  353. const char *const ldap_uri = m_config->GetSetting("ldap_uri");
  354. if (ldap_uri && strlen(ldap_uri))
  355. ldap_servers = tokenize(std::string(ldap_uri), ' ', true);
  356. else {
  357. const char *const ldap_host = m_config->GetSetting("ldap_host");
  358. const char *const ldap_port = m_config->GetSetting("ldap_port");
  359. const char *const ldap_proto = m_config->GetSetting("ldap_protocol");
  360. std::string url;
  361. if (ldap_proto != NULL && strcmp(ldap_proto, "ldaps") == 0)
  362. url = format("ldaps://%s:%s", ldap_host, ldap_port);
  363. else
  364. url = format("ldap://%s:%s", ldap_host, ldap_port);
  365. ldap_servers.push_back(std::move(url));
  366. }
  367. if (ldap_servers.empty())
  368. throw ldap_error(string("No LDAP servers configured in ldap.cfg"));
  369. m_timeout.tv_sec = atoui(m_config->GetSetting("ldap_network_timeout"));
  370. m_timeout.tv_usec = 0;
  371. }
  372. void LDAPUserPlugin::InitPlugin()
  373. {
  374. const char *ldap_binddn = m_config->GetSetting("ldap_bind_user");
  375. const char *ldap_bindpw = m_config->GetSetting("ldap_bind_passwd");
  376. /* FIXME encode the user and password, now it depends on which charset the config is saved in */
  377. m_ldap = ConnectLDAP(ldap_binddn, ldap_bindpw);
  378. const char *ldap_server_charset = m_config->GetSetting("ldap_server_charset");
  379. m_iconv = new ECIConv("UTF-8", ldap_server_charset);
  380. if (!m_iconv -> canConvert())
  381. throw ldap_error(format("Cannot convert %s to UTF8", ldap_server_charset));
  382. m_iconvrev = new ECIConv(m_config->GetSetting("ldap_server_charset"), "UTF-8");
  383. if (!m_iconvrev -> canConvert())
  384. throw ldap_error(format("Cannot convert UTF-8 to %s", ldap_server_charset));
  385. }
  386. LDAP *LDAPUserPlugin::ConnectLDAP(const char *bind_dn, const char *bind_pw) {
  387. int rc = -1;
  388. LDAP *ld = NULL;
  389. struct timeval tstart, tend;
  390. LONGLONG llelapsedtime = 0;
  391. gettimeofday(&tstart, NULL);
  392. if ((bind_dn && bind_dn[0] != 0) && (bind_pw == NULL || bind_pw[0] == 0)) {
  393. // Username specified, but no password. Apparently, OpenLDAP will attempt
  394. // an anonymous bind when this is attempted. We therefore disallow this
  395. // to make sure you can authenticate a user's password with this function
  396. throw ldap_error(string("Disallowing NULL password for user ") + bind_dn);
  397. }
  398. // Initialize LDAP struct
  399. for (unsigned long int loop = 0; loop < ldap_servers.size(); ++loop) {
  400. const int limit = 0;
  401. const int version = LDAP_VERSION3;
  402. std::string currentServer = ldap_servers.at(ldapServerIndex);
  403. ulock_normal biglock(m_plugin_lock);
  404. rc = ldap_initialize(&ld, currentServer.c_str());
  405. biglock.unlock();
  406. if (rc != LDAP_SUCCESS) {
  407. m_lpStatsCollector->Increment(SCN_LDAP_CONNECT_FAILED);
  408. ec_log_crit("Failed to initialize LDAP for \"%s\": %s", currentServer.c_str(), ldap_err2string(rc));
  409. goto fail2;
  410. }
  411. LOG_PLUGIN_DEBUG("Trying to connect to %s", currentServer.c_str());
  412. if ((rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version)) != LDAP_OPT_SUCCESS) {
  413. ec_log_err("LDAP_OPT_PROTOCOL_VERSION failed: %s", ldap_err2string(rc));
  414. goto fail;
  415. }
  416. // Disable response message size restrictions (but the server's
  417. // restrictions still apply)
  418. if ((rc = ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &limit)) != LDAP_OPT_SUCCESS) {
  419. ec_log_err("LDAP_OPT_SIZELIMIT failed: %s", ldap_err2string(rc));
  420. goto fail;
  421. }
  422. // Search referrals are never accepted - FIXME maybe needs config option
  423. if ((rc = ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF)) != LDAP_OPT_SUCCESS) {
  424. ec_log_err("LDAP_OPT_REFERRALS failed: %s", ldap_err2string(rc));
  425. goto fail;
  426. }
  427. // Set network timeout (for connect)
  428. if ((rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &m_timeout)) != LDAP_OPT_SUCCESS) {
  429. ec_log_err("LDAP_OPT_NETWORK_TIMEOUT failed: %s", ldap_err2string(rc));
  430. goto fail;
  431. }
  432. // Bind
  433. // For these two values: if they are both NULL, anonymous bind
  434. // will be used (ldap_binddn, ldap_bindpw)
  435. LOG_PLUGIN_DEBUG("Issuing LDAP bind");
  436. if ((rc = ldap_simple_bind_s(ld, (char *)bind_dn, (char *)bind_pw)) == LDAP_SUCCESS)
  437. break;
  438. ec_log_warn("LDAP (simple) bind failed: %s", ldap_err2string(rc));
  439. fail:
  440. if (ldap_unbind_s(ld) == -1)
  441. ec_log_err("LDAP unbind failed");
  442. fail2:
  443. // see if another (if any) server does work
  444. ++ldapServerIndex;
  445. if (ldapServerIndex >= ldap_servers.size())
  446. ldapServerIndex = 0;
  447. m_lpStatsCollector->Increment(SCN_LDAP_CONNECT_FAILED);
  448. ld = NULL;
  449. if (loop == ldap_servers.size() - 1)
  450. throw ldap_error("Failure connecting any of the LDAP servers");
  451. }
  452. gettimeofday(&tend, NULL);
  453. llelapsedtime = difftimeval(&tstart, &tend);
  454. m_lpStatsCollector->Increment(SCN_LDAP_CONNECTS);
  455. m_lpStatsCollector->Increment(SCN_LDAP_CONNECT_TIME, llelapsedtime);
  456. m_lpStatsCollector->Max(SCN_LDAP_CONNECT_TIME_MAX, llelapsedtime);
  457. LOG_PLUGIN_DEBUG("ldaptiming [%08.2f] connected to ldap", llelapsedtime / 1000000.0);
  458. return ld;
  459. }
  460. LDAPUserPlugin::~LDAPUserPlugin() {
  461. // Disconnect from the LDAP server
  462. if (m_ldap) {
  463. LOG_PLUGIN_DEBUG("%s", "Disconnecting from LDAP since unloading plugin instance");
  464. if (ldap_unbind_s(m_ldap) == -1)
  465. ec_log_err("LDAP unbind failed");
  466. }
  467. delete m_iconv;
  468. delete m_iconvrev;
  469. }
  470. void LDAPUserPlugin::my_ldap_search_s(char *base, int scope, char *filter, char *attrs[], int attrsonly, LDAPMessage **lppres, LDAPControl **serverControls)
  471. {
  472. int result=LDAP_SUCCESS;
  473. string req;
  474. struct timeval tstart, tend;
  475. LONGLONG llelapsedtime;
  476. auto_free_ldap_message res;
  477. gettimeofday(&tstart, NULL);
  478. if (attrs != NULL)
  479. for (unsigned int i = 0; attrs[i] != NULL; ++i)
  480. req += string(attrs[i]) + " ";
  481. // filter must be NULL to request everything (becomes (objectClass=*) in ldap library)
  482. if (filter[0] == '\0') {
  483. assert(scope == LDAP_SCOPE_BASE);
  484. filter = NULL;
  485. }
  486. /*
  487. * When an m_ldap connection object already exists, use it to make
  488. * a query, and, if that fails for any reason, reconnect-and-retry
  489. * exactly once.
  490. * When no m_ldap connection object existed, this boils down to
  491. * one standard connect plus query.
  492. */
  493. if (m_ldap != NULL)
  494. result = ldap_search_ext_s(m_ldap, base, scope, filter, attrs,
  495. attrsonly, serverControls, nullptr, &m_timeout, 0, &~res);
  496. if (m_ldap == NULL || LDAP_API_ERROR(result)) {
  497. const char *ldap_binddn = m_config->GetSetting("ldap_bind_user");
  498. const char *ldap_bindpw = m_config->GetSetting("ldap_bind_passwd");
  499. if (m_ldap != NULL) {
  500. ec_log_err("LDAP search error: %s. Will unbind, reconnect and retry.", ldap_err2string(result));
  501. if (ldap_unbind_s(m_ldap) == -1)
  502. ec_log_err("LDAP unbind failed");
  503. m_ldap = NULL;
  504. }
  505. /// @todo encode the user and password, now it's depended in which charset the config is saved
  506. m_ldap = ConnectLDAP(ldap_binddn, ldap_bindpw);
  507. m_lpStatsCollector->Increment(SCN_LDAP_RECONNECTS);
  508. result = ldap_search_ext_s(m_ldap, base, scope, filter, attrs,
  509. attrsonly, serverControls, nullptr, nullptr, 0, &~res);
  510. }
  511. if(result != LDAP_SUCCESS) {
  512. ec_log_err("LDAP query in \"%s\" failed: %s (result=0x%02x, %s)", base, filter, result, ldap_err2string(result));
  513. if(LDAP_API_ERROR(result)) {
  514. // Some kind of API error occurred (error is not from the server). Unbind the connection so any next try will re-bind
  515. // which will possibly connect to a different (failed over) server.
  516. if (m_ldap != NULL) {
  517. ec_log_err("Unbinding from LDAP because of continued error (%s)", ldap_err2string(result));
  518. if (ldap_unbind_s(m_ldap) == -1)
  519. ec_log_err("LDAP unbind failed");
  520. m_ldap = NULL;
  521. }
  522. }
  523. goto exit;
  524. }
  525. gettimeofday(&tend, NULL);
  526. llelapsedtime = difftimeval(&tstart,&tend);
  527. LOG_PLUGIN_DEBUG("ldaptiming [%08.2f] (\"%s\" \"%s\" %s), results: %d", llelapsedtime/1000000.0, base, filter, req.c_str(), ldap_count_entries(m_ldap, res));
  528. *lppres = res.release(); // deref the pointer from object
  529. m_lpStatsCollector->Increment(SCN_LDAP_SEARCH);
  530. m_lpStatsCollector->Increment(SCN_LDAP_SEARCH_TIME, llelapsedtime);
  531. m_lpStatsCollector->Max(SCN_LDAP_SEARCH_TIME_MAX, llelapsedtime);
  532. exit:
  533. if (result != LDAP_SUCCESS) {
  534. m_lpStatsCollector->Increment(SCN_LDAP_SEARCH_FAILED);
  535. // throw ldap error
  536. throw ldap_error(string("ldap_search_ext_s: ") + ldap_err2string(result), result);
  537. }
  538. // In rare situations ldap_search_s can return LDAP_SUCCESS, but leave res at NULL. This
  539. // seems to happen when the connection to the server is lost at a very specific time.
  540. // The problem is that libldap checks its input parameters with an assert. So the next
  541. // call to ldap_find_first will cause an assertion to kick in because the result from
  542. // ldap_search_s is inconsistent.
  543. // The easiest way around this is to net let this function return with a NULL result.
  544. else if (*lppres == NULL) {
  545. m_lpStatsCollector->Increment(SCN_LDAP_SEARCH_FAILED);
  546. throw ldap_error("ldap_search_ext_s: spurious NULL result");
  547. }
  548. }
  549. std::list<std::string> LDAPUserPlugin::GetClasses(const char *lpszClasses)
  550. {
  551. std::vector<std::string> vecClasses = tokenize(lpszClasses, ',');
  552. std::list<std::string> lstClasses;
  553. for (unsigned int i = 0; i < vecClasses.size(); ++i)
  554. lstClasses.push_back(trim(vecClasses[i]));
  555. return lstClasses;
  556. }
  557. bool LDAPUserPlugin::MatchClasses(std::set<std::string> setClasses, std::list<std::string> lstClasses)
  558. {
  559. for (const auto &cls : lstClasses)
  560. if (setClasses.find(strToUpper(cls)) == setClasses.cend())
  561. return false;
  562. return true;
  563. }
  564. std::string LDAPUserPlugin::GetObjectClassFilter(const char *lpszObjectClassAttr, const char *lpszClasses)
  565. {
  566. std::list<std::string> lstObjectClasses = GetClasses(lpszClasses);
  567. std::string filter;
  568. if(lstObjectClasses.size() == 0) {
  569. filter = "";
  570. }
  571. else if(lstObjectClasses.size() == 1) {
  572. filter = (std::string)"(" + lpszObjectClassAttr + "=" + *lstObjectClasses.begin() + ")";
  573. }
  574. else {
  575. filter = "(&";
  576. for (const auto &cls : lstObjectClasses)
  577. filter += (std::string)"(" + lpszObjectClassAttr + "=" + cls + ")";
  578. filter += ")";
  579. }
  580. return filter;
  581. }
  582. objectid_t LDAPUserPlugin::GetObjectIdForEntry(LDAPMessage *entry)
  583. {
  584. list<string> objclasses;
  585. objectclass_t objclass = OBJECTCLASS_UNKNOWN;
  586. string nonactive_type;
  587. string resource_type;
  588. string security_type;
  589. string user_unique;
  590. string group_unique;
  591. string company_unique;
  592. string addresslist_unique;
  593. string dynamicgroup_unique;
  594. string object_uid;
  595. const char *class_attr = m_config->GetSetting("ldap_object_type_attribute");
  596. const char *nonactive_attr = m_config->GetSetting("ldap_nonactive_attribute");
  597. const char *resource_attr = m_config->GetSetting("ldap_resource_type_attribute");
  598. const char *security_attr = m_config->GetSetting("ldap_group_security_attribute");
  599. const char *security_attr_type = m_config->GetSetting("ldap_group_security_attribute_type");
  600. const char *user_unique_attr = m_config->GetSetting("ldap_user_unique_attribute");
  601. const char *group_unique_attr = m_config->GetSetting("ldap_group_unique_attribute");
  602. const char *company_unique_attr = m_config->GetSetting("ldap_company_unique_attribute");
  603. const char *addresslist_unique_attr = m_config->GetSetting("ldap_addresslist_unique_attribute");
  604. const char *dynamicgroup_unique_attr = m_config->GetSetting("ldap_dynamicgroup_unique_attribute");
  605. const char *class_user_type = m_config->GetSetting("ldap_user_type_attribute_value");
  606. const char *class_contact_type = m_config->GetSetting("ldap_contact_type_attribute_value");
  607. const char *class_group_type = m_config->GetSetting("ldap_group_type_attribute_value");
  608. const char *class_company_type = m_config->GetSetting("ldap_company_type_attribute_value");
  609. const char *class_address_type = m_config->GetSetting("ldap_addresslist_type_attribute_value");
  610. const char *class_dynamic_type = m_config->GetSetting("ldap_dynamicgroup_type_attribute_value");
  611. FOREACH_ATTR(entry) {
  612. if (class_attr && strcasecmp(att, class_attr) == 0)
  613. objclasses = getLDAPAttributeValues(att, entry);
  614. if (nonactive_attr && strcasecmp(att, nonactive_attr) == 0) {
  615. nonactive_type = getLDAPAttributeValue(att, entry);
  616. }
  617. if (resource_attr && strcasecmp(att, resource_attr) == 0)
  618. resource_type = getLDAPAttributeValue(att, entry);
  619. if (security_attr && strcasecmp(att, security_attr) == 0) {
  620. security_type = getLDAPAttributeValue(att, entry);
  621. }
  622. if (user_unique_attr && strcasecmp(att, user_unique_attr) == 0)
  623. user_unique = getLDAPAttributeValue(att, entry);
  624. if (group_unique_attr && strcasecmp(att, group_unique_attr) == 0)
  625. group_unique = getLDAPAttributeValue(att, entry);
  626. if (company_unique_attr && strcasecmp(att, company_unique_attr) == 0)
  627. company_unique = getLDAPAttributeValue(att, entry);
  628. if (addresslist_unique_attr && strcasecmp(att, addresslist_unique_attr) == 0)
  629. addresslist_unique = getLDAPAttributeValue(att, entry);
  630. if (dynamicgroup_unique_attr && strcasecmp(att, dynamicgroup_unique_attr) == 0)
  631. dynamicgroup_unique = getLDAPAttributeValue(att, entry);
  632. }
  633. END_FOREACH_ATTR
  634. // All object classes for a certain object
  635. std::set<std::string> setObjectClasses;
  636. // List of matching Kopano object classes
  637. std::list<std::pair<unsigned int, objectclass_t> > lstMatches;
  638. std::list<std::string> lstLDAPObjectClasses;
  639. /*
  640. * Find the Kopano object type by looking at the object classes.
  641. *
  642. * We do this by checking the best-match of objectclasses; if an LDAP objectClass is
  643. * listed for multiple Kopano object classes, we use the following rules:
  644. *
  645. * First, the Kopano object class with the most matching LDAP objectClasses is selected.
  646. * If that does not resolve the ambiguity, then object classes with higher numerical values
  647. * override those with lower numerical values (most importantly, contacts override users)
  648. */
  649. for (const auto &i : objclasses)
  650. setObjectClasses.insert(strToUpper(i));
  651. lstLDAPObjectClasses = GetClasses(class_user_type);
  652. if(MatchClasses(setObjectClasses, lstLDAPObjectClasses))
  653. lstMatches.push_back(std::pair<unsigned int, objectclass_t>(lstLDAPObjectClasses.size(), OBJECTCLASS_USER)); // Could still be active or nonactive, will resolve later
  654. lstLDAPObjectClasses = GetClasses(class_contact_type);
  655. if(MatchClasses(setObjectClasses, lstLDAPObjectClasses))
  656. lstMatches.push_back(std::pair<unsigned int, objectclass_t>(lstLDAPObjectClasses.size(), NONACTIVE_CONTACT));
  657. lstLDAPObjectClasses = GetClasses(class_group_type);
  658. if(MatchClasses(setObjectClasses, lstLDAPObjectClasses))
  659. lstMatches.push_back(std::pair<unsigned int, objectclass_t>(lstLDAPObjectClasses.size(), OBJECTCLASS_DISTLIST)); // Could be permission or distribution group, will resolve later
  660. lstLDAPObjectClasses = GetClasses(class_dynamic_type);
  661. if(MatchClasses(setObjectClasses, lstLDAPObjectClasses))
  662. lstMatches.push_back(std::pair<unsigned int, objectclass_t>(lstLDAPObjectClasses.size(), DISTLIST_DYNAMIC));
  663. lstLDAPObjectClasses = GetClasses(class_company_type);
  664. if(MatchClasses(setObjectClasses, lstLDAPObjectClasses))
  665. lstMatches.push_back(std::pair<unsigned int, objectclass_t>(lstLDAPObjectClasses.size(), CONTAINER_COMPANY));
  666. lstLDAPObjectClasses = GetClasses(class_address_type);
  667. if(MatchClasses(setObjectClasses, lstLDAPObjectClasses))
  668. lstMatches.push_back(std::pair<unsigned int, objectclass_t>(lstLDAPObjectClasses.size(), CONTAINER_ADDRESSLIST));
  669. // lstMatches now contains all the kopano object classes that the object COULD be, now sort by number of object classes
  670. if(lstMatches.empty())
  671. throw data_error("Unable to detect object class for object: " + GetLDAPEntryDN(entry));
  672. lstMatches.sort();
  673. lstMatches.reverse();
  674. // Class we want is now at the top of the list (since we sorted by number of matches, then by class id)
  675. objclass = lstMatches.begin()->second;
  676. // Subspecify some generic types now
  677. if (objclass == OBJECTCLASS_USER) {
  678. if (atoi(nonactive_type.c_str()) != 0)
  679. objclass = NONACTIVE_USER;
  680. else
  681. objclass = ACTIVE_USER;
  682. if (objclass == NONACTIVE_USER && !resource_type.empty()) {
  683. /* Overwrite objectclass, a resource is allowed to overwrite the nonactive type */
  684. if (strcasecmp(resource_type.c_str(), "room") == 0)
  685. objclass = NONACTIVE_ROOM;
  686. else if (strcasecmp(resource_type.c_str(), "equipment") == 0)
  687. objclass = NONACTIVE_EQUIPMENT;
  688. }
  689. object_uid = user_unique;
  690. }
  691. if (objclass == NONACTIVE_CONTACT) {
  692. object_uid = user_unique;
  693. }
  694. if (objclass == OBJECTCLASS_DISTLIST) {
  695. if (!strcasecmp(security_attr_type, "ads")) {
  696. if(atoi(security_type.c_str()) & 0x80000000)
  697. objclass = DISTLIST_SECURITY;
  698. else
  699. objclass = DISTLIST_GROUP;
  700. } else {
  701. if (parseBool(security_type.c_str()))
  702. objclass = DISTLIST_SECURITY;
  703. else
  704. objclass = DISTLIST_GROUP;
  705. }
  706. object_uid = group_unique;
  707. }
  708. if (objclass == DISTLIST_DYNAMIC) {
  709. object_uid = dynamicgroup_unique;
  710. }
  711. if (objclass == CONTAINER_COMPANY) {
  712. object_uid = company_unique;
  713. }
  714. if (objclass == CONTAINER_ADDRESSLIST) {
  715. object_uid = addresslist_unique;
  716. }
  717. return objectid_t(object_uid, objclass);
  718. }
  719. std::unique_ptr<signatures_t>
  720. LDAPUserPlugin::getAllObjectsByFilter(const std::string &basedn, int scope,
  721. const std::string &search_filter, const std::string &strCompanyDN,
  722. bool bCache)
  723. {
  724. std::unique_ptr<signatures_t> signatures(new signatures_t());
  725. objectid_t objectid;
  726. string dn;
  727. string signature;
  728. map<objectclass_t, dn_cache_t*> mapDNCache;
  729. std::unique_ptr<dn_list_t> dnFilter;
  730. auto_free_ldap_message res;
  731. /*
  732. * When working in multi-company mode we need to determine if the found object
  733. * is really a member of the requested company and not turned up in the
  734. * result because he is member of a subcompany.
  735. * Create a filter by requesting all subcompanies for he given company,
  736. * and remove any returned objects which are member of these subcompanies.
  737. */
  738. if (m_bHosted && !strCompanyDN.empty()) {
  739. std::unique_ptr<dn_cache_t> lpCompanyCache = m_lpCache->getObjectDNCache(this, CONTAINER_COMPANY);
  740. dnFilter = m_lpCache->getChildrenForDN(lpCompanyCache, strCompanyDN);
  741. }
  742. std::unique_ptr<attrArray> request_attrs(new attrArray(15));
  743. /* Needed for GetObjectIdForEntry() */
  744. CONFIG_TO_ATTR(request_attrs, class_attr, "ldap_object_type_attribute");
  745. CONFIG_TO_ATTR(request_attrs, nonactive_attr, "ldap_nonactive_attribute");
  746. CONFIG_TO_ATTR(request_attrs, resource_attr, "ldap_resource_type_attribute");
  747. CONFIG_TO_ATTR(request_attrs, security_attr, "ldap_group_security_attribute");
  748. CONFIG_TO_ATTR(request_attrs, user_unique_attr, "ldap_user_unique_attribute");
  749. CONFIG_TO_ATTR(request_attrs, group_unique_attr, "ldap_group_unique_attribute");
  750. CONFIG_TO_ATTR(request_attrs, company_unique_attr, "ldap_company_unique_attribute");
  751. CONFIG_TO_ATTR(request_attrs, addresslist_unique_attr, "ldap_addresslist_unique_attribute");
  752. CONFIG_TO_ATTR(request_attrs, dynamicgroup_unique_attr, "ldap_dynamicgroup_unique_attribute");
  753. /* Needed for cache */
  754. CONFIG_TO_ATTR(request_attrs, modify_attr, "ldap_last_modification_attribute");
  755. FOREACH_PAGING_SEARCH((char *)basedn.c_str(), scope,
  756. (char *)search_filter.c_str(), (char **)request_attrs->get(),
  757. FETCH_ATTR_VALS, res)
  758. {
  759. FOREACH_ENTRY(res) {
  760. dn = GetLDAPEntryDN(entry);
  761. /* Make sure the DN isn't filtered because it is located in the subcontainer */
  762. if (m_bHosted && !strCompanyDN.empty()) {
  763. if (m_lpCache->isDNInList(dnFilter, dn))
  764. continue;
  765. }
  766. FOREACH_ATTR(entry) {
  767. if (modify_attr && strcasecmp(att, modify_attr) == 0)
  768. signature = getLDAPAttributeValue(att, entry);
  769. }
  770. END_FOREACH_ATTR
  771. try {
  772. objectid = GetObjectIdForEntry(entry);
  773. } catch(data_error &e) {
  774. ec_log_warn("Unable to get object id: %s", e.what());
  775. continue;
  776. }
  777. if (objectid.id.empty()) {
  778. ec_log_warn("Unique id not found for DN: %s", dn.c_str());
  779. continue;
  780. }
  781. signatures->push_back(objectsignature_t(objectid, signature));
  782. if (bCache) {
  783. std::pair<map<objectclass_t, dn_cache_t*>::iterator, bool> retval;
  784. retval = mapDNCache.insert(make_pair(objectid.objclass, (dn_cache_t*)NULL));
  785. if (retval.second)
  786. retval.first->second = new dn_cache_t();
  787. auto iterDNCache = retval.first;
  788. iterDNCache->second->insert(make_pair(objectid, dn));
  789. }
  790. }
  791. END_FOREACH_ENTRY
  792. }
  793. END_FOREACH_LDAP_PAGING
  794. /* Update cache */
  795. for (const auto &p : mapDNCache)
  796. m_lpCache->setObjectDNCache(p.first, std::unique_ptr<dn_cache_t>(p.second));
  797. return signatures;
  798. }
  799. string LDAPUserPlugin::getSearchBase(const objectid_t &company)
  800. {
  801. const char *lpszSearchBase = m_config->GetSetting("ldap_search_base");
  802. string search_base;
  803. if (lpszSearchBase == nullptr)
  804. /* GetSetting returns "" for all options it knows.. */
  805. throw logic_error("getSearchBase: unexpected nullptr");
  806. if (m_bHosted && !company.id.empty()) {
  807. // find company DN, and use as search_base
  808. std::unique_ptr<dn_cache_t> lpCompanyCache = m_lpCache->getObjectDNCache(this, company.objclass);
  809. search_base = m_lpCache->getDNForObject(lpCompanyCache, company);
  810. // CHECK: should not be possible to not already know the company
  811. if (search_base.empty()) {
  812. ec_log_crit("No search base found for company \"%s\"", company.id.c_str());
  813. search_base = lpszSearchBase;
  814. }
  815. } else {
  816. search_base = lpszSearchBase;
  817. }
  818. return search_base;
  819. }
  820. string LDAPUserPlugin::getServerSearchFilter()
  821. {
  822. string filter, subfilter;
  823. const char *objecttype = m_config->GetSetting("ldap_object_type_attribute", "", NULL);
  824. const char *servertype = m_config->GetSetting("ldap_server_type_attribute_value", "", NULL);
  825. const char *serverfilter = m_config->GetSetting("ldap_server_search_filter");
  826. if (!objecttype)
  827. throw runtime_error("No object type attribute defined");
  828. if (!servertype)
  829. throw runtime_error("No server type attribute value defined");
  830. filter = serverfilter;
  831. subfilter = "(" + string(objecttype) + "=" + servertype + ")";
  832. if (!filter.empty())
  833. filter = "(&(|" + filter + ")" + subfilter + ")";
  834. else
  835. filter = subfilter;
  836. return filter;
  837. }
  838. string LDAPUserPlugin::getSearchFilter(objectclass_t objclass)
  839. {
  840. string filter, subfilter;
  841. const char *objecttype = m_config->GetSetting("ldap_object_type_attribute", "", NULL);
  842. const char *usertype = m_config->GetSetting("ldap_user_type_attribute_value", "", NULL);
  843. const char *contacttype = m_config->GetSetting("ldap_contact_type_attribute_value", "", NULL);
  844. const char *grouptype = m_config->GetSetting("ldap_group_type_attribute_value", "", NULL);
  845. const char *companytype = m_config->GetSetting("ldap_company_type_attribute_value", "", NULL);
  846. const char *addresslisttype = m_config->GetSetting("ldap_addresslist_type_attribute_value", "", NULL);
  847. const char *dynamicgrouptype = m_config->GetSetting("ldap_dynamicgroup_type_attribute_value", "", NULL);
  848. const char *userfilter = m_config->GetSetting("ldap_user_search_filter");
  849. const char *groupfilter = m_config->GetSetting("ldap_group_search_filter");
  850. const char *companyfilter = m_config->GetSetting("ldap_company_search_filter");
  851. const char *addresslistfilter = m_config->GetSetting("ldap_addresslist_search_filter");
  852. const char *dynamicgroupfilter = m_config->GetSetting("ldap_dynamicgroup_search_filter");
  853. if (!objecttype)
  854. throw runtime_error("No object type attribute defined");
  855. switch (objclass) {
  856. case OBJECTCLASS_UNKNOWN:
  857. /* No objectclass is given, merge all filters together */
  858. subfilter = getSearchFilter(OBJECTCLASS_USER);
  859. if (contacttype)
  860. subfilter += getSearchFilter(NONACTIVE_CONTACT);
  861. subfilter += getSearchFilter(OBJECTCLASS_DISTLIST);
  862. subfilter += getSearchFilter(OBJECTCLASS_CONTAINER);
  863. subfilter = "(|" + subfilter + ")";
  864. break;
  865. case OBJECTCLASS_USER:
  866. case ACTIVE_USER:
  867. case NONACTIVE_USER:
  868. case NONACTIVE_ROOM:
  869. case NONACTIVE_EQUIPMENT:
  870. if (!usertype)
  871. throw runtime_error("No user type attribute value defined");
  872. filter = userfilter;
  873. subfilter += "(|";
  874. subfilter += GetObjectClassFilter(objecttype, usertype);
  875. /* Generic user type should not exclude Contacts */
  876. if (objclass == OBJECTCLASS_USER && contacttype)
  877. subfilter += GetObjectClassFilter(objecttype, contacttype);
  878. subfilter += ")";
  879. break;
  880. case NONACTIVE_CONTACT:
  881. if (!contacttype)
  882. throw runtime_error("No contact type attribute value defined");
  883. filter = userfilter;
  884. subfilter = GetObjectClassFilter(objecttype, contacttype);
  885. break;
  886. case OBJECTCLASS_DISTLIST:
  887. case DISTLIST_GROUP:
  888. case DISTLIST_SECURITY:
  889. case DISTLIST_DYNAMIC:
  890. if ((grouptype || (groupfilter && groupfilter[0] != '\0')) && (dynamicgrouptype || (dynamicgroupfilter && dynamicgroupfilter[0] != '\0')))
  891. subfilter = "(|";
  892. if (grouptype && groupfilter && groupfilter[0] != '\0')
  893. subfilter += string("(&") + GetObjectClassFilter(objecttype, grouptype) + groupfilter + ")";
  894. else if (grouptype)
  895. subfilter += GetObjectClassFilter(objecttype, grouptype);
  896. else if (groupfilter && groupfilter[0] != '\0')
  897. subfilter += groupfilter;
  898. if (dynamicgrouptype && dynamicgroupfilter && dynamicgroupfilter[0] != '\0')
  899. subfilter += string("(&") + GetObjectClassFilter(objecttype, dynamicgrouptype) + dynamicgroupfilter + ")";
  900. else if (dynamicgrouptype)
  901. subfilter += GetObjectClassFilter(objecttype, dynamicgrouptype);
  902. else if (dynamicgroupfilter && dynamicgroupfilter[0] != '\0')
  903. subfilter += dynamicgroupfilter;
  904. if ((grouptype || (groupfilter && groupfilter[0] != '\0')) && (dynamicgrouptype || (dynamicgroupfilter && dynamicgroupfilter[0] != '\0')))
  905. subfilter += ")";
  906. break;
  907. case OBJECTCLASS_CONTAINER:
  908. subfilter = "(|";
  909. if (m_bHosted) {
  910. if (!companytype)
  911. throw runtime_error("No company type attribute value defined");
  912. subfilter += string("(&") + companyfilter + GetObjectClassFilter(objecttype, companytype) + ")";
  913. }
  914. if (addresslisttype)
  915. subfilter += string("(&") + addresslistfilter + GetObjectClassFilter(objecttype, addresslisttype) + ")";
  916. else
  917. subfilter += addresslistfilter;
  918. subfilter += ")";
  919. break;
  920. case CONTAINER_COMPANY:
  921. if (!m_bHosted)
  922. throw runtime_error("Searching for companies is not supported in singlecompany server");
  923. if (!companytype)
  924. throw runtime_error("No company type attribute value defined");
  925. filter = companyfilter;
  926. subfilter = GetObjectClassFilter(objecttype, companytype);
  927. break;
  928. case CONTAINER_ADDRESSLIST:
  929. if (!addresslisttype)
  930. throw runtime_error("No addresslist type attribute value defined");
  931. filter = addresslistfilter;
  932. subfilter = GetObjectClassFilter(objecttype, addresslisttype);
  933. break;
  934. default:
  935. throw runtime_error("Unknown object type " + stringify(objclass));
  936. }
  937. if (!filter.empty())
  938. filter = "(&" + filter + subfilter + ")";
  939. else
  940. filter = subfilter;
  941. return filter;
  942. }
  943. string LDAPUserPlugin::getSearchFilter(const string &data, const char *attr, const char *attr_type)
  944. {
  945. string search_data;
  946. // Set binary uniqueid to escaped string
  947. if(attr_type && strcasecmp(attr_type, LDAP_DATA_TYPE_BINARY) == 0)
  948. BintoEscapeSequence(data.c_str(), data.size(), &search_data);
  949. else
  950. search_data = StringEscapeSequence(data);
  951. if (attr)
  952. return "(" + string(attr) + "=" + search_data + ")";
  953. return "";
  954. }
  955. string LDAPUserPlugin::getObjectSearchFilter(const objectid_t &id, const char *attr, const char *attr_type)
  956. {
  957. if (attr)
  958. return "(&" + getSearchFilter(id.objclass) + getSearchFilter(id.id, attr, attr_type) + ")";
  959. switch (id.objclass) {
  960. case OBJECTCLASS_USER:
  961. case ACTIVE_USER:
  962. case NONACTIVE_USER:
  963. case NONACTIVE_ROOM:
  964. case NONACTIVE_EQUIPMENT:
  965. case NONACTIVE_CONTACT:
  966. return getObjectSearchFilter(id,
  967. m_config->GetSetting("ldap_user_unique_attribute"),
  968. m_config->GetSetting("ldap_user_unique_attribute_type"));
  969. break;
  970. case OBJECTCLASS_DISTLIST:
  971. return
  972. "(&" +
  973. getSearchFilter(id.objclass) +
  974. "(|" +
  975. getSearchFilter(id.id,
  976. m_config->GetSetting("ldap_group_unique_attribute"),
  977. m_config->GetSetting("ldap_group_unique_attribute_type")) +
  978. getSearchFilter(id.id,
  979. m_config->GetSetting("ldap_dynamicgroup_unique_attribute"),
  980. m_config->GetSetting("ldap_dynamicgroup_unique_attribute_type")) +
  981. "))";
  982. break;
  983. case DISTLIST_GROUP:
  984. case DISTLIST_SECURITY:
  985. return getObjectSearchFilter(id,
  986. m_config->GetSetting("ldap_group_unique_attribute"),
  987. m_config->GetSetting("ldap_group_unique_attribute_type"));
  988. break;
  989. case DISTLIST_DYNAMIC:
  990. return getObjectSearchFilter(id,
  991. m_config->GetSetting("ldap_dynamicgroup_unique_attribute"),
  992. m_config->GetSetting("ldap_dynamicgroup_unique_attribute_type"));
  993. break;
  994. case OBJECTCLASS_CONTAINER:
  995. return
  996. "(&" +
  997. getSearchFilter(id.objclass) +
  998. "(|" +
  999. getSearchFilter(id.id,
  1000. m_config->GetSetting("ldap_company_unique_attribute"),
  1001. m_config->GetSetting("ldap_company_unique_attribute_type")) +
  1002. getSearchFilter(id.id,
  1003. m_config->GetSetting("ldap_addresslist_unique_attribute"),
  1004. m_config->GetSetting("ldap_addresslist_unique_attribute_type")) +
  1005. "))";
  1006. break;
  1007. case CONTAINER_COMPANY:
  1008. return getObjectSearchFilter(id,
  1009. m_config->GetSetting("ldap_company_unique_attribute"),
  1010. m_config->GetSetting("ldap_company_unique_attribute_type"));
  1011. break;
  1012. case CONTAINER_ADDRESSLIST:
  1013. return getObjectSearchFilter(id,
  1014. m_config->GetSetting("ldap_addresslist_unique_attribute"),
  1015. m_config->GetSetting("ldap_addresslist_unique_attribute_type"));
  1016. break;
  1017. case OBJECTCLASS_UNKNOWN:
  1018. default:
  1019. throw runtime_error("Object is wrong type");
  1020. }
  1021. }
  1022. string LDAPUserPlugin::objectUniqueIDtoAttributeData(const objectid_t &uniqueid, const char* lpAttr)
  1023. {
  1024. auto_free_ldap_message res;
  1025. string strData;
  1026. LDAPMessage* entry;
  1027. bool bDataAttrFound = false;
  1028. string ldap_basedn = getSearchBase();
  1029. string ldap_filter = getObjectSearchFilter(uniqueid);
  1030. char *request_attrs[] = {
  1031. (char*)lpAttr,
  1032. NULL
  1033. };
  1034. if (lpAttr == NULL)
  1035. throw runtime_error("Cannot convert uniqueid to unknown attribute");
  1036. my_ldap_search_s(
  1037. (char *)ldap_basedn.c_str(), LDAP_SCOPE_SUBTREE,
  1038. (char *)ldap_filter.c_str(),
  1039. request_attrs, FETCH_ATTR_VALS, &~res);
  1040. switch(ldap_count_entries(m_ldap, res)) {
  1041. case 0:
  1042. throw objectnotfound(ldap_filter);
  1043. case 1:
  1044. break;
  1045. default:
  1046. throw toomanyobjects(string("More than one object returned in search ") + ldap_filter);
  1047. }
  1048. entry = ldap_first_entry(m_ldap, res);
  1049. if(entry == NULL) {
  1050. throw runtime_error("ldap_dn: broken.");
  1051. }
  1052. FOREACH_ATTR(entry) {
  1053. if (strcasecmp(att, lpAttr) == 0) {
  1054. strData = getLDAPAttributeValue(att, entry);
  1055. bDataAttrFound = true;
  1056. }
  1057. }
  1058. END_FOREACH_ATTR
  1059. if(bDataAttrFound == false)
  1060. throw data_error(string(lpAttr)+" attribute not found");
  1061. return strData;
  1062. }
  1063. string LDAPUserPlugin::objectUniqueIDtoObjectDN(const objectid_t &uniqueid, bool cache)
  1064. {
  1065. std::unique_ptr<dn_cache_t> lpCache = m_lpCache->getObjectDNCache(this, uniqueid.objclass);
  1066. auto_free_ldap_message res;
  1067. string dn;
  1068. LDAPMessage* entry = NULL;
  1069. /*
  1070. * The cache should actually contain this entry, search for the uniqueid in there first.
  1071. * In the rare case that the cache didn't contain the entry, check LDAP.
  1072. */
  1073. if (cache) {
  1074. dn = m_lpCache->getDNForObject(lpCache, uniqueid);
  1075. if (!dn.empty())
  1076. return dn;
  1077. }
  1078. /*
  1079. * That's odd, the cache didn't contain the uniqueid. Perform a LDAP query
  1080. * to search for the object, but chances are high the object doesn't exist at all.
  1081. *
  1082. * Except if we skipped the cache as per ZCP-11720, where we always
  1083. * want to issue an LDAP query.
  1084. */
  1085. string ldap_basedn = getSearchBase();
  1086. string ldap_filter = getObjectSearchFilter(uniqueid);
  1087. std::unique_ptr<attrArray> request_attrs(new attrArray(1));
  1088. request_attrs->add("dn");
  1089. my_ldap_search_s(
  1090. (char *)ldap_basedn.c_str(), LDAP_SCOPE_SUBTREE,
  1091. (char *)ldap_filter.c_str(), (char **)request_attrs->get(),
  1092. DONT_FETCH_ATTR_VALS, &~res);
  1093. switch(ldap_count_entries(m_ldap, res)) {
  1094. case 0:
  1095. throw objectnotfound(ldap_filter);
  1096. case 1:
  1097. break;
  1098. default:
  1099. throw toomanyobjects(string("More than one object returned in search ") + ldap_filter);
  1100. }
  1101. entry = ldap_first_entry(m_ldap, res);
  1102. if(entry == NULL) {
  1103. throw runtime_error("ldap_dn: broken.");
  1104. }
  1105. dn = GetLDAPEntryDN(entry);
  1106. return dn;
  1107. }
  1108. objectsignature_t LDAPUserPlugin::objectDNtoObjectSignature(objectclass_t objclass, const string &dn)
  1109. {
  1110. std::unique_ptr<signatures_t> signatures;
  1111. string ldap_filter;
  1112. ldap_filter = getSearchFilter(objclass);
  1113. signatures = getAllObjectsByFilter(dn, LDAP_SCOPE_BASE, ldap_filter, string(), false);
  1114. if (signatures->empty())
  1115. throw objectnotfound(dn);
  1116. else if (signatures->size() != 1)
  1117. throw toomanyobjects("More than one object returned in search for DN " + dn);
  1118. return signatures->front();
  1119. }
  1120. std::unique_ptr<signatures_t>
  1121. LDAPUserPlugin::objectDNtoObjectSignatures(objectclass_t objclass,
  1122. const std::list<std::string> &dn)
  1123. {
  1124. std::unique_ptr<signatures_t> signatures(new signatures_t());
  1125. for (const auto &i : dn) {
  1126. try {
  1127. signatures->push_back(objectDNtoObjectSignature(objclass, i));
  1128. } catch (objectnotfound &e) {
  1129. // resolve failed, drop entry
  1130. continue;
  1131. } catch (ldap_error &e) {
  1132. if(LDAP_NAME_ERROR(e.GetLDAPError()))
  1133. continue;
  1134. throw;
  1135. } catch (std::exception &e) {
  1136. // query failed, drop entry
  1137. continue;
  1138. }
  1139. }
  1140. return signatures;
  1141. }
  1142. std::unique_ptr<signatures_t>
  1143. LDAPUserPlugin::resolveObjectsFromAttribute(objectclass_t objclass,
  1144. const std::list<std::string> &objects, const char *lpAttr,
  1145. const objectid_t &company)
  1146. {
  1147. const char *lpAttrs[2] = {
  1148. lpAttr,
  1149. NULL,
  1150. };
  1151. return resolveObjectsFromAttributes(objclass, objects, lpAttrs, company);
  1152. }
  1153. std::unique_ptr<signatures_t>
  1154. LDAPUserPlugin::resolveObjectsFromAttributes(objectclass_t objclass,
  1155. const std::list<std::string> &objects, const char **lppAttr,
  1156. const objectid_t &company)
  1157. {
  1158. string ldap_basedn;
  1159. string ldap_filter;
  1160. string companyDN;
  1161. if (lppAttr == NULL || lppAttr[0] == NULL)
  1162. throw runtime_error("Unable to search for unknown attribute");
  1163. ldap_basedn = getSearchBase(company);
  1164. ldap_filter = getSearchFilter(objclass);
  1165. if (!company.id.empty())
  1166. companyDN = ldap_basedn; // in hosted, companyDN is the same as searchbase?
  1167. ldap_filter = "(&" + ldap_filter + "(|";
  1168. for (const auto &i : objects)
  1169. for (unsigned int j = 0; lppAttr[j] != NULL; ++j)
  1170. ldap_filter += "(" + string(lppAttr[j]) + "=" + StringEscapeSequence(i) + ")";
  1171. ldap_filter += "))";
  1172. return getAllObjectsByFilter(ldap_basedn, LDAP_SCOPE_SUBTREE, ldap_filter, companyDN, false);
  1173. }
  1174. objectsignature_t LDAPUserPlugin::resolveObjectFromAttributeType(objectclass_t objclass, const string &object, const char* lpAttr, const char* lpAttrType, const objectid_t &company)
  1175. {
  1176. std::unique_ptr<signatures_t> signatures;
  1177. list<string> objects;
  1178. objects.push_back(object);
  1179. signatures = resolveObjectsFromAttributeType(objclass, objects, lpAttr, lpAttrType, company);
  1180. if (!signatures.get() || signatures->empty())
  1181. throw objectnotfound(object+" not found in LDAP");
  1182. return signatures->front();
  1183. }
  1184. std::unique_ptr<signatures_t>
  1185. LDAPUserPlugin::resolveObjectsFromAttributeType(objectclass_t objclass,
  1186. const std::list<std::string> &objects, const char *lpAttr,
  1187. const char *lpAttrType, const objectid_t &company)
  1188. {
  1189. const char *lpAttrs[2] = {
  1190. lpAttr,
  1191. NULL,
  1192. };
  1193. return resolveObjectsFromAttributesType(objclass, objects, lpAttrs, lpAttrType, company);
  1194. }
  1195. std::unique_ptr<signatures_t>
  1196. LDAPUserPlugin::resolveObjectsFromAttributesType(objectclass_t objclass,
  1197. const std::list<std::string> &objects, const char **lppAttr,
  1198. const char *lpAttrType, const objectid_t &company)
  1199. {
  1200. std::unique_ptr<signatures_t> signatures;
  1201. /* When the relation attribute the is the DN, we cannot perform any optimizations
  1202. * and we must incur the penalty of having to resolve each entry one by one.
  1203. * When the relation attribute is not the DN, we can optimize the lookup
  1204. * by creating a single query that obtains all the required data in a single query. */
  1205. if (lpAttrType && strcasecmp(lpAttrType, LDAP_DATA_TYPE_DN) == 0) {
  1206. signatures = objectDNtoObjectSignatures(objclass, objects);
  1207. } else {
  1208. /* We have the full member list, create a new query that
  1209. * will request the unique modification attributes for all
  1210. * members in a single shot. With this data we can construct
  1211. * the list of object signatures */
  1212. signatures = resolveObjectsFromAttributes(objclass, objects, lppAttr, company);
  1213. }
  1214. return signatures;
  1215. }
  1216. objectsignature_t LDAPUserPlugin::resolveName(objectclass_t objclass, const string &name, const objectid_t &company)
  1217. {
  1218. list<string> objects;
  1219. std::unique_ptr<attrArray> attrs(new attrArray(6));
  1220. std::unique_ptr<signatures_t> signatures;
  1221. const char *loginname_attr = m_config->GetSetting("ldap_loginname_attribute", "", NULL);
  1222. const char *groupname_attr = m_config->GetSetting("ldap_groupname_attribute", "", NULL);
  1223. const char *dyngroupname_attr = m_config->GetSetting("ldap_dynamicgroupname_attribute", "", NULL);
  1224. const char *companyname_attr = m_config->GetSetting("ldap_companyname_attribute", "", NULL);
  1225. const char *addresslistname_attr = m_config->GetSetting("ldap_addresslist_name_attribute", "", NULL);
  1226. if (company.id.empty()) {
  1227. LOG_PLUGIN_DEBUG("%s Class %x, Name %s", __FUNCTION__, objclass, name.c_str());
  1228. } else {
  1229. LOG_PLUGIN_DEBUG("%s Class %x, Name %s, Company %s", __FUNCTION__, objclass, name.c_str(), company.id.c_str());
  1230. }
  1231. switch (objclass) {
  1232. case OBJECTCLASS_UNKNOWN:
  1233. if (loginname_attr)
  1234. attrs->add(loginname_attr);
  1235. if (groupname_attr)
  1236. attrs->add(groupname_attr);
  1237. if (dyngroupname_attr)
  1238. attrs->add(dyngroupname_attr);
  1239. if (companyname_attr)
  1240. attrs->add(companyname_attr);
  1241. if (addresslistname_attr)
  1242. attrs->add(addresslistname_attr);
  1243. break;
  1244. case OBJECTCLASS_USER:
  1245. if (loginname_attr)
  1246. attrs->add(loginname_attr);
  1247. break;
  1248. case ACTIVE_USER:
  1249. case NONACTIVE_USER:
  1250. case NONACTIVE_ROOM:
  1251. case NONACTIVE_EQUIPMENT:
  1252. case NONACTIVE_CONTACT:
  1253. if (loginname_attr)
  1254. attrs->add(loginname_attr);
  1255. break;
  1256. case OBJECTCLASS_DISTLIST:
  1257. if (groupname_attr)
  1258. attrs->add(groupname_attr);
  1259. if (dyngroupname_attr)
  1260. attrs->add(dyngroupname_attr);
  1261. break;
  1262. case DISTLIST_GROUP:
  1263. case DISTLIST_SECURITY:
  1264. if (groupname_attr)
  1265. attrs->add(groupname_attr);
  1266. break;
  1267. case DISTLIST_DYNAMIC:
  1268. if (dyngroupname_attr)
  1269. attrs->add(dyngroupname_attr);
  1270. break;
  1271. case OBJECTCLASS_CONTAINER:
  1272. if (companyname_attr)
  1273. attrs->add(companyname_attr);
  1274. if (addresslistname_attr)
  1275. attrs->add(addresslistname_attr);
  1276. break;
  1277. case CONTAINER_COMPANY:
  1278. if (companyname_attr)
  1279. attrs->add(companyname_attr);
  1280. break;
  1281. case CONTAINER_ADDRESSLIST:
  1282. if (addresslistname_attr)
  1283. attrs->add(addresslistname_attr);
  1284. break;
  1285. default:
  1286. throw runtime_error(string("resolveName: request for unknown object type"));
  1287. }
  1288. if (attrs->empty())
  1289. throw runtime_error(string("Unable to resolve name with no attributes"));
  1290. objects.push_back(m_iconvrev->convert(name));
  1291. signatures = resolveObjectsFromAttributes(objclass, objects, attrs->get(), company);
  1292. if (!signatures.get() || signatures->empty())
  1293. throw objectnotfound(name+" not found in LDAP");
  1294. // we can only resolve one name. caller should be more specific
  1295. if (signatures->size() > 1)
  1296. throw collision_error(name + " found " + stringify(signatures->size()) + " times in LDAP");
  1297. if (!OBJECTCLASS_COMPARE(signatures->front().id.objclass, objclass))
  1298. throw objectnotfound("No object has been found with name " + name);
  1299. return signatures->front();
  1300. }
  1301. objectsignature_t LDAPUserPlugin::authenticateUser(const string &username, const string &password, const objectid_t &company)
  1302. {
  1303. struct timeval tstart, tend;
  1304. const char *authmethod = m_config->GetSetting("ldap_authentication_method");
  1305. objectsignature_t id;
  1306. LONGLONG llelapsedtime;
  1307. gettimeofday(&tstart, NULL);
  1308. try {
  1309. if (!strcasecmp(authmethod, "password")) {
  1310. id = authenticateUserPassword(username, password, company);
  1311. } else {
  1312. id = authenticateUserBind(username, password, company);
  1313. }
  1314. } catch (...) {
  1315. m_lpStatsCollector->Increment(SCN_LDAP_AUTH_DENIED);
  1316. throw;
  1317. }
  1318. gettimeofday(&tend, NULL);
  1319. llelapsedtime = difftimeval(&tstart,&tend);
  1320. m_lpStatsCollector->Increment(SCN_LDAP_AUTH_LOGINS);
  1321. m_lpStatsCollector->Increment(SCN_LDAP_AUTH_TIME, llelapsedtime);
  1322. m_lpStatsCollector->Max(SCN_LDAP_AUTH_TIME_MAX, llelapsedtime);
  1323. m_lpStatsCollector->Avg(SCN_LDAP_AUTH_TIME_AVG, llelapsedtime);
  1324. return id;
  1325. }
  1326. objectsignature_t LDAPUserPlugin::authenticateUserBind(const string &username, const string &password, const objectid_t &company)
  1327. {
  1328. LDAP* ld = NULL;
  1329. string dn;
  1330. objectsignature_t signature;
  1331. try {
  1332. signature = resolveName(ACTIVE_USER, username, company);
  1333. /*
  1334. * ZCP-11720: When looking for users, explicitly request
  1335. * skipping the cache.
  1336. */
  1337. dn = objectUniqueIDtoObjectDN(signature.id, false);
  1338. ld = ConnectLDAP(dn.c_str(), m_iconvrev->convert(password).c_str());
  1339. } catch (exception &e) {
  1340. throw login_error((string)"Trying to authenticate failed: " + e.what() + (string)"; username = " + username);
  1341. }
  1342. if(ld == NULL) {
  1343. throw runtime_error("Trying to authenticate failed: connection failed");
  1344. }
  1345. if (ldap_unbind_s(ld) == -1)
  1346. ec_log_err("LDAP unbind failed");
  1347. return signature;
  1348. }
  1349. objectsignature_t LDAPUserPlugin::authenticateUserPassword(const string &username, const string &password, const objectid_t &company)
  1350. {
  1351. auto_free_ldap_message res;
  1352. objectdetails_t d;
  1353. LDAPMessage* entry = NULL;
  1354. string ldap_basedn;
  1355. string ldap_filter;
  1356. string strCryptedpw;
  1357. string strPasswordConverted;
  1358. objectsignature_t signature;
  1359. std::unique_ptr<attrArray> request_attrs(new attrArray(4));
  1360. CONFIG_TO_ATTR(request_attrs, loginname_attr, "ldap_loginname_attribute" );
  1361. CONFIG_TO_ATTR(request_attrs, password_attr, "ldap_password_attribute");
  1362. CONFIG_TO_ATTR(request_attrs, unique_attr, "ldap_user_unique_attribute");
  1363. CONFIG_TO_ATTR(request_attrs, nonactive_attr, "ldap_nonactive_attribute");
  1364. ldap_basedn = getSearchBase(company);
  1365. ldap_filter = getObjectSearchFilter(objectid_t(m_iconvrev->convert(username), ACTIVE_USER), loginname_attr);
  1366. /* LDAP filter does not exist, user does not exist, user cannot login */
  1367. if (ldap_filter.empty())
  1368. throw objectnotfound("LDAP filter is empty");
  1369. my_ldap_search_s(
  1370. (char *)ldap_basedn.c_str(), LDAP_SCOPE_SUBTREE,
  1371. (char *)ldap_filter.c_str(), (char **)request_attrs->get(),
  1372. FETCH_ATTR_VALS, &~res);
  1373. switch(ldap_count_entries(m_ldap, res)) {
  1374. case 0:
  1375. throw login_error("Trying to authenticate failed: wrong username or password");
  1376. case 1:
  1377. break;
  1378. default:
  1379. throw login_error("Trying to authenticate failed: unknown error");
  1380. }
  1381. entry = ldap_first_entry(m_ldap, res);
  1382. if(entry == NULL) {
  1383. throw runtime_error("ldap_dn: broken.");
  1384. }
  1385. FOREACH_ATTR(entry) {
  1386. if (loginname_attr && !strcasecmp(att, loginname_attr)) {
  1387. d.SetPropString(OB_PROP_S_LOGIN, m_iconv->convert(getLDAPAttributeValue(att, entry)));
  1388. } else if (password_attr && !strcasecmp(att, password_attr)) {
  1389. d.SetPropString(OB_PROP_S_PASSWORD, getLDAPAttributeValue(att, entry));
  1390. }
  1391. if (unique_attr && !strcasecmp(att, unique_attr)) {
  1392. signature.id.id = getLDAPAttributeValue(att, entry);
  1393. signature.id.objclass = ACTIVE_USER; // only users can authenticate
  1394. }
  1395. if (nonactive_attr && !strcasecmp(att, nonactive_attr)) {
  1396. if (parseBool(getLDAPAttributeValue(att, entry).c_str()))
  1397. throw login_error("Cannot login as nonactive user");
  1398. }
  1399. }
  1400. END_FOREACH_ATTR
  1401. strCryptedpw = d.GetPropString(OB_PROP_S_PASSWORD);
  1402. strPasswordConverted = m_iconvrev->convert(password).c_str();
  1403. if (strCryptedpw.empty())
  1404. throw login_error("Trying to authenticate failed: password field is empty or unreadable");
  1405. if (signature.id.id.empty())
  1406. throw login_error("Trying to authenticate failed: uniqueid is empty or unreadable");
  1407. if (!strncasecmp("{CRYPT}", strCryptedpw.c_str(), 7)) {
  1408. if(checkPassword(PASSWORD_CRYPT, strPasswordConverted.c_str(), &(strCryptedpw.c_str()[7])) != 0)
  1409. throw login_error("Trying to authenticate failed: wrong username or password");
  1410. } else if (!strncasecmp("{MD5}", strCryptedpw.c_str(), 5)) {
  1411. if(checkPassword(PASSWORD_MD5, strPasswordConverted.c_str(), &(strCryptedpw.c_str()[5])) != 0)
  1412. throw login_error("Trying to authenticate failed: wrong username or password");
  1413. } else if (!strncasecmp("{SMD5}", strCryptedpw.c_str(), 6)) {
  1414. if(checkPassword(PASSWORD_SMD5, strPasswordConverted.c_str(), &(strCryptedpw.c_str()[6])) != 0)
  1415. throw login_error("Trying to authenticate failed: wrong username or password");
  1416. } else if (!strncasecmp("{SSHA}", strCryptedpw.c_str(), 6)) {
  1417. if(checkPassword(PASSWORD_SSHA, strPasswordConverted.c_str(), &(strCryptedpw.c_str()[6])) != 0)
  1418. throw login_error("Trying to authenticate failed: wrong username or password");
  1419. } else if (!strncasecmp("{SHA}", strCryptedpw.c_str(), 5)) {
  1420. if(checkPassword(PASSWORD_SHA, strPasswordConverted.c_str(), &(strCryptedpw.c_str()[5])) != 0)
  1421. throw login_error("Trying to authenticate failed: wrong username or password");
  1422. } else if(!strncasecmp("{MD5CRYPT}", strCryptedpw.c_str(), 10)) {
  1423. throw login_error("Trying to authenticate failed: unsupported encryption scheme");
  1424. } else {
  1425. if(strcmp(strCryptedpw.c_str(), strPasswordConverted.c_str()) != 0) { //Plain password
  1426. throw login_error("Trying to authenticate failed: wrong username or password");
  1427. }
  1428. }
  1429. return signature;
  1430. }
  1431. std::unique_ptr<signatures_t>
  1432. LDAPUserPlugin::getAllObjects(const objectid_t &company, objectclass_t objclass)
  1433. {
  1434. string companyDN;
  1435. if (!company.id.empty()) {
  1436. LOG_PLUGIN_DEBUG("%s Company %s, Class %x", __FUNCTION__, company.id.c_str(), objclass);
  1437. companyDN = getSearchBase(company);
  1438. } else {
  1439. LOG_PLUGIN_DEBUG("%s Class %x", __FUNCTION__, objclass);
  1440. }
  1441. return getAllObjectsByFilter(getSearchBase(company), LDAP_SCOPE_SUBTREE, getSearchFilter(objclass), companyDN, true);
  1442. }
  1443. string LDAPUserPlugin::getLDAPAttributeValue(char *attribute, LDAPMessage *entry) {
  1444. list<string> l = getLDAPAttributeValues(attribute, entry);
  1445. if (!l.empty())
  1446. return *(l.begin());
  1447. else
  1448. return string();
  1449. }
  1450. list<string> LDAPUserPlugin::getLDAPAttributeValues(char *attribute, LDAPMessage *entry) {
  1451. list<string> r;
  1452. string s;
  1453. auto_free_ldap_berval berval(ldap_get_values_len(m_ldap, entry, attribute));
  1454. if (berval != NULL)
  1455. for (int i = 0; berval[i] != NULL; ++i) {
  1456. s.assign(berval[i]->bv_val, berval[i]->bv_len);
  1457. r.push_back(std::move(s));
  1458. }
  1459. return r;
  1460. }
  1461. std::string LDAPUserPlugin::GetLDAPEntryDN(LDAPMessage *entry)
  1462. {
  1463. std::string dn;
  1464. auto_free_ldap_attribute ptrDN(ldap_get_dn(m_ldap, entry));
  1465. if (*ptrDN != '\0')
  1466. dn = ptrDN;
  1467. return dn;
  1468. }
  1469. struct postaction {
  1470. objectid_t objectid; //!< object to act on in the resolved map
  1471. objectclass_t objclass; //!< resolveObject(s)FromAttributeType 1st parameter
  1472. string ldap_attr; //!< resolveObjectFromAttributeType 2nd parameter
  1473. list<string> ldap_attrs; //!< resolveObjectsFromAttributeType 2nd parameter
  1474. const char *relAttr; //!< resolveObject(s)FromAttributeType 3rd parameter
  1475. const char *relAttrType; //!< resolveObject(s)FromAttributeType 4th parameter
  1476. property_key_t propname; //!< object prop to add/set from the result
  1477. std::string result_attr; //!< optional: attribute to use from resulting object(s), if none then unique id
  1478. };
  1479. std::unique_ptr<std::map<objectid_t, objectdetails_t> >
  1480. LDAPUserPlugin::getObjectDetails(const std::list<objectid_t> &objectids)
  1481. {
  1482. std::unique_ptr<std::map<objectid_t, objectdetails_t> > mapdetails(new std::map<objectid_t, objectdetails_t>);
  1483. auto_free_ldap_message res;
  1484. LOG_PLUGIN_DEBUG("%s N=%d", __FUNCTION__, (int)objectids.size() );
  1485. /* That was easy ... */
  1486. if (objectids.empty())
  1487. return mapdetails;
  1488. bool bCutOff = false;
  1489. std::unique_ptr<dn_cache_t> lpCompanyCache;
  1490. string ldap_filter;
  1491. string ldap_basedn;
  1492. string ldap_attr;
  1493. list<string> ldap_attrs;
  1494. string strDN;
  1495. list<postaction> lPostActions;
  1496. set<objectid_t> setObjectIds;
  1497. list<configsetting_t> lExtraAttrs = m_config->GetSettingGroup(CONFIGGROUP_PROPMAP);
  1498. std::unique_ptr<attrArray> request_attrs(new attrArray(33 + lExtraAttrs.size()));
  1499. CONFIG_TO_ATTR(request_attrs, object_attr, "ldap_object_type_attribute");
  1500. CONFIG_TO_ATTR(request_attrs, user_unique_attr, "ldap_user_unique_attribute");
  1501. CONFIG_TO_ATTR(request_attrs, user_unique_attr_type, "ldap_user_unique_attribute_type");
  1502. CONFIG_TO_ATTR(request_attrs, user_fullname_attr, "ldap_fullname_attribute");
  1503. CONFIG_TO_ATTR(request_attrs, loginname_attr, "ldap_loginname_attribute");
  1504. CONFIG_TO_ATTR(request_attrs, password_attr, "ldap_password_attribute");
  1505. CONFIG_TO_ATTR(request_attrs, emailaddress_attr, "ldap_emailaddress_attribute");
  1506. CONFIG_TO_ATTR(request_attrs, emailaliases_attr, "ldap_emailaliases_attribute");
  1507. CONFIG_TO_ATTR(request_attrs, isadmin_attr, "ldap_isadmin_attribute");
  1508. CONFIG_TO_ATTR(request_attrs, nonactive_attr, "ldap_nonactive_attribute");
  1509. CONFIG_TO_ATTR(request_attrs, resource_type_attr, "ldap_resource_type_attribute");
  1510. CONFIG_TO_ATTR(request_attrs, resource_capacity_attr, "ldap_resource_capacity_attribute");
  1511. CONFIG_TO_ATTR(request_attrs, usercert_attr, "ldap_user_certificate_attribute");
  1512. CONFIG_TO_ATTR(request_attrs, sendas_attr, "ldap_sendas_attribute");
  1513. CONFIG_TO_ATTR(request_attrs, group_unique_attr, "ldap_group_unique_attribute");
  1514. CONFIG_TO_ATTR(request_attrs, group_unique_attr_type, "ldap_group_unique_attribute_type");
  1515. CONFIG_TO_ATTR(request_attrs, group_fullname_attr, "ldap_groupname_attribute");
  1516. CONFIG_TO_ATTR(request_attrs, group_security_attr, "ldap_group_security_attribute");
  1517. CONFIG_TO_ATTR(request_attrs, company_unique_attr, "ldap_company_unique_attribute");
  1518. CONFIG_TO_ATTR(request_attrs, company_unique_attr_type, "ldap_company_unique_attribute_type");
  1519. CONFIG_TO_ATTR(request_attrs, company_fullname_attr, "ldap_companyname_attribute");
  1520. CONFIG_TO_ATTR(request_attrs, sysadmin_attr, "ldap_company_system_admin_attribute");
  1521. CONFIG_TO_ATTR(request_attrs, sysadmin_attr_type, "ldap_company_system_admin_attribute_type");
  1522. CONFIG_TO_ATTR(request_attrs, sysadmin_attr_rel, "ldap_company_system_admin_relation_attribute");
  1523. CONFIG_TO_ATTR(request_attrs, addresslist_unique_attr, "ldap_addresslist_unique_attribute");
  1524. CONFIG_TO_ATTR(request_attrs, addresslist_unique_attr_type, "ldap_addresslist_unique_attribute_type");
  1525. CONFIG_TO_ATTR(request_attrs, addresslist_name_attr, "ldap_addresslist_name_attribute");
  1526. CONFIG_TO_ATTR(request_attrs, dynamicgroup_unique_attr, "ldap_dynamicgroup_unique_attribute");
  1527. CONFIG_TO_ATTR(request_attrs, dynamicgroup_unique_attr_type, "ldap_dynamicgroup_unique_attribute_type");
  1528. CONFIG_TO_ATTR(request_attrs, dynamicgroup_name_attr, "ldap_dynamicgroup_name_attribute");
  1529. CONFIG_TO_ATTR(request_attrs, ldap_addressbook_hide_attr, "ldap_addressbook_hide_attribute");
  1530. CONFIG_TO_ATTR(request_attrs, user_server_attr, "ldap_user_server_attribute");
  1531. CONFIG_TO_ATTR(request_attrs, company_server_attr, "ldap_company_server_attribute");
  1532. if (!m_bDistributed) {
  1533. user_server_attr = NULL;
  1534. company_server_attr = NULL;
  1535. }
  1536. for (const auto &c : lExtraAttrs)
  1537. request_attrs->add(c.szValue);
  1538. unsigned int ulCutoff = atoui(m_config->GetSetting("ldap_filter_cutoff_elements"));
  1539. /*
  1540. * When working in multi-company mode we need to determine to which company
  1541. * this object belongs. To do this efficiently we are using the cache for
  1542. * resolving the company based on the DN.
  1543. */
  1544. if (m_bHosted)
  1545. lpCompanyCache = m_lpCache->getObjectDNCache(this, CONTAINER_COMPANY);
  1546. ldap_basedn = getSearchBase();
  1547. /*
  1548. * Optimize the filter, by sorting the objectids we made sure that all
  1549. * object classes are grouped together. We then only need to create the
  1550. * objectclass filter once for each class and add the id filter after that.
  1551. * This makes the entire ldap filter much shorter and can be be handled much
  1552. * better by LDAP.
  1553. */
  1554. setObjectIds.insert(objectids.begin(), objectids.end());
  1555. /*
  1556. * This is purely an optimization: to make sure we don't send huge 5000-item filters
  1557. * to the LDAP Server (which may be slow), we have a cutoff point at which we simply
  1558. * retrieve all LDAP data. This is sometimes (depending on the type of LDAP Server)
  1559. * much, much faster (factor of 40 has been seen), with the minor expense of receiving
  1560. * more data than actually needed. This means we're trading minor local processing time
  1561. * and network activity for a much lower LDAP-server processing overhead.
  1562. */
  1563. if (ulCutoff != 0 && setObjectIds.size() >= ulCutoff) {
  1564. // find all the different object classes in the objectids list, and make an or filter based on that
  1565. objectclass_t objclass = (objectclass_t)-1; // set to something invalid
  1566. ldap_filter = "(|";
  1567. for (const auto &id : setObjectIds)
  1568. if (objclass != id.objclass) {
  1569. ldap_filter += getSearchFilter(id.objclass);
  1570. objclass = id.objclass;
  1571. }
  1572. ldap_filter += ")";
  1573. bCutOff = true;
  1574. } else {
  1575. ldap_filter = "(|";
  1576. for (auto iter = setObjectIds.cbegin(); iter != setObjectIds.cend(); /* no increment */) {
  1577. objectclass_t objclass = iter->objclass;
  1578. ldap_filter += "(&" + getSearchFilter(iter->objclass) + "(|";
  1579. while (iter != setObjectIds.cend() && objclass == iter->objclass) {
  1580. switch (objclass) {
  1581. case OBJECTCLASS_USER:
  1582. case ACTIVE_USER:
  1583. case NONACTIVE_USER:
  1584. case NONACTIVE_ROOM:
  1585. case NONACTIVE_EQUIPMENT:
  1586. case NONACTIVE_CONTACT:
  1587. ldap_filter += getSearchFilter(iter->id, user_unique_attr, user_unique_attr_type);
  1588. break;
  1589. case OBJECTCLASS_DISTLIST:
  1590. ldap_filter += getSearchFilter(iter->id, group_unique_attr, group_unique_attr_type);
  1591. ldap_filter += getSearchFilter(iter->id, dynamicgroup_unique_attr, dynamicgroup_unique_attr_type);
  1592. break;
  1593. case DISTLIST_GROUP:
  1594. case DISTLIST_SECURITY:
  1595. ldap_filter += getSearchFilter(iter->id, group_unique_attr, group_unique_attr_type);
  1596. break;
  1597. case DISTLIST_DYNAMIC:
  1598. ldap_filter += getSearchFilter(iter->id, dynamicgroup_unique_attr, dynamicgroup_unique_attr_type);
  1599. break;
  1600. case OBJECTCLASS_CONTAINER:
  1601. ldap_filter += getSearchFilter(iter->id, company_unique_attr, company_unique_attr_type);
  1602. ldap_filter += getSearchFilter(iter->id, addresslist_unique_attr, addresslist_unique_attr_type);
  1603. break;
  1604. case CONTAINER_COMPANY:
  1605. ldap_filter += getSearchFilter(iter->id, company_unique_attr, company_unique_attr_type);
  1606. break;
  1607. case CONTAINER_ADDRESSLIST:
  1608. ldap_filter += getSearchFilter(iter->id, addresslist_unique_attr, addresslist_unique_attr_type);
  1609. break;
  1610. case OBJECTCLASS_UNKNOWN:
  1611. ldap_filter += getSearchFilter(iter->id, user_unique_attr, user_unique_attr_type);
  1612. ldap_filter += getSearchFilter(iter->id, group_unique_attr, group_unique_attr_type);
  1613. ldap_filter += getSearchFilter(iter->id, dynamicgroup_unique_attr, dynamicgroup_unique_attr_type);
  1614. ldap_filter += getSearchFilter(iter->id, company_unique_attr, company_unique_attr_type);
  1615. ldap_filter += getSearchFilter(iter->id, addresslist_unique_attr, addresslist_unique_attr_type);
  1616. break;
  1617. default:
  1618. ec_log_crit("Incorrect object class %d for item \"%s\"", iter->objclass, iter->id.c_str());
  1619. continue;
  1620. }
  1621. ++iter;
  1622. }
  1623. ldap_filter += "))";
  1624. }
  1625. ldap_filter += ")";
  1626. }
  1627. FOREACH_PAGING_SEARCH((char *)ldap_basedn.c_str(), LDAP_SCOPE_SUBTREE,
  1628. (char *)ldap_filter.c_str(), (char **)request_attrs->get(),
  1629. FETCH_ATTR_VALS, res)
  1630. {
  1631. FOREACH_ENTRY(res) {
  1632. strDN = GetLDAPEntryDN(entry);
  1633. objectid_t objectid = GetObjectIdForEntry(entry);
  1634. objectdetails_t sObjDetails = objectdetails_t(objectid.objclass);
  1635. /* Default value, will be overridden later */
  1636. if (sObjDetails.GetClass() == CONTAINER_COMPANY)
  1637. sObjDetails.SetPropObject(OB_PROP_O_SYSADMIN, objectid_t("SYSTEM", ACTIVE_USER));
  1638. // when cutoff is used, filter only the requested entries.
  1639. if(bCutOff == true && setObjectIds.find(objectid) == setObjectIds.end())
  1640. continue;
  1641. FOREACH_ATTR(entry) {
  1642. /*
  1643. * We check the attribute for every match possible,
  1644. * because an attribute can be used in multiple config options
  1645. */
  1646. if (ldap_addressbook_hide_attr && !strcasecmp(att, ldap_addressbook_hide_attr)) {
  1647. ldap_attr = getLDAPAttributeValue(att, entry);
  1648. sObjDetails.SetPropBool(OB_PROP_B_AB_HIDDEN, parseBool(ldap_attr.c_str()));
  1649. }
  1650. for (const auto &se : lExtraAttrs) {
  1651. unsigned int ulPropTag;
  1652. /*
  1653. * The value should be set to something, as protection to make sure
  1654. * the name is a property tag all names should be prefixed with '0x'.
  1655. */
  1656. if (strcasecmp(se.szValue, att) != 0 || strncasecmp(se.szName, "0x", strlen("0x")) != 0)
  1657. continue;
  1658. ulPropTag = xtoi(se.szName);
  1659. /* Handle property actions */
  1660. switch (PROP_ID(ulPropTag)) {
  1661. // this property is only supported on ADS and OpenLDAP 2.4+ with slapo-memberof enabled
  1662. case 0x8008: /* PR_EMS_AB_IS_MEMBER_OF_DL */
  1663. case 0x800E: /* PR_EMS_AB_REPORTS */
  1664. {
  1665. /*
  1666. * These properties should contain the DN of the object,
  1667. * resolve them to the objectid and them as the PT_MV_BINARY
  1668. * version, the server will create the EntryIDs so the client
  1669. * can use the openEntry() to get the correct references.
  1670. */
  1671. ulPropTag = (ulPropTag & 0xffff0000) | 0x1102;
  1672. postaction p;
  1673. p.objectid = objectid;
  1674. if(ulPropTag == 0x800E1102)
  1675. p.objclass = OBJECTCLASS_USER;
  1676. else
  1677. p.objclass = OBJECTCLASS_DISTLIST;
  1678. p.ldap_attrs = getLDAPAttributeValues(att, entry);
  1679. p.relAttr = "dn";
  1680. p.relAttrType = "dn";
  1681. p.propname = (property_key_t)ulPropTag;
  1682. lPostActions.push_back(std::move(p));
  1683. break;
  1684. }
  1685. case 0x3A4E: /* PR_MANAGER_NAME */
  1686. /* Rename to PR_EMS_AB_MANAGER */
  1687. ulPropTag = 0x8005001E;
  1688. /* fallthru */
  1689. case 0x8005: /* PR_EMS_AB_MANAGER */
  1690. case 0x800C: /* PR_EMS_AB_OWNER */
  1691. {
  1692. /*
  1693. * These properties should contain the DN of the object,
  1694. * resolve it to the objectid and store it as the PT_BINARY
  1695. * version, the server will create the EntryID so the client
  1696. * can use OpenEntry() to get the correct reference.
  1697. */
  1698. ulPropTag = (ulPropTag & 0xffff0000) | 0x0102;
  1699. postaction p;
  1700. p.objectid = objectid;
  1701. p.objclass = OBJECTCLASS_USER;
  1702. p.ldap_attr = getLDAPAttributeValue(att, entry);
  1703. p.relAttr = "dn";
  1704. p.relAttrType = "dn";
  1705. p.propname = (property_key_t)ulPropTag;
  1706. lPostActions.push_back(std::move(p));
  1707. break;
  1708. }
  1709. case 0x3A30: /* PR_ASSISTANT */
  1710. {
  1711. /*
  1712. * These properties should contain the full name of the object,
  1713. * the client won't need to resolve them to a Address Book entry
  1714. * so a fullname will be sufficient.
  1715. */
  1716. ulPropTag = 0x3A30001E;
  1717. postaction p;
  1718. p.objectid = objectid;
  1719. p.objclass = OBJECTCLASS_USER;
  1720. p.ldap_attr = getLDAPAttributeValue(att, entry);
  1721. p.relAttr = "dn";
  1722. p.relAttrType = "dn";
  1723. p.result_attr = m_config->GetSetting("ldap_fullname_attribute");
  1724. p.propname = (property_key_t)ulPropTag;
  1725. lPostActions.push_back(std::move(p));
  1726. break;
  1727. }
  1728. default:
  1729. ldap_attrs = getLDAPAttributeValues(att, entry);
  1730. if ((ulPropTag & 0xFFFE) == 0x1E) // if (PROP_TYPE(ulPropTag) == PT_STRING8 || PT_UNICODE)
  1731. for (auto &i : ldap_attrs)
  1732. i = m_iconv->convert(i);
  1733. if (ulPropTag & 0x1000) /* MV_FLAG */
  1734. sObjDetails.SetPropListString((property_key_t)ulPropTag, ldap_attrs);
  1735. else
  1736. sObjDetails.SetPropString((property_key_t)ulPropTag, ldap_attrs.front());
  1737. break;
  1738. }
  1739. }
  1740. /* Objectclass specific properties */
  1741. switch (sObjDetails.GetClass()) {
  1742. case ACTIVE_USER:
  1743. case NONACTIVE_USER:
  1744. case NONACTIVE_ROOM:
  1745. case NONACTIVE_EQUIPMENT:
  1746. case NONACTIVE_CONTACT:
  1747. if (loginname_attr && !strcasecmp(att, loginname_attr)) {
  1748. ldap_attr = m_iconv->convert(getLDAPAttributeValue(att, entry));
  1749. sObjDetails.SetPropString(OB_PROP_S_LOGIN, ldap_attr);
  1750. }
  1751. if (user_fullname_attr && !strcasecmp(att, user_fullname_attr)) {
  1752. ldap_attr = m_iconv->convert(getLDAPAttributeValue(att, entry));
  1753. sObjDetails.SetPropString(OB_PROP_S_FULLNAME, ldap_attr);
  1754. }
  1755. if (password_attr && !strcasecmp(att, password_attr)) {
  1756. ldap_attr = getLDAPAttributeValue(att, entry);
  1757. sObjDetails.SetPropString(OB_PROP_S_PASSWORD, ldap_attr);
  1758. }
  1759. if (emailaddress_attr && !strcasecmp(att, emailaddress_attr)) {
  1760. ldap_attr = m_iconv->convert(getLDAPAttributeValue(att, entry));
  1761. sObjDetails.SetPropString(OB_PROP_S_EMAIL, ldap_attr);
  1762. }
  1763. if (emailaliases_attr && !strcasecmp(att, emailaliases_attr)) {
  1764. ldap_attrs = getLDAPAttributeValues(att, entry);
  1765. sObjDetails.SetPropListString(OB_PROP_LS_ALIASES, ldap_attrs);
  1766. }
  1767. if (isadmin_attr && !strcasecmp(att, isadmin_attr)) {
  1768. ldap_attr = getLDAPAttributeValue(att, entry);
  1769. sObjDetails.SetPropInt(OB_PROP_I_ADMINLEVEL, min(2, atoi(ldap_attr.c_str())));
  1770. }
  1771. if (resource_type_attr && !strcasecmp(att, resource_type_attr)) {
  1772. ldap_attr = m_iconv->convert(getLDAPAttributeValue(att, entry));
  1773. sObjDetails.SetPropString(OB_PROP_S_RESOURCE_DESCRIPTION, ldap_attr);
  1774. }
  1775. if (resource_capacity_attr && !strcasecmp(att, resource_capacity_attr)) {
  1776. ldap_attr = getLDAPAttributeValue(att, entry);
  1777. sObjDetails.SetPropInt(OB_PROP_I_RESOURCE_CAPACITY, atoi(ldap_attr.c_str()));
  1778. }
  1779. if (usercert_attr && !strcasecmp(att, usercert_attr)) {
  1780. ldap_attrs = getLDAPAttributeValues(att, entry);
  1781. sObjDetails.SetPropListString(OB_PROP_LS_CERTIFICATE, ldap_attrs);
  1782. }
  1783. if (sendas_attr && !strcasecmp(att, sendas_attr)) {
  1784. postaction p;
  1785. p.objectid = objectid;
  1786. p.objclass = OBJECTCLASS_UNKNOWN;
  1787. p.ldap_attrs = getLDAPAttributeValues(att, entry);
  1788. p.relAttr = m_config->GetSetting("ldap_sendas_relation_attribute");
  1789. p.relAttrType = m_config->GetSetting("ldap_sendas_attribute_type");
  1790. if (p.relAttr == NULL || p.relAttr[0] == '\0')
  1791. p.relAttr = m_config->GetSetting("ldap_user_unique_attribute");
  1792. p.propname = OB_PROP_LO_SENDAS;
  1793. lPostActions.push_back(std::move(p));
  1794. }
  1795. if (user_server_attr && !strcasecmp(att, user_server_attr)) {
  1796. ldap_attr = getLDAPAttributeValue(att, entry);
  1797. sObjDetails.SetPropString(OB_PROP_S_SERVERNAME, ldap_attr);
  1798. }
  1799. break;
  1800. case DISTLIST_GROUP:
  1801. case DISTLIST_SECURITY:
  1802. if (group_fullname_attr && !strcasecmp(att, group_fullname_attr)) {
  1803. ldap_attr = m_iconv->convert(getLDAPAttributeValue(att, entry));
  1804. sObjDetails.SetPropString(OB_PROP_S_LOGIN, ldap_attr);
  1805. sObjDetails.SetPropString(OB_PROP_S_FULLNAME, ldap_attr);
  1806. }
  1807. if (emailaddress_attr && !strcasecmp(att, emailaddress_attr)) {
  1808. ldap_attr = m_iconv->convert(getLDAPAttributeValue(att, entry));
  1809. sObjDetails.SetPropString(OB_PROP_S_EMAIL, ldap_attr);
  1810. }
  1811. if (emailaliases_attr && !strcasecmp(att, emailaliases_attr)) {
  1812. ldap_attrs = getLDAPAttributeValues(att, entry);
  1813. sObjDetails.SetPropListString(OB_PROP_LS_ALIASES, ldap_attrs);
  1814. }
  1815. if (sendas_attr && !strcasecmp(att, sendas_attr)) {
  1816. postaction p;
  1817. p.objectid = objectid;
  1818. p.objclass = OBJECTCLASS_UNKNOWN;
  1819. p.ldap_attrs = getLDAPAttributeValues(att, entry);
  1820. p.relAttr = m_config->GetSetting("ldap_sendas_relation_attribute");
  1821. p.relAttrType = m_config->GetSetting("ldap_sendas_attribute_type");
  1822. if (p.relAttr == NULL || p.relAttr[0] == '\0')
  1823. p.relAttr = m_config->GetSetting("ldap_user_unique_attribute");
  1824. p.propname = OB_PROP_LO_SENDAS;
  1825. lPostActions.push_back(std::move(p));
  1826. }
  1827. break;
  1828. case DISTLIST_DYNAMIC:
  1829. if (dynamicgroup_name_attr && !strcasecmp(att, dynamicgroup_name_attr)) {
  1830. ldap_attr = m_iconv->convert(getLDAPAttributeValue(att, entry));
  1831. sObjDetails.SetPropString(OB_PROP_S_LOGIN, ldap_attr);
  1832. sObjDetails.SetPropString(OB_PROP_S_FULLNAME, ldap_attr);
  1833. }
  1834. if (emailaddress_attr && !strcasecmp(att, emailaddress_attr)) {
  1835. ldap_attr = m_iconv->convert(getLDAPAttributeValue(att, entry));
  1836. sObjDetails.SetPropString(OB_PROP_S_EMAIL, ldap_attr);
  1837. }
  1838. if (emailaliases_attr && !strcasecmp(att, emailaliases_attr)) {
  1839. ldap_attrs = getLDAPAttributeValues(att, entry);
  1840. sObjDetails.SetPropListString(OB_PROP_LS_ALIASES, ldap_attrs);
  1841. }
  1842. break;
  1843. case CONTAINER_COMPANY:
  1844. if (company_fullname_attr && !strcasecmp(att, company_fullname_attr)) {
  1845. ldap_attr = m_iconv->convert(getLDAPAttributeValue(att, entry));
  1846. sObjDetails.SetPropString(OB_PROP_S_LOGIN, ldap_attr);
  1847. sObjDetails.SetPropString(OB_PROP_S_FULLNAME, ldap_attr);
  1848. }
  1849. if (company_server_attr && !strcasecmp(att, company_server_attr)) {
  1850. ldap_attr = getLDAPAttributeValue(att, entry);
  1851. sObjDetails.SetPropString(OB_PROP_S_SERVERNAME, ldap_attr);
  1852. }
  1853. if (sysadmin_attr && !strcasecmp(att, sysadmin_attr)) {
  1854. postaction p;
  1855. p.objectid = objectid;
  1856. p.objclass = OBJECTCLASS_USER;
  1857. p.ldap_attr = getLDAPAttributeValue(att, entry);
  1858. p.relAttr = sysadmin_attr_rel ? sysadmin_attr_rel : user_unique_attr;
  1859. p.relAttrType = sysadmin_attr_type;
  1860. p.propname = OB_PROP_O_SYSADMIN;
  1861. lPostActions.push_back(std::move(p));
  1862. }
  1863. break;
  1864. case CONTAINER_ADDRESSLIST:
  1865. if (addresslist_name_attr && !strcasecmp(att, addresslist_name_attr)) {
  1866. ldap_attr = m_iconv->convert(getLDAPAttributeValue(att, entry));
  1867. sObjDetails.SetPropString(OB_PROP_S_LOGIN, ldap_attr);
  1868. sObjDetails.SetPropString(OB_PROP_S_FULLNAME, ldap_attr);
  1869. }
  1870. break;
  1871. case OBJECTCLASS_UNKNOWN:
  1872. case OBJECTCLASS_USER:
  1873. case OBJECTCLASS_DISTLIST:
  1874. case OBJECTCLASS_CONTAINER:
  1875. throw runtime_error("getObjectDetails: found unknown object type");
  1876. default:
  1877. break;
  1878. }
  1879. }
  1880. END_FOREACH_ATTR
  1881. if (m_bHosted && sObjDetails.GetClass() != CONTAINER_COMPANY) {
  1882. objectid_t company = m_lpCache->getParentForDN(lpCompanyCache, strDN);
  1883. sObjDetails.SetPropObject(OB_PROP_O_COMPANYID, company);
  1884. }
  1885. if (!objectid.id.empty())
  1886. (*mapdetails)[objectid] = sObjDetails;
  1887. }
  1888. END_FOREACH_ENTRY
  1889. }
  1890. END_FOREACH_LDAP_PAGING
  1891. // paged loop ended, so now we can process the postactions.
  1892. for (const auto &p : lPostActions) {
  1893. map<objectid_t, objectdetails_t>::iterator o = mapdetails->find(p.objectid);
  1894. if (o == mapdetails->cend()) {
  1895. // this should never happen, but only some details will be missing, not the end of the world.
  1896. ec_log_crit("No object \"%s\" found for postaction", p.objectid.id.c_str());
  1897. continue;
  1898. }
  1899. if (p.ldap_attr.empty()) {
  1900. // list, so use AddPropObject()
  1901. try {
  1902. // Currently not supported for multivalued arrays. This would require multiple calls to objectUniqueIDtoAttributeData
  1903. // which is inefficient, and it is currently unused.
  1904. assert(p.result_attr.empty());
  1905. std::unique_ptr<signatures_t> lstSignatures = resolveObjectsFromAttributeType(p.objclass, p.ldap_attrs, p.relAttr, p.relAttrType);
  1906. if (lstSignatures->size() != p.ldap_attrs.size()) {
  1907. // try to rat out the object causing the failed ldap query
  1908. ec_log_err("Not all objects in relation found for object \"%s\"", o->second.GetPropString(OB_PROP_S_LOGIN).c_str());
  1909. }
  1910. for (const auto &sig : *lstSignatures)
  1911. o->second.AddPropObject(p.propname, sig.id);
  1912. } catch (ldap_error &e) {
  1913. if(!LDAP_NAME_ERROR(e.GetLDAPError()))
  1914. throw;
  1915. } catch (std::exception &e) {
  1916. ec_log_err("Unable to resolve object from relational attribute type \"%s\"", p.relAttr);
  1917. }
  1918. } else {
  1919. // string, so use SetPropObject
  1920. try {
  1921. objectsignature_t signature;
  1922. signature = resolveObjectFromAttributeType(p.objclass, p.ldap_attr, p.relAttr, p.relAttrType);
  1923. if (!p.result_attr.empty()) {
  1924. // String type
  1925. try {
  1926. o->second.SetPropString(p.propname, objectUniqueIDtoAttributeData(signature.id, p.result_attr.c_str()));
  1927. } catch (ldap_error &e) {
  1928. if(!LDAP_NAME_ERROR(e.GetLDAPError()))
  1929. throw;
  1930. } catch (std::exception &e) {
  1931. ec_log_err("Unable to get attribute \"%s\" for relation \"%s\" for object \"%s\"", p.result_attr.c_str(), p.ldap_attr.c_str(), o->second.GetPropString(OB_PROP_S_LOGIN).c_str());
  1932. }
  1933. } else {
  1934. // ID type
  1935. if (!signature.id.id.empty())
  1936. o->second.SetPropObject(p.propname, signature.id);
  1937. else
  1938. ec_log_err("Unable to find relation \"%s\" in attribute \"%s\"", p.ldap_attr.c_str(), p.relAttr);
  1939. }
  1940. } catch (ldap_error &e) {
  1941. if(!LDAP_NAME_ERROR(e.GetLDAPError()))
  1942. throw;
  1943. } catch (std::exception &e) {
  1944. ec_log_err("Unable to resolve object from relational attribute type \"%s\"", p.relAttr);
  1945. }
  1946. }
  1947. }
  1948. return mapdetails;
  1949. }
  1950. std::unique_ptr<objectdetails_t>
  1951. LDAPUserPlugin::getObjectDetails(const objectid_t &id)
  1952. {
  1953. std::unique_ptr<std::map<objectid_t, objectdetails_t> > mapDetails;
  1954. list<objectid_t> objectids;
  1955. objectids.push_back(id);
  1956. mapDetails = getObjectDetails(objectids);
  1957. auto iterDetails = mapDetails->find(id);
  1958. if (iterDetails == mapDetails->cend())
  1959. throw objectnotfound("No details for "+id.id);
  1960. return std::unique_ptr<objectdetails_t>(new objectdetails_t(iterDetails->second));
  1961. }
  1962. static LDAPMod *newLDAPModification(char *attribute, const list<string> &values) {
  1963. auto mod = static_cast<LDAPMod *>(calloc(1, sizeof(LDAPMod)));
  1964. // The only type of modification allowed here is replace. It
  1965. // should be enough for our needs, but if an addition or removal
  1966. // is needed, this value has to be changed.
  1967. mod->mod_op = LDAP_MOD_REPLACE;
  1968. mod->mod_type = attribute;
  1969. mod->mod_vals.modv_strvals = (char**) calloc(values.size() + 1, sizeof(char*));
  1970. int idx = 0;
  1971. for (const auto &i : values)
  1972. // A strdup is necessary to be able to call free on it in the
  1973. // method changeAttribute, below.
  1974. mod->mod_vals.modv_strvals[idx++] = strdup(i.c_str());
  1975. mod->mod_vals.modv_strvals[idx] = NULL;
  1976. return mod;
  1977. }
  1978. static LDAPMod *newLDAPModification(char *attribute, const char *value) {
  1979. // Pretty lame function, all it does is use newLDAPModification
  1980. // with a list with only one element.
  1981. list<string> values;
  1982. values.push_back(value);
  1983. return newLDAPModification(attribute, std::move(values));
  1984. }
  1985. int LDAPUserPlugin::changeAttribute(const char *dn, char *attribute, const char *value) {
  1986. LDAPMod *mods[2];
  1987. mods[0] = newLDAPModification(attribute, value);
  1988. mods[1] = NULL;
  1989. // Actual LDAP call
  1990. int rc;
  1991. if ((rc = ldap_modify_s(m_ldap, (char *)dn, mods)) != LDAP_SUCCESS) {
  1992. return 1;
  1993. }
  1994. // Free all calloced memory
  1995. free(mods[0]->mod_vals.modv_strvals[0]);
  1996. free(mods[0]->mod_vals.modv_strvals);
  1997. free(mods[0]);
  1998. return 0;
  1999. }
  2000. int LDAPUserPlugin::changeAttribute(const char *dn, char *attribute, const std::list<std::string> &values) {
  2001. LDAPMod *mods[2];
  2002. mods[0] = newLDAPModification(attribute, values);
  2003. mods[1] = NULL;
  2004. // Actual LDAP call
  2005. int rc;
  2006. if ((rc = ldap_modify_s(m_ldap, (char *)dn, mods)) != LDAP_SUCCESS) {
  2007. return 1;
  2008. }
  2009. // Free all calloced / strduped memory
  2010. for (int i = 0; mods[0]->mod_vals.modv_strvals[i] != NULL; ++i)
  2011. free(mods[0]->mod_vals.modv_strvals[i]);
  2012. free(mods[0]->mod_vals.modv_strvals);
  2013. free(mods[0]);
  2014. return 0;
  2015. }
  2016. void LDAPUserPlugin::changeObject(const objectid_t &id, const objectdetails_t &details, const std::list<std::string> *lpDelProps) {
  2017. throw notimplemented("Change object is not supported when using the LDAP user plugin.");
  2018. }
  2019. objectsignature_t LDAPUserPlugin::createObject(const objectdetails_t &details) {
  2020. throw notimplemented("Creating objects is not supported when using the LDAP user plugin.");
  2021. }
  2022. void LDAPUserPlugin::deleteObject(const objectid_t &id) {
  2023. throw notimplemented("Deleting users is not supported when using the LDAP user plugin.");
  2024. }
  2025. void LDAPUserPlugin::modifyObjectId(const objectid_t &oldId, const objectid_t &newId)
  2026. {
  2027. throw notimplemented("Modifying objectid is not supported when using the LDAP user plugin.");
  2028. }
  2029. /**
  2030. * @todo speedup: read group info on the user (with ADS you know the groups if you have the user dn)
  2031. * @note Missing one group: with linux you have one group id on the user and other objectid on every possible group.
  2032. * this function checks all groups from searchfilter for an existing objectid.
  2033. */
  2034. std::unique_ptr<signatures_t>
  2035. LDAPUserPlugin::getParentObjectsForObject(userobject_relation_t relation,
  2036. const objectid_t &childobject)
  2037. {
  2038. string ldap_filter;
  2039. string member_data;
  2040. objectclass_t parentobjclass = OBJECTCLASS_UNKNOWN;
  2041. string ldap_basedn;
  2042. const char *unique_attr = NULL;
  2043. const char *member_attr = NULL;
  2044. const char *member_attr_type = NULL;
  2045. const char *member_attr_rel = NULL;
  2046. switch (childobject.objclass) {
  2047. case OBJECTCLASS_USER:
  2048. case ACTIVE_USER:
  2049. case NONACTIVE_USER:
  2050. case NONACTIVE_ROOM:
  2051. case NONACTIVE_EQUIPMENT:
  2052. case NONACTIVE_CONTACT:
  2053. unique_attr = m_config->GetSetting("ldap_user_unique_attribute");
  2054. break;
  2055. case OBJECTCLASS_DISTLIST:
  2056. case DISTLIST_GROUP:
  2057. case DISTLIST_SECURITY:
  2058. unique_attr = m_config->GetSetting("ldap_group_unique_attribute");
  2059. break;
  2060. case DISTLIST_DYNAMIC:
  2061. unique_attr = m_config->GetSetting("ldap_dynamicgroup_unique_attribute");
  2062. break;
  2063. case CONTAINER_COMPANY:
  2064. unique_attr = m_config->GetSetting("ldap_company_unique_attribute");
  2065. break;
  2066. case CONTAINER_ADDRESSLIST:
  2067. unique_attr = m_config->GetSetting("ldap_addresslist_unique_attribute");
  2068. break;
  2069. case OBJECTCLASS_UNKNOWN:
  2070. case OBJECTCLASS_CONTAINER:
  2071. default:
  2072. throw runtime_error("Object is wrong type");
  2073. }
  2074. switch (relation) {
  2075. case OBJECTRELATION_GROUP_MEMBER:
  2076. LOG_PLUGIN_DEBUG("%s Relation: Group member", __FUNCTION__);
  2077. parentobjclass = OBJECTCLASS_DISTLIST;
  2078. member_attr = m_config->GetSetting("ldap_groupmembers_attribute");
  2079. member_attr_type = m_config->GetSetting("ldap_groupmembers_attribute_type");
  2080. member_attr_rel = m_config->GetSetting("ldap_groupmembers_relation_attribute");
  2081. break;
  2082. case OBJECTRELATION_COMPANY_VIEW:
  2083. LOG_PLUGIN_DEBUG("%s Relation: Company view", __FUNCTION__);
  2084. parentobjclass = CONTAINER_COMPANY;
  2085. member_attr = m_config->GetSetting("ldap_company_view_attribute");
  2086. member_attr_type = m_config->GetSetting("ldap_company_view_attribute_type");
  2087. member_attr_rel = m_config->GetSetting("ldap_company_view_relation_attribute", "", NULL);
  2088. // restrict to have only companies, nevery anything else
  2089. if (!member_attr_rel)
  2090. member_attr_rel = m_config->GetSetting("ldap_company_unique_attribute");
  2091. break;
  2092. case OBJECTRELATION_COMPANY_ADMIN:
  2093. LOG_PLUGIN_DEBUG("%s Relation: Company admin", __FUNCTION__);
  2094. parentobjclass = CONTAINER_COMPANY;
  2095. member_attr = m_config->GetSetting("ldap_company_admin_attribute");
  2096. member_attr_type = m_config->GetSetting("ldap_company_admin_attribute_type");
  2097. member_attr_rel = m_config->GetSetting("ldap_company_admin_relation_attribute");
  2098. break;
  2099. case OBJECTRELATION_QUOTA_USERRECIPIENT:
  2100. LOG_PLUGIN_DEBUG("%s Relation: Quota user recipient", __FUNCTION__);
  2101. parentobjclass = CONTAINER_COMPANY;
  2102. member_attr = m_config->GetSetting("ldap_quota_userwarning_recipients_attribute");
  2103. member_attr_type = m_config->GetSetting("ldap_quota_userwarning_recipients_attribute_type");
  2104. member_attr_rel = m_config->GetSetting("ldap_quota_userwarning_recipients_relation_attribute");
  2105. break;
  2106. case OBJECTRELATION_QUOTA_COMPANYRECIPIENT:
  2107. LOG_PLUGIN_DEBUG("%s Relation: Quota company recipient", __FUNCTION__);
  2108. parentobjclass = CONTAINER_COMPANY;
  2109. member_attr = m_config->GetSetting("ldap_quota_companywarning_recipients_attribute");
  2110. member_attr_type = m_config->GetSetting("ldap_quota_companywarning_recipients_attribute_type");
  2111. member_attr_rel = m_config->GetSetting("ldap_quota_companywarning_recipients_relation_attribute");
  2112. break;
  2113. default:
  2114. LOG_PLUGIN_DEBUG("%s Relation: Unhandled %x", __FUNCTION__, relation);
  2115. throw runtime_error("Cannot obtain parents for relation " + stringify(relation));
  2116. break;
  2117. }
  2118. ldap_basedn = getSearchBase();
  2119. ldap_filter = getSearchFilter(parentobjclass);
  2120. if(member_attr_rel == NULL || strlen(member_attr_rel) == 0)
  2121. member_attr_rel = unique_attr;
  2122. if (member_attr_type && strcasecmp(member_attr_type, LDAP_DATA_TYPE_DN) == 0) {
  2123. //ads
  2124. member_data = objectUniqueIDtoObjectDN(childobject);
  2125. } else { //LDAP_DATA_TYPE_ATTRIBUTE
  2126. //posix ldap
  2127. // FIXME: No binary support for member_attr_rel
  2128. if (strcasecmp(member_attr_rel, unique_attr) == 0)
  2129. member_data = childobject.id;
  2130. else
  2131. member_data = objectUniqueIDtoAttributeData(childobject, member_attr_rel);
  2132. }
  2133. ldap_filter = "(&" + ldap_filter + "(" + member_attr + "=" + StringEscapeSequence(member_data) + "))";
  2134. return getAllObjectsByFilter(ldap_basedn, LDAP_SCOPE_SUBTREE, ldap_filter, string(), false);
  2135. }
  2136. std::unique_ptr<signatures_t>
  2137. LDAPUserPlugin::getSubObjectsForObject(userobject_relation_t relation,
  2138. const objectid_t &sExternId)
  2139. {
  2140. enum LISTTYPE { MEMBERS, FILTER } ulType = MEMBERS;
  2141. std::unique_ptr<signatures_t> members(new signatures_t());
  2142. list<string> memberlist;
  2143. auto_free_ldap_message res;
  2144. string dn;
  2145. string ldap_basedn;
  2146. string ldap_filter;
  2147. string ldap_member_filter;
  2148. objectid_t companyid;
  2149. string companyDN;
  2150. objectclass_t childobjclass = OBJECTCLASS_UNKNOWN;
  2151. const char *unique_attr = NULL;
  2152. const char *unique_attr_type = NULL;
  2153. const char *member_attr = NULL;
  2154. const char *member_attr_type = NULL;
  2155. const char *base_attr = NULL;
  2156. std::unique_ptr<attrArray> member_attr_rel(new attrArray(5));
  2157. std::unique_ptr<attrArray> child_unique_attr(new attrArray(5));
  2158. CONFIG_TO_ATTR(child_unique_attr, user_unique_attr, "ldap_user_unique_attribute");
  2159. CONFIG_TO_ATTR(child_unique_attr, group_unique_attr, "ldap_group_unique_attribute");
  2160. CONFIG_TO_ATTR(child_unique_attr, company_unique_attr, "ldap_company_unique_attribute");
  2161. CONFIG_TO_ATTR(child_unique_attr, addresslist_unique_attr, "ldap_addresslist_unique_attribute");
  2162. CONFIG_TO_ATTR(child_unique_attr, dynamicgroup_unique_attr, "ldap_dynamicgroup_unique_attribute");
  2163. switch (relation) {
  2164. case OBJECTRELATION_GROUP_MEMBER: {
  2165. LOG_PLUGIN_DEBUG("%s Relation: Group member", __FUNCTION__);
  2166. childobjclass = OBJECTCLASS_UNKNOWN; /* Support users & groups */
  2167. if (sExternId.objclass != DISTLIST_DYNAMIC) {
  2168. unique_attr = group_unique_attr;
  2169. unique_attr_type = m_config->GetSetting("ldap_group_unique_attribute_type");
  2170. member_attr = m_config->GetSetting("ldap_groupmembers_attribute");
  2171. member_attr_type = m_config->GetSetting("ldap_groupmembers_attribute_type");
  2172. CONFIG_TO_ATTR(member_attr_rel, group_rel_attr, "ldap_groupmembers_relation_attribute");
  2173. if (member_attr_rel->empty()) {
  2174. member_attr_rel->add(user_unique_attr);
  2175. member_attr_rel->add(group_unique_attr);
  2176. }
  2177. } else {
  2178. // dynamic group
  2179. unique_attr = dynamicgroup_unique_attr;
  2180. unique_attr_type = m_config->GetSetting("ldap_dynamicgroup_unique_attribute_type");
  2181. member_attr = m_config->GetSetting("ldap_dynamicgroup_filter_attribute");
  2182. base_attr = m_config->GetSetting("ldap_dynamicgroup_search_base_attribute");
  2183. member_attr_rel->add(child_unique_attr->get());
  2184. ulType = FILTER;
  2185. }
  2186. break;
  2187. }
  2188. case OBJECTRELATION_COMPANY_VIEW: {
  2189. LOG_PLUGIN_DEBUG("%s Relation: Company view", __FUNCTION__);
  2190. childobjclass = CONTAINER_COMPANY;
  2191. unique_attr = company_unique_attr;
  2192. unique_attr_type = m_config->GetSetting("ldap_company_unique_attribute_type");
  2193. member_attr = m_config->GetSetting("ldap_company_view_attribute");
  2194. member_attr_type = m_config->GetSetting("ldap_company_view_attribute_type");
  2195. CONFIG_TO_ATTR(member_attr_rel, view_rel_attr, "ldap_company_view_relation_attribute");
  2196. if (member_attr_rel->empty()) {
  2197. member_attr_rel->add(company_unique_attr);
  2198. }
  2199. break;
  2200. }
  2201. case OBJECTRELATION_COMPANY_ADMIN: {
  2202. LOG_PLUGIN_DEBUG("%s Relation: Company admin", __FUNCTION__);
  2203. childobjclass = ACTIVE_USER; /* Only active users can perform administrative tasks */
  2204. unique_attr = company_unique_attr;
  2205. unique_attr_type = m_config->GetSetting("ldap_company_unique_attribute_type");
  2206. member_attr = m_config->GetSetting("ldap_company_admin_attribute");
  2207. member_attr_type = m_config->GetSetting("ldap_company_admin_attribute_type");
  2208. CONFIG_TO_ATTR(member_attr_rel, admin_rel_attr, "ldap_company_admin_relation_attribute");
  2209. if (member_attr_rel->empty()) {
  2210. member_attr_rel->add(user_unique_attr);
  2211. }
  2212. break;
  2213. }
  2214. case OBJECTRELATION_QUOTA_USERRECIPIENT: {
  2215. LOG_PLUGIN_DEBUG("%s Relation: Quota user recipient", __FUNCTION__);
  2216. childobjclass = OBJECTCLASS_USER;
  2217. unique_attr = company_unique_attr;
  2218. unique_attr_type = m_config->GetSetting("ldap_company_unique_attribute_type");
  2219. member_attr = m_config->GetSetting("ldap_quota_userwarning_recipients_attribute");
  2220. member_attr_type = m_config->GetSetting("ldap_quota_userwarning_recipients_attribute_type");
  2221. CONFIG_TO_ATTR(member_attr_rel, qur_rel_attr, "ldap_quota_userwarning_recipients_relation_attribute");
  2222. if (member_attr_rel->empty()) {
  2223. member_attr_rel->add(user_unique_attr);
  2224. member_attr_rel->add(group_unique_attr);
  2225. }
  2226. break;
  2227. }
  2228. case OBJECTRELATION_QUOTA_COMPANYRECIPIENT: {
  2229. LOG_PLUGIN_DEBUG("%s Relation: Quota company recipient", __FUNCTION__);
  2230. childobjclass = OBJECTCLASS_USER;
  2231. unique_attr = company_unique_attr;
  2232. unique_attr_type = m_config->GetSetting("ldap_company_unique_attribute_type");
  2233. member_attr = m_config->GetSetting("ldap_quota_companywarning_recipients_attribute");
  2234. member_attr_type = m_config->GetSetting("ldap_quota_companywarning_recipients_attribute_type");
  2235. CONFIG_TO_ATTR(member_attr_rel, qcr_rel_attr, "ldap_quota_companywarning_recipients_relation_attribute");
  2236. if (member_attr_rel->empty()) {
  2237. member_attr_rel->add(user_unique_attr);
  2238. member_attr_rel->add(group_unique_attr);
  2239. }
  2240. break;
  2241. }
  2242. case OBJECTRELATION_ADDRESSLIST_MEMBER:
  2243. LOG_PLUGIN_DEBUG("%s Relation: Addresslist member", __FUNCTION__);
  2244. childobjclass = OBJECTCLASS_UNKNOWN;
  2245. unique_attr = addresslist_unique_attr;
  2246. unique_attr_type = m_config->GetSetting("ldap_addresslist_unique_attribute_type");
  2247. member_attr = m_config->GetSetting("ldap_addresslist_filter_attribute");
  2248. base_attr = m_config->GetSetting("ldap_addresslist_search_base_attribute");
  2249. member_attr_rel->add(child_unique_attr->get());
  2250. ulType = FILTER;
  2251. break;
  2252. default:
  2253. LOG_PLUGIN_DEBUG("%s Relation: Unhandled %x", __FUNCTION__, relation);
  2254. throw runtime_error("Cannot obtain children for relation " + stringify(relation));
  2255. break;
  2256. }
  2257. const char *request_attrs[] = {
  2258. member_attr,
  2259. base_attr,
  2260. NULL
  2261. };
  2262. //Get group DN from unique id
  2263. ldap_basedn = getSearchBase();
  2264. ldap_filter = getObjectSearchFilter(sExternId, unique_attr, unique_attr_type);
  2265. /* LDAP filter empty, parent does not exist */
  2266. if (ldap_filter.empty())
  2267. throw objectnotfound("ldap filter is empty");
  2268. my_ldap_search_s(
  2269. (char *)ldap_basedn.c_str(), LDAP_SCOPE_SUBTREE,
  2270. (char *)ldap_filter.c_str(), (char **)request_attrs,
  2271. FETCH_ATTR_VALS, &~res);
  2272. if(ulType == MEMBERS) {
  2273. // Get the DN for each of the returned entries
  2274. FOREACH_ENTRY(res) {
  2275. FOREACH_ATTR(entry) {
  2276. if (member_attr && !strcasecmp(att, member_attr))
  2277. memberlist = getLDAPAttributeValues(att, entry);
  2278. }
  2279. END_FOREACH_ATTR
  2280. }
  2281. END_FOREACH_ENTRY
  2282. if (!memberlist.empty())
  2283. members = resolveObjectsFromAttributesType(childobjclass, memberlist, member_attr_rel->get(), member_attr_type);
  2284. } else {
  2285. // Members are specified by a filter
  2286. FOREACH_ENTRY(res) {
  2287. // We need the DN of the addresslist so that we can later find out which company it is in
  2288. dn = GetLDAPEntryDN(entry);
  2289. FOREACH_ATTR(entry) {
  2290. if (member_attr && !strcasecmp(att, member_attr))
  2291. ldap_member_filter = getLDAPAttributeValue(att, entry);
  2292. if (base_attr && !strcasecmp(att, base_attr))
  2293. ldap_basedn = getLDAPAttributeValue(att, entry);
  2294. }
  2295. END_FOREACH_ATTR
  2296. }
  2297. END_FOREACH_ENTRY
  2298. if(!ldap_member_filter.empty()) {
  2299. if(m_bHosted) {
  2300. std::unique_ptr<dn_cache_t> lpCompanyCache = m_lpCache->getObjectDNCache(this, CONTAINER_COMPANY);
  2301. companyid = m_lpCache->getParentForDN(lpCompanyCache, dn);
  2302. companyDN = m_lpCache->getDNForObject(lpCompanyCache, companyid);
  2303. }
  2304. // Use the filter to get all members matching the specified search filter
  2305. if (ldap_basedn.empty())
  2306. ldap_basedn = getSearchBase();
  2307. ldap_filter = "(&" + getSearchFilter(childobjclass) + ldap_member_filter + ")";
  2308. members = getAllObjectsByFilter(ldap_basedn, LDAP_SCOPE_SUBTREE, ldap_filter, companyDN, false);
  2309. }
  2310. }
  2311. return members;
  2312. }
  2313. void LDAPUserPlugin::addSubObjectRelation(userobject_relation_t relation, const objectid_t &id, const objectid_t &member) {
  2314. throw notimplemented("add object relations is not supported when using the LDAP user plugin.");
  2315. }
  2316. void LDAPUserPlugin::deleteSubObjectRelation(userobject_relation_t relation, const objectid_t &id, const objectid_t &member) {
  2317. throw notimplemented("Delete object relations is not supported when using the LDAP user plugin.");
  2318. }
  2319. std::unique_ptr<signatures_t>
  2320. LDAPUserPlugin::searchObject(const std::string &match, unsigned int ulFlags)
  2321. {
  2322. std::unique_ptr<signatures_t> signatures;
  2323. string escMatch;
  2324. string ldap_basedn;
  2325. string ldap_filter;
  2326. string search_filter;
  2327. size_t pos;
  2328. LOG_PLUGIN_DEBUG("%s %s flags:%x", __FUNCTION__, match.c_str(), ulFlags);
  2329. ldap_basedn = getSearchBase();
  2330. ldap_filter = getSearchFilter();
  2331. // Escape match string
  2332. escMatch = StringEscapeSequence(m_iconvrev->convert(match));
  2333. if (! (ulFlags & EMS_AB_ADDRESS_LOOKUP)) {
  2334. try {
  2335. // the admin should place %s* in the search filter
  2336. search_filter = m_config->GetSetting("ldap_object_search_filter");
  2337. // search/replace %s -> escMatch
  2338. while ((pos = search_filter.find("%s")) != string::npos)
  2339. search_filter.replace(pos, 2, escMatch);
  2340. } catch (...) {};
  2341. // custom filter was empty, add * for a full search
  2342. if (search_filter.empty())
  2343. escMatch += "*";
  2344. }
  2345. if (search_filter.empty()) {
  2346. // if building the filter failed, use our search filter
  2347. // @todo optimize filter, a lot of attributes can be the same.
  2348. search_filter =
  2349. "(|"
  2350. "(" + string(m_config->GetSetting("ldap_loginname_attribute")) + "=" + escMatch + ")"
  2351. "(" + string(m_config->GetSetting("ldap_fullname_attribute")) + "=" + escMatch + ")"
  2352. "(" + string(m_config->GetSetting("ldap_emailaddress_attribute")) + "=" + escMatch + ")"
  2353. "(" + string(m_config->GetSetting("ldap_emailaliases_attribute")) + "=" + escMatch + ")"
  2354. "(" + string(m_config->GetSetting("ldap_groupname_attribute")) + "=" + escMatch + ")"
  2355. "(" + string(m_config->GetSetting("ldap_companyname_attribute")) + "=" + escMatch + ")"
  2356. "(" + string(m_config->GetSetting("ldap_addresslist_name_attribute")) + "=" + escMatch + ")"
  2357. "(" + string(m_config->GetSetting("ldap_dynamicgroup_name_attribute")) + "=" + escMatch + ")"
  2358. ")";
  2359. }
  2360. ldap_filter = "(&" + ldap_filter + search_filter + ")";
  2361. signatures = getAllObjectsByFilter(ldap_basedn, LDAP_SCOPE_SUBTREE, ldap_filter, string(), false);
  2362. if (signatures->empty())
  2363. throw objectnotfound(ldap_filter);
  2364. return signatures;
  2365. }
  2366. std::unique_ptr<objectdetails_t> LDAPUserPlugin::getPublicStoreDetails(void)
  2367. {
  2368. auto_free_ldap_message res;
  2369. LDAPMessage *entry = NULL;
  2370. string ldap_basedn;
  2371. string search_filter;
  2372. std::unique_ptr<objectdetails_t> details(new objectdetails_t(CONTAINER_COMPANY));
  2373. if (!m_bDistributed)
  2374. throw objectnotfound("public store");
  2375. LOG_PLUGIN_DEBUG("%s", __FUNCTION__);
  2376. const char *publicstore_attr = m_config->GetSetting("ldap_server_contains_public_attribute", "", NULL);
  2377. ldap_basedn = getSearchBase();
  2378. search_filter = getServerSearchFilter();
  2379. if (publicstore_attr)
  2380. search_filter = "(&" + search_filter + "(" + publicstore_attr + "=1))";
  2381. std::unique_ptr<attrArray> request_attrs(new attrArray(1));
  2382. CONFIG_TO_ATTR(request_attrs, unique_attr, "ldap_server_unique_attribute");
  2383. // Do a search request to get all attributes for this user. The
  2384. // attributes requested should be passed as the fifth argument, if
  2385. // necessary
  2386. my_ldap_search_s(
  2387. (char *)ldap_basedn.c_str(), LDAP_SCOPE_SUBTREE,
  2388. (char *)search_filter.c_str(), (char **)request_attrs->get(),
  2389. FETCH_ATTR_VALS, &~res);
  2390. switch (ldap_count_entries(m_ldap, res)) {
  2391. case 0:
  2392. throw objectnotfound("public store server");
  2393. case 1:
  2394. break;
  2395. default:
  2396. throw toomanyobjects("public store server");
  2397. }
  2398. entry = ldap_first_entry(m_ldap, res);
  2399. if(entry == NULL) {
  2400. throw runtime_error("ldap_dn: broken.");
  2401. }
  2402. FOREACH_ATTR(entry) {
  2403. if (unique_attr && !strcasecmp(att, unique_attr))
  2404. details->SetPropString(OB_PROP_S_SERVERNAME, m_iconv->convert(getLDAPAttributeValue(att, entry)));
  2405. }
  2406. END_FOREACH_ATTR
  2407. return details;
  2408. }
  2409. std::unique_ptr<serverlist_t> LDAPUserPlugin::getServers(void)
  2410. {
  2411. auto_free_ldap_message res;
  2412. string ldap_basedn;
  2413. string search_filter;
  2414. std::unique_ptr<serverlist_t> serverlist(new serverlist_t());
  2415. if (!m_bDistributed)
  2416. throw objectnotfound("Distributed not enabled");
  2417. LOG_PLUGIN_DEBUG("%s", __FUNCTION__);
  2418. string strName;
  2419. ldap_basedn = getSearchBase();
  2420. search_filter =
  2421. "(&" +
  2422. getServerSearchFilter() +
  2423. ")";
  2424. std::unique_ptr<attrArray> request_attrs(new attrArray(1));
  2425. CONFIG_TO_ATTR(request_attrs, name_attr, "ldap_server_unique_attribute");
  2426. my_ldap_search_s(
  2427. (char *)ldap_basedn.c_str(), LDAP_SCOPE_SUBTREE,
  2428. (char *)search_filter.c_str(), (char **)request_attrs->get(),
  2429. FETCH_ATTR_VALS, &~res);
  2430. FOREACH_ENTRY(res)
  2431. {
  2432. FOREACH_ATTR(entry) {
  2433. if (name_attr && !strcasecmp(att, name_attr)) {
  2434. strName = m_iconv->convert(getLDAPAttributeValue(att, entry));
  2435. serverlist->push_back(std::move(strName));
  2436. }
  2437. }
  2438. END_FOREACH_ATTR
  2439. }
  2440. END_FOREACH_ENTRY
  2441. return serverlist;
  2442. }
  2443. std::unique_ptr<serverdetails_t>
  2444. LDAPUserPlugin::getServerDetails(const std::string &server)
  2445. {
  2446. auto_free_ldap_message res;
  2447. LDAPMessage *entry = NULL;
  2448. string ldap_basedn;
  2449. string search_filter;
  2450. if (!m_bDistributed)
  2451. throw objectnotfound("Distributed not enabled for" +server);
  2452. LOG_PLUGIN_DEBUG("%s for server %s", __FUNCTION__, server.c_str());
  2453. std::unique_ptr<serverdetails_t> serverDetails(new serverdetails_t(server));
  2454. string strAddress;
  2455. string strHttpPort;
  2456. string strSslPort;
  2457. string strFilePath;
  2458. string strProxyPath;
  2459. ldap_basedn = getSearchBase();
  2460. search_filter =
  2461. "(&" +
  2462. getServerSearchFilter() +
  2463. getSearchFilter(server, m_config->GetSetting("ldap_server_unique_attribute")) +
  2464. ")";
  2465. std::unique_ptr<attrArray> request_attrs(new attrArray(5));
  2466. CONFIG_TO_ATTR(request_attrs, address_attr, "ldap_server_address_attribute");
  2467. CONFIG_TO_ATTR(request_attrs, http_port_attr, "ldap_server_http_port_attribute");
  2468. CONFIG_TO_ATTR(request_attrs, ssl_port_attr, "ldap_server_ssl_port_attribute");
  2469. CONFIG_TO_ATTR(request_attrs, file_path_attr, "ldap_server_file_path_attribute");
  2470. CONFIG_TO_ATTR(request_attrs, proxy_path_attr, "ldap_server_proxy_path_attribute");
  2471. if (request_attrs->empty())
  2472. throw runtime_error("no attributes defined");
  2473. // Do a search request to get all attributes for this server. The
  2474. // attributes requested should be passed as the fifth argument, if
  2475. // necessary
  2476. my_ldap_search_s(
  2477. (char *)ldap_basedn.c_str(), LDAP_SCOPE_SUBTREE,
  2478. (char *)search_filter.c_str(), (char **)request_attrs->get(),
  2479. FETCH_ATTR_VALS, &~res);
  2480. switch (ldap_count_entries(m_ldap, res)) {
  2481. case 0:
  2482. throw objectnotfound("No results from ldap for "+server);
  2483. case 1:
  2484. break;
  2485. default:
  2486. throw toomanyobjects(server);
  2487. }
  2488. entry = ldap_first_entry(m_ldap, res);
  2489. if(entry == NULL) {
  2490. throw runtime_error("ldap_dn: broken.");
  2491. }
  2492. FOREACH_ATTR(entry) {
  2493. if (address_attr && !strcasecmp(att, address_attr)) {
  2494. strAddress = m_iconv->convert(getLDAPAttributeValue(att, entry));
  2495. }
  2496. if (http_port_attr && !strcasecmp(att, http_port_attr)) {
  2497. strHttpPort = m_iconv->convert(getLDAPAttributeValue(att, entry));
  2498. }
  2499. if (ssl_port_attr && !strcasecmp(att, ssl_port_attr)) {
  2500. strSslPort = m_iconv->convert(getLDAPAttributeValue(att, entry));
  2501. }
  2502. if (file_path_attr && !strcasecmp(att, file_path_attr)) {
  2503. strFilePath = m_iconv->convert(getLDAPAttributeValue(att, entry));
  2504. }
  2505. if (proxy_path_attr && !strcasecmp(att, proxy_path_attr)) {
  2506. strProxyPath = m_iconv->convert(getLDAPAttributeValue(att, entry));
  2507. }
  2508. }
  2509. END_FOREACH_ATTR
  2510. if (strAddress.empty())
  2511. throw runtime_error("obligatory address missing for server '" + server + "'");
  2512. if (strHttpPort.empty())
  2513. throw runtime_error("obligatory http port missing for server '" + server + "'");
  2514. serverDetails->SetHostAddress(strAddress);
  2515. serverDetails->SetHttpPort(atoi(strHttpPort.c_str()));
  2516. serverDetails->SetSslPort(atoi(strSslPort.c_str()));
  2517. serverDetails->SetFilePath(strFilePath);
  2518. serverDetails->SetProxyPath(strProxyPath);
  2519. return serverDetails;
  2520. }
  2521. // Convert unsigned char to 2-char hex string
  2522. static std::string toHex(unsigned char n)
  2523. {
  2524. std::string s;
  2525. static const char digits[] = "0123456789ABCDEF";
  2526. s += digits[n >> 4];
  2527. s += digits[n & 0xf];
  2528. return s;
  2529. }
  2530. HRESULT LDAPUserPlugin::BintoEscapeSequence(const char* lpdata, size_t size, string* lpEscaped)
  2531. {
  2532. HRESULT hr = 0;
  2533. lpEscaped->clear();
  2534. for (size_t t = 0; t < size; ++t)
  2535. lpEscaped->append("\\"+toHex(lpdata[t]));
  2536. return hr;
  2537. }
  2538. std::string LDAPUserPlugin::StringEscapeSequence(const string &strData)
  2539. {
  2540. return StringEscapeSequence(strData.c_str(), strData.size());
  2541. }
  2542. std::string LDAPUserPlugin::StringEscapeSequence(const char* lpdata, size_t size)
  2543. {
  2544. std::string strEscaped;
  2545. for (size_t t = 0; t < size; ++t)
  2546. if (lpdata[t] != ' ' && (lpdata[t] < '0' || lpdata[t] > 122 ||
  2547. (lpdata[t] >= 58 /*:*/ && lpdata[t] <= 64 /*:;<=>?@*/) ||
  2548. (lpdata[t] >= 91 && lpdata[t] <= 96) /* [\]^_`*/))
  2549. strEscaped.append("\\"+toHex(lpdata[t]));
  2550. else
  2551. strEscaped.append((char*)&lpdata[t], 1);
  2552. return strEscaped;
  2553. }
  2554. std::unique_ptr<quotadetails_t> LDAPUserPlugin::getQuota(const objectid_t &id,
  2555. bool bGetUserDefault)
  2556. {
  2557. auto_free_ldap_message res;
  2558. string dn;
  2559. std::unique_ptr<quotadetails_t> quotaDetails(new quotadetails_t());
  2560. const char *multiplier_s = m_config->GetSetting("ldap_quota_multiplier");
  2561. long long multiplier = 1L;
  2562. if (multiplier_s) {
  2563. multiplier = fromstring<const char *, long long>(multiplier_s);
  2564. }
  2565. std::unique_ptr<attrArray> request_attrs(new attrArray(4));
  2566. CONFIG_TO_ATTR(request_attrs, usedefaults_attr,
  2567. bGetUserDefault ?
  2568. "ldap_userdefault_quotaoverride_attribute" :
  2569. "ldap_quotaoverride_attribute");
  2570. CONFIG_TO_ATTR(request_attrs, warnquota_attr,
  2571. bGetUserDefault ?
  2572. "ldap_userdefault_warnquota_attribute" :
  2573. "ldap_warnquota_attribute");
  2574. CONFIG_TO_ATTR(request_attrs, softquota_attr,
  2575. bGetUserDefault ?
  2576. "ldap_userdefault_softquota_attribute" :
  2577. "ldap_softquota_attribute");
  2578. CONFIG_TO_ATTR(request_attrs, hardquota_attr,
  2579. bGetUserDefault ?
  2580. "ldap_userdefault_hardquota_attribute" :
  2581. "ldap_hardquota_attribute");
  2582. string ldap_basedn = getSearchBase();
  2583. string ldap_filter = getObjectSearchFilter(id);
  2584. /* LDAP filter empty, object does not exist */
  2585. if (ldap_filter.empty())
  2586. throw objectnotfound("no ldap filter for "+id.id);
  2587. LOG_PLUGIN_DEBUG("%s", __FUNCTION__);
  2588. // Do a search request to get all attributes for this user. The
  2589. // attributes requested should be passed as the fifth argument, if
  2590. // necessary
  2591. my_ldap_search_s(
  2592. (char *)ldap_basedn.c_str(), LDAP_SCOPE_SUBTREE,
  2593. (char *)ldap_filter.c_str(), (char **)request_attrs->get(),
  2594. FETCH_ATTR_VALS, &~res);
  2595. quotaDetails->bIsUserDefaultQuota = bGetUserDefault;
  2596. // Get the DN for each of the returned entries
  2597. // ldap_first_message would work too, but that needs much more
  2598. // work parsing the results from the ber structs.
  2599. FOREACH_ENTRY(res) {
  2600. FOREACH_ATTR(entry) {
  2601. if (usedefaults_attr && !strcasecmp(att, usedefaults_attr)) {
  2602. // Workarround quotaoverride == !usedefaultquota
  2603. quotaDetails->bUseDefaultQuota = !parseBool(getLDAPAttributeValue(att, entry).c_str());
  2604. } else if (warnquota_attr && !strcasecmp(att, warnquota_attr)) {
  2605. quotaDetails->llWarnSize = fromstring<string, long long>(getLDAPAttributeValue(att, entry)) * multiplier;
  2606. } else if (id.objclass != CONTAINER_COMPANY && softquota_attr && !strcasecmp(att, softquota_attr)) {
  2607. quotaDetails->llSoftSize = fromstring<string, long long>(getLDAPAttributeValue(att, entry)) * multiplier;
  2608. } else if (id.objclass != CONTAINER_COMPANY && hardquota_attr && !strcasecmp(att, hardquota_attr)) {
  2609. quotaDetails->llHardSize = fromstring<string, long long>(getLDAPAttributeValue(att, entry)) * multiplier;
  2610. }
  2611. }
  2612. END_FOREACH_ATTR
  2613. }
  2614. END_FOREACH_ENTRY
  2615. return quotaDetails;
  2616. }
  2617. void LDAPUserPlugin::setQuota(const objectid_t &id, const quotadetails_t &quotadetails)
  2618. {
  2619. throw notimplemented("set quota is not supported when using the LDAP user plugin.");
  2620. }
  2621. std::unique_ptr<abprops_t> LDAPUserPlugin::getExtraAddressbookProperties(void)
  2622. {
  2623. std::unique_ptr<abprops_t> lProps(new abprops_t());
  2624. list<configsetting_t> lExtraAttrs = m_config->GetSettingGroup(CONFIGGROUP_PROPMAP);
  2625. LOG_PLUGIN_DEBUG("%s", __FUNCTION__);
  2626. for (const auto &cs : lExtraAttrs)
  2627. lProps->push_back(xtoi(cs.szName));
  2628. return lProps;
  2629. }
  2630. void LDAPUserPlugin::removeAllObjects(objectid_t except)
  2631. {
  2632. throw notimplemented("removeAllObjects is not implemented in the LDAP user plugin.");
  2633. }
  2634. } /* namespace */