source: lib/oauth2.c @ b39859e

Last change on this file since b39859e was 5535a47, checked in by dequis <dx@…>, at 2015-05-07T23:12:06Z

More coverity fixes!

CID 18634: 'Logically dead code' in jabber_get_info
CID 18638: 'Dereference after null check' in oauth2_access_token_done
CID 18691: 'Copy into fixed size buffer' in bee_irc_user_new
CID 20274: Leak in bee_irc_chat_invite
CID 20297, CID 20283: Leaks in crypt_main

Some the base64 leaks there weren't detected, needs modeling.

  • Property mode set to 100644
File size: 6.8 KB
RevLine 
[57b4525]1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
[c153808]4*  Simple OAuth2 client (consumer) implementation.                          *
[57b4525]5*                                                                           *
[c153808]6*  Copyright 2010-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
[57b4525]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
[c153808]24/* Out of protest, I should rename this file. OAuth2 is a pathetic joke, and
25   of all things, DEFINITELY NOT A STANDARD. The only thing various OAuth2
26   implementations have in common is that name, wrongfully stolen from
27   a pretty nice standard called OAuth 1.0a. That, and the fact that they
28   use JSON. Wait, no, Facebook's version doesn't use JSON. For some of its
29   responses.
[5ebff60]30
[c153808]31   Apparently too many people were too retarded to comprehend the elementary
32   bits of crypto in OAuth 1.0a (took me one afternoon to implement) so
33   the standard was replaced with what comes down to a complicated scheme
34   around what's really just application-specific passwords.
[5ebff60]35
[c153808]36   And then a bunch of mostly incompatible implementations. Great work, guys.
[5ebff60]37
[c153808]38   http://hueniverse.com/2012/07/oauth-2-0-and-the-road-to-hell/ */
39
[57b4525]40#include <glib.h>
[4a5d885]41#include "http_client.h"
[57b4525]42#include "oauth2.h"
[4a5d885]43#include "oauth.h"
[ba654ec]44#include "json.h"
[c153808]45#include "json_util.h"
[4a5d885]46#include "url.h"
[57b4525]47
[5ebff60]48char *oauth2_url(const struct oauth2_service *sp)
[57b4525]49{
[5ebff60]50        return g_strconcat(sp->auth_url,
51                           "?scope=", sp->scope,
52                           "&response_type=code"
53                           "&redirect_uri=", sp->redirect_url,
54                           "&client_id=", sp->consumer_key,
55                           NULL);
[57b4525]56}
[4a5d885]57
[5ebff60]58struct oauth2_access_token_data {
[4a5d885]59        oauth2_token_callback func;
60        gpointer data;
61};
62
[5ebff60]63static void oauth2_access_token_done(struct http_request *req);
[4a5d885]64
[5ebff60]65int oauth2_access_token(const struct oauth2_service *sp,
66                        const char *auth_type, const char *auth,
67                        oauth2_token_callback func, gpointer data)
[4a5d885]68{
69        GSList *args = NULL;
70        char *args_s, *s;
71        url_t url_p;
72        struct http_request *req;
73        struct oauth2_access_token_data *cb_data;
[5ebff60]74
75        if (!url_set(&url_p, sp->token_url)) {
[4a5d885]76                return 0;
77        }
[5ebff60]78
79        oauth_params_add(&args, "client_id", sp->consumer_key);
80        oauth_params_add(&args, "client_secret", sp->consumer_secret);
81        oauth_params_add(&args, "grant_type", auth_type);
82        if (strcmp(auth_type, OAUTH2_AUTH_CODE) == 0) {
83                oauth_params_add(&args, "redirect_uri", sp->redirect_url);
84                oauth_params_add(&args, "code", auth);
85        } else {
86                oauth_params_add(&args, "refresh_token", auth);
[4a5d885]87        }
[5ebff60]88        args_s = oauth_params_string(args);
89        oauth_params_free(&args);
90
91        s = g_strdup_printf("POST %s HTTP/1.0\r\n"
92                            "Host: %s\r\n"
93                            "Content-Type: application/x-www-form-urlencoded\r\n"
94                            "Content-Length: %zd\r\n"
95                            "\r\n"
96                            "%s", url_p.file, url_p.host, strlen(args_s), args_s);
97        g_free(args_s);
98
99        cb_data = g_new0(struct oauth2_access_token_data, 1);
[4a5d885]100        cb_data->func = func;
101        cb_data->data = data;
[5ebff60]102
103        req = http_dorequest(url_p.host, url_p.port, url_p.proto == PROTO_HTTPS,
104                             s, oauth2_access_token_done, cb_data);
105
106        g_free(s);
107
108        if (req == NULL) {
109                g_free(cb_data);
110        }
111
[4a5d885]112        return req != NULL;
113}
114
[5ebff60]115static char* oauth2_parse_error(json_value *e)
[c153808]116{
117        /* This does a reasonable job with some of the flavours of error
118           responses I've seen. Because apparently it's not standardised. */
[5ebff60]119
120        if (e->type == json_object) {
[c153808]121                /* Facebook style */
[5ebff60]122                const char *msg = json_o_str(e, "message");
123                const char *type = json_o_str(e, "type");
124                json_value *code_o = json_o_get(e, "code");
[c153808]125                int code = 0;
[5ebff60]126
127                if (code_o && code_o->type == json_integer) {
[c153808]128                        code = code_o->u.integer;
[5ebff60]129                }
130
131                return g_strdup_printf("Error %d: %s", code, msg ? msg : type ? type : "Unknown error");
132        } else if (e->type == json_string) {
133                return g_strdup(e->u.string.ptr);
[c153808]134        }
135        return NULL;
136}
137
[5ebff60]138static void oauth2_access_token_done(struct http_request *req)
[4a5d885]139{
140        struct oauth2_access_token_data *cb_data = req->data;
[c153808]141        char *atoken = NULL, *rtoken = NULL, *error = NULL;
[5535a47]142        char *content_type = NULL;
[5ebff60]143
[5535a47]144        if (req->status_code <= 0 && !req->reply_body) {
145                cb_data->func(cb_data->data, NULL, NULL, req->status_string);
146                g_free(cb_data);
147                return;
148        }
149
150        if (getenv("BITLBEE_DEBUG")) {
[5ebff60]151                printf("%s\n", req->reply_body);
152        }
153
154        content_type = get_rfc822_header(req->reply_headers, "Content-Type", 0);
155
156        if (content_type && (strstr(content_type, "application/json") ||
157                             strstr(content_type, "text/javascript"))) {
158                json_value *js = json_parse(req->reply_body, req->body_size);
159                if (js && js->type == json_object) {
160                        JSON_O_FOREACH(js, k, v){
161                                if (strcmp(k, "error") == 0) {
162                                        error = oauth2_parse_error(v);
163                                }
164                                if (v->type != json_string) {
[ba654ec]165                                        continue;
[5ebff60]166                                }
167                                if (strcmp(k, "access_token") == 0) {
168                                        atoken = g_strdup(v->u.string.ptr);
169                                }
170                                if (strcmp(k, "refresh_token") == 0) {
171                                        rtoken = g_strdup(v->u.string.ptr);
172                                }
[ba654ec]173                        }
174                }
[5ebff60]175                json_value_free(js);
176        } else {
[bf57cd1]177                /* Facebook use their own odd format here, seems to be URL-encoded. */
178                GSList *p_in = NULL;
[5ebff60]179
180                oauth_params_parse(&p_in, req->reply_body);
181                atoken = g_strdup(oauth_params_get(&p_in, "access_token"));
182                rtoken = g_strdup(oauth_params_get(&p_in, "refresh_token"));
183                oauth_params_free(&p_in);
[bf57cd1]184        }
[5ebff60]185        if (getenv("BITLBEE_DEBUG")) {
186                printf("Extracted atoken=%s rtoken=%s\n", atoken, rtoken);
187        }
188        if (!atoken && !rtoken && !error) {
189                error = g_strdup("Unusable response");
190        }
191
192        cb_data->func(cb_data->data, atoken, rtoken, error);
193        g_free(content_type);
194        g_free(atoken);
195        g_free(rtoken);
196        g_free(error);
197        g_free(cb_data);
[4a5d885]198}
Note: See TracBrowser for help on using the repository browser.