basesecurity.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #pragma warning (disable : 4786)
  14. #pragma warning (disable : 4018)
  15. #pragma warning (disable : 4146)
  16. #pragma warning (disable : 4275)
  17. #ifdef _WIN32
  18. #define AXA_API __declspec(dllexport)
  19. #endif
  20. //#include "ctconnection.h"
  21. #include "basesecurity.hpp"
  22. #include "jmd5.hpp"
  23. #define cacheTimeout 30000
  24. //#ifdef _WIN32
  25. CBaseSecurityManager::CBaseSecurityManager(const char *serviceName, const char *config)
  26. {
  27. Owned<IPropertyTree> cfg = createPTreeFromXMLString(config, ipt_caseInsensitive);
  28. if(cfg.get() == NULL)
  29. throw MakeStringException(-1, "createPTreeFromXMLString() failed for %s", config);
  30. init(serviceName,cfg);
  31. cfg->Release();
  32. }
  33. CBaseSecurityManager::CBaseSecurityManager(const char *serviceName, IPropertyTree *config)
  34. {
  35. m_dbpasswordEncoding = SecPwEnc_unknown;
  36. init(serviceName,config);
  37. }
  38. void CBaseSecurityManager::init(const char *serviceName, IPropertyTree *config)
  39. {
  40. if(config == NULL)
  41. return;
  42. m_config.set(config);
  43. m_permissionsCache.setCacheTimeout( 60 * config->getPropInt("@cacheTimeout", 5) );
  44. m_dbserver.appendf("%s",config->queryProp("@serverName"));
  45. m_dbuser.appendf("%s",config->queryProp("@systemUser"));
  46. if(config->hasProp("@ConnectionPoolSize"))
  47. m_poolsize = atoi(config->queryProp("@connectionPoolSize"));
  48. else
  49. m_poolsize = 2;
  50. StringBuffer encodedPass,encryptedPass;
  51. encodedPass.appendf("%s",config->queryProp("@systemPassword"));
  52. decrypt(m_dbpassword, encodedPass.str());
  53. m_dbpasswordEncoding = SecPwEnc_plain_text;
  54. StringBuffer strPasswordEncoding;
  55. const char* encodingType = config->queryProp("@encodePassword");
  56. if(encodingType && strcmp(encodingType,"MD5") == 0)
  57. m_dbpasswordEncoding=SecPwEnc_salt_md5;
  58. else if (encodingType && strcmp(encodingType,"Rijndael") == 0)
  59. m_dbpasswordEncoding=SecPwEnc_Rijndael;
  60. else if (encodingType && strcmp(encodingType,"Accurint MD5") == 0)
  61. m_dbpasswordEncoding = SecPwEnc_salt_accurint_md5;
  62. if(m_dbserver.length() == 0 || m_dbuser.length() == 0)
  63. throw MakeStringException(-1, "CBaseSecurityManager() - db server or user is missing");
  64. IPropertyTree* pNonRestrictedIPTree = config->queryBranch("SafeIPList");
  65. if(pNonRestrictedIPTree)
  66. {
  67. Owned<IPropertyTreeIterator> Itr = pNonRestrictedIPTree->getElements("ip");
  68. for(Itr->first();Itr->isValid();Itr->next())
  69. {
  70. IPropertyTree& tree = Itr->query();
  71. m_safeIPList[tree.queryProp("")]=true;
  72. }
  73. }
  74. m_enableIPRoaming = config->getPropBool("@enableIPRoaming");
  75. m_enableOTP = config->getPropBool("@enableOTP",false);
  76. m_passwordExpirationWarningDays = config->getPropInt(".//@passwordExpirationWarningDays", 10); //Default to 10 days
  77. }
  78. CBaseSecurityManager::~CBaseSecurityManager()
  79. {
  80. MapStrToUsers::iterator pos;
  81. for(pos=m_userList.begin();pos!=m_userList.end();){
  82. pos->second->Release();
  83. pos++;
  84. }
  85. dbDisconnect();
  86. }
  87. //interface ISecManager : extends IInterface
  88. ISecUser * CBaseSecurityManager::createUser(const char * user_name)
  89. {
  90. return (new CSecureUser(user_name, NULL));
  91. }
  92. ISecResourceList * CBaseSecurityManager::createResourceList(const char * rlname)
  93. {
  94. return (new CSecurityResourceList(rlname));
  95. }
  96. bool CBaseSecurityManager::subscribe(ISecAuthenticEvents & events)
  97. {
  98. m_subscriber.set(&events);
  99. return true;
  100. }
  101. bool CBaseSecurityManager::unsubscribe(ISecAuthenticEvents & events)
  102. {
  103. if (&events == m_subscriber.get())
  104. {
  105. m_subscriber.set(NULL);
  106. }
  107. return true;
  108. }
  109. bool CBaseSecurityManager::authorize(ISecUser & sec_user, ISecResourceList * Resources)
  110. {
  111. if(sec_user.getAuthenticateStatus() != AS_AUTHENTICATED)
  112. {
  113. bool bOk = ValidateUser(sec_user);
  114. if(bOk == false)
  115. return false;
  116. }
  117. return ValidateResources(sec_user,Resources);
  118. }
  119. bool CBaseSecurityManager::updateSettings(ISecUser &sec_user, ISecPropertyList* resources)
  120. {
  121. CSecurityResourceList * reslist = (CSecurityResourceList*)resources;
  122. if(!reslist)
  123. return true;
  124. IArrayOf<ISecResource>& rlist = reslist->getResourceList();
  125. int nResources = rlist.length();
  126. if (nResources <= 0)
  127. return true;
  128. bool rc = false;
  129. if (m_permissionsCache.isCacheEnabled()==false)
  130. return updateSettings(sec_user, rlist);
  131. bool* cached_found = (bool*)alloca(nResources*sizeof(bool));
  132. int nFound = m_permissionsCache.lookup(sec_user, rlist, cached_found);
  133. if (nFound >= nResources)
  134. return true;
  135. IArrayOf<ISecResource> rlist2;
  136. for (int i=0; i < nResources; i++)
  137. {
  138. if (*(cached_found+i) == false)
  139. {
  140. ISecResource& secRes = rlist.item(i);
  141. secRes.Link();
  142. rlist2.append(secRes);
  143. }
  144. }
  145. rc = updateSettings(sec_user, rlist2);
  146. if (rc)
  147. m_permissionsCache.add(sec_user, rlist2);
  148. return rc;
  149. }
  150. bool CBaseSecurityManager::updateSettings(ISecUser & sec_user,IArrayOf<ISecResource>& rlist)
  151. {
  152. CSecureUser* user = (CSecureUser*)&sec_user;
  153. if(user == NULL)
  154. return false;
  155. int usernum = findUser(user->getName(),user->getRealm());
  156. if(usernum < 0)
  157. {
  158. PrintLog("User number of %s can't be found", user->getName());
  159. return false;
  160. }
  161. bool sqchecked = false, sqverified = false, otpchecked = false;
  162. int otpok = -1;
  163. ForEachItemIn(x, rlist)
  164. {
  165. ISecResource* secRes = (ISecResource*)(&(rlist.item(x)));
  166. if(secRes == NULL)
  167. continue;
  168. //AccessFlags default value is -1. Set it to 0 so that the settings can be cached. AccessFlags is not being used for settings.
  169. secRes->setAccessFlags(0);
  170. if(secRes->getParameter("userprop") && *secRes->getParameter("userprop")!='\0')
  171. {
  172. //if we have a parameter in the user or company table it will have been added as a parameter to the ISecUser when
  173. // the authentication query was run. We should keep this messiness here so that the the end user is insulated....
  174. dbValidateSetting(*secRes,sec_user);
  175. continue;
  176. }
  177. const char* resource_name = secRes->getParameter("resource");
  178. if(resource_name && *resource_name &&
  179. (stricmp(resource_name, "SSN Masking") == 0 || stricmp(resource_name, "Driver License Masking") == 0))
  180. {
  181. //If OTP Enabled and OTP2FACTOR cookie not valid, mask
  182. if(m_enableOTP)
  183. {
  184. if(!otpchecked)
  185. {
  186. const char* otpcookie = sec_user.getProperty("OTP2FACTOR");
  187. // -1 means OTP is not enabled for the user. 0: failed verfication, 1: passed verification.
  188. otpok = validateOTP(&sec_user, otpcookie);
  189. otpchecked = true;
  190. }
  191. if(otpok == 0)
  192. {
  193. CSecurityResource* cres = dynamic_cast<CSecurityResource*>(secRes);
  194. if(resource_name && *resource_name && cres)
  195. {
  196. if(stricmp(resource_name, "SSN Masking") == 0)
  197. {
  198. cres->setValue("All");
  199. continue;
  200. }
  201. else if(stricmp(resource_name, "Driver License Masking") == 0)
  202. {
  203. cres->setValue("1");
  204. continue;
  205. }
  206. }
  207. }
  208. else if(otpok == 1)
  209. {
  210. CSecurityResource* cres = dynamic_cast<CSecurityResource*>(secRes);
  211. if(resource_name && *resource_name && cres)
  212. {
  213. if(stricmp(resource_name, "SSN Masking") == 0)
  214. {
  215. cres->setValue("None");
  216. continue;
  217. }
  218. else if(stricmp(resource_name, "Driver License Masking") == 0)
  219. {
  220. cres->setValue("0");
  221. continue;
  222. }
  223. }
  224. }
  225. }
  226. if(m_enableIPRoaming && sec_user.getPropertyInt("IPRoaming") == 1)
  227. {
  228. if(!sqchecked)
  229. {
  230. const char* sequest = sec_user.getProperty("SEQUEST");
  231. if(sequest && *sequest)
  232. {
  233. sqverified = validateSecurityQuestion(&sec_user, sequest);
  234. }
  235. sqchecked = true;
  236. }
  237. if(!sqverified)
  238. {
  239. CSecurityResource* cres = dynamic_cast<CSecurityResource*>(secRes);
  240. if(resource_name && *resource_name && cres)
  241. {
  242. if(stricmp(resource_name, "SSN Masking") == 0)
  243. {
  244. cres->setValue("All");
  245. continue;
  246. }
  247. else if(stricmp(resource_name, "Driver License Masking") == 0)
  248. {
  249. cres->setValue("1");
  250. continue;
  251. }
  252. }
  253. }
  254. }
  255. }
  256. dbValidateSetting(*secRes,usernum,user->getRealm());
  257. }
  258. return true;
  259. }
  260. bool CBaseSecurityManager::ValidateResources(ISecUser & sec_user, ISecResourceList * resources)
  261. {
  262. CSecurityResourceList * reslist = (CSecurityResourceList*)resources;
  263. if(!reslist)
  264. return true;
  265. IArrayOf<ISecResource>& rlist = reslist->getResourceList();
  266. int nResources = rlist.length();
  267. if (nResources <= 0)
  268. return true;
  269. bool rc = false;
  270. if (m_permissionsCache.isCacheEnabled()==false)
  271. return ValidateResources(sec_user, rlist);
  272. bool* cached_found = (bool*)alloca(nResources*sizeof(bool));
  273. int nFound = m_permissionsCache.lookup(sec_user, rlist, cached_found);
  274. if (nFound >= nResources)
  275. {
  276. return true;
  277. }
  278. IArrayOf<ISecResource> rlist2;
  279. for (int i=0; i < nResources; i++)
  280. {
  281. if (*(cached_found+i) == false)
  282. {
  283. ISecResource& secRes = rlist.item(i);
  284. secRes.Link();
  285. rlist2.append(secRes);
  286. }
  287. }
  288. rc = ValidateResources(sec_user, rlist2);
  289. if (rc)
  290. {
  291. IArrayOf<ISecResource> rlistValid;
  292. for (int i=0; i < rlist2.ordinality(); i++)
  293. {
  294. ISecResource& secRes = rlist2.item(i);
  295. if(secRes.getAccessFlags() >= secRes.getRequiredAccessFlags() || secRes.getAccessFlags() == SecAccess_Unknown)
  296. {
  297. secRes.Link();
  298. rlistValid.append(secRes);
  299. }
  300. }
  301. m_permissionsCache.add(sec_user, rlistValid);
  302. }
  303. return rc;
  304. }
  305. static bool stringDiff(const char* str1, const char* str2)
  306. {
  307. if(!str1 || !*str1)
  308. {
  309. if(!str2 || !*str2)
  310. return false;
  311. else
  312. return true;
  313. }
  314. else
  315. {
  316. if(!str2 || !*str2)
  317. return true;
  318. else
  319. return (strcmp(str1, str2) != 0);
  320. }
  321. }
  322. bool CBaseSecurityManager::ValidateUser(ISecUser & sec_user)
  323. {
  324. StringBuffer clientip(sec_user.getPeer());
  325. StringBuffer otpbuf, sqbuf;
  326. if(m_enableOTP)
  327. {
  328. otpbuf.append(sec_user.getProperty("OTP2FACTOR"));
  329. }
  330. if(m_enableIPRoaming)
  331. {
  332. sqbuf.append(sec_user.getProperty("SEQUEST"));
  333. }
  334. if(m_permissionsCache.isCacheEnabled() && m_permissionsCache.lookup(sec_user))
  335. {
  336. bool bReturn = true;
  337. if(IsIPRestricted(sec_user))
  338. {
  339. const char* cachedclientip = sec_user.getPeer();
  340. if(clientip.length() > 0 && cachedclientip && strncmp(clientip.str(), cachedclientip , clientip.length()) != 0)
  341. {
  342. //we seem to be coming from a different peer... this is not good
  343. WARNLOG("Found user %d in cache, but have to re-validate IP, because it was coming from %s but is now coming from %s",sec_user.getUserID(), cachedclientip, clientip.str());
  344. sec_user.setAuthenticateStatus(AS_INVALID_CREDENTIALS);
  345. sec_user.setPeer(clientip.str());
  346. m_permissionsCache.removeFromUserCache(sec_user);
  347. bReturn = false;
  348. }
  349. }
  350. if(m_enableOTP)
  351. {
  352. const char* old_otp = sec_user.getProperty("OTP2FACTOR");
  353. if(stringDiff(old_otp, otpbuf.str()))
  354. bReturn = false;
  355. }
  356. if(m_enableIPRoaming)
  357. {
  358. const char* old_sq = sec_user.getProperty("SEQUEST");
  359. if(stringDiff(old_sq, sqbuf.str()))
  360. bReturn = false;
  361. }
  362. if(bReturn)
  363. {
  364. sec_user.setAuthenticateStatus(AS_AUTHENTICATED);
  365. return true;
  366. }
  367. }
  368. if(!IsPasswordValid(sec_user))
  369. {
  370. ERRLOG("Password validation failed for user: %s",sec_user.getName());
  371. return false;
  372. }
  373. else
  374. {
  375. if(IsIPRestricted(sec_user)==true)
  376. {
  377. if(ValidateSourceIP(sec_user,m_safeIPList)==false)
  378. {
  379. ERRLOG("IP check failed for user:%s coming from %s",sec_user.getName(),sec_user.getPeer());
  380. sec_user.setAuthenticateStatus(AS_INVALID_CREDENTIALS);
  381. return false;
  382. }
  383. }
  384. if(m_permissionsCache.isCacheEnabled())
  385. m_permissionsCache.add(sec_user);
  386. sec_user.setAuthenticateStatus(AS_AUTHENTICATED);
  387. }
  388. return true;
  389. }
  390. bool CBaseSecurityManager::IsPasswordValid(ISecUser& sec_user)
  391. {
  392. StringBuffer password(sec_user.credentials().getPassword());
  393. EncodePassword(password);
  394. StringBuffer SQLQuery;
  395. buildAuthenticateQuery(sec_user.getName(),password.str(),sec_user.getRealm(),SQLQuery);
  396. return dbauthenticate(sec_user , SQLQuery);
  397. }
  398. bool CBaseSecurityManager::IsIPRestricted(ISecUser& sec_user)
  399. {
  400. const char* iprestricted = sec_user.getProperty("iprestricted");
  401. if(iprestricted!=NULL && strncmp(iprestricted,"1",1)==0)
  402. return true;
  403. return false;
  404. }
  405. void CBaseSecurityManager::EncodePassword(StringBuffer& password)
  406. {
  407. StringBuffer encodedPassword;
  408. switch (m_dbpasswordEncoding)
  409. {
  410. case SecPwEnc_salt_md5:
  411. md5_string(password,encodedPassword);
  412. password.clear().append(encodedPassword.str());
  413. break;
  414. case SecPwEnc_Rijndael:
  415. encrypt(encodedPassword,password.str());
  416. password.clear().append(encodedPassword.str());
  417. break;
  418. case SecPwEnc_salt_accurint_md5:
  419. password.toUpperCase();
  420. md5_string(password,encodedPassword);
  421. password.clear().append(encodedPassword.str());
  422. break;
  423. }
  424. }
  425. bool CBaseSecurityManager::addResources(ISecUser & sec_user, ISecResourceList * Resources)
  426. {
  427. return false;
  428. }
  429. bool CBaseSecurityManager::addUser(ISecUser & user)
  430. {
  431. return false;
  432. }
  433. int CBaseSecurityManager::getUserID(ISecUser& user)
  434. {
  435. return findUser(user.getName(),user.getRealm());
  436. }
  437. bool CBaseSecurityManager::ValidateResources(ISecUser & sec_user,IArrayOf<ISecResource>& rlist)
  438. {
  439. CSecureUser* user = (CSecureUser*)&sec_user;
  440. if(user == NULL)
  441. return false;
  442. int usernum = findUser(user->getName(),user->getRealm());
  443. if(usernum < 0)
  444. {
  445. PrintLog("User number of %s can't be found", user->getName());
  446. return false;
  447. }
  448. ForEachItemIn(x, rlist)
  449. {
  450. ISecResource* res = (ISecResource*)(&(rlist.item(x)));
  451. if(res == NULL)
  452. continue;
  453. dbValidateResource(*res,usernum,user->getRealm());
  454. }
  455. return true;
  456. }
  457. bool CBaseSecurityManager::updateResources(ISecUser & user, ISecResourceList * resources)
  458. {
  459. //("CBaseSecurityManager::updateResources");
  460. if(!resources)
  461. return false;
  462. const char* username = user.getName();
  463. //const char* realm = user.getRealm();
  464. const char* realm = NULL;
  465. int usernum = findUser(username,realm);
  466. if(usernum <= 0)
  467. {
  468. PrintLog("User number of %s can't be found", username);
  469. return false;
  470. }
  471. CSecurityResourceList * reslist = (CSecurityResourceList*)resources;
  472. if (reslist)
  473. {
  474. IArrayOf<ISecResource>& rlist = reslist->getResourceList();
  475. ForEachItemIn(x, rlist)
  476. {
  477. ISecResource* res = (ISecResource*)(&(rlist.item(x)));
  478. if(res == NULL)
  479. continue;
  480. dbUpdateResource(*res,usernum,realm);
  481. }
  482. }
  483. return true;
  484. }
  485. bool CBaseSecurityManager::updateUserPassword(ISecUser& user, const char* newPassword, const char* currPassword)
  486. {
  487. //("CBaseSecurityManager::updateUser");
  488. if(!newPassword)
  489. return false;
  490. StringBuffer password(newPassword);
  491. EncodePassword(password);
  492. const char* realm = NULL;
  493. bool bReturn = dbUpdatePasswrd(user.getName(),realm,password.str());
  494. if(bReturn == true)
  495. user.credentials().setPassword(password.str());
  496. //need to flush the users info from the cache....
  497. if(m_permissionsCache.isCacheEnabled())
  498. m_permissionsCache.removeFromUserCache(user);
  499. return bReturn;
  500. }
  501. void CBaseSecurityManager::logon_failed(const char* user, const char* msg)
  502. {
  503. PrintLog("%s: %s", user, msg);
  504. }
  505. int CBaseSecurityManager::findUser(const char* user,const char* realm)
  506. {
  507. if(user == NULL)
  508. return -1;
  509. synchronized block(m_usermap_mutex);
  510. int* uidptr = m_usermap.getValue(user);
  511. if(uidptr != NULL)
  512. {
  513. return *uidptr;
  514. }
  515. else
  516. {
  517. int uid = dbLookupUser(user,realm);
  518. if(uid >= 0)
  519. {
  520. m_usermap.setValue(user, uid);
  521. }
  522. return uid;
  523. }
  524. }
  525. //#endif //_WIN32