HTTPDigestCredentials.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. //
  2. // HTTPDigestCredentials.cpp
  3. //
  4. // Library: Net
  5. // Package: HTTP
  6. // Module: HTTPDigestCredentials
  7. //
  8. // Copyright (c) 2011, Anton V. Yabchinskiy (arn at bestmx dot ru).
  9. // Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
  10. // and Contributors.
  11. //
  12. // SPDX-License-Identifier: BSL-1.0
  13. //
  14. #include "Poco/DateTime.h"
  15. #include "Poco/DateTimeFormat.h"
  16. #include "Poco/DateTimeFormatter.h"
  17. #include "Poco/Exception.h"
  18. #include "Poco/MD5Engine.h"
  19. #include "Poco/Net/HTTPDigestCredentials.h"
  20. #include "Poco/Net/HTTPRequest.h"
  21. #include "Poco/Net/HTTPResponse.h"
  22. #include "Poco/NumberFormatter.h"
  23. #include "Poco/StringTokenizer.h"
  24. namespace
  25. {
  26. std::string digest(Poco::DigestEngine& engine,
  27. const std::string& a,
  28. const std::string& b,
  29. const std::string& c = std::string(),
  30. const std::string& d = std::string(),
  31. const std::string& e = std::string(),
  32. const std::string& f = std::string())
  33. {
  34. engine.reset();
  35. engine.update(a);
  36. engine.update(':');
  37. engine.update(b);
  38. if (!c.empty())
  39. {
  40. engine.update(':');
  41. engine.update(c);
  42. if (!d.empty())
  43. {
  44. engine.update(':');
  45. engine.update(d);
  46. engine.update(':');
  47. engine.update(e);
  48. engine.update(':');
  49. engine.update(f);
  50. }
  51. }
  52. return Poco::DigestEngine::digestToHex(engine.digest());
  53. }
  54. std::string formatNonceCounter(int counter)
  55. {
  56. return Poco::NumberFormatter::formatHex(counter, 8);
  57. }
  58. }
  59. namespace Poco {
  60. namespace Net {
  61. const std::string HTTPDigestCredentials::SCHEME = "Digest";
  62. const std::string HTTPDigestCredentials::DEFAULT_ALGORITHM("MD5");
  63. const std::string HTTPDigestCredentials::DEFAULT_QOP("");
  64. const std::string HTTPDigestCredentials::NONCE_PARAM("nonce");
  65. const std::string HTTPDigestCredentials::REALM_PARAM("realm");
  66. const std::string HTTPDigestCredentials::QOP_PARAM("qop");
  67. const std::string HTTPDigestCredentials::ALGORITHM_PARAM("algorithm");
  68. const std::string HTTPDigestCredentials::USERNAME_PARAM("username");
  69. const std::string HTTPDigestCredentials::OPAQUE_PARAM("opaque");
  70. const std::string HTTPDigestCredentials::URI_PARAM("uri");
  71. const std::string HTTPDigestCredentials::RESPONSE_PARAM("response");
  72. const std::string HTTPDigestCredentials::AUTH_PARAM("auth");
  73. const std::string HTTPDigestCredentials::CNONCE_PARAM("cnonce");
  74. const std::string HTTPDigestCredentials::NC_PARAM("nc");
  75. int HTTPDigestCredentials::_nonceCounter(0);
  76. Poco::FastMutex HTTPDigestCredentials::_nonceMutex;
  77. HTTPDigestCredentials::HTTPDigestCredentials()
  78. {
  79. }
  80. HTTPDigestCredentials::HTTPDigestCredentials(const std::string& username, const std::string& password):
  81. _username(username),
  82. _password(password)
  83. {
  84. }
  85. HTTPDigestCredentials::~HTTPDigestCredentials()
  86. {
  87. }
  88. void HTTPDigestCredentials::reset()
  89. {
  90. _requestAuthParams.clear();
  91. _nc.clear();
  92. }
  93. void HTTPDigestCredentials::setUsername(const std::string& username)
  94. {
  95. _username = username;
  96. }
  97. void HTTPDigestCredentials::setPassword(const std::string& password)
  98. {
  99. _password = password;
  100. }
  101. void HTTPDigestCredentials::clear()
  102. {
  103. _username.clear();
  104. _password.clear();
  105. }
  106. void HTTPDigestCredentials::authenticate(HTTPRequest& request, const HTTPResponse& response)
  107. {
  108. authenticate(request, HTTPAuthenticationParams(response));
  109. }
  110. void HTTPDigestCredentials::authenticate(HTTPRequest& request, const HTTPAuthenticationParams& responseAuthParams)
  111. {
  112. createAuthParams(request, responseAuthParams);
  113. request.setCredentials(SCHEME, _requestAuthParams.toString());
  114. }
  115. void HTTPDigestCredentials::updateAuthInfo(HTTPRequest& request)
  116. {
  117. updateAuthParams(request);
  118. request.setCredentials(SCHEME, _requestAuthParams.toString());
  119. }
  120. void HTTPDigestCredentials::proxyAuthenticate(HTTPRequest& request, const HTTPResponse& response)
  121. {
  122. proxyAuthenticate(request, HTTPAuthenticationParams(response, HTTPAuthenticationParams::PROXY_AUTHENTICATE));
  123. }
  124. void HTTPDigestCredentials::proxyAuthenticate(HTTPRequest& request, const HTTPAuthenticationParams& responseAuthParams)
  125. {
  126. createAuthParams(request, responseAuthParams);
  127. request.setProxyCredentials(SCHEME, _requestAuthParams.toString());
  128. }
  129. void HTTPDigestCredentials::updateProxyAuthInfo(HTTPRequest& request)
  130. {
  131. updateAuthParams(request);
  132. request.setProxyCredentials(SCHEME, _requestAuthParams.toString());
  133. }
  134. std::string HTTPDigestCredentials::createNonce()
  135. {
  136. Poco::FastMutex::ScopedLock lock(_nonceMutex);
  137. MD5Engine md5;
  138. Timestamp::TimeVal now = Timestamp().epochMicroseconds();
  139. md5.update(&_nonceCounter, sizeof(_nonceCounter));
  140. md5.update(&now, sizeof(now));
  141. ++_nonceCounter;
  142. return DigestEngine::digestToHex(md5.digest());
  143. }
  144. void HTTPDigestCredentials::createAuthParams(const HTTPRequest& request, const HTTPAuthenticationParams& responseAuthParams)
  145. {
  146. // Not implemented: "domain" auth parameter and integrity protection.
  147. if (!responseAuthParams.has(NONCE_PARAM) || !responseAuthParams.has(REALM_PARAM))
  148. throw InvalidArgumentException("Invalid HTTP authentication parameters");
  149. const std::string& algorithm = responseAuthParams.get(ALGORITHM_PARAM, DEFAULT_ALGORITHM);
  150. if (icompare(algorithm, DEFAULT_ALGORITHM) != 0)
  151. throw NotImplementedException("Unsupported digest algorithm", algorithm);
  152. const std::string& nonce = responseAuthParams.get(NONCE_PARAM);
  153. const std::string& qop = responseAuthParams.get(QOP_PARAM, DEFAULT_QOP);
  154. const std::string& realm = responseAuthParams.getRealm();
  155. _requestAuthParams.clear();
  156. _requestAuthParams.set(USERNAME_PARAM, _username);
  157. _requestAuthParams.set(NONCE_PARAM, nonce);
  158. _requestAuthParams.setRealm(realm);
  159. if (responseAuthParams.has(OPAQUE_PARAM))
  160. {
  161. _requestAuthParams.set(OPAQUE_PARAM, responseAuthParams.get(OPAQUE_PARAM));
  162. }
  163. if (qop.empty())
  164. {
  165. updateAuthParams(request);
  166. }
  167. else
  168. {
  169. Poco::StringTokenizer tok(qop, ",", Poco::StringTokenizer::TOK_TRIM);
  170. bool qopSupported = false;
  171. for (Poco::StringTokenizer::Iterator it = tok.begin(); it != tok.end(); ++it)
  172. {
  173. if (icompare(*it, AUTH_PARAM) == 0)
  174. {
  175. qopSupported = true;
  176. _requestAuthParams.set(CNONCE_PARAM, createNonce());
  177. _requestAuthParams.set(QOP_PARAM, *it);
  178. updateAuthParams(request);
  179. break;
  180. }
  181. }
  182. if (!qopSupported)
  183. throw NotImplementedException("Unsupported QoP requested", qop);
  184. }
  185. }
  186. void HTTPDigestCredentials::updateAuthParams(const HTTPRequest& request)
  187. {
  188. MD5Engine engine;
  189. const std::string& qop = _requestAuthParams.get(QOP_PARAM, DEFAULT_QOP);
  190. const std::string& realm = _requestAuthParams.getRealm();
  191. const std::string& nonce = _requestAuthParams.get(NONCE_PARAM);
  192. _requestAuthParams.set(URI_PARAM, request.getURI());
  193. if (qop.empty())
  194. {
  195. const std::string ha1 = digest(engine, _username, realm, _password);
  196. const std::string ha2 = digest(engine, request.getMethod(), request.getURI());
  197. _requestAuthParams.set(RESPONSE_PARAM, digest(engine, ha1, nonce, ha2));
  198. }
  199. else if (icompare(qop, AUTH_PARAM) == 0)
  200. {
  201. const std::string& cnonce = _requestAuthParams.get(CNONCE_PARAM);
  202. const std::string ha1 = digest(engine, _username, realm, _password);
  203. const std::string ha2 = digest(engine, request.getMethod(), request.getURI());
  204. const std::string nc = formatNonceCounter(updateNonceCounter(nonce));
  205. _requestAuthParams.set(NC_PARAM, nc);
  206. _requestAuthParams.set(RESPONSE_PARAM, digest(engine, ha1, nonce, nc, cnonce, qop, ha2));
  207. }
  208. }
  209. bool HTTPDigestCredentials::verifyAuthInfo(const HTTPRequest& request) const
  210. {
  211. HTTPAuthenticationParams params(request);
  212. return verifyAuthParams(request, params);
  213. }
  214. bool HTTPDigestCredentials::verifyAuthParams(const HTTPRequest& request, const HTTPAuthenticationParams& params) const
  215. {
  216. const std::string& nonce = params.get(NONCE_PARAM);
  217. const std::string& realm = params.getRealm();
  218. const std::string& qop = params.get(QOP_PARAM, DEFAULT_QOP);
  219. std::string response;
  220. MD5Engine engine;
  221. if (qop.empty())
  222. {
  223. const std::string ha1 = digest(engine, _username, realm, _password);
  224. const std::string ha2 = digest(engine, request.getMethod(), request.getURI());
  225. response = digest(engine, ha1, nonce, ha2);
  226. }
  227. else if (icompare(qop, AUTH_PARAM) == 0)
  228. {
  229. const std::string& cnonce = params.get(CNONCE_PARAM);
  230. const std::string& nc = params.get(NC_PARAM);
  231. const std::string ha1 = digest(engine, _username, realm, _password);
  232. const std::string ha2 = digest(engine, request.getMethod(), request.getURI());
  233. response = digest(engine, ha1, nonce, nc, cnonce, qop, ha2);
  234. }
  235. return response == params.get(RESPONSE_PARAM);
  236. }
  237. int HTTPDigestCredentials::updateNonceCounter(const std::string& nonce)
  238. {
  239. NonceCounterMap::iterator iter = _nc.find(nonce);
  240. if (iter == _nc.end())
  241. {
  242. iter = _nc.insert(NonceCounterMap::value_type(nonce, 0)).first;
  243. }
  244. iter->second++;
  245. return iter->second;
  246. }
  247. } } // namespace Poco::Net