yong-stropheruby 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/ext/auth.c ADDED
@@ -0,0 +1,990 @@
1
+ /* auth.c
2
+ ** strophe XMPP client library -- auth functions and handlers
3
+ **
4
+ ** Copyright (C) 2005-2008 OGG, LLC. All rights reserved.
5
+ **
6
+ ** This software is provided AS-IS with no warranty, either express or
7
+ ** implied.
8
+ **
9
+ ** This software is distributed under license and may not be copied,
10
+ ** modified or distributed except as expressly authorized under the
11
+ ** terms of the license contained in the file LICENSE.txt in this
12
+ ** distribution.
13
+ */
14
+
15
+ /** @file
16
+ * Authentication function and handlers.
17
+ */
18
+
19
+ #include <stdio.h>
20
+ #include <stdlib.h>
21
+ #include <string.h>
22
+
23
+ #include "strophe.h"
24
+ #include "common.h"
25
+ #include "sasl.h"
26
+
27
+ #ifdef _WIN32
28
+ #define strcasecmp stricmp
29
+ #endif
30
+
31
+ /* TODO: these should configurable at runtime on a per connection basis */
32
+
33
+ #ifndef FEATURES_TIMEOUT
34
+ /** @def FEATURES_TIMEOUT
35
+ * Time to wait for &lt;stream:features/&gt; stanza.
36
+ */
37
+ #define FEATURES_TIMEOUT 15000 /* 15 seconds */
38
+ #endif
39
+ #ifndef BIND_TIMEOUT
40
+ /** @def BIND_TIMEOUT
41
+ * Time to wait for &lt;bind/&gt; stanza reply.
42
+ */
43
+ #define BIND_TIMEOUT 15000 /* 15 seconds */
44
+ #endif
45
+ #ifndef SESSION_TIMEOUT
46
+ /** @def SESSION_TIMEOUT
47
+ * Time to wait for &lt;session/&gt; stanza reply.
48
+ */
49
+ #define SESSION_TIMEOUT 15000 /* 15 seconds */
50
+ #endif
51
+ #ifndef LEGACY_TIMEOUT
52
+ /** @def LEGACY_TIMEOUT
53
+ * Time to wait for legacy authentication to complete.
54
+ */
55
+ #define LEGACY_TIMEOUT 15000 /* 15 seconds */
56
+ #endif
57
+
58
+ static void _auth(xmpp_conn_t * const conn);
59
+ static void _handle_open_tls(xmpp_conn_t * const conn);
60
+ static void _handle_open_sasl(xmpp_conn_t * const conn);
61
+ static int _handle_missing_legacy(xmpp_conn_t * const conn,
62
+ void * const userdata);
63
+ static int _handle_legacy(xmpp_conn_t * const conn,
64
+ xmpp_stanza_t * const stanza,
65
+ void * const userdata);
66
+ static int _handle_features_sasl(xmpp_conn_t * const conn,
67
+ xmpp_stanza_t * const stanza,
68
+ void * const userdata);
69
+ static int _handle_sasl_result(xmpp_conn_t * const conn,
70
+ xmpp_stanza_t * const stanza,
71
+ void * const userdata);
72
+ static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
73
+ xmpp_stanza_t * const stanza,
74
+ void * const userdata);
75
+ static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
76
+ xmpp_stanza_t * const stanza,
77
+ void * const userdata);
78
+
79
+ static int _handle_missing_features_sasl(xmpp_conn_t * const conn,
80
+ void * const userdata);
81
+ static int _handle_missing_bind(xmpp_conn_t * const conn,
82
+ void * const userdata);
83
+ static int _handle_bind(xmpp_conn_t * const conn,
84
+ xmpp_stanza_t * const stanza,
85
+ void * const userdata);
86
+ static int _handle_session(xmpp_conn_t * const conn,
87
+ xmpp_stanza_t * const stanza,
88
+ void * const userdata);
89
+ static int _handle_missing_session(xmpp_conn_t * const conn,
90
+ void * const userdata);
91
+
92
+ /* stream:error handler */
93
+ static int _handle_error(xmpp_conn_t * const conn,
94
+ xmpp_stanza_t * const stanza,
95
+ void * const userdata)
96
+ {
97
+ xmpp_stanza_t *child;
98
+ char *name;
99
+
100
+ /* free old stream error if it's still there */
101
+ if (conn->stream_error) {
102
+ xmpp_stanza_release(conn->stream_error->stanza);
103
+ if (conn->stream_error->text)
104
+ xmpp_free(conn->ctx, conn->stream_error->text);
105
+ xmpp_free(conn->ctx, conn->stream_error);
106
+ }
107
+
108
+ /* create stream error structure */
109
+ conn->stream_error = (xmpp_stream_error_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_stream_error_t));
110
+
111
+ conn->stream_error->text = NULL;
112
+ conn->stream_error->type = XMPP_SE_UNDEFINED_CONDITION;
113
+
114
+ if (conn->stream_error) {
115
+ child = xmpp_stanza_get_children(stanza);
116
+ do {
117
+ char *ns = NULL;
118
+
119
+ if (child) {
120
+ ns = xmpp_stanza_get_ns(child);
121
+ }
122
+
123
+ if (ns && strcmp(ns, XMPP_NS_STREAMS_IETF) == 0) {
124
+ name = xmpp_stanza_get_name(child);
125
+ if (strcmp(name, "text") == 0) {
126
+ if (conn->stream_error->text)
127
+ xmpp_free(conn->ctx, conn->stream_error->text);
128
+ conn->stream_error->text = xmpp_stanza_get_text(child);
129
+ } else if (strcmp(name, "bad-format") == 0)
130
+ conn->stream_error->type = XMPP_SE_BAD_FORMAT;
131
+ else if (strcmp(name, "bad-namespace-prefix") == 0)
132
+ conn->stream_error->type = XMPP_SE_BAD_NS_PREFIX;
133
+ else if (strcmp(name, "conflict") == 0)
134
+ conn->stream_error->type = XMPP_SE_CONFLICT;
135
+ else if (strcmp(name, "connection-timeout") == 0)
136
+ conn->stream_error->type = XMPP_SE_CONN_TIMEOUT;
137
+ else if (strcmp(name, "host-gone") == 0)
138
+ conn->stream_error->type = XMPP_SE_HOST_GONE;
139
+ else if (strcmp(name, "host-unknown") == 0)
140
+ conn->stream_error->type = XMPP_SE_HOST_UNKNOWN;
141
+ else if (strcmp(name, "improper-addressing") == 0)
142
+ conn->stream_error->type = XMPP_SE_IMPROPER_ADDR;
143
+ else if (strcmp(name, "internal-server-error") == 0)
144
+ conn->stream_error->type = XMPP_SE_INTERNAL_SERVER_ERROR;
145
+ else if (strcmp(name, "invalid-from") == 0)
146
+ conn->stream_error->type = XMPP_SE_INVALID_FROM;
147
+ else if (strcmp(name, "invalid-id") == 0)
148
+ conn->stream_error->type = XMPP_SE_INVALID_ID;
149
+ else if (strcmp(name, "invalid-namespace") == 0)
150
+ conn->stream_error->type = XMPP_SE_INVALID_NS;
151
+ else if (strcmp(name, "invalid-xml") == 0)
152
+ conn->stream_error->type = XMPP_SE_INVALID_XML;
153
+ else if (strcmp(name, "not-authorized") == 0)
154
+ conn->stream_error->type = XMPP_SE_NOT_AUTHORIZED;
155
+ else if (strcmp(name, "policy-violation") == 0)
156
+ conn->stream_error->type = XMPP_SE_POLICY_VIOLATION;
157
+ else if (strcmp(name, "remote-connection-failed") == 0)
158
+ conn->stream_error->type = XMPP_SE_REMOTE_CONN_FAILED;
159
+ else if (strcmp(name, "resource-constraint") == 0)
160
+ conn->stream_error->type = XMPP_SE_RESOURCE_CONSTRAINT;
161
+ else if (strcmp(name, "restricted-xml") == 0)
162
+ conn->stream_error->type = XMPP_SE_RESTRICTED_XML;
163
+ else if (strcmp(name, "see-other-host") == 0)
164
+ conn->stream_error->type = XMPP_SE_SEE_OTHER_HOST;
165
+ else if (strcmp(name, "system-shutdown") == 0)
166
+ conn->stream_error->type = XMPP_SE_SYSTEM_SHUTDOWN;
167
+ else if (strcmp(name, "undefined-condition") == 0)
168
+ conn->stream_error->type = XMPP_SE_UNDEFINED_CONDITION;
169
+ else if (strcmp(name, "unsupported-encoding") == 0)
170
+ conn->stream_error->type = XMPP_SE_UNSUPPORTED_ENCODING;
171
+ else if (strcmp(name, "unsupported-stanza-type") == 0)
172
+ conn->stream_error->type = XMPP_SE_UNSUPPORTED_STANZA_TYPE;
173
+ else if (strcmp(name, "unsupported-version") == 0)
174
+ conn->stream_error->type = XMPP_SE_UNSUPPORTED_VERSION;
175
+ else if (strcmp(name, "xml-not-well-formed") == 0)
176
+ conn->stream_error->type = XMPP_SE_XML_NOT_WELL_FORMED;
177
+ }
178
+ } while ((child = xmpp_stanza_get_next(child)));
179
+
180
+ conn->stream_error->stanza = xmpp_stanza_clone(stanza);
181
+ }
182
+
183
+ return 1;
184
+ }
185
+
186
+ /* stream:features handlers */
187
+ static int _handle_missing_features(xmpp_conn_t * const conn,
188
+ void * const userdata)
189
+ {
190
+ xmpp_debug(conn->ctx, "xmpp", "didn't get stream features");
191
+
192
+ /* legacy auth will be attempted */
193
+ _auth(conn);
194
+
195
+ return 0;
196
+ }
197
+
198
+
199
+
200
+ static int _handle_features(xmpp_conn_t * const conn,
201
+ xmpp_stanza_t * const stanza,
202
+ void * const userdata)
203
+ {
204
+ xmpp_stanza_t *child, *mech;
205
+ char *text;
206
+
207
+ /* remove the handler that detects missing stream:features */
208
+ xmpp_timed_handler_delete(conn, _handle_missing_features);
209
+
210
+ /* check for TLS */
211
+ child = xmpp_stanza_get_child_by_name(stanza, "starttls");
212
+ if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_TLS) == 0))
213
+ conn->tls_support = 1;
214
+
215
+ /* check for SASL */
216
+ child = xmpp_stanza_get_child_by_name(stanza, "mechanisms");
217
+ if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_SASL) == 0)) {
218
+ for (mech = xmpp_stanza_get_children(child); mech;
219
+ mech = xmpp_stanza_get_next(mech)) {
220
+ if (strcmp(xmpp_stanza_get_name(mech), "mechanism") == 0) {
221
+ text = xmpp_stanza_get_text(mech);
222
+ if (strcasecmp(text, "PLAIN") == 0)
223
+ conn->sasl_support |= SASL_MASK_PLAIN;
224
+ else if (strcasecmp(text, "DIGEST-MD5") == 0)
225
+ conn->sasl_support |= SASL_MASK_DIGESTMD5;
226
+ else if (strcasecmp(text, "ANONYMOUS") == 0)
227
+ conn->sasl_support |= SASL_MASK_ANONYMOUS;
228
+
229
+ xmpp_free(conn->ctx, text);
230
+ }
231
+ }
232
+ }
233
+
234
+ if (conn->pass == NULL || conn->pass[0] == '\0') {
235
+ xmpp_debug(conn->ctx, "auth", "do not attempt auth as there is no password given");
236
+ //If no password, do not login, this will give 'register' a chance to run
237
+ conn->authenticated = 1;
238
+ /* call connection handler */
239
+ conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
240
+ } else {
241
+ _auth(conn);
242
+ }
243
+
244
+ return 0;
245
+ }
246
+
247
+ /* returns the correct auth id for a component or a client.
248
+ * returned string must be freed by caller */
249
+ static char *_get_authid(xmpp_conn_t * const conn)
250
+ {
251
+ char *authid = NULL;
252
+
253
+ if (conn->type == XMPP_CLIENT) {
254
+ /* authid is the node portion of jid */
255
+ if (!conn->jid) return NULL;
256
+ authid = xmpp_jid_node(conn->ctx, conn->jid);
257
+ }
258
+
259
+ return authid;
260
+ }
261
+
262
+ static int _handle_proceedtls_default(xmpp_conn_t * const conn,
263
+ xmpp_stanza_t * const stanza,
264
+ void * const userdata)
265
+ {
266
+ char *name;
267
+ name = xmpp_stanza_get_name(stanza);
268
+ xmpp_debug(conn->ctx, "xmpp",
269
+ "handle proceedtls called for %s", name);
270
+
271
+ if (strcmp(name, "proceed") == 0) {
272
+ xmpp_debug(conn->ctx, "xmpp", "proceeding with TLS");
273
+
274
+ conn->tls = tls_new(conn->ctx, conn->sock);
275
+
276
+ if (!tls_start(conn->tls))
277
+ {
278
+ xmpp_debug(conn->ctx, "xmpp", "Couldn't start TLS! error %d", tls_error(conn->tls));
279
+ tls_free(conn->tls);
280
+ conn->tls = NULL;
281
+ conn->tls_failed = 1;
282
+
283
+ /* failed tls spoils the connection, so disconnect */
284
+ xmpp_disconnect(conn);
285
+ }
286
+ else
287
+ {
288
+ parser_prepare_reset(conn, _handle_open_tls);
289
+
290
+ conn_open_stream(conn);
291
+ }
292
+ }
293
+
294
+ return 0;
295
+ }
296
+
297
+ static int _handle_sasl_result(xmpp_conn_t * const conn,
298
+ xmpp_stanza_t * const stanza,
299
+ void * const userdata)
300
+ {
301
+ char *name;
302
+
303
+ name = xmpp_stanza_get_name(stanza);
304
+
305
+ /* the server should send a <success> or <failure> stanza */
306
+ if (strcmp(name, "failure") == 0) {
307
+ xmpp_debug(conn->ctx, "xmpp", "SASL %s auth failed",
308
+ (char *)userdata);
309
+
310
+ /* fall back to next auth method */
311
+ _auth(conn);
312
+ } else if (strcmp(name, "success") == 0) {
313
+ /* SASL PLAIN auth successful, we need to restart the stream */
314
+ xmpp_debug(conn->ctx, "xmpp", "SASL %s auth successful",
315
+ (char *)userdata);
316
+
317
+ /* reset parser */
318
+ parser_prepare_reset(conn, _handle_open_sasl);
319
+
320
+ /* send stream tag */
321
+ conn_open_stream(conn);
322
+ } else {
323
+ /* got unexpected reply */
324
+ xmpp_error(conn->ctx, "xmpp", "Got unexpected reply to SASL %s"\
325
+ "authentication.", (char *)userdata);
326
+ xmpp_disconnect(conn);
327
+ }
328
+
329
+ return 0;
330
+ }
331
+
332
+ /* handle the challenge phase of digest auth */
333
+ static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
334
+ xmpp_stanza_t * const stanza,
335
+ void * const userdata)
336
+ {
337
+ char *text;
338
+ char *response;
339
+ xmpp_stanza_t *auth, *authdata;
340
+ char *name;
341
+
342
+ name = xmpp_stanza_get_name(stanza);
343
+ xmpp_debug(conn->ctx, "xmpp",\
344
+ "handle digest-md5 (challenge) called for %s", name);
345
+
346
+ if (strcmp(name, "challenge") == 0) {
347
+ text = xmpp_stanza_get_text(stanza);
348
+ response = sasl_digest_md5(conn->ctx, text, conn->jid, conn->pass);
349
+ if (!response) {
350
+ disconnect_mem_error(conn);
351
+ return 0;
352
+ }
353
+ xmpp_free(conn->ctx, text);
354
+
355
+ auth = xmpp_stanza_new(conn->ctx);
356
+ if (!auth) {
357
+ disconnect_mem_error(conn);
358
+ return 0;
359
+ }
360
+ xmpp_stanza_set_name(auth, "response");
361
+ xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
362
+
363
+ authdata = xmpp_stanza_new(conn->ctx);
364
+ if (!authdata) {
365
+ disconnect_mem_error(conn);
366
+ return 0;
367
+ }
368
+
369
+ xmpp_stanza_set_text(authdata, response);
370
+ xmpp_free(conn->ctx, response);
371
+
372
+ xmpp_stanza_add_child(auth, authdata);
373
+ xmpp_stanza_release(authdata);
374
+
375
+ handler_add(conn, _handle_digestmd5_rspauth,
376
+ XMPP_NS_SASL, NULL, NULL, NULL);
377
+
378
+ xmpp_send(conn, auth);
379
+ xmpp_stanza_release(auth);
380
+
381
+ } else {
382
+ return _handle_sasl_result(conn, stanza, "DIGEST-MD5");
383
+ }
384
+
385
+ /* remove ourselves */
386
+ return 0;
387
+ }
388
+
389
+ /* handle the rspauth phase of digest auth */
390
+ static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
391
+ xmpp_stanza_t * const stanza,
392
+ void * const userdata)
393
+ {
394
+ xmpp_stanza_t *auth;
395
+ char *name;
396
+
397
+ name = xmpp_stanza_get_name(stanza);
398
+ xmpp_debug(conn->ctx, "xmpp",
399
+ "handle digest-md5 (rspauth) called for %s", name);
400
+
401
+
402
+ if (strcmp(name, "challenge") == 0) {
403
+ /* assume it's an rspauth response */
404
+ auth = xmpp_stanza_new(conn->ctx);
405
+ if (!auth) {
406
+ disconnect_mem_error(conn);
407
+ return 0;
408
+ }
409
+ xmpp_stanza_set_name(auth, "response");
410
+ xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
411
+ xmpp_send(conn, auth);
412
+ xmpp_stanza_release(auth);
413
+ } else {
414
+ return _handle_sasl_result(conn, stanza, "DIGEST-MD5");
415
+ }
416
+
417
+ return 1;
418
+ }
419
+
420
+ static xmpp_stanza_t *_make_starttls(xmpp_conn_t * const conn)
421
+ {
422
+ xmpp_stanza_t *starttls;
423
+
424
+ /* build start stanza */
425
+ starttls = xmpp_stanza_new(conn->ctx);
426
+ if (starttls) {
427
+ xmpp_stanza_set_name(starttls, "starttls");
428
+ xmpp_stanza_set_ns(starttls, XMPP_NS_TLS);
429
+ }
430
+
431
+ return starttls;
432
+ }
433
+
434
+ static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t * const conn,
435
+ const char * const mechanism)
436
+ {
437
+ xmpp_stanza_t *auth;
438
+
439
+ /* build auth stanza */
440
+ auth = xmpp_stanza_new(conn->ctx);
441
+ if (auth) {
442
+ xmpp_stanza_set_name(auth, "auth");
443
+ xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
444
+ xmpp_stanza_set_attribute(auth, "mechanism", mechanism);
445
+ }
446
+
447
+ return auth;
448
+ }
449
+
450
+ /* authenticate the connection
451
+ * this may get called multiple times. if any auth method fails,
452
+ * this will get called again until one auth method succeeds or every
453
+ * method fails
454
+ */
455
+ static void _auth(xmpp_conn_t * const conn)
456
+ {
457
+ xmpp_stanza_t *auth, *authdata, *query, *child, *iq;
458
+ char *str, *authid;
459
+ int anonjid;
460
+
461
+ /* if there is no node in conn->jid, we assume anonymous connect */
462
+ str = xmpp_jid_node(conn->ctx, conn->jid);
463
+ if (str == NULL) {
464
+ anonjid = 1;
465
+ } else {
466
+ xmpp_free(conn->ctx, str);
467
+ anonjid = 0;
468
+ }
469
+
470
+ if (conn->tls_support)
471
+ {
472
+ tls_t *tls = tls_new(conn->ctx, conn->sock);
473
+
474
+ /* If we couldn't init tls, it isn't there, so go on */
475
+ if (!tls)
476
+ {
477
+ conn->tls_support = 0;
478
+ _auth(conn);
479
+ return;
480
+ }
481
+ else
482
+ {
483
+ tls_free(tls);
484
+ }
485
+
486
+ auth = _make_starttls(conn);
487
+
488
+ if (!auth) {
489
+ disconnect_mem_error(conn);
490
+ return;
491
+ }
492
+
493
+ handler_add(conn, _handle_proceedtls_default,
494
+ XMPP_NS_TLS, NULL, NULL, NULL);
495
+
496
+ xmpp_send(conn, auth);
497
+ xmpp_stanza_release(auth);
498
+
499
+ /* TLS was tried, unset flag */
500
+ conn->tls_support = 0;
501
+ } else if (anonjid && conn->sasl_support & SASL_MASK_ANONYMOUS) {
502
+ /* some crap here */
503
+ auth = _make_sasl_auth(conn, "ANONYMOUS");
504
+ if (!auth) {
505
+ disconnect_mem_error(conn);
506
+ return;
507
+ }
508
+
509
+ handler_add(conn, _handle_sasl_result, XMPP_NS_SASL,
510
+ NULL, NULL, "ANONYMOUS");
511
+
512
+ xmpp_send(conn, auth);
513
+ xmpp_stanza_release(auth);
514
+
515
+ /* SASL ANONYMOUS was tried, unset flag */
516
+ conn->sasl_support &= ~SASL_MASK_ANONYMOUS;
517
+ } else if (anonjid) {
518
+ xmpp_error(conn->ctx, "auth",
519
+ "No node in JID, and SASL ANONYMOUS unsupported.");
520
+ xmpp_disconnect(conn);
521
+ } else if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
522
+ auth = _make_sasl_auth(conn, "DIGEST-MD5");
523
+ if (!auth) {
524
+ disconnect_mem_error(conn);
525
+ return;
526
+
527
+ }
528
+
529
+ handler_add(conn, _handle_digestmd5_challenge,
530
+ XMPP_NS_SASL, NULL, NULL, NULL);
531
+
532
+ xmpp_send(conn, auth);
533
+ xmpp_stanza_release(auth);
534
+
535
+ /* SASL DIGEST-MD5 was tried, unset flag */
536
+ conn->sasl_support &= ~SASL_MASK_DIGESTMD5;
537
+ } else if (conn->sasl_support & SASL_MASK_PLAIN) {
538
+ auth = _make_sasl_auth(conn, "PLAIN");
539
+ if (!auth) {
540
+ disconnect_mem_error(conn);
541
+ return;
542
+ }
543
+ authdata = xmpp_stanza_new(conn->ctx);
544
+ if (!authdata) {
545
+ disconnect_mem_error(conn);
546
+ return;
547
+ }
548
+ authid = _get_authid(conn);
549
+ if (!authid) {
550
+ disconnect_mem_error(conn);
551
+ return;
552
+ }
553
+ str = sasl_plain(conn->ctx, authid, conn->pass);
554
+ if (!str) {
555
+ disconnect_mem_error(conn);
556
+ return;
557
+ }
558
+ xmpp_stanza_set_text(authdata, str);
559
+ xmpp_free(conn->ctx, str);
560
+
561
+ xmpp_stanza_add_child(auth, authdata);
562
+ xmpp_stanza_release(authdata);
563
+
564
+ handler_add(conn, _handle_sasl_result,
565
+ XMPP_NS_SASL, NULL, NULL, "PLAIN");
566
+
567
+ xmpp_send(conn, auth);
568
+ xmpp_stanza_release(auth);
569
+
570
+ /* SASL PLAIN was tried */
571
+ conn->sasl_support &= ~SASL_MASK_PLAIN;
572
+ } else if (conn->type == XMPP_CLIENT) {
573
+ /* legacy client authentication */
574
+
575
+ iq = xmpp_stanza_new(conn->ctx);
576
+ if (!iq) {
577
+ disconnect_mem_error(conn);
578
+ return;
579
+ }
580
+ xmpp_stanza_set_name(iq, "iq");
581
+ xmpp_stanza_set_type(iq, "set");
582
+ xmpp_stanza_set_id(iq, "_xmpp_auth1");
583
+
584
+ query = xmpp_stanza_new(conn->ctx);
585
+ if (!query) {
586
+ xmpp_stanza_release(iq);
587
+ disconnect_mem_error(conn);
588
+ return;
589
+ }
590
+ xmpp_stanza_set_name(query, "query");
591
+ xmpp_stanza_set_ns(query, XMPP_NS_AUTH);
592
+ xmpp_stanza_add_child(iq, query);
593
+ xmpp_stanza_release(query);
594
+
595
+ child = xmpp_stanza_new(conn->ctx);
596
+ if (!child) {
597
+ xmpp_stanza_release(iq);
598
+ disconnect_mem_error(conn);
599
+ return;
600
+ }
601
+ xmpp_stanza_set_name(child, "username");
602
+ xmpp_stanza_add_child(query, child);
603
+ xmpp_stanza_release(child);
604
+
605
+ authdata = xmpp_stanza_new(conn->ctx);
606
+ if (!authdata) {
607
+ xmpp_stanza_release(iq);
608
+ disconnect_mem_error(conn);
609
+ return;
610
+ }
611
+ str = xmpp_jid_node(conn->ctx, conn->jid);
612
+ xmpp_stanza_set_text(authdata, str);
613
+ xmpp_free(conn->ctx, str);
614
+ xmpp_stanza_add_child(child, authdata);
615
+ xmpp_stanza_release(authdata);
616
+
617
+ child = xmpp_stanza_new(conn->ctx);
618
+ if (!child) {
619
+ xmpp_stanza_release(iq);
620
+ disconnect_mem_error(conn);
621
+ return;
622
+ }
623
+ xmpp_stanza_set_name(child, "password");
624
+ xmpp_stanza_add_child(query, child);
625
+ xmpp_stanza_release(child);
626
+
627
+ authdata = xmpp_stanza_new(conn->ctx);
628
+ if (!authdata) {
629
+ xmpp_stanza_release(iq);
630
+ disconnect_mem_error(conn);
631
+ return;
632
+ }
633
+ xmpp_stanza_set_text(authdata, conn->pass);
634
+ xmpp_stanza_add_child(child, authdata);
635
+ xmpp_stanza_release(authdata);
636
+
637
+ child = xmpp_stanza_new(conn->ctx);
638
+ if (!child) {
639
+ xmpp_stanza_release(iq);
640
+ disconnect_mem_error(conn);
641
+ return;
642
+ }
643
+ xmpp_stanza_set_name(child, "resource");
644
+ xmpp_stanza_add_child(query, child);
645
+ xmpp_stanza_release(child);
646
+
647
+ authdata = xmpp_stanza_new(conn->ctx);
648
+ if (!authdata) {
649
+ xmpp_stanza_release(iq);
650
+ disconnect_mem_error(conn);
651
+ return;
652
+ }
653
+ str = xmpp_jid_resource(conn->ctx, conn->jid);
654
+ if (str) {
655
+ xmpp_stanza_set_text(authdata, str);
656
+ xmpp_free(conn->ctx, str);
657
+ } else {
658
+ xmpp_stanza_release(authdata);
659
+ xmpp_stanza_release(iq);
660
+ xmpp_error(conn->ctx, "auth",
661
+ "Cannot authenticate without resource");
662
+ xmpp_disconnect(conn);
663
+ return;
664
+ }
665
+ xmpp_stanza_add_child(child, authdata);
666
+ xmpp_stanza_release(authdata);
667
+
668
+ handler_add_id(conn, _handle_legacy, "_xmpp_auth1", NULL);
669
+ handler_add_timed(conn, _handle_missing_legacy,
670
+ LEGACY_TIMEOUT, NULL);
671
+
672
+ xmpp_send(conn, iq);
673
+ xmpp_stanza_release(iq);
674
+ }
675
+ }
676
+
677
+
678
+ /** Set up handlers at stream start.
679
+ * This function is called internally to Strophe for handling the opening
680
+ * of an XMPP stream. It's called by the parser when a stream is opened
681
+ * or reset, and adds the initial handlers for <stream:error/> and
682
+ * <stream:features/>. This function is not intended for use outside
683
+ * of Strophe.
684
+ *
685
+ * @param conn a Strophe connection object
686
+ */
687
+ void auth_handle_open(xmpp_conn_t * const conn)
688
+ {
689
+ /* reset all timed handlers */
690
+ handler_reset_timed(conn, 0);
691
+
692
+ /* setup handler for stream:error */
693
+ handler_add(conn, _handle_error,
694
+ NULL, "stream:error", NULL, NULL);
695
+
696
+ /* setup handlers for incoming <stream:features> */
697
+ handler_add(conn, _handle_features,
698
+ NULL, "stream:features", NULL, NULL);
699
+ handler_add_timed(conn, _handle_missing_features,
700
+ FEATURES_TIMEOUT, NULL);
701
+ }
702
+
703
+ /* called when stream:stream tag received after TLS connection */
704
+ static void _handle_open_tls(xmpp_conn_t * const conn)
705
+ {
706
+ xmpp_debug(conn->ctx, "xmpp", "TLS successful, proceeding with SASL");
707
+
708
+ /* go straight to SASL auth */
709
+ _auth(conn);
710
+ }
711
+
712
+
713
+ /* called when stream:stream tag received after SASL auth */
714
+ static void _handle_open_sasl(xmpp_conn_t * const conn)
715
+ {
716
+ xmpp_debug(conn->ctx, "xmpp", "Reopened stream successfully.");
717
+
718
+ /* setup stream:features handlers */
719
+ handler_add(conn, _handle_features_sasl,
720
+ NULL, "stream:features", NULL, NULL);
721
+ handler_add_timed(conn, _handle_missing_features_sasl,
722
+ FEATURES_TIMEOUT, NULL);
723
+ }
724
+
725
+ static int _handle_features_sasl(xmpp_conn_t * const conn,
726
+ xmpp_stanza_t * const stanza,
727
+ void * const userdata)
728
+ {
729
+ xmpp_stanza_t *bind, *session, *iq, *res, *text;
730
+ char *resource;
731
+
732
+ /* remove missing features handler */
733
+ xmpp_timed_handler_delete(conn, _handle_missing_features_sasl);
734
+
735
+ /* we are expecting <bind/> and <session/> since this is a
736
+ XMPP style connection */
737
+
738
+ bind = xmpp_stanza_get_child_by_name(stanza, "bind");
739
+ if (bind && strcmp(xmpp_stanza_get_ns(bind), XMPP_NS_BIND) == 0) {
740
+ /* resource binding is required */
741
+ conn->bind_required = 1;
742
+ }
743
+
744
+ session = xmpp_stanza_get_child_by_name(stanza, "session");
745
+ if (session && strcmp(xmpp_stanza_get_ns(session), XMPP_NS_SESSION) == 0) {
746
+ /* session establishment required */
747
+ conn->session_required = 1;
748
+ }
749
+
750
+ /* if bind is required, go ahead and start it */
751
+ if (conn->bind_required) {
752
+ /* bind resource */
753
+
754
+ /* setup response handlers */
755
+ handler_add_id(conn, _handle_bind, "_xmpp_bind1", NULL);
756
+ handler_add_timed(conn, _handle_missing_bind,
757
+ BIND_TIMEOUT, NULL);
758
+
759
+ /* send bind request */
760
+ iq = xmpp_stanza_new(conn->ctx);
761
+ if (!iq) {
762
+ disconnect_mem_error(conn);
763
+ return 0;
764
+ }
765
+
766
+ xmpp_stanza_set_name(iq, "iq");
767
+ xmpp_stanza_set_type(iq, "set");
768
+ xmpp_stanza_set_id(iq, "_xmpp_bind1");
769
+
770
+ bind = xmpp_stanza_copy(bind);
771
+ if (!bind) {
772
+ xmpp_stanza_release(iq);
773
+ disconnect_mem_error(conn);
774
+ return 0;
775
+ }
776
+
777
+ /* request a specific resource if we have one */
778
+ resource = xmpp_jid_resource(conn->ctx, conn->jid);
779
+ if ((resource != NULL) && (strlen(resource) == 0)) {
780
+ /* jabberd2 doesn't handle an empty resource */
781
+ xmpp_free(conn->ctx, resource);
782
+ resource = NULL;
783
+ }
784
+
785
+ /* if we have a resource to request, do it. otherwise the
786
+ server will assign us one */
787
+ if (resource) {
788
+ res = xmpp_stanza_new(conn->ctx);
789
+ if (!res) {
790
+ xmpp_stanza_release(bind);
791
+ xmpp_stanza_release(iq);
792
+ disconnect_mem_error(conn);
793
+ return 0;
794
+ }
795
+ xmpp_stanza_set_name(res, "resource");
796
+ text = xmpp_stanza_new(conn->ctx);
797
+ if (!text) {
798
+ xmpp_stanza_release(res);
799
+ xmpp_stanza_release(bind);
800
+ xmpp_stanza_release(iq);
801
+ disconnect_mem_error(conn);
802
+ return 0;
803
+ }
804
+ xmpp_stanza_set_text(text, resource);
805
+ xmpp_stanza_add_child(res, text);
806
+ xmpp_stanza_add_child(bind, res);
807
+ xmpp_free(conn->ctx, resource);
808
+ }
809
+
810
+ xmpp_stanza_add_child(iq, bind);
811
+ xmpp_stanza_release(bind);
812
+
813
+ /* send bind request */
814
+ xmpp_send(conn, iq);
815
+ xmpp_stanza_release(iq);
816
+ } else {
817
+ /* can't bind, disconnect */
818
+ xmpp_error(conn->ctx, "xmpp", "Stream features does not allow "\
819
+ "resource bind.");
820
+ xmpp_disconnect(conn);
821
+ }
822
+
823
+ return 0;
824
+ }
825
+
826
+ static int _handle_missing_features_sasl(xmpp_conn_t * const conn,
827
+ void * const userdata)
828
+ {
829
+ xmpp_error(conn->ctx, "xmpp", "Did not receive stream features "\
830
+ "after SASL authentication.");
831
+ xmpp_disconnect(conn);
832
+ return 0;
833
+ }
834
+
835
+ static int _handle_bind(xmpp_conn_t * const conn,
836
+ xmpp_stanza_t * const stanza,
837
+ void * const userdata)
838
+ {
839
+ char *type;
840
+ xmpp_stanza_t *iq, *session;
841
+
842
+ /* delete missing bind handler */
843
+ xmpp_timed_handler_delete(conn, _handle_missing_bind);
844
+
845
+ /* server has replied to bind request */
846
+ type = xmpp_stanza_get_type(stanza);
847
+ if (type && strcmp(type, "error") == 0) {
848
+ xmpp_error(conn->ctx, "xmpp", "Binding failed.");
849
+ xmpp_disconnect(conn);
850
+ } else if (type && strcmp(type, "result") == 0) {
851
+ /* TODO: extract resource if present */
852
+ xmpp_debug(conn->ctx, "xmpp", "Bind successful.");
853
+
854
+ /* establish a session if required */
855
+ if (conn->session_required) {
856
+ /* setup response handlers */
857
+ handler_add_id(conn, _handle_session, "_xmpp_session1", NULL);
858
+ handler_add_timed(conn, _handle_missing_session,
859
+ SESSION_TIMEOUT, NULL);
860
+
861
+ /* send session request */
862
+ iq = xmpp_stanza_new(conn->ctx);
863
+ if (!iq) {
864
+ disconnect_mem_error(conn);
865
+ return 0;
866
+ }
867
+
868
+ xmpp_stanza_set_name(iq, "iq");
869
+ xmpp_stanza_set_type(iq, "set");
870
+ xmpp_stanza_set_id(iq, "_xmpp_session1");
871
+
872
+ session = xmpp_stanza_new(conn->ctx);
873
+ if (!session) {
874
+ xmpp_stanza_release(iq);
875
+ disconnect_mem_error(conn);
876
+ }
877
+
878
+ xmpp_stanza_set_name(session, "session");
879
+ xmpp_stanza_set_ns(session, XMPP_NS_SESSION);
880
+
881
+ xmpp_stanza_add_child(iq, session);
882
+ xmpp_stanza_release(session);
883
+
884
+ /* send session establishment request */
885
+ xmpp_send(conn, iq);
886
+ xmpp_stanza_release(iq);
887
+ } else {
888
+ conn->authenticated = 1;
889
+
890
+ /* call connection handler */
891
+ conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL,
892
+ conn->userdata);
893
+ }
894
+ } else {
895
+ xmpp_error(conn->ctx, "xmpp", "Server sent malformed bind reply.");
896
+ xmpp_disconnect(conn);
897
+ }
898
+
899
+ return 0;
900
+ }
901
+
902
+ static int _handle_missing_bind(xmpp_conn_t * const conn,
903
+ void * const userdata)
904
+ {
905
+ xmpp_error(conn->ctx, "xmpp", "Server did not reply to bind request.");
906
+ xmpp_disconnect(conn);
907
+ return 0;
908
+ }
909
+
910
+ static int _handle_session(xmpp_conn_t * const conn,
911
+ xmpp_stanza_t * const stanza,
912
+ void * const userdata)
913
+ {
914
+ char *type;
915
+
916
+ /* delete missing session handler */
917
+ xmpp_timed_handler_delete(conn, _handle_missing_session);
918
+
919
+ /* server has replied to the session request */
920
+ type = xmpp_stanza_get_type(stanza);
921
+ if (type && strcmp(type, "error") == 0) {
922
+ xmpp_error(conn->ctx, "xmpp", "Session establishment failed.");
923
+ xmpp_disconnect(conn);
924
+ } else if (type && strcmp(type, "result") == 0) {
925
+ xmpp_debug(conn->ctx, "xmpp", "Session establishment successful.");
926
+
927
+ conn->authenticated = 1;
928
+
929
+ /* call connection handler */
930
+ conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
931
+ } else {
932
+ xmpp_error(conn->ctx, "xmpp", "Server sent malformed session reply.");
933
+ xmpp_disconnect(conn);
934
+ }
935
+
936
+ return 0;
937
+ }
938
+
939
+ static int _handle_missing_session(xmpp_conn_t * const conn,
940
+ void * const userdata)
941
+ {
942
+ xmpp_error(conn->ctx, "xmpp", "Server did not reply to session request.");
943
+ xmpp_disconnect(conn);
944
+ return 0;
945
+ }
946
+
947
+ static int _handle_legacy(xmpp_conn_t * const conn,
948
+ xmpp_stanza_t * const stanza,
949
+ void * const userdata)
950
+ {
951
+ char *type, *name;
952
+
953
+ /* delete missing handler */
954
+ xmpp_timed_handler_delete(conn, _handle_missing_legacy);
955
+
956
+ /* server responded to legacy auth request */
957
+ type = xmpp_stanza_get_type(stanza);
958
+ name = xmpp_stanza_get_name(stanza);
959
+ if (!type || strcmp(name, "iq") != 0) {
960
+ xmpp_error(conn->ctx, "xmpp", "Server sent us an unexpected response "\
961
+ "to legacy authentication request.");
962
+ xmpp_disconnect(conn);
963
+ } else if (strcmp(type, "error") == 0) {
964
+ /* legacy client auth failed, no more fallbacks */
965
+ xmpp_error(conn->ctx, "xmpp", "Legacy client authentication failed.");
966
+ xmpp_disconnect(conn);
967
+ } else if (strcmp(type, "result") == 0) {
968
+ /* auth succeeded */
969
+ xmpp_debug(conn->ctx, "xmpp", "Legacy auth succeeded.");
970
+
971
+ conn->authenticated = 1;
972
+ conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
973
+ } else {
974
+ xmpp_error(conn->ctx, "xmpp", "Server sent us a legacy authentication "\
975
+ "response with a bad type.");
976
+ xmpp_disconnect(conn);
977
+ }
978
+
979
+ return 0;
980
+ }
981
+
982
+ static int _handle_missing_legacy(xmpp_conn_t * const conn,
983
+ void * const userdata)
984
+ {
985
+ xmpp_error(conn->ctx, "xmpp", "Server did not reply to legacy "\
986
+ "authentication request.");
987
+ xmpp_disconnect(conn);
988
+ return 0;
989
+ }
990
+