summaryrefslogtreecommitdiff
path: root/daemon/gvfsafpserver.c
diff options
context:
space:
mode:
authorCarl-Anton Ingmarsson <ca.ingmarsson@gmail.com>2011-06-16 14:33:13 +0200
committerChristian Kellner <gicmo@gnome.org>2011-08-25 21:18:38 +0200
commit97a38721fee149919fc28f59863acf10a5f98073 (patch)
tree2daeb8145ca670d2d243db0562e7c1347d305801 /daemon/gvfsafpserver.c
parent0d9ee3370ceb842600c1480c516deefa42a36bbe (diff)
downloadgvfs-97a38721fee149919fc28f59863acf10a5f98073.tar.gz
afp: add support for Diffie-Hellman 2 authentication
Diffstat (limited to 'daemon/gvfsafpserver.c')
-rw-r--r--daemon/gvfsafpserver.c304
1 files changed, 303 insertions, 1 deletions
diff --git a/daemon/gvfsafpserver.c b/daemon/gvfsafpserver.c
index d8a0e8e3..3813c129 100644
--- a/daemon/gvfsafpserver.c
+++ b/daemon/gvfsafpserver.c
@@ -66,6 +66,304 @@ string_to_afp_version (const char *str)
#ifdef HAVE_GCRYPT
static gboolean
+dhx2_login (GVfsAfpServer *afp_serv,
+ const char *username,
+ const char *password,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean res;
+ gcry_error_t gcry_err;
+ GVfsAfpCommand *comm;
+ GVfsAfpReply *reply;
+ AfpResultCode res_code;
+
+ guint8 C2SIV[] = { 0x4c, 0x57, 0x61, 0x6c, 0x6c, 0x61, 0x63, 0x65 };
+ guint8 S2CIV[] = { 0x43, 0x4a, 0x61, 0x6c, 0x62, 0x65, 0x72, 0x74 };
+
+ /* reply 1 */
+ guint16 id;
+ guint8 g_buf[4];
+ guint16 len;
+ guint32 bits;
+ guint8 *buf;
+
+ gcry_mpi_t g, p, Ma, Mb, Ra, key;
+ gcry_cipher_hd_t cipher;
+
+ gcry_mpi_t clientNonce;
+ guint8 clientNonce_buf[16];
+ guint8 key_md5_buf[16];
+
+ /* reply 2 */
+ guint8 reply2_buf[32];
+ gcry_mpi_t clientNonce1, serverNonce;
+
+ /* request 3 */
+ guint8 answer_buf[272] = {0};
+ size_t nonce_len;
+
+ /* initialize for easy cleanup */
+ g = NULL,
+ p = NULL;
+ Ma = NULL;
+ Mb = NULL;
+ Ra = NULL;
+ key = NULL;
+ clientNonce = NULL;
+ clientNonce1 = NULL;
+ serverNonce = NULL;
+ buf = NULL;
+
+ /* setup cipher */
+ gcry_err = gcry_cipher_open (&cipher, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC,
+ 0);
+ g_assert (gcry_err == 0);
+
+
+ if (strlen (password) > 256)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+ _("Server doesn't support passwords longer than 256 characters"));
+ goto error;
+ }
+
+ /* Request 1 */
+ comm = g_vfs_afp_command_new (AFP_COMMAND_LOGIN);
+ g_vfs_afp_command_put_pascal (comm, afp_version_to_string (afp_serv->version));
+ g_vfs_afp_command_put_pascal (comm, AFP_UAM_DHX2);
+ g_vfs_afp_command_put_pascal (comm, username);
+ g_vfs_afp_command_pad_to_even (comm);
+
+ res = g_vfs_afp_connection_send_command_sync (afp_serv->conn, comm,
+ cancellable, error);
+ g_object_unref (comm);
+ if (!res)
+ goto error;
+
+ reply = g_vfs_afp_connection_read_reply_sync (afp_serv->conn, cancellable, error);
+ if (!reply)
+ goto error;
+
+ res_code = g_vfs_afp_reply_get_result_code (reply);
+ if (res_code != AFP_RESULT_AUTH_CONTINUE)
+ {
+ g_object_unref (reply);
+ if (res_code == AFP_RESULT_USER_NOT_AUTH)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+ _("An invalid username was provided"));
+ goto error;
+ }
+ else
+ goto generic_error;
+ }
+
+ /* Get data from reply */
+ id = g_data_input_stream_read_uint16 (G_DATA_INPUT_STREAM (reply), NULL, NULL);
+
+ /* read g */
+ g_input_stream_read_all (G_INPUT_STREAM (reply), &g_buf, 4, NULL, NULL, NULL);
+ gcry_err = gcry_mpi_scan (&g, GCRYMPI_FMT_USG, &g_buf, 4, NULL);
+ g_assert (gcry_err == 0);
+
+ len = g_data_input_stream_read_uint16 (G_DATA_INPUT_STREAM (reply), NULL, NULL);
+ bits = len * 8;
+ buf = g_malloc (len);
+
+ /* read p */
+ g_input_stream_read_all (G_INPUT_STREAM (reply), buf, len, NULL, NULL, NULL);
+ gcry_err = gcry_mpi_scan (&p, GCRYMPI_FMT_USG, buf, len, NULL);
+ g_assert (gcry_err == 0);
+
+ /* read Mb */
+ g_input_stream_read_all (G_INPUT_STREAM (reply), buf, len, NULL, NULL, NULL);
+ gcry_err = gcry_mpi_scan (&Mb, GCRYMPI_FMT_USG, buf, len, NULL);
+ g_assert (gcry_err == 0);
+
+ g_object_unref (reply);
+
+ /* generate random number Ra != 0 */
+ Ra = gcry_mpi_new (bits);
+ while (gcry_mpi_cmp_ui (Ra, 0) == 0)
+ gcry_mpi_randomize (Ra, bits, GCRY_STRONG_RANDOM);
+
+ /* Secret key value must be less than half of prime */
+ if (gcry_mpi_get_nbits (Ra) > bits - 1)
+ gcry_mpi_clear_highbit (Ra, bits - 1);
+
+ /* generate Ma */
+ Ma = gcry_mpi_new (bits);
+ gcry_mpi_powm (Ma, g, Ra, p);
+
+ /* derive Key */
+ key = gcry_mpi_new (bits);
+ gcry_mpi_powm (key, Mb, Ra, p);
+
+ gcry_err = gcry_mpi_print (GCRYMPI_FMT_USG, buf, len, NULL,
+ key);
+ g_assert (gcry_err == 0);
+ gcry_md_hash_buffer (GCRY_MD_MD5, key_md5_buf, buf, len);
+
+ /* generate random clientNonce != 0 */
+ clientNonce = gcry_mpi_new (128);
+ while (gcry_mpi_cmp_ui (clientNonce, 0) == 0)
+ gcry_mpi_randomize (clientNonce, 128, GCRY_STRONG_RANDOM);
+
+ gcry_err = gcry_mpi_print (GCRYMPI_FMT_USG, clientNonce_buf, 16, &nonce_len,
+ clientNonce);
+ g_assert (gcry_err == 0);
+ if (nonce_len < 16)
+ {
+ memmove(clientNonce_buf + 16 - nonce_len, clientNonce_buf, nonce_len);
+ memset(clientNonce_buf, 0, 16 - nonce_len);
+ }
+
+ gcry_cipher_setiv (cipher, C2SIV, G_N_ELEMENTS (C2SIV));
+ gcry_cipher_setkey (cipher, key_md5_buf, 16);
+
+ gcry_err = gcry_cipher_encrypt (cipher, clientNonce_buf, 16,
+ NULL, 0);
+ g_assert (gcry_err == 0);
+
+
+ /* Create Request 2 */
+ comm = g_vfs_afp_command_new (AFP_COMMAND_LOGIN_CONT);
+
+ /* pad byte */
+ g_data_output_stream_put_byte (G_DATA_OUTPUT_STREAM (comm), 0, NULL, NULL);
+ /* Id */
+ g_data_output_stream_put_uint16 (G_DATA_OUTPUT_STREAM (comm), id, NULL, NULL);
+ /* Ma */
+ memset (buf, 0, len);
+ gcry_err = gcry_mpi_print (GCRYMPI_FMT_USG, buf, len, NULL,
+ Ma);
+ g_assert (gcry_err == 0);
+ g_output_stream_write_all (G_OUTPUT_STREAM (comm), buf, len, NULL, NULL, NULL);
+ /* clientNonce */
+ g_output_stream_write_all (G_OUTPUT_STREAM (comm), clientNonce_buf, 16, NULL, NULL, NULL);
+
+ res = g_vfs_afp_connection_send_command_sync (afp_serv->conn, comm,
+ cancellable, error);
+ g_object_unref (comm);
+ if (!res)
+ goto error;
+
+
+ reply = g_vfs_afp_connection_read_reply_sync (afp_serv->conn, cancellable, error);
+ if (!reply)
+ goto error;
+
+
+ res_code = g_vfs_afp_reply_get_result_code (reply);
+ if (res_code != AFP_RESULT_AUTH_CONTINUE)
+ {
+ g_object_unref (reply);
+ goto generic_error;
+ }
+
+ /* read data from reply 2 */
+ id = g_data_input_stream_read_uint16 (G_DATA_INPUT_STREAM (reply), NULL, NULL);
+
+ g_input_stream_read_all (G_INPUT_STREAM (reply), reply2_buf, 32, NULL, NULL, NULL);
+
+ g_object_unref (reply);
+
+ /* decrypt */
+ gcry_cipher_setiv (cipher, S2CIV, G_N_ELEMENTS (S2CIV));
+ gcry_err = gcry_cipher_decrypt (cipher, reply2_buf, 32, NULL, 0);
+ g_assert (gcry_err == 0);
+
+ /* check clientNonce + 1 */
+ gcry_err = gcry_mpi_scan (&clientNonce1, GCRYMPI_FMT_USG, reply2_buf, 16, NULL);
+ g_assert (gcry_err == 0);
+ gcry_mpi_add_ui (clientNonce, clientNonce, 1);
+ if (gcry_mpi_cmp (clientNonce, clientNonce1) != 0)
+ goto generic_error;
+
+ gcry_err = gcry_mpi_scan (&serverNonce, GCRYMPI_FMT_USG, reply2_buf + 16, 16, NULL);
+ g_assert (gcry_err == 0);
+ gcry_mpi_add_ui (serverNonce, serverNonce, 1);
+
+ /* create encrypted answer */
+ gcry_err = gcry_mpi_print (GCRYMPI_FMT_USG, answer_buf, 16, &nonce_len, serverNonce);
+ g_assert (gcry_err == 0);
+
+ if (nonce_len < 16)
+ {
+ memmove(answer_buf + 16 - nonce_len, answer_buf, nonce_len);
+ memset(answer_buf, 0, 16 - nonce_len);
+ }
+
+ memcpy (answer_buf + 16, password, strlen (password));
+
+ gcry_cipher_setiv (cipher, C2SIV, G_N_ELEMENTS (C2SIV));
+ gcry_err = gcry_cipher_encrypt (cipher, answer_buf, G_N_ELEMENTS (answer_buf),
+ NULL, 0);
+ g_assert (gcry_err == 0);
+
+ /* Create request 3 */
+ comm = g_vfs_afp_command_new (AFP_COMMAND_LOGIN_CONT);
+
+ /* pad byte */
+ g_data_output_stream_put_byte (G_DATA_OUTPUT_STREAM (comm), 0, NULL, NULL);
+ /* id */
+ g_data_output_stream_put_uint16 (G_DATA_OUTPUT_STREAM (comm), id, NULL, NULL);
+ g_output_stream_write_all (G_OUTPUT_STREAM (comm), answer_buf,
+ G_N_ELEMENTS (answer_buf), NULL, NULL, NULL);
+
+
+ res = g_vfs_afp_connection_send_command_sync (afp_serv->conn, comm,
+ cancellable, error);
+ if (!res)
+ goto error;
+
+ reply = g_vfs_afp_connection_read_reply_sync (afp_serv->conn, cancellable, error);
+ if (!reply)
+ goto error;
+
+ res_code = g_vfs_afp_reply_get_result_code (reply);
+ g_object_unref (reply);
+ if (res_code != AFP_RESULT_NO_ERROR)
+ {
+ if (res_code == AFP_RESULT_USER_NOT_AUTH)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+ _("Server \"%s\" declined the submitted password"),
+ afp_serv->server_name);
+ goto error;
+ }
+ else
+ goto generic_error;
+ }
+
+ res = TRUE;
+
+cleanup:
+ gcry_mpi_release (g);
+ gcry_mpi_release (p);
+ gcry_mpi_release (Ma);
+ gcry_mpi_release (Mb);
+ gcry_mpi_release (key);
+ gcry_mpi_release (clientNonce);
+ gcry_mpi_release (clientNonce1);
+ gcry_mpi_release (serverNonce);
+ gcry_cipher_close (cipher);
+ g_free (buf);
+
+ return res;
+
+error:
+ res = FALSE;
+ goto cleanup;
+
+generic_error:
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Login to server \"%s\" failed"), afp_serv->server_name);
+ goto error;
+}
+
+static gboolean
dhx_login (GVfsAfpServer *afp_serv,
const char *username,
const char *password,
@@ -236,7 +534,7 @@ dhx_login (GVfsAfpServer *afp_serv,
gcry_err = gcry_cipher_encrypt (cipher, answer_buf, G_N_ELEMENTS (answer_buf),
NULL, 0);
g_assert (gcry_err == 0);
-
+ gcry_cipher_close (cipher);
/* Create Login Continue command */
comm = g_vfs_afp_command_new (AFP_COMMAND_LOGIN_CONT);
@@ -355,6 +653,10 @@ do_login (GVfsAfpServer *afp_serv,
else {
#ifdef HAVE_GCRYPT
+ /* Diffie-Hellman 2 */
+ if (g_slist_find_custom (afp_serv->uams, AFP_UAM_DHX2, g_str_equal))
+ return dhx2_login (afp_serv, username, password, cancellable, error);
+
/* Diffie-Hellman */
if (g_slist_find_custom (afp_serv->uams, AFP_UAM_DHX, g_str_equal))
return dhx_login (afp_serv, username, password, cancellable, error);