source: lib/oauth.c @ 8bcd160

Last change on this file since 8bcd160 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
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
[5ebff60]37static char *oauth_sign(const char *method, const char *url,
38                        const char *params, struct oauth_info *oi)
[be28fe7]39{
[34afea7]40        uint8_t hash[SHA1_HASH_SIZE];
[5ebff60]41        GString *payload = g_string_new("");
[31db8165]42        char *key;
[be28fe7]43        char *s;
[5ebff60]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
[ee84bdb]67           need it that away) and we're done. */
[5ebff60]68        s = base64_encode(hash, SHA1_HASH_SIZE);
69        s = g_realloc(s, strlen(s) * 3 + 1);
70        http_encode(s);
71
[ee84bdb]72        return s;
[be28fe7]73}
[da2efd4]74
75static char *oauth_nonce()
76{
[ce617f0]77        unsigned char bytes[21];
[5ebff60]78
79        random_bytes(bytes, sizeof(bytes));
80        return base64_encode(bytes, sizeof(bytes));
[da2efd4]81}
82
[5ebff60]83void oauth_params_add(GSList **params, const char *key, const char *value)
[da2efd4]84{
85        char *item;
[5ebff60]86
87        if (!key || !value) {
[f138bd2]88                return;
[5ebff60]89        }
90
91        item = g_strdup_printf("%s=%s", key, value);
92        *params = g_slist_insert_sorted(*params, item, (GCompareFunc) strcmp);
[da2efd4]93}
94
[5ebff60]95void oauth_params_del(GSList **params, const char *key)
[da2efd4]96{
[5ebff60]97        int key_len = strlen(key);
[a880e34]98        GSList *l;
[5ebff60]99
[a880e34]100        if (!params) {
[4be0e34]101                return;
[5ebff60]102        }
103
[a880e34]104        for (l = *params; l; l = l->next) {
105                char *data = l->data;
[5ebff60]106
[a880e34]107                if (strncmp(data, key, key_len) == 0 && data[key_len] == '=') {
108                        *params = g_slist_remove(*params, data);
109                        g_free(data);
[da2efd4]110                }
111        }
112}
113
[5ebff60]114void oauth_params_set(GSList **params, const char *key, const char *value)
[da2efd4]115{
[5ebff60]116        oauth_params_del(params, key);
117        oauth_params_add(params, key, value);
[da2efd4]118}
119
[5ebff60]120const char *oauth_params_get(GSList **params, const char *key)
[da2efd4]121{
[5ebff60]122        int key_len = strlen(key);
[da2efd4]123        GSList *l;
[5ebff60]124
125        if (params == NULL) {
[bf57cd1]126                return NULL;
[da2efd4]127        }
[5ebff60]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
[da2efd4]136        return NULL;
137}
138
[5ebff60]139void oauth_params_parse(GSList **params, char *in)
[da2efd4]140{
[ee84bdb]141        char *amp, *eq, *s;
[5ebff60]142
143        while (in && *in) {
144                eq = strchr(in, '=');
145                if (!eq) {
[da2efd4]146                        break;
[5ebff60]147                }
148
[da2efd4]149                *eq = '\0';
[5ebff60]150                if ((amp = strchr(eq + 1, '&'))) {
[da2efd4]151                        *amp = '\0';
[5ebff60]152                }
153
154                s = g_strdup(eq + 1);
155                http_decode(s);
156                oauth_params_add(params, in, s);
157                g_free(s);
158
[da2efd4]159                *eq = '=';
[5ebff60]160                if (amp == NULL) {
[da2efd4]161                        break;
[5ebff60]162                }
163
[da2efd4]164                *amp = '&';
165                in = amp + 1;
166        }
167}
168
[5ebff60]169void oauth_params_free(GSList **params)
[da2efd4]170{
[5ebff60]171        while (params && *params) {
172                g_free((*params)->data);
173                *params = g_slist_remove(*params, (*params)->data);
[da2efd4]174        }
175}
176
[5ebff60]177char *oauth_params_string(GSList *params)
[da2efd4]178{
179        GSList *l;
[5ebff60]180        GString *str = g_string_new("");
181
182        for (l = params; l; l = l->next) {
[ee84bdb]183                char *s, *eq;
[5ebff60]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                }
[da2efd4]196        }
[5ebff60]197
198        return g_string_free(str, FALSE);
[da2efd4]199}
200
[5ebff60]201void oauth_info_free(struct oauth_info *info)
[18dbb20]202{
[5ebff60]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);
[18dbb20]210        }
211}
212
[5ebff60]213static void oauth_add_default_params(GSList **params, const struct oauth_service *sp)
[b2bc25c]214{
215        char *s;
[5ebff60]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
[b2bc25c]224        s = oauth_nonce();
[5ebff60]225        oauth_params_set(params, "oauth_nonce", s);
226        g_free(s);
227
228        oauth_params_set(params, "oauth_version", "1.0");
[b2bc25c]229}
230
[5ebff60]231static void *oauth_post_request(const char *url, GSList **params_, http_input_function func, struct oauth_info *oi)
[da2efd4]232{
233        GSList *params = NULL;
234        char *s, *params_s, *post;
235        void *req;
236        url_t url_p;
[5ebff60]237
238        if (!url_set(&url_p, url)) {
239                oauth_params_free(params_);
[da2efd4]240                return NULL;
241        }
[5ebff60]242
243        if (params_) {
[da2efd4]244                params = *params_;
[5ebff60]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
[da2efd4]269        return req;
270}
271
[5ebff60]272static void oauth_request_token_done(struct http_request *req);
[da2efd4]273
[5ebff60]274struct oauth_info *oauth_request_token(const struct oauth_service *sp, oauth_cb func, void *data)
[da2efd4]275{
[5ebff60]276        struct oauth_info *st = g_new0(struct oauth_info, 1);
[346dfd9]277        GSList *params = NULL;
[5ebff60]278
[da2efd4]279        st->func = func;
280        st->data = data;
[c2ecadc]281        st->sp = sp;
[5ebff60]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);
[18dbb20]287                return NULL;
288        }
[5ebff60]289
[18dbb20]290        return st;
[da2efd4]291}
292
[5ebff60]293static void oauth_request_token_done(struct http_request *req)
[da2efd4]294{
295        struct oauth_info *st = req->data;
[5ebff60]296
[da2efd4]297        st->http = req;
[5ebff60]298
299        if (req->status_code == 200) {
[508c340]300                GSList *params = NULL;
[5ebff60]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);
[da2efd4]307        }
[5ebff60]308
[c42e8b9]309        st->stage = OAUTH_REQUEST_TOKEN;
[5ebff60]310        st->func(st);
[346dfd9]311}
312
[5ebff60]313static void oauth_access_token_done(struct http_request *req);
[346dfd9]314
[5ebff60]315gboolean oauth_access_token(const char *pin, struct oauth_info *st)
[346dfd9]316{
317        GSList *params = NULL;
[5ebff60]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;
[346dfd9]323}
324
[5ebff60]325static void oauth_access_token_done(struct http_request *req)
[346dfd9]326{
[b2bc25c]327        struct oauth_info *st = req->data;
[5ebff60]328
[0bff877]329        st->http = req;
[5ebff60]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"));
[c42e8b9]336        }
[5ebff60]337
[c42e8b9]338        st->stage = OAUTH_ACCESS_TOKEN;
[5ebff60]339        if (st->func(st)) {
[c2ecadc]340                /* Don't need these anymore, but keep the rest. */
[5ebff60]341                g_free(st->auth_url);
[c2ecadc]342                st->auth_url = NULL;
[5ebff60]343                g_free(st->request_token);
[c2ecadc]344                st->request_token = NULL;
[5ebff60]345                oauth_params_free(&st->params);
[c2ecadc]346        }
[b2bc25c]347}
348
[5ebff60]349char *oauth_http_header(struct oauth_info *oi, const char *method, const char *url, char *args)
[b2bc25c]350{
[508c340]351        GSList *params = NULL, *l;
[c2ecadc]352        char *sig = NULL, *params_s, *s;
[b2bc25c]353        GString *ret = NULL;
[5ebff60]354
355        oauth_params_add(&params, "oauth_token", oi->token);
356        oauth_add_default_params(&params, oi->sp);
357
[18dbb20]358        /* Start building the OAuth header. 'key="value", '... */
[5ebff60]359        ret = g_string_new("OAuth ");
360        for (l = params; l; l = l->next) {
[b2bc25c]361                char *kv = l->data;
[5ebff60]362                char *eq = strchr(kv, '=');
363                char esc[strlen(kv) * 3 + 1];
364
365                if (eq == NULL) {
[b2bc25c]366                        break; /* WTF */
[5ebff60]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, "\", ");
[b2bc25c]376        }
[5ebff60]377
[18dbb20]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). */
[5ebff60]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);
[508c340]388        }
[5ebff60]389
[18dbb20]390        /* Append the signature and we're done! */
[5ebff60]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;
[da2efd4]400}
[f4b0911]401
[5ebff60]402char *oauth_to_string(struct oauth_info *oi)
[f4b0911]403{
404        GSList *params = NULL;
405        char *ret;
[5ebff60]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
[f4b0911]412        return ret;
413}
414
[5ebff60]415struct oauth_info *oauth_from_string(char *in, const struct oauth_service *sp)
[f4b0911]416{
[5ebff60]417        struct oauth_info *oi = g_new0(struct oauth_info, 1);
[f4b0911]418        GSList *params = NULL;
[5ebff60]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);
[f4b0911]424        oi->sp = sp;
[5ebff60]425
[f4b0911]426        return oi;
427}
Note: See TracBrowser for help on using the repository browser.