summaryrefslogtreecommitdiff
path: root/src/plugins/irc
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/irc')
-rw-r--r--src/plugins/irc/irc-config.c5
-rw-r--r--src/plugins/irc/irc-protocol.c9
-rw-r--r--src/plugins/irc/irc-sasl.c260
-rw-r--r--src/plugins/irc/irc-sasl.h4
4 files changed, 221 insertions, 57 deletions
diff --git a/src/plugins/irc/irc-config.c b/src/plugins/irc/irc-config.c
index 604a591da..a96f95682 100644
--- a/src/plugins/irc/irc-config.c
+++ b/src/plugins/irc/irc-config.c
@@ -1577,9 +1577,10 @@ irc_config_server_new_option (struct t_config_file *config_file,
config_file, section,
option_name, "integer",
N_("mechanism for SASL authentication: \"plain\" for plain text "
- "password, \"dh-blowfish\" for crypted password, \"external\" "
+ "password, \"dh-blowfish\" for blowfish crypted password, "
+ "\"dh-aes\" for AES crypted password, \"external\" "
"for authentication using client side SSL cert"),
- "plain|dh-blowfish|external", 0, 0,
+ "plain|dh-blowfish|dh-aes|external", 0, 0,
default_value, value,
null_value_allowed,
callback_check_value, callback_check_value_data,
diff --git a/src/plugins/irc/irc-protocol.c b/src/plugins/irc/irc-protocol.c
index 15ce86d10..917f57a5e 100644
--- a/src/plugins/irc/irc-protocol.c
+++ b/src/plugins/irc/irc-protocol.c
@@ -170,6 +170,11 @@ IRC_PROTOCOL_CALLBACK(authenticate)
sasl_username,
sasl_password);
break;
+ case IRC_SASL_MECHANISM_DH_AES:
+ answer = irc_sasl_mechanism_dh_aes (argv_eol[1],
+ sasl_username,
+ sasl_password);
+ break;
case IRC_SASL_MECHANISM_EXTERNAL:
answer = strdup ("+");
break;
@@ -336,6 +341,10 @@ IRC_PROTOCOL_CALLBACK(cap)
irc_server_sendf (server, 0, NULL,
"AUTHENTICATE DH-BLOWFISH");
break;
+ case IRC_SASL_MECHANISM_DH_AES:
+ irc_server_sendf (server, 0, NULL,
+ "AUTHENTICATE DH-AES");
+ break;
case IRC_SASL_MECHANISM_EXTERNAL:
irc_server_sendf (server, 0, NULL,
"AUTHENTICATE EXTERNAL");
diff --git a/src/plugins/irc/irc-sasl.c b/src/plugins/irc/irc-sasl.c
index db3ab9f7d..a410ad826 100644
--- a/src/plugins/irc/irc-sasl.c
+++ b/src/plugins/irc/irc-sasl.c
@@ -31,7 +31,7 @@
char *irc_sasl_mechanism_string[IRC_NUM_SASL_MECHANISMS] =
-{ "plain", "dh-blowfish", "external" };
+{ "plain", "dh-blowfish", "dh-aes", "external" };
/*
@@ -68,39 +68,28 @@ irc_sasl_mechanism_plain (const char *sasl_username, const char *sasl_password)
}
/*
- * Builds answer for SASL authentication, using mechanism "DH-BLOWFISH".
+ * Reads key sent by server (Diffie-Hellman key exchange).
*
- * Argument data_base64 is a concatenation of 3 strings, each string is composed
- * of 2 bytes (length of string), followed by content of string:
- * 1. a prime number
- * 2. a generator number
- * 3. server-generated public key
- *
- * Note: result must be freed after use.
+ * Returns:
+ * 1: OK
+ * 0: error
*/
-char *
-irc_sasl_mechanism_dh_blowfish (const char *data_base64,
- const char *sasl_username,
- const char *sasl_password)
+int
+irc_sasl_dh (const char *data_base64,
+ unsigned char **public_bin, unsigned char **secret_bin,
+ int *length_key)
{
- char *data, *answer, *ptr_answer, *answer_base64;
- unsigned char *ptr_data, *secret_bin, *public_bin;
- unsigned char *password_clear, *password_crypted;
- int length_data, size, num_bits_prime_number, length_key;
- int length_username, length_password, length_answer;
+ char *data;
+ unsigned char *ptr_data;
+ int length_data, size, num_bits_prime_number, rc;
size_t num_written;
gcry_mpi_t data_prime_number, data_generator_number, data_server_pub_key;
gcry_mpi_t pub_key, priv_key, secret_mpi;
- gcry_cipher_hd_t gcrypt_handle;
+
+ rc = 0;
data = NULL;
- secret_bin = NULL;
- public_bin = NULL;
- password_clear = NULL;
- password_crypted = NULL;
- answer = NULL;
- answer_base64 = NULL;
data_prime_number = NULL;
data_generator_number = NULL;
data_server_pub_key = NULL;
@@ -118,7 +107,7 @@ irc_sasl_mechanism_dh_blowfish (const char *data_base64,
ptr_data += 2;
length_data -= 2;
if (size > length_data)
- goto end;
+ goto dhend;
data_prime_number = gcry_mpi_new (size * 8);
gcry_mpi_scan (&data_prime_number, GCRYMPI_FMT_USG, ptr_data, size, NULL);
num_bits_prime_number = gcry_mpi_get_nbits (data_prime_number);
@@ -130,7 +119,7 @@ irc_sasl_mechanism_dh_blowfish (const char *data_base64,
ptr_data += 2;
length_data -= 2;
if (size > length_data)
- goto end;
+ goto dhend;
data_generator_number = gcry_mpi_new (size * 8);
gcry_mpi_scan (&data_generator_number, GCRYMPI_FMT_USG, ptr_data, size, NULL);
ptr_data += size;
@@ -141,7 +130,7 @@ irc_sasl_mechanism_dh_blowfish (const char *data_base64,
ptr_data += 2;
length_data -= 2;
if (size > length_data)
- goto end;
+ goto dhend;
data_server_pub_key = gcry_mpi_new (size * 8);
gcry_mpi_scan (&data_server_pub_key, GCRYMPI_FMT_USG, ptr_data, size, NULL);
@@ -153,18 +142,70 @@ irc_sasl_mechanism_dh_blowfish (const char *data_base64,
gcry_mpi_powm (pub_key, data_generator_number, priv_key, data_prime_number);
/* compute secret_bin */
- length_key = num_bits_prime_number / 8;
- secret_bin = malloc (length_key);
+ *length_key = num_bits_prime_number / 8;
+ *secret_bin = malloc (*length_key);
secret_mpi = gcry_mpi_new (num_bits_prime_number);
/* secret_mpi = (y ^ priv_key) % p */
gcry_mpi_powm (secret_mpi, data_server_pub_key, priv_key, data_prime_number);
- gcry_mpi_print (GCRYMPI_FMT_USG, secret_bin, length_key,
+ gcry_mpi_print (GCRYMPI_FMT_USG, *secret_bin, *length_key,
&num_written, secret_mpi);
/* create public_bin */
- public_bin = malloc (length_key);
- gcry_mpi_print (GCRYMPI_FMT_USG, public_bin, length_key,
+ *public_bin = malloc (*length_key);
+ gcry_mpi_print (GCRYMPI_FMT_USG, *public_bin, *length_key,
&num_written, pub_key);
+ rc = 1;
+
+dhend:
+ if (data)
+ free (data);
+ if (data_prime_number)
+ gcry_mpi_release (data_prime_number);
+ if (data_generator_number)
+ gcry_mpi_release (data_generator_number);
+ if (data_server_pub_key)
+ gcry_mpi_release (data_server_pub_key);
+ if (pub_key)
+ gcry_mpi_release (pub_key);
+ if (priv_key)
+ gcry_mpi_release (priv_key);
+ if (secret_mpi)
+ gcry_mpi_release (secret_mpi);
+
+ return rc;
+}
+
+/*
+ * Builds answer for SASL authentication, using mechanism "DH-BLOWFISH".
+ *
+ * Argument data_base64 is a concatenation of 3 strings, each string is composed
+ * of 2 bytes (length of string), followed by content of string:
+ * 1. a prime number
+ * 2. a generator number
+ * 3. server-generated public key
+ *
+ * Note: result must be freed after use.
+ */
+char *
+irc_sasl_mechanism_dh_blowfish (const char *data_base64,
+ const char *sasl_username,
+ const char *sasl_password)
+{
+ char *answer, *ptr_answer, *answer_base64;
+ unsigned char *password_clear, *password_crypted;
+ int length_key, length_username, length_password, length_answer;
+ unsigned char *public_bin, *secret_bin;
+ gcry_cipher_hd_t gcrypt_handle;
+
+ password_clear = NULL;
+ password_crypted = NULL;
+ answer = NULL;
+ answer_base64 = NULL;
+ secret_bin = NULL;
+ public_bin = NULL;
+
+ if (!irc_sasl_dh (data_base64, &public_bin, &secret_bin, &length_key))
+ goto bfend;
/* create password buffers (clear and crypted) */
length_password = strlen (sasl_password) +
@@ -178,13 +219,15 @@ irc_sasl_mechanism_dh_blowfish (const char *data_base64,
/* crypt password using blowfish */
if (gcry_cipher_open (&gcrypt_handle, GCRY_CIPHER_BLOWFISH,
GCRY_CIPHER_MODE_ECB, 0) != 0)
- goto end;
+ goto bfend;
if (gcry_cipher_setkey (gcrypt_handle, secret_bin, length_key) != 0)
- goto end;
+ goto bfend;
if (gcry_cipher_encrypt (gcrypt_handle,
password_crypted, length_password,
password_clear, length_password) != 0)
- goto end;
+ goto bfend;
+
+ gcry_cipher_close (gcrypt_handle);
/*
* build answer for server, it is concatenation of:
@@ -193,16 +236,16 @@ irc_sasl_mechanism_dh_blowfish (const char *data_base64,
* 3. sasl_username ('length_username'+1 bytes)
* 4. encrypted password ('length_password' bytes)
*/
- length_username = strlen (sasl_username);
- length_answer = 2 + length_key + length_username + 1 + length_password;
+ length_username = strlen (sasl_username) + 1;
+ length_answer = 2 + length_key + length_username + length_password;
answer = malloc (length_answer);
ptr_answer = answer;
*((unsigned int *)ptr_answer) = htons(length_key);
ptr_answer += 2;
memcpy (ptr_answer, public_bin, length_key);
ptr_answer += length_key;
- memcpy (ptr_answer, sasl_username, length_username + 1);
- ptr_answer += length_username + 1;
+ memcpy (ptr_answer, sasl_username, length_username);
+ ptr_answer += length_username;
memcpy (ptr_answer, password_crypted, length_password);
/* encode answer to base64 */
@@ -210,9 +253,7 @@ irc_sasl_mechanism_dh_blowfish (const char *data_base64,
if (answer_base64)
weechat_string_encode_base64 (answer, length_answer, answer_base64);
-end:
- if (data)
- free (data);
+bfend:
if (secret_bin)
free (secret_bin);
if (public_bin)
@@ -223,18 +264,127 @@ end:
free (password_crypted);
if (answer)
free (answer);
- if (data_prime_number)
- gcry_mpi_release (data_prime_number);
- if (data_generator_number)
- gcry_mpi_release (data_generator_number);
- if (data_server_pub_key)
- gcry_mpi_release (data_server_pub_key);
- if (pub_key)
- gcry_mpi_release (pub_key);
- if (priv_key)
- gcry_mpi_release (priv_key);
- if (secret_mpi)
- gcry_mpi_release (secret_mpi);
+
+ return answer_base64;
+}
+
+/*
+ * Builds answer for SASL authentication, using mechanism "DH-AES".
+ *
+ * Argument data_base64 is a concatenation of 3 strings, each string is composed
+ * of 2 bytes (length of string), followed by content of string:
+ * 1. a prime number
+ * 2. a generator number
+ * 3. server-generated public key
+ *
+ * Note: result must be freed after use.
+ */
+char *
+irc_sasl_mechanism_dh_aes (const char *data_base64,
+ const char *sasl_username,
+ const char *sasl_password)
+{
+ char *answer, *ptr_answer, *answer_base64;
+ unsigned char *ptr_userpass, *userpass_clear, *userpass_crypted;
+ int length_key, length_answer;
+ int length_username, length_password, length_userpass;
+ unsigned char *public_bin, *secret_bin;
+ char iv[16];
+ int cipher_algo;
+ gcry_cipher_hd_t gcrypt_handle;
+
+ userpass_clear = NULL;
+ userpass_crypted = NULL;
+ answer = NULL;
+ answer_base64 = NULL;
+ secret_bin = NULL;
+ public_bin = NULL;
+
+ if (irc_sasl_dh(data_base64, &public_bin, &secret_bin, &length_key) == 0)
+ goto aesend;
+
+ /* Select cipher algorithm: key length * 8 = cipher bit size */
+ switch (length_key)
+ {
+ case 32:
+ cipher_algo = GCRY_CIPHER_AES256;
+ break;
+ case 24:
+ cipher_algo = GCRY_CIPHER_AES192;
+ break;
+ case 16:
+ cipher_algo = GCRY_CIPHER_AES128;
+ break;
+ default:
+ /* Invalid bit length */
+ goto aesend;
+ }
+
+ /* Generate the IV */
+ gcry_randomize (iv, sizeof (iv), GCRY_STRONG_RANDOM);
+
+ /* create user/pass buffers (clear and crypted) */
+ length_username = strlen (sasl_username) + 1;
+ length_password = strlen (sasl_password) + 1;
+ length_userpass = length_username + length_password +
+ ((16 - ((length_username + length_password) % 16)) % 16);
+ ptr_userpass = userpass_clear = malloc (length_userpass);
+ userpass_crypted = malloc (length_userpass);
+ memset (userpass_clear, 0, length_password);
+ memset (userpass_crypted, 0, length_password);
+ memcpy (ptr_userpass, sasl_username, length_username);
+ ptr_userpass += length_username;
+ memcpy (ptr_userpass, sasl_password, length_password);
+
+ /* crypt password using AES in CBC mode */
+ if (gcry_cipher_open (&gcrypt_handle, cipher_algo,
+ GCRY_CIPHER_MODE_CBC, 0) != 0)
+ goto aesend;
+ if (gcry_cipher_setkey (gcrypt_handle, secret_bin, length_key) != 0)
+ goto aesend;
+ if (gcry_cipher_setiv (gcrypt_handle, iv, sizeof(iv)) != 0)
+ goto aesend;
+ if (gcry_cipher_encrypt (gcrypt_handle,
+ userpass_crypted, length_userpass,
+ userpass_clear, length_userpass) != 0)
+ goto aesend;
+
+ gcry_cipher_close (gcrypt_handle);
+
+ /*
+ * build answer for server, it is concatenation of:
+ * 1. key length (2 bytes)
+ * 2. public key ('length_key' bytes)
+ * 3. IV (sizeof (iv) bytes)
+ * 4. encrypted password ('length_userpass' bytes)
+ */
+ length_answer = 2 + length_key + sizeof (iv) + length_userpass;
+ answer = malloc (length_answer);
+ ptr_answer = answer;
+ *((unsigned int *)ptr_answer) = htons(length_key);
+ ptr_answer += 2;
+ memcpy (ptr_answer, public_bin, length_key);
+ ptr_answer += length_key;
+ memcpy (ptr_answer, iv, sizeof (iv));
+ ptr_answer += sizeof (iv);
+ memcpy (ptr_answer, userpass_crypted, length_userpass);
+
+ /* encode answer to base64 */
+ answer_base64 = malloc (length_answer * 4);
+ if (answer_base64)
+ weechat_string_encode_base64 (answer, length_answer, answer_base64);
+
+aesend:
+ if (secret_bin)
+ free (secret_bin);
+ if (public_bin)
+ free (public_bin);
+ if (userpass_clear)
+ free (userpass_clear);
+ if (userpass_crypted)
+ free (userpass_crypted);
+ if (answer)
+ free (answer);
return answer_base64;
}
diff --git a/src/plugins/irc/irc-sasl.h b/src/plugins/irc/irc-sasl.h
index 75a96900c..211381ef8 100644
--- a/src/plugins/irc/irc-sasl.h
+++ b/src/plugins/irc/irc-sasl.h
@@ -26,6 +26,7 @@ enum t_irc_sasl_mechanism
{
IRC_SASL_MECHANISM_PLAIN = 0,
IRC_SASL_MECHANISM_DH_BLOWFISH,
+ IRC_SASL_MECHANISM_DH_AES,
IRC_SASL_MECHANISM_EXTERNAL,
/* number of SASL mechanisms */
IRC_NUM_SASL_MECHANISMS,
@@ -38,5 +39,8 @@ extern char *irc_sasl_mechanism_plain (const char *sasl_username,
extern char *irc_sasl_mechanism_dh_blowfish (const char *data_base64,
const char *sasl_username,
const char *sasl_password);
+extern char *irc_sasl_mechanism_dh_aes (const char *data_base64,
+ const char *sasl_username,
+ const char *sasl_password);
#endif /* __WEECHAT_IRC_SASL_H */