tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / network / libnetapi / HttpAuthentication.cpp
blob32e47dbf5a5385cac214431fb9a7a5138c8949b7
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 // #pragma mark Field modification
59 void
60 BHttpAuthentication::SetUserName(const BString& username)
62 fLock.Lock();
63 fUserName = username;
64 fLock.Unlock();
68 void
69 BHttpAuthentication::SetPassword(const BString& password)
71 fLock.Lock();
72 fPassword = password;
73 fLock.Unlock();
77 void
78 BHttpAuthentication::SetMethod(BHttpAuthenticationMethod method)
80 fLock.Lock();
81 fAuthenticationMethod = method;
82 fLock.Unlock();
86 status_t
87 BHttpAuthentication::Initialize(const BString& wwwAuthenticate)
89 BPrivate::AutoLocker<BLocker> lock(fLock);
91 fAuthenticationMethod = B_HTTP_AUTHENTICATION_NONE;
92 fDigestQop = B_HTTP_QOP_NONE;
94 if (wwwAuthenticate.Length() == 0)
95 return B_BAD_VALUE;
97 BString authRequired;
98 BString additionalData;
99 int32 firstSpace = wwwAuthenticate.FindFirst(' ');
101 if (firstSpace == -1)
102 wwwAuthenticate.CopyInto(authRequired, 0, wwwAuthenticate.Length());
103 else {
104 wwwAuthenticate.CopyInto(authRequired, 0, firstSpace);
105 wwwAuthenticate.CopyInto(additionalData, firstSpace + 1,
106 wwwAuthenticate.Length() - (firstSpace + 1));
109 authRequired.ToLower();
110 if (authRequired == "basic")
111 fAuthenticationMethod = B_HTTP_AUTHENTICATION_BASIC;
112 else if (authRequired == "digest") {
113 fAuthenticationMethod = B_HTTP_AUTHENTICATION_DIGEST;
114 fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_MD5;
115 } else
116 return B_ERROR;
119 while (additionalData.Length()) {
120 int32 firstComma = additionalData.FindFirst(',');
121 if (firstComma == -1)
122 firstComma = additionalData.Length();
124 BString value;
125 additionalData.MoveInto(value, 0, firstComma);
126 additionalData.Remove(0, 1);
127 additionalData.Trim();
129 int32 equal = value.FindFirst('=');
130 if (equal <= 0)
131 continue;
133 BString name;
134 value.MoveInto(name, 0, equal);
135 value.Remove(0, 1);
136 name.ToLower();
138 if (value.Length() > 0 && value[0] == '"') {
139 value.Remove(0, 1);
140 value.Remove(value.Length() - 1, 1);
143 PRINT(("HttpAuth: name=%s, value=%s\n", name.String(),
144 value.String()));
146 if (name == "realm")
147 fRealm = value;
148 else if (name == "nonce")
149 fDigestNonce = value;
150 else if (name == "opaque")
151 fDigestOpaque = value;
152 else if (name == "stale") {
153 value.ToLower();
154 fDigestStale = (value == "true");
155 } else if (name == "algorithm") {
156 value.ToLower();
158 if (value == "md5")
159 fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_MD5;
160 else if (value == "md5-sess")
161 fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS;
162 else
163 fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_NONE;
164 } else if (name == "qop")
165 fDigestQop = B_HTTP_QOP_AUTH;
168 if (fAuthenticationMethod == B_HTTP_AUTHENTICATION_BASIC)
169 return B_OK;
170 else if (fAuthenticationMethod == B_HTTP_AUTHENTICATION_DIGEST
171 && fDigestNonce.Length() > 0
172 && fDigestAlgorithm != B_HTTP_AUTHENTICATION_ALGORITHM_NONE) {
173 return B_OK;
174 } else
175 return B_ERROR;
179 // #pragma mark Field access
182 const BString&
183 BHttpAuthentication::UserName() const
185 BPrivate::AutoLocker<BLocker> lock(fLock);
186 return fUserName;
190 const BString&
191 BHttpAuthentication::Password() const
193 BPrivate::AutoLocker<BLocker> lock(fLock);
194 return fPassword;
198 BHttpAuthenticationMethod
199 BHttpAuthentication::Method() const
201 BPrivate::AutoLocker<BLocker> lock(fLock);
202 return fAuthenticationMethod;
206 BString
207 BHttpAuthentication::Authorization(const BUrl& url, const BString& method) const
209 BPrivate::AutoLocker<BLocker> lock(fLock);
210 BString authorizationString;
212 switch (fAuthenticationMethod) {
213 case B_HTTP_AUTHENTICATION_NONE:
214 break;
216 case B_HTTP_AUTHENTICATION_BASIC:
218 BString basicEncode;
219 basicEncode << fUserName << ':' << fPassword;
220 authorizationString << "Basic " << Base64Encode(basicEncode);
221 break;
224 case B_HTTP_AUTHENTICATION_DIGEST:
225 case B_HTTP_AUTHENTICATION_IE_DIGEST:
226 authorizationString << "Digest " << "username=\"" << fUserName
227 << "\", realm=\"" << fRealm << "\", nonce=\"" << fDigestNonce
228 << "\", algorithm=";
230 if (fDigestAlgorithm == B_HTTP_AUTHENTICATION_ALGORITHM_MD5)
231 authorizationString << "MD5";
232 else
233 authorizationString << "MD5-sess";
235 if (fDigestOpaque.Length() > 0)
236 authorizationString << ", opaque=\"" << fDigestOpaque << "\"";
238 if (fDigestQop != B_HTTP_QOP_NONE) {
239 if (fDigestCnonce.Length() == 0) {
240 fDigestCnonce = _H(fDigestOpaque);
241 //fDigestCnonce = "03c6790a055cbbac";
242 fDigestNc = 0;
245 authorizationString << ", uri=\"" << url.Path() << "\"";
246 authorizationString << ", qop=auth, cnonce=\"" << fDigestCnonce
247 << "\"";
249 char strNc[9];
250 snprintf(strNc, 9, "%08x", ++fDigestNc);
251 authorizationString << ", nc=" << strNc;
255 authorizationString << ", response=\""
256 << _DigestResponse(url.Path(), method) << "\"";
257 break;
260 return authorizationString;
264 // #pragma mark Base64 encoding
267 /*static*/ BString
268 BHttpAuthentication::Base64Encode(const BString& string)
270 BString result;
271 BString tmpString = string;
273 while (tmpString.Length()) {
274 char in[3] = { 0, 0, 0 };
275 char out[4] = { 0, 0, 0, 0 };
276 int8 remaining = tmpString.Length();
278 tmpString.MoveInto(in, 0, 3);
280 out[0] = (in[0] & 0xFC) >> 2;
281 out[1] = ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4);
282 out[2] = ((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6);
283 out[3] = in[2] & 0x3F;
285 for (int i = 0; i < 4; i++)
286 out[i] = kBase64Symbols[(int)out[i]];
288 // Add padding if the input length is not a multiple
289 // of 3
290 switch (remaining) {
291 case 1:
292 out[2] = '=';
293 // Fall through
294 case 2:
295 out[3] = '=';
296 break;
299 result.Append(out, 4);
302 return result;
306 /*static*/ BString
307 BHttpAuthentication::Base64Decode(const BString& string)
309 BString result;
311 // Check for invalid input
312 if (string.Length() % 4 != 0)
313 return result;
315 BString base64Reverse(kBase64Symbols);
317 BString tmpString(string);
318 while (tmpString.Length()) {
319 char in[4] = { 0, 0, 0, 0 };
320 char out[3] = { 0, 0, 0 };
322 tmpString.MoveInto(in, 0, 4);
324 for (int i = 0; i < 4; i++) {
325 if (in[i] == '=')
326 in[i] = 0;
327 else
328 in[i] = base64Reverse.FindFirst(in[i], 0);
331 out[0] = (in[0] << 2) | ((in[1] & 0x30) >> 4);
332 out[1] = ((in[1] & 0x0F) << 4) | ((in[2] & 0x3C) >> 2);
333 out[2] = ((in[2] & 0x03) << 6) | in[3];
335 result.Append(out, 3);
338 return result;
342 BString
343 BHttpAuthentication::_DigestResponse(const BString& uri, const BString& method) const
345 PRINT(("HttpAuth: Computing digest response: \n"));
346 PRINT(("HttpAuth: > username = %s\n", fUserName.String()));
347 PRINT(("HttpAuth: > password = %s\n", fPassword.String()));
348 PRINT(("HttpAuth: > realm = %s\n", fRealm.String()));
349 PRINT(("HttpAuth: > nonce = %s\n", fDigestNonce.String()));
350 PRINT(("HttpAuth: > cnonce = %s\n", fDigestCnonce.String()));
351 PRINT(("HttpAuth: > nc = %08x\n", fDigestNc));
352 PRINT(("HttpAuth: > uri = %s\n", uri.String()));
353 PRINT(("HttpAuth: > method = %s\n", method.String()));
354 PRINT(("HttpAuth: > algorithm = %d (MD5:%d, MD5-sess:%d)\n",
355 fDigestAlgorithm, B_HTTP_AUTHENTICATION_ALGORITHM_MD5,
356 B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS));
358 BString A1;
359 A1 << fUserName << ':' << fRealm << ':' << fPassword;
361 if (fDigestAlgorithm == B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS) {
362 A1 = _H(A1);
363 A1 << ':' << fDigestNonce << ':' << fDigestCnonce;
367 BString A2;
368 A2 << method << ':' << uri;
370 PRINT(("HttpAuth: > A1 = %s\n", A1.String()));
371 PRINT(("HttpAuth: > A2 = %s\n", A2.String()));
372 PRINT(("HttpAuth: > H(A1) = %s\n", _H(A1).String()));
373 PRINT(("HttpAuth: > H(A2) = %s\n", _H(A2).String()));
375 char strNc[9];
376 snprintf(strNc, 9, "%08x", fDigestNc);
378 BString secretResp;
379 secretResp << fDigestNonce << ':' << strNc << ':' << fDigestCnonce
380 << ":auth:" << _H(A2);
382 PRINT(("HttpAuth: > R2 = %s\n", secretResp.String()));
384 BString response = _KD(_H(A1), secretResp);
385 PRINT(("HttpAuth: > response = %s\n", response.String()));
387 return response;
391 BString
392 BHttpAuthentication::_H(const BString& value) const
394 MD5_CTX context;
395 uchar hashResult[MD5_DIGEST_LENGTH];
396 MD5_Init(&context);
397 MD5_Update(&context, (void *)(value.String()), value.Length());
398 MD5_Final(hashResult, &context);
400 BString result;
401 // Preallocate the string
402 char* resultChar = result.LockBuffer(MD5_DIGEST_LENGTH * 2);
403 if (resultChar == NULL)
404 return BString();
406 for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
407 char c = ((hashResult[i] & 0xF0) >> 4);
408 c += (c > 9) ? 'a' - 10 : '0';
409 resultChar[0] = c;
410 resultChar++;
412 c = hashResult[i] & 0x0F;
413 c += (c > 9) ? 'a' - 10 : '0';
414 resultChar[0] = c;
415 resultChar++;
417 result.UnlockBuffer(MD5_DIGEST_LENGTH * 2);
419 return result;
423 BString
424 BHttpAuthentication::_KD(const BString& secret, const BString& data) const
426 BString encode;
427 encode << secret << ':' << data;
429 return _H(encode);