Fix XMPPAuthenticator to support non-optimized message flow

While RFC6120 allows and encourages sending data with the success
message it is also legal to send the same data as a challenge and
await an empty response. This rework honors that fact.
This commit is contained in:
Florian Zeitz 2011-09-18 21:33:19 +02:00
parent 64f131641f
commit 74727c159c
6 changed files with 83 additions and 69 deletions

View file

@ -68,20 +68,13 @@
/** /**
* \return A OFDataAray containing the initial authentication message * \return A OFDataAray containing the initial authentication message
*/ */
- (OFDataArray*)clientFirstMessage; - (OFDataArray*)initialMessage;
/** /**
* \param challenge The challenge to generate a response for * \param data The continuation data send by the server
* \return The response to the given challenge * \return The appropriate response if the data was a challenge, nil otherwise
*/ */
- (OFDataArray*)calculateResponseWithChallenge: (OFDataArray*)challenge; - (OFDataArray*)continueWithData: (OFDataArray*)data;
/**
* Checks whether the servers final message was valid
*
* \param message The servers final message
*/
- (void)parseServerFinalMessage: (OFDataArray*)message;
- (void)setAuthzid: (OFString*)authzid; - (void)setAuthzid: (OFString*)authzid;
- (OFString*)authzid; - (OFString*)authzid;

View file

@ -93,21 +93,13 @@
return [[password copy] autorelease]; return [[password copy] autorelease];
} }
- (OFDataArray*)clientFirstMessage - (OFDataArray*)initialMessage
{ {
@throw [OFNotImplementedException newWithClass: isa return nil;
selector: _cmd];
} }
- (OFDataArray*)calculateResponseWithChallenge: (OFDataArray*)challenge - (OFDataArray*)continueWithData: (OFDataArray*)challenge
{ {
@throw [OFNotImplementedException newWithClass: isa return nil;
selector: _cmd];
}
- (void)parseServerFinalMessage: (OFDataArray*)message
{
@throw [OFNotImplementedException newWithClass: isa
selector: _cmd];
} }
@end @end

View file

@ -619,19 +619,26 @@
OFDataArray *challenge = [OFDataArray OFDataArray *challenge = [OFDataArray
dataArrayWithBase64EncodedString: [element stringValue]]; dataArrayWithBase64EncodedString: [element stringValue]];
OFDataArray *response = [authModule OFDataArray *response = [authModule
calculateResponseWithChallenge: challenge]; continueWithData: challenge];
responseTag = [OFXMLElement elementWithName: @"response" responseTag = [OFXMLElement elementWithName: @"response"
namespace: XMPP_NS_SASL]; namespace: XMPP_NS_SASL];
[responseTag addChild: [OFXMLElement elementWithCharacters: if (response) {
[response stringByBase64Encoding]]]; if ([response count] == 0)
[responseTag addChild: [OFXMLElement
elementWithCharacters: @"="]];
else
[responseTag addChild: [OFXMLElement
elementWithCharacters: [response
stringByBase64Encoding]]];
}
[self sendStanza: responseTag]; [self sendStanza: responseTag];
return; return;
} }
if ([[element name] isEqual: @"success"]) { if ([[element name] isEqual: @"success"]) {
[authModule parseServerFinalMessage: [OFDataArray [authModule continueWithData: [OFDataArray
dataArrayWithBase64EncodedString: [element stringValue]]]; dataArrayWithBase64EncodedString: [element stringValue]]];
if ([delegate respondsToSelector: if ([delegate respondsToSelector:
@ -780,13 +787,20 @@
- (void)XMPP_sendAuth: (OFString*)authName - (void)XMPP_sendAuth: (OFString*)authName
{ {
OFXMLElement *authTag; OFXMLElement *authTag;
OFDataArray *initialMessage = [authModule initialMessage];
authTag = [OFXMLElement elementWithName: @"auth" authTag = [OFXMLElement elementWithName: @"auth"
namespace: XMPP_NS_SASL]; namespace: XMPP_NS_SASL];
[authTag addAttributeWithName: @"mechanism" [authTag addAttributeWithName: @"mechanism"
stringValue: authName]; stringValue: authName];
[authTag addChild: [OFXMLElement elementWithCharacters: if (initialMessage) {
[[authModule clientFirstMessage] stringByBase64Encoding]]]; if ([initialMessage count] == 0)
[authTag addChild: [OFXMLElement
elementWithCharacters: @"="]];
else
[authTag addChild: [OFXMLElement elementWithCharacters:
[initialMessage stringByBase64Encoding]]];
}
[self sendStanza: authTag]; [self sendStanza: authTag];
} }

View file

@ -44,7 +44,7 @@
password: password] autorelease]; password: password] autorelease];
} }
- (OFDataArray*)clientFirstMessage - (OFDataArray*)initialMessage
{ {
OFDataArray *message = [OFDataArray dataArrayWithItemSize: 1]; OFDataArray *message = [OFDataArray dataArrayWithItemSize: 1];
@ -68,17 +68,4 @@
return message; return message;
} }
- (OFDataArray*)calculateResponseWithChallenge: (OFDataArray*)challenge
{
@throw [XMPPAuthFailedException newWithClass: isa
connection: nil
reason: @"Received a challenge "
@"during PLAIN auth"];
}
- (void)parseServerFinalMessage: (OFDataArray*)message
{
return;
}
@end @end

View file

@ -36,6 +36,7 @@
OFDataArray *serverSignature; OFDataArray *serverSignature;
XMPPConnection *connection; XMPPConnection *connection;
BOOL plusAvailable; BOOL plusAvailable;
BOOL authenticated;
} }
/** /**
@ -114,4 +115,6 @@
- (OFDataArray*)XMPP_hiWithData: (OFDataArray *)str - (OFDataArray*)XMPP_hiWithData: (OFDataArray *)str
salt: (OFDataArray *)salt_ salt: (OFDataArray *)salt_
iterationCount: (intmax_t)i; iterationCount: (intmax_t)i;
- (OFDataArray*)XMPP_parseServerFirstMessage: (OFDataArray*)data;
- (OFDataArray*)XMPP_parseServerFinalMessage: (OFDataArray*)data;
@end @end

View file

@ -143,12 +143,18 @@
[old release]; [old release];
} }
- (OFDataArray*)clientFirstMessage - (OFDataArray*)initialMessage
{ {
OFDataArray *ret = [OFDataArray dataArrayWithItemSize: 1]; OFDataArray *ret = [OFDataArray dataArrayWithItemSize: 1];
/* New authentication attempt, reset status */
[cNonce release];
cNonce = nil;
[GS2Header release]; [GS2Header release];
GS2Header = nil; GS2Header = nil;
[serverSignature release];
serverSignature = nil;
authenticated = NO;
if (authzid) if (authzid)
GS2Header = [[OFString alloc] GS2Header = [[OFString alloc]
@ -158,8 +164,6 @@
else else
GS2Header = (plusAvailable ? @"p=tls-unique,," : @"y,,"); GS2Header = (plusAvailable ? @"p=tls-unique,," : @"y,,");
[cNonce release];
cNonce = nil;
cNonce = [[self XMPP_genNonce] retain]; cNonce = [[self XMPP_genNonce] retain];
[clientFirstMessageBare release]; [clientFirstMessageBare release];
@ -174,10 +178,27 @@
[ret addNItems: [clientFirstMessageBare UTF8StringLength] [ret addNItems: [clientFirstMessageBare UTF8StringLength]
fromCArray: [clientFirstMessageBare UTF8String]]; fromCArray: [clientFirstMessageBare UTF8String]];
return ret; return ret;
} }
- (OFDataArray*)calculateResponseWithChallenge: (OFDataArray*)challenge - (OFDataArray*)continueWithData: (OFDataArray*)data
{
OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
OFDataArray *ret;
if (!serverSignature)
ret = [self XMPP_parseServerFirstMessage: data];
else
ret = [self XMPP_parseServerFinalMessage: data];
[ret retain];
[pool release];
return [ret autorelease];
}
- (OFDataArray*)XMPP_parseServerFirstMessage: (OFDataArray*)data
{ {
size_t i; size_t i;
uint8_t *clientKey, *serverKey, *clientSignature; uint8_t *clientKey, *serverKey, *clientSignature;
@ -185,7 +206,6 @@
OFHash *hash; OFHash *hash;
OFDataArray *ret, *authMessage, *tmpArray, *salt = nil, *saltedPassword; OFDataArray *ret, *authMessage, *tmpArray, *salt = nil, *saltedPassword;
OFString *tmpString, *sNonce = nil; OFString *tmpString, *sNonce = nil;
OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
OFEnumerator *enumerator; OFEnumerator *enumerator;
OFString *comp; OFString *comp;
enum { enum {
@ -198,9 +218,9 @@
ret = [OFDataArray dataArrayWithItemSize: 1]; ret = [OFDataArray dataArrayWithItemSize: 1];
authMessage = [OFDataArray dataArrayWithItemSize: 1]; authMessage = [OFDataArray dataArrayWithItemSize: 1];
OFString *chal = [OFString stringWithUTF8String: [challenge cArray] OFString *chal = [OFString stringWithUTF8String: [data cArray]
length: [challenge count] * length: [data count] *
[challenge itemSize]]; [data itemSize]];
enumerator = enumerator =
[[chal componentsSeparatedByString: @","] objectEnumerator]; [[chal componentsSeparatedByString: @","] objectEnumerator];
@ -253,14 +273,14 @@
[ret addNItems: [sNonce UTF8StringLength] [ret addNItems: [sNonce UTF8StringLength]
fromCArray: [sNonce UTF8String]]; fromCArray: [sNonce UTF8String]];
tmpArray = [OFDataArray dataArrayWithItemSize: 1];
[tmpArray addNItems: [password UTF8StringLength]
fromCArray: [password UTF8String]];
/* /*
* IETF RFC 5802: * IETF RFC 5802:
* SaltedPassword := Hi(Normalize(password), salt, i) * SaltedPassword := Hi(Normalize(password), salt, i)
*/ */
tmpArray = [OFDataArray dataArrayWithItemSize: 1];
[tmpArray addNItems: [password UTF8StringLength]
fromCArray: [password UTF8String]];
saltedPassword = [self XMPP_hiWithData: tmpArray saltedPassword = [self XMPP_hiWithData: tmpArray
salt: salt salt: salt
iterationCount: iterCount]; iterationCount: iterCount];
@ -274,8 +294,8 @@
[authMessage addNItems: [clientFirstMessageBare UTF8StringLength] [authMessage addNItems: [clientFirstMessageBare UTF8StringLength]
fromCArray: [clientFirstMessageBare UTF8String]]; fromCArray: [clientFirstMessageBare UTF8String]];
[authMessage addItem: ","]; [authMessage addItem: ","];
[authMessage addNItems: [challenge count] * [challenge itemSize] [authMessage addNItems: [data count] * [data itemSize]
fromCArray: [challenge cArray]]; fromCArray: [data cArray]];
[authMessage addItem: ","]; [authMessage addItem: ","];
[authMessage addNItems: [ret count] [authMessage addNItems: [ret count]
fromCArray: [ret cArray]]; fromCArray: [ret cArray]];
@ -347,20 +367,24 @@
[ret addNItems: [tmpString UTF8StringLength] [ret addNItems: [tmpString UTF8StringLength]
fromCArray: [tmpString UTF8String]]; fromCArray: [tmpString UTF8String]];
[ret retain]; return ret;
[pool release];
return [ret autorelease];
} }
- (void)parseServerFinalMessage: (OFDataArray*)message - (OFDataArray*)XMPP_parseServerFinalMessage: (OFDataArray*)data
{ {
OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; OFString *mess, *value;
OFString *mess = [OFString stringWithUTF8String: [message cArray]
length: [message count] * /*
[message itemSize]]; * server-final-message already received,
OFString *value = [mess substringWithRange: * we were just waiting for the last word from the server
of_range(2, [mess length] - 2)]; */
if (authenticated)
return nil;
mess = [OFString stringWithUTF8String: [data cArray]
length: [data count] *
[data itemSize]];
value = [mess substringWithRange: of_range(2, [mess length] - 2)];
if ([mess hasPrefix: @"v="]) { if ([mess hasPrefix: @"v="]) {
if (![value isEqual: [serverSignature stringByBase64Encoding]]) if (![value isEqual: [serverSignature stringByBase64Encoding]])
@ -368,12 +392,13 @@
newWithClass: isa newWithClass: isa
connection: nil connection: nil
reason: @"Received wrong ServerSignature"]; reason: @"Received wrong ServerSignature"];
authenticated = YES;
} else } else
@throw [XMPPAuthFailedException newWithClass: isa @throw [XMPPAuthFailedException newWithClass: isa
connection: nil connection: nil
reason: value]; reason: value];
[pool release]; return nil;
} }
- (OFString*)XMPP_genNonce - (OFString*)XMPP_genNonce