vfs: check userland buffers before reading them.
[haiku.git] / src / kits / network / libnetapi / HttpAuthentication.cpp
blobea63ecf356dbd46d4c642a44f2c3d1b2cb6f519f
1 /*
2 * Copyright 2010-2013 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Christophe Huriaux, c.huriaux@gmail.com
7 */
10 #include <HttpAuthentication.h>
12 #include <stdlib.h>
13 #include <stdio.h>
15 #include <AutoLocker.h>
18 #if DEBUG > 0
19 #define PRINT(x) printf x
20 #else
21 #define PRINT(x)
22 #endif
24 extern "C" {
25 #ifdef OPENSSL_ENABLED
26 #include <openssl/md5.h>
27 #else
28 #include "md5.h"
29 #endif
32 #ifndef MD5_DIGEST_LENGTH
33 #define MD5_DIGEST_LENGTH 16
34 #endif
36 static const char* kBase64Symbols
37 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
40 BHttpAuthentication::BHttpAuthentication()
42 fAuthenticationMethod(B_HTTP_AUTHENTICATION_NONE)
47 BHttpAuthentication::BHttpAuthentication(const BString& username, const BString& password)
49 fAuthenticationMethod(B_HTTP_AUTHENTICATION_NONE),
50 fUserName(username),
51 fPassword(password)
56 BHttpAuthentication::BHttpAuthentication(const BHttpAuthentication& other)
58 fAuthenticationMethod(other.fAuthenticationMethod),
59 fUserName(other.fUserName),
60 fPassword(other.fPassword),
61 fRealm(other.fRealm),
62 fDigestNonce(other.fDigestNonce),
63 fDigestCnonce(other.fDigestCnonce),
64 fDigestNc(other.fDigestNc),
65 fDigestOpaque(other.fDigestOpaque),
66 fDigestStale(other.fDigestStale),
67 fDigestAlgorithm(other.fDigestAlgorithm),
68 fDigestQop(other.fDigestQop),
69 fAuthorizationString(other.fAuthorizationString)
74 BHttpAuthentication& BHttpAuthentication::operator=(
75 const BHttpAuthentication& other)
77 fAuthenticationMethod = other.fAuthenticationMethod;
78 fUserName = other.fUserName;
79 fPassword = other.fPassword;
80 fRealm = other.fRealm;
81 fDigestNonce = other.fDigestNonce;
82 fDigestCnonce = other.fDigestCnonce;
83 fDigestNc = other.fDigestNc;
84 fDigestOpaque = other.fDigestOpaque;
85 fDigestStale = other.fDigestStale;
86 fDigestAlgorithm = other.fDigestAlgorithm;
87 fDigestQop = other.fDigestQop;
88 fAuthorizationString = other.fAuthorizationString;
89 return *this;
93 // #pragma mark Field modification
96 void
97 BHttpAuthentication::SetUserName(const BString& username)
99 fLock.Lock();
100 fUserName = username;
101 fLock.Unlock();
105 void
106 BHttpAuthentication::SetPassword(const BString& password)
108 fLock.Lock();
109 fPassword = password;
110 fLock.Unlock();
114 void
115 BHttpAuthentication::SetMethod(BHttpAuthenticationMethod method)
117 fLock.Lock();
118 fAuthenticationMethod = method;
119 fLock.Unlock();
123 status_t
124 BHttpAuthentication::Initialize(const BString& wwwAuthenticate)
126 BPrivate::AutoLocker<BLocker> lock(fLock);
128 fAuthenticationMethod = B_HTTP_AUTHENTICATION_NONE;
129 fDigestQop = B_HTTP_QOP_NONE;
131 if (wwwAuthenticate.Length() == 0)
132 return B_BAD_VALUE;
134 BString authRequired;
135 BString additionalData;
136 int32 firstSpace = wwwAuthenticate.FindFirst(' ');
138 if (firstSpace == -1)
139 wwwAuthenticate.CopyInto(authRequired, 0, wwwAuthenticate.Length());
140 else {
141 wwwAuthenticate.CopyInto(authRequired, 0, firstSpace);
142 wwwAuthenticate.CopyInto(additionalData, firstSpace + 1,
143 wwwAuthenticate.Length() - (firstSpace + 1));
146 authRequired.ToLower();
147 if (authRequired == "basic")
148 fAuthenticationMethod = B_HTTP_AUTHENTICATION_BASIC;
149 else if (authRequired == "digest") {
150 fAuthenticationMethod = B_HTTP_AUTHENTICATION_DIGEST;
151 fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_MD5;
152 } else
153 return B_ERROR;
156 while (additionalData.Length()) {
157 int32 firstComma = additionalData.FindFirst(',');
158 if (firstComma == -1)
159 firstComma = additionalData.Length();
161 BString value;
162 additionalData.MoveInto(value, 0, firstComma);
163 additionalData.Remove(0, 1);
164 additionalData.Trim();
166 int32 equal = value.FindFirst('=');
167 if (equal <= 0)
168 continue;
170 BString name;
171 value.MoveInto(name, 0, equal);
172 value.Remove(0, 1);
173 name.ToLower();
175 if (value.Length() > 0 && value[0] == '"') {
176 value.Remove(0, 1);
177 value.Remove(value.Length() - 1, 1);
180 PRINT(("HttpAuth: name=%s, value=%s\n", name.String(),
181 value.String()));
183 if (name == "realm")
184 fRealm = value;
185 else if (name == "nonce")
186 fDigestNonce = value;
187 else if (name == "opaque")
188 fDigestOpaque = value;
189 else if (name == "stale") {
190 value.ToLower();
191 fDigestStale = (value == "true");
192 } else if (name == "algorithm") {
193 value.ToLower();
195 if (value == "md5")
196 fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_MD5;
197 else if (value == "md5-sess")
198 fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS;
199 else
200 fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_NONE;
201 } else if (name == "qop")
202 fDigestQop = B_HTTP_QOP_AUTH;
205 if (fAuthenticationMethod == B_HTTP_AUTHENTICATION_BASIC)
206 return B_OK;
207 else if (fAuthenticationMethod == B_HTTP_AUTHENTICATION_DIGEST
208 && fDigestNonce.Length() > 0
209 && fDigestAlgorithm != B_HTTP_AUTHENTICATION_ALGORITHM_NONE) {
210 return B_OK;
211 } else
212 return B_ERROR;
216 // #pragma mark Field access
219 const BString&
220 BHttpAuthentication::UserName() const
222 BPrivate::AutoLocker<BLocker> lock(fLock);
223 return fUserName;
227 const BString&
228 BHttpAuthentication::Password() const
230 BPrivate::AutoLocker<BLocker> lock(fLock);
231 return fPassword;
235 BHttpAuthenticationMethod
236 BHttpAuthentication::Method() const
238 BPrivate::AutoLocker<BLocker> lock(fLock);
239 return fAuthenticationMethod;
243 BString
244 BHttpAuthentication::Authorization(const BUrl& url, const BString& method) const
246 BPrivate::AutoLocker<BLocker> lock(fLock);
247 BString authorizationString;
249 switch (fAuthenticationMethod) {
250 case B_HTTP_AUTHENTICATION_NONE:
251 break;
253 case B_HTTP_AUTHENTICATION_BASIC:
255 BString basicEncode;
256 basicEncode << fUserName << ':' << fPassword;
257 authorizationString << "Basic " << Base64Encode(basicEncode);
258 break;
261 case B_HTTP_AUTHENTICATION_DIGEST:
262 case B_HTTP_AUTHENTICATION_IE_DIGEST:
263 authorizationString << "Digest " << "username=\"" << fUserName
264 << "\", realm=\"" << fRealm << "\", nonce=\"" << fDigestNonce
265 << "\", algorithm=";
267 if (fDigestAlgorithm == B_HTTP_AUTHENTICATION_ALGORITHM_MD5)
268 authorizationString << "MD5";
269 else
270 authorizationString << "MD5-sess";
272 if (fDigestOpaque.Length() > 0)
273 authorizationString << ", opaque=\"" << fDigestOpaque << "\"";
275 if (fDigestQop != B_HTTP_QOP_NONE) {
276 if (fDigestCnonce.Length() == 0) {
277 fDigestCnonce = _H(fDigestOpaque);
278 //fDigestCnonce = "03c6790a055cbbac";
279 fDigestNc = 0;
282 authorizationString << ", uri=\"" << url.Path() << "\"";
283 authorizationString << ", qop=auth, cnonce=\"" << fDigestCnonce
284 << "\"";
286 char strNc[9];
287 snprintf(strNc, 9, "%08x", ++fDigestNc);
288 authorizationString << ", nc=" << strNc;
292 authorizationString << ", response=\""
293 << _DigestResponse(url.Path(), method) << "\"";
294 break;
297 return authorizationString;
301 // #pragma mark Base64 encoding
304 /*static*/ BString
305 BHttpAuthentication::Base64Encode(const BString& string)
307 BString result;
308 BString tmpString = string;
310 while (tmpString.Length()) {
311 char in[3] = { 0, 0, 0 };
312 char out[4] = { 0, 0, 0, 0 };
313 int8 remaining = tmpString.Length();
315 tmpString.MoveInto(in, 0, 3);
317 out[0] = (in[0] & 0xFC) >> 2;
318 out[1] = ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4);
319 out[2] = ((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6);
320 out[3] = in[2] & 0x3F;
322 for (int i = 0; i < 4; i++)
323 out[i] = kBase64Symbols[(int)out[i]];
325 // Add padding if the input length is not a multiple
326 // of 3
327 switch (remaining) {
328 case 1:
329 out[2] = '=';
330 // Fall through
331 case 2:
332 out[3] = '=';
333 break;
336 result.Append(out, 4);
339 return result;
343 /*static*/ BString
344 BHttpAuthentication::Base64Decode(const BString& string)
346 BString result;
348 // Check for invalid input
349 if (string.Length() % 4 != 0)
350 return result;
352 BString base64Reverse(kBase64Symbols);
354 BString tmpString(string);
355 while (tmpString.Length()) {
356 char in[4] = { 0, 0, 0, 0 };
357 char out[3] = { 0, 0, 0 };
359 tmpString.MoveInto(in, 0, 4);
361 for (int i = 0; i < 4; i++) {
362 if (in[i] == '=')
363 in[i] = 0;
364 else
365 in[i] = base64Reverse.FindFirst(in[i], 0);
368 out[0] = (in[0] << 2) | ((in[1] & 0x30) >> 4);
369 out[1] = ((in[1] & 0x0F) << 4) | ((in[2] & 0x3C) >> 2);
370 out[2] = ((in[2] & 0x03) << 6) | in[3];
372 result.Append(out, 3);
375 return result;
379 BString
380 BHttpAuthentication::_DigestResponse(const BString& uri, const BString& method) const
382 PRINT(("HttpAuth: Computing digest response: \n"));
383 PRINT(("HttpAuth: > username = %s\n", fUserName.String()));
384 PRINT(("HttpAuth: > password = %s\n", fPassword.String()));
385 PRINT(("HttpAuth: > realm = %s\n", fRealm.String()));
386 PRINT(("HttpAuth: > nonce = %s\n", fDigestNonce.String()));
387 PRINT(("HttpAuth: > cnonce = %s\n", fDigestCnonce.String()));
388 PRINT(("HttpAuth: > nc = %08x\n", fDigestNc));
389 PRINT(("HttpAuth: > uri = %s\n", uri.String()));
390 PRINT(("HttpAuth: > method = %s\n", method.String()));
391 PRINT(("HttpAuth: > algorithm = %d (MD5:%d, MD5-sess:%d)\n",
392 fDigestAlgorithm, B_HTTP_AUTHENTICATION_ALGORITHM_MD5,
393 B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS));
395 BString A1;
396 A1 << fUserName << ':' << fRealm << ':' << fPassword;
398 if (fDigestAlgorithm == B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS) {
399 A1 = _H(A1);
400 A1 << ':' << fDigestNonce << ':' << fDigestCnonce;
404 BString A2;
405 A2 << method << ':' << uri;
407 PRINT(("HttpAuth: > A1 = %s\n", A1.String()));
408 PRINT(("HttpAuth: > A2 = %s\n", A2.String()));
409 PRINT(("HttpAuth: > H(A1) = %s\n", _H(A1).String()));
410 PRINT(("HttpAuth: > H(A2) = %s\n", _H(A2).String()));
412 char strNc[9];
413 snprintf(strNc, 9, "%08x", fDigestNc);
415 BString secretResp;
416 secretResp << fDigestNonce << ':' << strNc << ':' << fDigestCnonce
417 << ":auth:" << _H(A2);
419 PRINT(("HttpAuth: > R2 = %s\n", secretResp.String()));
421 BString response = _KD(_H(A1), secretResp);
422 PRINT(("HttpAuth: > response = %s\n", response.String()));
424 return response;
428 BString
429 BHttpAuthentication::_H(const BString& value) const
431 MD5_CTX context;
432 uchar hashResult[MD5_DIGEST_LENGTH];
433 MD5_Init(&context);
434 MD5_Update(&context, (void *)(value.String()), value.Length());
435 MD5_Final(hashResult, &context);
437 BString result;
438 // Preallocate the string
439 char* resultChar = result.LockBuffer(MD5_DIGEST_LENGTH * 2);
440 if (resultChar == NULL)
441 return BString();
443 for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
444 char c = ((hashResult[i] & 0xF0) >> 4);
445 c += (c > 9) ? 'a' - 10 : '0';
446 resultChar[0] = c;
447 resultChar++;
449 c = hashResult[i] & 0x0F;
450 c += (c > 9) ? 'a' - 10 : '0';
451 resultChar[0] = c;
452 resultChar++;
454 result.UnlockBuffer(MD5_DIGEST_LENGTH * 2);
456 return result;
460 BString
461 BHttpAuthentication::_KD(const BString& secret, const BString& data) const
463 BString encode;
464 encode << secret << ':' << data;
466 return _H(encode);