source: lib/oauth.c @ 1522faf

Last change on this file since 1522faf was a880e34, checked in by dequis <dx@…>, at 2015-02-28T20:40:07Z

Refactor oauth_params_del to fix use-after-free that i introduced

Yeah ok that was dumb.

This is essentially just using a 'data' variable instead of 'l->data',
but i went ahead and cleaned up the function.

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