mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 09:04:13 +00:00
Add class to handle http digest authentication.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@23082 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
db7cf4a3fc
commit
efcaebc76b
6 changed files with 426 additions and 11 deletions
|
@ -1,3 +1,12 @@
|
|||
2006-06-19 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/GSURLPrivate.h: Add GSHTTPDigest interface.
|
||||
* Source/GSHTTPDigest.m: Add GSHTTPDigest implementation.
|
||||
* Source/GNUmakefile: Build GSHTTPDigest.
|
||||
* Source/NSURLProtectionSpace.m: Implement ([-hash]) and ([-isEqual:])
|
||||
* Headers/Foundation/NSURLAuthenticationChallenge.h: Improve comments.
|
||||
Add GSHTTPDigest class to handle HTTP digest authentication.
|
||||
|
||||
2006-06-17 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/GSString.m: Fix memory leak.
|
||||
|
|
|
@ -36,20 +36,31 @@
|
|||
@class NSURLResponse;
|
||||
|
||||
/**
|
||||
* A challenge sender (usually an NSURLProtocol subclass handling a
|
||||
* connection/download) provides these methods to permit a client to
|
||||
* control authentication.
|
||||
*/
|
||||
@protocol NSURLAuthenticationChallengeSender <NSObject>
|
||||
|
||||
/**
|
||||
* Cancels the authenticatiopn challenge, ensuring that the load operation
|
||||
* will fail to retrieve data, completing with only the response headers
|
||||
* containing the challenge having been read from the server.
|
||||
*/
|
||||
- (void) cancelAuthenticationChallenge:
|
||||
(NSURLAuthenticationChallenge *)challenge;
|
||||
|
||||
/**
|
||||
* Tells the sender to continue the load without providing a new credential
|
||||
* for it to use ... if the challenge already had a proposed credential,
|
||||
* the sender may elect to use it.
|
||||
*/
|
||||
- (void) continueWithoutCredentialForAuthenticationChallenge:
|
||||
(NSURLAuthenticationChallenge *)challenge;
|
||||
|
||||
/**
|
||||
* Tells the sender to continue the load using the new credential
|
||||
* provided by this method.
|
||||
*/
|
||||
- (void) useCredential: (NSURLCredential *)credential
|
||||
forAuthenticationChallenge: (NSURLAuthenticationChallenge *)challenge;
|
||||
|
|
|
@ -138,6 +138,7 @@ GSCountedSet.m \
|
|||
GSDictionary.m \
|
||||
GSFormat.m \
|
||||
GSFTPURLHandle.m \
|
||||
GSHTTPDigest.m \
|
||||
GSHTTPURLHandle.m \
|
||||
GSRunLoopWatcher.m \
|
||||
GSSet.m \
|
||||
|
|
300
Source/GSHTTPDigest.m
Normal file
300
Source/GSHTTPDigest.m
Normal file
|
@ -0,0 +1,300 @@
|
|||
/* Implementation for GSHTTPDigest for GNUstep
|
||||
Copyright (C) 2006 Software Foundation, Inc.
|
||||
|
||||
Written by: Richard Frith-Macdonald <frm@gnu.org>
|
||||
Date: 2006
|
||||
|
||||
This file is part of the GNUstep Base Library.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02111 USA.
|
||||
*/
|
||||
|
||||
#include "GSURLPrivate.h"
|
||||
#include "Foundation/NSDictionary.h"
|
||||
#include "Foundation/NSScanner.h"
|
||||
#include "Foundation/NSDebug.h"
|
||||
#include "GNUstepBase/GSLock.h"
|
||||
#include "GNUstepBase/GSMime.h"
|
||||
|
||||
|
||||
static NSMutableDictionary *store = nil;
|
||||
static GSLazyLock *storeLock = nil;
|
||||
|
||||
@interface NSData(GSHTTPDigest)
|
||||
- (NSString*) digestHex;
|
||||
@end
|
||||
@implementation NSData(GSHTTPDigest)
|
||||
- (NSString*) digestHex
|
||||
{
|
||||
static const char *hexChars = "0123456789abcdef";
|
||||
unsigned slen = [self length];
|
||||
unsigned dlen = slen * 2;
|
||||
const unsigned char *src = (const unsigned char *)[self bytes];
|
||||
char *dst = (char*)NSZoneMalloc(NSDefaultMallocZone(), dlen);
|
||||
unsigned spos = 0;
|
||||
unsigned dpos = 0;
|
||||
NSData *data;
|
||||
NSString *string;
|
||||
|
||||
while (spos < slen)
|
||||
{
|
||||
unsigned char c = src[spos++];
|
||||
|
||||
dst[dpos++] = hexChars[(c >> 4) & 0x0f];
|
||||
dst[dpos++] = hexChars[c & 0x0f];
|
||||
}
|
||||
data = [NSData allocWithZone: NSDefaultMallocZone()];
|
||||
data = [data initWithBytesNoCopy: dst length: dlen];
|
||||
string = [[NSString alloc] initWithData: data
|
||||
encoding: NSASCIIStringEncoding];
|
||||
RELEASE(data);
|
||||
return AUTORELEASE(string);
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation GSHTTPDigest
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
if (store == nil)
|
||||
{
|
||||
store = [NSMutableDictionary new];
|
||||
storeLock = [GSLazyLock new];
|
||||
}
|
||||
}
|
||||
|
||||
+ (GSHTTPDigest *) digestWithCredential: (NSURLCredential*)credential
|
||||
inProtectionSpace: (NSURLProtectionSpace*)space
|
||||
{
|
||||
NSMutableDictionary *cDict;
|
||||
GSHTTPDigest *digest = nil;
|
||||
|
||||
[storeLock lock];
|
||||
cDict = [store objectForKey: space];
|
||||
if (cDict == nil)
|
||||
{
|
||||
cDict = [NSMutableDictionary new];
|
||||
[store setObject: cDict forKey: space];
|
||||
RELEASE(cDict);
|
||||
}
|
||||
digest = [cDict objectForKey: credential];
|
||||
if (digest == nil)
|
||||
{
|
||||
digest = [[GSHTTPDigest alloc] initWithCredential: credential
|
||||
inProtectionSpace: space];
|
||||
[cDict setObject: digest forKey: [digest credential]];
|
||||
}
|
||||
else
|
||||
{
|
||||
RETAIN(digest);
|
||||
}
|
||||
[storeLock unlock];
|
||||
return AUTORELEASE(digest);
|
||||
}
|
||||
|
||||
- (NSString*) authorizationForAuthentication: (NSString*)authentication
|
||||
method: (NSString*)method
|
||||
path: (NSString*)path
|
||||
{
|
||||
NSString *realm = nil;
|
||||
NSString *qop = nil;
|
||||
NSString *nonce = nil;
|
||||
NSString *opaque = nil;
|
||||
NSString *stale = @"FALSE";
|
||||
NSString *algorithm = @"MD5";
|
||||
NSString *cnonce;
|
||||
NSString *HA1;
|
||||
NSString *HA2;
|
||||
NSString *response;
|
||||
NSString *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)
|
||||
{
|
||||
if ([sc scanString: @"=" intoString: 0] == NO)
|
||||
{
|
||||
NSDebugMLog(@"Missing '=' in HTTP digest '%@'", authentication);
|
||||
return nil; // Bad name=value specification
|
||||
}
|
||||
if ((val = [p scanToken: sc]) == nil)
|
||||
{
|
||||
NSDebugMLog(@"Missing value in HTTP digest '%@'", authentication);
|
||||
return nil; // Bad name=value specification
|
||||
}
|
||||
if ([key caseInsensitiveCompare: @"realm"] == NSOrderedSame)
|
||||
{
|
||||
realm = val;
|
||||
}
|
||||
if ([key caseInsensitiveCompare: @"qop"] == NSOrderedSame)
|
||||
{
|
||||
qop = val;
|
||||
}
|
||||
if ([key caseInsensitiveCompare: @"nonce"] == NSOrderedSame)
|
||||
{
|
||||
nonce = val;
|
||||
}
|
||||
if ([key caseInsensitiveCompare: @"opaque"] == NSOrderedSame)
|
||||
{
|
||||
opaque = val;
|
||||
}
|
||||
if ([key caseInsensitiveCompare: @"stale"] == NSOrderedSame)
|
||||
{
|
||||
stale = val;
|
||||
}
|
||||
if ([key caseInsensitiveCompare: @"algorithm"] == NSOrderedSame)
|
||||
{
|
||||
algorithm = val;
|
||||
}
|
||||
if ([sc scanString: @"," intoString: 0] == NO)
|
||||
{
|
||||
break; // No more in list.
|
||||
}
|
||||
}
|
||||
|
||||
if (realm == nil)
|
||||
{
|
||||
NSDebugMLog(@"Missing HTTP digest realm in '%@'", authentication);
|
||||
return nil;
|
||||
}
|
||||
if ([realm isEqual: [self->_space realm]] == NO)
|
||||
{
|
||||
NSDebugMLog(@"Bad HTTP digest realm in '%@'", authentication);
|
||||
return nil;
|
||||
}
|
||||
if (nonce == 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)
|
||||
{
|
||||
NSDebugMLog(@"Unsupported HTTP digest algorithm in '%@'",
|
||||
authentication);
|
||||
return nil;
|
||||
}
|
||||
if (![[qop componentsSeparatedByString: @","] containsObject: @"auth"])
|
||||
{
|
||||
NSDebugMLog(@"Unsupported/missing HTTP digest qop in '%@'",
|
||||
authentication);
|
||||
return nil;
|
||||
}
|
||||
|
||||
[self->_lock lock];
|
||||
if ([stale boolValue] == YES || [nonce isEqual: _nonce] == NO)
|
||||
{
|
||||
_nc = 1;
|
||||
}
|
||||
ASSIGN(_nonce, nonce);
|
||||
ASSIGN(_qop, qop);
|
||||
ASSIGN(_opaque, opaque);
|
||||
}
|
||||
else
|
||||
{
|
||||
[self->_lock lock];
|
||||
nonce = _nonce;
|
||||
opaque = _opaque;
|
||||
qop = _qop;
|
||||
realm = [self->_space realm];
|
||||
}
|
||||
|
||||
nc = _nc++;
|
||||
|
||||
qop = @"auth";
|
||||
|
||||
cnonce = [[[[[NSProcessInfo processInfo] globallyUniqueString]
|
||||
dataUsingEncoding: NSUTF8StringEncoding] md5Digest] digestHex];
|
||||
|
||||
HA1 = [[[[NSString stringWithFormat: @"%@:%@:%@",
|
||||
[self->_credential user], realm, [self->_credential password]]
|
||||
dataUsingEncoding: NSUTF8StringEncoding] md5Digest] digestHex];
|
||||
|
||||
HA2 = [[[[NSString stringWithFormat: @"%@:%@", method, path]
|
||||
dataUsingEncoding: NSUTF8StringEncoding] md5Digest] digestHex];
|
||||
|
||||
response = [[[[NSString stringWithFormat: @"%@:%@:%08x:%@:%@:%@",
|
||||
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];
|
||||
|
||||
[self->_lock unlock];
|
||||
|
||||
return authorisation;
|
||||
}
|
||||
|
||||
- (NSURLCredential *) credential
|
||||
{
|
||||
return self->_credential;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE(_credential);
|
||||
RELEASE(_space);
|
||||
RELEASE(_nonce);
|
||||
RELEASE(_opaque);
|
||||
RELEASE(_qop);
|
||||
RELEASE(_lock);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id) initWithCredential: (NSURLCredential*)credential
|
||||
inProtectionSpace: (NSURLProtectionSpace*)space
|
||||
{
|
||||
if ((self = [super init]) != nil)
|
||||
{
|
||||
self->_lock = [GSLazyLock new];
|
||||
ASSIGNCOPY(self->_space, space);
|
||||
ASSIGNCOPY(self->_credential, credential);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSURLProtectionSpace *) space
|
||||
{
|
||||
return self->_space;
|
||||
}
|
||||
@end
|
||||
|
|
@ -62,5 +62,39 @@
|
|||
- (void) _setProperty: (id)value forKey: (NSString*)key;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Internal class for handling HTTP digest authentication
|
||||
*/
|
||||
@interface GSHTTPDigest : NSObject
|
||||
{
|
||||
GSLazyLock *_lock;
|
||||
NSURLCredential *_credential;
|
||||
NSURLProtectionSpace *_space;
|
||||
NSString *_nonce;
|
||||
NSString *_opaque;
|
||||
NSString *_qop;
|
||||
int _nc;
|
||||
}
|
||||
/*
|
||||
* Return the object for the specified credential/protection space.
|
||||
*/
|
||||
+ (GSHTTPDigest *) digestWithCredential: (NSURLCredential*)credential
|
||||
inProtectionSpace: (NSURLProtectionSpace*)space;
|
||||
/*
|
||||
* Generate next authorisation header for the specified authentication
|
||||
* header, method, and path.
|
||||
*/
|
||||
- (NSString*) authorizationForAuthentication: (NSString*)authentication
|
||||
method: (NSString*)method
|
||||
path: (NSString*)path;
|
||||
- (NSURLCredential *) credential;
|
||||
- (id) initWithCredential: (NSURLCredential*)credential
|
||||
inProtectionSpace: (NSURLProtectionSpace*)space;
|
||||
- (NSURLProtectionSpace *) space;
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -74,19 +74,26 @@ typedef struct {
|
|||
|
||||
- (id) copyWithZone: (NSZone*)z
|
||||
{
|
||||
NSURLProtectionSpace *o = [[self class] allocWithZone: z];
|
||||
|
||||
o = [o initWithHost: this->host
|
||||
port: this->port
|
||||
protocol: this->protocol
|
||||
realm: this->realm
|
||||
authenticationMethod: this->authenticationMethod];
|
||||
if (o != nil)
|
||||
if (NSShouldRetainWithZone(self, z) == YES)
|
||||
{
|
||||
inst->isProxy = this->isProxy;
|
||||
ASSIGN(inst->proxyType, this->proxyType);
|
||||
return RETAIN(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSURLProtectionSpace *o = [[self class] allocWithZone: z];
|
||||
|
||||
o = [o initWithHost: this->host
|
||||
port: this->port
|
||||
protocol: this->protocol
|
||||
realm: this->realm
|
||||
authenticationMethod: this->authenticationMethod];
|
||||
if (o != nil)
|
||||
{
|
||||
inst->isProxy = this->isProxy;
|
||||
ASSIGN(inst->proxyType, this->proxyType);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
|
@ -103,6 +110,13 @@ typedef struct {
|
|||
[super dealloc];
|
||||
}
|
||||
|
||||
- (unsigned) hash
|
||||
{
|
||||
return [[self host] hash] + [self port]
|
||||
+ [[self realm] hash] + [[self protocol] hash]
|
||||
+ [[self proxyType] hash] + [[self authenticationMethod] hash];
|
||||
}
|
||||
|
||||
- (NSString *) host
|
||||
{
|
||||
return this->host;
|
||||
|
@ -146,6 +160,52 @@ authenticationMethod: (NSString *)authenticationMethod
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (unsigned) isEqual: (id)other
|
||||
{
|
||||
if ([other isKindOfClass: [NSURLProtocol class]] == NO)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSURLProtectionSpace *o = (NSURLProtectionSpace*)other;
|
||||
|
||||
if (![[self host] isEqual: [o host]] == NO)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
if (![[self realm] isEqual: [o realm]] == NO)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
if ([self port] != [o port])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
if (![[self authenticationMethod] isEqual: [o authenticationMethod]])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
if ([self isProxy] == YES)
|
||||
{
|
||||
if ([o isProxy] == NO
|
||||
|| [[self proxyType] isEqual: [o proxyType]] == NO)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([o isProxy] == YES
|
||||
|| [[self protocol] isEqual: [o protocol]] == NO)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) isProxy
|
||||
{
|
||||
return this->isProxy;
|
||||
|
|
Loading…
Reference in a new issue