source: protocols/jabber/io.c @ ad46e4d

Last change on this file since ad46e4d was ad46e4d, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-12-22T11:23:18Z

Use initgroups() as well when dropping privileges. Closes bug #852.

  • Property mode set to 100644
File size: 15.8 KB
RevLine 
[21167d2]1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - I/O stuff (plain, SSL), queues, etc                      *
5*                                                                           *
6*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT 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 along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include "jabber.h"
[42127dc]25#include "ssl_client.h"
[21167d2]26
27static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond );
[0da65d5]28static gboolean jabber_write_queue( struct im_connection *ic );
[21167d2]29
[0da65d5]30int jabber_write_packet( struct im_connection *ic, struct xt_node *node )
[21167d2]31{
32        char *buf;
33        int st;
34       
35        buf = xt_to_string( node );
[0da65d5]36        st = jabber_write( ic, buf, strlen( buf ) );
[21167d2]37        g_free( buf );
38       
39        return st;
40}
41
[0da65d5]42int jabber_write( struct im_connection *ic, char *buf, int len )
[21167d2]43{
[0da65d5]44        struct jabber_data *jd = ic->proto_data;
[cc2cb2d]45        gboolean ret;
[21167d2]46       
[8e3890b]47        if( jd->flags & JFLAG_XMLCONSOLE && !( ic->flags & OPT_LOGGING_OUT ) )
[bb95d43]48        {
[fe79f7a7]49                char *msg, *s;
[bb95d43]50               
51                msg = g_strdup_printf( "TX: %s", buf );
[fe79f7a7]52                /* Don't include auth info in XML logs. */
53                if( strncmp( msg, "TX: <auth ", 10 ) == 0 && ( s = strchr( msg, '>' ) ) )
54                {
55                        s++;
56                        while( *s && *s != '<' )
57                                *(s++) = '*';
58                }
[bb95d43]59                imcb_buddy_msg( ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0 );
60                g_free( msg );
61        }
62       
[21167d2]63        if( jd->tx_len == 0 )
64        {
65                /* If the queue is empty, allocate a new buffer. */
66                jd->tx_len = len;
67                jd->txq = g_memdup( buf, len );
68               
69                /* Try if we can write it immediately so we don't have to do
70                   it via the event handler. If not, add the handler. (In
71                   most cases it probably won't be necessary.) */
[0da65d5]72                if( ( ret = jabber_write_queue( ic ) ) && jd->tx_len > 0 )
[e046390]73                        jd->w_inpa = b_input_add( jd->fd, B_EV_IO_WRITE, jabber_write_callback, ic );
[21167d2]74        }
75        else
76        {
77                /* Just add it to the buffer if it's already filled. The
78                   event handler is already set. */
79                jd->txq = g_renew( char, jd->txq, jd->tx_len + len );
80                memcpy( jd->txq + jd->tx_len, buf, len );
81                jd->tx_len += len;
[cc2cb2d]82               
83                /* The return value for write() doesn't necessarily mean
84                   that everything got sent, it mainly means that the
85                   connection (officially) still exists and can still
86                   be accessed without hitting SIGSEGV. IOW: */
87                ret = TRUE;
[21167d2]88        }
89       
[cc2cb2d]90        return ret;
[21167d2]91}
92
[cc2cb2d]93/* Splitting up in two separate functions: One to use as a callback and one
94   to use in the function above to escape from having to wait for the event
95   handler to call us, if possible.
96   
97   Two different functions are necessary because of the return values: The
98   callback should only return TRUE if the write was successful AND if the
99   buffer is not empty yet (ie. if the handler has to be called again when
100   the socket is ready for more data). */
[21167d2]101static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond )
102{
[0da65d5]103        struct jabber_data *jd = ((struct im_connection *)data)->proto_data;
[cc2cb2d]104       
105        return jd->fd != -1 &&
106               jabber_write_queue( data ) &&
107               jd->tx_len > 0;
108}
109
[0da65d5]110static gboolean jabber_write_queue( struct im_connection *ic )
[cc2cb2d]111{
[0da65d5]112        struct jabber_data *jd = ic->proto_data;
[21167d2]113        int st;
114       
[42127dc]115        if( jd->ssl )
116                st = ssl_write( jd->ssl, jd->txq, jd->tx_len );
117        else
118                st = write( jd->fd, jd->txq, jd->tx_len );
119       
[21167d2]120        if( st == jd->tx_len )
121        {
122                /* We wrote everything, clear the buffer. */
123                g_free( jd->txq );
124                jd->txq = NULL;
125                jd->tx_len = 0;
126               
[cc2cb2d]127                return TRUE;
[21167d2]128        }
[d52111a]129        else if( st == 0 || ( st < 0 && !ssl_sockerr_again( jd->ssl ) ) )
[21167d2]130        {
[8d74291]131                /* Set fd to -1 to make sure we won't write to it anymore. */
132                closesocket( jd->fd );  /* Shouldn't be necessary after errors? */
133                jd->fd = -1;
134               
[84b045d]135                imcb_error( ic, "Short write() to server" );
[c2fb3809]136                imc_logout( ic, TRUE );
[21167d2]137                return FALSE;
138        }
139        else if( st > 0 )
140        {
141                char *s;
142               
143                s = g_memdup( jd->txq + st, jd->tx_len - st );
144                jd->tx_len -= st;
145                g_free( jd->txq );
146                jd->txq = s;
147               
[8d74291]148                return TRUE;
[21167d2]149        }
150        else
151        {
152                /* Just in case we had EINPROGRESS/EAGAIN: */
153               
154                return TRUE;
155        }
156}
157
158static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition cond )
159{
[0da65d5]160        struct im_connection *ic = data;
161        struct jabber_data *jd = ic->proto_data;
[21167d2]162        char buf[512];
163        int st;
164       
[8d74291]165        if( jd->fd == -1 )
166                return FALSE;
167       
[42127dc]168        if( jd->ssl )
169                st = ssl_read( jd->ssl, buf, sizeof( buf ) );
170        else
171                st = read( jd->fd, buf, sizeof( buf ) );
172       
[21167d2]173        if( st > 0 )
174        {
[ad46e4d]175                if( jd->flags & JFLAG_MOCK )
176                        return TRUE;
177               
[21167d2]178                /* Parse. */
[b56b220]179                if( xt_feed( jd->xt, buf, st ) < 0 )
[21167d2]180                {
[84b045d]181                        imcb_error( ic, "XML stream error" );
[c2fb3809]182                        imc_logout( ic, TRUE );
[21167d2]183                        return FALSE;
184                }
185               
186                /* Execute all handlers. */
[101d84f]187                if( !xt_handle( jd->xt, NULL, 1 ) )
[21167d2]188                {
189                        /* Don't do anything, the handlers should have
[25984f2]190                           aborted the connection already. */
[21167d2]191                        return FALSE;
192                }
193               
[5997488]194                if( jd->flags & JFLAG_STREAM_RESTART )
195                {
196                        jd->flags &= ~JFLAG_STREAM_RESTART;
[0da65d5]197                        jabber_start_stream( ic );
[5997488]198                }
199               
[21167d2]200                /* Garbage collection. */
[101d84f]201                xt_cleanup( jd->xt, NULL, 1 );
[21167d2]202               
203                /* This is a bit hackish, unfortunately. Although xmltree
204                   has nifty event handler stuff, it only calls handlers
205                   when nodes are complete. Since the server should only
206                   send an opening <stream:stream> tag, we have to check
207                   this by hand. :-( */
208                if( !( jd->flags & JFLAG_STREAM_STARTED ) && jd->xt && jd->xt->root )
209                {
210                        if( g_strcasecmp( jd->xt->root->name, "stream:stream" ) == 0 )
211                        {
212                                jd->flags |= JFLAG_STREAM_STARTED;
[8d74291]213                               
214                                /* If there's no version attribute, assume
215                                   this is an old server that can't do SASL
216                                   authentication. */
[06b5893]217                                if( !set_getbool( &ic->acc->set, "sasl") || !sasl_supported( ic ) )
[cc2cb2d]218                                {
219                                        /* If there's no version= tag, we suppose
220                                           this server does NOT implement: XMPP 1.0,
221                                           SASL and TLS. */
[0da65d5]222                                        if( set_getbool( &ic->acc->set, "tls" ) )
[cc2cb2d]223                                        {
[84b045d]224                                                imcb_error( ic, "TLS is turned on for this "
[cc2cb2d]225                                                          "account, but is not supported by this server" );
[c2fb3809]226                                                imc_logout( ic, FALSE );
[cc2cb2d]227                                                return FALSE;
228                                        }
229                                        else
230                                        {
[0da65d5]231                                                return jabber_init_iq_auth( ic );
[cc2cb2d]232                                        }
233                                }
[21167d2]234                        }
235                        else
236                        {
[84b045d]237                                imcb_error( ic, "XML stream error" );
[c2fb3809]238                                imc_logout( ic, TRUE );
[21167d2]239                                return FALSE;
240                        }
241                }
242        }
[d52111a]243        else if( st == 0 || ( st < 0 && !ssl_sockerr_again( jd->ssl ) ) )
[21167d2]244        {
[8d74291]245                closesocket( jd->fd );
246                jd->fd = -1;
247               
[84b045d]248                imcb_error( ic, "Error while reading from server" );
[c2fb3809]249                imc_logout( ic, TRUE );
[21167d2]250                return FALSE;
251        }
252       
[8a2221a7]253        if( ssl_pending( jd->ssl ) )
254                /* OpenSSL empties the TCP buffers completely but may keep some
255                   data in its internap buffers. select() won't see that, but
256                   ssl_pending() does. */
257                return jabber_read_callback( data, fd, cond );
258        else
259                return TRUE;
[21167d2]260}
261
262gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond )
263{
[0da65d5]264        struct im_connection *ic = data;
[21167d2]265       
[b5c8a34]266        if( g_slist_find( jabber_connections, ic ) == NULL )
267                return FALSE;
268       
[21167d2]269        if( source == -1 )
270        {
[84b045d]271                imcb_error( ic, "Could not connect to server" );
[c2fb3809]272                imc_logout( ic, TRUE );
[21167d2]273                return FALSE;
274        }
275       
[84b045d]276        imcb_log( ic, "Connected to server, logging in" );
[21167d2]277       
[0da65d5]278        return jabber_start_stream( ic );
[21167d2]279}
280
[42127dc]281gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond )
282{
[0da65d5]283        struct im_connection *ic = data;
[b5c8a34]284        struct jabber_data *jd;
285       
286        if( g_slist_find( jabber_connections, ic ) == NULL )
287                return FALSE;
288       
289        jd = ic->proto_data;
[42127dc]290       
291        if( source == NULL )
292        {
[c1ed6527]293                /* The SSL connection will be cleaned up by the SSL lib
294                   already, set it to NULL here to prevent a double cleanup: */
295                jd->ssl = NULL;
296               
[84b045d]297                imcb_error( ic, "Could not connect to server" );
[c2fb3809]298                imc_logout( ic, TRUE );
[42127dc]299                return FALSE;
300        }
301       
[84b045d]302        imcb_log( ic, "Connected to server, logging in" );
[42127dc]303       
[0da65d5]304        return jabber_start_stream( ic );
[42127dc]305}
306
[21167d2]307static xt_status jabber_end_of_stream( struct xt_node *node, gpointer data )
308{
[c2fb3809]309        imc_logout( data, TRUE );
[21167d2]310        return XT_ABORT;
311}
312
[8d74291]313static xt_status jabber_pkt_features( struct xt_node *node, gpointer data )
314{
[0da65d5]315        struct im_connection *ic = data;
316        struct jabber_data *jd = ic->proto_data;
[fe7a554]317        struct xt_node *c, *reply;
[e101506]318        int trytls;
[8d74291]319       
[0da65d5]320        trytls = g_strcasecmp( set_getstr( &ic->acc->set, "tls" ), "try" ) == 0;
[8d74291]321        c = xt_find_node( node->children, "starttls" );
[42127dc]322        if( c && !jd->ssl )
[8d74291]323        {
[42127dc]324                /* If the server advertises the STARTTLS feature and if we're
325                   not in a secure connection already: */
326               
327                c = xt_find_node( c->children, "required" );
328               
[0da65d5]329                if( c && ( !trytls && !set_getbool( &ic->acc->set, "tls" ) ) )
[e101506]330                {
[84b045d]331                        imcb_error( ic, "Server requires TLS connections, but TLS is turned off for this account" );
[c2fb3809]332                        imc_logout( ic, FALSE );
[e101506]333                       
334                        return XT_ABORT;
335                }
336               
[42127dc]337                /* Only run this if the tls setting is set to true or try: */
[0da65d5]338                if( ( trytls || set_getbool( &ic->acc->set, "tls" ) ) )
[42127dc]339                {
340                        reply = xt_new_node( "starttls", NULL, NULL );
[47d3ac4]341                        xt_add_attr( reply, "xmlns", XMLNS_TLS );
[0da65d5]342                        if( !jabber_write_packet( ic, reply ) )
[42127dc]343                        {
344                                xt_free_node( reply );
345                                return XT_ABORT;
346                        }
347                        xt_free_node( reply );
348                       
349                        return XT_HANDLED;
350                }
351        }
[e101506]352        else if( !c && !jd->ssl )
[42127dc]353        {
[e101506]354                /* If the server does not advertise the STARTTLS feature and
355                   we're not in a secure connection already: (Servers have a
356                   habit of not advertising <starttls/> anymore when already
357                   using SSL/TLS. */
358               
[0da65d5]359                if( !trytls && set_getbool( &ic->acc->set, "tls" ) )
[e101506]360                {
[84b045d]361                        imcb_error( ic, "TLS is turned on for this account, but is not supported by this server" );
[c2fb3809]362                        imc_logout( ic, FALSE );
[e101506]363                       
364                        return XT_ABORT;
365                }
[8d74291]366        }
367       
[42127dc]368        /* This one used to be in jabber_handlers[], but it has to be done
369           from here to make sure the TLS session will be initialized
370           properly before we attempt SASL authentication. */
371        if( ( c = xt_find_node( node->children, "mechanisms" ) ) )
[0e2d97f]372        {
[42127dc]373                if( sasl_pkt_mechanisms( c, data ) == XT_ABORT )
374                        return XT_ABORT;
[0e2d97f]375        }
[88591fd]376        /* If the server *SEEMS* to support SASL authentication but doesn't
377           support it after all, we should try to do authentication the
378           other way. jabber.com doesn't seem to do SASL while it pretends
379           to be XMPP 1.0 compliant! */
[06b5893]380        else if( !( jd->flags & JFLAG_AUTHENTICATED ) && set_getbool( &ic->acc->set, "sasl") && sasl_supported( ic ) )
[0e2d97f]381        {
[0da65d5]382                if( !jabber_init_iq_auth( ic ) )
[88591fd]383                        return XT_ABORT;
[0e2d97f]384        }
[42127dc]385       
[fe7a554]386        if( ( c = xt_find_node( node->children, "bind" ) ) )
[8fb1263]387                jd->flags |= JFLAG_WANT_BIND;
[fe7a554]388       
389        if( ( c = xt_find_node( node->children, "session" ) ) )
[8fb1263]390                jd->flags |= JFLAG_WANT_SESSION;
[fe7a554]391       
[315dd4c]392        if( jd->flags & JFLAG_AUTHENTICATED )
[8fb1263]393                return jabber_pkt_bind_sess( ic, NULL, NULL );
[8d74291]394       
395        return XT_HANDLED;
396}
397
[42127dc]398static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data )
399{
[0da65d5]400        struct im_connection *ic = data;
401        struct jabber_data *jd = ic->proto_data;
[42127dc]402        char *xmlns;
403       
404        xmlns = xt_find_attr( node, "xmlns" );
405       
406        /* Just ignore it when it doesn't seem to be TLS-related (is that at
407           all possible??). */
[47d3ac4]408        if( !xmlns || strcmp( xmlns, XMLNS_TLS ) != 0 )
[42127dc]409                return XT_HANDLED;
410       
411        /* We don't want event handlers to touch our TLS session while it's
412           still initializing! */
413        b_event_remove( jd->r_inpa );
414        if( jd->tx_len > 0 )
415        {
416                /* Actually the write queue should be empty here, but just
417                   to be sure... */
418                b_event_remove( jd->w_inpa );
419                g_free( jd->txq );
420                jd->txq = NULL;
421                jd->tx_len = 0;
422        }
423        jd->w_inpa = jd->r_inpa = 0;
424       
[84b045d]425        imcb_log( ic, "Converting stream to TLS" );
[42127dc]426       
[af7f046]427        jd->flags |= JFLAG_STARTTLS_DONE;
[0da65d5]428        jd->ssl = ssl_starttls( jd->fd, jabber_connected_ssl, ic );
[42127dc]429       
430        return XT_HANDLED;
431}
432
[b56b220]433static xt_status jabber_pkt_stream_error( struct xt_node *node, gpointer data )
434{
[0da65d5]435        struct im_connection *ic = data;
[c2fb3809]436        int allow_reconnect = TRUE;
[1baaef8]437        struct jabber_error *err;
[b56b220]438       
[1baaef8]439        err = jabber_error_parse( node, XMLNS_STREAM_ERROR );
[b56b220]440       
441        /* Tssk... */
[1baaef8]442        if( err->code == NULL )
[b56b220]443        {
[84b045d]444                imcb_error( ic, "Unknown stream error reported by server" );
[c2fb3809]445                imc_logout( ic, allow_reconnect );
[1baaef8]446                jabber_error_free( err );
[b56b220]447                return XT_ABORT;
448        }
449       
450        /* We know that this is a fatal error. If it's a "conflict" error, we
451           should turn off auto-reconnect to make sure we won't get some nasty
452           infinite loop! */
[1baaef8]453        if( strcmp( err->code, "conflict" ) == 0 )
[259edd4]454        {
[84b045d]455                imcb_error( ic, "Account and resource used from a different location" );
[c2fb3809]456                allow_reconnect = FALSE;
[259edd4]457        }
458        else
459        {
[1baaef8]460                imcb_error( ic, "Stream error: %s%s%s", err->code, err->text ? ": " : "",
461                            err->text ? err->text : "" );
[259edd4]462        }
[b56b220]463       
[1baaef8]464        jabber_error_free( err );
[c2fb3809]465        imc_logout( ic, allow_reconnect );
[b56b220]466       
467        return XT_ABORT;
468}
469
[bb95d43]470static xt_status jabber_xmlconsole( struct xt_node *node, gpointer data )
471{
472        struct im_connection *ic = data;
473        struct jabber_data *jd = ic->proto_data;
474       
475        if( jd->flags & JFLAG_XMLCONSOLE )
476        {
477                char *msg, *pkt;
478               
479                pkt = xt_to_string( node );
480                msg = g_strdup_printf( "RX: %s", pkt );
481                imcb_buddy_msg( ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0 );
482                g_free( msg );
483                g_free( pkt );
484        }
485       
486        return XT_NEXT;
487}
488
[21167d2]489static const struct xt_handler_entry jabber_handlers[] = {
[bb95d43]490        { NULL,                 "stream:stream",        jabber_xmlconsole },
[21167d2]491        { "stream:stream",      "<root>",               jabber_end_of_stream },
492        { "message",            "stream:stream",        jabber_pkt_message },
493        { "presence",           "stream:stream",        jabber_pkt_presence },
[8d74291]494        { "iq",                 "stream:stream",        jabber_pkt_iq },
495        { "stream:features",    "stream:stream",        jabber_pkt_features },
[b56b220]496        { "stream:error",       "stream:stream",        jabber_pkt_stream_error },
[42127dc]497        { "proceed",            "stream:stream",        jabber_pkt_proceed_tls },
[5997488]498        { "challenge",          "stream:stream",        sasl_pkt_challenge },
499        { "success",            "stream:stream",        sasl_pkt_result },
500        { "failure",            "stream:stream",        sasl_pkt_result },
[21167d2]501        { NULL,                 NULL,                   NULL }
502};
503
[0da65d5]504gboolean jabber_start_stream( struct im_connection *ic )
[21167d2]505{
[0da65d5]506        struct jabber_data *jd = ic->proto_data;
[21167d2]507        int st;
508        char *greet;
509       
510        /* We'll start our stream now, so prepare everything to receive one
511           from the server too. */
512        xt_free( jd->xt );      /* In case we're RE-starting. */
[4bbcba3]513        jd->xt = xt_new( jabber_handlers, ic );
[21167d2]514       
[8d74291]515        if( jd->r_inpa <= 0 )
[e046390]516                jd->r_inpa = b_input_add( jd->fd, B_EV_IO_READ, jabber_read_callback, ic );
[21167d2]517       
[af7f046]518        greet = g_strdup_printf( "%s<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
519                                  "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", 
520                                  ( jd->flags & JFLAG_STARTTLS_DONE ) ? "" : "<?xml version='1.0' ?>",
521                                  jd->server );
[21167d2]522       
[0da65d5]523        st = jabber_write( ic, greet, strlen( greet ) );
[21167d2]524       
525        g_free( greet );
526       
527        return st;
528}
[4a0614e]529
[0da65d5]530void jabber_end_stream( struct im_connection *ic )
[4a0614e]531{
[0da65d5]532        struct jabber_data *jd = ic->proto_data;
[4a0614e]533       
534        /* Let's only do this if the queue is currently empty, otherwise it'd
535           take too long anyway. */
[5bcf70a]536        if( jd->tx_len == 0 )
537        {
538                char eos[] = "</stream:stream>";
539                struct xt_node *node;
[8d74291]540                int st = 1;
[5bcf70a]541               
[0da65d5]542                if( ic->flags & OPT_LOGGED_IN )
[8d74291]543                {
544                        node = jabber_make_packet( "presence", "unavailable", NULL, NULL );
[0da65d5]545                        st = jabber_write_packet( ic, node );
[8d74291]546                        xt_free_node( node );
547                }
[5bcf70a]548               
549                if( st )
[0da65d5]550                        jabber_write( ic, eos, strlen( eos ) );
[5bcf70a]551        }
[4a0614e]552}
Note: See TracBrowser for help on using the repository browser.