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();