Updates to perform rudimentary digest authentication.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@23086 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2006-06-19 15:06:08 +00:00
parent aa858059f5
commit a3898bd77b
4 changed files with 138 additions and 23 deletions

View file

@ -6,6 +6,7 @@
* Source/NSURLProtectionSpace.m: Implement ([-hash]) and ([-isEqual:])
* Headers/Foundation/NSURLAuthenticationChallenge.h: Improve comments.
Add GSHTTPDigest class to handle HTTP digest authentication.
* Source/GSHTTPURLHandle.m: Perform rudimentary digest authentication.
2006-06-17 Richard Frith-Macdonald <rfm@gnu.org>

View file

@ -32,6 +32,7 @@
static NSMutableDictionary *store = nil;
static GSLazyLock *storeLock = nil;
static GSMimeParser *mimeParser = nil;
@interface NSData(GSHTTPDigest)
- (NSString*) digestHex;
@ -73,6 +74,7 @@ static GSLazyLock *storeLock = nil;
{
if (store == nil)
{
mimeParser = [GSMimeParser new];
store = [NSMutableDictionary new];
storeLock = [GSLazyLock new];
}
@ -107,6 +109,38 @@ static GSLazyLock *storeLock = nil;
return AUTORELEASE(digest);
}
+ (NSString*) digestRealmForAuthentication: (NSString*)authentication
{
if (authentication != nil)
{
NSScanner *sc;
NSString *key;
NSString *val;
sc = [NSScanner scannerWithString: authentication];
if ([sc scanString: @"Digest" intoString: 0] == NO)
{
return nil; // Not a digest authentication
}
while ((key = [mimeParser scanName: sc]) != nil)
{
if ([sc scanString: @"=" intoString: 0] == NO)
{
return nil; // Bad name=value specification
}
if ((val = [mimeParser scanToken: sc]) == nil)
{
return nil; // Bad name=value specification
}
if ([key caseInsensitiveCompare: @"realm"] == NSOrderedSame)
{
return val;
}
}
}
return nil;
}
- (NSString*) authorizationForAuthentication: (NSString*)authentication
method: (NSString*)method
path: (NSString*)path
@ -121,34 +155,29 @@ static GSLazyLock *storeLock = nil;
NSString *HA1;
NSString *HA2;
NSString *response;
NSString *authorisation;
NSMutableString *authorisation;
int nc;
if (authentication != nil)
{
static GSMimeParser *p = nil;
NSScanner *sc;
NSString *key;
NSString *val;
if (p == nil)
{
p = [GSMimeParser new];
}
sc = [NSScanner scannerWithString: authentication];
if ([sc scanString: @"Digest" intoString: 0] == NO)
{
NSDebugMLog(@"Bad format HTTP digest in '%@'", authentication);
return nil; // Not a digest authentication
}
while ((key = [p scanName: sc]) != nil)
while ((key = [mimeParser scanName: sc]) != nil)
{
if ([sc scanString: @"=" intoString: 0] == NO)
{
NSDebugMLog(@"Missing '=' in HTTP digest '%@'", authentication);
return nil; // Bad name=value specification
}
if ((val = [p scanToken: sc]) == nil)
if ((val = [mimeParser scanToken: sc]) == nil)
{
NSDebugMLog(@"Missing value in HTTP digest '%@'", authentication);
return nil; // Bad name=value specification
@ -198,11 +227,6 @@ static GSLazyLock *storeLock = nil;
NSDebugMLog(@"Missing HTTP digest nonce in '%@'", authentication);
return nil;
}
if (opaque == nil)
{
NSDebugMLog(@"Missing HTTP digest opaque in '%@'", authentication);
return nil;
}
if ([algorithm isEqual: @"MD5"] == NO)
{
@ -253,11 +277,19 @@ static GSLazyLock *storeLock = nil;
HA1, nonce, nc, cnonce, qop, HA2]
dataUsingEncoding: NSUTF8StringEncoding] md5Digest] digestHex];
authorisation = [NSString stringWithFormat: @"Digest username=\"%@\","
@"realm=\"%@\",nonce=\"%@\",uri=\"%@\",qop=\"%@\",nc=%08x,cnonce=\"%@\","
@"response=\"%@\",opaque=\"%@\"",
[self->_credential user],
realm, nonce, path, qop, nc, cnonce, response, opaque];
authorisation = [NSMutableString stringWithCapacity: 512];
[authorisation appendFormat: @"Digest realm=\"%@\"", realm];
[authorisation appendFormat: @",username=\"%@\"", [self->_credential user]];
[authorisation appendFormat: @",nonce=\"%@\"", nonce];
[authorisation appendFormat: @",uri=\"%@\"", path];
[authorisation appendFormat: @",response=\"%@\"", response];
[authorisation appendFormat: @",qop=\"%@\"", qop];
[authorisation appendFormat: @",nc=%08x", nc];
[authorisation appendFormat: @",cnonce=\"%@\"", cnonce];
if (opaque != nil)
{
[authorisation appendFormat: @",opaque=\"%@\"", opaque];
}
[self->_lock unlock];

View file

@ -44,6 +44,7 @@
#include "GNUstepBase/GSMime.h"
#include "GNUstepBase/GSLock.h"
#include "NSCallBacks.h"
#include "GSURLPrivate.h"
#include <string.h>
#ifdef HAVE_UNISTD_H
@ -569,17 +570,94 @@ static void debugWrite(GSHTTPURLHandle *handle, NSData *data)
* Retrieve essential keys from document
*/
info = [document headerNamed: @"http"];
val = [info objectForKey: NSHTTPPropertyStatusCodeKey];
if ([val intValue] == 401)
{
NSString *a;
GSMimeHeader *ah;
a = (id)NSMapGet(wProperties, (void*)@"Authorization");
if ([a hasPrefix: @"Basic"] == YES
&& (ah = [document headerNamed: @"WWW-Authenticate"]) != nil)
{
NSString *realm;
NSString *ac;
ac = [ah value];
realm = [GSHTTPDigest digestRealmForAuthentication: ac];
if (realm != nil)
{
NSURLProtectionSpace *space;
NSURLCredential *cred;
GSHTTPDigest *digest;
NSString *method;
NSString *a;
/*
* Create credential from user and password
* stored in the URL.
*/
cred = [[NSURLCredential alloc]
initWithUser: [url user]
password: [url password]
persistence: NSURLCredentialPersistenceForSession];
/*
* Create protection space from the information in
* the URL and the realm of the authentication
* challenge.
*/
space = [[NSURLProtectionSpace alloc]
initWithHost: [url host]
port: [[url port] intValue]
protocol: [url scheme]
realm: realm
authenticationMethod:
NSURLAuthenticationMethodHTTPDigest];
/*
* Get the digest object and ask it for a header
* to use for authorisation.
*/
digest = [GSHTTPDigest digestWithCredential: cred
inProtectionSpace: space];
RELEASE(cred);
RELEASE(space);
method = [request objectForKey: GSHTTPPropertyMethodKey];
if (method == nil)
{
if ([wData length] > 0)
{
method = @"POST";
}
else
{
method = @"GET";
}
}
a = [digest authorizationForAuthentication: ac
method: method
path: [url path]];
if (a != nil)
{
[self writeProperty: a forKey: @"Authorization"];
[self _tryLoadInBackground: u];
return; // Retrying.
}
}
}
}
if (val != nil)
{
[pageInfo setObject: val forKey: NSHTTPPropertyStatusCodeKey];
}
val = [info objectForKey: NSHTTPPropertyServerHTTPVersionKey];
if (val != nil)
{
[pageInfo setObject: val
forKey: NSHTTPPropertyServerHTTPVersionKey];
}
val = [info objectForKey: NSHTTPPropertyStatusCodeKey];
if (val != nil)
{
[pageInfo setObject: val forKey: NSHTTPPropertyStatusCodeKey];
}
val = [info objectForKey: NSHTTPPropertyStatusReasonKey];
if (val != nil)
{

View file

@ -84,6 +84,10 @@
*/
+ (GSHTTPDigest *) digestWithCredential: (NSURLCredential*)credential
inProtectionSpace: (NSURLProtectionSpace*)space;
/*
* Look for a digest realm in a header
*/
+ (NSString*) digestRealmForAuthentication: (NSString*)authentication;
/*
* Generate next authorisation header for the specified authentication
* header, method, and path.