ObjXMPP  Check-in [6a3b0a9988]

Overview
Comment:Add SCRAM-SHA-1 support

This adds the new base class XMPPAuthenticator and the derived
classes XMPPSCRAMAuth and XMPPPLAINAuth.
They are now used for authentication from within XMPPConnection.

Also adds XMPPAuthFailedException which is thrown in appropriate places.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 6a3b0a99882a89bef4d0597082bd7a8e3a853715ce92524b5cb08e054a745179
User & Date: florob@babelmonkeys.de on 2011-02-21 03:09:39
Other Links: manifest | tags
Context
2011-02-24
18:51
Escape SCRAM usernames (authzid and authcid) check-in: a59df3b671 user: florob@babelmonkeys.de tags: trunk
2011-02-21
03:09
Add SCRAM-SHA-1 support check-in: 6a3b0a9988 user: florob@babelmonkeys.de tags: trunk
2011-02-19
22:39
Fix mechanisms parsing check-in: 5027cc014a user: florob@babelmonkeys.de tags: trunk
Changes

Modified src/Makefile from [aed6ade7ee] to [2d3658b273].

1
2
3

4
5
6
1
2

3
4
5
6


-
+



all:
	objfw-compile -Wall --lib 0.0 -o objxmpp *.m \
		`pkg-config --cflags --libs libidn`
		`pkg-config --cflags --libs libidn libbsd`

clean:
	rm -f *.o *.so *.dylib *.dll

Added src/XMPPAuthenticator.h version [dd2be7ab8f].

Added src/XMPPAuthenticator.m version [ce929b9acc].

Modified src/XMPPConnection.h from [4559cd5da6] to [1b5e581e8a].

24
25
26
27
28
29
30

31
32
33
34
35
36
37
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38







+







#import <ObjFW/ObjFW.h>

@class XMPPConnection;
@class XMPPJID;
@class XMPPIQ;
@class XMPPMessage;
@class XMPPPresence;
@class XMPPAuthenticator;

@protocol XMPPConnectionDelegate
- (void)connectionWasClosed: (XMPPConnection*)conn;
- (void)connection: (XMPPConnection*)conn
      didReceiveIQ: (XMPPIQ*)iq;
-   (void)connection: (XMPPConnection*)conn
  didReceivePresence: (XMPPPresence*)pres;
59
60
61
62
63
64
65

66
67
68
69
70
71
72
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74







+







	XMPPJID *JID;
	/// The port to connect to
	short port;
	/// Whether to use TLS
	BOOL useTLS;
	id <XMPPConnectionDelegate> delegate;
	OFMutableArray *mechanisms;
	XMPPAuthenticator *authModule;
}

@property (copy) OFString *username;
@property (copy) OFString *password;
@property (copy) OFString *server;
@property (copy) OFString *resource;
@property (copy, readonly) XMPPJID *JID;

Modified src/XMPPConnection.m from [8f383aeac0] to [b8bdaffc4b].

23
24
25
26
27
28
29


30
31
32
33
34
35
36
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38







+
+








#include <assert.h>

#include <stringprep.h>
#include <idna.h>

#import "XMPPConnection.h"
#import "XMPPSCRAMAuth.h"
#import "XMPPPLAINAuth.h"
#import "XMPPStanza.h"
#import "XMPPJID.h"
#import "XMPPIQ.h"
#import "XMPPExceptions.h"

#define NS_BIND @"urn:ietf:params:xml:ns:xmpp-bind"
#define NS_CLIENT @"jabber:client"
60
61
62
63
64
65
66

67
68
69
70
71
72
73
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76







+







}

- (void)dealloc
{
	[sock release];
	[parser release];
	[elementBuilder release];
	[authModule release];

	[super dealloc];
}

- (void)setUsername: (OFString*)username_
{
	OFString *old = username;
219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

249
250

251
252
253
254
255
256
257
222
223
224
225
226
227
228

229
230
231















232
233
234
235

236
237

238
239
240
241
242
243
244
245







-
+


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-




-
+

-
+







			assert(0);
		}
	}

	parser.delegate = elementBuilder;
}

- (void)_sendPLAINAuth
- (void)_sendAuth: (OFString*)name
{
	OFXMLElement *authTag;
	OFDataArray *message;

	message = [OFDataArray dataArrayWithItemSize: 1];
	/* XXX: authzid would go here */
	//[message addItem: authzid];
	/* separator */
	[message addItem: ""];
	/* authcid */
	[message addNItems: [username cStringLength]
		fromCArray: [username cString]];
	/* separator */
	[message addItem: ""];
	/* passwd */
	[message addNItems: [password cStringLength]
		fromCArray: [password cString]];

	authTag = [OFXMLElement elementWithName: @"auth"
				      namespace: NS_SASL];
	[authTag addAttributeWithName: @"mechanism"
			  stringValue: @"PLAIN"];
			  stringValue: name];
	[authTag addChild: [OFXMLElement elementWithCharacters:
	    [message stringByBase64Encoding]]];
	    [[authModule getClientFirstMessage] stringByBase64Encoding]]];

	[self sendStanza: authTag];
}

- (void)_sendResourceBind
{
	XMPPIQ *iq = [XMPPIQ IQWithType: @"set"
285
286
287
288
289
290
291
292
293












294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313



















314
315
316
317
318
319
320
321
322
323
324
325

326
327






328
329
330
331
332
333
334
273
274
275
276
277
278
279


280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310

311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337




338
339

340
341
342
343
344
345
346
347
348
349
350
351
352







-
-
+
+
+
+
+
+
+
+
+
+
+
+



















-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
-
-
-
+

-
+
+
+
+
+
+







				     namespace: NS_SASL];
	OFXMLElement *bind = [elem elementsForName: @"bind"
					 namespace: NS_BIND].firstObject;

	for (OFXMLElement *mech in [mechs.firstObject children])
		[mechanisms addObject: [mech.children.firstObject stringValue]];

	if ([mechanisms containsObject: @"PLAIN"])
		[self _sendPLAINAuth];
	if ([mechanisms containsObject: @"SCRAM-SHA-1"]) {
		authModule = [[XMPPSCRAMAuth alloc]
		    initWithAuthcid: username
			   password: password
			       hash: [OFSHA1Hash class]];
		[self _sendAuth: @"SCRAM-SHA-1"];
	} else if ([mechanisms containsObject: @"PLAIN"]) {
		authModule = [[XMPPPLAINAuth alloc]
		    initWithAuthcid: username
			   password: password];
		[self _sendAuth: @"PLAIN"];
	}

	if (bind != nil)
		[self _sendResourceBind];
}

- (void)elementBuilder: (OFXMLElementBuilder*)b
       didBuildElement: (OFXMLElement*)elem
{
	elem.defaultNamespace = NS_CLIENT;
	[elem setPrefix: @"stream"
	   forNamespace: NS_STREAM];

	if ([elem.name isEqual: @"features"] &&
	    [elem.namespace isEqual: NS_STREAM]) {
		[self _handleFeatures: elem];
		return;
	}

	if ([elem.namespace isEqual: NS_SASL]) {
		if ([elem.name isEqual: @"success"]) {
		if ([elem.name isEqual: @"challenge"]) {
			OFXMLElement *responseTag;
			OFDataArray *challenge =
			    [OFDataArray dataArrayWithBase64EncodedString:
				[elem.children.firstObject stringValue]];
			OFDataArray *response =
			    [authModule getResponseWithChallenge: challenge];

			responseTag = [OFXMLElement elementWithName: @"response"
							  namespace: NS_SASL];
			[responseTag
			    addChild: [OFXMLElement elementWithCharacters:
				[response stringByBase64Encoding]]];

			[self sendStanza: responseTag];
		} else if ([elem.name isEqual: @"success"]) {
			[authModule parseServerFinalMessage:
			    [OFDataArray dataArrayWithBase64EncodedString:
				[elem.children.firstObject stringValue]]];
			of_log(@"Auth successful");

			/* Stream restart */
			[mechanisms release];
			mechanisms = [[OFMutableArray alloc] init];
			parser.delegate = self;

			[self _startStream];
			return;
		}

		if ([elem.name isEqual: @"failure"])
		} else if ([elem.name isEqual: @"failure"]) {
			of_log(@"Auth failed!");
			// FIXME: Handle!
			// FIXME: Do more parsing/handling
			@throw [XMPPAuthFailedException
			    newWithClass: isa
			      connection: self
				  reason: [elem stringValue]];
		}
	}

	if ([elem.name isEqual: @"iq"] &&
	    [elem.namespace isEqual: NS_CLIENT]) {
		XMPPIQ *iq = [XMPPIQ stanzaWithElement: elem];

		// FIXME: More checking!

Modified src/XMPPExceptions.h from [c92946c52e] to [e4ea710d7a].

19
20
21
22
23
24
25

26
27
28
29
30
31
32
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33







+







 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import <ObjFW/ObjFW.h>

@class XMPPConnection;
@class XMPPAuthenticator;

@interface XMPPException: OFException
{
	XMPPConnection *connection;
}

@property (readonly, nonatomic) XMPPConnection *connection;
68
69
70
71
72
73
74















69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
     operation: (OFString*)operation
	string: (OFString*)string;
- initWithClass: (Class)class_
     connection: (XMPPConnection*)conn
      operation: (OFString*)operation
	 string: (OFString*)string;
@end

@interface XMPPAuthFailedException: XMPPException
{
	OFString *reason;
}

@property (readonly, nonatomic) OFString *reason;

+ newWithClass: (Class)class_
    connection: (XMPPConnection*)conn
	reason: (OFString*)reason_;
- initWithClass: (Class)class_
     connection: (XMPPConnection*)conn
	 reason: (OFString*)reason_;
@end

Modified src/XMPPExceptions.m from [efe19df829] to [60f59f8f6f].

203
204
205
206
207
208
209





























































210
211
212
213
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




		return description;

	pool = [[OFAutoreleasePool alloc] init];
	description = [[OFString alloc] initWithFormat:
	    @"IDNA operation %@ failed on string '%@'!",
	    operation, string];
	[pool release];

	return description;
}
@end

@implementation XMPPAuthFailedException
@synthesize reason;

+ newWithClass: (Class)class_
    connection: (XMPPConnection*)conn
	reason: (OFString*)reason_;
{
	return [[self alloc] initWithClass: class_
				connection: conn
				    reason: reason_];
}

- initWithClass: (Class)class_
     connection: (XMPPConnection*)conn
{
	Class c = isa;
	[self release];
	@throw [OFNotImplementedException newWithClass: c
					      selector: _cmd];
}

- initWithClass: (Class)class_
     connection: (XMPPConnection*)conn
	 reason: (OFString*)reason_
{
	self = [super initWithClass: class_
			 connection: conn];

	@try {
		reason = [reason_ copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[reason release];

	[super dealloc];
}

- (OFString*)description
{
	OFAutoreleasePool *pool;

	if (description != nil)
		return description;

	pool = [[OFAutoreleasePool alloc] init];
	description = [[OFString alloc] initWithFormat:
	    @"Authentication failed. Reason: %@!", reason];
	[pool release];

	return description;
}
@end

Added src/XMPPPLAINAuth.h version [838ab06906].

Added src/XMPPPLAINAuth.m version [14bbbb67be].

Added src/XMPPSCRAMAuth.h version [c2b505a01d].

Added src/XMPPSCRAMAuth.m version [662da97f8a].

Modified tests/test.m from [00843fd022] to [1d590d00a0].

93
94
95
96
97
98
99

100




101
102
93
94
95
96
97
98
99
100

101
102
103
104
105
106







+
-
+
+
+
+


	[conn setServer: [arguments objectAtIndex: 0]];
	[conn setUsername: [arguments objectAtIndex: 1]];
	[conn setPassword: [arguments objectAtIndex: 2]];
	[conn setResource: @"ObjXMPP"];
	[conn setUseTLS: NO];

	[conn connect];
	@try {
	[conn handleConnection];
		[conn handleConnection];
	} @catch (id e) {
		of_log(@"%@", e);
	}
}
@end