예제 #1
0
파일: sasl.c 프로젝트: mrdon/bitlbee
static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token, const char *error )
{
	struct im_connection *ic = data;
	struct jabber_data *jd;
	GSList *auth = NULL;
	
	if( g_slist_find( jabber_connections, ic ) == NULL )
		return;
	
	jd = ic->proto_data;
	
	if( access_token == NULL )
	{
		imcb_error( ic, "OAuth failure (%s)", error );
		imc_logout( ic, TRUE );
		return;
	}
	
	oauth_params_parse( &auth, ic->acc->pass );
	if( refresh_token )
		oauth_params_set( &auth, "refresh_token", refresh_token );
	if( access_token )
		oauth_params_set( &auth, "access_token", access_token );
	
	g_free( ic->acc->pass );
	ic->acc->pass = oauth_params_string( auth );
	oauth_params_free( &auth );
	
	g_free( jd->oauth2_access_token );
	jd->oauth2_access_token = g_strdup( access_token );
	
	jabber_connect( ic );
}
예제 #2
0
파일: sasl.c 프로젝트: mrdon/bitlbee
xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data )
{
	struct im_connection *ic = data;
	struct jabber_data *jd = ic->proto_data;
	struct xt_node *reply_pkt = NULL;
	char *nonce = NULL, *realm = NULL, *cnonce = NULL;
	unsigned char cnonce_bin[30];
	char *digest_uri = NULL;
	char *dec = NULL;
	char *s = NULL, *reply = NULL;
	xt_status ret = XT_ABORT;
	
	if( node->text_len == 0 )
		goto error;
	
	dec = frombase64( node->text );
	
	if( jd->flags & JFLAG_SASL_FB )
	{
		/* New-style Facebook OAauth2 support. Instead of sending a refresh
		   token, they just send an access token that should never expire. */
		GSList *p_in = NULL, *p_out = NULL;
		char time[33];
		
		oauth_params_parse( &p_in, dec );
		oauth_params_add( &p_out, "nonce", oauth_params_get( &p_in, "nonce" ) );
		oauth_params_add( &p_out, "method", oauth_params_get( &p_in, "method" ) );
		oauth_params_free( &p_in );
		
		g_snprintf( time, sizeof( time ), "%lld", (long long) ( gettime() * 1000 ) );
		oauth_params_add( &p_out, "call_id", time );
		oauth_params_add( &p_out, "api_key", oauth2_service_facebook.consumer_key );
		oauth_params_add( &p_out, "v", "1.0" );
		oauth_params_add( &p_out, "format", "XML" );
		oauth_params_add( &p_out, "access_token", jd->oauth2_access_token );
		
		reply = oauth_params_string( p_out );
		oauth_params_free( &p_out );
	}
	else if( !( s = sasl_get_part( dec, "rspauth" ) ) )
	{
		/* See RFC 2831 for for information. */
		md5_state_t A1, A2, H;
		md5_byte_t A1r[16], A2r[16], Hr[16];
		char A1h[33], A2h[33], Hh[33];
		int i;
		
		nonce = sasl_get_part( dec, "nonce" );
		realm = sasl_get_part( dec, "realm" );
		
		if( !nonce )
			goto error;
		
		/* Jabber.Org considers the realm part optional and doesn't
		   specify one. Oh well, actually they're right, but still,
		   don't know if this is right... */
		if( !realm )
			realm = g_strdup( jd->server );
		
		random_bytes( cnonce_bin, sizeof( cnonce_bin ) );
		cnonce = base64_encode( cnonce_bin, sizeof( cnonce_bin ) );
		digest_uri = g_strdup_printf( "%s/%s", "xmpp", jd->server );
		
		/* Generate the MD5 hash of username:realm:password,
		   I decided to call it H. */
		md5_init( &H );
		s = g_strdup_printf( "%s:%s:%s", jd->username, realm, ic->acc->pass );
		md5_append( &H, (unsigned char *) s, strlen( s ) );
		g_free( s );
		md5_finish( &H, Hr );
		
		/* Now generate the hex. MD5 hash of H:nonce:cnonce, called A1. */
		md5_init( &A1 );
		s = g_strdup_printf( ":%s:%s", nonce, cnonce );
		md5_append( &A1, Hr, 16 );
		md5_append( &A1, (unsigned char *) s, strlen( s ) );
		g_free( s );
		md5_finish( &A1, A1r );
		for( i = 0; i < 16; i ++ )
			sprintf( A1h + i * 2, "%02x", A1r[i] );
		
		/* A2... */
		md5_init( &A2 );
		s = g_strdup_printf( "%s:%s", "AUTHENTICATE", digest_uri );
		md5_append( &A2, (unsigned char *) s, strlen( s ) );
		g_free( s );
		md5_finish( &A2, A2r );
		for( i = 0; i < 16; i ++ )
			sprintf( A2h + i * 2, "%02x", A2r[i] );
		
		/* Final result: A1:nonce:00000001:cnonce:auth:A2. Let's reuse H for it. */
		md5_init( &H );
		s = g_strdup_printf( "%s:%s:%s:%s:%s:%s", A1h, nonce, "00000001", cnonce, "auth", A2h );
		md5_append( &H, (unsigned char *) s, strlen( s ) );
		g_free( s );
		md5_finish( &H, Hr );
		for( i = 0; i < 16; i ++ )
			sprintf( Hh + i * 2, "%02x", Hr[i] );
		
		/* Now build the SASL response string: */
		reply = g_strdup_printf( "username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\","
		                         "nc=%08x,qop=auth,digest-uri=\"%s\",response=%s,charset=%s",
		                         jd->username, realm, nonce, cnonce, 1, digest_uri, Hh, "utf-8" );
	}
	else
	{
		/* We found rspauth, but don't really care... */
		g_free( s );
	}
	
	s = reply ? tobase64( reply ) : NULL;
	reply_pkt = xt_new_node( "response", s, NULL );
	xt_add_attr( reply_pkt, "xmlns", XMLNS_SASL );
	
	if( !jabber_write_packet( ic, reply_pkt ) )
		goto silent_error;
	
	ret = XT_HANDLED;
	goto silent_error;

error:
	imcb_error( ic, "Incorrect SASL challenge received" );
	imc_logout( ic, FALSE );

silent_error:
	g_free( digest_uri );
	g_free( cnonce );
	g_free( nonce );
	g_free( reply );
	g_free( realm );
	g_free( dec );
	g_free( s );
	xt_free_node( reply_pkt );
	
	return ret;
}
예제 #3
0
파일: jabber.c 프로젝트: AlD/bitlbee
static void jabber_login( account_t *acc )
{
	struct im_connection *ic = imcb_new( acc );
	struct jabber_data *jd = g_new0( struct jabber_data, 1 );
	char *s;
	
	/* For now this is needed in the _connected() handlers if using
	   GLib event handling, to make sure we're not handling events
	   on dead connections. */
	jabber_connections = g_slist_prepend( jabber_connections, ic );
	
	jd->ic = ic;
	ic->proto_data = jd;
	
	jabber_set_me( ic, acc->user );
	
	jd->fd = jd->r_inpa = jd->w_inpa = -1;
	
	if( jd->server == NULL )
	{
		imcb_error( ic, "Incomplete account name (format it like <*****@*****.**>)" );
		imc_logout( ic, FALSE );
		return;
	}
	
	if( ( s = strchr( jd->server, '/' ) ) )
	{
		*s = 0;
		set_setstr( &acc->set, "resource", s + 1 );
		
		/* Also remove the /resource from the original variable so we
		   won't have to do this again every time. */
		s = strchr( acc->user, '/' );
		*s = 0;
	}
	
	jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free );
	jd->buddies = g_hash_table_new( g_str_hash, g_str_equal );
	
	if( set_getbool( &acc->set, "oauth" ) )
	{
		GSList *p_in = NULL;
		const char *tok;
		
		jd->fd = jd->r_inpa = jd->w_inpa = -1;
		
		if( strstr( jd->server, ".live.com" ) )
			jd->oauth2_service = &oauth2_service_mslive;
		else if( strstr( jd->server, ".facebook.com" ) )
			jd->oauth2_service = &oauth2_service_facebook;
		else
			jd->oauth2_service = &oauth2_service_google;
		
		oauth_params_parse( &p_in, ic->acc->pass );
		
		/* First see if we have a refresh token, in which case any
		   access token we *might* have has probably expired already
		   anyway. */
		if( ( tok = oauth_params_get( &p_in, "refresh_token" ) ) )
		{
			sasl_oauth2_refresh( ic, tok );
		}
		/* If we don't have a refresh token, let's hope the access
		   token is still usable. */
		else if( ( tok = oauth_params_get( &p_in, "access_token" ) ) )
		{
			jd->oauth2_access_token = g_strdup( tok );
			jabber_connect( ic );
		}
		/* If we don't have any, start the OAuth process now. Don't
		   even open an XMPP connection yet. */
		else
		{
			sasl_oauth2_init( ic );
			ic->flags |= OPT_SLOW_LOGIN;
		}
		
		oauth_params_free( &p_in );
	}
	else
		jabber_connect( ic );
}