[evolution] CamelSaslXOAuth: Use GHmac to sign the client request.



commit 8f9ae36681d8db4d68a4f13b2bd0c6ba10a12782
Author: Matthew Barnes <mbarnes redhat com>
Date:   Wed Oct 10 20:35:38 2012 -0400

    CamelSaslXOAuth: Use GHmac to sign the client request.
    
    Also refactor the code to more closely resemble EGDataGoaAuthorizer.

 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 d40c134..b3f855f 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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]