source: lib/oauth.c @ 6f10697

Last change on this file since 6f10697 was b38d399, checked in by dequis <dx@…>, at 2014-11-24T05:16:09Z

Use glib functions for base64 decoding/encoding

This fixes several coverity warnings about 'tainted data index sink' and
a fixme about thread safety in the old base64_decode implementation.

Had to adapt the code that used base64_encode_real:

  • oauth.c: different character set order, but it's for the nonce so it doesn't matter
  • libyahoo2.c: used as part of the auth, changes "+/=" into "._-". Fixed by encoding first the usual way through glib, then replacing.
  • Property mode set to 100644
File size: 11.0 KB
RevLine 
[be28fe7]1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Simple OAuth client (consumer) implementation.                           *
5*                                                                           *
6*  Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net>                   *
7*                                                                           *
[4f7255d]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.                                      *
[be28fe7]12*                                                                           *
[4f7255d]13*  This program is distributed in the hope that it will be useful,          *
[be28fe7]14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
[4f7255d]15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
[be28fe7]17*                                                                           *
[4f7255d]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.              *
[be28fe7]21*                                                                           *
22\***************************************************************************/
23
24#include <glib.h>
25#include <gmodule.h>
26#include <stdlib.h>
27#include <string.h>
[da2efd4]28#include "http_client.h"
[be28fe7]29#include "base64.h"
30#include "misc.h"
31#include "sha1.h"
[da2efd4]32#include "url.h"
[acba168]33#include "oauth.h"
[be28fe7]34
35#define HMAC_BLOCK_SIZE 64
36
37static char *oauth_sign( const char *method, const char *url,
[c2ecadc]38                         const char *params, struct oauth_info *oi )
[be28fe7]39{
40        uint8_t hash[sha1_hash_size];
[31db8165]41        GString *payload = g_string_new( "" );
42        char *key;
[be28fe7]43        char *s;
44       
[31db8165]45        key = g_strdup_printf( "%s&%s", oi->sp->consumer_secret, oi->token_secret ? oi->token_secret : "" );
[be28fe7]46       
[31db8165]47        g_string_append_printf( payload, "%s&", method );
[be28fe7]48       
49        s = g_new0( char, strlen( url ) * 3 + 1 );
50        strcpy( s, url );
51        http_encode( s );
[31db8165]52        g_string_append_printf( payload, "%s&", s );
[be28fe7]53        g_free( s );
54       
55        s = g_new0( char, strlen( params ) * 3 + 1 );
56        strcpy( s, params );
57        http_encode( s );
[31db8165]58        g_string_append( payload, s );
[be28fe7]59        g_free( s );
60       
[31db8165]61        sha1_hmac( key, 0, payload->str, 0, hash );
[be28fe7]62       
[31db8165]63        g_free( key );
64        g_string_free( payload, TRUE );
[be28fe7]65       
[ee84bdb]66        /* base64_encode + HTTP escape it (both consumers
67           need it that away) and we're done. */
68        s = base64_encode( hash, sha1_hash_size );
69        s = g_realloc( s, strlen( s ) * 3 + 1 );
70        http_encode( s );
71       
72        return s;
[be28fe7]73}
[da2efd4]74
75static char *oauth_nonce()
76{
[ce617f0]77        unsigned char bytes[21];
[da2efd4]78        random_bytes( bytes, sizeof( bytes ) );
[b38d399]79        return base64_encode( bytes, sizeof( bytes ) );
[da2efd4]80}
81
82void oauth_params_add( GSList **params, const char *key, const char *value )
83{
84        char *item;
85       
[f138bd2]86        if( !key || !value )
87                return;
88       
[da2efd4]89        item = g_strdup_printf( "%s=%s", key, value );
90        *params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp );
91}
92
93void oauth_params_del( GSList **params, const char *key )
94{
95        int key_len = strlen( key );
96        GSList *l, *n;
97       
[bf57cd1]98        if( params == NULL )
[4be0e34]99                return;
[bf57cd1]100       
[da2efd4]101        for( l = *params; l; l = n )
102        {
103                n = l->next;
104               
105                if( strncmp( (char*) l->data, key, key_len ) == 0 &&
106                    ((char*)l->data)[key_len] == '=' )
107                {
108                        g_free( l->data );
[b2bc25c]109                        *params = g_slist_remove( *params, l->data );
[da2efd4]110                }
111        }
112}
113
114void oauth_params_set( GSList **params, const char *key, const char *value )
115{
116        oauth_params_del( params, key );
117        oauth_params_add( params, key, value );
118}
119
120const char *oauth_params_get( GSList **params, const char *key )
121{
122        int key_len = strlen( key );
123        GSList *l;
124       
[bf57cd1]125        if( params == NULL )
126                return NULL;
127       
[da2efd4]128        for( l = *params; l; l = l->next )
129        {
130                if( strncmp( (char*) l->data, key, key_len ) == 0 &&
131                    ((char*)l->data)[key_len] == '=' )
132                        return (const char*) l->data + key_len + 1;
133        }
134       
135        return NULL;
136}
137
[aa9f1ac]138void oauth_params_parse( GSList **params, char *in )
[da2efd4]139{
[ee84bdb]140        char *amp, *eq, *s;
[da2efd4]141       
142        while( in && *in )
143        {
144                eq = strchr( in, '=' );
145                if( !eq )
146                        break;
147               
148                *eq = '\0';
149                if( ( amp = strchr( eq + 1, '&' ) ) )
150                        *amp = '\0';
151               
[ee84bdb]152                s = g_strdup( eq + 1 );
153                http_decode( s );
154                oauth_params_add( params, in, s );
155                g_free( s );
[da2efd4]156               
157                *eq = '=';
158                if( amp == NULL )
159                        break;
160               
161                *amp = '&';
162                in = amp + 1;
163        }
164}
165
166void oauth_params_free( GSList **params )
167{
168        while( params && *params )
169        {
170                g_free( (*params)->data );
171                *params = g_slist_remove( *params, (*params)->data );
172        }
173}
174
175char *oauth_params_string( GSList *params )
176{
177        GSList *l;
178        GString *str = g_string_new( "" );
179       
180        for( l = params; l; l = l->next )
181        {
[ee84bdb]182                char *s, *eq;
183               
184                s = g_malloc( strlen( l->data ) * 3 + 1 );
185                strcpy( s, l->data );
186                if( ( eq = strchr( s, '=' ) ) )
187                        http_encode( eq + 1 );
188                g_string_append( str, s );
189                g_free( s );
190               
[da2efd4]191                if( l->next )
192                        g_string_append_c( str, '&' );
193        }
194       
195        return g_string_free( str, FALSE );
196}
197
[18dbb20]198void oauth_info_free( struct oauth_info *info )
199{
200        if( info )
201        {
[c2ecadc]202                g_free( info->auth_url );
[18dbb20]203                g_free( info->request_token );
[c2ecadc]204                g_free( info->token );
205                g_free( info->token_secret );
[93cc86f]206                oauth_params_free( &info->params );
[18dbb20]207                g_free( info );
208        }
209}
210
[3b878a1]211static void oauth_add_default_params( GSList **params, const struct oauth_service *sp )
[b2bc25c]212{
213        char *s;
214       
[c2ecadc]215        oauth_params_set( params, "oauth_consumer_key", sp->consumer_key );
[b2bc25c]216        oauth_params_set( params, "oauth_signature_method", "HMAC-SHA1" );
217       
218        s = g_strdup_printf( "%d", (int) time( NULL ) );
219        oauth_params_set( params, "oauth_timestamp", s );
220        g_free( s );
221       
222        s = oauth_nonce();
223        oauth_params_set( params, "oauth_nonce", s );
224        g_free( s );
225       
226        oauth_params_set( params, "oauth_version", "1.0" );
227}
228
[c2ecadc]229static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, struct oauth_info *oi )
[da2efd4]230{
231        GSList *params = NULL;
232        char *s, *params_s, *post;
233        void *req;
234        url_t url_p;
235       
236        if( !url_set( &url_p, url ) )
237        {
238                oauth_params_free( params_ );
239                return NULL;
240        }
241       
242        if( params_ )
243                params = *params_;
244       
[c2ecadc]245        oauth_add_default_params( &params, oi->sp );
[da2efd4]246       
247        params_s = oauth_params_string( params );
[0bff877]248        oauth_params_free( &params );
[da2efd4]249       
[c2ecadc]250        s = oauth_sign( "POST", url, params_s, oi );
[da2efd4]251        post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s );
252        g_free( params_s );
253        g_free( s );
254       
255        s = g_strdup_printf( "POST %s HTTP/1.0\r\n"
256                             "Host: %s\r\n"
257                             "Content-Type: application/x-www-form-urlencoded\r\n"
258                             "Content-Length: %zd\r\n"
259                             "\r\n"
260                             "%s", url_p.file, url_p.host, strlen( post ), post );
261        g_free( post );
262       
263        req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS,
[c2ecadc]264                              s, func, oi );
[da2efd4]265        g_free( s );
266       
267        return req;
268}
269
[346dfd9]270static void oauth_request_token_done( struct http_request *req );
[da2efd4]271
[3b878a1]272struct oauth_info *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data )
[da2efd4]273{
274        struct oauth_info *st = g_new0( struct oauth_info, 1 );
[346dfd9]275        GSList *params = NULL;
[da2efd4]276       
277        st->func = func;
278        st->data = data;
[c2ecadc]279        st->sp = sp;
[da2efd4]280       
[346dfd9]281        oauth_params_add( &params, "oauth_callback", "oob" );
282       
[c2ecadc]283        if( !oauth_post_request( sp->url_request_token, &params, oauth_request_token_done, st ) )
[18dbb20]284        {
285                oauth_info_free( st );
286                return NULL;
287        }
288       
289        return st;
[da2efd4]290}
291
[346dfd9]292static void oauth_request_token_done( struct http_request *req )
[da2efd4]293{
294        struct oauth_info *st = req->data;
295       
296        st->http = req;
297       
298        if( req->status_code == 200 )
299        {
[508c340]300                GSList *params = NULL;
[da2efd4]301               
[c2ecadc]302                st->auth_url = g_strdup_printf( "%s?%s", st->sp->url_authorize, req->reply_body );
303                oauth_params_parse( &params, req->reply_body );
[713d611]304                st->request_token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
[e306fbf]305                st->token_secret = g_strdup( oauth_params_get( &params, "oauth_token_secret" ) );
[da2efd4]306                oauth_params_free( &params );
307        }
308       
[c42e8b9]309        st->stage = OAUTH_REQUEST_TOKEN;
[c2ecadc]310        st->func( st );
[346dfd9]311}
312
313static void oauth_access_token_done( struct http_request *req );
314
[c2ecadc]315gboolean oauth_access_token( const char *pin, struct oauth_info *st )
[346dfd9]316{
317        GSList *params = NULL;
318       
[713d611]319        oauth_params_add( &params, "oauth_token", st->request_token );
[346dfd9]320        oauth_params_add( &params, "oauth_verifier", pin );
321       
[c2ecadc]322        return oauth_post_request( st->sp->url_access_token, &params, oauth_access_token_done, st ) != NULL;
[346dfd9]323}
324
325static void oauth_access_token_done( struct http_request *req )
326{
[b2bc25c]327        struct oauth_info *st = req->data;
328       
[0bff877]329        st->http = req;
330       
[b2bc25c]331        if( req->status_code == 200 )
[c42e8b9]332        {
[93cc86f]333                oauth_params_parse( &st->params, req->reply_body );
334                st->token = g_strdup( oauth_params_get( &st->params, "oauth_token" ) );
[e306fbf]335                g_free( st->token_secret );
[93cc86f]336                st->token_secret = g_strdup( oauth_params_get( &st->params, "oauth_token_secret" ) );
[c42e8b9]337        }
[b2bc25c]338       
[c42e8b9]339        st->stage = OAUTH_ACCESS_TOKEN;
[c2ecadc]340        if( st->func( st ) )
341        {
342                /* Don't need these anymore, but keep the rest. */
343                g_free( st->auth_url );
344                st->auth_url = NULL;
345                g_free( st->request_token );
346                st->request_token = NULL;
[93cc86f]347                oauth_params_free( &st->params );
[c2ecadc]348        }
[b2bc25c]349}
350
[c2ecadc]351char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args )
[b2bc25c]352{
[508c340]353        GSList *params = NULL, *l;
[c2ecadc]354        char *sig = NULL, *params_s, *s;
[b2bc25c]355        GString *ret = NULL;
356       
[c2ecadc]357        oauth_params_add( &params, "oauth_token", oi->token );
358        oauth_add_default_params( &params, oi->sp );
[b2bc25c]359       
[18dbb20]360        /* Start building the OAuth header. 'key="value", '... */
[b2bc25c]361        ret = g_string_new( "OAuth " );
362        for( l = params; l; l = l->next )
363        {
364                char *kv = l->data;
365                char *eq = strchr( kv, '=' );
366                char esc[strlen(kv)*3+1];
367               
368                if( eq == NULL )
369                        break; /* WTF */
370               
371                strcpy( esc, eq + 1 );
372                http_encode( esc );
373               
374                g_string_append_len( ret, kv, eq - kv + 1 );
375                g_string_append_c( ret, '"' );
376                g_string_append( ret, esc );
377                g_string_append( ret, "\", " );
378        }
379       
[18dbb20]380        /* Now, before generating the signature, add GET/POST arguments to params
381           since they should be included in the base signature string (but not in
382           the HTTP header). */
[508c340]383        if( args )
384                oauth_params_parse( &params, args );
385        if( ( s = strchr( url, '?' ) ) )
386        {
387                s = g_strdup( s + 1 );
[c2ecadc]388                oauth_params_parse( &params, s );
[508c340]389                g_free( s );
390        }
391       
[18dbb20]392        /* Append the signature and we're done! */
[508c340]393        params_s = oauth_params_string( params );
[c2ecadc]394        sig = oauth_sign( method, url, params_s, oi );
[b2bc25c]395        g_string_append_printf( ret, "oauth_signature=\"%s\"", sig );
[ee84bdb]396        g_free( params_s );
[b2bc25c]397       
398        oauth_params_free( &params );
399        g_free( sig );
400       
401        return ret ? g_string_free( ret, FALSE ) : NULL;
[da2efd4]402}
[f4b0911]403
404char *oauth_to_string( struct oauth_info *oi )
405{
406        GSList *params = NULL;
407        char *ret;
408       
409        oauth_params_add( &params, "oauth_token", oi->token );
410        oauth_params_add( &params, "oauth_token_secret", oi->token_secret );
411        ret = oauth_params_string( params );
412        oauth_params_free( &params );
413       
414        return ret;
415}
416
[3b878a1]417struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp )
[f4b0911]418{
419        struct oauth_info *oi = g_new0( struct oauth_info, 1 );
420        GSList *params = NULL;
421       
422        oauth_params_parse( &params, in );
423        oi->token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
424        oi->token_secret = g_strdup( oauth_params_get( &params, "oauth_token_secret" ) );
425        oauth_params_free( &params );
426        oi->sp = sp;
427       
428        return oi;
429}
Note: See TracBrowser for help on using the repository browser.