diff options
Diffstat (limited to 'protocols')
| -rw-r--r-- | protocols/msn/Makefile | 2 | ||||
| -rw-r--r-- | protocols/msn/msn.c | 3 | ||||
| -rw-r--r-- | protocols/msn/msn.h | 3 | ||||
| -rw-r--r-- | protocols/msn/msn_util.c | 41 | ||||
| -rw-r--r-- | protocols/msn/ns.c | 32 | ||||
| -rw-r--r-- | protocols/msn/sb.c | 6 | ||||
| -rw-r--r-- | protocols/msn/soap.c | 175 | ||||
| -rw-r--r-- | protocols/msn/soap.h | 71 | 
8 files changed, 269 insertions, 64 deletions
| diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index b9c7ed28..28fe81c9 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -42,4 +42,4 @@ msn_mod.o: $(objects)  	@echo '*' Linking msn_mod.o  	@$(LD) $(LFLAGS) $(objects) -o msn_mod.o - +soap.o: soap.h soap.c diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 6222e1b6..10c27a1d 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -102,7 +102,8 @@ static void msn_logout( struct im_connection *ic )  		while( md->groupcount > 0 )  			g_free( md->grouplist[--md->groupcount] );  		g_free( md->grouplist ); -		g_free( md->passport_token ); +		g_free( md->tokens[0] ); +		g_free( md->tokens[1] );  		g_free( md->lock_key );  		while( md->grpq ) diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index f060000a..d4f3442e 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -75,7 +75,7 @@ struct msn_data  	struct msn_handler_data *handler;  	int trId; -	char *passport_token; +	char *tokens[2];  	char *lock_key;  	GSList *msgq, *grpq; @@ -170,6 +170,7 @@ extern GSList *msn_switchboards;  /* ns.c */  gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ); +void msn_auth_got_passport_token( struct im_connection *ic, char *token );  /* msn_util.c */  int msn_write( struct im_connection *ic, char *s, int len ); diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index 954ee716..65b12476 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -33,6 +33,12 @@ int msn_write( struct im_connection *ic, char *s, int len )  	struct msn_data *md = ic->proto_data;  	int st; +	if( getenv( "BITLBEE_DEBUG" ) ) +	{ +		write( 2, "->NS:", 5 ); +		write( 2, s, len ); +	} +	  	st = write( md->fd, s, len );  	if( st != len )  	{ @@ -280,6 +286,12 @@ int msn_handler( struct msn_handler_data *h )  	if( st <= 0 )  		return( -1 ); +	if( getenv( "BITLBEE_DEBUG" ) ) +	{ +		write( 2, "->C:", 4 ); +		write( 2, h->rxq + h->rxlen - st, st ); +	} +	  	while( st )  	{  		int i; @@ -445,27 +457,6 @@ gboolean msn_set_display_name( struct im_connection *ic, const char *rawname )  	return msn_write( ic, buf, strlen( buf ) ) != 0;  } -unsigned int little_endian( unsigned int dw ) -{ -#if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN -	return dw; -#else -	/* We're still not sure if this machine is big endian since the -	   constants above are not that portable. Don't swap bytes, just -	   force-compose a 32-bit little endian integer. */ -	unsigned int ret = 0, i; -	char *dst = (char*) (&ret + 1); -	 -	for (i = 0; i < 4; i ++) -	{ -		*(--dst) = dw >> 24; -		dw <<= 8; -	} -	 -	return ret; -#endif -} -  /* Copied and heavily modified from http://tmsnc.sourceforge.net/chl.c */  char *msn_p11_challenge( char *challenge )  { @@ -486,7 +477,7 @@ char *msn_p11_challenge( char *challenge )  	md5Parts = (unsigned int *)md5Hash;  	for (i = 0; i < 4; i ++)  	{   -		md5Parts[i] = little_endian(md5Parts[i]); +		md5Parts[i] = GUINT32_TO_LE(md5Parts[i]);  		/* & each integer with 0x7FFFFFFF */  		/* and save one unmodified array for later */ @@ -507,8 +498,8 @@ char *msn_p11_challenge( char *challenge )  	{  		long long temp; -		chlStringParts[i]   = little_endian(chlStringParts[i]); -		chlStringParts[i+1] = little_endian(chlStringParts[i+1]); +		chlStringParts[i]   = GUINT32_TO_LE(chlStringParts[i]); +		chlStringParts[i+1] = GUINT32_TO_LE(chlStringParts[i+1]);  		temp  = (md5Parts[0] * (((0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF)+nHigh) + md5Parts[1])%0x7FFFFFFF;  		nHigh = (md5Parts[2] * (((long long)chlStringParts[i+1]+temp) % 0x7FFFFFFF) + md5Parts[3]) % 0x7FFFFFFF; @@ -524,7 +515,7 @@ char *msn_p11_challenge( char *challenge )  	/* swap more bytes if big endian */  	for (i = 0; i < 4; i ++) -		newHashParts[i] = little_endian(newHashParts[i]);  +		newHashParts[i] = GUINT32_TO_LE(newHashParts[i]);   	/* make a string of the parts */  	newHash = (unsigned char *)newHashParts; diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 8236b731..f0d6a58d 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -34,7 +34,6 @@ static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition c  static int msn_ns_command( gpointer data, char **cmd, int num_parts );  static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ); -static void msn_auth_got_passport_token( struct msn_auth_data *mad );  static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name );  gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ) @@ -73,7 +72,7 @@ gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )  	md->handler->fd = md->fd;  	md->handler->rxq = g_new0( char, 1 ); -	g_snprintf( s, sizeof( s ), "VER %d MSNP14 CVR0\r\n", ++md->trId ); +	g_snprintf( s, sizeof( s ), "VER %d MSNP15 CVR0\r\n", ++md->trId );  	if( msn_write( ic, s, strlen( s ) ) )  	{  		ic->inpa = b_input_add( md->fd, B_EV_IO_READ, msn_ns_callback, ic ); @@ -113,7 +112,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  	if( strcmp( cmd[0], "VER" ) == 0 )  	{ -		if( cmd[2] && strncmp( cmd[2], "MSNP14", 5 ) != 0 ) +		if( cmd[2] && strncmp( cmd[2], "MSNP15", 5 ) != 0 )  		{  			imcb_error( ic, "Unsupported protocol" );  			imc_logout( ic, FALSE ); @@ -127,7 +126,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  	else if( strcmp( cmd[0], "CVR" ) == 0 )  	{  		/* We don't give a damn about the information we just received */ -		g_snprintf( buf, sizeof( buf ), "USR %d TWN I %s\r\n", ++md->trId, ic->acc->user ); +		g_snprintf( buf, sizeof( buf ), "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user );  		return( msn_write( ic, buf, strlen( buf ) ) );  	}  	else if( strcmp( cmd[0], "XFR" ) == 0 ) @@ -220,15 +219,10 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  	}  	else if( strcmp( cmd[0], "USR" ) == 0 )  	{ -		if( num_parts == 5 && strcmp( cmd[2], "TWN" ) == 0 && strcmp( cmd[3], "S" ) == 0 ) +		if( num_parts >= 6 && strcmp( cmd[2], "SSO" ) == 0 && +		    strcmp( cmd[3], "S" ) == 0 )  		{ -			/* Time for some Passport black magic... */ -			if( !passport_get_token( msn_auth_got_passport_token, ic, ic->acc->user, ic->acc->pass, cmd[4] ) ) -			{ -				imcb_error( ic, "Error while contacting Passport server" ); -				imc_logout( ic, TRUE ); -				return( 0 ); -			} +			msn_soap_passport_sso_request( ic, cmd[4], cmd[5] );  		}  		else if( strcmp( cmd[2], "OK" ) == 0 )  		{ @@ -774,9 +768,8 @@ static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int  	return( 1 );  } -static void msn_auth_got_passport_token( struct msn_auth_data *mad ) +void msn_auth_got_passport_token( struct im_connection *ic, char *token )  { -	struct im_connection *ic = mad->data;  	struct msn_data *md;  	/* Dead connection? */ @@ -784,20 +777,13 @@ static void msn_auth_got_passport_token( struct msn_auth_data *mad )  		return;  	md = ic->proto_data; -	if( mad->token ) +	  	{  		char buf[1024]; -		md->passport_token = g_strdup( mad->token ); -		 -		g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, mad->token ); +		g_snprintf( buf, sizeof( buf ), "USR %d SSO S %s %s\r\n", ++md->trId, md->tokens[0], token );  		msn_write( ic, buf, strlen( buf ) );  	} -	else -	{ -		imcb_error( ic, "Error during Passport authentication: %s", mad->error ); -		imc_logout( ic, TRUE ); -	}  }  static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name ) diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 1eb919e3..07e94072 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -39,6 +39,12 @@ int msn_sb_write( struct msn_switchboard *sb, char *s, int len )  {  	int st; +	if( getenv( "BITLBEE_DEBUG" ) ) +	{ +		write( 2, "->SB:", 5 ); +		write( 2, s, len ); +	} +	  	st = write( sb->fd, s, len );  	if( st != len )  	{ diff --git a/protocols/msn/soap.c b/protocols/msn/soap.c index 252bddeb..e837986e 100644 --- a/protocols/msn/soap.c +++ b/protocols/msn/soap.c @@ -27,6 +27,7 @@  #include "bitlbee.h"  #include "url.h"  #include "misc.h" +#include "sha1.h"  #include "base64.h"  #include "xmltree.h"  #include <ctype.h> @@ -82,27 +83,25 @@ static void msn_soap_handle_response( struct http_request *http_req );  static int msn_soap_send_request( struct msn_soap_req_data *soap_req )  { -	struct msn_data *md = soap_req->ic->proto_data;  	char *http_req; -	char *pom, *s; +	char *soap_action = NULL;  	url_t url;  	soap_req->build_request( soap_req ); -	pom = g_new0( char, strlen( md->passport_token ) * 3 ); -	strcpy( pom, md->passport_token + 2 ); -	if( ( s = strchr( pom, '&' ) ) ) -		*s = '\0'; +	if( soap_req->action ) +		soap_action = g_strdup_printf( "SOAPAction: \"%s\"\r\n", soap_req->action );  	url_set( &url, soap_req->url );  	http_req = g_strdup_printf( SOAP_HTTP_REQUEST, url.file, url.host, -		soap_req->action, pom, +		soap_action ? soap_action : "",  		strlen( soap_req->payload ), soap_req->payload );  	soap_req->http_req = http_dorequest( url.host, url.port, url.proto == PROTO_HTTPS,  		http_req, msn_soap_handle_response, soap_req );  	g_free( http_req ); +	g_free( soap_action );  	return soap_req->http_req != NULL;  } @@ -139,6 +138,160 @@ static void msn_soap_handle_response( struct http_request *http_req )  } +/* passport_sso: Authentication MSNP15+ */ + +struct msn_soap_passport_sso_data +{ +	char *policy; +	char *nonce; +	char *secret; +}; + +static int msn_soap_passport_sso_build_request( struct msn_soap_req_data *soap_req ) +{ +	struct msn_soap_passport_sso_data *sd = soap_req->data; +	struct im_connection *ic = soap_req->ic; +	 +	soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL ); +	soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD, +		ic->acc->user, ic->acc->pass, sd->policy ); +	 +	return MSN_SOAP_OK; +} + +static xt_status msn_soap_passport_sso_token( struct xt_node *node, gpointer data ) +{ +	struct msn_soap_req_data *soap_req = data; +	struct msn_soap_passport_sso_data *sd = soap_req->data; +	struct msn_data *md = soap_req->ic->proto_data; +	struct xt_node *p; +	char *id; +	 +	if( ( id = xt_find_attr( node, "Id" ) ) == NULL ) +		return XT_HANDLED; +	id += strlen( id ) - 1; +	if( *id == '1' && +	    ( p = node->parent ) && ( p = p->parent ) && +	    ( p = xt_find_node( p->children, "wst:RequestedProofToken" ) ) && +	    ( p = xt_find_node( p->children, "wst:BinarySecret" ) ) && +	    p->text ) +	    	sd->secret = g_strdup( p->text ); +	 +	if( *id == '1' ) +		md->tokens[0] = g_strdup( node->text ); +	else if( *id == '2' ) +		md->tokens[1] = g_strdup( node->text ); +	 +	return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_passport_sso_parser[] = { +	{ "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token }, +	{ NULL, NULL, NULL } +}; + +static char *msn_key_fuckery( char *key, int key_len, char *type ) +{ +	unsigned char hash1[20+strlen(type)+1]; +	unsigned char hash2[20]; +	char *ret; +	 +	sha1_hmac( key, key_len, type, 0, hash1 ); +	strcpy( (char*) hash1 + 20, type ); +	sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash2 ); +	 +	/* This is okay as hash1 is read completely before it's overwritten. */ +	sha1_hmac( key, key_len, (char*) hash1, 20, hash1 ); +	sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash1 ); +	 +	ret = g_malloc( 24 ); +	memcpy( ret, hash2, 20 ); +	memcpy( ret + 20, hash1, 4 ); +	return ret; +} + +static int msn_soap_passport_sso_handle_response( struct msn_soap_req_data *soap_req ) +{ +	struct msn_soap_passport_sso_data *sd = soap_req->data; +	struct im_connection *ic = soap_req->ic; +	char *key1, *key2, *key3, *blurb64; +	int key1_len; +	unsigned char *padnonce, *des3res; +	struct +	{ +		unsigned int uStructHeaderSize; // 28. Does not count data +		unsigned int uCryptMode; // CRYPT_MODE_CBC (1) +		unsigned int uCipherType; // TripleDES (0x6603) +		unsigned int uHashType; // SHA1 (0x8004) +		unsigned int uIVLen;    // 8 +		unsigned int uHashLen;  // 20 +		unsigned int uCipherLen; // 72 +		unsigned char iv[8]; +		unsigned char hash[20]; +		unsigned char cipherbytes[72]; +	} blurb = { +		GUINT32_TO_LE( 28 ), +		GUINT32_TO_LE( 1 ), +		GUINT32_TO_LE( 0x6603 ), +		GUINT32_TO_LE( 0x8004 ), +		GUINT32_TO_LE( 8 ), +		GUINT32_TO_LE( 20 ), +		GUINT32_TO_LE( 72 ), +	}; + +	key1_len = base64_decode( sd->secret, (unsigned char**) &key1 ); +	 +	key2 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY HASH" ); +	key3 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY ENCRYPTION" ); +	 +	sha1_hmac( key2, 24, sd->nonce, 0, blurb.hash ); +	padnonce = g_malloc( strlen( sd->nonce ) + 8 ); +	strcpy( (char*) padnonce, sd->nonce ); +	memset( padnonce + strlen( sd->nonce ), 8, 8 ); +	 +	random_bytes( blurb.iv, 8 ); +	 +	ssl_des3_encrypt( (unsigned char*) key3, 24, padnonce, strlen( sd->nonce ) + 8, blurb.iv, &des3res ); +	memcpy( blurb.cipherbytes, des3res, 72 ); +	 +	blurb64 = base64_encode( (unsigned char*) &blurb, sizeof( blurb ) ); +	msn_auth_got_passport_token( ic, blurb64 ); +	 +	g_free( padnonce ); +	g_free( blurb64 ); +	g_free( des3res ); +	g_free( key1 ); +	g_free( key2 ); +	g_free( key3 ); +	 +	return MSN_SOAP_OK; +} + +static int msn_soap_passport_sso_free_data( struct msn_soap_req_data *soap_req ) +{ +	struct msn_soap_passport_sso_data *sd = soap_req->data; +	 +	g_free( sd->policy ); +	g_free( sd->nonce ); +	g_free( sd->secret ); +	 +	return MSN_SOAP_OK; +} + +int msn_soap_passport_sso_request( struct im_connection *ic, const char *policy, const char *nonce ) +{ +	struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 ); +	 +	sd->policy = g_strdup( policy ); +	sd->nonce = g_strdup( nonce ); +	 +	return msn_soap_start( ic, sd, msn_soap_passport_sso_build_request, +	                               msn_soap_passport_sso_parser, +	                               msn_soap_passport_sso_handle_response, +	                               msn_soap_passport_sso_free_data ); +} + +  /* oim_send: Sending offline messages */  struct msn_soap_oim_send_data @@ -161,13 +314,13 @@ static int msn_soap_oim_build_request( struct msn_soap_req_data *soap_req )  	soap_req->url = g_strdup( SOAP_OIM_SEND_URL );  	soap_req->action = g_strdup( SOAP_OIM_SEND_ACTION );  	soap_req->payload = g_markup_printf_escaped( SOAP_OIM_SEND_PAYLOAD, -		ic->acc->user, display_name_b64, oim->to, md->passport_token, +		ic->acc->user, display_name_b64, oim->to, "bla", //md->passport_token,  		MSNP11_PROD_ID, md->lock_key ? md->lock_key : "",  		oim->number, oim->number, oim->msg );  	g_free( display_name_b64 ); -	return 1; +	return MSN_SOAP_OK;  }  static xt_status msn_soap_oim_send_challenge( struct xt_node *node, gpointer data ) @@ -219,7 +372,7 @@ static int msn_soap_oim_free_data( struct msn_soap_req_data *soap_req )  	g_free( oim->msg );  	g_free( oim ); -	return 0; +	return MSN_SOAP_OK;  }  int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg ) @@ -262,6 +415,8 @@ int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq )  		*msgq = g_slist_remove( *msgq, m );  	} +	 +	return 1;  } diff --git a/protocols/msn/soap.h b/protocols/msn/soap.h index 78808fdf..5fbac3c4 100644 --- a/protocols/msn/soap.h +++ b/protocols/msn/soap.h @@ -42,16 +42,81 @@  "POST %s HTTP/1.0\r\n" \  "Host: %s\r\n" \  "Accept: */*\r\n" \ -"SOAPAction: \"%s\"\r\n" \  "User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \  "Content-Type: text/xml; charset=utf-8\r\n" \ -"Cookie: MSPAuth=%s\r\n" \ -"Content-Length: %d\r\n" \ +"%s" \ +"Content-Length: %zd\r\n" \  "Cache-Control: no-cache\r\n" \  "\r\n" \  "%s" +#define SOAP_PASSPORT_SSO_URL "https://login.live.com/RST.srf" +#define SOAP_PASSPORT_SSO_URL_MSN "https://msnia.login.live.com/pp550/RST.srf" + +#define SOAP_PASSPORT_SSO_PAYLOAD \ +"<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>" \ +           "<wst:RequestSecurityToken Id=\"RST1\">" \ +               "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ +               "<wsp:AppliesTo>" \ +                   "<wsa:EndpointReference>" \ +                       "<wsa:Address>messengerclear.live.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 xmlns=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" URI=\"MBI\"></wsse:PolicyReference>" \ +           "</wst:RequestSecurityToken>" \ +       "</ps:RequestMultipleSecurityTokens>" \ +   "</Body>" \ +"</Envelope>" + +int msn_soap_passport_sso_request( struct im_connection *ic, const char *policy, const char *nonce ); + +  #define SOAP_OIM_SEND_URL "https://ows.messenger.msn.com/OimWS/oim.asmx"  #define SOAP_OIM_SEND_ACTION "http://messenger.msn.com/ws/2004/09/oim/Store" | 
