aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2012-10-11 08:35:38 +0800
committerMatthew Barnes <mbarnes@redhat.com>2012-10-11 08:35:38 +0800
commit8f9ae36681d8db4d68a4f13b2bd0c6ba10a12782 (patch)
treeaab8593dbc88e70e6c39cafeb2d8799304b57654 /modules
parent6f038fba293e5cad56d8917739ffbb92b461ceea (diff)
downloadgsoc2013-evolution-8f9ae36681d8db4d68a4f13b2bd0c6ba10a12782.tar.gz
gsoc2013-evolution-8f9ae36681d8db4d68a4f13b2bd0c6ba10a12782.tar.zst
gsoc2013-evolution-8f9ae36681d8db4d68a4f13b2bd0c6ba10a12782.zip
CamelSaslXOAuth: Use GHmac to sign the client request.
Also refactor the code to more closely resemble EGDataGoaAuthorizer.
Diffstat (limited to 'modules')
-rw-r--r--modules/online-accounts/camel-sasl-xoauth.c366
1 files changed, 124 insertions, 242 deletions
diff --git a/modules/online-accounts/camel-sasl-xoauth.c b/modules/online-accounts/camel-sasl-xoauth.c
index d40c134d8a..b3f855f1a8 100644
--- a/modules/online-accounts/camel-sasl-xoauth.c
+++ b/modules/online-accounts/camel-sasl-xoauth.c
@@ -32,279 +32,161 @@
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), CAMEL_TYPE_SASL_XOAUTH, CamelSaslXOAuthPrivate))
+#define HMAC_SHA1_LEN 20 /* bytes, raw */
+
struct _CamelSaslXOAuthPrivate {
gint placeholder;
};
G_DEFINE_DYNAMIC_TYPE (CamelSaslXOAuth, camel_sasl_xoauth, CAMEL_TYPE_SASL)
-/*****************************************************************************
- * This is based on an old revision of gnome-online-accounts
- * which demonstrated OAuth authentication with an IMAP server.
- *
- * See commit 5bcbe2a3eac4821892680e0655b27ab8c128ab15
- *****************************************************************************/
-
-#include <libsoup/soup.h>
-
-#define OAUTH_ENCODE_STRING(str) \
- (str ? soup_uri_encode ((str), "!$&'()*+,;=@") : g_strdup (""))
-
-#define SHA1_BLOCK_SIZE 64
-#define SHA1_LENGTH 20
-
-/*
- * hmac_sha1:
- * @key: The key
- * @message: The message
- *
- * Given the key and message, compute the HMAC-SHA1 hash and return the base-64
- * encoding of it. This is very geared towards OAuth, and as such both key and
- * message must be NULL-terminated strings, and the result is base-64 encoded.
- */
-static gchar *
-hmac_sha1 (const gchar *key,
- const gchar *message)
-{
- GChecksum *checksum;
- gchar *real_key;
- guchar ipad[SHA1_BLOCK_SIZE];
- guchar opad[SHA1_BLOCK_SIZE];
- guchar inner[SHA1_LENGTH];
- guchar digest[SHA1_LENGTH];
- gsize key_length, inner_length, digest_length;
- gint i;
-
- g_return_val_if_fail (key, NULL);
- g_return_val_if_fail (message, NULL);
-
- checksum = g_checksum_new (G_CHECKSUM_SHA1);
-
- /* If the key is longer than the block size, hash it first */
- if (strlen (key) > SHA1_BLOCK_SIZE) {
- guchar new_key[SHA1_LENGTH];
-
- key_length = sizeof (new_key);
-
- g_checksum_update (checksum, (guchar *) key, strlen (key));
- g_checksum_get_digest (checksum, new_key, &key_length);
- g_checksum_reset (checksum);
-
- real_key = g_memdup (new_key, key_length);
- } else {
- real_key = g_strdup (key);
- key_length = strlen (key);
- }
-
- /* Sanity check the length */
- g_assert (key_length <= SHA1_BLOCK_SIZE);
-
- /* Protect against use of the provided key by NULLing it */
- key = NULL;
-
- /* Stage 1 */
- memset (ipad, 0, sizeof (ipad));
- memset (opad, 0, sizeof (opad));
-
- memcpy (ipad, real_key, key_length);
- memcpy (opad, real_key, key_length);
-
- /* Stage 2 and 5 */
- for (i = 0; i < sizeof (ipad); i++) {
- ipad[i] ^= 0x36;
- opad[i] ^= 0x5C;
- }
-
- /* Stage 3 and 4 */
- g_checksum_update (checksum, ipad, sizeof (ipad));
- g_checksum_update (checksum, (guchar *) message, strlen (message));
- inner_length = sizeof (inner);
- g_checksum_get_digest (checksum, inner, &inner_length);
- g_checksum_reset (checksum);
-
- /* Stage 6 and 7 */
- g_checksum_update (checksum, opad, sizeof (opad));
- g_checksum_update (checksum, inner, inner_length);
-
- digest_length = sizeof (digest);
- g_checksum_get_digest (checksum, digest, &digest_length);
-
- g_checksum_free (checksum);
- g_free (real_key);
-
- return g_base64_encode (digest, digest_length);
-}
-
-static gchar *
-sign_plaintext (const gchar *consumer_secret,
- const gchar *token_secret)
-{
- gchar *cs;
- gchar *ts;
- gchar *rv;
-
- cs = OAUTH_ENCODE_STRING (consumer_secret);
- ts = OAUTH_ENCODE_STRING (token_secret);
- rv = g_strconcat (cs, "&", ts, NULL);
-
- g_free (cs);
- g_free (ts);
-
- return rv;
-}
-
static gchar *
-sign_hmac (const gchar *consumer_secret,
- const gchar *token_secret,
- const gchar *http_method,
- const gchar *request_uri,
- const gchar *encoded_params)
+sasl_xoauth_build_request (const gchar *request_uri,
+ const gchar *consumer_key,
+ const gchar *consumer_secret,
+ const gchar *access_token,
+ const gchar *access_token_secret)
{
- GString *text;
- gchar *signature;
- gchar *key;
-
- text = g_string_new (NULL);
- g_string_append (text, http_method);
- g_string_append_c (text, '&');
- g_string_append_uri_escaped (text, request_uri, NULL, FALSE);
- g_string_append_c (text, '&');
- g_string_append_uri_escaped (text, encoded_params, NULL, FALSE);
-
- /* PLAINTEXT signature value is the HMAC-SHA1 key value */
- key = sign_plaintext (consumer_secret, token_secret);
- signature = hmac_sha1 (key, text->str);
- g_free (key);
-
- g_string_free (text, TRUE);
-
- return signature;
-}
-
-static GHashTable *
-calculate_xoauth_params (const gchar *request_uri,
- const gchar *consumer_key,
- const gchar *consumer_secret,
- const gchar *access_token,
- const gchar *access_token_secret)
-{
- gchar *signature;
- GHashTable *params;
- gchar *nonce;
- gchar *timestamp;
+ GString *query;
+ GString *base_string;
+ GString *signing_key;
+ GString *request;
+ GHashTable *parameters;
GList *keys;
GList *iter;
- GString *normalized;
- gpointer key;
-
- nonce = g_strdup_printf ("%u", g_random_int ());
- timestamp = g_strdup_printf (
- "%" G_GINT64_FORMAT, (gint64) time (NULL));
-
- params = g_hash_table_new_full (
+ GHmac *signature_hmac;
+ guchar signature_digest[HMAC_SHA1_LEN];
+ gsize signature_digest_len;
+ gchar *string;
+ gpointer key, val;
+ guint ii;
+
+ const gchar *oauth_keys[] = {
+ "oauth_version",
+ "oauth_nonce",
+ "oauth_timestamp",
+ "oauth_consumer_key",
+ "oauth_token",
+ "oauth_signature_method",
+ "oauth_signature"
+ };
+
+ parameters = g_hash_table_new_full (
(GHashFunc) g_str_hash,
(GEqualFunc) g_str_equal,
(GDestroyNotify) NULL,
(GDestroyNotify) g_free);
- key = (gpointer) "oauth_consumer_key";
- g_hash_table_insert (params, key, g_strdup (consumer_key));
+ /* Add OAuth parameters. */
+
+ key = (gpointer) "oauth_version";
+ g_hash_table_insert (parameters, key, g_strdup ("1.0"));
key = (gpointer) "oauth_nonce";
- g_hash_table_insert (params, key, nonce); /* takes ownership */
+ string = g_strdup_printf ("%u", g_random_int ());
+ g_hash_table_insert (parameters, key, string); /* takes ownership */
key = (gpointer) "oauth_timestamp";
- g_hash_table_insert (params, key, timestamp); /* takes ownership */
+ string = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) time (NULL));
+ g_hash_table_insert (parameters, key, string); /* takes ownership */
- key = (gpointer) "oauth_version";
- g_hash_table_insert (params, key, g_strdup ("1.0"));
+ key = (gpointer) "oauth_consumer_key";
+ g_hash_table_insert (parameters, key, g_strdup (consumer_key));
+
+ key = (gpointer) "oauth_token";
+ g_hash_table_insert (parameters, key, g_strdup (access_token));
key = (gpointer) "oauth_signature_method";
- g_hash_table_insert (params, key, g_strdup ("HMAC-SHA1"));
+ g_hash_table_insert (parameters, key, g_strdup ("HMAC-SHA1"));
- key = (gpointer) "oauth_token";
- g_hash_table_insert (params, key, g_strdup (access_token));
+ /* Build the query part of the signature base string.
+ * Parameters in the query part must be sorted by name. */
- normalized = g_string_new (NULL);
- keys = g_hash_table_get_keys (params);
+ query = g_string_sized_new (512);
+ keys = g_hash_table_get_keys (parameters);
keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
for (iter = keys; iter != NULL; iter = iter->next) {
- const gchar *key = iter->data;
- const gchar *value;
- gchar *k;
- gchar *v;
-
- value = g_hash_table_lookup (params, key);
- if (normalized->len > 0)
- g_string_append_c (normalized, '&');
+ key = iter->data;
+ val = g_hash_table_lookup (parameters, key);
- k = OAUTH_ENCODE_STRING (key);
- v = OAUTH_ENCODE_STRING (value);
-
- g_string_append_printf (normalized, "%s=%s", k, v);
+ if (iter != keys)
+ g_string_append_c (query, '&');
- g_free (k);
- g_free (v);
+ g_string_append_uri_escaped (query, key, NULL, FALSE);
+ g_string_append_c (query, '=');
+ g_string_append_uri_escaped (query, val, NULL, FALSE);
}
g_list_free (keys);
- signature = sign_hmac (
- consumer_secret, access_token_secret,
- "GET", request_uri, normalized->str);
+ /* Build the signature base string. */
+
+ base_string = g_string_new (NULL);
+ g_string_append (base_string, "GET");
+ g_string_append_c (base_string, '&');
+ g_string_append_uri_escaped (base_string, request_uri, NULL, FALSE);
+ g_string_append_c (base_string, '&');
+ g_string_append_uri_escaped (base_string, query->str, NULL, FALSE);
+
+ /* Build the HMAC-SHA1 signing key. */
+
+ signing_key = g_string_sized_new (512);
+ g_string_append_uri_escaped (
+ signing_key, consumer_secret, NULL, FALSE);
+ g_string_append_c (signing_key, '&');
+ g_string_append_uri_escaped (
+ signing_key, access_token_secret, NULL, FALSE);
+
+ /* Sign the request. */
+
+ signature_digest_len = sizeof (signature_digest);
+
+ signature_hmac = g_hmac_new (
+ G_CHECKSUM_SHA1,
+ (guchar *) signing_key->str,
+ signing_key->len);
+ g_hmac_update (
+ signature_hmac,
+ (guchar *) base_string->str,
+ base_string->len);
+ g_hmac_get_digest (
+ signature_hmac,
+ signature_digest,
+ &signature_digest_len);
+ g_hmac_unref (signature_hmac);
key = (gpointer) "oauth_signature";
- g_hash_table_insert (params, key, signature); /* takes ownership */
+ string = g_base64_encode (signature_digest, signature_digest_len);
+ g_hash_table_insert (parameters, key, string); /* takes ownership */
- g_string_free (normalized, TRUE);
+ /* Build the formal request string. */
- return params;
-}
-
-static gchar *
-calculate_xoauth_param (const gchar *request_uri,
- const gchar *consumer_key,
- const gchar *consumer_secret,
- const gchar *access_token,
- const gchar *access_token_secret)
-{
- GString *str;
- GHashTable *params;
- GList *keys;
- GList *iter;
+ request = g_string_new ("GET ");
+ g_string_append (request, request_uri);
+ g_string_append_c (request, ' ');
- params = calculate_xoauth_params (
- request_uri,
- consumer_key,
- consumer_secret,
- access_token,
- access_token_secret);
-
- str = g_string_new ("GET ");
- g_string_append (str, request_uri);
- g_string_append_c (str, ' ');
- keys = g_hash_table_get_keys (params);
- keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
- for (iter = keys; iter != NULL; iter = iter->next) {
- const gchar *key = iter->data;
- const gchar *value;
- gchar *k;
- gchar *v;
+ for (ii = 0; ii < G_N_ELEMENTS (oauth_keys); ii++) {
+ key = (gpointer) oauth_keys[ii];
+ val = g_hash_table_lookup (parameters, key);
- value = g_hash_table_lookup (params, key);
- if (iter != keys)
- g_string_append_c (str, ',');
+ if (ii > 0)
+ g_string_append_c (request, ',');
- k = OAUTH_ENCODE_STRING (key);
- v = OAUTH_ENCODE_STRING (value);
- g_string_append_printf (str, "%s=\"%s\"", k, v);
- g_free (k);
- g_free (v);
+ g_string_append (request, key);
+ g_string_append_c (request, '=');
+ g_string_append_c (request, '"');
+ g_string_append_uri_escaped (request, val, NULL, FALSE);
+ g_string_append_c (request, '"');
}
- g_list_free (keys);
- g_hash_table_unref (params);
+ /* Clean up. */
+
+ g_string_free (query, TRUE);
+ g_string_free (base_string, TRUE);
+ g_string_free (signing_key, TRUE);
+
+ g_hash_table_unref (parameters);
- return g_string_free (str, FALSE);
+ return g_string_free (request, FALSE);
}
/****************************************************************************/
@@ -383,13 +265,13 @@ sasl_xoauth_challenge_sync (CamelSasl *sasl,
GoaClient *goa_client;
GoaObject *goa_object;
GoaAccount *goa_account;
- GByteArray *parameters = NULL;
+ GByteArray *byte_array = NULL;
CamelService *service;
CamelSession *session;
ESourceRegistry *registry;
const gchar *uid;
gchar *account_id;
- gchar *xoauth_param = NULL;
+ gchar *request = NULL;
gboolean success;
service = camel_sasl_get_service (sasl);
@@ -456,7 +338,7 @@ sasl_xoauth_challenge_sync (CamelSasl *sasl,
error);
if (success)
- xoauth_param = calculate_xoauth_param (
+ request = sasl_xoauth_build_request (
request_uri,
consumer_key,
consumer_secret,
@@ -476,18 +358,18 @@ sasl_xoauth_challenge_sync (CamelSasl *sasl,
if (success) {
/* Sanity check. */
- g_return_val_if_fail (xoauth_param != NULL, NULL);
+ g_return_val_if_fail (request != NULL, NULL);
- parameters = g_byte_array_new ();
+ byte_array = g_byte_array_new ();
g_byte_array_append (
- parameters, (guint8 *) xoauth_param,
- strlen (xoauth_param) + 1);
- g_free (xoauth_param);
+ byte_array, (guint8 *) request,
+ strlen (request) + 1);
+ g_free (request);
}
- /* IMAP and SMTP services will Base64-encode the XOAUTH parameters. */
+ /* IMAP and SMTP services will Base64-encode the request. */
- return parameters;
+ return byte_array;
}
static gpointer