changeset 23489:25899ec348a4

Patch 2 from Qulogic, this one adds SSO authentication committer: Ka-Hing Cheung <khc@hxbc.us>
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Wed, 26 Dec 2007 00:34:12 +0000
parents 75be80ddeca5
children b70f30dd4753
files libpurple/protocols/msn/nexus.c libpurple/protocols/msn/nexus.h libpurple/protocols/msn/notification.c
diffstat 3 files changed, 435 insertions(+), 247 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/msn/nexus.c	Wed Dec 26 00:33:39 2007 +0000
+++ b/libpurple/protocols/msn/nexus.c	Wed Dec 26 00:34:12 2007 +0000
@@ -26,7 +26,24 @@
 #include "nexus.h"
 #include "notification.h"
 
-#undef NEXUS_LOGIN_TWN
+
+/**************************************************************************
+ * Valid Ticket Tokens
+ **************************************************************************/
+
+#define SSO_VALID_TICKET_DOMAIN 0
+#define SSO_VALID_TICKET_POLICY 1
+static char *ticket_domains[][2] = {
+	/* http://msnpiki.msnfanatic.com/index.php/MSNP15:SSO */
+	/* {"Domain", "Policy Ref URI"}, Purpose */
+	{"messengerclear.live.com", NULL},       /* Authentication for messenger. */
+	{"messenger.msn.com", "?id=507"},        /* Messenger website authentication. */
+	{"contacts.msn.com", "MBI"},             /* Authentication for the Contact server. */
+	{"messengersecure.live.com", "MBI_SSL"}, /* Unknown */
+	{"spaces.live.com", "MBI"},              /* Authentication for the Windows Live Spaces */
+	{"livecontacts.live.com", "MBI"},        /* Live Contacts API, a simplified version of the Contacts SOAP service */
+	{"storage.live.com", "MBI"},             /* Storage REST API */
+};
 
 /**************************************************************************
  * Main
@@ -36,12 +53,17 @@
 msn_nexus_new(MsnSession *session)
 {
 	MsnNexus *nexus;
+	int i;
 
 	nexus = g_new0(MsnNexus, 1);
 	nexus->session = session;
 
-	nexus->challenge_data = g_hash_table_new_full(g_str_hash,
-		g_str_equal, g_free, g_free);
+	nexus->token_len = sizeof(ticket_domains) / sizeof(char *[2]);
+	nexus->tokens = g_malloc(sizeof(MsnTicketToken) * nexus->token_len);
+
+	for (i = 0; i < nexus->token_len; i++)
+		nexus->tokens[i].token = g_hash_table_new_full(g_str_hash, g_str_equal,
+		                                               g_free, g_free);
 
 	return nexus;
 }
@@ -49,47 +71,198 @@
 void
 msn_nexus_destroy(MsnNexus *nexus)
 {
-	if (nexus->challenge_data != NULL)
-		g_hash_table_destroy(nexus->challenge_data);
+	int i;
+	for (i = 0; i < nexus->token_len; i++) {
+		g_hash_table_destroy(nexus->tokens[i].token);
+		g_free(nexus->tokens[i].secret);
+	}
+
+	g_free(nexus->tokens);
+	g_free(nexus->policy);
+	g_free(nexus->nonce);
+	g_free(nexus);
+}
+
+/**************************************************************************
+ * RPS/SSO Authentication
+ **************************************************************************/
+
+static char *
+sha1_hmac(const char *key, int key_len, const char *message, int msg_len)
+{
+	PurpleCipherContext *context;
+	char *result;
+	gboolean ret;
+
+	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_append(context, (guchar *)message, msg_len);
+	result = g_malloc(20);
+	ret = purple_cipher_context_digest(context, 20, (guchar *)result, NULL);
+
+	purple_cipher_context_destroy(context);
+
+	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)
+{
+	PurpleCipherContext *des3;
+	char *out;
+	size_t outlen;
 
-	g_free(nexus);
+	des3 = purple_cipher_context_new_by_name("des3", NULL);
+	purple_cipher_context_set_key(des3, (guchar *)key);
+	purple_cipher_context_set_batch_mode(des3, PURPLE_CIPHER_BATCH_MODE_CBC);
+	purple_cipher_context_set_iv(des3, (guchar *)iv, 8);
+
+	out = g_malloc(len);
+	purple_cipher_context_encrypt(des3, (guchar *)data, len, (guchar *)out, &outlen);
+
+	purple_cipher_context_destroy(des3);
+
+	return out;
+}
+
+#define CRYPT_MODE_CBC 1
+#define CIPHER_TRIPLE_DES 0x6603
+#define HASH_SHA1 0x8004
+static char *
+msn_rps_encrypt(MsnNexus *nexus)
+{
+	MsnUsrKey *usr_key;
+	const char *magic1 = "WS-SecureConversationSESSION KEY HASH";
+	const char *magic2 = "WS-SecureConversationSESSION KEY ENCRYPTION";
+	size_t len;
+	char *hash;
+	char *key1, *key2, *key3;
+	gsize key1_len;
+	char *nonce_fixed;
+	char *cipher;
+	char *response;
+
+	usr_key = g_malloc(sizeof(MsnUsrKey));
+	usr_key->size = GUINT32_TO_LE(28);
+	usr_key->crypt_mode = GUINT32_TO_LE(CRYPT_MODE_CBC);
+	usr_key->cipher_type = GUINT32_TO_LE(CIPHER_TRIPLE_DES);
+	usr_key->hash_type = GUINT32_TO_LE(HASH_SHA1);
+	usr_key->iv_len = GUINT32_TO_LE(8);
+	usr_key->hash_len = GUINT32_TO_LE(20);
+	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);
+
+	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;
+
+	len = strlen(nexus->nonce);
+	hash = sha1_hmac(key2, 24, nexus->nonce, len);
+
+	/* 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);
+	g_free(nonce_fixed);
+
+	memcpy(usr_key->hash, hash, 20);
+	memcpy(usr_key->cipher, cipher, 72);
+
+	g_free(key1);
+	g_free(key2);
+	g_free(key3);
+	g_free(hash);
+	g_free(cipher);
+
+	response = purple_base64_encode((guchar *)usr_key, sizeof(MsnUsrKey));
+
+	g_free(usr_key);
+
+	return response;
 }
 
 /**************************************************************************
  * Login
  **************************************************************************/
 
-static void
-nexus_got_response_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
+static gboolean
+nexus_parse_response(MsnNexus *nexus, xmlnode *xml)
 {
-	MsnNexus *nexus = data;
-	MsnSession *session = nexus->session;
 	xmlnode *node;
+	gboolean result = FALSE;
 
-	if (resp == NULL) {
-		msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Unable to connect"));
-		return;
-	}
+	node = msn_soap_xml_get(xml, "Body/RequestSecurityTokenResponseCollection/RequestSecurityTokenResponse");
 
-	node = msn_soap_xml_get(resp->xml,	"Body/"
-		"RequestSecurityTokenResponseCollection/RequestSecurityTokenResponse");
+	if (node)
+		node = node->next;	/* The first one is not useful */
+	else
+		return FALSE;
 
 	for (; node; node = node->next) {
-		xmlnode *token = msn_soap_xml_get(node,
-			"RequestedSecurityToken/BinarySecurityToken");
+		xmlnode *token = msn_soap_xml_get(node, "RequestedSecurityToken/BinarySecurityToken");
+		xmlnode *secret = msn_soap_xml_get(node, "RequestedProofToken/BinarySecret");
+		xmlnode *expires = msn_soap_xml_get(node, "LifeTime/Expires");
 
 		if (token) {
 			char *token_str = xmlnode_get_data(token);
+			const char *id_str = xmlnode_get_attrib(token, "Id");
 			char **elems, **cur, **tokens;
-			char *msn_twn_t, *msn_twn_p, *cert_str;
+			int id;
 
 			if (token_str == NULL) continue;
+			if (id_str == NULL) continue;
+
+			id = atol(id_str + 7) - 1;	/* 'Compact#' or 'PPToken#' */
+			if (id >= nexus->token_len)
+				continue;	/* Where did this come from? */
 
 			elems = g_strsplit(token_str, "&", 0);
 
 			for (cur = elems; *cur != NULL; cur++){
 				tokens = g_strsplit(*cur, "=", 2);
-				g_hash_table_insert(nexus->challenge_data, tokens[0], tokens[1]);
+				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);
 			}
@@ -97,33 +270,55 @@
 			g_free(token_str);
 			g_strfreev(elems);
 
-			msn_twn_t = g_hash_table_lookup(nexus->challenge_data, "t");
-			msn_twn_p = g_hash_table_lookup(nexus->challenge_data, "p");
-
-			/*setup the t and p parameter for session*/
-			if (session->passport_info.t != NULL){
-				g_free(session->passport_info.t);
-			}
-			session->passport_info.t = g_strdup(msn_twn_t);
+			if (secret)
+				nexus->tokens[id].secret = g_strdup(xmlnode_get_data(secret));
+			else
+				nexus->tokens[id].secret = NULL;
 
-			if (session->passport_info.p != NULL)
-				g_free(session->passport_info.p);
-			session->passport_info.p = g_strdup(msn_twn_p);
-
-			cert_str = g_strdup_printf("t=%s&p=%s",msn_twn_t,msn_twn_p);
-			msn_got_login_params(session, cert_str);
+			/* Yay for MS using ISO-8601 */
+			nexus->tokens[id].expiry = purple_str_to_time(xmlnode_get_data(expires),
+			                                              FALSE, NULL, NULL, NULL);
 
-			purple_debug_info("MSN Nexus","Close nexus connection!\n");
-			g_free(cert_str);
-			msn_nexus_destroy(nexus);
-			session->nexus = NULL;
-
-			return;
+			purple_debug_info("msnp15", "Updated ticket for domain '%s'\n",
+			                  ticket_domains[id][SSO_VALID_TICKET_DOMAIN]);
+			result = TRUE;
 		}
 	}
 
-	/* we must have failed! */
-	msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication: cannot find authenticate token in server response"));
+	return result;
+}
+
+static void
+nexus_got_response_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
+{
+	MsnNexus *nexus = data;
+	MsnSession *session = nexus->session;
+	char *msn_twn_t, *msn_twn_p, *ticket;
+	char *response;
+
+	if (resp == NULL) {
+		msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Unable to connect"));
+		return;
+	}
+
+	if (!nexus_parse_response(nexus, 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);
+	response = msn_rps_encrypt(nexus);
+	msn_got_login_params(session, ticket, response);
+	g_free(ticket);
+	g_free(response);
 }
 
 /*when connect, do the SOAP Style windows Live ID authentication */
@@ -131,92 +326,113 @@
 msn_nexus_connect(MsnNexus *nexus)
 {
 	MsnSession *session = nexus->session;
-	char *ru,*lc,*id,*tw,*ct,*kpp,*kv,*ver,*rn,*tpf;
-	char *fs0,*fs;
 	char *username, *password;
-	char *tail;
-#ifdef NEXUS_LOGIN_TWN
-	char *challenge_str;
-#else
-	char *rst1_str,*rst2_str,*rst3_str;
-#endif
+	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);
 
-	/*prepare the Windows Live ID authentication token*/
 	username = g_strdup(purple_account_get_username(session->account));
 	password = g_strndup(purple_connection_get_password(session->account->gc), 16);
 
-	lc =	(char *)g_hash_table_lookup(nexus->challenge_data, "lc");
-	id =	(char *)g_hash_table_lookup(nexus->challenge_data, "id");
-	tw =	(char *)g_hash_table_lookup(nexus->challenge_data, "tw");
-	fs0=	(char *)g_hash_table_lookup(nexus->challenge_data, "fs");
-	ru =	(char *)g_hash_table_lookup(nexus->challenge_data, "ru");
-	ct =	(char *)g_hash_table_lookup(nexus->challenge_data, "ct");
-	kpp=	(char *)g_hash_table_lookup(nexus->challenge_data, "kpp");
-	kv =	(char *)g_hash_table_lookup(nexus->challenge_data, "kv");
-	ver=	(char *)g_hash_table_lookup(nexus->challenge_data, "ver");
-	rn =	(char *)g_hash_table_lookup(nexus->challenge_data, "rn");
-	tpf=	(char *)g_hash_table_lookup(nexus->challenge_data, "tpf");
+	purple_debug_info("msnp15", "Logging on %s, with policy '%s', nonce '%s'\n",
+	                  username, nexus->policy, nexus->nonce);
 
-	/*
-	 * add some fail-safe code to avoid windows Purple Crash bug #1540454
-	 * If any of these string is NULL, will return Authentication Fail!
-	 * for when windows g_strdup_printf() implementation get NULL point,It crashed!
-	 */
-	if(!(lc && id && tw && ru && ct && kpp && kv && ver && tpf)){
-		purple_debug_error("MSN Nexus","WLM Authenticate Key Error!\n");
-		msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication Failed"));
-		g_free(username);
-		g_free(password);
-		msn_nexus_destroy(nexus);
-		session->nexus = NULL;
-		return;
+	domains = g_string_new(NULL);
+	for (i = 0; i < nexus->token_len; i++) {
+		g_string_append_printf(domains, MSN_SSO_RST_TEMPLATE,
+		                       i+1,
+		                       ticket_domains[i][SSO_VALID_TICKET_DOMAIN],
+		                       ticket_domains[i][SSO_VALID_TICKET_POLICY] != NULL ?
+		                           ticket_domains[i][SSO_VALID_TICKET_POLICY] :
+		                           nexus->policy);
 	}
 
-	/*
-	 * in old MSN NS server's "USR TWN S" return,didn't include fs string
-	 * so we use a default "1" for fs.
-	 */
-	if(fs0){
-		fs = g_strdup(fs0);
-	}else{
-		fs = g_strdup("1");
-	}
-
-#ifdef NEXUS_LOGIN_TWN
-	challenge_str = g_strdup_printf(
-		"lc=%s&amp;id=%s&amp;tw=%s&amp;fs=%s&amp;ru=%s&amp;ct=%s&amp;kpp=%s&amp;kv=%s&amp;ver=%s&amp;rn=%s&amp;tpf=%s\r\n",
-		lc,id,tw,fs,ru,ct,kpp,kv,ver,rn,tpf
-		);
+	request = g_strdup_printf(MSN_SSO_TEMPLATE, username, password, domains->str);
+	g_free(username);
+	g_free(password);
+	g_string_free(domains, TRUE);
 
-	/*build the SOAP windows Live ID XML body */
-	tail = g_strdup_printf(TWN_ENVELOP_TEMPLATE, username, password, challenge_str);
-	g_free(challenge_str);
-#else
-	rst1_str = g_strdup_printf(
-		"id=%s&amp;tw=%s&amp;fs=%s&amp;kpp=%s&amp;kv=%s&amp;ver=%s&amp;rn=%s",
-		id,tw,fs,kpp,kv,ver,rn
-		);
-	rst2_str = g_strdup_printf(
-		"fs=%s&amp;id=%s&amp;kv=%s&amp;rn=%s&amp;tw=%s&amp;ver=%s",
-		fs,id,kv,rn,tw,ver
-		);
-	rst3_str = g_strdup_printf("id=%s",id);
-	tail = g_strdup_printf(TWN_LIVE_ENVELOP_TEMPLATE,username,password,rst1_str,rst2_str,rst3_str);
-	g_free(rst1_str);
-	g_free(rst2_str);
-	g_free(rst3_str);
-#endif
-	g_free(fs);
-	g_free(password);
+	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_response_cb, nexus);
+}
 
-	soap = msn_soap_message_new(NULL, xmlnode_from_str(tail, -1));
-	g_free(tail);
-	msn_soap_message_send(nexus->session, soap, MSN_TWN_SERVER, TWN_POST_URL,
-		nexus_got_response_cb, nexus);
+static void
+nexus_got_update_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
+{
+	MsnNexus *nexus = data;
+
+	nexus_parse_response(nexus, resp->xml);
 }
 
+static void
+msn_nexus_update_token(MsnNexus *nexus, int id)
+{
+	MsnSession *session = nexus->session;
+	char *username, *password;
+	char *domain;
+	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",
+	                  username, ticket_domains[id][SSO_VALID_TICKET_DOMAIN]);
+
+	/* TODO: This really assumes if we send RSTn, the server responds with
+	 Compactn, even if there is no RST(n-1). This needs checking.
+	*/
+	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);
+
+	request = g_strdup_printf(MSN_SSO_TEMPLATE, username, password, domain);
+	g_free(username);
+	g_free(password);
+	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);
+}
+
+GHashTable *
+msn_nexus_get_token(MsnNexus *nexus, MsnAuthDomains id)
+{
+	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 g_hash_table_ref(nexus->tokens[id].token);
+}
+
+char *
+msn_nexus_get_token_str(MsnNexus *session, MsnAuthDomains id)
+{
+#if 0
+	GHashTable *token = msn_nexus_get_token(nexus, id);
+	GString *token_str;
+
+	g_return_val_if_fail(token != NULL, NULL);
+
+	token_str = g_string_new(NULL);
+	g_hash_table_foreach(token, GHFunc func, token_str);
+
+	g_hash_table_unref (token);
+#endif
+	return NULL;
+}
+
--- a/libpurple/protocols/msn/nexus.h	Wed Dec 26 00:33:39 2007 +0000
+++ b/libpurple/protocols/msn/nexus.h	Wed Dec 26 00:34:12 2007 +0000
@@ -26,125 +26,115 @@
 
 #include "soap.h"
 
-/*#define MSN_TWN_SERVER	"loginnet.passport.com"*/
-#define MSN_TWN_SERVER	"login.live.com"
-
-#define TWN_START_TOKEN		"<wsse:BinarySecurityToken Id=\"PPToken1\">"
-#define TWN_END_TOKEN		"</wsse:BinarySecurityToken>"
+/* Index into ticket_tokens in nexus.c Keep updated! */
+typedef enum
+{
+	MSN_AUTH_MESSENGER     = 0,
+	MSN_AUTH_MESSENGER_WEB = 1,
+	MSN_AUTH_CONTACTS      = 2,
+	MSN_AUTH_LIVE_UNKNOWN  = 3,
+	MSN_AUTH_SPACES        = 4,
+	MSN_AUTH_LIVE_CONTACTS = 5,
+	MSN_AUTH_STORAGE       = 6
+} MsnAuthDomains;
 
-#define TWN_POST_URL			"/RST.srf"
-#define TWN_ENVELOP_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>"\
-						"<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">"\
-						"<ps:HostingApp>{3:B}</ps:HostingApp>"\
-						"<ps:BinaryVersion>4</ps:BinaryVersion>"\
-						"<ps:UIVersion>1</ps:UIVersion>"\
-						"<ps:Cookies></ps:Cookies>"\
-						"<ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>"\
-						"</ps:AuthInfo>"\
-						"<wsse:Security>"\
-						"<wsse:UsernameToken Id=\"user\">"\
-						"<wsse:Username>%s</wsse:Username>"\
-						"<wsse:Password>%s</wsse:Password>"\
-						"</wsse:UsernameToken>"\
-						"</wsse:Security>"\
-						"</Header>"\
-						"<Body>"\
-						"<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">"\
-						"<wst:RequestSecurityToken Id=\"RST0\">"\
-						"<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
-						"<wsp:AppliesTo>"\
-						"<wsa:EndpointReference>"\
-						"<wsa:Address>http://Passport.NET/tb</wsa:Address>"\
-						"</wsa:EndpointReference>"\
-						"</wsp:AppliesTo>"\
-						"</wst:RequestSecurityToken>"\
-						"<wst:RequestSecurityToken Id=\"RST1\">"\
-						"<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
-						"<wsp:AppliesTo>"\
-						"<wsa:EndpointReference>"\
-						"<wsa:Address>messenger.msn.com</wsa:Address>"\
-						"</wsa:EndpointReference>"\
-						"</wsp:AppliesTo>"\
-						"<wsse:PolicyReference URI=\"?%s\">"\
-						"</wsse:PolicyReference>"\
-						"</wst:RequestSecurityToken>"\
-						"</ps:RequestMultipleSecurityTokens>"\
-						"</Body>"\
-						"</Envelope>"
+#define MSN_SSO_SERVER	"login.live.com"
+#define SSO_POST_URL	"/RST.srf"
+
+#define MSN_SSO_RST_TEMPLATE \
+"<wst:RequestSecurityToken Id=\"RST%d\">"\
+	"<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+	"<wsp:AppliesTo>"\
+		"<wsa:EndpointReference>"\
+			"<wsa:Address>%s</wsa:Address>"\
+		"</wsa:EndpointReference>"\
+	"</wsp:AppliesTo>"\
+	"<wsse:PolicyReference URI=\"%s\"></wsse:PolicyReference>"\
+"</wst:RequestSecurityToken>"
 
-#define TWN_LIVE_START_TOKEN	"<wsse:BinarySecurityToken Id=\"PPToken1\">"
-#define TWN_LIVE_END_TOKEN	"</wsse:BinarySecurityToken>"
-#define TWN_LIVE_ENVELOP_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>"\
-    "<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>AQAAAAIAAABsYwQAAAAyMDUy</ps:RequestParams>"\
-    "</ps:AuthInfo>"\
-    "<wsse:Security>"\
-      "<wsse:UsernameToken Id=\"user\">"\
-        "<wsse:Username>%s</wsse:Username>"\
-        "<wsse:Password>%s</wsse:Password>"\
-      "</wsse:UsernameToken>"\
-    "</wsse:Security>"\
-  "</Header>"\
-  "<Body>"\
-    "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">"\
-      "<wst:RequestSecurityToken Id=\"RST0\">"\
-        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
-        "<wsp:AppliesTo>"\
-          "<wsa:EndpointReference>"\
-            "<wsa:Address>http://Passport.NET/tb</wsa:Address>"\
-          "</wsa:EndpointReference>"\
-        "</wsp:AppliesTo>"\
-      "</wst:RequestSecurityToken>"\
-      "<wst:RequestSecurityToken Id=\"RST1\">"\
-        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
-        "<wsp:AppliesTo>"\
-          "<wsa:EndpointReference>"\
-            "<wsa:Address>messenger.msn.com</wsa:Address>"\
-          "</wsa:EndpointReference>"\
-        "</wsp:AppliesTo>"\
-        "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\
-      "</wst:RequestSecurityToken>"\
-      "<wst:RequestSecurityToken Id=\"RST2\">"\
-        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
-        "<wsp:AppliesTo>"\
-          "<wsa:EndpointReference>"\
-            "<wsa:Address>contacts.msn.com</wsa:Address>"\
-         "</wsa:EndpointReference>"\
-        "</wsp:AppliesTo>"\
-       "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\
-     " </wst:RequestSecurityToken>"\
-      "<wst:RequestSecurityToken Id=\"RST3\">"\
-        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
-        "<wsp:AppliesTo>"\
-          "<wsa:EndpointReference>"\
-            "<wsa:Address>voice.messenger.msn.com</wsa:Address>"\
-          "</wsa:EndpointReference>"\
-       " </wsp:AppliesTo>"\
-        "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\
-      "</wst:RequestSecurityToken>"\
-    "</ps:RequestMultipleSecurityTokens>"\
-  "</Body>"\
+#define MSN_SSO_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>"\
+		"<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>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>"\
+		"</ps:AuthInfo>"\
+		"<wsse:Security>"\
+			"<wsse:UsernameToken Id=\"user\">"\
+				"<wsse:Username>%s</wsse:Username>"\
+				"<wsse:Password>%s</wsse:Password>"\
+			"</wsse:UsernameToken>"\
+		"</wsse:Security>"\
+	"</Header>"\
+	"<Body>"\
+		"<ps:RequestMultipleSecurityTokens"\
+			" xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\""\
+			" Id=\"RSTS\">"\
+			"<wst:RequestSecurityToken Id=\"RST0\">"\
+				"<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+				"<wsp:AppliesTo>"\
+					"<wsa:EndpointReference>"\
+						"<wsa:Address>http://Passport.NET/tb</wsa:Address>"\
+					"</wsa:EndpointReference>"\
+				"</wsp:AppliesTo>"\
+			"</wst:RequestSecurityToken>"\
+			"%s"	/* Other RSTn tokens */\
+		"</ps:RequestMultipleSecurityTokens>"\
+	"</Body>"\
 "</Envelope>"
 
+typedef struct _MsnUsrKey MsnUsrKey;
+struct _MsnUsrKey
+{
+	int size; // 28. Does not count data
+	int crypt_mode; // CRYPT_MODE_CBC (1)
+	int cipher_type; // TripleDES (0x6603)
+	int hash_type; // SHA1 (0x8004)
+	int iv_len;    // 8
+	int hash_len;  // 20
+	int cipher_len; // 72
+	// Data
+	char iv[8];
+	char hash[20];
+	char cipher[72];
+};
+
+typedef struct _MsnTicketToken MsnTicketToken;
+struct _MsnTicketToken {
+	GHashTable *token;
+	char *secret;
+	time_t expiry;
+};
+
 typedef struct _MsnNexus MsnNexus;
 
 struct _MsnNexus
 {
 	MsnSession *session;
-	char * challenge_data_str;
-	GHashTable *challenge_data;
+	char *policy;
+	char *nonce;
+
+	MsnTicketToken *tokens;
+	int token_len;
 };
 
 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);
+char *msn_nexus_get_token_str(MsnNexus *session, MsnAuthDomains id);
 
 #endif /* _MSN_NEXUS_H_ */
+
--- a/libpurple/protocols/msn/notification.c	Wed Dec 26 00:33:39 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Wed Dec 26 00:34:12 2007 +0000
@@ -204,7 +204,7 @@
  **************************************************************************/
 
 void
-msn_got_login_params(MsnSession *session, const char *login_params)
+msn_got_login_params(MsnSession *session, const char *ticket, const char *response)
 {
 	MsnCmdProc *cmdproc;
 
@@ -212,7 +212,7 @@
 
 	msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END);
 
-	msn_cmdproc_send(cmdproc, "USR", "TWN S %s", login_params);
+	msn_cmdproc_send(cmdproc, "USR", "SSO S %s %s", ticket, response);
 }
 
 static void
@@ -221,8 +221,8 @@
 	PurpleAccount *account;
 
 	account = cmdproc->session->account;
-	msn_cmdproc_send(cmdproc, "USR", "TWN I %s",
-					 purple_account_get_username(account));
+
+	msn_cmdproc_send(cmdproc, "USR", "SSO I %s", purple_account_get_username(account));
 }
 
 static void
@@ -248,32 +248,14 @@
 //		msn_cmdproc_send(cmdproc, "SYN", "%s", "0");
 		//TODO we should use SOAP contact to fetch contact list
 	}
-	else if (!g_ascii_strcasecmp(cmd->params[1], "TWN"))
+	else if (!g_ascii_strcasecmp(cmd->params[1], "SSO"))
 	{
-		/* Passport authentication */
-		char **elems, **cur, **tokens;
+		/* RPS authentication */
 
 		session->nexus = msn_nexus_new(session);
 
-		/* Parse the challenge data. */
-		session->nexus->challenge_data_str = g_strdup(cmd->params[3]);
-		elems = g_strsplit(cmd->params[3], ",", 0);
-
-		for (cur = elems; *cur != NULL; cur++)
-		{
-			tokens = g_strsplit(*cur, "=", 2);
-			if(tokens[0] && tokens[1])
-			{
-				purple_debug_info("MSNP14","challenge %p,key:%s,value:%s\n",
-									session->nexus->challenge_data,tokens[0],tokens[1]);
-				g_hash_table_insert(session->nexus->challenge_data, tokens[0], tokens[1]);
-				/* Don't free each of the tokens, only the array. */
-				g_free(tokens);
-			} else
-				g_strfreev(tokens);
-		}
-
-		g_strfreev(elems);
+		session->nexus->policy = g_strdup(cmd->params[3]);
+		session->nexus->nonce = g_strdup(cmd->params[4]);
 
 		msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_START);