Mercurial > pidgin
comparison libpurple/protocols/msn/nexus.c @ 23476:9fdf0accd4aa
Finally got MSN token updating working (I think).
So it seems after creating the signature, the xmlnode_to_str must have
added some xmlns attributes which were sent to the server. I thought that
CanonicalizationMethod stuff the XML specified meant the server would
normalize everything nicely, but apparently not.
I added the xmlns to the XML string before creating the signature and
it looks like things work now. It just needs a full 8-hour test to be
certain.
Did a bit of re-factoring to the token response parsing, as well. It
should now fail the first time something shows up that isn't expected,
or if something is missing, instead of blindly going forward with half
the tokens we requested.
References #4875.
author | Elliott Sales de Andrade <qulogic@pidgin.im> |
---|---|
date | Sun, 15 Jun 2008 06:22:25 +0000 |
parents | 1b98e2090a71 |
children | c5891c8d0c28 |
comparison
equal
deleted
inserted
replaced
23475:f85450504940 | 23476:9fdf0accd4aa |
---|---|
237 int id; | 237 int id; |
238 GSourceFunc cb; | 238 GSourceFunc cb; |
239 gpointer data; | 239 gpointer data; |
240 }; | 240 }; |
241 | 241 |
242 static void | 242 static gboolean |
243 save_tokens(GHashTable *table, const char *str) | 243 nexus_parse_token(MsnNexus *nexus, int id, xmlnode *node) |
244 { | 244 { |
245 char *token_str, *expiry_str; | |
246 const char *id_str; | |
245 char **elems, **cur, **tokens; | 247 char **elems, **cur, **tokens; |
246 | 248 xmlnode *token = xmlnode_get_child(node, "RequestedSecurityToken/BinarySecurityToken"); |
247 elems = g_strsplit(str, "&", 0); | 249 xmlnode *secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret"); |
250 xmlnode *expires = xmlnode_get_child(node, "LifeTime/Expires"); | |
251 | |
252 if (!token) | |
253 return FALSE; | |
254 | |
255 /* Use the ID that the server sent us */ | |
256 if (id == -1) { | |
257 id_str = xmlnode_get_attrib(token, "Id"); | |
258 if (id_str == NULL) | |
259 return FALSE; | |
260 | |
261 id = atol(id_str + 7) - 1; /* 'Compact#' or 'PPToken#' */ | |
262 if (id >= nexus->token_len) | |
263 return FALSE; /* Where did this come from? */ | |
264 } | |
265 | |
266 token_str = xmlnode_get_data(token); | |
267 if (token_str == NULL) | |
268 return FALSE; | |
269 | |
270 elems = g_strsplit(token_str, "&", 0); | |
248 | 271 |
249 for (cur = elems; *cur != NULL; cur++) { | 272 for (cur = elems; *cur != NULL; cur++) { |
250 tokens = g_strsplit(*cur, "=", 2); | 273 tokens = g_strsplit(*cur, "=", 2); |
251 g_hash_table_insert(table, tokens[0], tokens[1]); | 274 g_hash_table_insert(nexus->tokens[id].token, tokens[0], tokens[1]); |
252 /* Don't free each of the tokens, only the array. */ | 275 /* Don't free each of the tokens, only the array. */ |
253 g_free(tokens); | 276 g_free(tokens); |
254 } | 277 } |
255 g_strfreev(elems); | 278 g_strfreev(elems); |
279 g_free(token_str); | |
280 | |
281 if (secret) | |
282 nexus->tokens[id].secret = xmlnode_get_data(secret); | |
283 else | |
284 nexus->tokens[id].secret = NULL; | |
285 | |
286 /* Yay for MS using ISO-8601 */ | |
287 expiry_str = xmlnode_get_data(expires); | |
288 nexus->tokens[id].expiry = purple_str_to_time(expiry_str, | |
289 FALSE, NULL, NULL, NULL); | |
290 g_free(expiry_str); | |
291 | |
292 purple_debug_info("msnp15", "Updated ticket for domain '%s', expires at %" G_GINT64_FORMAT ".\n", | |
293 ticket_domains[id][SSO_VALID_TICKET_DOMAIN], | |
294 (gint64)nexus->tokens[id].expiry); | |
295 return TRUE; | |
256 } | 296 } |
257 | 297 |
258 static gboolean | 298 static gboolean |
259 nexus_parse_response(MsnNexus *nexus, int id, xmlnode *xml) | 299 nexus_parse_response(MsnNexus *nexus, xmlnode *xml) |
260 { | 300 { |
261 xmlnode *node; | 301 xmlnode *node; |
262 xmlnode *cipher; | 302 xmlnode *cipher; |
263 xmlnode *secret; | 303 xmlnode *secret; |
264 char *data; | 304 char *data; |
265 gboolean result = FALSE; | 305 gboolean result; |
266 gboolean parse_all = (id == -1); | |
267 | 306 |
268 node = xmlnode_get_child(xml, "Body/RequestSecurityTokenResponseCollection/RequestSecurityTokenResponse"); | 307 node = xmlnode_get_child(xml, "Body/RequestSecurityTokenResponseCollection/RequestSecurityTokenResponse"); |
269 | 308 |
270 if (!node) | 309 if (!node) |
271 return FALSE; | 310 return FALSE; |
276 secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret"); | 315 secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret"); |
277 data = xmlnode_get_data(secret); | 316 data = xmlnode_get_data(secret); |
278 nexus->secret = (char *)purple_base64_decode(data, NULL); | 317 nexus->secret = (char *)purple_base64_decode(data, NULL); |
279 g_free(data); | 318 g_free(data); |
280 | 319 |
281 for (node = node->next; node; node = node->next) { | 320 result = TRUE; |
282 xmlnode *token = xmlnode_get_child(node, "RequestedSecurityToken/BinarySecurityToken"); | 321 for (node = node->next; node && result; node = node->next) |
283 xmlnode *secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret"); | 322 result = nexus_parse_token(nexus, -1, node); |
284 xmlnode *expires = xmlnode_get_child(node, "LifeTime/Expires"); | |
285 | |
286 if (token) { | |
287 char *token_str, *expiry_str; | |
288 const char *id_str = xmlnode_get_attrib(token, "Id"); | |
289 | |
290 if (id_str == NULL) continue; | |
291 | |
292 if (parse_all) | |
293 id = atol(id_str + 7) - 1; /* 'Compact#' or 'PPToken#' */ | |
294 if (id >= nexus->token_len) | |
295 continue; /* Where did this come from? */ | |
296 | |
297 token_str = xmlnode_get_data(token); | |
298 if (token_str == NULL) continue; | |
299 | |
300 save_tokens(nexus->tokens[id].token, token_str); | |
301 g_free(token_str); | |
302 | |
303 if (secret) | |
304 nexus->tokens[id].secret = xmlnode_get_data(secret); | |
305 else | |
306 nexus->tokens[id].secret = NULL; | |
307 | |
308 /* Yay for MS using ISO-8601 */ | |
309 expiry_str = xmlnode_get_data(expires); | |
310 | |
311 nexus->tokens[id].expiry = purple_str_to_time(expiry_str, | |
312 FALSE, NULL, NULL, NULL); | |
313 | |
314 g_free(expiry_str); | |
315 | |
316 purple_debug_info("msnp15", "Updated ticket for domain '%s'\n", | |
317 ticket_domains[id][SSO_VALID_TICKET_DOMAIN]); | |
318 result = TRUE; | |
319 } | |
320 } | |
321 | 323 |
322 return result; | 324 return result; |
323 } | 325 } |
324 | 326 |
325 static void | 327 static void |
333 if (resp == NULL) { | 335 if (resp == NULL) { |
334 msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Unable to connect")); | 336 msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Unable to connect")); |
335 return; | 337 return; |
336 } | 338 } |
337 | 339 |
338 if (!nexus_parse_response(nexus, -1, resp->xml)) { | 340 if (!nexus_parse_response(nexus, resp->xml)) { |
339 msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Invalid response")); | 341 msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Invalid response")); |
340 return; | 342 return; |
341 } | 343 } |
342 | 344 |
343 ticket = msn_nexus_get_token_str(nexus, MSN_AUTH_MESSENGER); | 345 ticket = msn_nexus_get_token_str(nexus, MSN_AUTH_MESSENGER); |
398 char *tmp; | 400 char *tmp; |
399 char *nonce; | 401 char *nonce; |
400 gsize len; | 402 gsize len; |
401 char *key; | 403 char *key; |
402 | 404 |
405 #if 0 | |
403 char *decrypted_pp; | 406 char *decrypted_pp; |
407 #endif | |
404 char *decrypted_data; | 408 char *decrypted_data; |
405 | 409 |
406 purple_debug_info("msnp15", "Got Update Response for %s.\n", ticket_domains[ud->id][SSO_VALID_TICKET_DOMAIN]); | 410 purple_debug_info("msnp15", "Got Update Response for %s.\n", ticket_domains[ud->id][SSO_VALID_TICKET_DOMAIN]); |
407 | 411 |
408 enckey = xmlnode_get_child(resp->xml, "Header/Security/DerivedKeyToken"); | 412 enckey = xmlnode_get_child(resp->xml, "Header/Security/DerivedKeyToken"); |
420 nonce = (char *)purple_base64_decode(tmp, &len); | 424 nonce = (char *)purple_base64_decode(tmp, &len); |
421 key = rps_create_key(nexus->secret, 24, nonce, len); | 425 key = rps_create_key(nexus->secret, 24, nonce, len); |
422 g_free(tmp); | 426 g_free(tmp); |
423 g_free(nonce); | 427 g_free(nonce); |
424 | 428 |
429 #if 0 | |
430 /* Don't know what this is for yet */ | |
425 tmp = xmlnode_get_data(xmlnode_get_child(resp->xml, | 431 tmp = xmlnode_get_data(xmlnode_get_child(resp->xml, |
426 "Header/EncryptedPP/EncryptedData/CipherData/CipherValue")); | 432 "Header/EncryptedPP/EncryptedData/CipherData/CipherValue")); |
427 if (tmp) { | 433 if (tmp) { |
428 /* Don't know what this is for yet */ | |
429 decrypted_pp = des3_cbc(key, iv, tmp, len, TRUE); | 434 decrypted_pp = des3_cbc(key, iv, tmp, len, TRUE); |
430 g_free(tmp); | 435 g_free(tmp); |
431 purple_debug_info("msnp15", "Got Response Header EncryptedPP: %s\n", decrypted_pp); | 436 purple_debug_info("msnp15", "Got Response Header EncryptedPP: %s\n", decrypted_pp); |
432 g_free(decrypted_pp); | 437 g_free(decrypted_pp); |
433 } | 438 } |
439 #endif | |
434 | 440 |
435 tmp = xmlnode_get_data(xmlnode_get_child(resp->xml, | 441 tmp = xmlnode_get_data(xmlnode_get_child(resp->xml, |
436 "Body/EncryptedData/CipherData/CipherValue")); | 442 "Body/EncryptedData/CipherData/CipherValue")); |
437 if (tmp) { | 443 if (tmp) { |
438 char *unescaped; | 444 char *unescaped; |
439 xmlnode *rstresponse; | 445 xmlnode *rstresponse; |
446 | |
440 unescaped = (char *)purple_base64_decode(tmp, &len); | 447 unescaped = (char *)purple_base64_decode(tmp, &len); |
441 g_free(tmp); | 448 g_free(tmp); |
449 | |
442 decrypted_data = des3_cbc(key, iv, unescaped, len, TRUE); | 450 decrypted_data = des3_cbc(key, iv, unescaped, len, TRUE); |
443 g_free(unescaped); | 451 g_free(unescaped); |
452 purple_debug_info("msnp15", "Got Response Body EncryptedData: %s\n", decrypted_data); | |
453 | |
444 rstresponse = xmlnode_from_str(decrypted_data, -1); | 454 rstresponse = xmlnode_from_str(decrypted_data, -1); |
445 g_hash_table_remove_all(nexus->tokens[ud->id].token); | 455 g_hash_table_remove_all(nexus->tokens[ud->id].token); |
446 save_tokens(nexus->tokens[ud->id].token, | 456 nexus_parse_token(nexus, ud->id, rstresponse); |
447 xmlnode_get_data(xmlnode_get_child(rstresponse, | |
448 "RequestSecurityTokenResponse/RequestedSecurityToken/BinarySecurityToken"))); | |
449 purple_debug_info("msnp15", "Got Response Body EncryptedData: %s\n", decrypted_data); | |
450 g_free(decrypted_data); | 457 g_free(decrypted_data); |
451 } | 458 } |
452 | 459 |
453 if (ud->cb) | 460 if (ud->cb) |
454 purple_timeout_add(0, ud->cb, ud->data); | 461 purple_timeout_add(0, ud->cb, ud->data); |
499 ud->data = data; | 506 ud->data = data; |
500 | 507 |
501 sha1 = purple_cipher_context_new_by_name("sha1", NULL); | 508 sha1 = purple_cipher_context_new_by_name("sha1", NULL); |
502 | 509 |
503 domain = g_strdup_printf(MSN_SSO_RST_TEMPLATE, | 510 domain = g_strdup_printf(MSN_SSO_RST_TEMPLATE, |
504 0, | 511 id, |
505 ticket_domains[id][SSO_VALID_TICKET_DOMAIN], | 512 ticket_domains[id][SSO_VALID_TICKET_DOMAIN], |
506 ticket_domains[id][SSO_VALID_TICKET_POLICY] != NULL ? | 513 ticket_domains[id][SSO_VALID_TICKET_POLICY] != NULL ? |
507 ticket_domains[id][SSO_VALID_TICKET_POLICY] : | 514 ticket_domains[id][SSO_VALID_TICKET_POLICY] : |
508 nexus->policy); | 515 nexus->policy); |
509 purple_cipher_context_append(sha1, (guchar *)domain, strlen(domain)); | 516 purple_cipher_context_append(sha1, (guchar *)domain, strlen(domain)); |
525 g_free(now_str); | 532 g_free(now_str); |
526 | 533 |
527 purple_cipher_context_destroy(sha1); | 534 purple_cipher_context_destroy(sha1); |
528 | 535 |
529 signedinfo = g_strdup_printf(MSN_SSO_SIGNEDINFO_TEMPLATE, | 536 signedinfo = g_strdup_printf(MSN_SSO_SIGNEDINFO_TEMPLATE, |
537 id, | |
530 domain_b64, | 538 domain_b64, |
531 timestamp_b64); | 539 timestamp_b64); |
532 | 540 |
533 for (i = 0; i < 6; i++) | 541 for (i = 0; i < 6; i++) |
534 nonce[i] = rand(); | 542 nonce[i] = rand(); |