source: lib/oauth.c @ 782a6ee

Last change on this file since 782a6ee 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
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);
[da2efd4]98        GSList *l, *n;
[5ebff60]99
100        if (params == NULL) {
[4be0e34]101                return;
[5ebff60]102        }
103
104        for (l = *params; l; l = n) {
[da2efd4]105                n = l->next;
[5ebff60]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);
[da2efd4]111                }
112        }
113}
114
[5ebff60]115void oauth_params_set(GSList **params, const char *key, const char *value)
[da2efd4]116{
[5ebff60]117        oauth_params_del(params, key);
118        oauth_params_add(params, key, value);
[da2efd4]119}
120
[5ebff60]121const char *oauth_params_get(GSList **params, const char *key)
[da2efd4]122{
[5ebff60]123        int key_len = strlen(key);
[da2efd4]124        GSList *l;
[5ebff60]125
126        if (params == NULL) {
[bf57cd1]127                return NULL;
[da2efd4]128        }
[5ebff60]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
[da2efd4]137        return NULL;
138}
139
[5ebff60]140void oauth_params_parse(GSList **params, char *in)
[da2efd4]141{
[ee84bdb]142        char *amp, *eq, *s;
[5ebff60]143
144        while (in && *in) {
145                eq = strchr(in, '=');
146                if (!eq) {
[da2efd4]147                        break;
[5ebff60]148                }
149
[da2efd4]150                *eq = '\0';
[5ebff60]151                if ((amp = strchr(eq + 1, '&'))) {
[da2efd4]152                        *amp = '\0';
[5ebff60]153                }
154
155                s = g_strdup(eq + 1);
156                http_decode(s);
157                oauth_params_add(params, in, s);
158                g_free(s);
159
[da2efd4]160                *eq = '=';
[5ebff60]161                if (amp == NULL) {
[da2efd4]162                        break;
[5ebff60]163                }
164
[da2efd4]165                *amp = '&';
166                in = amp + 1;
167        }
168}
169
[5ebff60]170void oauth_params_free(GSList **params)
[da2efd4]171{
[5ebff60]172        while (params && *params) {
173                g_free((*params)->data);
174                *params = g_slist_remove(*params, (*params)->data);
[da2efd4]175        }
176}
177
[5ebff60]178char *oauth_params_string(GSList *params)
[da2efd4]179{
180        GSList *l;
[5ebff60]181        GString *str = g_string_new("");
182
183        for (l = params; l; l = l->next) {
[ee84bdb]184                char *s, *eq;
[5ebff60]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                }
[da2efd4]197        }
[5ebff60]198
199        return g_string_free(str, FALSE);
[da2efd4]200}
201
[5ebff60]202void oauth_info_free(struct oauth_info *info)
[18dbb20]203{
[5ebff60]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);
[18dbb20]211        }
212}
213
[5ebff60]214static void oauth_add_default_params(GSList **params, const struct oauth_service *sp)
[b2bc25c]215{
216        char *s;
[5ebff60]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
[b2bc25c]225        s = oauth_nonce();
[5ebff60]226        oauth_params_set(params, "oauth_nonce", s);
227        g_free(s);
228
229        oauth_params_set(params, "oauth_version", "1.0");
[b2bc25c]230}
231
[5ebff60]232static void *oauth_post_request(const char *url, GSList **params_, http_input_function func, struct oauth_info *oi)
[da2efd4]233{
234        GSList *params = NULL;
235        char *s, *params_s, *post;
236        void *req;
237        url_t url_p;
[5ebff60]238
239        if (!url_set(&url_p, url)) {
240                oauth_params_free(params_);
[da2efd4]241                return NULL;
242        }
[5ebff60]243
244        if (params_) {
[da2efd4]245                params = *params_;
[5ebff60]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
[da2efd4]270        return req;
271}
272
[5ebff60]273static void oauth_request_token_done(struct http_request *req);
[da2efd4]274
[5ebff60]275struct oauth_info *oauth_request_token(const struct oauth_service *sp, oauth_cb func, void *data)
[da2efd4]276{
[5ebff60]277        struct oauth_info *st = g_new0(struct oauth_info, 1);
[346dfd9]278        GSList *params = NULL;
[5ebff60]279
[da2efd4]280        st->func = func;
281        st->data = data;
[c2ecadc]282        st->sp = sp;
[5ebff60]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);
[18dbb20]288                return NULL;
289        }
[5ebff60]290
[18dbb20]291        return st;
[da2efd4]292}
293
[5ebff60]294static void oauth_request_token_done(struct http_request *req)
[da2efd4]295{
296        struct oauth_info *st = req->data;
[5ebff60]297
[da2efd4]298        st->http = req;
[5ebff60]299
300        if (req->status_code == 200) {
[508c340]301                GSList *params = NULL;
[5ebff60]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);
[da2efd4]308        }
[5ebff60]309
[c42e8b9]310        st->stage = OAUTH_REQUEST_TOKEN;
[5ebff60]311        st->func(st);
[346dfd9]312}
313
[5ebff60]314static void oauth_access_token_done(struct http_request *req);
[346dfd9]315
[5ebff60]316gboolean oauth_access_token(const char *pin, struct oauth_info *st)
[346dfd9]317{
318        GSList *params = NULL;
[5ebff60]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;
[346dfd9]324}
325
[5ebff60]326static void oauth_access_token_done(struct http_request *req)
[346dfd9]327{
[b2bc25c]328        struct oauth_info *st = req->data;
[5ebff60]329
[0bff877]330        st->http = req;
[5ebff60]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"));
[c42e8b9]337        }
[5ebff60]338
[c42e8b9]339        st->stage = OAUTH_ACCESS_TOKEN;
[5ebff60]340        if (st->func(st)) {
[c2ecadc]341                /* Don't need these anymore, but keep the rest. */
[5ebff60]342                g_free(st->auth_url);
[c2ecadc]343                st->auth_url = NULL;
[5ebff60]344                g_free(st->request_token);
[c2ecadc]345                st->request_token = NULL;
[5ebff60]346                oauth_params_free(&st->params);
[c2ecadc]347        }
[b2bc25c]348}
349
[5ebff60]350char *oauth_http_header(struct oauth_info *oi, const char *method, const char *url, char *args)
[b2bc25c]351{
[508c340]352        GSList *params = NULL, *l;
[c2ecadc]353        char *sig = NULL, *params_s, *s;
[b2bc25c]354        GString *ret = NULL;
[5ebff60]355
356        oauth_params_add(&params, "oauth_token", oi->token);
357        oauth_add_default_params(&params, oi->sp);
358
[18dbb20]359        /* Start building the OAuth header. 'key="value", '... */
[5ebff60]360        ret = g_string_new("OAuth ");
361        for (l = params; l; l = l->next) {
[b2bc25c]362                char *kv = l->data;
[5ebff60]363                char *eq = strchr(kv, '=');
364                char esc[strlen(kv) * 3 + 1];
365
366                if (eq == NULL) {
[b2bc25c]367                        break; /* WTF */
[5ebff60]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, "\", ");
[b2bc25c]377        }
[5ebff60]378
[18dbb20]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). */
[5ebff60]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);
[508c340]389        }
[5ebff60]390
[18dbb20]391        /* Append the signature and we're done! */
[5ebff60]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;
[da2efd4]401}
[f4b0911]402
[5ebff60]403char *oauth_to_string(struct oauth_info *oi)
[f4b0911]404{
405        GSList *params = NULL;
406        char *ret;
[5ebff60]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
[f4b0911]413        return ret;
414}
415
[5ebff60]416struct oauth_info *oauth_from_string(char *in, const struct oauth_service *sp)
[f4b0911]417{
[5ebff60]418        struct oauth_info *oi = g_new0(struct oauth_info, 1);
[f4b0911]419        GSList *params = NULL;
[5ebff60]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);
[f4b0911]425        oi->sp = sp;
[5ebff60]426
[f4b0911]427        return oi;
428}
Note: See TracBrowser for help on using the repository browser.