diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2012-10-11 08:35:38 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2012-10-11 08:35:38 +0800 |
commit | 8f9ae36681d8db4d68a4f13b2bd0c6ba10a12782 (patch) | |
tree | aab8593dbc88e70e6c39cafeb2d8799304b57654 /modules | |
parent | 6f038fba293e5cad56d8917739ffbb92b461ceea (diff) | |
download | gsoc2013-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.c | 366 |
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 |