2006-06-20 09:52:57 +00:00
|
|
|
|
/* Implementation for GSHTTPAuthentication for GNUstep
|
2006-06-19 11:20:17 +00:00
|
|
|
|
Copyright (C) 2006 Software Foundation, Inc.
|
|
|
|
|
|
2009-03-20 18:52:59 +00:00
|
|
|
|
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
2006-06-19 11:20:17 +00:00
|
|
|
|
Date: 2006
|
|
|
|
|
|
|
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
2007-09-14 11:36:11 +00:00
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
2006-06-19 11:20:17 +00:00
|
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-08 10:38:33 +00:00
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
2006-06-19 11:20:17 +00:00
|
|
|
|
|
|
|
|
|
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
|
2019-12-09 23:36:00 +00:00
|
|
|
|
Lesser General Public License for more details.
|
2006-06-19 11:20:17 +00:00
|
|
|
|
|
2007-09-14 11:36:11 +00:00
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
2006-06-19 11:20:17 +00:00
|
|
|
|
License along with this library; if not, write to the Free
|
2024-11-07 13:37:59 +00:00
|
|
|
|
Software Foundation, Inc., 31 Milk Street #960789 Boston, MA 02196 USA.
|
2006-06-19 11:20:17 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2010-02-19 08:12:46 +00:00
|
|
|
|
#import "common.h"
|
2007-11-29 20:53:26 +00:00
|
|
|
|
#import "GSURLPrivate.h"
|
|
|
|
|
#import "Foundation/NSDictionary.h"
|
|
|
|
|
#import "Foundation/NSEnumerator.h"
|
|
|
|
|
#import "Foundation/NSScanner.h"
|
|
|
|
|
#import "Foundation/NSSet.h"
|
|
|
|
|
#import "Foundation/NSValue.h"
|
|
|
|
|
#import "GNUstepBase/GSMime.h"
|
2010-02-14 10:48:10 +00:00
|
|
|
|
#import "GNUstepBase/NSData+GNUstepBase.h"
|
2006-06-19 11:20:17 +00:00
|
|
|
|
|
|
|
|
|
|
2006-06-20 09:52:57 +00:00
|
|
|
|
static NSMutableDictionary *domainMap = nil;
|
2006-06-19 15:37:50 +00:00
|
|
|
|
static NSMutableSet *spaces = nil;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
static NSMutableDictionary *store = nil;
|
2018-04-10 13:59:35 +00:00
|
|
|
|
static NSLock *storeLock = nil;
|
2006-06-19 15:06:08 +00:00
|
|
|
|
static GSMimeParser *mimeParser = nil;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
|
|
|
|
|
@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];
|
2009-02-11 17:33:31 +00:00
|
|
|
|
char *dst;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
unsigned spos = 0;
|
|
|
|
|
unsigned dpos = 0;
|
|
|
|
|
NSData *data;
|
|
|
|
|
NSString *string;
|
|
|
|
|
|
2009-02-11 17:33:31 +00:00
|
|
|
|
dst = (char*)NSZoneMalloc(NSDefaultMallocZone(), dlen);
|
2006-06-19 11:20:17 +00:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-06-20 09:52:57 +00:00
|
|
|
|
@implementation GSHTTPAuthentication
|
2006-06-19 11:20:17 +00:00
|
|
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
|
|
|
|
if (store == nil)
|
|
|
|
|
{
|
2006-06-19 15:06:08 +00:00
|
|
|
|
mimeParser = [GSMimeParser new];
|
2013-08-22 15:44:54 +00:00
|
|
|
|
[[NSObject leakAt: &mimeParser] release];
|
2006-06-19 15:37:50 +00:00
|
|
|
|
spaces = [NSMutableSet new];
|
2013-08-22 15:44:54 +00:00
|
|
|
|
[[NSObject leakAt: &spaces] release];
|
2006-06-20 09:52:57 +00:00
|
|
|
|
domainMap = [NSMutableDictionary new];
|
2013-08-22 15:44:54 +00:00
|
|
|
|
[[NSObject leakAt: &domainMap] release];
|
2006-06-19 11:20:17 +00:00
|
|
|
|
store = [NSMutableDictionary new];
|
2013-08-22 15:44:54 +00:00
|
|
|
|
[[NSObject leakAt: &store] release];
|
2018-04-10 13:59:35 +00:00
|
|
|
|
storeLock = [NSLock new];
|
2013-08-22 15:44:54 +00:00
|
|
|
|
[[NSObject leakAt: &storeLock] release];
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-07-04 10:54:12 +00:00
|
|
|
|
+ (GSHTTPAuthentication *) authenticationWithCredential:
|
|
|
|
|
(NSURLCredential*)credential
|
|
|
|
|
inProtectionSpace: (NSURLProtectionSpace*)space
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
NSMutableDictionary *cDict = nil;
|
|
|
|
|
NSURLProtectionSpace *known = nil;
|
|
|
|
|
GSHTTPAuthentication *authentication = nil;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
|
2006-10-04 10:48:09 +00:00
|
|
|
|
NSAssert([credential isKindOfClass: [NSURLCredential class]] == YES,
|
|
|
|
|
NSInvalidArgumentException);
|
|
|
|
|
NSAssert([space isKindOfClass: [NSURLProtectionSpace class]] == YES,
|
|
|
|
|
NSInvalidArgumentException);
|
|
|
|
|
|
2006-06-19 11:20:17 +00:00
|
|
|
|
[storeLock lock];
|
2007-03-02 11:53:20 +00:00
|
|
|
|
NS_DURING
|
2006-06-19 15:37:50 +00:00
|
|
|
|
{
|
2007-03-02 11:53:20 +00:00
|
|
|
|
/*
|
|
|
|
|
* Keep track of known protection spaces so we don't make lots of
|
|
|
|
|
* duplicate copies, but share one copy between authentication objects.
|
|
|
|
|
*/
|
2006-06-19 15:37:50 +00:00
|
|
|
|
known = [spaces member: space];
|
2007-03-02 11:53:20 +00:00
|
|
|
|
if (known == nil)
|
|
|
|
|
{
|
|
|
|
|
[spaces addObject: space];
|
|
|
|
|
known = [spaces member: space];
|
|
|
|
|
}
|
|
|
|
|
space = known;
|
|
|
|
|
cDict = [store objectForKey: space];
|
|
|
|
|
if (cDict == nil)
|
|
|
|
|
{
|
|
|
|
|
cDict = [NSMutableDictionary new];
|
|
|
|
|
[store setObject: cDict forKey: space];
|
|
|
|
|
RELEASE(cDict);
|
|
|
|
|
}
|
|
|
|
|
authentication = [cDict objectForKey: credential];
|
2006-07-04 10:54:12 +00:00
|
|
|
|
|
2007-03-02 11:53:20 +00:00
|
|
|
|
if (authentication == nil)
|
|
|
|
|
{
|
|
|
|
|
authentication = [[GSHTTPAuthentication alloc]
|
|
|
|
|
initWithCredential: credential
|
|
|
|
|
inProtectionSpace: space];
|
|
|
|
|
if (authentication != nil)
|
|
|
|
|
{
|
|
|
|
|
[cDict setObject: authentication
|
|
|
|
|
forKey: [authentication credential]];
|
|
|
|
|
RELEASE(authentication);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-17 10:08:18 +00:00
|
|
|
|
IF_NO_ARC([[authentication retain] autorelease];)
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2007-03-02 11:53:20 +00:00
|
|
|
|
NS_HANDLER
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2007-03-02 11:53:20 +00:00
|
|
|
|
[storeLock unlock];
|
|
|
|
|
[localException raise];
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2007-03-02 11:53:20 +00:00
|
|
|
|
NS_ENDHANDLER
|
2006-06-19 11:20:17 +00:00
|
|
|
|
[storeLock unlock];
|
2007-03-02 11:53:20 +00:00
|
|
|
|
return authentication;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-06-20 09:52:57 +00:00
|
|
|
|
+ (NSURLProtectionSpace*) protectionSpaceForAuthentication: (NSString*)auth
|
|
|
|
|
requestURL: (NSURL*)URL;
|
2006-06-19 15:06:08 +00:00
|
|
|
|
{
|
2008-01-07 17:51:02 +00:00
|
|
|
|
if ([auth isKindOfClass: [NSString class]] == YES)
|
2006-06-19 15:06:08 +00:00
|
|
|
|
{
|
2006-06-20 09:52:57 +00:00
|
|
|
|
NSString *method = nil;
|
|
|
|
|
NSURLProtectionSpace *space;
|
|
|
|
|
NSScanner *sc;
|
|
|
|
|
NSString *domain = nil;
|
|
|
|
|
NSString *realm = nil;
|
|
|
|
|
NSString *key;
|
|
|
|
|
NSString *val;
|
2006-06-19 15:06:08 +00:00
|
|
|
|
|
2006-06-20 09:52:57 +00:00
|
|
|
|
space = [self protectionSpaceForURL: URL];
|
|
|
|
|
sc = [NSScanner scannerWithString: auth];
|
2006-10-04 10:48:09 +00:00
|
|
|
|
key = [mimeParser scanName: sc];
|
|
|
|
|
if ([key caseInsensitiveCompare: @"Basic"] == NSOrderedSame)
|
2006-06-20 09:52:57 +00:00
|
|
|
|
{
|
|
|
|
|
method = NSURLAuthenticationMethodHTTPBasic;
|
2006-07-04 11:10:13 +00:00
|
|
|
|
domain = [URL path];
|
2006-06-20 09:52:57 +00:00
|
|
|
|
}
|
2006-10-04 10:48:09 +00:00
|
|
|
|
else if ([key caseInsensitiveCompare: @"Digest"] == NSOrderedSame)
|
2006-06-20 09:52:57 +00:00
|
|
|
|
{
|
|
|
|
|
method = NSURLAuthenticationMethodHTTPDigest;
|
|
|
|
|
}
|
2017-06-21 00:01:56 +00:00
|
|
|
|
else if ([key caseInsensitiveCompare: @"NTLM"] == NSOrderedSame)
|
|
|
|
|
{
|
|
|
|
|
method = NSURLAuthenticationMethodNTLM;
|
|
|
|
|
}
|
|
|
|
|
else if ([key caseInsensitiveCompare: @"Negotiate"] == NSOrderedSame)
|
|
|
|
|
{
|
|
|
|
|
method = NSURLAuthenticationMethodNegotiate;
|
|
|
|
|
}
|
2006-06-20 09:52:57 +00:00
|
|
|
|
else
|
2006-06-19 15:06:08 +00:00
|
|
|
|
{
|
2006-10-04 10:48:09 +00:00
|
|
|
|
return nil; // Unknown authentication
|
2006-06-19 15:06:08 +00:00
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
}
|
2006-06-20 09:52:57 +00:00
|
|
|
|
if ([key caseInsensitiveCompare: @"domain"] == NSOrderedSame)
|
2006-06-19 15:06:08 +00:00
|
|
|
|
{
|
2006-06-20 09:52:57 +00:00
|
|
|
|
domain = val;
|
2006-06-19 15:06:08 +00:00
|
|
|
|
}
|
2006-06-20 09:52:57 +00:00
|
|
|
|
else if ([key caseInsensitiveCompare: @"realm"] == NSOrderedSame)
|
|
|
|
|
{
|
|
|
|
|
realm = val;
|
|
|
|
|
}
|
2006-06-20 16:42:08 +00:00
|
|
|
|
if ([sc scanString: @"," intoString: 0] == NO)
|
|
|
|
|
{
|
|
|
|
|
break; // No more in list.
|
|
|
|
|
}
|
2006-06-20 09:52:57 +00:00
|
|
|
|
}
|
|
|
|
|
if (realm == nil)
|
|
|
|
|
{
|
2006-06-20 16:42:08 +00:00
|
|
|
|
return nil; // No realm to authenticate in
|
2006-06-19 15:06:08 +00:00
|
|
|
|
}
|
2006-06-20 09:52:57 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the realm and authentication method match the space we
|
|
|
|
|
* found for the URL, assume that it is unchanged.
|
|
|
|
|
*/
|
|
|
|
|
if ([[space realm] isEqualToString: realm]
|
2006-07-04 10:54:12 +00:00
|
|
|
|
&& [space authenticationMethod] == method)
|
2006-06-20 09:52:57 +00:00
|
|
|
|
{
|
|
|
|
|
return space;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
space = [[NSURLProtectionSpace alloc] initWithHost: [URL host]
|
|
|
|
|
port: [[URL port] intValue]
|
|
|
|
|
protocol: [URL scheme]
|
|
|
|
|
realm: realm
|
|
|
|
|
authenticationMethod: method];
|
|
|
|
|
[self setProtectionSpace: space
|
|
|
|
|
forDomains: [domain componentsSeparatedByString: @" "]
|
|
|
|
|
baseURL: URL];
|
|
|
|
|
return AUTORELEASE(space);
|
2006-06-19 15:06:08 +00:00
|
|
|
|
}
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-20 09:52:57 +00:00
|
|
|
|
+ (NSURLProtectionSpace *) protectionSpaceForURL: (NSURL*)URL
|
|
|
|
|
{
|
|
|
|
|
NSURLProtectionSpace *space = nil;
|
|
|
|
|
NSString *scheme;
|
|
|
|
|
NSNumber *port;
|
|
|
|
|
NSString *server;
|
|
|
|
|
|
|
|
|
|
scheme = [URL scheme];
|
|
|
|
|
port = [URL port];
|
|
|
|
|
if ([port intValue] == 80 && [scheme isEqualToString: @"http"])
|
|
|
|
|
{
|
|
|
|
|
port = nil;
|
|
|
|
|
}
|
|
|
|
|
else if ([port intValue] == 443 && [scheme isEqualToString: @"https"])
|
|
|
|
|
{
|
|
|
|
|
port = nil;
|
|
|
|
|
}
|
|
|
|
|
if ([port intValue] == 0)
|
|
|
|
|
{
|
|
|
|
|
server = [NSString stringWithFormat: @"%@://%@",
|
|
|
|
|
scheme, [URL host]];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
server = [NSString stringWithFormat: @"%@://%@:%@",
|
|
|
|
|
scheme, [URL host], port];
|
|
|
|
|
}
|
2007-03-02 11:53:20 +00:00
|
|
|
|
|
2006-06-20 09:52:57 +00:00
|
|
|
|
[storeLock lock];
|
2007-03-02 11:53:20 +00:00
|
|
|
|
NS_DURING
|
2006-06-20 09:52:57 +00:00
|
|
|
|
{
|
2007-03-02 11:53:20 +00:00
|
|
|
|
NSString *found = nil;
|
|
|
|
|
NSDictionary *sDict;
|
|
|
|
|
NSArray *keys;
|
|
|
|
|
unsigned count;
|
|
|
|
|
NSString *path;
|
2006-06-20 09:52:57 +00:00
|
|
|
|
|
2007-03-02 11:53:20 +00:00
|
|
|
|
sDict = [domainMap objectForKey: server];
|
|
|
|
|
keys = [sDict allKeys];
|
|
|
|
|
count = [keys count];
|
|
|
|
|
path = [URL path];
|
|
|
|
|
while (count-- > 0)
|
|
|
|
|
{
|
|
|
|
|
NSString *key = [keys objectAtIndex: count];
|
|
|
|
|
unsigned kl = [key length];
|
|
|
|
|
|
|
|
|
|
if (found == nil || kl > [found length])
|
2006-06-20 09:52:57 +00:00
|
|
|
|
{
|
2007-03-02 11:53:20 +00:00
|
|
|
|
if (kl == 0 || [path hasPrefix: key] == YES)
|
|
|
|
|
{
|
|
|
|
|
found = key;
|
|
|
|
|
}
|
2006-06-20 09:52:57 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2007-03-02 11:53:20 +00:00
|
|
|
|
if (found != nil)
|
|
|
|
|
{
|
|
|
|
|
space = AUTORELEASE(RETAIN([sDict objectForKey: found]));
|
|
|
|
|
}
|
2006-06-20 09:52:57 +00:00
|
|
|
|
}
|
2007-03-02 11:53:20 +00:00
|
|
|
|
NS_HANDLER
|
2006-06-20 09:52:57 +00:00
|
|
|
|
{
|
2007-03-02 11:53:20 +00:00
|
|
|
|
[storeLock unlock];
|
|
|
|
|
[localException raise];
|
2006-06-20 09:52:57 +00:00
|
|
|
|
}
|
2007-03-02 11:53:20 +00:00
|
|
|
|
NS_ENDHANDLER
|
2006-06-20 09:52:57 +00:00
|
|
|
|
[storeLock unlock];
|
2007-03-02 11:53:20 +00:00
|
|
|
|
return space;
|
2006-06-20 09:52:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (void) setProtectionSpace: (NSURLProtectionSpace *)space
|
|
|
|
|
forDomains: (NSArray*)domains
|
|
|
|
|
baseURL: (NSURL*)base
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If there are no URIs specified, everything on the
|
|
|
|
|
* host of the base URL is in the protection space
|
|
|
|
|
*/
|
|
|
|
|
if ([domains count] == 0)
|
|
|
|
|
{
|
|
|
|
|
domains = [NSArray arrayWithObject: @"/"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[storeLock lock];
|
2007-03-02 11:53:20 +00:00
|
|
|
|
NS_DURING
|
2006-06-20 09:52:57 +00:00
|
|
|
|
{
|
2007-03-02 11:53:20 +00:00
|
|
|
|
NSEnumerator *e = [domains objectEnumerator];
|
|
|
|
|
NSString *domain;
|
|
|
|
|
|
|
|
|
|
while ((domain = [e nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
NSURL *u;
|
|
|
|
|
NSString *path;
|
|
|
|
|
NSNumber *port;
|
|
|
|
|
NSString *scheme;
|
|
|
|
|
NSString *server;
|
|
|
|
|
NSMutableDictionary *sDict;
|
|
|
|
|
|
|
|
|
|
u = [NSURL URLWithString: domain];
|
|
|
|
|
scheme = [u scheme];
|
|
|
|
|
if (scheme == nil)
|
|
|
|
|
{
|
|
|
|
|
u = [NSURL URLWithString: domain relativeToURL: base];
|
|
|
|
|
scheme = [u scheme];
|
|
|
|
|
}
|
|
|
|
|
port = [u port];
|
|
|
|
|
if ([port intValue] == 80 && [scheme isEqualToString: @"http"])
|
|
|
|
|
{
|
|
|
|
|
port = nil;
|
|
|
|
|
}
|
|
|
|
|
else if ([port intValue] == 443 && [scheme isEqualToString: @"https"])
|
|
|
|
|
{
|
|
|
|
|
port = nil;
|
|
|
|
|
}
|
|
|
|
|
path = [u path];
|
|
|
|
|
if (path == nil)
|
|
|
|
|
{
|
|
|
|
|
path = @"";
|
|
|
|
|
}
|
|
|
|
|
if ([port intValue] == 0)
|
|
|
|
|
{
|
|
|
|
|
server = [NSString stringWithFormat: @"%@://%@",
|
|
|
|
|
scheme, [u host]];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
server = [NSString stringWithFormat: @"%@://%@:%@",
|
|
|
|
|
scheme, [u host], port];
|
|
|
|
|
}
|
|
|
|
|
sDict = [domainMap objectForKey: server];
|
|
|
|
|
if (sDict == nil)
|
|
|
|
|
{
|
|
|
|
|
sDict = [NSMutableDictionary new];
|
|
|
|
|
[domainMap setObject: sDict forKey: server];
|
|
|
|
|
RELEASE(sDict);
|
|
|
|
|
}
|
2011-03-09 10:24:18 +00:00
|
|
|
|
[sDict setObject: space forKey: path];
|
2006-06-20 09:52:57 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2007-03-02 11:53:20 +00:00
|
|
|
|
NS_HANDLER
|
|
|
|
|
{
|
|
|
|
|
[storeLock unlock];
|
|
|
|
|
[localException raise];
|
|
|
|
|
}
|
|
|
|
|
NS_ENDHANDLER
|
2006-06-20 09:52:57 +00:00
|
|
|
|
[storeLock unlock];
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-19 11:20:17 +00:00
|
|
|
|
- (NSString*) authorizationForAuthentication: (NSString*)authentication
|
|
|
|
|
method: (NSString*)method
|
|
|
|
|
path: (NSString*)path
|
|
|
|
|
{
|
2006-06-19 15:06:08 +00:00
|
|
|
|
NSMutableString *authorisation;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
|
2006-07-04 10:54:12 +00:00
|
|
|
|
if ([self->_space authenticationMethod]
|
|
|
|
|
== NSURLAuthenticationMethodHTTPDigest)
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
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;
|
|
|
|
|
int nc;
|
|
|
|
|
|
|
|
|
|
if (authentication != nil)
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
NSScanner *sc;
|
|
|
|
|
NSString *key;
|
|
|
|
|
NSString *val;
|
|
|
|
|
|
|
|
|
|
sc = [NSScanner scannerWithString: authentication];
|
|
|
|
|
if ([sc scanString: @"Digest" intoString: 0] == NO)
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
NSDebugMLog(@"Bad format HTTP digest in '%@'", authentication);
|
|
|
|
|
return nil; // Not a digest authentication
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2006-07-04 10:54:12 +00:00
|
|
|
|
while ((key = [mimeParser scanName: sc]) != nil)
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
if ([sc scanString: @"=" intoString: 0] == NO)
|
|
|
|
|
{
|
|
|
|
|
NSDebugMLog(@"Missing '=' in HTTP digest '%@'",
|
|
|
|
|
authentication);
|
|
|
|
|
return nil; // Bad name=value specification
|
|
|
|
|
}
|
|
|
|
|
if ((val = [mimeParser 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.
|
|
|
|
|
}
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2006-07-04 10:54:12 +00:00
|
|
|
|
|
|
|
|
|
if (realm == nil)
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
NSDebugMLog(@"Missing HTTP digest realm in '%@'", authentication);
|
|
|
|
|
return nil;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2006-07-04 10:54:12 +00:00
|
|
|
|
if ([realm isEqualToString: [self->_space realm]] == NO)
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
NSDebugMLog(@"Bad HTTP digest realm in '%@'", authentication);
|
|
|
|
|
return nil;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2006-07-04 10:54:12 +00:00
|
|
|
|
if (nonce == nil)
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
NSDebugMLog(@"Missing HTTP digest nonce in '%@'", authentication);
|
|
|
|
|
return nil;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2006-07-04 10:54:12 +00:00
|
|
|
|
|
|
|
|
|
if ([algorithm isEqualToString: @"MD5"] == NO)
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
NSDebugMLog(@"Unsupported HTTP digest algorithm in '%@'",
|
|
|
|
|
authentication);
|
|
|
|
|
return nil;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2006-07-04 10:54:12 +00:00
|
|
|
|
if (![[qop componentsSeparatedByString: @","]
|
|
|
|
|
containsObject: @"auth"])
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
NSDebugMLog(@"Unsupported/missing HTTP digest qop in '%@'",
|
|
|
|
|
authentication);
|
|
|
|
|
return nil;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2006-07-04 10:54:12 +00:00
|
|
|
|
|
|
|
|
|
[self->_lock lock];
|
|
|
|
|
if ([stale boolValue] == YES
|
|
|
|
|
|| [nonce isEqualToString: _nonce] == NO)
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
_nc = 1;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2006-07-04 10:54:12 +00:00
|
|
|
|
ASSIGN(_nonce, nonce);
|
|
|
|
|
ASSIGN(_qop, qop);
|
|
|
|
|
ASSIGN(_opaque, opaque);
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2006-07-04 10:54:12 +00:00
|
|
|
|
else
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
[self->_lock lock];
|
|
|
|
|
nonce = _nonce;
|
|
|
|
|
opaque = _opaque;
|
|
|
|
|
realm = [self->_space realm];
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-07-04 10:54:12 +00:00
|
|
|
|
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];
|
2006-06-19 11:20:17 +00:00
|
|
|
|
|
2006-07-04 10:54:12 +00:00
|
|
|
|
HA2 = [[[[NSString stringWithFormat: @"%@:%@", method, path]
|
|
|
|
|
dataUsingEncoding: NSUTF8StringEncoding] md5Digest] digestHex];
|
|
|
|
|
|
|
|
|
|
response = [[[[NSString stringWithFormat: @"%@:%@:%08x:%@:%@:%@",
|
|
|
|
|
HA1, nonce, nc, cnonce, qop, HA2]
|
|
|
|
|
dataUsingEncoding: NSUTF8StringEncoding] md5Digest] digestHex];
|
|
|
|
|
|
|
|
|
|
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)
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
[authorisation appendFormat: @",opaque=\"%@\"", opaque];
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2006-07-04 10:54:12 +00:00
|
|
|
|
|
|
|
|
|
[self->_lock unlock];
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
2017-06-21 00:01:56 +00:00
|
|
|
|
else if ([self->_space authenticationMethod]
|
|
|
|
|
== NSURLAuthenticationMethodHTMLForm)
|
|
|
|
|
{
|
|
|
|
|
// This should not generate any authentication header.
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
else if ([self->_space authenticationMethod]
|
|
|
|
|
== NSURLAuthenticationMethodNTLM)
|
|
|
|
|
{
|
|
|
|
|
// FIXME: this needs to be implemented
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
else if ([self->_space authenticationMethod]
|
|
|
|
|
== NSURLAuthenticationMethodNegotiate)
|
|
|
|
|
{
|
|
|
|
|
// FIXME: this needs to be implemented
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
else if ([self->_space authenticationMethod]
|
|
|
|
|
== NSURLAuthenticationMethodDefault
|
|
|
|
|
|| [self->_space authenticationMethod]
|
|
|
|
|
== NSURLAuthenticationMethodHTTPBasic)
|
2006-06-19 11:20:17 +00:00
|
|
|
|
{
|
2006-07-04 10:54:12 +00:00
|
|
|
|
NSString *toEncode;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
|
2006-07-04 10:54:12 +00:00
|
|
|
|
if (authentication != nil)
|
|
|
|
|
{
|
|
|
|
|
NSScanner *sc;
|
2006-06-19 11:20:17 +00:00
|
|
|
|
|
2006-07-04 10:54:12 +00:00
|
|
|
|
sc = [NSScanner scannerWithString: authentication];
|
|
|
|
|
if ([sc scanString: @"Basic" intoString: 0] == NO)
|
|
|
|
|
{
|
|
|
|
|
NSDebugMLog(@"Bad format HTTP basic in '%@'", authentication);
|
|
|
|
|
return nil; // Not a basic authentication
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-06-19 11:20:17 +00:00
|
|
|
|
|
2006-07-04 10:54:12 +00:00
|
|
|
|
authorisation = [NSMutableString stringWithCapacity: 64];
|
|
|
|
|
if ([[self->_credential password] length] > 0)
|
|
|
|
|
{
|
|
|
|
|
toEncode = [NSString stringWithFormat: @"%@:%@",
|
|
|
|
|
[self->_credential user], [self->_credential password]];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
toEncode = [NSString stringWithFormat: @"%@",
|
|
|
|
|
[self->_credential user]];
|
|
|
|
|
}
|
|
|
|
|
[authorisation appendFormat: @"Basic %@",
|
|
|
|
|
[GSMimeDocument encodeBase64String: toEncode]];
|
2006-06-19 15:06:08 +00:00
|
|
|
|
}
|
2017-06-21 00:01:56 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// FIXME: Currently, ClientCertificate and ServerTrust authentication
|
|
|
|
|
// methods are NOT implemented and will end up here. They should, in fact,
|
|
|
|
|
// be handled in the SSL connection layer (in GSHTTPURLHandle) rather than
|
|
|
|
|
// in this method.
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
2006-06-19 11:20:17 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2018-04-10 13:59:35 +00:00
|
|
|
|
self->_lock = [NSLock new];
|
2006-06-19 15:37:50 +00:00
|
|
|
|
ASSIGN(self->_space, space);
|
|
|
|
|
ASSIGN(self->_credential, credential);
|
2006-06-19 11:20:17 +00:00
|
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSURLProtectionSpace *) space
|
|
|
|
|
{
|
|
|
|
|
return self->_space;
|
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|