Changeset 5f40da7 for protocols/jabber/sasl.c
- Timestamp:
- 2011-12-26T10:51:19Z (13 years ago)
- Branches:
- master
- Children:
- 199fea6
- Parents:
- 96f954d (diff), 644b808 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
protocols/jabber/sasl.c
r96f954d r5f40da7 26 26 #include "jabber.h" 27 27 #include "base64.h" 28 #include "oauth2.h" 29 #include "oauth.h" 30 31 const struct oauth2_service oauth2_service_google = 32 { 33 "https://accounts.google.com/o/oauth2/auth", 34 "https://accounts.google.com/o/oauth2/token", 35 "urn:ietf:wg:oauth:2.0:oob", 36 "https://www.googleapis.com/auth/googletalk", 37 "783993391592.apps.googleusercontent.com", 38 "6C-Zgf7Tr7gEQTPlBhMUgo7R", 39 }; 40 const struct oauth2_service oauth2_service_facebook = 41 { 42 "https://www.facebook.com/dialog/oauth", 43 "https://graph.facebook.com/oauth/access_token", 44 "http://www.bitlbee.org/main.php/Facebook/oauth2.html", 45 "offline_access,xmpp_login", 46 "126828914005625", 47 "4b100f0f244d620bf3f15f8b217d4c32", 48 }; 49 const struct oauth2_service oauth2_service_mslive = 50 { 51 "https://oauth.live.com/authorize", 52 "https://oauth.live.com/token", 53 "http://www.bitlbee.org/main.php/Messenger/oauth2.html", 54 "wl.offline_access%20wl.messenger", 55 "000000004C06FCD1", 56 "IRKlBPzJJAWcY-TbZjiTEJu9tn7XCFaV", 57 }; 28 58 29 59 xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data ) … … 33 63 struct xt_node *c, *reply; 34 64 char *s; 35 int sup_plain = 0, sup_digest = 0; 65 int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_fb = 0, sup_ms = 0; 66 int want_oauth = FALSE; 67 GString *mechs; 36 68 37 69 if( !sasl_supported( ic ) ) … … 52 84 } 53 85 86 mechs = g_string_new( "" ); 54 87 c = node->children; 55 88 while( ( c = xt_find_node( c, "mechanism" ) ) ) … … 57 90 if( c->text && g_strcasecmp( c->text, "PLAIN" ) == 0 ) 58 91 sup_plain = 1; 59 if( c->text && g_strcasecmp( c->text, "DIGEST-MD5" ) == 0 )92 else if( c->text && g_strcasecmp( c->text, "DIGEST-MD5" ) == 0 ) 60 93 sup_digest = 1; 94 else if( c->text && g_strcasecmp( c->text, "X-OAUTH2" ) == 0 ) 95 sup_gtalk = 1; 96 else if( c->text && g_strcasecmp( c->text, "X-FACEBOOK-PLATFORM" ) == 0 ) 97 sup_fb = 1; 98 else if( c->text && g_strcasecmp( c->text, "X-MESSENGER-OAUTH2" ) == 0 ) 99 sup_ms = 1; 100 101 if( c->text ) 102 g_string_append_printf( mechs, " %s", c->text ); 61 103 62 104 c = c->next; … … 65 107 if( !sup_plain && !sup_digest ) 66 108 { 67 imcb_error( ic, "No known SASL authentication schemes supported" ); 109 if( !sup_gtalk && !sup_fb && !sup_ms ) 110 imcb_error( ic, "This server requires OAuth " 111 "(supported schemes:%s)", mechs->str ); 112 else 113 imcb_error( ic, "BitlBee does not support any of the offered SASL " 114 "authentication schemes:%s", mechs->str ); 68 115 imc_logout( ic, FALSE ); 116 g_string_free( mechs, TRUE ); 69 117 return XT_ABORT; 70 118 } 119 g_string_free( mechs, TRUE ); 71 120 72 121 reply = xt_new_node( "auth", NULL, NULL ); 73 122 xt_add_attr( reply, "xmlns", XMLNS_SASL ); 74 75 if( sup_digest ) 123 want_oauth = set_getbool( &ic->acc->set, "oauth" ); 124 125 if( sup_gtalk && want_oauth ) 126 { 127 int len; 128 129 /* X-OAUTH2 is, not *the* standard OAuth2 SASL/XMPP implementation. 130 It's currently used by GTalk and vaguely documented on 131 http://code.google.com/apis/cloudprint/docs/rawxmpp.html . */ 132 xt_add_attr( reply, "mechanism", "X-OAUTH2" ); 133 134 len = strlen( jd->username ) + strlen( jd->oauth2_access_token ) + 2; 135 s = g_malloc( len + 1 ); 136 s[0] = 0; 137 strcpy( s + 1, jd->username ); 138 strcpy( s + 2 + strlen( jd->username ), jd->oauth2_access_token ); 139 reply->text = base64_encode( (unsigned char *)s, len ); 140 reply->text_len = strlen( reply->text ); 141 g_free( s ); 142 } 143 else if( sup_ms && want_oauth ) 144 { 145 xt_add_attr( reply, "mechanism", "X-MESSENGER-OAUTH2" ); 146 reply->text = g_strdup( jd->oauth2_access_token ); 147 reply->text_len = strlen( jd->oauth2_access_token ); 148 } 149 else if( sup_fb && want_oauth ) 150 { 151 xt_add_attr( reply, "mechanism", "X-FACEBOOK-PLATFORM" ); 152 jd->flags |= JFLAG_SASL_FB; 153 } 154 else if( want_oauth ) 155 { 156 imcb_error( ic, "OAuth requested, but not supported by server" ); 157 imc_logout( ic, FALSE ); 158 xt_free_node( reply ); 159 return XT_ABORT; 160 } 161 else if( sup_digest ) 76 162 { 77 163 xt_add_attr( reply, "mechanism", "DIGEST-MD5" ); … … 96 182 } 97 183 98 if( !jabber_write_packet( ic, reply ) )184 if( reply && !jabber_write_packet( ic, reply ) ) 99 185 { 100 186 xt_free_node( reply ); … … 197 283 struct im_connection *ic = data; 198 284 struct jabber_data *jd = ic->proto_data; 199 struct xt_node *reply = NULL;285 struct xt_node *reply_pkt = NULL; 200 286 char *nonce = NULL, *realm = NULL, *cnonce = NULL; 201 287 unsigned char cnonce_bin[30]; 202 288 char *digest_uri = NULL; 203 289 char *dec = NULL; 204 char *s = NULL ;290 char *s = NULL, *reply = NULL; 205 291 xt_status ret = XT_ABORT; 206 292 … … 210 296 dec = frombase64( node->text ); 211 297 212 if( !( s = sasl_get_part( dec, "rspauth" ) ) ) 298 if( jd->flags & JFLAG_SASL_FB ) 299 { 300 /* New-style Facebook OAauth2 support. Instead of sending a refresh 301 token, they just send an access token that should never expire. */ 302 GSList *p_in = NULL, *p_out = NULL; 303 char time[33]; 304 305 oauth_params_parse( &p_in, dec ); 306 oauth_params_add( &p_out, "nonce", oauth_params_get( &p_in, "nonce" ) ); 307 oauth_params_add( &p_out, "method", oauth_params_get( &p_in, "method" ) ); 308 oauth_params_free( &p_in ); 309 310 g_snprintf( time, sizeof( time ), "%lld", (long long) ( gettime() * 1000 ) ); 311 oauth_params_add( &p_out, "call_id", time ); 312 oauth_params_add( &p_out, "api_key", oauth2_service_facebook.consumer_key ); 313 oauth_params_add( &p_out, "v", "1.0" ); 314 oauth_params_add( &p_out, "format", "XML" ); 315 oauth_params_add( &p_out, "access_token", jd->oauth2_access_token ); 316 317 reply = oauth_params_string( p_out ); 318 oauth_params_free( &p_out ); 319 } 320 else if( !( s = sasl_get_part( dec, "rspauth" ) ) ) 213 321 { 214 322 /* See RFC 2831 for for information. */ … … 271 379 272 380 /* Now build the SASL response string: */ 273 g_free( dec ); 274 dec = g_strdup_printf( "username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\"," 275 "nc=%08x,qop=auth,digest-uri=\"%s\",response=%s,charset=%s", 276 jd->username, realm, nonce, cnonce, 1, digest_uri, Hh, "utf-8" ); 277 s = tobase64( dec ); 381 reply = g_strdup_printf( "username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\"," 382 "nc=%08x,qop=auth,digest-uri=\"%s\",response=%s,charset=%s", 383 jd->username, realm, nonce, cnonce, 1, digest_uri, Hh, "utf-8" ); 278 384 } 279 385 else 280 386 { 281 387 /* We found rspauth, but don't really care... */ 282 g_free( s ); 283 s = NULL; 284 } 285 286 reply = xt_new_node( "response", s, NULL ); 287 xt_add_attr( reply, "xmlns", XMLNS_SASL ); 288 289 if( !jabber_write_packet( ic, reply ) ) 388 } 389 390 s = reply ? tobase64( reply ) : NULL; 391 reply_pkt = xt_new_node( "response", s, NULL ); 392 xt_add_attr( reply_pkt, "xmlns", XMLNS_SASL ); 393 394 if( !jabber_write_packet( ic, reply_pkt ) ) 290 395 goto silent_error; 291 396 … … 301 406 g_free( cnonce ); 302 407 g_free( nonce ); 408 g_free( reply ); 303 409 g_free( realm ); 304 410 g_free( dec ); 305 411 g_free( s ); 306 xt_free_node( reply );412 xt_free_node( reply_pkt ); 307 413 308 414 return ret; … … 347 453 return ( jd->xt && jd->xt->root && xt_find_attr( jd->xt->root, "version" ) ) != 0; 348 454 } 455 456 void sasl_oauth2_init( struct im_connection *ic ) 457 { 458 struct jabber_data *jd = ic->proto_data; 459 char *msg, *url; 460 461 imcb_log( ic, "Starting OAuth authentication" ); 462 463 /* Temporary contact, just used to receive the OAuth response. */ 464 imcb_add_buddy( ic, JABBER_OAUTH_HANDLE, NULL ); 465 url = oauth2_url( jd->oauth2_service ); 466 msg = g_strdup_printf( "Open this URL in your browser to authenticate: %s", url ); 467 imcb_buddy_msg( ic, JABBER_OAUTH_HANDLE, msg, 0, 0 ); 468 imcb_buddy_msg( ic, JABBER_OAUTH_HANDLE, "Respond to this message with the returned " 469 "authorization token.", 0, 0 ); 470 471 g_free( msg ); 472 g_free( url ); 473 } 474 475 static gboolean sasl_oauth2_remove_contact( gpointer data, gint fd, b_input_condition cond ) 476 { 477 struct im_connection *ic = data; 478 if( g_slist_find( jabber_connections, ic ) ) 479 imcb_remove_buddy( ic, JABBER_OAUTH_HANDLE, NULL ); 480 return FALSE; 481 } 482 483 static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token ); 484 485 int sasl_oauth2_get_refresh_token( struct im_connection *ic, const char *msg ) 486 { 487 struct jabber_data *jd = ic->proto_data; 488 char *code; 489 int ret; 490 491 imcb_log( ic, "Requesting OAuth access token" ); 492 493 /* Don't do it here because the caller may get confused if the contact 494 we're currently sending a message to is deleted. */ 495 b_timeout_add( 1, sasl_oauth2_remove_contact, ic ); 496 497 code = g_strdup( msg ); 498 g_strstrip( code ); 499 ret = oauth2_access_token( jd->oauth2_service, OAUTH2_AUTH_CODE, 500 code, sasl_oauth2_got_token, ic ); 501 502 g_free( code ); 503 return ret; 504 } 505 506 int sasl_oauth2_refresh( struct im_connection *ic, const char *refresh_token ) 507 { 508 struct jabber_data *jd = ic->proto_data; 509 510 return oauth2_access_token( jd->oauth2_service, OAUTH2_AUTH_REFRESH, 511 refresh_token, sasl_oauth2_got_token, ic ); 512 } 513 514 static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token ) 515 { 516 struct im_connection *ic = data; 517 struct jabber_data *jd; 518 GSList *auth = NULL; 519 520 if( g_slist_find( jabber_connections, ic ) == NULL ) 521 return; 522 523 jd = ic->proto_data; 524 525 if( access_token == NULL ) 526 { 527 imcb_error( ic, "OAuth failure (missing access token)" ); 528 imc_logout( ic, TRUE ); 529 return; 530 } 531 532 oauth_params_parse( &auth, ic->acc->pass ); 533 if( refresh_token ) 534 oauth_params_set( &auth, "refresh_token", refresh_token ); 535 if( access_token ) 536 oauth_params_set( &auth, "access_token", access_token ); 537 538 g_free( ic->acc->pass ); 539 ic->acc->pass = oauth_params_string( auth ); 540 oauth_params_free( &auth ); 541 542 g_free( jd->oauth2_access_token ); 543 jd->oauth2_access_token = g_strdup( access_token ); 544 545 jabber_connect( ic ); 546 }
Note: See TracChangeset
for help on using the changeset viewer.