source: lib/oauth.c @ 90adf84

Last change on this file since 90adf84 was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

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