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 BHttpAuthentication::BHttpAuthentication(const BHttpAuthentication
& other
)
58 fAuthenticationMethod(other
.fAuthenticationMethod
),
59 fUserName(other
.fUserName
),
60 fPassword(other
.fPassword
),
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
;
93 // #pragma mark Field modification
97 BHttpAuthentication::SetUserName(const BString
& username
)
100 fUserName
= username
;
106 BHttpAuthentication::SetPassword(const BString
& password
)
109 fPassword
= password
;
115 BHttpAuthentication::SetMethod(BHttpAuthenticationMethod method
)
118 fAuthenticationMethod
= method
;
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)
134 BString authRequired
;
135 BString additionalData
;
136 int32 firstSpace
= wwwAuthenticate
.FindFirst(' ');
138 if (firstSpace
== -1)
139 wwwAuthenticate
.CopyInto(authRequired
, 0, wwwAuthenticate
.Length());
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
;
156 while (additionalData
.Length()) {
157 int32 firstComma
= additionalData
.FindFirst(',');
158 if (firstComma
== -1)
159 firstComma
= additionalData
.Length();
162 additionalData
.MoveInto(value
, 0, firstComma
);
163 additionalData
.Remove(0, 1);
164 additionalData
.Trim();
166 int32 equal
= value
.FindFirst('=');
171 value
.MoveInto(name
, 0, equal
);
175 if (value
.Length() > 0 && value
[0] == '"') {
177 value
.Remove(value
.Length() - 1, 1);
180 PRINT(("HttpAuth: name=%s, value=%s\n", name
.String(),
185 else if (name
== "nonce")
186 fDigestNonce
= value
;
187 else if (name
== "opaque")
188 fDigestOpaque
= value
;
189 else if (name
== "stale") {
191 fDigestStale
= (value
== "true");
192 } else if (name
== "algorithm") {
196 fDigestAlgorithm
= B_HTTP_AUTHENTICATION_ALGORITHM_MD5
;
197 else if (value
== "md5-sess")
198 fDigestAlgorithm
= B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS
;
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
)
207 else if (fAuthenticationMethod
== B_HTTP_AUTHENTICATION_DIGEST
208 && fDigestNonce
.Length() > 0
209 && fDigestAlgorithm
!= B_HTTP_AUTHENTICATION_ALGORITHM_NONE
) {
216 // #pragma mark Field access
220 BHttpAuthentication::UserName() const
222 BPrivate::AutoLocker
<BLocker
> lock(fLock
);
228 BHttpAuthentication::Password() const
230 BPrivate::AutoLocker
<BLocker
> lock(fLock
);
235 BHttpAuthenticationMethod
236 BHttpAuthentication::Method() const
238 BPrivate::AutoLocker
<BLocker
> lock(fLock
);
239 return fAuthenticationMethod
;
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
:
253 case B_HTTP_AUTHENTICATION_BASIC
:
256 basicEncode
<< fUserName
<< ':' << fPassword
;
257 authorizationString
<< "Basic " << Base64Encode(basicEncode
);
261 case B_HTTP_AUTHENTICATION_DIGEST
:
262 case B_HTTP_AUTHENTICATION_IE_DIGEST
:
263 authorizationString
<< "Digest " << "username=\"" << fUserName
264 << "\", realm=\"" << fRealm
<< "\", nonce=\"" << fDigestNonce
267 if (fDigestAlgorithm
== B_HTTP_AUTHENTICATION_ALGORITHM_MD5
)
268 authorizationString
<< "MD5";
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";
282 authorizationString
<< ", uri=\"" << url
.Path() << "\"";
283 authorizationString
<< ", qop=auth, cnonce=\"" << fDigestCnonce
287 snprintf(strNc
, 9, "%08x", ++fDigestNc
);
288 authorizationString
<< ", nc=" << strNc
;
292 authorizationString
<< ", response=\""
293 << _DigestResponse(url
.Path(), method
) << "\"";
297 return authorizationString
;
301 // #pragma mark Base64 encoding
305 BHttpAuthentication::Base64Encode(const BString
& string
)
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
336 result
.Append(out
, 4);
344 BHttpAuthentication::Base64Decode(const BString
& string
)
348 // Check for invalid input
349 if (string
.Length() % 4 != 0)
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
++) {
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);
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
));
396 A1
<< fUserName
<< ':' << fRealm
<< ':' << fPassword
;
398 if (fDigestAlgorithm
== B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS
) {
400 A1
<< ':' << fDigestNonce
<< ':' << fDigestCnonce
;
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()));
413 snprintf(strNc
, 9, "%08x", fDigestNc
);
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()));
429 BHttpAuthentication::_H(const BString
& value
) const
432 uchar hashResult
[MD5_DIGEST_LENGTH
];
434 MD5_Update(&context
, (void *)(value
.String()), value
.Length());
435 MD5_Final(hashResult
, &context
);
438 // Preallocate the string
439 char* resultChar
= result
.LockBuffer(MD5_DIGEST_LENGTH
* 2);
440 if (resultChar
== NULL
)
443 for (int i
= 0; i
< MD5_DIGEST_LENGTH
; i
++) {
444 char c
= ((hashResult
[i
] & 0xF0) >> 4);
445 c
+= (c
> 9) ? 'a' - 10 : '0';
449 c
= hashResult
[i
] & 0x0F;
450 c
+= (c
> 9) ? 'a' - 10 : '0';
454 result
.UnlockBuffer(MD5_DIGEST_LENGTH
* 2);
461 BHttpAuthentication::_KD(const BString
& secret
, const BString
& data
) const
464 encode
<< secret
<< ':' << data
;