irc: add support of "dh-aes" SASL mechanism (patch #8020)
This commit is contained in:
parent
2479f427f7
commit
e009884595
1
AUTHORS
1
AUTHORS
@ -26,6 +26,7 @@ Alphabetically:
|
||||
* Dmitry Kobylin
|
||||
* Dominik Honnef
|
||||
* Elián Hanisch (m4v)
|
||||
* Elizabeth Myers (Elizacat)
|
||||
* Frank Zacharias
|
||||
* Gu1ll4um3r0m41n
|
||||
* gwenn
|
||||
|
@ -1,7 +1,7 @@
|
||||
WeeChat ChangeLog
|
||||
=================
|
||||
Sébastien Helleu <flashcode@flashtux.org>
|
||||
v0.4.1-dev, 2013-04-28
|
||||
v0.4.1-dev, 2013-05-01
|
||||
|
||||
|
||||
This document lists all changes for each version.
|
||||
@ -51,6 +51,7 @@ Version 0.4.1 (under dev!)
|
||||
list with arguments inside), guile >= 2.0 is now required (bug #38350)
|
||||
* guile: fix crash on calls to callbacks during load of script (bug #38343)
|
||||
* guile: fix compilation with guile 2.0
|
||||
* irc: add support of "dh-aes" SASL mechanism (patch #8020)
|
||||
* irc: fix duplicate nick completion when someone rejoins the channel with same
|
||||
nick but a different case (bug #38841)
|
||||
* irc: add support of UHNAMES (capability "userhost-in-names") (task #9353)
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -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,76 +142,23 @@ 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;
|
||||
|
||||
/* create password buffers (clear and crypted) */
|
||||
length_password = strlen (sasl_password) +
|
||||
((8 - (strlen (sasl_password) % 8)) % 8);
|
||||
password_clear = malloc (length_password);
|
||||
password_crypted = malloc (length_password);
|
||||
memset (password_clear, 0, length_password);
|
||||
memset (password_crypted, 0, length_password);
|
||||
memcpy (password_clear, sasl_password, strlen (sasl_password));
|
||||
|
||||
/* crypt password using blowfish */
|
||||
if (gcry_cipher_open (&gcrypt_handle, GCRY_CIPHER_BLOWFISH,
|
||||
GCRY_CIPHER_MODE_ECB, 0) != 0)
|
||||
goto end;
|
||||
if (gcry_cipher_setkey (gcrypt_handle, secret_bin, length_key) != 0)
|
||||
goto end;
|
||||
if (gcry_cipher_encrypt (gcrypt_handle,
|
||||
password_crypted, length_password,
|
||||
password_clear, length_password) != 0)
|
||||
goto end;
|
||||
|
||||
/*
|
||||
* build answer for server, it is concatenation of:
|
||||
* 1. key length (2 bytes)
|
||||
* 2. public key ('length_key' bytes)
|
||||
* 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;
|
||||
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, password_crypted, length_password);
|
||||
|
||||
/* encode answer to base64 */
|
||||
answer_base64 = malloc (length_answer * 4);
|
||||
if (answer_base64)
|
||||
weechat_string_encode_base64 (answer, length_answer, answer_base64);
|
||||
|
||||
end:
|
||||
dhend:
|
||||
if (data)
|
||||
free (data);
|
||||
if (secret_bin)
|
||||
free (secret_bin);
|
||||
if (public_bin)
|
||||
free (public_bin);
|
||||
if (password_clear)
|
||||
free (password_clear);
|
||||
if (password_crypted)
|
||||
free (password_crypted);
|
||||
if (answer)
|
||||
free (answer);
|
||||
if (data_prime_number)
|
||||
gcry_mpi_release (data_prime_number);
|
||||
if (data_generator_number)
|
||||
@ -236,5 +172,219 @@ end:
|
||||
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) +
|
||||
((8 - (strlen (sasl_password) % 8)) % 8);
|
||||
password_clear = malloc (length_password);
|
||||
password_crypted = malloc (length_password);
|
||||
memset (password_clear, 0, length_password);
|
||||
memset (password_crypted, 0, length_password);
|
||||
memcpy (password_clear, sasl_password, strlen (sasl_password));
|
||||
|
||||
/* crypt password using blowfish */
|
||||
if (gcry_cipher_open (&gcrypt_handle, GCRY_CIPHER_BLOWFISH,
|
||||
GCRY_CIPHER_MODE_ECB, 0) != 0)
|
||||
goto bfend;
|
||||
if (gcry_cipher_setkey (gcrypt_handle, secret_bin, length_key) != 0)
|
||||
goto bfend;
|
||||
if (gcry_cipher_encrypt (gcrypt_handle,
|
||||
password_crypted, length_password,
|
||||
password_clear, length_password) != 0)
|
||||
goto bfend;
|
||||
|
||||
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. sasl_username ('length_username'+1 bytes)
|
||||
* 4. encrypted password ('length_password' bytes)
|
||||
*/
|
||||
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);
|
||||
ptr_answer += length_username;
|
||||
memcpy (ptr_answer, password_crypted, length_password);
|
||||
|
||||
/* encode answer to base64 */
|
||||
answer_base64 = malloc (length_answer * 4);
|
||||
if (answer_base64)
|
||||
weechat_string_encode_base64 (answer, length_answer, answer_base64);
|
||||
|
||||
bfend:
|
||||
if (secret_bin)
|
||||
free (secret_bin);
|
||||
if (public_bin)
|
||||
free (public_bin);
|
||||
if (password_clear)
|
||||
free (password_clear);
|
||||
if (password_crypted)
|
||||
free (password_crypted);
|
||||
if (answer)
|
||||
free (answer);
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user