htpasswdSecurity.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2013 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. #include "basesecurity.hpp"
  15. #include "authmap.ipp"
  16. #include <apr_md5.h>
  17. #include "htpasswdSecurity.hpp"
  18. class CHtpasswdSecurityManager : public CBaseSecurityManager
  19. {
  20. public:
  21. CHtpasswdSecurityManager(const char *serviceName, IPropertyTree *secMgrCfg, IPropertyTree *bindConfig) : CBaseSecurityManager(serviceName, (IPropertyTree *)NULL)
  22. {
  23. if (secMgrCfg)
  24. pwFile.set(secMgrCfg->queryProp("@htpasswdFile"));
  25. if(pwFile.isEmpty())
  26. throw MakeStringException(-1, "htpasswdFile not found in configuration");
  27. {
  28. Owned<IPropertyTree> authcfg = bindConfig->getPropTree("Authenticate");
  29. if(authcfg != nullptr)
  30. {
  31. StringBuffer authxml;
  32. toXML(authcfg, authxml);
  33. DBGLOG("HTPASS Authenticate Config: %s", authxml.str());
  34. }
  35. }
  36. {
  37. Owned<IPropertyTree> custombindingconfig = bindConfig->getPropTree("CustomBindingParameters");
  38. if(custombindingconfig != nullptr)
  39. {
  40. StringBuffer custconfigxml;
  41. toXML(custombindingconfig, custconfigxml);
  42. DBGLOG("HTPASS Custom Binding Config: %s", custconfigxml.str());
  43. }
  44. }
  45. apr_initialized = false;
  46. }
  47. ~CHtpasswdSecurityManager()
  48. {
  49. userMap.kill();
  50. }
  51. secManagerType querySecMgrType()
  52. {
  53. return SMT_HTPasswd;
  54. }
  55. inline virtual const char* querySecMgrTypeName() { return "htpasswd"; }
  56. IAuthMap * createAuthMap(IPropertyTree * authconfig)
  57. {
  58. CAuthMap* authmap = new CAuthMap(this);
  59. Owned<IPropertyTreeIterator> loc_iter;
  60. loc_iter.setown(authconfig->getElements(".//Location"));
  61. if (loc_iter)
  62. {
  63. IPropertyTree *location = NULL;
  64. loc_iter->first();
  65. while(loc_iter->isValid())
  66. {
  67. location = &loc_iter->query();
  68. if (location)
  69. {
  70. StringBuffer pathstr, rstr, required, description;
  71. location->getProp("@path", pathstr);
  72. location->getProp("@resource", rstr);
  73. location->getProp("@required", required);
  74. location->getProp("@description", description);
  75. if(pathstr.length() == 0)
  76. throw MakeStringException(-1, "path empty in Authenticate/Location");
  77. if(rstr.length() == 0)
  78. throw MakeStringException(-1, "resource empty in Authenticate/Location");
  79. ISecResourceList* rlist = authmap->queryResourceList(pathstr.str());
  80. if(rlist == NULL)
  81. {
  82. rlist = createResourceList("htpasswdsecurity");
  83. authmap->add(pathstr.str(), rlist);
  84. }
  85. ISecResource* rs = rlist->addResource(rstr.str());
  86. SecAccessFlags requiredaccess = str2perm(required.str());
  87. rs->setRequiredAccessFlags(requiredaccess);
  88. rs->setDescription(description.str());
  89. rs->setAccessFlags(SecAccess_Full);//grant full access to authenticated users
  90. }
  91. loc_iter->next();
  92. }
  93. }
  94. return authmap;
  95. }
  96. IAuthMap * createFeatureMap(IPropertyTree * authconfig)
  97. {
  98. CAuthMap* feature_authmap = new CAuthMap(this);
  99. Owned<IPropertyTreeIterator> feature_iter;
  100. feature_iter.setown(authconfig->getElements(".//Feature"));
  101. ForEach(*feature_iter)
  102. {
  103. IPropertyTree *feature = NULL;
  104. feature = &feature_iter->query();
  105. if (feature)
  106. {
  107. StringBuffer pathstr, rstr, required, description;
  108. feature->getProp("@path", pathstr);
  109. feature->getProp("@resource", rstr);
  110. feature->getProp("@required", required);
  111. feature->getProp("@description", description);
  112. ISecResourceList* rlist = feature_authmap->queryResourceList(pathstr.str());
  113. if(rlist == NULL)
  114. {
  115. rlist = createResourceList(pathstr.str());
  116. feature_authmap->add(pathstr.str(), rlist);
  117. }
  118. if (!rstr.isEmpty())
  119. {
  120. ISecResource* rs = rlist->addResource(rstr.str());
  121. SecAccessFlags requiredaccess = str2perm(required.str());
  122. rs->setRequiredAccessFlags(requiredaccess);
  123. rs->setDescription(description.str());
  124. rs->setAccessFlags(SecAccess_Full);//grant full access to authenticated users
  125. }
  126. }
  127. }
  128. return feature_authmap;
  129. }
  130. protected:
  131. //ISecManager
  132. bool IsPasswordValid(ISecUser& sec_user)
  133. {
  134. StringBuffer user;
  135. user.append(sec_user.getName());
  136. if (0 == user.length())
  137. throw MakeStringException(-1, "htpasswd User name is NULL");
  138. if (sec_user.credentials().getSessionToken() != 0)//Already authenticated it token
  139. return true;
  140. CriticalBlock block(crit);
  141. if (!apr_initialized)
  142. initAPR();
  143. loadPwds();//reload password file if modified
  144. StringBuffer *encPW = userMap.getValue(user.str());
  145. if (encPW && encPW->length())
  146. {
  147. apr_status_t rc = apr_password_validate(sec_user.credentials().getPassword(), encPW->str());
  148. if (rc != APR_SUCCESS)
  149. DBGLOG("htpasswd authentication for user %s failed - APR RC %d", user.str(), rc );
  150. return rc == APR_SUCCESS;
  151. }
  152. DBGLOG("User %s not in htpasswd file", user.str());
  153. return false;
  154. }
  155. const char * getDescription() override
  156. {
  157. return "HTPASSWD Security Manager";
  158. }
  159. bool authorize(ISecUser & user, ISecResourceList * resources, IEspSecureContext* secureContext) override
  160. {
  161. return IsPasswordValid(user);
  162. }
  163. unsigned getPasswordExpirationWarningDays() override
  164. {
  165. return -2;//never expires
  166. }
  167. SecAccessFlags authorizeEx(SecResourceType rtype, ISecUser & user, const char * resourcename, IEspSecureContext* secureContext) override
  168. {
  169. return SecAccess_Full;//grant full access to authenticated users
  170. }
  171. SecAccessFlags getAccessFlagsEx(SecResourceType rtype, ISecUser& sec_user, const char* resourcename) override
  172. {
  173. return SecAccess_Full;//grant full access to authenticated users
  174. }
  175. SecAccessFlags authorizeFileScope(ISecUser & user, const char * filescope) override
  176. {
  177. return SecAccess_Full;//grant full access to authenticated users
  178. }
  179. bool authorizeViewScope(ISecUser & user, ISecResourceList * resources)
  180. {
  181. int nResources = resources->count();
  182. for (int ri = 0; ri < nResources; ri++)
  183. {
  184. ISecResource* res = resources->queryResource(ri);
  185. if(res != nullptr)
  186. {
  187. assertex(res->getResourceType() == RT_VIEW_SCOPE);
  188. res->setAccessFlags(SecAccess_Full);//grant full access to authenticated users
  189. }
  190. }
  191. return true;//success
  192. }
  193. SecAccessFlags authorizeWorkunitScope(ISecUser & user, const char * filescope) override
  194. {
  195. return SecAccess_Full;//grant full access to authenticated users
  196. }
  197. bool logoutUser(ISecUser & user) override
  198. {
  199. return true;
  200. }
  201. private:
  202. void initAPR()
  203. {
  204. try
  205. {
  206. apr_status_t rc = apr_md5_init(&md5_ctx);
  207. if (rc != APR_SUCCESS)
  208. throw MakeStringException(-1, "htpasswd apr_md5_init returns error %d", rc );
  209. apr_initialized = true;
  210. }
  211. catch (...)
  212. {
  213. throw MakeStringException(-1, "htpasswd exception calling apr_md5_init");
  214. }
  215. }
  216. bool loadPwds()
  217. {
  218. try
  219. {
  220. if (!pwFile.length())
  221. throw MakeStringException(-1, "htpasswd Password file not specified");
  222. Owned<IFile> file = createIFile(pwFile.str());
  223. if (!file->exists())
  224. {
  225. userMap.kill();
  226. throw MakeStringException(-1, "htpasswd Password file does not exist");
  227. }
  228. bool isDir;
  229. offset_t size;
  230. CDateTime whenChanged;
  231. file->getInfo(isDir,size,whenChanged);
  232. if (isDir)
  233. {
  234. userMap.kill();
  235. throw MakeStringException(-1, "htpasswd Password file specifies a directory");
  236. }
  237. if (0 == whenChanged.compare(pwFileLastMod))
  238. return true;//Don't reload if file unchanged
  239. userMap.kill();
  240. OwnedIFileIO io = file->open(IFOread);
  241. if (!io)
  242. throw MakeStringException(-1, "htpasswd Unable to open Password file");
  243. MemoryBuffer mb;
  244. size32_t count = read(io, 0, (size32_t)-1, mb);
  245. if (0 == count)
  246. throw MakeStringException(-1, "htpasswd Password file is empty");
  247. mb.append((char)NULL);
  248. char * p = (char*)mb.toByteArray();
  249. char *saveptr;
  250. const char * seps = "\f\r\n";
  251. char * next = strtok_r(p, seps, &saveptr);
  252. if (next)
  253. {
  254. do
  255. {
  256. char * colon = strchr(next,':');
  257. if (NULL == colon)
  258. throw MakeStringException(-1, "htpasswd Password file appears malformed");
  259. *colon = (char)NULL;
  260. userMap.setValue(next, colon+1);//username, enctypted password
  261. next = strtok_r(NULL, seps, &saveptr);
  262. } while (next);
  263. }
  264. io->close();
  265. pwFileLastMod = whenChanged;//remember when last changed
  266. }
  267. catch(IException*)
  268. {
  269. throw MakeStringException(-1, "htpasswd Exception accessing Password file");
  270. }
  271. return true;
  272. }
  273. private:
  274. mutable CriticalSection crit;
  275. StringBuffer pwFile;
  276. CDateTime pwFileLastMod;
  277. bool apr_initialized;
  278. MapStringTo<StringBuffer> userMap;
  279. apr_md5_ctx_t md5_ctx;
  280. };
  281. extern "C"
  282. {
  283. HTPASSWDSECURITY_API ISecManager * createInstance(const char *serviceName, IPropertyTree &secMgrCfg, IPropertyTree &bndCfg)
  284. {
  285. return new CHtpasswdSecurityManager(serviceName, &secMgrCfg, &bndCfg);
  286. }
  287. }