source: lib/oauth.c @ ee84bdb

Last change on this file since ee84bdb was ee84bdb, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-04-27T22:11:11Z

The escaping, I fixed it for you. More expensive code this way and most of
the vars don't need escaping. But this shouldn't be so fragile anymore.

  • Property mode set to 100644
File size: 10.8 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*                                                                           *
8*  This library is free software; you can redistribute it and/or            *
9*  modify it under the terms of the GNU Lesser General Public               *
10*  License as published by the Free Software Foundation, version            *
11*  2.1.                                                                     *
12*                                                                           *
13*  This library 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 GNU        *
16*  Lesser General Public License for more details.                          *
17*                                                                           *
18*  You should have received a copy of the GNU Lesser General Public License *
19*  along with this library; if not, write to the Free Software Foundation,  *
20*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
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 CONSUMER_KEY "xsDNKJuNZYkZyMcu914uEA"
36#define CONSUMER_SECRET "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo"
37/* How can it be a secret if it's right here in the source code? No clue... */
38
39#define HMAC_BLOCK_SIZE 64
40
41static char *oauth_sign( const char *method, const char *url,
42                         const char *params, const char *token_secret )
43{
44        sha1_state_t sha1;
45        uint8_t hash[sha1_hash_size];
46        uint8_t key[HMAC_BLOCK_SIZE+1];
47        char *s;
48        int i;
49       
50        /* Create K. If our current key is >64 chars we have to hash it,
51           otherwise just pad. */
52        memset( key, 0, HMAC_BLOCK_SIZE );
[da2efd4]53        i = strlen( CONSUMER_SECRET ) + 1 + ( token_secret ? strlen( token_secret ) : 0 );
[be28fe7]54        if( i > HMAC_BLOCK_SIZE )
55        {
56                sha1_init( &sha1 );
[da2efd4]57                sha1_append( &sha1, (uint8_t*) CONSUMER_SECRET, strlen( CONSUMER_SECRET ) );
58                sha1_append( &sha1, (uint8_t*) "&", 1 );
[be28fe7]59                if( token_secret )
[da2efd4]60                        sha1_append( &sha1, (uint8_t*) token_secret, strlen( token_secret ) );
[be28fe7]61                sha1_finish( &sha1, key );
62        }
63        else
64        {
[da2efd4]65                g_snprintf( (gchar*) key, HMAC_BLOCK_SIZE + 1, "%s&%s",
[be28fe7]66                            CONSUMER_SECRET, token_secret ? : "" );
67        }
68       
69        /* Inner part: H(K XOR 0x36, text) */
70        sha1_init( &sha1 );
71       
72        for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
73                key[i] ^= 0x36;
74        sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
75       
76        /* OAuth: text = method&url&params, all http_encoded. */
77        sha1_append( &sha1, (const uint8_t*) method, strlen( method ) );
78        sha1_append( &sha1, (const uint8_t*) "&", 1 );
79       
80        s = g_new0( char, strlen( url ) * 3 + 1 );
81        strcpy( s, url );
82        http_encode( s );
83        sha1_append( &sha1, (const uint8_t*) s, strlen( s ) );
84        sha1_append( &sha1, (const uint8_t*) "&", 1 );
85        g_free( s );
86       
87        s = g_new0( char, strlen( params ) * 3 + 1 );
88        strcpy( s, params );
89        http_encode( s );
90        sha1_append( &sha1, (const uint8_t*) s, strlen( s ) );
91        g_free( s );
92       
93        sha1_finish( &sha1, hash );
94       
95        /* Final result: H(K XOR 0x5C, inner stuff) */
96        sha1_init( &sha1 );
97        for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
98                key[i] ^= 0x36 ^ 0x5c;
99        sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
100        sha1_append( &sha1, hash, sha1_hash_size );
101        sha1_finish( &sha1, hash );
102       
[ee84bdb]103        /* base64_encode + HTTP escape it (both consumers
104           need it that away) and we're done. */
105        s = base64_encode( hash, sha1_hash_size );
106        s = g_realloc( s, strlen( s ) * 3 + 1 );
107        http_encode( s );
108       
109        return s;
[be28fe7]110}
[da2efd4]111
112static char *oauth_nonce()
113{
114        unsigned char bytes[9];
115       
116        random_bytes( bytes, sizeof( bytes ) );
[ee84bdb]117        return base64_encode( bytes, sizeof( bytes ) );
[da2efd4]118}
119
120void oauth_params_add( GSList **params, const char *key, const char *value )
121{
122        char *item;
123       
124        item = g_strdup_printf( "%s=%s", key, value );
125        *params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp );
126}
127
128void oauth_params_del( GSList **params, const char *key )
129{
130        int key_len = strlen( key );
131        GSList *l, *n;
132       
133        for( l = *params; l; l = n )
134        {
135                n = l->next;
136               
137                if( strncmp( (char*) l->data, key, key_len ) == 0 &&
138                    ((char*)l->data)[key_len] == '=' )
139                {
140                        g_free( l->data );
[b2bc25c]141                        *params = g_slist_remove( *params, l->data );
[da2efd4]142                }
143        }
144}
145
146void oauth_params_set( GSList **params, const char *key, const char *value )
147{
148        oauth_params_del( params, key );
149        oauth_params_add( params, key, value );
150}
151
152const char *oauth_params_get( GSList **params, const char *key )
153{
154        int key_len = strlen( key );
155        GSList *l;
156       
157        for( l = *params; l; l = l->next )
158        {
159                if( strncmp( (char*) l->data, key, key_len ) == 0 &&
160                    ((char*)l->data)[key_len] == '=' )
161                        return (const char*) l->data + key_len + 1;
162        }
163       
164        return NULL;
165}
166
[508c340]167static void oauth_params_parse( GSList **params, char *in )
[da2efd4]168{
[ee84bdb]169        char *amp, *eq, *s;
[da2efd4]170       
171        while( in && *in )
172        {
173                eq = strchr( in, '=' );
174                if( !eq )
175                        break;
176               
177                *eq = '\0';
178                if( ( amp = strchr( eq + 1, '&' ) ) )
179                        *amp = '\0';
180               
[ee84bdb]181                s = g_strdup( eq + 1 );
182                http_decode( s );
183                oauth_params_add( params, in, s );
184                g_free( s );
[da2efd4]185               
186                *eq = '=';
187                if( amp == NULL )
188                        break;
189               
190                *amp = '&';
191                in = amp + 1;
192        }
193}
194
195void oauth_params_free( GSList **params )
196{
197        while( params && *params )
198        {
199                g_free( (*params)->data );
200                *params = g_slist_remove( *params, (*params)->data );
201        }
202}
203
204char *oauth_params_string( GSList *params )
205{
206        GSList *l;
207        GString *str = g_string_new( "" );
208       
209        for( l = params; l; l = l->next )
210        {
[ee84bdb]211                char *s, *eq;
212               
213                s = g_malloc( strlen( l->data ) * 3 + 1 );
214                strcpy( s, l->data );
215                if( ( eq = strchr( s, '=' ) ) )
216                        http_encode( eq + 1 );
217                g_string_append( str, s );
218                g_free( s );
219               
[da2efd4]220                if( l->next )
221                        g_string_append_c( str, '&' );
222        }
223       
224        return g_string_free( str, FALSE );
225}
226
[b2bc25c]227static void oauth_add_default_params( GSList **params )
228{
229        char *s;
230       
231        oauth_params_set( params, "oauth_consumer_key", CONSUMER_KEY );
232        oauth_params_set( params, "oauth_signature_method", "HMAC-SHA1" );
233       
234        s = g_strdup_printf( "%d", (int) time( NULL ) );
235        oauth_params_set( params, "oauth_timestamp", s );
236        g_free( s );
237       
238        s = oauth_nonce();
239        oauth_params_set( params, "oauth_nonce", s );
240        g_free( s );
241       
242        oauth_params_set( params, "oauth_version", "1.0" );
243}
244
[da2efd4]245static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, void *data )
246{
247        GSList *params = NULL;
248        char *s, *params_s, *post;
249        void *req;
250        url_t url_p;
251       
252        if( !url_set( &url_p, url ) )
253        {
254                oauth_params_free( params_ );
255                return NULL;
256        }
257       
258        if( params_ )
259                params = *params_;
260       
[b2bc25c]261        oauth_add_default_params( &params );
[da2efd4]262       
263        params_s = oauth_params_string( params );
264        oauth_params_free( params_ );
265       
266        s = oauth_sign( "POST", url, params_s, NULL );
267        post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s );
268        g_free( params_s );
269        g_free( s );
270       
271        s = g_strdup_printf( "POST %s HTTP/1.0\r\n"
272                             "Host: %s\r\n"
273                             "Content-Type: application/x-www-form-urlencoded\r\n"
274                             "Content-Length: %zd\r\n"
275                             "\r\n"
276                             "%s", url_p.file, url_p.host, strlen( post ), post );
277        g_free( post );
278       
279        req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS,
280                              s, func, data );
281        g_free( s );
282       
283        return req;
284}
285
[346dfd9]286static void oauth_request_token_done( struct http_request *req );
[da2efd4]287
288void *oauth_request_token( const char *url, oauth_cb func, void *data )
289{
290        struct oauth_info *st = g_new0( struct oauth_info, 1 );
[346dfd9]291        GSList *params = NULL;
[da2efd4]292       
293        st->func = func;
294        st->data = data;
295       
[346dfd9]296        oauth_params_add( &params, "oauth_callback", "oob" );
297       
[da2efd4]298        return oauth_post_request( url, NULL, oauth_request_token_done, st );
299}
300
[346dfd9]301static void oauth_request_token_done( struct http_request *req )
[da2efd4]302{
303        struct oauth_info *st = req->data;
304       
305        st->http = req;
306       
307        if( req->status_code == 200 )
308        {
[508c340]309                GSList *params = NULL;
[da2efd4]310               
311                st->auth_params = g_strdup( req->reply_body );
[508c340]312                oauth_params_parse( &params, st->auth_params );
[713d611]313                st->request_token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
[da2efd4]314                oauth_params_free( &params );
315        }
316       
[c42e8b9]317        st->stage = OAUTH_REQUEST_TOKEN;
[713d611]318        st->func( st );
[346dfd9]319}
320
321static void oauth_access_token_done( struct http_request *req );
322
323void *oauth_access_token( const char *url, const char *pin, struct oauth_info *st )
324{
325        GSList *params = NULL;
326       
[713d611]327        oauth_params_add( &params, "oauth_token", st->request_token );
[346dfd9]328        oauth_params_add( &params, "oauth_verifier", pin );
329       
330        return oauth_post_request( url, &params, oauth_access_token_done, st );
331}
332
333static void oauth_access_token_done( struct http_request *req )
334{
[b2bc25c]335        struct oauth_info *st = req->data;
336       
337        if( req->status_code == 200 )
[c42e8b9]338        {
339                GSList *params;
340                const char *token, *token_secret;
341               
342                oauth_params_parse( &params, req->reply_body );
343                token = oauth_params_get( &params, "oauth_token" );
344                token_secret = oauth_params_get( &params, "oauth_token_secret" );
345                st->access_token = g_strdup_printf(
346                        "oauth_token=%s&oauth_token_secret=%s", token, token_secret );
347                oauth_params_free( &params );
348        }
[b2bc25c]349       
[c42e8b9]350        st->stage = OAUTH_ACCESS_TOKEN;
[713d611]351        st->func( st );
[b2bc25c]352}
353
[508c340]354char *oauth_http_header( char *access_token, const char *method, const char *url, char *args )
[b2bc25c]355{
[508c340]356        GSList *params = NULL, *l;
357        char *token_secret, *sig, *params_s, *s;
[b2bc25c]358        GString *ret = NULL;
359       
[508c340]360        oauth_params_parse( &params, access_token );
[b2bc25c]361        if( params == NULL )
362                goto err;
363        token_secret = g_strdup( oauth_params_get( &params, "oauth_token_secret" ) );
364        if( token_secret == NULL )
365                goto err;
366        oauth_params_del( &params, "oauth_token_secret" );
367       
368        oauth_add_default_params( &params );
369       
370        ret = g_string_new( "OAuth " );
371        for( l = params; l; l = l->next )
372        {
373                char *kv = l->data;
374                char *eq = strchr( kv, '=' );
375                char esc[strlen(kv)*3+1];
376               
377                if( eq == NULL )
378                        break; /* WTF */
379               
380                strcpy( esc, eq + 1 );
381                http_encode( esc );
382               
383                g_string_append_len( ret, kv, eq - kv + 1 );
384                g_string_append_c( ret, '"' );
385                g_string_append( ret, esc );
386                g_string_append( ret, "\", " );
387        }
388       
[508c340]389        if( args )
390                oauth_params_parse( &params, args );
391        if( ( s = strchr( url, '?' ) ) )
392        {
393                s = g_strdup( s + 1 );
394                oauth_params_parse( &params, s + 1 );
395                g_free( s );
396        }
397       
398        params_s = oauth_params_string( params );
399        sig = oauth_sign( method, url, params_s, token_secret );
[b2bc25c]400        g_string_append_printf( ret, "oauth_signature=\"%s\"", sig );
[ee84bdb]401        g_free( params_s );
[b2bc25c]402       
403err:
404        oauth_params_free( &params );
405        g_free( sig );
406        g_free( token_secret );
407       
408        return ret ? g_string_free( ret, FALSE ) : NULL;
[da2efd4]409}
Note: See TracBrowser for help on using the repository browser.