source: protocols/msn/soap.c @ 73efe3a

Last change on this file since 73efe3a was 73efe3a, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-14T11:07:45Z

Allow Passport authentication with @msn.com accounts.

  • Property mode set to 100644
File size: 17.0 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* MSN module - All the SOAPy XML stuff.
8   Some manager at Microsoft apparently thought MSNP wasn't XMLy enough so
9   someone stepped up and changed that. This is the result. Kilobytes and
10   more kilobytes of XML vomit to transfer tiny bits of informaiton. */
11
12/*
13  This program is free software; you can redistribute it and/or modify
14  it under the terms of the GNU General Public License as published by
15  the Free Software Foundation; either version 2 of the License, or
16  (at your option) any later version.
17
18  This program is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  GNU General Public License for more details.
22
23  You should have received a copy of the GNU General Public License with
24  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
25  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
26  Suite 330, Boston, MA  02111-1307  USA
27*/
28
29#include "http_client.h"
30#include "soap.h"
31#include "msn.h"
32#include "bitlbee.h"
33#include "url.h"
34#include "misc.h"
35#include "sha1.h"
36#include "base64.h"
37#include "xmltree.h"
38#include <ctype.h>
39#include <errno.h>
40
41/* This file tries to make SOAP stuff pretty simple to do by letting you just
42   provide a function to build a request, a few functions to parse various
43   parts of the response, and a function to run when the full response was
44   received and parsed. See the various examples below. */
45
46typedef enum
47{
48        MSN_SOAP_OK,
49        MSN_SOAP_RETRY,
50        MSN_SOAP_ABORT,
51} msn_soap_result_t;
52
53struct msn_soap_req_data
54{
55        void *data;
56        struct im_connection *ic;
57        int ttl;
58       
59        char *url, *action, *payload;
60        struct http_request *http_req;
61       
62        const struct xt_handler_entry *xml_parser;
63        msn_soap_func build_request, handle_response, free_data;
64};
65
66typedef int (*msn_soap_func) ( struct msn_soap_req_data * );
67
68static int msn_soap_send_request( struct msn_soap_req_data *req );
69
70static int msn_soap_start( struct im_connection *ic,
71                    void *data,
72                    msn_soap_func build_request,
73                    const struct xt_handler_entry *xml_parser,
74                    msn_soap_func handle_response,
75                    msn_soap_func free_data )
76{
77        struct msn_soap_req_data *req = g_new0( struct msn_soap_req_data, 1 );
78       
79        req->ic = ic;
80        req->data = data;
81        req->xml_parser = xml_parser;
82        req->build_request = build_request;
83        req->handle_response = handle_response;
84        req->free_data = free_data;
85        req->ttl = 3;
86       
87        return msn_soap_send_request( req );
88}
89
90static void msn_soap_handle_response( struct http_request *http_req );
91
92static int msn_soap_send_request( struct msn_soap_req_data *soap_req )
93{
94        char *http_req;
95        char *soap_action = NULL;
96        url_t url;
97       
98        soap_req->build_request( soap_req );
99       
100        if( soap_req->action )
101                soap_action = g_strdup_printf( "SOAPAction: \"%s\"\r\n", soap_req->action );
102       
103        url_set( &url, soap_req->url );
104        http_req = g_strdup_printf( SOAP_HTTP_REQUEST, url.file, url.host,
105                soap_action ? soap_action : "",
106                strlen( soap_req->payload ), soap_req->payload );
107       
108        soap_req->http_req = http_dorequest( url.host, url.port, url.proto == PROTO_HTTPS,
109                http_req, msn_soap_handle_response, soap_req );
110       
111        g_free( http_req );
112        g_free( soap_action );
113       
114        return soap_req->http_req != NULL;
115}
116
117static void msn_soap_handle_response( struct http_request *http_req )
118{
119        struct msn_soap_req_data *soap_req = http_req->data;
120        int st;
121       
122        if( http_req->body_size > 0 )
123        {
124                struct xt_parser *parser;
125               
126                parser = xt_new( soap_req->xml_parser, soap_req );
127                xt_feed( parser, http_req->reply_body, http_req->body_size );
128                xt_handle( parser, NULL, -1 );
129                xt_free( parser );
130        }
131       
132        st = soap_req->handle_response( soap_req );
133       
134        g_free( soap_req->url );
135        g_free( soap_req->action );
136        g_free( soap_req->payload );
137        soap_req->url = soap_req->action = soap_req->payload = NULL;
138       
139        if( st == MSN_SOAP_RETRY && --soap_req->ttl )
140                msn_soap_send_request( soap_req );
141        else
142        {
143                soap_req->free_data( soap_req );
144                g_free( soap_req );
145        }
146}
147
148
149/* passport_sso: Authentication MSNP15+ */
150
151struct msn_soap_passport_sso_data
152{
153        char *policy;
154        char *nonce;
155        char *secret;
156};
157
158static int msn_soap_passport_sso_build_request( struct msn_soap_req_data *soap_req )
159{
160        struct msn_soap_passport_sso_data *sd = soap_req->data;
161        struct im_connection *ic = soap_req->ic;
162       
163        if( g_str_has_suffix( ic->acc->user, "@msn.com" ) )
164                soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL_MSN );
165        else
166                soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL );
167       
168        soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD,
169                ic->acc->user, ic->acc->pass, sd->policy );
170       
171        return MSN_SOAP_OK;
172}
173
174static xt_status msn_soap_passport_sso_token( struct xt_node *node, gpointer data )
175{
176        struct msn_soap_req_data *soap_req = data;
177        struct msn_soap_passport_sso_data *sd = soap_req->data;
178        struct msn_data *md = soap_req->ic->proto_data;
179        struct xt_node *p;
180        char *id;
181       
182        if( ( id = xt_find_attr( node, "Id" ) ) == NULL )
183                return XT_HANDLED;
184        id += strlen( id ) - 1;
185        if( *id == '1' &&
186            ( p = node->parent ) && ( p = p->parent ) &&
187            ( p = xt_find_node( p->children, "wst:RequestedProofToken" ) ) &&
188            ( p = xt_find_node( p->children, "wst:BinarySecret" ) ) &&
189            p->text )
190                sd->secret = g_strdup( p->text );
191       
192        *id -= '1';
193        if( *id >= 0 && *id <= 2 )
194        {
195                g_free( md->tokens[(int)*id] );
196                md->tokens[(int)*id] = g_strdup( node->text );
197        }
198       
199        return XT_HANDLED;
200}
201
202static const struct xt_handler_entry msn_soap_passport_sso_parser[] = {
203        { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token },
204        { NULL, NULL, NULL }
205};
206
207static char *msn_key_fuckery( char *key, int key_len, char *type )
208{
209        unsigned char hash1[20+strlen(type)+1];
210        unsigned char hash2[20];
211        char *ret;
212       
213        sha1_hmac( key, key_len, type, 0, hash1 );
214        strcpy( (char*) hash1 + 20, type );
215        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash2 );
216       
217        /* This is okay as hash1 is read completely before it's overwritten. */
218        sha1_hmac( key, key_len, (char*) hash1, 20, hash1 );
219        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash1 );
220       
221        ret = g_malloc( 24 );
222        memcpy( ret, hash2, 20 );
223        memcpy( ret + 20, hash1, 4 );
224        return ret;
225}
226
227static int msn_soap_passport_sso_handle_response( struct msn_soap_req_data *soap_req )
228{
229        struct msn_soap_passport_sso_data *sd = soap_req->data;
230        struct im_connection *ic = soap_req->ic;
231        char *key1, *key2, *key3, *blurb64;
232        int key1_len;
233        unsigned char *padnonce, *des3res;
234        struct
235        {
236                unsigned int uStructHeaderSize; // 28. Does not count data
237                unsigned int uCryptMode; // CRYPT_MODE_CBC (1)
238                unsigned int uCipherType; // TripleDES (0x6603)
239                unsigned int uHashType; // SHA1 (0x8004)
240                unsigned int uIVLen;    // 8
241                unsigned int uHashLen;  // 20
242                unsigned int uCipherLen; // 72
243                unsigned char iv[8];
244                unsigned char hash[20];
245                unsigned char cipherbytes[72];
246        } blurb = {
247                GUINT32_TO_LE( 28 ),
248                GUINT32_TO_LE( 1 ),
249                GUINT32_TO_LE( 0x6603 ),
250                GUINT32_TO_LE( 0x8004 ),
251                GUINT32_TO_LE( 8 ),
252                GUINT32_TO_LE( 20 ),
253                GUINT32_TO_LE( 72 ),
254        };
255
256        key1_len = base64_decode( sd->secret, (unsigned char**) &key1 );
257       
258        key2 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY HASH" );
259        key3 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY ENCRYPTION" );
260       
261        sha1_hmac( key2, 24, sd->nonce, 0, blurb.hash );
262        padnonce = g_malloc( strlen( sd->nonce ) + 8 );
263        strcpy( (char*) padnonce, sd->nonce );
264        memset( padnonce + strlen( sd->nonce ), 8, 8 );
265       
266        random_bytes( blurb.iv, 8 );
267       
268        ssl_des3_encrypt( (unsigned char*) key3, 24, padnonce, strlen( sd->nonce ) + 8, blurb.iv, &des3res );
269        memcpy( blurb.cipherbytes, des3res, 72 );
270       
271        blurb64 = base64_encode( (unsigned char*) &blurb, sizeof( blurb ) );
272        msn_auth_got_passport_token( ic, blurb64 );
273       
274        g_free( padnonce );
275        g_free( blurb64 );
276        g_free( des3res );
277        g_free( key1 );
278        g_free( key2 );
279        g_free( key3 );
280       
281        return MSN_SOAP_OK;
282}
283
284static int msn_soap_passport_sso_free_data( struct msn_soap_req_data *soap_req )
285{
286        struct msn_soap_passport_sso_data *sd = soap_req->data;
287       
288        g_free( sd->policy );
289        g_free( sd->nonce );
290        g_free( sd->secret );
291       
292        return MSN_SOAP_OK;
293}
294
295int msn_soap_passport_sso_request( struct im_connection *ic, const char *policy, const char *nonce )
296{
297        struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 );
298       
299        sd->policy = g_strdup( policy );
300        sd->nonce = g_strdup( nonce );
301       
302        return msn_soap_start( ic, sd, msn_soap_passport_sso_build_request,
303                                       msn_soap_passport_sso_parser,
304                                       msn_soap_passport_sso_handle_response,
305                                       msn_soap_passport_sso_free_data );
306}
307
308
309/* oim_send: Sending offline messages */
310
311struct msn_soap_oim_send_data
312{
313        char *to;
314        char *msg;
315        int number;
316        int need_retry;
317};
318
319static int msn_soap_oim_build_request( struct msn_soap_req_data *soap_req )
320{
321        struct msn_soap_oim_send_data *oim = soap_req->data;
322        struct im_connection *ic = soap_req->ic;
323        struct msn_data *md = ic->proto_data;
324        char *display_name_b64;
325       
326        display_name_b64 = tobase64( set_getstr( &ic->acc->set, "display_name" ) );
327       
328        soap_req->url = g_strdup( SOAP_OIM_SEND_URL );
329        soap_req->action = g_strdup( SOAP_OIM_SEND_ACTION );
330        soap_req->payload = g_markup_printf_escaped( SOAP_OIM_SEND_PAYLOAD,
331                ic->acc->user, display_name_b64, MSNP_VER, MSNP_BUILD,
332                oim->to, md->tokens[2],
333                MSNP11_PROD_ID, md->lock_key ? md->lock_key : "",
334                oim->number, oim->number, oim->msg );
335       
336        g_free( display_name_b64 );
337       
338        return MSN_SOAP_OK;
339}
340
341static xt_status msn_soap_oim_send_challenge( struct xt_node *node, gpointer data )
342{
343        struct msn_soap_req_data *soap_req = data;
344        struct msn_soap_oim_send_data *oim = soap_req->data;
345        struct im_connection *ic = soap_req->ic;
346        struct msn_data *md = ic->proto_data;
347       
348        g_free( md->lock_key );
349        md->lock_key = msn_p11_challenge( node->text );
350       
351        oim->need_retry = 1;
352       
353        return XT_HANDLED;
354}
355
356static const struct xt_handler_entry msn_soap_oim_send_parser[] = {
357        { "LockKeyChallenge", "detail", msn_soap_oim_send_challenge },
358        { NULL,               NULL,     NULL                        }
359};
360
361static int msn_soap_oim_handle_response( struct msn_soap_req_data *soap_req )
362{
363        struct msn_soap_oim_send_data *oim = soap_req->data;
364       
365        if( soap_req->http_req->status_code == 500 && oim->need_retry && soap_req->ttl > 0 )
366        {
367                oim->need_retry = 0;
368                return MSN_SOAP_RETRY;
369        }
370        else if( soap_req->http_req->status_code == 200 )
371        {
372                imcb_log( soap_req->ic, "Offline message successfully delivered to %s", oim->to );
373                return MSN_SOAP_OK;
374        }
375        else
376        {
377                imcb_log( soap_req->ic, "Failed to deliver offline message to %s:\n%s", oim->to, oim->msg );
378                return MSN_SOAP_ABORT;
379        }
380}
381
382static int msn_soap_oim_free_data( struct msn_soap_req_data *soap_req )
383{
384        struct msn_soap_oim_send_data *oim = soap_req->data;
385       
386        g_free( oim->to );
387        g_free( oim->msg );
388        g_free( oim );
389       
390        return MSN_SOAP_OK;
391}
392
393int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg )
394{
395        struct msn_soap_oim_send_data *data;
396       
397        data = g_new0( struct msn_soap_oim_send_data, 1 );
398        data->to = g_strdup( to );
399        data->msg = tobase64( msg );
400        data->number = 1;
401       
402        return msn_soap_start( ic, data, msn_soap_oim_build_request,
403                                         msn_soap_oim_send_parser,
404                                         msn_soap_oim_handle_response,
405                                         msn_soap_oim_free_data );
406}
407
408int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq )
409{
410        GSList *l;
411        char *n = NULL;
412       
413        for( l = *msgq; l; l = l->next )
414        {
415                struct msn_message *m = l->data;
416               
417                if( n == NULL )
418                        n = m->who;
419                if( strcmp( n, m->who ) == 0 )
420                        msn_soap_oim_send( ic, m->who, m->text );
421        }
422       
423        while( *msgq != NULL )
424        {
425                struct msn_message *m = (*msgq)->data;
426               
427                g_free( m->who );
428                g_free( m->text );
429                g_free( m );
430               
431                *msgq = g_slist_remove( *msgq, m );
432        }
433       
434        return 1;
435}
436
437
438/* memlist: Fetching the membership list (NOT address book) */
439
440static int msn_soap_memlist_build_request( struct msn_soap_req_data *soap_req )
441{
442        struct msn_data *md = soap_req->ic->proto_data;
443       
444        soap_req->url = g_strdup( SOAP_MEMLIST_URL );
445        soap_req->action = g_strdup( SOAP_MEMLIST_ACTION );
446        soap_req->payload = g_markup_printf_escaped( SOAP_MEMLIST_PAYLOAD, md->tokens[1] );
447       
448        return 1;
449}
450
451static xt_status msn_soap_memlist_member( struct xt_node *node, gpointer data )
452{
453        bee_user_t *bu;
454        struct msn_buddy_data *bd;
455        struct xt_node *p;
456        char *role = NULL, *handle = NULL;
457        struct msn_soap_req_data *soap_req = data;
458        struct im_connection *ic = soap_req->ic;
459       
460        if( ( p = node->parent ) && ( p = p->parent ) &&
461            ( p = xt_find_node( p->children, "MemberRole" ) ) )
462                role = p->text;
463       
464        if( ( p = xt_find_node( node->children, "PassportName" ) ) )
465                handle = p->text;
466       
467        if( !role || !handle || 
468            !( ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) ||
469               ( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) )
470                return XT_HANDLED;
471       
472        bd = bu->data;
473        if( strcmp( role, "Allow" ) == 0 )
474                bd->flags |= MSN_BUDDY_AL;
475        else if( strcmp( role, "Block" ) == 0 )
476                bd->flags |= MSN_BUDDY_BL;
477        else if( strcmp( role, "Reverse" ) == 0 )
478                bd->flags |= MSN_BUDDY_RL;
479        else if( strcmp( role, "Pending" ) == 0 )
480                bd->flags |= MSN_BUDDY_PL;
481       
482        return XT_HANDLED;
483}
484
485static const struct xt_handler_entry msn_soap_memlist_parser[] = {
486        { "Member", "Members", msn_soap_memlist_member },
487        { NULL,               NULL,     NULL                        }
488};
489
490static int msn_soap_memlist_handle_response( struct msn_soap_req_data *soap_req )
491{
492        msn_soap_addressbook_request( soap_req->ic );
493       
494        return MSN_SOAP_OK;
495}
496
497static int msn_soap_memlist_free_data( struct msn_soap_req_data *soap_req )
498{
499        return 0;
500}
501
502int msn_soap_memlist_request( struct im_connection *ic )
503{
504        return msn_soap_start( ic, NULL, msn_soap_memlist_build_request,
505                                         msn_soap_memlist_parser,
506                                         msn_soap_memlist_handle_response,
507                                         msn_soap_memlist_free_data );
508}
509
510
511/* addressbook: Fetching the membership list (NOT address book) */
512
513static int msn_soap_addressbook_build_request( struct msn_soap_req_data *soap_req )
514{
515        struct msn_data *md = soap_req->ic->proto_data;
516       
517        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
518        soap_req->action = g_strdup( SOAP_ADDRESSBOOK_ACTION );
519        soap_req->payload = g_markup_printf_escaped( SOAP_ADDRESSBOOK_PAYLOAD, md->tokens[1] );
520       
521        return 1;
522}
523
524static xt_status msn_soap_addressbook_group( struct xt_node *node, gpointer data )
525{
526        struct xt_node *p;
527        char *id = NULL, *name = NULL;
528        struct msn_soap_req_data *soap_req = data;
529        struct im_connection *ic = soap_req->ic;
530       
531        if( ( p = node->parent ) &&
532            ( p = xt_find_node( p->children, "groupId" ) ) )
533                id = p->text;
534       
535        if( ( p = xt_find_node( node->children, "name" ) ) )
536                name = p->text;
537       
538        printf( "%s %s\n", id, name );
539       
540        return XT_HANDLED;
541}
542
543static xt_status msn_soap_addressbook_contact( struct xt_node *node, gpointer data )
544{
545        bee_user_t *bu;
546        struct msn_buddy_data *bd;
547        struct xt_node *p;
548        char *id = NULL, *type = NULL, *handle = NULL, *display_name = NULL;
549        struct msn_soap_req_data *soap_req = data;
550        struct im_connection *ic = soap_req->ic;
551       
552        if( ( p = node->parent ) &&
553            ( p = xt_find_node( p->children, "contactId" ) ) )
554                id = p->text;
555        if( ( p = xt_find_node( node->children, "contactType" ) ) )
556                type = p->text;
557        if( ( p = xt_find_node( node->children, "passportName" ) ) )
558                handle = p->text;
559        if( ( p = xt_find_node( node->children, "displayName" ) ) )
560                display_name = p->text;
561       
562        if( type && g_strcasecmp( type, "me" ) == 0 )
563        {
564                set_t *set = set_find( &ic->acc->set, "display_name" );
565                g_free( set->value );
566                set->value = g_strdup( display_name );
567               
568                return XT_HANDLED;
569        }
570       
571        if( !( bu = bee_user_by_handle( ic->bee, ic, handle ) ) &&
572            !( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) )
573                return XT_HANDLED;
574       
575        bd = bu->data;
576        bd->flags |= MSN_BUDDY_FL;
577        g_free( bd->cid );
578        bd->cid = g_strdup( id );
579       
580        imcb_rename_buddy( ic, handle, display_name );
581       
582        printf( "%s %s %s %s\n", id, type, handle, display_name );
583       
584        return XT_HANDLED;
585}
586
587static const struct xt_handler_entry msn_soap_addressbook_parser[] = {
588        { "contactInfo", "Contact", msn_soap_addressbook_contact },
589        { "groupInfo", "Group", msn_soap_addressbook_group },
590        { NULL,               NULL,     NULL                        }
591};
592
593static int msn_soap_addressbook_handle_response( struct msn_soap_req_data *soap_req )
594{
595        msn_auth_got_contact_list( soap_req->ic );
596        return MSN_SOAP_OK;
597}
598
599static int msn_soap_addressbook_free_data( struct msn_soap_req_data *soap_req )
600{
601        return 0;
602}
603
604int msn_soap_addressbook_request( struct im_connection *ic )
605{
606        return msn_soap_start( ic, NULL, msn_soap_addressbook_build_request,
607                                         msn_soap_addressbook_parser,
608                                         msn_soap_addressbook_handle_response,
609                                         msn_soap_addressbook_free_data );
610}
Note: See TracBrowser for help on using the repository browser.