changeset 23513:1b98e2090a71

Update MSN nexus functions so that tokens are properly updateable. It also uses a callback to signify that the token has been updated. Note: The updating does not actually work yet, but this commit is so that the next two updates will compile.
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Sat, 07 Jun 2008 06:08:01 +0000
parents b3890180aa2e
children 5f9e6f8b2aea
files libpurple/protocols/msn/nexus.c libpurple/protocols/msn/nexus.h libpurple/protocols/msn/session.c libpurple/protocols/msn/session.h
diffstat 4 files changed, 367 insertions(+), 127 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/msn/nexus.c	Sat Jun 07 04:32:25 2008 +0000
+++ b/libpurple/protocols/msn/nexus.c	Sat Jun 07 06:08:01 2008 +0000
@@ -26,7 +26,6 @@
 #include "nexus.h"
 #include "notification.h"
 
-
 /**************************************************************************
  * Valid Ticket Tokens
  **************************************************************************/
@@ -80,6 +79,8 @@
 	g_free(nexus->tokens);
 	g_free(nexus->policy);
 	g_free(nexus->nonce);
+	g_free(nexus->cipher);
+	g_free(nexus->secret);
 	g_free(nexus);
 }
 
@@ -88,56 +89,56 @@
  **************************************************************************/
 
 static char *
-sha1_hmac(const char *key, int key_len, const char *message, int msg_len)
+rps_create_key(const char *key, int key_len, const char *data, size_t data_len)
 {
-	PurpleCipherContext *context;
+	const guchar magic[] = "WS-SecureConversation";
+	const int magic_len = sizeof(magic) - 1;
+
+	PurpleCipherContext *hmac;
+	guchar hash1[20], hash2[20], hash3[20], hash4[20];
 	char *result;
-	gboolean ret;
+
+	hmac = purple_cipher_context_new_by_name("hmac", NULL);
+
+	purple_cipher_context_set_option(hmac, "hash", "sha1");
+	purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
+	purple_cipher_context_append(hmac, magic, magic_len);
+	purple_cipher_context_append(hmac, (guchar *)data, data_len);
+	purple_cipher_context_digest(hmac, sizeof(hash1), hash1, NULL);
 
-	context = purple_cipher_context_new_by_name("hmac", NULL);
-	purple_cipher_context_set_option(context, "hash", "sha1");
-	purple_cipher_context_set_key_with_len(context, (guchar *)key, key_len);
+	purple_cipher_context_reset(hmac, NULL);
+	purple_cipher_context_set_option(hmac, "hash", "sha1");
+	purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
+	purple_cipher_context_append(hmac, hash1, 20);
+	purple_cipher_context_append(hmac, magic, magic_len);
+	purple_cipher_context_append(hmac, (guchar *)data, data_len);
+	purple_cipher_context_digest(hmac, sizeof(hash2), hash2, NULL);
+
+	purple_cipher_context_reset(hmac, NULL);
+	purple_cipher_context_set_option(hmac, "hash", "sha1");
+	purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
+	purple_cipher_context_append(hmac, hash1, 20);
+	purple_cipher_context_digest(hmac, sizeof(hash3), hash3, NULL);
 
-	purple_cipher_context_append(context, (guchar *)message, msg_len);
-	result = g_malloc(20);
-	ret = purple_cipher_context_digest(context, 20, (guchar *)result, NULL);
+	purple_cipher_context_reset(hmac, NULL);
+	purple_cipher_context_set_option(hmac, "hash", "sha1");
+	purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
+	purple_cipher_context_append(hmac, hash3, sizeof(hash3));
+	purple_cipher_context_append(hmac, magic, magic_len);
+	purple_cipher_context_append(hmac, (guchar *)data, data_len);
+	purple_cipher_context_digest(hmac, sizeof(hash4), hash4, NULL);
 
-	purple_cipher_context_destroy(context);
+	purple_cipher_context_destroy(hmac);
+
+	result = g_malloc(24);
+	memcpy(result, hash2, sizeof(hash2));
+	memcpy(result + sizeof(hash2), hash4, 4);
 
 	return result;
 }
 
 static char *
-rps_create_key(const char *key, int key_len, const char *data, size_t data_len)
-{
-	char *hash1, *hash2, *hash3, *hash4;
-	char *result;
-
-	hash1 = sha1_hmac(key, key_len, data, data_len);
-	hash1 = g_realloc(hash1, 20 + data_len);
-	memcpy(hash1 + 20, data, data_len);
-	hash2 = sha1_hmac(key, key_len, hash1, 20 + data_len);
-
-	hash3 = sha1_hmac(key, key_len, hash1, 20);
-
-	hash3 = g_realloc(hash3, 20 + data_len);
-	memcpy(hash3 + 20, data, data_len);
-	hash4 = sha1_hmac(key, key_len, hash3, 20 + data_len);
-
-	result = g_malloc(24);
-	memcpy(result, hash2, 20);
-	memcpy(result + 20, hash4, 4);
-
-	g_free(hash1);
-	g_free(hash2);
-	g_free(hash3);
-	g_free(hash4);
-
-	return result;
-}
-
-static char *
-des3_cbc(const char *key, const char *iv, const char *data, int len)
+des3_cbc(const char *key, const char *iv, const char *data, int len, gboolean decrypt)
 {
 	PurpleCipherContext *des3;
 	char *out;
@@ -149,7 +150,10 @@
 	purple_cipher_context_set_iv(des3, (guchar *)iv, 8);
 
 	out = g_malloc(len);
-	purple_cipher_context_encrypt(des3, (guchar *)data, len, (guchar *)out, &outlen);
+	if (decrypt)
+		purple_cipher_context_decrypt(des3, (guchar *)data, len, (guchar *)out, &outlen);
+	else
+		purple_cipher_context_encrypt(des3, (guchar *)data, len, (guchar *)out, &outlen);
 
 	purple_cipher_context_destroy(des3);
 
@@ -163,12 +167,14 @@
 msn_rps_encrypt(MsnNexus *nexus)
 {
 	MsnUsrKey *usr_key;
-	const char *magic1 = "WS-SecureConversationSESSION KEY HASH";
-	const char *magic2 = "WS-SecureConversationSESSION KEY ENCRYPTION";
+	const char magic1[] = "SESSION KEY HASH";
+	const char magic2[] = "SESSION KEY ENCRYPTION";
+	PurpleCipherContext *hmac;
 	size_t len;
-	char *hash;
+	guchar hash[20];
 	char *key1, *key2, *key3;
 	gsize key1_len;
+	int *iv;
 	char *nonce_fixed;
 	char *cipher;
 	char *response;
@@ -183,28 +189,26 @@
 	usr_key->cipher_len = GUINT32_TO_LE(72);
 
 	key1 = (char *)purple_base64_decode((const char *)nexus->tokens[MSN_AUTH_MESSENGER].secret, &key1_len);
-	len = strlen(magic1);
-	key2 = rps_create_key(key1, key1_len, magic1, len);
-	len = strlen(magic2);
-	key3 = rps_create_key(key1, key1_len, magic2, len);
+	key2 = rps_create_key(key1, key1_len, magic1, sizeof(magic1) - 1);
+	key3 = rps_create_key(key1, key1_len, magic2, sizeof(magic2) - 1);
 
-	usr_key->iv[0] = 0x46; //rand() % 256;
-	usr_key->iv[1] = 0xC4;
-	usr_key->iv[2] = 0x14;
-	usr_key->iv[3] = 0x9F;
-	usr_key->iv[4] = 0xFF;
-	usr_key->iv[5] = 0xFC;
-	usr_key->iv[6] = 0x91;
-	usr_key->iv[7] = 0x61;
+	iv = (int *)usr_key->iv;
+	iv[0] = rand();
+	iv[1] = rand();
 
 	len = strlen(nexus->nonce);
-	hash = sha1_hmac(key2, 24, nexus->nonce, len);
+	hmac = purple_cipher_context_new_by_name("hmac", NULL);
+	purple_cipher_context_set_option(hmac, "hash", "sha1");
+	purple_cipher_context_set_key_with_len(hmac, (guchar *)key2, 24);
+	purple_cipher_context_append(hmac, (guchar *)nexus->nonce, len);
+	purple_cipher_context_digest(hmac, 20, hash, NULL);
+	purple_cipher_context_destroy(hmac);
 
 	/* We need to pad this to 72 bytes, apparently */
 	nonce_fixed = g_malloc(len + 8);
 	memcpy(nonce_fixed, nexus->nonce, len);
 	memset(nonce_fixed + len, 0x08, 8);
-	cipher = des3_cbc(key3, usr_key->iv, nonce_fixed, len + 8);
+	cipher = des3_cbc(key3, usr_key->iv, nonce_fixed, len + 8, FALSE);
 	g_free(nonce_fixed);
 
 	memcpy(usr_key->hash, hash, 20);
@@ -213,7 +217,6 @@
 	g_free(key1);
 	g_free(key2);
 	g_free(key3);
-	g_free(hash);
 	g_free(cipher);
 
 	response = purple_base64_encode((guchar *)usr_key, sizeof(MsnUsrKey));
@@ -227,20 +230,55 @@
  * Login
  **************************************************************************/
 
+/* Used to specify which token to update when only doing single updates */
+typedef struct _MsnNexusUpdateData MsnNexusUpdateData;
+struct _MsnNexusUpdateData {
+	MsnNexus *nexus;
+	int id;
+	GSourceFunc cb;
+	gpointer data;
+};
+
+static void
+save_tokens(GHashTable *table, const char *str)
+{
+	char **elems, **cur, **tokens;
+
+	elems = g_strsplit(str, "&", 0);
+
+	for (cur = elems; *cur != NULL; cur++) {
+		tokens = g_strsplit(*cur, "=", 2);
+		g_hash_table_insert(table, tokens[0], tokens[1]);
+		/* Don't free each of the tokens, only the array. */
+		g_free(tokens);
+	}
+	g_strfreev(elems);
+}
+
 static gboolean
-nexus_parse_response(MsnNexus *nexus, xmlnode *xml)
+nexus_parse_response(MsnNexus *nexus, int id, xmlnode *xml)
 {
 	xmlnode *node;
+	xmlnode *cipher;
+	xmlnode *secret;
+	char *data;
 	gboolean result = FALSE;
+	gboolean parse_all = (id == -1);
 
 	node = xmlnode_get_child(xml, "Body/RequestSecurityTokenResponseCollection/RequestSecurityTokenResponse");
 
-	if (node)
-		node = node->next;	/* The first one is not useful */
-	else
+	if (!node)
 		return FALSE;
 
-	for (; node; node = node->next) {
+	/* The first node contains the stuff for updating tokens. */
+	cipher = xmlnode_get_child(node, "RequestedSecurityToken/EncryptedData/CipherData/CipherValue");
+	nexus->cipher = xmlnode_get_data(cipher);
+	secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret");
+	data = xmlnode_get_data(secret);
+	nexus->secret = (char *)purple_base64_decode(data, NULL);
+	g_free(data);
+
+	for (node = node->next; node; node = node->next) {
 		xmlnode *token = xmlnode_get_child(node, "RequestedSecurityToken/BinarySecurityToken");
 		xmlnode *secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret");
 		xmlnode *expires = xmlnode_get_child(node, "LifeTime/Expires");
@@ -248,28 +286,19 @@
 		if (token) {
 			char *token_str, *expiry_str;
 			const char *id_str = xmlnode_get_attrib(token, "Id");
-			char **elems, **cur, **tokens;
-			int id;
 
 			if (id_str == NULL) continue;
 
-			id = atol(id_str + 7) - 1;	/* 'Compact#' or 'PPToken#' */
+			if (parse_all)
+				id = atol(id_str + 7) - 1;	/* 'Compact#' or 'PPToken#' */
 			if (id >= nexus->token_len)
 				continue;	/* Where did this come from? */
 
 			token_str = xmlnode_get_data(token);
 			if (token_str == NULL) continue;
-			elems = g_strsplit(token_str, "&", 0);
 
-			for (cur = elems; *cur != NULL; cur++){
-				tokens = g_strsplit(*cur, "=", 2);
-				g_hash_table_insert(nexus->tokens[id].token, tokens[0], tokens[1]);
-				/* Don't free each of the tokens, only the array. */
-				g_free(tokens);
-			}
-
+			save_tokens(nexus->tokens[id].token, token_str);
 			g_free(token_str);
-			g_strfreev(elems);
 
 			if (secret)
 				nexus->tokens[id].secret = xmlnode_get_data(secret);
@@ -298,7 +327,7 @@
 {
 	MsnNexus *nexus = data;
 	MsnSession *session = nexus->session;
-	char *msn_twn_t, *msn_twn_p, *ticket;
+	const char *ticket;
 	char *response;
 
 	if (resp == NULL) {
@@ -306,23 +335,14 @@
 		return;
 	}
 
-	if (!nexus_parse_response(nexus, resp->xml)) {
+	if (!nexus_parse_response(nexus, -1, resp->xml)) {
 		msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Invalid response"));
 		return;
 	}
 
-	/*setup the t and p parameter for session*/
-	msn_twn_t = g_hash_table_lookup(nexus->tokens[MSN_AUTH_MESSENGER].token, "t");
-	msn_twn_p = g_hash_table_lookup(nexus->tokens[MSN_AUTH_MESSENGER].token, "p");
-	g_free(session->passport_info.t);
-	session->passport_info.t = g_strdup(msn_twn_t);
-	g_free(session->passport_info.p);
-	session->passport_info.p = g_strdup(msn_twn_p);
-
-	ticket = g_strdup_printf("t=%s&p=%s", msn_twn_t, msn_twn_p);
+	ticket = msn_nexus_get_token_str(nexus, MSN_AUTH_MESSENGER);
 	response = msn_rps_encrypt(nexus);
 	msn_got_login_params(session, ticket, response);
-	g_free(ticket);
 	g_free(response);
 }
 
@@ -331,16 +351,18 @@
 msn_nexus_connect(MsnNexus *nexus)
 {
 	MsnSession *session = nexus->session;
-	char *username, *password;
+	const char *username;
+	char *password;
 	GString *domains;
 	char *request;
 	int i;
 
 	MsnSoapMessage *soap;
 
+	purple_debug_info("MSN Nexus","Starting Windows Live ID authentication\n");
 	msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE);
 
-	username = g_strdup(purple_account_get_username(session->account));
+	username = purple_account_get_username(session->account);
 	password = g_strndup(purple_connection_get_password(session->account->gc), 16);
 
 	purple_debug_info("msnp15", "Logging on %s, with policy '%s', nonce '%s'\n",
@@ -357,7 +379,6 @@
 	}
 
 	request = g_strdup_printf(MSN_SSO_TEMPLATE, username, password, domains->str);
-	g_free(username);
 	g_free(password);
 	g_string_free(domains, TRUE);
 
@@ -370,46 +391,179 @@
 static void
 nexus_got_update_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
-	MsnNexus *nexus = data;
+	MsnNexusUpdateData *ud = data;
+	MsnNexus *nexus = ud->nexus;
+	char iv[8] = {0,0,0,0,0,0,0,0};
+	xmlnode *enckey;
+	char *tmp;
+	char *nonce;
+	gsize len;
+	char *key;
+
+	char *decrypted_pp;
+	char *decrypted_data;
+
+	purple_debug_info("msnp15", "Got Update Response for %s.\n", ticket_domains[ud->id][SSO_VALID_TICKET_DOMAIN]);
+
+	enckey = xmlnode_get_child(resp->xml, "Header/Security/DerivedKeyToken");
+	while (enckey) {
+		if (g_str_equal(xmlnode_get_attrib(enckey, "Id"), "EncKey"))
+			break;
+		enckey = xmlnode_get_next_twin(enckey);
+	}
+	if (!enckey) {
+		purple_debug_info("msnp15", "Invalid Response.\n");
+		return;
+	}
+
+	tmp = xmlnode_get_data(xmlnode_get_child(enckey, "Nonce"));
+	nonce = (char *)purple_base64_decode(tmp, &len);
+	key = rps_create_key(nexus->secret, 24, nonce, len);
+	g_free(tmp);
+	g_free(nonce);
 
-	nexus_parse_response(nexus, resp->xml);
+	tmp = xmlnode_get_data(xmlnode_get_child(resp->xml,
+		"Header/EncryptedPP/EncryptedData/CipherData/CipherValue"));
+	if (tmp) {
+		/* Don't know what this is for yet */
+		decrypted_pp = des3_cbc(key, iv, tmp, len, TRUE);
+		g_free(tmp);
+		purple_debug_info("msnp15", "Got Response Header EncryptedPP: %s\n", decrypted_pp);
+		g_free(decrypted_pp);
+	}
+
+	tmp = xmlnode_get_data(xmlnode_get_child(resp->xml,
+		"Body/EncryptedData/CipherData/CipherValue"));
+	if (tmp) {
+		char *unescaped;
+		xmlnode *rstresponse;
+		unescaped = (char *)purple_base64_decode(tmp, &len);
+		g_free(tmp);
+		decrypted_data = des3_cbc(key, iv, unescaped, len, TRUE);
+		g_free(unescaped);
+		rstresponse = xmlnode_from_str(decrypted_data, -1);
+		g_hash_table_remove_all(nexus->tokens[ud->id].token);
+		save_tokens(nexus->tokens[ud->id].token,
+			xmlnode_get_data(xmlnode_get_child(rstresponse,
+				"RequestSecurityTokenResponse/RequestedSecurityToken/BinarySecurityToken")));
+		purple_debug_info("msnp15", "Got Response Body EncryptedData: %s\n", decrypted_data);
+		g_free(decrypted_data);
+	}
+
+	if (ud->cb)
+		purple_timeout_add(0, ud->cb, ud->data);
+
+	g_free(ud);
 }
 
-static void
-msn_nexus_update_token(MsnNexus *nexus, int id)
+void
+msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data)
 {
 	MsnSession *session = nexus->session;
-	char *username, *password;
+	MsnNexusUpdateData *ud;
+	PurpleCipherContext *sha1;
+	PurpleCipherContext *hmac;
+
+	char *key;
+
+	guchar digest[20];
+
+	struct tm *tm;
+	time_t now;
+	char *now_str;
+	char *timestamp;
+	char *timestamp_b64;
+
 	char *domain;
+	char *domain_b64;
+
+	char *signedinfo;
+	gint32 nonce[6];
+	int i;
+	char *nonce_b64;
+	char *signature_b64;
+	guchar signature[20];
+
 	char *request;
-
 	MsnSoapMessage *soap;
 
-	username = g_strdup(purple_account_get_username(session->account));
-	password = g_strndup(purple_connection_get_password(session->account->gc), 16);
+	purple_debug_info("msnp15",
+	                  "Updating ticket for user '%s' on domain '%s'\n",
+	                  purple_account_get_username(session->account),
+	                  ticket_domains[id][SSO_VALID_TICKET_DOMAIN]);
 
-	purple_debug_info("msnp15", "Updating ticket for user '%s' on domain '%s'\n",
-	                  username, ticket_domains[id][SSO_VALID_TICKET_DOMAIN]);
+	ud = g_new0(MsnNexusUpdateData, 1);
+	ud->nexus = nexus;
+	ud->id = id;
+	ud->cb = cb;
+	ud->data = data;
 
-	/* TODO: This really assumes if we send RSTn, the server responds with
-	 Compactn, even if there is no RST(n-1). This needs checking.
-	*/
+	sha1 = purple_cipher_context_new_by_name("sha1", NULL);
+
 	domain = g_strdup_printf(MSN_SSO_RST_TEMPLATE,
-		                       id,
-		                       ticket_domains[id][SSO_VALID_TICKET_DOMAIN],
-		                       ticket_domains[id][SSO_VALID_TICKET_POLICY] != NULL ?
-		                           ticket_domains[id][SSO_VALID_TICKET_POLICY] :
-		                           nexus->policy);
+	                         0,
+	                         ticket_domains[id][SSO_VALID_TICKET_DOMAIN],
+	                         ticket_domains[id][SSO_VALID_TICKET_POLICY] != NULL ?
+	                             ticket_domains[id][SSO_VALID_TICKET_POLICY] :
+	                             nexus->policy);
+	purple_cipher_context_append(sha1, (guchar *)domain, strlen(domain));
+	purple_cipher_context_digest(sha1, 20, digest, NULL);
+	domain_b64 = purple_base64_encode(digest, 20);
+
+	now = time(NULL);
+	tm = gmtime(&now);
+	now_str = g_strdup(purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm));
+	now += 5*60;
+	tm = gmtime(&now);
+	timestamp = g_strdup_printf(MSN_SSO_TIMESTAMP_TEMPLATE,
+	                            now_str,
+	                            purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm));
+	purple_cipher_context_reset(sha1, NULL);
+	purple_cipher_context_append(sha1, (guchar *)timestamp, strlen(timestamp));
+	purple_cipher_context_digest(sha1, 20, digest, NULL);
+	timestamp_b64 = purple_base64_encode(digest, 20);
+	g_free(now_str);
+
+	purple_cipher_context_destroy(sha1);
+
+	signedinfo = g_strdup_printf(MSN_SSO_SIGNEDINFO_TEMPLATE,
+	                             domain_b64,
+	                             timestamp_b64);
 
-	request = g_strdup_printf(MSN_SSO_TEMPLATE, username, password, domain);
-	g_free(username);
-	g_free(password);
+	for (i = 0; i < 6; i++)
+		nonce[i] = rand();
+	nonce_b64 = purple_base64_encode((guchar *)&nonce, sizeof(nonce));
+
+	key = rps_create_key(nexus->secret, 24, (char *)nonce, sizeof(nonce));
+	hmac = purple_cipher_context_new_by_name("hmac", NULL);
+	purple_cipher_context_set_option(hmac, "hash", "sha1");
+	purple_cipher_context_set_key_with_len(hmac, (guchar *)key, 24);
+	purple_cipher_context_append(hmac, (guchar *)signedinfo, strlen(signedinfo));
+	purple_cipher_context_digest(hmac, 20, signature, NULL);
+	purple_cipher_context_destroy(hmac);
+	signature_b64 = purple_base64_encode(signature, 20);
+
+	request = g_strdup_printf(MSN_SSO_TOKEN_UPDATE_TEMPLATE,
+	                          nexus->cipher,
+	                          nonce_b64,
+	                          timestamp,
+	                          signedinfo,
+	                          signature_b64,
+	                          domain);
+
+	g_free(nonce_b64);
+	g_free(domain_b64);
+	g_free(timestamp_b64);
+	g_free(timestamp);
+	g_free(key);
+	g_free(signature_b64);
+	g_free(signedinfo);
 	g_free(domain);
 
 	soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1));
 	g_free(request);
 	msn_soap_message_send(session, soap, MSN_SSO_SERVER, SSO_POST_URL,
-	                      nexus_got_update_cb, nexus);
+	                      nexus_got_update_cb, ud);
 }
 
 GHashTable *
@@ -418,9 +572,6 @@
 	g_return_val_if_fail(nexus != NULL, NULL);
 	g_return_val_if_fail(id < nexus->token_len, NULL);
 
-	if (time(NULL) > nexus->tokens[id].expiry)
-		msn_nexus_update_token(nexus, id);
-
 	return nexus->tokens[id].token;
 }
 
@@ -441,7 +592,7 @@
 	g_return_val_if_fail(msn_t != NULL, NULL);
 	g_return_val_if_fail(msn_p != NULL, NULL);
 
-	ret = g_snprintf(buf, sizeof(buf) - 1, "t=%s&amp;p=%s", msn_t, msn_p);
+	ret = g_snprintf(buf, sizeof(buf) - 1, "t=%s&p=%s", msn_t, msn_p);
 	g_return_val_if_fail(ret != -1, NULL);
 
 	return buf;
--- a/libpurple/protocols/msn/nexus.h	Sat Jun 07 04:32:25 2008 +0000
+++ b/libpurple/protocols/msn/nexus.h	Sat Jun 07 06:08:01 2008 +0000
@@ -95,6 +95,96 @@
 	"</Body>"\
 "</Envelope>"
 
+#define MSN_SSO_AUTHINFO_TEMPLATE \
+"<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">"\
+	"<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>"\
+	"<ps:BinaryVersion>4</ps:BinaryVersion>"\
+	"<ps:UIVersion>1</ps:UIVersion>"\
+	"<ps:Cookies></ps:Cookies>"\
+	"<ps:RequestParams>AQAAAAIAAABsYwQAAAA0MTA1</ps:RequestParams>"\
+"</ps:AuthInfo>"
+/* Not sure what's editable here, so I'll just hard-code the SHA1 hash */
+#define MSN_SSO_AUTHINFO_SHA1_BASE64 "d2IeTF4DAkPEa/tVETHznsivEpc="
+
+#define MSN_SSO_TIMESTAMP_TEMPLATE \
+"<wsu:Timestamp Id=\"Timestamp\">"\
+	"<wsu:Created>%s</wsu:Created>"\
+	"<wsu:Expires>%s</wsu:Expires>"\
+"</wsu:Timestamp>"
+
+#define MSN_SSO_SIGNEDINFO_TEMPLATE \
+"<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"\
+	"<CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></CanonicalizationMethod>"\
+	"<SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#hmac-sha1\"></SignatureMethod>"\
+	"<Reference URI=\"#RST0\">"\
+		"<Transforms>"\
+			"<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></Transform>"\
+		"</Transforms>"\
+		"<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"></DigestMethod>"\
+		"<DigestValue>%s</DigestValue>"\
+	"</Reference>"\
+	"<Reference URI=\"#Timestamp\">"\
+		"<Transforms>"\
+			"<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></Transform>"\
+		"</Transforms>"\
+		"<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"></DigestMethod>"\
+		"<DigestValue>%s</DigestValue>"\
+	"</Reference>"\
+	"<Reference URI=\"#PPAuthInfo\">"\
+		"<Transforms>"\
+			"<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></Transform>"\
+		"</Transforms>"\
+		"<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"></DigestMethod>"\
+		"<DigestValue>" MSN_SSO_AUTHINFO_SHA1_BASE64 "</DigestValue>"\
+	"</Reference>"\
+"</SignedInfo>"
+
+#define MSN_SSO_TOKEN_UPDATE_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<Envelope"\
+	" xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\""\
+	" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\""\
+	" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\""\
+	" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""\
+	" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\""\
+	" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\""\
+	" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\
+	"<Header>"\
+		MSN_SSO_AUTHINFO_TEMPLATE /* ps:AuthInfo */ \
+		"<wsse:Security>"\
+			"<EncryptedData xmlns=\"http://www.w3.org/2001/04/xmlenc#\" Id=\"BinaryDAToken0\" Type=\"http://www.w3.org/2001/04/xmlenc#Element\">"\
+				"<EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#tripledes-cbc\"></EncryptionMethod>"\
+				"<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">"\
+					"<ds:KeyName>http://Passport.NET/STS</ds:KeyName>"\
+				"</ds:KeyInfo>"\
+				"<CipherData>"\
+					"<CipherValue>%s</CipherValue>"\
+				"</CipherData>"\
+			"</EncryptedData>"\
+			"<wssc:DerivedKeyToken Id=\"SignKey\">"\
+				"<wsse:RequestedTokenReference>"\
+					"<wsse:KeyIdentifier ValueType=\"http://docs.oasis-open.org/wss/2004/XX/oasis-2004XX-wss-saml-token-profile-1.0#SAMLAssertionID\" />"\
+					"<wsse:Reference URI=\"#BinaryDAToken0\" />"\
+				"</wsse:RequestedTokenReference>"\
+				"<wssc:Nonce>%s</wssc:Nonce>"\
+			"</wssc:DerivedKeyToken>"\
+			"%s" /* wsu:Timestamp */\
+			"<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"\
+				"%s" /* SignedInfo */\
+				"<SignatureValue>%s</SignatureValue>"\
+				"<KeyInfo>"\
+					"<wsse:SecurityTokenReference>"\
+						"<wsse:Reference URI=\"#SignKey\" />"\
+					"</wsse:SecurityTokenReference>"\
+				"</KeyInfo>"\
+			"</Signature>"\
+		"</wsse:Security>"\
+	"</Header>"\
+	"<Body>"\
+		"%s" /* wst:RequestSecurityToken */ \
+	"</Body>"\
+"</Envelope>"
+
 typedef struct _MsnUsrKey MsnUsrKey;
 struct _MsnUsrKey
 {
@@ -123,9 +213,14 @@
 struct _MsnNexus
 {
 	MsnSession *session;
+
+	/* From server via USR command */
 	char *policy;
 	char *nonce;
 
+	/* From server via SOAP stuff */
+	char *cipher;
+	char *secret;
 	MsnTicketToken *tokens;
 	int token_len;
 };
@@ -133,8 +228,8 @@
 void msn_nexus_connect(MsnNexus *nexus);
 MsnNexus *msn_nexus_new(MsnSession *session);
 void msn_nexus_destroy(MsnNexus *nexus);
-GHashTable *msn_nexus_get_token(MsnNexus *session, MsnAuthDomains id);
-const char *msn_nexus_get_token_str(MsnNexus *session, MsnAuthDomains id);
-
+GHashTable *msn_nexus_get_token(MsnNexus *nexus, MsnAuthDomains id);
+const char *msn_nexus_get_token_str(MsnNexus *nexus, MsnAuthDomains id);
+void msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data);
 #endif /* _MSN_NEXUS_H_ */
 
--- a/libpurple/protocols/msn/session.c	Sat Jun 07 04:32:25 2008 +0000
+++ b/libpurple/protocols/msn/session.c	Sat Jun 07 06:08:01 2008 +0000
@@ -72,8 +72,6 @@
 	msn_userlist_destroy(session->userlist);
 
 	g_free(session->psm);
-	g_free(session->passport_info.t);
-	g_free(session->passport_info.p);
 	g_free(session->passport_info.kv);
 	g_free(session->passport_info.sid);
 	g_free(session->passport_info.mspauth);
--- a/libpurple/protocols/msn/session.h	Sat Jun 07 04:32:25 2008 +0000
+++ b/libpurple/protocols/msn/session.h	Sat Jun 07 06:08:01 2008 +0000
@@ -110,10 +110,6 @@
 
 	struct
 	{
-		/*t and p, get via USR TWN*/
-		char *t;
-		char *p;
-
 		char *kv;
 		char *sid;
 		char *mspauth;