コード例 #1
0
ファイル: io.c プロジェクト: jianingy/bitlbee-clone
static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition cond )
{
	struct im_connection *ic = data;
	struct jabber_data *jd = ic->proto_data;
	char buf[512];
	int st;
	
	if( jd->fd == -1 )
		return FALSE;
	
	if( jd->ssl )
		st = ssl_read( jd->ssl, buf, sizeof( buf ) );
	else
		st = read( jd->fd, buf, sizeof( buf ) );
	
	if( st > 0 )
	{
		/* Parse. */
		if( xt_feed( jd->xt, buf, st ) < 0 )
		{
			imcb_error( ic, "XML stream error" );
			imc_logout( ic, TRUE );
			return FALSE;
		}
		
		/* Execute all handlers. */
		if( !xt_handle( jd->xt, NULL, 1 ) )
		{
			/* Don't do anything, the handlers should have
			   aborted the connection already. */
			return FALSE;
		}
		
		if( jd->flags & JFLAG_STREAM_RESTART )
		{
			jd->flags &= ~JFLAG_STREAM_RESTART;
			jabber_start_stream( ic );
		}
		
		/* Garbage collection. */
		xt_cleanup( jd->xt, NULL, 1 );
		
		/* This is a bit hackish, unfortunately. Although xmltree
		   has nifty event handler stuff, it only calls handlers
		   when nodes are complete. Since the server should only
		   send an opening <stream:stream> tag, we have to check
		   this by hand. :-( */
		if( !( jd->flags & JFLAG_STREAM_STARTED ) && jd->xt && jd->xt->root )
		{
			if( g_strcasecmp( jd->xt->root->name, "stream:stream" ) == 0 )
			{
				jd->flags |= JFLAG_STREAM_STARTED;
				
				/* If there's no version attribute, assume
				   this is an old server that can't do SASL
				   authentication. */
				if( !sasl_supported( ic ) )
				{
					/* If there's no version= tag, we suppose
					   this server does NOT implement: XMPP 1.0,
					   SASL and TLS. */
					if( set_getbool( &ic->acc->set, "tls" ) )
					{
						imcb_error( ic, "TLS is turned on for this "
						          "account, but is not supported by this server" );
						imc_logout( ic, FALSE );
						return FALSE;
					}
					else
					{
						return jabber_init_iq_auth( ic );
					}
				}
			}
			else
			{
				imcb_error( ic, "XML stream error" );
				imc_logout( ic, TRUE );
				return FALSE;
			}
		}
	}
	else if( st == 0 || ( st < 0 && !ssl_sockerr_again( jd->ssl ) ) )
	{
		closesocket( jd->fd );
		jd->fd = -1;
		
		imcb_error( ic, "Error while reading from server" );
		imc_logout( ic, TRUE );
		return FALSE;
	}
	
	if( ssl_pending( jd->ssl ) )
		/* OpenSSL empties the TCP buffers completely but may keep some
		   data in its internap buffers. select() won't see that, but
		   ssl_pending() does. */
		return jabber_read_callback( data, fd, cond );
	else
		return TRUE;
}
コード例 #2
0
ファイル: io.c プロジェクト: jianingy/bitlbee-clone
static xt_status jabber_pkt_features( struct xt_node *node, gpointer data )
{
	struct im_connection *ic = data;
	struct jabber_data *jd = ic->proto_data;
	struct xt_node *c, *reply;
	int trytls;
	
	trytls = g_strcasecmp( set_getstr( &ic->acc->set, "tls" ), "try" ) == 0;
	c = xt_find_node( node->children, "starttls" );
	if( c && !jd->ssl )
	{
		/* If the server advertises the STARTTLS feature and if we're
		   not in a secure connection already: */
		
		c = xt_find_node( c->children, "required" );
		
		if( c && ( !trytls && !set_getbool( &ic->acc->set, "tls" ) ) )
		{
			imcb_error( ic, "Server requires TLS connections, but TLS is turned off for this account" );
			imc_logout( ic, FALSE );
			
			return XT_ABORT;
		}
		
		/* Only run this if the tls setting is set to true or try: */
		if( ( trytls || set_getbool( &ic->acc->set, "tls" ) ) )
		{
			reply = xt_new_node( "starttls", NULL, NULL );
			xt_add_attr( reply, "xmlns", XMLNS_TLS );
			if( !jabber_write_packet( ic, reply ) )
			{
				xt_free_node( reply );
				return XT_ABORT;
			}
			xt_free_node( reply );
			
			return XT_HANDLED;
		}
	}
	else if( !c && !jd->ssl )
	{
		/* If the server does not advertise the STARTTLS feature and
		   we're not in a secure connection already: (Servers have a
		   habit of not advertising <starttls/> anymore when already
		   using SSL/TLS. */
		
		if( !trytls && set_getbool( &ic->acc->set, "tls" ) )
		{
			imcb_error( ic, "TLS is turned on for this account, but is not supported by this server" );
			imc_logout( ic, FALSE );
			
			return XT_ABORT;
		}
	}
	
	/* This one used to be in jabber_handlers[], but it has to be done
	   from here to make sure the TLS session will be initialized
	   properly before we attempt SASL authentication. */
	if( ( c = xt_find_node( node->children, "mechanisms" ) ) )
	{
		if( sasl_pkt_mechanisms( c, data ) == XT_ABORT )
			return XT_ABORT;
	}
	/* If the server *SEEMS* to support SASL authentication but doesn't
	   support it after all, we should try to do authentication the
	   other way. jabber.com doesn't seem to do SASL while it pretends
	   to be XMPP 1.0 compliant! */
	else if( !( jd->flags & JFLAG_AUTHENTICATED ) && sasl_supported( ic ) )
	{
		if( !jabber_init_iq_auth( ic ) )
			return XT_ABORT;
	}
	
	if( ( c = xt_find_node( node->children, "bind" ) ) )
	{
		reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
		xt_add_attr( reply, "xmlns", XMLNS_BIND );
		reply = jabber_make_packet( "iq", "set", NULL, reply );
		jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
		
		if( !jabber_write_packet( ic, reply ) )
			return XT_ABORT;
		
		jd->flags |= JFLAG_WAIT_BIND;
	}
	
	if( ( c = xt_find_node( node->children, "session" ) ) )
	{
		reply = xt_new_node( "session", NULL, NULL );
		xt_add_attr( reply, "xmlns", XMLNS_SESSION );
		reply = jabber_make_packet( "iq", "set", NULL, reply );
		jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
		
		if( !jabber_write_packet( ic, reply ) )
			return XT_ABORT;
		
		jd->flags |= JFLAG_WAIT_SESSION;
	}
	
	/* This flag is already set if we authenticated via SASL, so now
	   we can resume the session in the new stream, if we don't have
	   to bind/initialize the session. */
	if( jd->flags & JFLAG_AUTHENTICATED && ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
	{
		if( !jabber_get_roster( ic ) )
			return XT_ABORT;
	}
	
	return XT_HANDLED;
}
コード例 #3
0
ファイル: sasl.c プロジェクト: mrdon/bitlbee
xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data )
{
	struct im_connection *ic = data;
	struct jabber_data *jd = ic->proto_data;
	struct xt_node *c, *reply;
	char *s;
	int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_fb = 0;
	int want_oauth = FALSE;
	GString *mechs;
	
	if( !sasl_supported( ic ) )
	{
		/* Should abort this now, since we should already be doing
		   IQ authentication. Strange things happen when you try
		   to do both... */
		imcb_log( ic, "XMPP 1.0 non-compliant server seems to support SASL, please report this as a BitlBee bug!" );
		return XT_HANDLED;
	}
	
	s = xt_find_attr( node, "xmlns" );
	if( !s || strcmp( s, XMLNS_SASL ) != 0 )
	{
		imcb_log( ic, "Stream error while authenticating" );
		imc_logout( ic, FALSE );
		return XT_ABORT;
	}
	
	want_oauth = set_getbool( &ic->acc->set, "oauth" );
	
	mechs = g_string_new( "" );
	c = node->children;
	while( ( c = xt_find_node( c, "mechanism" ) ) )
	{
		if( c->text && g_strcasecmp( c->text, "PLAIN" ) == 0 )
			sup_plain = 1;
		else if( c->text && g_strcasecmp( c->text, "DIGEST-MD5" ) == 0 )
			sup_digest = 1;
		else if( c->text && g_strcasecmp( c->text, "X-OAUTH2" ) == 0 )
			sup_gtalk = 1;
		else if( c->text && g_strcasecmp( c->text, "X-FACEBOOK-PLATFORM" ) == 0 )
			sup_fb = 1;
		
		if( c->text )
			g_string_append_printf( mechs, " %s", c->text );
		
		c = c->next;
	}
	
	if( !want_oauth && !sup_plain && !sup_digest )
	{
		if( !sup_gtalk && !sup_fb )
			imcb_error( ic, "This server requires OAuth "
			                "(supported schemes:%s)", mechs->str );
		else
			imcb_error( ic, "BitlBee does not support any of the offered SASL "
			                "authentication schemes:%s", mechs->str );
		imc_logout( ic, FALSE );
		g_string_free( mechs, TRUE );
		return XT_ABORT;
	}
	g_string_free( mechs, TRUE );
	
	reply = xt_new_node( "auth", NULL, NULL );
	xt_add_attr( reply, "xmlns", XMLNS_SASL );
	
	if( sup_gtalk && want_oauth )
	{
		int len;
		
		/* X-OAUTH2 is, not *the* standard OAuth2 SASL/XMPP implementation.
		   It's currently used by GTalk and vaguely documented on
		   http://code.google.com/apis/cloudprint/docs/rawxmpp.html . */
		xt_add_attr( reply, "mechanism", "X-OAUTH2" );
		
		len = strlen( jd->username ) + strlen( jd->oauth2_access_token ) + 2;
		s = g_malloc( len + 1 );
		s[0] = 0;
		strcpy( s + 1, jd->username );
		strcpy( s + 2 + strlen( jd->username ), jd->oauth2_access_token );
		reply->text = base64_encode( (unsigned char *)s, len );
		reply->text_len = strlen( reply->text );
		g_free( s );
	}
	else if( sup_fb && want_oauth )
	{
		xt_add_attr( reply, "mechanism", "X-FACEBOOK-PLATFORM" );
		jd->flags |= JFLAG_SASL_FB;
	}
	else if( want_oauth )
	{
		imcb_error( ic, "OAuth requested, but not supported by server" );
		imc_logout( ic, FALSE );
		xt_free_node( reply );
		return XT_ABORT;
	}
	else if( sup_digest )
	{
		xt_add_attr( reply, "mechanism", "DIGEST-MD5" );
		
		/* The rest will be done later, when we receive a <challenge/>. */
	}
	else if( sup_plain )
	{
		int len;
		
		xt_add_attr( reply, "mechanism", "PLAIN" );
		
		/* With SASL PLAIN in XMPP, the text should be b64(\0user\0pass) */
		len = strlen( jd->username ) + strlen( ic->acc->pass ) + 2;
		s = g_malloc( len + 1 );
		s[0] = 0;
		strcpy( s + 1, jd->username );
		strcpy( s + 2 + strlen( jd->username ), ic->acc->pass );
		reply->text = base64_encode( (unsigned char *)s, len );
		reply->text_len = strlen( reply->text );
		g_free( s );
	}
	
	if( reply && !jabber_write_packet( ic, reply ) )
	{
		xt_free_node( reply );
		return XT_ABORT;
	}
	xt_free_node( reply );
	
	/* To prevent classic authentication from happening. */
	jd->flags |= JFLAG_STREAM_STARTED;
	
	return XT_HANDLED;
}