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
15
16 #include "Poco/DateTime.h"
17 #include "Poco/DateTimeFormat.h"
18 #include "Poco/DateTimeFormatter.h"
19 #include "Poco/Exception.h"
20 #include "Poco/MD5Engine.h"
21 #include "Poco/Net/HTTPDigestCredentials.h"
22 #include "Poco/Net/HTTPRequest.h"
23 #include "Poco/Net/HTTPResponse.h"
24 #include "Poco/NumberFormatter.h"
25 #include "Poco/StringTokenizer.h"
26
27
28 namespace
29 {
digest(Poco::DigestEngine & engine,const std::string & a,const std::string & b,const std::string & c=std::string (),const std::string & d=std::string (),const std::string & e=std::string (),const std::string & f=std::string ())30 std::string digest(Poco::DigestEngine& engine,
31 const std::string& a,
32 const std::string& b,
33 const std::string& c = std::string(),
34 const std::string& d = std::string(),
35 const std::string& e = std::string(),
36 const std::string& f = std::string())
37 {
38 engine.reset();
39 engine.update(a);
40 engine.update(':');
41 engine.update(b);
42 if (!c.empty())
43 {
44 engine.update(':');
45 engine.update(c);
46 if (!d.empty())
47 {
48 engine.update(':');
49 engine.update(d);
50 engine.update(':');
51 engine.update(e);
52 engine.update(':');
53 engine.update(f);
54 }
55 }
56 return Poco::DigestEngine::digestToHex(engine.digest());
57 }
58
formatNonceCounter(int counter)59 std::string formatNonceCounter(int counter)
60 {
61 return Poco::NumberFormatter::formatHex(counter, 8);
62 }
63 }
64
65
66 namespace Poco {
67 namespace Net {
68
69
70 const std::string HTTPDigestCredentials::SCHEME = "Digest";
71 const std::string HTTPDigestCredentials::DEFAULT_ALGORITHM("MD5");
72 const std::string HTTPDigestCredentials::DEFAULT_QOP("");
73 const std::string HTTPDigestCredentials::NONCE_PARAM("nonce");
74 const std::string HTTPDigestCredentials::REALM_PARAM("realm");
75 const std::string HTTPDigestCredentials::QOP_PARAM("qop");
76 const std::string HTTPDigestCredentials::ALGORITHM_PARAM("algorithm");
77 const std::string HTTPDigestCredentials::USERNAME_PARAM("username");
78 const std::string HTTPDigestCredentials::OPAQUE_PARAM("opaque");
79 const std::string HTTPDigestCredentials::URI_PARAM("uri");
80 const std::string HTTPDigestCredentials::RESPONSE_PARAM("response");
81 const std::string HTTPDigestCredentials::AUTH_PARAM("auth");
82 const std::string HTTPDigestCredentials::CNONCE_PARAM("cnonce");
83 const std::string HTTPDigestCredentials::NC_PARAM("nc");
84 int HTTPDigestCredentials::_nonceCounter(0);
85 Poco::FastMutex HTTPDigestCredentials::_nonceMutex;
86
87
HTTPDigestCredentials()88 HTTPDigestCredentials::HTTPDigestCredentials()
89 {
90 }
91
92
HTTPDigestCredentials(const std::string & username,const std::string & password)93 HTTPDigestCredentials::HTTPDigestCredentials(const std::string& username, const std::string& password):
94 _username(username),
95 _password(password)
96 {
97 }
98
99
~HTTPDigestCredentials()100 HTTPDigestCredentials::~HTTPDigestCredentials()
101 {
102 }
103
104
reset()105 void HTTPDigestCredentials::reset()
106 {
107 _requestAuthParams.clear();
108 _nc.clear();
109 }
110
111
setUsername(const std::string & username)112 void HTTPDigestCredentials::setUsername(const std::string& username)
113 {
114 _username = username;
115 }
116
117
setPassword(const std::string & password)118 void HTTPDigestCredentials::setPassword(const std::string& password)
119 {
120 _password = password;
121 }
122
123
clear()124 void HTTPDigestCredentials::clear()
125 {
126 _username.clear();
127 _password.clear();
128 }
129
130
authenticate(HTTPRequest & request,const HTTPResponse & response)131 void HTTPDigestCredentials::authenticate(HTTPRequest& request, const HTTPResponse& response)
132 {
133 authenticate(request, HTTPAuthenticationParams(response));
134 }
135
136
authenticate(HTTPRequest & request,const HTTPAuthenticationParams & responseAuthParams)137 void HTTPDigestCredentials::authenticate(HTTPRequest& request, const HTTPAuthenticationParams& responseAuthParams)
138 {
139 createAuthParams(request, responseAuthParams);
140 request.setCredentials(SCHEME, _requestAuthParams.toString());
141 }
142
143
updateAuthInfo(HTTPRequest & request)144 void HTTPDigestCredentials::updateAuthInfo(HTTPRequest& request)
145 {
146 updateAuthParams(request);
147 request.setCredentials(SCHEME, _requestAuthParams.toString());
148 }
149
150
proxyAuthenticate(HTTPRequest & request,const HTTPResponse & response)151 void HTTPDigestCredentials::proxyAuthenticate(HTTPRequest& request, const HTTPResponse& response)
152 {
153 proxyAuthenticate(request, HTTPAuthenticationParams(response, HTTPAuthenticationParams::PROXY_AUTHENTICATE));
154 }
155
156
proxyAuthenticate(HTTPRequest & request,const HTTPAuthenticationParams & responseAuthParams)157 void HTTPDigestCredentials::proxyAuthenticate(HTTPRequest& request, const HTTPAuthenticationParams& responseAuthParams)
158 {
159 createAuthParams(request, responseAuthParams);
160 request.setProxyCredentials(SCHEME, _requestAuthParams.toString());
161 }
162
163
updateProxyAuthInfo(HTTPRequest & request)164 void HTTPDigestCredentials::updateProxyAuthInfo(HTTPRequest& request)
165 {
166 updateAuthParams(request);
167 request.setProxyCredentials(SCHEME, _requestAuthParams.toString());
168 }
169
170
createNonce()171 std::string HTTPDigestCredentials::createNonce()
172 {
173 Poco::FastMutex::ScopedLock lock(_nonceMutex);
174
175 MD5Engine md5;
176 Timestamp::TimeVal now = Timestamp().epochMicroseconds();
177
178 md5.update(&_nonceCounter, sizeof(_nonceCounter));
179 md5.update(&now, sizeof(now));
180
181 ++_nonceCounter;
182
183 return DigestEngine::digestToHex(md5.digest());
184 }
185
186
createAuthParams(const HTTPRequest & request,const HTTPAuthenticationParams & responseAuthParams)187 void HTTPDigestCredentials::createAuthParams(const HTTPRequest& request, const HTTPAuthenticationParams& responseAuthParams)
188 {
189 // Not implemented: "domain" auth parameter and integrity protection.
190
191 if (!responseAuthParams.has(NONCE_PARAM) || !responseAuthParams.has(REALM_PARAM))
192 throw InvalidArgumentException("Invalid HTTP authentication parameters");
193
194 const std::string& algorithm = responseAuthParams.get(ALGORITHM_PARAM, DEFAULT_ALGORITHM);
195
196 if (icompare(algorithm, DEFAULT_ALGORITHM) != 0)
197 throw NotImplementedException("Unsupported digest algorithm", algorithm);
198
199 const std::string& nonce = responseAuthParams.get(NONCE_PARAM);
200 const std::string& qop = responseAuthParams.get(QOP_PARAM, DEFAULT_QOP);
201 const std::string& realm = responseAuthParams.getRealm();
202
203 _requestAuthParams.clear();
204 _requestAuthParams.set(USERNAME_PARAM, _username);
205 _requestAuthParams.set(NONCE_PARAM, nonce);
206 _requestAuthParams.setRealm(realm);
207 if (responseAuthParams.has(OPAQUE_PARAM))
208 {
209 _requestAuthParams.set(OPAQUE_PARAM, responseAuthParams.get(OPAQUE_PARAM));
210 }
211
212 if (qop.empty())
213 {
214 updateAuthParams(request);
215 }
216 else
217 {
218 Poco::StringTokenizer tok(qop, ",", Poco::StringTokenizer::TOK_TRIM);
219 bool qopSupported = false;
220 for (Poco::StringTokenizer::Iterator it = tok.begin(); it != tok.end(); ++it)
221 {
222 if (icompare(*it, AUTH_PARAM) == 0)
223 {
224 qopSupported = true;
225 _requestAuthParams.set(CNONCE_PARAM, createNonce());
226 _requestAuthParams.set(QOP_PARAM, *it);
227 updateAuthParams(request);
228 break;
229 }
230 }
231 if (!qopSupported)
232 throw NotImplementedException("Unsupported QoP requested", qop);
233 }
234 }
235
236
updateAuthParams(const HTTPRequest & request)237 void HTTPDigestCredentials::updateAuthParams(const HTTPRequest& request)
238 {
239 MD5Engine engine;
240 const std::string qop = _requestAuthParams.get(QOP_PARAM, DEFAULT_QOP);
241 const std::string realm = _requestAuthParams.getRealm();
242 const std::string nonce = _requestAuthParams.get(NONCE_PARAM);
243
244 _requestAuthParams.set(URI_PARAM, request.getURI());
245
246 if (qop.empty())
247 {
248 const std::string ha1 = digest(engine, _username, realm, _password);
249 const std::string ha2 = digest(engine, request.getMethod(), request.getURI());
250
251 _requestAuthParams.set(RESPONSE_PARAM, digest(engine, ha1, nonce, ha2));
252 }
253 else if (icompare(qop, AUTH_PARAM) == 0)
254 {
255 const std::string cnonce = _requestAuthParams.get(CNONCE_PARAM);
256
257 const std::string ha1 = digest(engine, _username, realm, _password);
258 const std::string ha2 = digest(engine, request.getMethod(), request.getURI());
259 const std::string nc = formatNonceCounter(updateNonceCounter(nonce));
260
261 _requestAuthParams.set(NC_PARAM, nc);
262 _requestAuthParams.set(RESPONSE_PARAM, digest(engine, ha1, nonce, nc, cnonce, qop, ha2));
263 }
264 }
265
266
verifyAuthInfo(const HTTPRequest & request) const267 bool HTTPDigestCredentials::verifyAuthInfo(const HTTPRequest& request) const
268 {
269 HTTPAuthenticationParams params(request);
270 return verifyAuthParams(request, params);
271 }
272
273
verifyAuthParams(const HTTPRequest & request,const HTTPAuthenticationParams & params) const274 bool HTTPDigestCredentials::verifyAuthParams(const HTTPRequest& request, const HTTPAuthenticationParams& params) const
275 {
276 const std::string& nonce = params.get(NONCE_PARAM);
277 const std::string& realm = params.getRealm();
278 const std::string& qop = params.get(QOP_PARAM, DEFAULT_QOP);
279 std::string response;
280 MD5Engine engine;
281 if (qop.empty())
282 {
283 const std::string ha1 = digest(engine, _username, realm, _password);
284 const std::string ha2 = digest(engine, request.getMethod(), request.getURI());
285 response = digest(engine, ha1, nonce, ha2);
286 }
287 else if (icompare(qop, AUTH_PARAM) == 0)
288 {
289 const std::string& cnonce = params.get(CNONCE_PARAM);
290 const std::string& nc = params.get(NC_PARAM);
291 const std::string ha1 = digest(engine, _username, realm, _password);
292 const std::string ha2 = digest(engine, request.getMethod(), request.getURI());
293 response = digest(engine, ha1, nonce, nc, cnonce, qop, ha2);
294 }
295 return response == params.get(RESPONSE_PARAM);
296 }
297
298
updateNonceCounter(const std::string & nonce)299 int HTTPDigestCredentials::updateNonceCounter(const std::string& nonce)
300 {
301 NonceCounterMap::iterator iter = _nc.find(nonce);
302
303 if (iter == _nc.end())
304 {
305 iter = _nc.insert(NonceCounterMap::value_type(nonce, 0)).first;
306 }
307 iter->second++;
308
309 return iter->second;
310 }
311
312
313 } } // namespace Poco::Net
314