Mercurial > pidgin
annotate src/protocols/jabber/auth.c @ 13967:99b9b58b19dd
[gaim-migrate @ 16523]
Fix a crazy MSN crash. Basically it's possible to have more than one
slplink associated with a given switchboard, but our code did not
allow for that. I think it happens when you're in a multi-user
chat and you do stuff with multiple users that involves slplinks.
Like maybe file transfer and buddy icon related stuff.
Tracking this down took an ungodly amount of time, but thanks to
Meebo for letting me do it :-)
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Thu, 20 Jul 2006 07:31:15 +0000 |
parents | 25e63008d3bb |
children | b1ce2e9e494a |
rev | line source |
---|---|
7014 | 1 /* |
2 * gaim - Jabber Protocol Plugin | |
3 * | |
4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> | |
5 * | |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 * | |
20 */ | |
21 #include "internal.h" | |
22 | |
23 #include "jutil.h" | |
24 #include "auth.h" | |
25 #include "xmlnode.h" | |
26 #include "jabber.h" | |
27 #include "iq.h" | |
28 | |
29 #include "debug.h" | |
30 #include "util.h" | |
10684
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10496
diff
changeset
|
31 #include "cipher.h" |
7014 | 32 #include "sslconn.h" |
8397 | 33 #include "request.h" |
34 | |
35 static void auth_old_result_cb(JabberStream *js, xmlnode *packet, | |
36 gpointer data); | |
7014 | 37 |
8296 | 38 gboolean |
39 jabber_process_starttls(JabberStream *js, xmlnode *packet) | |
7014 | 40 { |
41 xmlnode *starttls; | |
42 | |
7157 | 43 if((starttls = xmlnode_get_child(packet, "starttls"))) { |
7630 | 44 if(gaim_account_get_bool(js->gc->account, "use_tls", TRUE) && |
45 gaim_ssl_is_supported()) { | |
7157 | 46 jabber_send_raw(js, |
7642 | 47 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>", -1); |
8296 | 48 return TRUE; |
7157 | 49 } else if(xmlnode_get_child(starttls, "required")) { |
10441 | 50 if(gaim_ssl_is_supported()) { |
10496 | 51 gaim_connection_error(js->gc, _("Server requires TLS/SSL for login. Select \"Use TLS if available\" in account properties")); |
10441 | 52 } else { |
53 gaim_connection_error(js->gc, _("Server requires TLS/SSL for login. No TLS/SSL support found.")); | |
54 } | |
8296 | 55 return TRUE; |
7157 | 56 } |
7014 | 57 } |
58 | |
8296 | 59 return FALSE; |
60 } | |
61 | |
8397 | 62 static void finish_plaintext_authentication(JabberStream *js) |
63 { | |
64 if(js->auth_type == JABBER_AUTH_PLAIN) { | |
65 xmlnode *auth; | |
66 GString *response; | |
11127 | 67 gchar *enc_out; |
8397 | 68 |
69 auth = xmlnode_new("auth"); | |
13806 | 70 xmlnode_set_namespace(auth, "urn:ietf:params:xml:ns:xmpp-sasl"); |
8397 | 71 |
72 response = g_string_new(""); | |
73 response = g_string_append_len(response, "\0", 1); | |
74 response = g_string_append(response, js->user->node); | |
75 response = g_string_append_len(response, "\0", 1); | |
76 response = g_string_append(response, | |
10740 | 77 gaim_connection_get_password(js->gc)); |
8397 | 78 |
11137 | 79 enc_out = gaim_base64_encode((guchar *)response->str, response->len); |
8397 | 80 |
81 xmlnode_set_attrib(auth, "mechanism", "PLAIN"); | |
82 xmlnode_insert_data(auth, enc_out, -1); | |
83 g_free(enc_out); | |
84 g_string_free(response, TRUE); | |
85 | |
86 jabber_send(js, auth); | |
87 xmlnode_free(auth); | |
88 } else if(js->auth_type == JABBER_AUTH_IQ_AUTH) { | |
89 JabberIq *iq; | |
90 xmlnode *query, *x; | |
91 | |
92 iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); | |
93 query = xmlnode_get_child(iq->node, "query"); | |
94 x = xmlnode_new_child(query, "username"); | |
95 xmlnode_insert_data(x, js->user->node, -1); | |
96 x = xmlnode_new_child(query, "resource"); | |
97 xmlnode_insert_data(x, js->user->resource, -1); | |
98 x = xmlnode_new_child(query, "password"); | |
10740 | 99 xmlnode_insert_data(x, gaim_connection_get_password(js->gc), -1); |
8397 | 100 jabber_iq_set_callback(iq, auth_old_result_cb, NULL); |
101 jabber_iq_send(iq); | |
102 } | |
103 } | |
104 | |
105 static void allow_plaintext_auth(GaimAccount *account) | |
106 { | |
107 gaim_account_set_bool(account, "auth_plain_in_clear", TRUE); | |
108 | |
109 finish_plaintext_authentication(account->gc->proto_data); | |
110 } | |
111 | |
112 static void disallow_plaintext_auth(GaimAccount *account) | |
113 { | |
114 gaim_connection_error(account->gc, _("Server requires plaintext authentication over an unencrypted stream")); | |
115 } | |
116 | |
12508 | 117 #ifdef HAVE_CYRUS_SASL |
118 | |
119 static void jabber_auth_start_cyrus(JabberStream *); | |
120 | |
121 /* Callbacks for Cyrus SASL */ | |
122 | |
123 static int jabber_sasl_cb_realm(void *ctx, int id, const char **avail, const char **result) | |
124 { | |
125 JabberStream *js = (JabberStream *)ctx; | |
126 | |
127 if (id != SASL_CB_GETREALM || !result) return SASL_BADPARAM; | |
128 | |
129 *result = js->user->domain; | |
130 | |
131 return SASL_OK; | |
132 } | |
133 | |
134 static int jabber_sasl_cb_simple(void *ctx, int id, const char **res, unsigned *len) | |
135 { | |
136 JabberStream *js = (JabberStream *)ctx; | |
137 | |
138 switch(id) { | |
139 case SASL_CB_AUTHNAME: | |
140 *res = js->user->node; | |
141 break; | |
142 case SASL_CB_USER: | |
12543 | 143 *res = ""; |
12508 | 144 break; |
145 default: | |
146 return SASL_BADPARAM; | |
147 } | |
148 if (len) *len = strlen((char *)*res); | |
149 return SASL_OK; | |
150 } | |
151 | |
152 static int jabber_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secret_t **secret) | |
153 { | |
154 JabberStream *js = (JabberStream *)ctx; | |
155 const char *pw = gaim_account_get_password(js->gc->account); | |
156 size_t len; | |
157 static sasl_secret_t *x = NULL; | |
158 | |
159 if (!conn || !secret || id != SASL_CB_PASS) | |
160 return SASL_BADPARAM; | |
161 | |
162 len = strlen(pw); | |
163 x = (sasl_secret_t *) realloc(x, sizeof(sasl_secret_t) + len); | |
164 | |
165 if (!x) | |
166 return SASL_NOMEM; | |
167 | |
168 x->len = len; | |
169 strcpy((char*)x->data, pw); | |
170 | |
171 *secret = x; | |
172 return SASL_OK; | |
173 } | |
174 | |
175 static void allow_cyrus_plaintext_auth(GaimAccount *account) | |
176 { | |
177 gaim_account_set_bool(account, "auth_plain_in_clear", TRUE); | |
178 | |
179 jabber_auth_start_cyrus(account->gc->proto_data); | |
180 } | |
181 | |
182 static void jabber_auth_start_cyrus(JabberStream *js) | |
183 { | |
184 const char *clientout, *mech; | |
185 char *enc_out; | |
186 unsigned coutlen; | |
187 xmlnode *auth; | |
188 sasl_security_properties_t secprops; | |
189 gboolean again; | |
190 gboolean plaintext = TRUE; | |
191 | |
192 /* Set up security properties and options */ | |
193 secprops.min_ssf = 0; | |
194 secprops.security_flags = SASL_SEC_NOANONYMOUS; | |
195 | |
196 if (!js->gsc) { | |
13206 | 197 secprops.max_ssf = -1; |
198 secprops.maxbufsize = 4096; | |
12508 | 199 plaintext = gaim_account_get_bool(js->gc->account, "auth_plain_in_clear", FALSE); |
200 if (!plaintext) | |
201 secprops.security_flags |= SASL_SEC_NOPLAINTEXT; | |
202 } else { | |
13206 | 203 secprops.max_ssf = 0; |
204 secprops.maxbufsize = 0; | |
12540 | 205 plaintext = TRUE; |
12508 | 206 } |
207 secprops.property_names = 0; | |
208 secprops.property_values = 0; | |
209 | |
210 do { | |
211 again = FALSE; | |
212 /* Use the user's domain for compatibility with the old | |
213 * DIGESTMD5 code. Note that this may cause problems where | |
214 * the user's domain doesn't match the FQDN of the jabber | |
215 * service | |
216 */ | |
217 | |
218 js->sasl_state = sasl_client_new("xmpp", js->user->domain, NULL, NULL, js->sasl_cb, 0, &js->sasl); | |
219 if (js->sasl_state==SASL_OK) { | |
220 sasl_setprop(js->sasl, SASL_SEC_PROPS, &secprops); | |
12543 | 221 gaim_debug_info("sasl", "Mechs found: %s\n", js->sasl_mechs->str); |
12508 | 222 js->sasl_state = sasl_client_start(js->sasl, js->sasl_mechs->str, NULL, &clientout, &coutlen, &mech); |
223 } | |
224 switch (js->sasl_state) { | |
225 /* Success */ | |
12543 | 226 case SASL_OK: |
12508 | 227 case SASL_CONTINUE: |
228 break; | |
229 case SASL_NOMECH: | |
230 /* No mechanisms do what we want. See if we can add | |
231 * plaintext ones to the list. */ | |
232 | |
233 if (!gaim_account_get_password(js->gc->account)) { | |
234 gaim_connection_error(js->gc, _("Server couldn't authenticate you without a password")); | |
235 return; | |
236 } else if (!plaintext) { | |
237 gaim_request_yes_no(js->gc, _("Plaintext Authentication"), | |
238 _("Plaintext Authentication"), | |
239 _("This server requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), | |
240 2, js->gc->account, | |
241 allow_cyrus_plaintext_auth, | |
242 disallow_plaintext_auth); | |
243 return; | |
244 } else { | |
245 gaim_connection_error(js->gc, _("Server does not use any supported authentication method")); | |
246 return; | |
247 } | |
248 /* not reached */ | |
249 break; | |
250 | |
251 /* Fatal errors. Give up and go home */ | |
252 case SASL_BADPARAM: | |
253 case SASL_NOMEM: | |
254 break; | |
255 | |
256 /* For everything else, fail the mechanism and try again */ | |
257 default: | |
12543 | 258 gaim_debug_info("sasl", "sasl_state is %d, failing the mech and trying again\n", js->sasl_state); |
12508 | 259 if (strlen(mech)>0) { |
260 char *pos; | |
261 pos = strstr(js->sasl_mechs->str,mech); | |
262 g_assert(pos!=NULL); | |
263 g_string_erase(js->sasl_mechs, pos-js->sasl_mechs->str,strlen(mech)); | |
264 } | |
265 sasl_dispose(&js->sasl); | |
266 again=TRUE; | |
267 } | |
268 } while (again); | |
269 | |
12543 | 270 if (js->sasl_state == SASL_CONTINUE || js->sasl_state == SASL_OK) { |
12508 | 271 auth = xmlnode_new("auth"); |
13806 | 272 xmlnode_set_namespace(auth, "urn:ietf:params:xml:ns:xmpp-sasl"); |
12508 | 273 xmlnode_set_attrib(auth,"mechanism", mech); |
274 if (clientout) { | |
275 if (coutlen == 0) { | |
276 xmlnode_insert_data(auth, "=", -1); | |
277 } else { | |
278 enc_out = gaim_base64_encode((unsigned char*)clientout, coutlen); | |
279 xmlnode_insert_data(auth, enc_out, -1); | |
280 g_free(enc_out); | |
281 } | |
282 } | |
283 jabber_send(js, auth); | |
284 xmlnode_free(auth); | |
285 } else { | |
286 gaim_connection_error(js->gc, "SASL authentication failed\n"); | |
287 } | |
288 } | |
289 | |
12543 | 290 static int |
291 jabber_sasl_cb_log(void *context, int level, const char *message) | |
292 { | |
293 if(level <= SASL_LOG_TRACE) | |
294 gaim_debug_info("sasl", "%s\n", message); | |
295 | |
296 return SASL_OK; | |
297 } | |
298 | |
12508 | 299 #endif |
300 | |
8296 | 301 void |
302 jabber_auth_start(JabberStream *js, xmlnode *packet) | |
303 { | |
12508 | 304 #ifdef HAVE_CYRUS_SASL |
305 int id; | |
306 #else | |
307 gboolean digest_md5 = FALSE, plain=FALSE; | |
308 #endif | |
8296 | 309 |
12508 | 310 xmlnode *mechs, *mechnode; |
8296 | 311 |
312 | |
8016 | 313 if(js->registration) { |
314 jabber_register_start(js); | |
315 return; | |
316 } | |
317 | |
7014 | 318 mechs = xmlnode_get_child(packet, "mechanisms"); |
319 | |
320 if(!mechs) { | |
7981 | 321 gaim_connection_error(js->gc, _("Invalid response from server.")); |
7014 | 322 return; |
323 } | |
324 | |
12508 | 325 #ifdef HAVE_CYRUS_SASL |
326 js->sasl_mechs = g_string_new(""); | |
327 #endif | |
328 | |
8135 | 329 for(mechnode = xmlnode_get_child(mechs, "mechanism"); mechnode; |
330 mechnode = xmlnode_get_next_twin(mechnode)) | |
7014 | 331 { |
8135 | 332 char *mech_name = xmlnode_get_data(mechnode); |
12508 | 333 #ifdef HAVE_CYRUS_SASL |
334 g_string_append(js->sasl_mechs, mech_name); | |
335 g_string_append_c(js->sasl_mechs,' '); | |
336 #else | |
8135 | 337 if(mech_name && !strcmp(mech_name, "DIGEST-MD5")) |
338 digest_md5 = TRUE; | |
339 else if(mech_name && !strcmp(mech_name, "PLAIN")) | |
340 plain = TRUE; | |
12508 | 341 #endif |
8135 | 342 g_free(mech_name); |
7014 | 343 } |
344 | |
12508 | 345 #ifdef HAVE_CYRUS_SASL |
346 js->auth_type = JABBER_AUTH_CYRUS; | |
347 | |
348 /* Set up our callbacks structure */ | |
12543 | 349 js->sasl_cb = g_new0(sasl_callback_t,6); |
12508 | 350 |
351 id = 0; | |
352 js->sasl_cb[id].id = SASL_CB_GETREALM; | |
353 js->sasl_cb[id].proc = jabber_sasl_cb_realm; | |
354 js->sasl_cb[id].context = (void *)js; | |
355 id++; | |
356 | |
357 js->sasl_cb[id].id = SASL_CB_AUTHNAME; | |
358 js->sasl_cb[id].proc = jabber_sasl_cb_simple; | |
359 js->sasl_cb[id].context = (void *)js; | |
360 id++; | |
361 | |
362 js->sasl_cb[id].id = SASL_CB_USER; | |
363 js->sasl_cb[id].proc = jabber_sasl_cb_simple; | |
364 js->sasl_cb[id].context = (void *)js; | |
365 id++; | |
366 | |
367 if (gaim_account_get_password(js->gc->account)) { | |
368 js->sasl_cb[id].id = SASL_CB_PASS; | |
369 js->sasl_cb[id].proc = jabber_sasl_cb_secret; | |
370 js->sasl_cb[id].context = (void *)js; | |
371 id++; | |
372 } | |
373 | |
12543 | 374 js->sasl_cb[id].id = SASL_CB_LOG; |
375 js->sasl_cb[id].proc = jabber_sasl_cb_log; | |
376 js->sasl_cb[id].context = (void*)js; | |
377 id++; | |
378 | |
12508 | 379 js->sasl_cb[id].id = SASL_CB_LIST_END; |
380 | |
381 jabber_auth_start_cyrus(js); | |
382 #else | |
7703 | 383 |
7645 | 384 if(digest_md5) { |
8397 | 385 xmlnode *auth; |
386 | |
387 js->auth_type = JABBER_AUTH_DIGEST_MD5; | |
388 auth = xmlnode_new("auth"); | |
13806 | 389 xmlnode_set_namespace(auth, "urn:ietf:params:xml:ns:xmpp-sasl"); |
7291 | 390 xmlnode_set_attrib(auth, "mechanism", "DIGEST-MD5"); |
8397 | 391 |
392 jabber_send(js, auth); | |
393 xmlnode_free(auth); | |
8086 | 394 } else if(plain) { |
8397 | 395 js->auth_type = JABBER_AUTH_PLAIN; |
7703 | 396 |
8086 | 397 if(js->gsc == NULL && !gaim_account_get_bool(js->gc->account, "auth_plain_in_clear", FALSE)) { |
8397 | 398 gaim_request_yes_no(js->gc, _("Plaintext Authentication"), |
399 _("Plaintext Authentication"), | |
400 _("This server requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), | |
401 2, js->gc->account, allow_plaintext_auth, | |
402 disallow_plaintext_auth); | |
8086 | 403 return; |
404 } | |
8397 | 405 finish_plaintext_authentication(js); |
7014 | 406 } else { |
407 gaim_connection_error(js->gc, | |
408 _("Server does not use any supported authentication method")); | |
409 } | |
12508 | 410 #endif |
7014 | 411 } |
412 | |
7395 | 413 static void auth_old_result_cb(JabberStream *js, xmlnode *packet, gpointer data) |
7014 | 414 { |
415 const char *type = xmlnode_get_attrib(packet, "type"); | |
416 | |
7730 | 417 if(type && !strcmp(type, "result")) { |
418 jabber_stream_set_state(js, JABBER_STREAM_CONNECTED); | |
419 } else { | |
8401 | 420 char *msg = jabber_parse_error(js, packet); |
421 xmlnode *error; | |
422 const char *err_code; | |
7014 | 423 |
8401 | 424 if((error = xmlnode_get_child(packet, "error")) && |
425 (err_code = xmlnode_get_attrib(error, "code")) && | |
426 !strcmp(err_code, "401")) { | |
427 js->gc->wants_to_die = TRUE; | |
7730 | 428 } |
7014 | 429 |
8401 | 430 gaim_connection_error(js->gc, msg); |
431 g_free(msg); | |
7014 | 432 } |
433 } | |
434 | |
7395 | 435 static void auth_old_cb(JabberStream *js, xmlnode *packet, gpointer data) |
7014 | 436 { |
437 JabberIq *iq; | |
438 xmlnode *query, *x; | |
7514 | 439 const char *type = xmlnode_get_attrib(packet, "type"); |
10740 | 440 const char *pw = gaim_connection_get_password(js->gc); |
7014 | 441 |
7514 | 442 if(!type) { |
7981 | 443 gaim_connection_error(js->gc, _("Invalid response from server.")); |
7014 | 444 return; |
7515 | 445 } else if(!strcmp(type, "error")) { |
8401 | 446 char *msg = jabber_parse_error(js, packet); |
447 gaim_connection_error(js->gc, msg); | |
448 g_free(msg); | |
7515 | 449 } else if(!strcmp(type, "result")) { |
7514 | 450 query = xmlnode_get_child(packet, "query"); |
451 if(js->stream_id && xmlnode_get_child(query, "digest")) { | |
452 unsigned char hashval[20]; | |
453 char *s, h[41], *p; | |
454 int i; | |
7014 | 455 |
8397 | 456 iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); |
457 query = xmlnode_get_child(iq->node, "query"); | |
458 x = xmlnode_new_child(query, "username"); | |
459 xmlnode_insert_data(x, js->user->node, -1); | |
460 x = xmlnode_new_child(query, "resource"); | |
461 xmlnode_insert_data(x, js->user->resource, -1); | |
462 | |
7514 | 463 x = xmlnode_new_child(query, "digest"); |
464 s = g_strdup_printf("%s%s", js->stream_id, pw); | |
10684
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10496
diff
changeset
|
465 |
11183 | 466 gaim_cipher_digest_region("sha1", (guchar *)s, strlen(s), |
10687
b256ce6b85b8
[gaim-migrate @ 12235]
Etan Reisner <pidgin@unreliablesource.net>
parents:
10684
diff
changeset
|
467 sizeof(hashval), hashval, NULL); |
10684
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10496
diff
changeset
|
468 |
7514 | 469 p = h; |
470 for(i=0; i<20; i++, p+=2) | |
471 snprintf(p, 3, "%02x", hashval[i]); | |
472 xmlnode_insert_data(x, h, -1); | |
473 g_free(s); | |
8397 | 474 jabber_iq_set_callback(iq, auth_old_result_cb, NULL); |
475 jabber_iq_send(iq); | |
476 | |
477 } else if(xmlnode_get_child(query, "password")) { | |
478 if(js->gsc == NULL && !gaim_account_get_bool(js->gc->account, | |
479 "auth_plain_in_clear", FALSE)) { | |
480 gaim_request_yes_no(js->gc, _("Plaintext Authentication"), | |
481 _("Plaintext Authentication"), | |
482 _("This server requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), | |
483 2, js->gc->account, allow_plaintext_auth, | |
484 disallow_plaintext_auth); | |
485 return; | |
486 } | |
487 finish_plaintext_authentication(js); | |
7514 | 488 } else { |
8397 | 489 gaim_connection_error(js->gc, |
490 _("Server does not use any supported authentication method")); | |
491 return; | |
7514 | 492 } |
7014 | 493 } |
494 } | |
495 | |
496 void jabber_auth_start_old(JabberStream *js) | |
497 { | |
498 JabberIq *iq; | |
499 xmlnode *query, *username; | |
500 | |
501 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:auth"); | |
502 | |
503 query = xmlnode_get_child(iq->node, "query"); | |
504 username = xmlnode_new_child(query, "username"); | |
505 xmlnode_insert_data(username, js->user->node, -1); | |
506 | |
7395 | 507 jabber_iq_set_callback(iq, auth_old_cb, NULL); |
7014 | 508 |
509 jabber_iq_send(iq); | |
510 } | |
511 | |
512 static GHashTable* parse_challenge(const char *challenge) | |
513 { | |
514 GHashTable *ret = g_hash_table_new_full(g_str_hash, g_str_equal, | |
515 g_free, g_free); | |
516 char **pairs; | |
517 int i; | |
518 | |
519 pairs = g_strsplit(challenge, ",", -1); | |
520 | |
521 for(i=0; pairs[i]; i++) { | |
522 char **keyval = g_strsplit(pairs[i], "=", 2); | |
523 if(keyval[0] && keyval[1]) { | |
524 if(keyval[1][0] == '"' && keyval[1][strlen(keyval[1])-1] == '"') | |
525 g_hash_table_replace(ret, g_strdup(keyval[0]), g_strndup(keyval[1]+1, strlen(keyval[1])-2)); | |
526 else | |
527 g_hash_table_replace(ret, g_strdup(keyval[0]), g_strdup(keyval[1])); | |
528 } | |
529 g_strfreev(keyval); | |
530 } | |
531 | |
532 g_strfreev(pairs); | |
533 | |
534 return ret; | |
535 } | |
536 | |
11163 | 537 static char * |
7014 | 538 generate_response_value(JabberID *jid, const char *passwd, const char *nonce, |
7267 | 539 const char *cnonce, const char *a2, const char *realm) |
7014 | 540 { |
10684
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10496
diff
changeset
|
541 GaimCipher *cipher; |
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10496
diff
changeset
|
542 GaimCipherContext *context; |
11137 | 543 guchar result[16]; |
10136 | 544 size_t a1len; |
7014 | 545 |
12549 | 546 gchar *a1, *convnode=NULL, *convpasswd = NULL, *ha1, *ha2, *kd, *x, *z; |
7014 | 547 |
10136 | 548 if((convnode = g_convert(jid->node, strlen(jid->node), "iso-8859-1", "utf-8", |
549 NULL, NULL, NULL)) == NULL) { | |
550 convnode = g_strdup(jid->node); | |
551 } | |
12549 | 552 if(passwd && ((convpasswd = g_convert(passwd, strlen(passwd), "iso-8859-1", |
553 "utf-8", NULL, NULL, NULL)) == NULL)) { | |
10136 | 554 convpasswd = g_strdup(passwd); |
555 } | |
556 | |
10684
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10496
diff
changeset
|
557 cipher = gaim_ciphers_find_cipher("md5"); |
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10496
diff
changeset
|
558 context = gaim_cipher_context_new(cipher, NULL); |
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10496
diff
changeset
|
559 |
12549 | 560 x = g_strdup_printf("%s:%s:%s", convnode, realm, convpasswd ? convpasswd : ""); |
11183 | 561 gaim_cipher_context_append(context, (const guchar *)x, strlen(x)); |
10687
b256ce6b85b8
[gaim-migrate @ 12235]
Etan Reisner <pidgin@unreliablesource.net>
parents:
10684
diff
changeset
|
562 gaim_cipher_context_digest(context, sizeof(result), result, NULL); |
7014 | 563 |
10136 | 564 a1 = g_strdup_printf("xxxxxxxxxxxxxxxx:%s:%s", nonce, cnonce); |
565 a1len = strlen(a1); | |
566 g_memmove(a1, result, 16); | |
7014 | 567 |
10684
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10496
diff
changeset
|
568 gaim_cipher_context_reset(context, NULL); |
11183 | 569 gaim_cipher_context_append(context, (const guchar *)a1, a1len); |
10687
b256ce6b85b8
[gaim-migrate @ 12235]
Etan Reisner <pidgin@unreliablesource.net>
parents:
10684
diff
changeset
|
570 gaim_cipher_context_digest(context, sizeof(result), result, NULL); |
7014 | 571 |
7106
db6bd3e794d8
[gaim-migrate @ 7671]
Christian Hammond <chipx86@chipx86.com>
parents:
7014
diff
changeset
|
572 ha1 = gaim_base16_encode(result, 16); |
7014 | 573 |
10684
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10496
diff
changeset
|
574 gaim_cipher_context_reset(context, NULL); |
11183 | 575 gaim_cipher_context_append(context, (const guchar *)a2, strlen(a2)); |
10687
b256ce6b85b8
[gaim-migrate @ 12235]
Etan Reisner <pidgin@unreliablesource.net>
parents:
10684
diff
changeset
|
576 gaim_cipher_context_digest(context, sizeof(result), result, NULL); |
7014 | 577 |
7106
db6bd3e794d8
[gaim-migrate @ 7671]
Christian Hammond <chipx86@chipx86.com>
parents:
7014
diff
changeset
|
578 ha2 = gaim_base16_encode(result, 16); |
7014 | 579 |
580 kd = g_strdup_printf("%s:%s:00000001:%s:auth:%s", ha1, nonce, cnonce, ha2); | |
581 | |
10684
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10496
diff
changeset
|
582 gaim_cipher_context_reset(context, NULL); |
11183 | 583 gaim_cipher_context_append(context, (const guchar *)kd, strlen(kd)); |
10687
b256ce6b85b8
[gaim-migrate @ 12235]
Etan Reisner <pidgin@unreliablesource.net>
parents:
10684
diff
changeset
|
584 gaim_cipher_context_digest(context, sizeof(result), result, NULL); |
10684
72a5babfa8b4
[gaim-migrate @ 12231]
Luke Schierer <lschiere@pidgin.im>
parents:
10496
diff
changeset
|
585 gaim_cipher_context_destroy(context); |
7014 | 586 |
7106
db6bd3e794d8
[gaim-migrate @ 7671]
Christian Hammond <chipx86@chipx86.com>
parents:
7014
diff
changeset
|
587 z = gaim_base16_encode(result, 16); |
7014 | 588 |
10136 | 589 g_free(convnode); |
590 g_free(convpasswd); | |
7014 | 591 g_free(x); |
592 g_free(a1); | |
593 g_free(ha1); | |
594 g_free(ha2); | |
595 g_free(kd); | |
596 | |
597 return z; | |
598 } | |
599 | |
600 void | |
601 jabber_auth_handle_challenge(JabberStream *js, xmlnode *packet) | |
602 { | |
603 | |
7703 | 604 if(js->auth_type == JABBER_AUTH_DIGEST_MD5) { |
7291 | 605 char *enc_in = xmlnode_get_data(packet); |
606 char *dec_in; | |
607 char *enc_out; | |
608 GHashTable *parts; | |
7014 | 609 |
7395 | 610 if(!enc_in) { |
7981 | 611 gaim_connection_error(js->gc, _("Invalid response from server.")); |
7395 | 612 return; |
613 } | |
614 | |
11127 | 615 dec_in = (char *)gaim_base64_decode(enc_in, NULL); |
7395 | 616 gaim_debug(GAIM_DEBUG_MISC, "jabber", "decoded challenge (%d): %s\n", |
617 strlen(dec_in), dec_in); | |
7291 | 618 |
619 parts = parse_challenge(dec_in); | |
7014 | 620 |
621 | |
7291 | 622 if (g_hash_table_lookup(parts, "rspauth")) { |
623 char *rspauth = g_hash_table_lookup(parts, "rspauth"); | |
7014 | 624 |
625 | |
7291 | 626 if(rspauth && js->expected_rspauth && |
627 !strcmp(rspauth, js->expected_rspauth)) { | |
628 jabber_send_raw(js, | |
7642 | 629 "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />", |
630 -1); | |
7291 | 631 } else { |
632 gaim_connection_error(js->gc, _("Invalid challenge from server")); | |
633 } | |
634 g_free(js->expected_rspauth); | |
635 } else { | |
636 /* assemble a response, and send it */ | |
637 /* see RFC 2831 */ | |
638 GString *response = g_string_new(""); | |
639 char *a2; | |
640 char *auth_resp; | |
641 char *buf; | |
642 char *cnonce; | |
643 char *realm; | |
644 char *nonce; | |
7014 | 645 |
7291 | 646 /* we're actually supposed to prompt the user for a realm if |
647 * the server doesn't send one, but that really complicates things, | |
648 * so i'm not gonna worry about it until is poses a problem to | |
649 * someone, or I get really bored */ | |
650 realm = g_hash_table_lookup(parts, "realm"); | |
651 if(!realm) | |
652 realm = js->user->domain; | |
7014 | 653 |
7291 | 654 cnonce = g_strdup_printf("%x%u%x", g_random_int(), (int)time(NULL), |
655 g_random_int()); | |
656 nonce = g_hash_table_lookup(parts, "nonce"); | |
7014 | 657 |
658 | |
7291 | 659 a2 = g_strdup_printf("AUTHENTICATE:xmpp/%s", realm); |
660 auth_resp = generate_response_value(js->user, | |
10740 | 661 gaim_connection_get_password(js->gc), nonce, cnonce, a2, realm); |
7291 | 662 g_free(a2); |
663 | |
664 a2 = g_strdup_printf(":xmpp/%s", realm); | |
665 js->expected_rspauth = generate_response_value(js->user, | |
10740 | 666 gaim_connection_get_password(js->gc), nonce, cnonce, a2, realm); |
7291 | 667 g_free(a2); |
668 | |
669 | |
670 g_string_append_printf(response, "username=\"%s\"", js->user->node); | |
671 g_string_append_printf(response, ",realm=\"%s\"", realm); | |
672 g_string_append_printf(response, ",nonce=\"%s\"", nonce); | |
673 g_string_append_printf(response, ",cnonce=\"%s\"", cnonce); | |
674 g_string_append_printf(response, ",nc=00000001"); | |
675 g_string_append_printf(response, ",qop=auth"); | |
676 g_string_append_printf(response, ",digest-uri=\"xmpp/%s\"", realm); | |
677 g_string_append_printf(response, ",response=%s", auth_resp); | |
678 g_string_append_printf(response, ",charset=utf-8"); | |
679 | |
680 g_free(auth_resp); | |
681 g_free(cnonce); | |
682 | |
11137 | 683 enc_out = gaim_base64_encode((guchar *)response->str, response->len); |
7291 | 684 |
685 gaim_debug(GAIM_DEBUG_MISC, "jabber", "decoded response (%d): %s\n", response->len, response->str); | |
686 | |
687 buf = g_strdup_printf("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>", enc_out); | |
688 | |
7642 | 689 jabber_send_raw(js, buf, -1); |
7291 | 690 |
691 g_free(buf); | |
692 | |
693 g_free(enc_out); | |
694 | |
695 g_string_free(response, TRUE); | |
7014 | 696 } |
7291 | 697 |
698 g_free(enc_in); | |
699 g_free(dec_in); | |
700 g_hash_table_destroy(parts); | |
7014 | 701 } |
12508 | 702 #ifdef HAVE_CYRUS_SASL |
703 else if (js->auth_type == JABBER_AUTH_CYRUS) { | |
704 char *enc_in = xmlnode_get_data(packet); | |
705 unsigned char *dec_in; | |
706 char *enc_out; | |
707 const char *c_out; | |
12543 | 708 unsigned int clen; |
709 gsize declen; | |
12508 | 710 xmlnode *response; |
711 | |
712 dec_in = gaim_base64_decode(enc_in, &declen); | |
713 | |
714 js->sasl_state = sasl_client_step(js->sasl, (char*)dec_in, declen, | |
715 NULL, &c_out, &clen); | |
716 g_free(dec_in); | |
717 if (js->sasl_state != SASL_CONTINUE && js->sasl_state != SASL_OK) { | |
718 gaim_debug_error("jabber", "Error is %d : %s\n",js->sasl_state,sasl_errdetail(js->sasl)); | |
719 gaim_connection_error(js->gc, _("SASL error")); | |
720 return; | |
721 } else { | |
722 response = xmlnode_new("response"); | |
13806 | 723 xmlnode_set_namespace(response, "urn:ietf:params:xml:ns:xmpp-sasl"); |
12508 | 724 if (c_out) { |
725 enc_out = gaim_base64_encode((unsigned char*)c_out, clen); | |
726 xmlnode_insert_data(response, enc_out, -1); | |
727 g_free(enc_out); | |
728 } | |
729 jabber_send(js, response); | |
730 xmlnode_free(response); | |
731 } | |
732 } | |
733 #endif | |
7014 | 734 } |
735 | |
736 void jabber_auth_handle_success(JabberStream *js, xmlnode *packet) | |
737 { | |
13806 | 738 const char *ns = xmlnode_get_namespace(packet); |
12508 | 739 #ifdef HAVE_CYRUS_SASL |
740 int *x; | |
741 #endif | |
7014 | 742 |
743 if(!ns || strcmp(ns, "urn:ietf:params:xml:ns:xmpp-sasl")) { | |
7981 | 744 gaim_connection_error(js->gc, _("Invalid response from server.")); |
7014 | 745 return; |
746 } | |
747 | |
12520
d85c2bfb2ea2
[gaim-migrate @ 14832]
Richard Laager <rlaager@wiktel.com>
parents:
12508
diff
changeset
|
748 #ifdef HAVE_CYRUS_SASL |
12508 | 749 /* The SASL docs say that if the client hasn't returned OK yet, we |
750 * should try one more round against it | |
751 */ | |
752 if (js->sasl_state != SASL_OK) { | |
753 js->sasl_state = sasl_client_step(js->sasl, NULL, 0, NULL, NULL, NULL); | |
754 if (js->sasl_state != SASL_OK) { | |
755 /* This should never happen! */ | |
756 gaim_connection_error(js->gc, _("Invalid response from server.")); | |
757 } | |
758 } | |
759 /* If we've negotiated a security layer, we need to enable it */ | |
760 sasl_getprop(js->sasl, SASL_SSF, (const void **)&x); | |
761 if (*x>0) { | |
762 sasl_getprop(js->sasl, SASL_MAXOUTBUF, (const void **)&x); | |
763 js->sasl_maxbuf = *x; | |
764 } | |
765 #endif | |
766 | |
7014 | 767 jabber_stream_set_state(js, JABBER_STREAM_REINITIALIZING); |
768 } | |
769 | |
770 void jabber_auth_handle_failure(JabberStream *js, xmlnode *packet) | |
771 { | |
8401 | 772 char *msg = jabber_parse_error(js, packet); |
7014 | 773 |
8401 | 774 if(!msg) { |
7981 | 775 gaim_connection_error(js->gc, _("Invalid response from server.")); |
8401 | 776 } else { |
777 gaim_connection_error(js->gc, msg); | |
778 g_free(msg); | |
7014 | 779 } |
780 } |