source: protocols/msn/soap.c @ 523fb23

Last change on this file since 523fb23 was 523fb23, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-11T08:08:39Z

Implement MSNP15 SSO (Sadistic Sign-On).

  • Property mode set to 100644
File size: 12.6 KB
RevLine 
[e5a8118]1/** soap.c
2 *
3 * SOAP-related functions. Some manager at Microsoft apparently thought
4 * MSNP wasn't XMLy enough so someone stepped up and changed that. This
5 * is the result.
6 *
7 * Copyright (C) 2010 Wilmer van der Gaast <wilmer@gaast.net>
8 *
9 * This program is free software; you can redistribute it and/or modify             
10 * it under the terms of the GNU General Public License version 2                   
11 * as published by the Free Software Foundation                                     
12 *                                                                                   
13 * This program is distributed in the hope that is will be useful,                 
14 * bit WITHOU ANY WARRANTY; without even the implied warranty of                   
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                   
16 * GNU General Public License for more details.                                     
17 *                                                                                   
18 * You should have received a copy of the GNU General Public License               
19 * along with this program; if not, write to the Free Software                     
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA         
21 *
22 */
23
24#include "http_client.h"
25#include "soap.h"
26#include "msn.h"
27#include "bitlbee.h"
28#include "url.h"
29#include "misc.h"
[523fb23]30#include "sha1.h"
[e5a8118]31#include "base64.h"
32#include "xmltree.h"
33#include <ctype.h>
34#include <errno.h>
35
36typedef enum
37{
38        MSN_SOAP_OK,
39        MSN_SOAP_RETRY,
40        MSN_SOAP_ABORT,
41} msn_soap_result_t;
42
43struct msn_soap_req_data;
44
45typedef int (*msn_soap_func) ( struct msn_soap_req_data * );
46
47struct msn_soap_req_data
48{
49        void *data;
50        struct im_connection *ic;
51        int ttl;
52       
53        char *url, *action, *payload;
54        struct http_request *http_req;
55       
56        const struct xt_handler_entry *xml_parser;
57        msn_soap_func build_request, handle_response, free_data;
58};
59
60static int msn_soap_send_request( struct msn_soap_req_data *req );
61
62static int msn_soap_start( struct im_connection *ic,
63                    void *data,
64                    msn_soap_func build_request,
65                    const struct xt_handler_entry *xml_parser,
66                    msn_soap_func handle_response,
67                    msn_soap_func free_data )
68{
69        struct msn_soap_req_data *req = g_new0( struct msn_soap_req_data, 1 );
70       
71        req->ic = ic;
72        req->data = data;
73        req->xml_parser = xml_parser;
74        req->build_request = build_request;
75        req->handle_response = handle_response;
76        req->free_data = free_data;
77        req->ttl = 3;
78       
79        return msn_soap_send_request( req );
80}
81
82static void msn_soap_handle_response( struct http_request *http_req );
83
84static int msn_soap_send_request( struct msn_soap_req_data *soap_req )
85{
86        char *http_req;
[523fb23]87        char *soap_action = NULL;
[e5a8118]88        url_t url;
89       
90        soap_req->build_request( soap_req );
91       
[523fb23]92        if( soap_req->action )
93                soap_action = g_strdup_printf( "SOAPAction: \"%s\"\r\n", soap_req->action );
[7db65b7]94       
[e5a8118]95        url_set( &url, soap_req->url );
96        http_req = g_strdup_printf( SOAP_HTTP_REQUEST, url.file, url.host,
[523fb23]97                soap_action ? soap_action : "",
[7db65b7]98                strlen( soap_req->payload ), soap_req->payload );
[e5a8118]99       
100        soap_req->http_req = http_dorequest( url.host, url.port, url.proto == PROTO_HTTPS,
101                http_req, msn_soap_handle_response, soap_req );
102       
[ffb6dea]103        g_free( http_req );
[523fb23]104        g_free( soap_action );
[ffb6dea]105       
[e5a8118]106        return soap_req->http_req != NULL;
107}
108
109static void msn_soap_handle_response( struct http_request *http_req )
110{
111        struct msn_soap_req_data *soap_req = http_req->data;
112        int st;
113       
114        if( http_req->body_size > 0 )
115        {
116                struct xt_parser *parser;
117               
118                parser = xt_new( soap_req->xml_parser, soap_req );
119                xt_feed( parser, http_req->reply_body, http_req->body_size );
120                xt_handle( parser, NULL, -1 );
121                xt_free( parser );
122        }
123       
124        st = soap_req->handle_response( soap_req );
125       
[ffb6dea]126        g_free( soap_req->url );
127        g_free( soap_req->action );
128        g_free( soap_req->payload );
129        soap_req->url = soap_req->action = soap_req->payload = NULL;
130       
[e5a8118]131        if( st == MSN_SOAP_RETRY && --soap_req->ttl )
132                msn_soap_send_request( soap_req );
133        else
134        {
135                soap_req->free_data( soap_req );
136                g_free( soap_req );
137        }
138}
139
[bc090f0]140
[523fb23]141/* passport_sso: Authentication MSNP15+ */
142
143struct msn_soap_passport_sso_data
144{
145        char *policy;
146        char *nonce;
147        char *secret;
148};
149
150static int msn_soap_passport_sso_build_request( struct msn_soap_req_data *soap_req )
151{
152        struct msn_soap_passport_sso_data *sd = soap_req->data;
153        struct im_connection *ic = soap_req->ic;
154       
155        soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL );
156        soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD,
157                ic->acc->user, ic->acc->pass, sd->policy );
158       
159        return MSN_SOAP_OK;
160}
161
162static xt_status msn_soap_passport_sso_token( struct xt_node *node, gpointer data )
163{
164        struct msn_soap_req_data *soap_req = data;
165        struct msn_soap_passport_sso_data *sd = soap_req->data;
166        struct msn_data *md = soap_req->ic->proto_data;
167        struct xt_node *p;
168        char *id;
169       
170        if( ( id = xt_find_attr( node, "Id" ) ) == NULL )
171                return XT_HANDLED;
172        id += strlen( id ) - 1;
173        if( *id == '1' &&
174            ( p = node->parent ) && ( p = p->parent ) &&
175            ( p = xt_find_node( p->children, "wst:RequestedProofToken" ) ) &&
176            ( p = xt_find_node( p->children, "wst:BinarySecret" ) ) &&
177            p->text )
178                sd->secret = g_strdup( p->text );
179       
180        if( *id == '1' )
181                md->tokens[0] = g_strdup( node->text );
182        else if( *id == '2' )
183                md->tokens[1] = g_strdup( node->text );
184       
185        return XT_HANDLED;
186}
187
188static const struct xt_handler_entry msn_soap_passport_sso_parser[] = {
189        { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token },
190        { NULL, NULL, NULL }
191};
192
193static char *msn_key_fuckery( char *key, int key_len, char *type )
194{
195        unsigned char hash1[20+strlen(type)+1];
196        unsigned char hash2[20];
197        char *ret;
198       
199        sha1_hmac( key, key_len, type, 0, hash1 );
200        strcpy( (char*) hash1 + 20, type );
201        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash2 );
202       
203        /* This is okay as hash1 is read completely before it's overwritten. */
204        sha1_hmac( key, key_len, (char*) hash1, 20, hash1 );
205        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash1 );
206       
207        ret = g_malloc( 24 );
208        memcpy( ret, hash2, 20 );
209        memcpy( ret + 20, hash1, 4 );
210        return ret;
211}
212
213static int msn_soap_passport_sso_handle_response( struct msn_soap_req_data *soap_req )
214{
215        struct msn_soap_passport_sso_data *sd = soap_req->data;
216        struct im_connection *ic = soap_req->ic;
217        char *key1, *key2, *key3, *blurb64;
218        int key1_len;
219        unsigned char *padnonce, *des3res;
220        struct
221        {
222                unsigned int uStructHeaderSize; // 28. Does not count data
223                unsigned int uCryptMode; // CRYPT_MODE_CBC (1)
224                unsigned int uCipherType; // TripleDES (0x6603)
225                unsigned int uHashType; // SHA1 (0x8004)
226                unsigned int uIVLen;    // 8
227                unsigned int uHashLen;  // 20
228                unsigned int uCipherLen; // 72
229                unsigned char iv[8];
230                unsigned char hash[20];
231                unsigned char cipherbytes[72];
232        } blurb = {
233                GUINT32_TO_LE( 28 ),
234                GUINT32_TO_LE( 1 ),
235                GUINT32_TO_LE( 0x6603 ),
236                GUINT32_TO_LE( 0x8004 ),
237                GUINT32_TO_LE( 8 ),
238                GUINT32_TO_LE( 20 ),
239                GUINT32_TO_LE( 72 ),
240        };
241
242        key1_len = base64_decode( sd->secret, (unsigned char**) &key1 );
243       
244        key2 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY HASH" );
245        key3 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY ENCRYPTION" );
246       
247        sha1_hmac( key2, 24, sd->nonce, 0, blurb.hash );
248        padnonce = g_malloc( strlen( sd->nonce ) + 8 );
249        strcpy( (char*) padnonce, sd->nonce );
250        memset( padnonce + strlen( sd->nonce ), 8, 8 );
251       
252        random_bytes( blurb.iv, 8 );
253       
254        ssl_des3_encrypt( (unsigned char*) key3, 24, padnonce, strlen( sd->nonce ) + 8, blurb.iv, &des3res );
255        memcpy( blurb.cipherbytes, des3res, 72 );
256       
257        blurb64 = base64_encode( (unsigned char*) &blurb, sizeof( blurb ) );
258        msn_auth_got_passport_token( ic, blurb64 );
259       
260        g_free( padnonce );
261        g_free( blurb64 );
262        g_free( des3res );
263        g_free( key1 );
264        g_free( key2 );
265        g_free( key3 );
266       
267        return MSN_SOAP_OK;
268}
269
270static int msn_soap_passport_sso_free_data( struct msn_soap_req_data *soap_req )
271{
272        struct msn_soap_passport_sso_data *sd = soap_req->data;
273       
274        g_free( sd->policy );
275        g_free( sd->nonce );
276        g_free( sd->secret );
277       
278        return MSN_SOAP_OK;
279}
280
281int msn_soap_passport_sso_request( struct im_connection *ic, const char *policy, const char *nonce )
282{
283        struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 );
284       
285        sd->policy = g_strdup( policy );
286        sd->nonce = g_strdup( nonce );
287       
288        return msn_soap_start( ic, sd, msn_soap_passport_sso_build_request,
289                                       msn_soap_passport_sso_parser,
290                                       msn_soap_passport_sso_handle_response,
291                                       msn_soap_passport_sso_free_data );
292}
293
294
[bc090f0]295/* oim_send: Sending offline messages */
296
297struct msn_soap_oim_send_data
298{
299        char *to;
300        char *msg;
301        int number;
302        int need_retry;
303};
304
[e5a8118]305static int msn_soap_oim_build_request( struct msn_soap_req_data *soap_req )
306{
307        struct msn_soap_oim_send_data *oim = soap_req->data;
308        struct im_connection *ic = soap_req->ic;
309        struct msn_data *md = ic->proto_data;
310        char *display_name_b64;
311       
312        display_name_b64 = tobase64( ic->displayname );
313       
314        soap_req->url = g_strdup( SOAP_OIM_SEND_URL );
[7db65b7]315        soap_req->action = g_strdup( SOAP_OIM_SEND_ACTION );
[e5a8118]316        soap_req->payload = g_markup_printf_escaped( SOAP_OIM_SEND_PAYLOAD,
[523fb23]317                ic->acc->user, display_name_b64, oim->to, "bla", //md->passport_token,
[5fecede]318                MSNP11_PROD_ID, md->lock_key ? md->lock_key : "",
319                oim->number, oim->number, oim->msg );
[e5a8118]320       
321        g_free( display_name_b64 );
322       
[523fb23]323        return MSN_SOAP_OK;
[e5a8118]324}
325
326static xt_status msn_soap_oim_send_challenge( struct xt_node *node, gpointer data )
327{
328        struct msn_soap_req_data *soap_req = data;
329        struct msn_soap_oim_send_data *oim = soap_req->data;
330        struct im_connection *ic = soap_req->ic;
331        struct msn_data *md = ic->proto_data;
332       
333        g_free( md->lock_key );
334        md->lock_key = msn_p11_challenge( node->text );
335       
336        oim->need_retry = 1;
337       
338        return XT_HANDLED;
339}
340
341static const struct xt_handler_entry msn_soap_oim_send_parser[] = {
342        { "LockKeyChallenge", "detail", msn_soap_oim_send_challenge },
343        { NULL,               NULL,     NULL                        }
344};
345
346static int msn_soap_oim_handle_response( struct msn_soap_req_data *soap_req )
347{
348        struct msn_soap_oim_send_data *oim = soap_req->data;
349       
[bc090f0]350        if( soap_req->http_req->status_code == 500 && oim->need_retry && soap_req->ttl > 0 )
[e5a8118]351        {
352                oim->need_retry = 0;
353                return MSN_SOAP_RETRY;
354        }
355        else if( soap_req->http_req->status_code == 200 )
[bc090f0]356        {
357                imcb_log( soap_req->ic, "Offline message successfully delivered to %s", oim->to );
[e5a8118]358                return MSN_SOAP_OK;
[bc090f0]359        }
[e5a8118]360        else
[bc090f0]361        {
362                imcb_log( soap_req->ic, "Failed to deliver offline message to %s:\n%s", oim->to, oim->msg );
[e5a8118]363                return MSN_SOAP_ABORT;
[bc090f0]364        }
[e5a8118]365}
366
367static int msn_soap_oim_free_data( struct msn_soap_req_data *soap_req )
368{
369        struct msn_soap_oim_send_data *oim = soap_req->data;
370       
371        g_free( oim->to );
372        g_free( oim->msg );
373        g_free( oim );
374       
[523fb23]375        return MSN_SOAP_OK;
[e5a8118]376}
377
378int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg )
379{
380        struct msn_soap_oim_send_data *data;
381       
382        data = g_new0( struct msn_soap_oim_send_data, 1 );
383        data->to = g_strdup( to );
384        data->msg = tobase64( msg );
385        data->number = 1;
386       
387        return msn_soap_start( ic, data, msn_soap_oim_build_request,
388                                         msn_soap_oim_send_parser,
389                                         msn_soap_oim_handle_response,
390                                         msn_soap_oim_free_data );
391}
[bc090f0]392
393int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq )
394{
395        GSList *l;
396        char *n = NULL;
397       
398        for( l = *msgq; l; l = l->next )
399        {
400                struct msn_message *m = l->data;
401               
402                if( n == NULL )
403                        n = m->who;
404                if( strcmp( n, m->who ) == 0 )
405                        msn_soap_oim_send( ic, m->who, m->text );
406        }
407       
408        while( *msgq != NULL )
409        {
410                struct msn_message *m = (*msgq)->data;
411               
412                g_free( m->who );
413                g_free( m->text );
414                g_free( m );
415               
416                *msgq = g_slist_remove( *msgq, m );
417        }
[523fb23]418       
419        return 1;
[bc090f0]420}
[7db65b7]421
422
423/* memlist: Fetching the membership list (NOT address book) */
424
425#if 0
426struct msn_soap_oim_send_data
427{
428        char *to;
429        char *msg;
430        int number;
431        int need_retry;
432};
433#endif
434
435static int msn_soap_memlist_build_request( struct msn_soap_req_data *soap_req )
436{
437        soap_req->url = g_strdup( SOAP_MEMLIST_URL );
438        soap_req->action = g_strdup( SOAP_MEMLIST_ACTION );
439        soap_req->payload = g_strdup( SOAP_MEMLIST_PAYLOAD );
440       
441        return 1;
442}
443
444static const struct xt_handler_entry msn_soap_memlist_parser[] = {
445        { NULL,               NULL,     NULL                        }
446};
447
448static int msn_soap_memlist_handle_response( struct msn_soap_req_data *soap_req )
449{
450        return 0;
451}
452
453static int msn_soap_memlist_free_data( struct msn_soap_req_data *soap_req )
454{
455        return 0;
456}
457
458int msn_soap_memlist_request( struct im_connection *ic )
459{
460        return msn_soap_start( ic, NULL, msn_soap_memlist_build_request,
461                                         msn_soap_memlist_parser,
462                                         msn_soap_memlist_handle_response,
463                                         msn_soap_memlist_free_data );
464}
Note: See TracBrowser for help on using the repository browser.