2 * Copyright 2010-2013 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Christophe Huriaux, c.huriaux@gmail.com
10 #include <HttpAuthentication.h>
15 #include <AutoLocker.h>
19 #define PRINT(x) printf x
25 #ifdef OPENSSL_ENABLED
26 #include <openssl/md5.h>
32 #ifndef MD5_DIGEST_LENGTH
33 #define MD5_DIGEST_LENGTH 16
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
),
56 // #pragma mark Field modification
60 BHttpAuthentication::SetUserName(const BString
& username
)
69 BHttpAuthentication::SetPassword(const BString
& password
)
78 BHttpAuthentication::SetMethod(BHttpAuthenticationMethod method
)
81 fAuthenticationMethod
= method
;
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)
98 BString additionalData
;
99 int32 firstSpace
= wwwAuthenticate
.FindFirst(' ');
101 if (firstSpace
== -1)
102 wwwAuthenticate
.CopyInto(authRequired
, 0, wwwAuthenticate
.Length());
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
;
119 while (additionalData
.Length()) {
120 int32 firstComma
= additionalData
.FindFirst(',');
121 if (firstComma
== -1)
122 firstComma
= additionalData
.Length();
125 additionalData
.MoveInto(value
, 0, firstComma
);
126 additionalData
.Remove(0, 1);
127 additionalData
.Trim();
129 int32 equal
= value
.FindFirst('=');
134 value
.MoveInto(name
, 0, equal
);
138 if (value
.Length() > 0 && value
[0] == '"') {
140 value
.Remove(value
.Length() - 1, 1);
143 PRINT(("HttpAuth: name=%s, value=%s\n", name
.String(),
148 else if (name
== "nonce")
149 fDigestNonce
= value
;
150 else if (name
== "opaque")
151 fDigestOpaque
= value
;
152 else if (name
== "stale") {
154 fDigestStale
= (value
== "true");
155 } else if (name
== "algorithm") {
159 fDigestAlgorithm
= B_HTTP_AUTHENTICATION_ALGORITHM_MD5
;
160 else if (value
== "md5-sess")
161 fDigestAlgorithm
= B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS
;
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
)
170 else if (fAuthenticationMethod
== B_HTTP_AUTHENTICATION_DIGEST
171 && fDigestNonce
.Length() > 0
172 && fDigestAlgorithm
!= B_HTTP_AUTHENTICATION_ALGORITHM_NONE
) {
179 // #pragma mark Field access
183 BHttpAuthentication::UserName() const
185 BPrivate::AutoLocker
<BLocker
> lock(fLock
);
191 BHttpAuthentication::Password() const
193 BPrivate::AutoLocker
<BLocker
> lock(fLock
);
198 BHttpAuthenticationMethod
199 BHttpAuthentication::Method() const
201 BPrivate::AutoLocker
<BLocker
> lock(fLock
);
202 return fAuthenticationMethod
;
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
:
216 case B_HTTP_AUTHENTICATION_BASIC
:
219 basicEncode
<< fUserName
<< ':' << fPassword
;
220 authorizationString
<< "Basic " << Base64Encode(basicEncode
);
224 case B_HTTP_AUTHENTICATION_DIGEST
:
225 case B_HTTP_AUTHENTICATION_IE_DIGEST
:
226 authorizationString
<< "Digest " << "username=\"" << fUserName
227 << "\", realm=\"" << fRealm
<< "\", nonce=\"" << fDigestNonce
230 if (fDigestAlgorithm
== B_HTTP_AUTHENTICATION_ALGORITHM_MD5
)
231 authorizationString
<< "MD5";
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";
245 authorizationString
<< ", uri=\"" << url
.Path() << "\"";
246 authorizationString
<< ", qop=auth, cnonce=\"" << fDigestCnonce
250 snprintf(strNc
, 9, "%08x", ++fDigestNc
);
251 authorizationString
<< ", nc=" << strNc
;
255 authorizationString
<< ", response=\""
256 << _DigestResponse(url
.Path(), method
) << "\"";
260 return authorizationString
;
264 // #pragma mark Base64 encoding
268 BHttpAuthentication::Base64Encode(const BString
& string
)
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
299 result
.Append(out
, 4);
307 BHttpAuthentication::Base64Decode(const BString
& string
)
311 // Check for invalid input
312 if (string
.Length() % 4 != 0)
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
++) {
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);
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
));
359 A1
<< fUserName
<< ':' << fRealm
<< ':' << fPassword
;
361 if (fDigestAlgorithm
== B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS
) {
363 A1
<< ':' << fDigestNonce
<< ':' << fDigestCnonce
;
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()));
376 snprintf(strNc
, 9, "%08x", fDigestNc
);
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()));
392 BHttpAuthentication::_H(const BString
& value
) const
395 uchar hashResult
[MD5_DIGEST_LENGTH
];
397 MD5_Update(&context
, (void *)(value
.String()), value
.Length());
398 MD5_Final(hashResult
, &context
);
401 // Preallocate the string
402 char* resultChar
= result
.LockBuffer(MD5_DIGEST_LENGTH
* 2);
403 if (resultChar
== NULL
)
406 for (int i
= 0; i
< MD5_DIGEST_LENGTH
; i
++) {
407 char c
= ((hashResult
[i
] & 0xF0) >> 4);
408 c
+= (c
> 9) ? 'a' - 10 : '0';
412 c
= hashResult
[i
] & 0x0F;
413 c
+= (c
> 9) ? 'a' - 10 : '0';
417 result
.UnlockBuffer(MD5_DIGEST_LENGTH
* 2);
424 BHttpAuthentication::_KD(const BString
& secret
, const BString
& data
) const
427 encode
<< secret
<< ':' << data
;