Skip to content

Commit e29af14

Browse files
committed
Pluggable authentication modules
1 parent be5eab5 commit e29af14

27 files changed

+1937
-1137
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#import <Foundation/Foundation.h>
2+
#import "XMPPSASLAuthentication.h"
3+
#import "XMPP.h"
4+
5+
6+
@interface XMPPAnonymousAuthentication : NSObject <XMPPSASLAuthentication>
7+
8+
- (id)initWithStream:(XMPPStream *)stream;
9+
10+
// This class implements the XMPPSASLAuthentication protocol.
11+
//
12+
// See XMPPSASLAuthentication.h for more information.
13+
14+
@end
15+
16+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
17+
#pragma mark -
18+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
19+
20+
@interface XMPPStream (XMPPAnonymousAuthentication)
21+
22+
/**
23+
* Returns whether or not the server support anonymous authentication.
24+
*
25+
* This information is available after the stream is connected.
26+
* In other words, after the delegate has received xmppStreamDidConnect: notification.
27+
**/
28+
- (BOOL)supportsAnonymousAuthentication;
29+
30+
/**
31+
* This method attempts to start the anonymous authentication process.
32+
*
33+
* This method is asynchronous.
34+
*
35+
* If there is something immediately wrong,
36+
* such as the stream is not connected or doesn't support anonymous authentication,
37+
* the method will return NO and set the error.
38+
* Otherwise the delegate callbacks are used to communicate auth success or failure.
39+
*
40+
* @see xmppStreamDidAuthenticate:
41+
* @see xmppStream:didNotAuthenticate:
42+
**/
43+
- (BOOL)authenticateAnonymously:(NSError **)errPtr;
44+
45+
@end
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#import "XMPPAnonymousAuthentication.h"
2+
#import "XMPP.h"
3+
#import "XMPPLogging.h"
4+
#import "XMPPInternal.h"
5+
#import "NSXMLElement+XMPP.h"
6+
7+
#if ! __has_feature(objc_arc)
8+
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
9+
#endif
10+
11+
// Log levels: off, error, warn, info, verbose
12+
#if DEBUG
13+
static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO; // | XMPP_LOG_FLAG_TRACE;
14+
#else
15+
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
16+
#endif
17+
18+
/**
19+
* Seeing a return statements within an inner block
20+
* can sometimes be mistaken for a return point of the enclosing method.
21+
* This makes inline blocks a bit easier to read.
22+
**/
23+
#define return_from_block return
24+
25+
26+
@implementation XMPPAnonymousAuthentication
27+
{
28+
#if __has_feature(objc_arc_weak)
29+
__weak XMPPStream *xmppStream;
30+
#else
31+
__unsafe_unretained XMPPStream *xmppStream;
32+
#endif
33+
}
34+
35+
+ (NSString *)mechanismName
36+
{
37+
return @"ANONYMOUS";
38+
}
39+
40+
- (id)initWithStream:(XMPPStream *)stream
41+
{
42+
if ((self = [super init]))
43+
{
44+
xmppStream = stream;
45+
}
46+
return self;
47+
}
48+
49+
- (id)initWithStream:(XMPPStream *)stream password:(NSString *)password
50+
{
51+
return [self initWithStream:stream];
52+
}
53+
54+
- (BOOL)start:(NSError **)errPtr
55+
{
56+
// <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="ANONYMOUS" />
57+
58+
NSXMLElement *auth = [NSXMLElement elementWithName:@"auth" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
59+
[auth addAttributeWithName:@"mechanism" stringValue:@"ANONYMOUS"];
60+
61+
[xmppStream sendAuthElement:auth];
62+
63+
return YES;
64+
}
65+
66+
- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)authResponse
67+
{
68+
// We're expecting a success response.
69+
// If we get anything else we can safely assume it's the equivalent of a failure response.
70+
71+
if ([[authResponse name] isEqualToString:@"success"])
72+
{
73+
return XMPP_AUTH_SUCCESS;
74+
}
75+
else
76+
{
77+
return XMPP_AUTH_FAIL;
78+
}
79+
}
80+
81+
@end
82+
83+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
84+
#pragma mark -
85+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
86+
87+
@implementation XMPPStream (XMPPAnonymousAuthentication)
88+
89+
- (BOOL)supportsAnonymousAuthentication
90+
{
91+
return [self supportsAuthenticationMechanism:[XMPPAnonymousAuthentication mechanismName]];
92+
}
93+
94+
- (BOOL)authenticateAnonymously:(NSError **)errPtr
95+
{
96+
XMPPLogTrace();
97+
98+
__block BOOL result = YES;
99+
__block NSError *err = nil;
100+
101+
dispatch_block_t block = ^{ @autoreleasepool {
102+
103+
if ([self supportsAnonymousAuthentication])
104+
{
105+
XMPPAnonymousAuthentication *anonymousAuth = [[XMPPAnonymousAuthentication alloc] initWithStream:self];
106+
107+
result = [self authenticate:anonymousAuth error:&err];
108+
}
109+
else
110+
{
111+
NSString *errMsg = @"The server does not support anonymous authentication.";
112+
NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
113+
114+
err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];
115+
116+
result = NO;
117+
}
118+
}};
119+
120+
if (dispatch_get_current_queue() == xmppQueue)
121+
block();
122+
else
123+
dispatch_sync(xmppQueue, block);
124+
125+
if (errPtr)
126+
*errPtr = err;
127+
128+
return result;
129+
}
130+
131+
@end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#import <Foundation/Foundation.h>
2+
#import "XMPPSASLAuthentication.h"
3+
#import "XMPPStream.h"
4+
5+
6+
@interface XMPPDeprecatedDigestAuthentication : NSObject <XMPPSASLAuthentication>
7+
8+
// This class implements the XMPPSASLAuthentication protocol.
9+
//
10+
// See XMPPSASLAuthentication.h for more information.
11+
12+
@end
13+
14+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
15+
#pragma mark -
16+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
17+
18+
@interface XMPPStream (XMPPDeprecatedDigestAuthentication)
19+
20+
- (BOOL)supportsDeprecatedDigestAuthentication;
21+
22+
@end
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#import "XMPPDeprecatedDigestAuthentication.h"
2+
#import "XMPP.h"
3+
#import "XMPPInternal.h"
4+
#import "XMPPLogging.h"
5+
#import "NSData+XMPP.h"
6+
#import "NSXMLElement+XMPP.h"
7+
8+
#if ! __has_feature(objc_arc)
9+
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
10+
#endif
11+
12+
// Log levels: off, error, warn, info, verbose
13+
#if DEBUG
14+
static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO; // | XMPP_LOG_FLAG_TRACE;
15+
#else
16+
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
17+
#endif
18+
19+
20+
@implementation XMPPDeprecatedDigestAuthentication
21+
{
22+
#if __has_feature(objc_arc_weak)
23+
__weak XMPPStream *xmppStream;
24+
#else
25+
__unsafe_unretained XMPPStream *xmppStream;
26+
#endif
27+
28+
NSString *password;
29+
}
30+
31+
+ (NSString *)mechanismName
32+
{
33+
// This deprecated method isn't listed in the normal mechanisms list
34+
return nil;
35+
}
36+
37+
- (id)initWithStream:(XMPPStream *)stream password:(NSString *)inPassword
38+
{
39+
if ((self = [super init]))
40+
{
41+
xmppStream = stream;
42+
password = inPassword;
43+
}
44+
return self;
45+
}
46+
47+
- (BOOL)start:(NSError **)errPtr
48+
{
49+
XMPPLogTrace();
50+
51+
// The server does not appear to support SASL authentication (at least any type we can use)
52+
// So we'll revert back to the old fashioned jabber:iq:auth mechanism
53+
54+
XMPPJID *myJID = xmppStream.myJID;
55+
56+
NSString *username = [myJID user];
57+
NSString *resource = [myJID resource];
58+
59+
if ([resource length] == 0)
60+
{
61+
// If resource is nil or empty, we need to auto-create one
62+
63+
resource = [XMPPStream generateUUID];
64+
}
65+
66+
NSString *rootID = [[[xmppStream rootElement] attributeForName:@"id"] stringValue];
67+
NSString *digestStr = [NSString stringWithFormat:@"%@%@", rootID, password];
68+
69+
NSString *digest = [[[digestStr dataUsingEncoding:NSUTF8StringEncoding] sha1Digest] hexStringValue];
70+
71+
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:auth"];
72+
[query addChild:[NSXMLElement elementWithName:@"username" stringValue:username]];
73+
[query addChild:[NSXMLElement elementWithName:@"resource" stringValue:resource]];
74+
[query addChild:[NSXMLElement elementWithName:@"digest" stringValue:digest]];
75+
76+
XMPPIQ *iq = [XMPPIQ iqWithType:@"set"];
77+
[iq addChild:query];
78+
79+
[xmppStream sendAuthElement:iq];
80+
81+
return YES;
82+
}
83+
84+
- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)authResponse
85+
{
86+
XMPPLogTrace();
87+
88+
// We used the old fashioned jabber:iq:auth mechanism
89+
90+
if ([[authResponse attributeStringValueForName:@"type"] isEqualToString:@"error"])
91+
{
92+
return XMPP_AUTH_FAIL;
93+
}
94+
else
95+
{
96+
return XMPP_AUTH_SUCCESS;
97+
}
98+
}
99+
100+
- (BOOL)shouldResendOpeningNegotiationAfterSuccessfulAuthentication
101+
{
102+
return NO;
103+
}
104+
105+
@end
106+
107+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
108+
#pragma mark -
109+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
110+
111+
@implementation XMPPStream (XMPPDeprecatedDigestAuthentication)
112+
113+
/**
114+
* This method only applies to servers that don't support XMPP version 1.0, as defined in RFC 3920.
115+
* With these servers, we attempt to discover supported authentication modes via the jabber:iq:auth namespace.
116+
**/
117+
- (BOOL)supportsDeprecatedDigestAuthentication
118+
{
119+
__block BOOL result = NO;
120+
121+
dispatch_block_t block = ^{ @autoreleasepool {
122+
123+
// The root element can be properly queried for authentication mechanisms anytime after the
124+
// stream:features are received, and TLS has been setup (if required)
125+
if (state >= STATE_XMPP_POST_NEGOTIATION)
126+
{
127+
// Search for an iq element within the rootElement.
128+
// Recall that some servers might stupidly add a "jabber:client" namespace which might cause problems
129+
// if we simply used the elementForName method.
130+
131+
NSXMLElement *iq = nil;
132+
133+
NSUInteger i, count = [rootElement childCount];
134+
for (i = 0; i < count; i++)
135+
{
136+
NSXMLNode *childNode = [rootElement childAtIndex:i];
137+
138+
if ([childNode kind] == NSXMLElementKind)
139+
{
140+
if ([[childNode name] isEqualToString:@"iq"])
141+
{
142+
iq = (NSXMLElement *)childNode;
143+
}
144+
}
145+
}
146+
147+
NSXMLElement *query = [iq elementForName:@"query" xmlns:@"jabber:iq:auth"];
148+
NSXMLElement *digest = [query elementForName:@"digest"];
149+
150+
result = (digest != nil);
151+
}
152+
}};
153+
154+
if (dispatch_get_current_queue() == xmppQueue)
155+
block();
156+
else
157+
dispatch_sync(xmppQueue, block);
158+
159+
return result;
160+
}
161+
162+
@end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#import <Foundation/Foundation.h>
2+
#import "XMPPSASLAuthentication.h"
3+
#import "XMPPStream.h"
4+
5+
6+
@interface XMPPDeprecatedPlainAuthentication : NSObject <XMPPSASLAuthentication>
7+
8+
// This class implements the XMPPSASLAuthentication protocol.
9+
//
10+
// See XMPPSASLAuthentication.h for more information.
11+
12+
@end
13+
14+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
15+
#pragma mark -
16+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
17+
18+
@interface XMPPStream (XMPPDeprecatedPlainAuthentication)
19+
20+
- (BOOL)supportsDeprecatedPlainAuthentication;
21+
22+
@end

0 commit comments

Comments
 (0)