/* SASL Mechanisms */
#define MECH_PLAIN 0
-#define MECH_BLOWFISH 1
-#define MECH_AES 2
-#define MECH_EXTERNAL 3
+#define MECH_EXTERNAL 1
typedef struct server
{
unsigned int skip_next_whois:1; /* hide whois output */
unsigned int inside_whois:1;
unsigned int doing_dns:1; /* /dns has been done */
- unsigned int retry_sasl:1; /* retrying another sasl mech */
unsigned int end_of_motd:1; /* end of motd reached (logged in) */
unsigned int sent_quit:1; /* sent a QUIT already? */
unsigned int use_listargs:1; /* undernet and dalnet need /list >0,<10000 */
unsigned int have_cert:1; /* have loaded a cert */
unsigned int use_who:1; /* whether to use WHO command to get dcc_ip */
unsigned int sasl_mech; /* mechanism for sasl auth */
- unsigned int sent_saslauth:1; /* have sent AUTHENICATE yet */
unsigned int sent_capend:1; /* have sent CAP END yet */
unsigned int waiting_on_cap:1; /* waiting on another line of CAP LS */
#ifdef USE_OPENSSL
}
}
+static const char *sasl_mechanisms[] =
+{
+ "PLAIN",
+ "EXTERNAL"
+};
+
static void
inbound_toggle_caps (server *serv, const char *extensions_str, gboolean enable)
{
serv->have_sasl = enable;
if (enable)
{
- serv->sent_saslauth = FALSE;
-
#ifdef USE_OPENSSL
if (serv->loginmethod == LOGIN_SASLEXTERNAL)
- {
serv->sasl_mech = MECH_EXTERNAL;
- tcp_send_len (serv, "AUTHENTICATE EXTERNAL\r\n", 23);
- }
- else
- {
- /* default to most secure, it will fallback if not supported */
- serv->sasl_mech = MECH_AES;
- tcp_send_len (serv, "AUTHENTICATE DH-AES\r\n", 21);
- }
-#else
- serv->sasl_mech = MECH_PLAIN;
- tcp_send_len (serv, "AUTHENTICATE PLAIN\r\n", 20);
#endif
+ /* Mechanism either defaulted to PLAIN or server gave us list */
+ tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[serv->sasl_mech]);
}
}
}
"twitch.tv/membership",
};
+static int
+get_supported_mech (server *serv, const char *list)
+{
+ char **mechs = g_strsplit (list, ",", 0);
+ gsize i;
+ int ret = -1;
+
+ for (i = 0; mechs[i]; ++i)
+ {
+#ifdef USE_OPENSSL
+ if (serv->loginmethod == LOGIN_SASLEXTERNAL)
+ {
+ if (!strcmp (mechs[i], "EXTERNAL"))
+ {
+ ret = MECH_EXTERNAL;
+ break;
+ }
+ }
+ else
+#endif
+ if (!strcmp (mechs[i], "PLAIN"))
+ {
+ ret = MECH_PLAIN;
+ break;
+ }
+ }
+
+ g_strfreev (mechs);
+ return ret;
+}
+
void
inbound_cap_ls (server *serv, char *nick, char *extensions_str,
const message_tags_data *tags_data)
((serv->loginmethod == LOGIN_SASL && strlen (serv->password) != 0)
|| (serv->loginmethod == LOGIN_SASLEXTERNAL && serv->have_cert)))
{
+ if (value)
+ {
+ int sasl_mech = get_supported_mech (serv, value);
+ if (sasl_mech == -1) /* No supported mech */
+ continue;
+ serv->sasl_mech = sasl_mech;
+ }
want_cap = TRUE;
want_sasl = TRUE;
g_strlcat (buffer, "sasl ", sizeof(buffer));
NULL, NULL, 0, tags_data->timestamp);
}
-static const char *sasl_mechanisms[] =
-{
- "PLAIN",
- "DH-BLOWFISH",
- "DH-AES",
- "EXTERNAL"
-};
-
-void
-inbound_sasl_supportedmechs (server *serv, char *list)
-{
- int i;
-
- if (serv->sasl_mech != MECH_EXTERNAL)
- {
- /* Use most secure one supported */
- for (i = MECH_AES; i >= MECH_PLAIN; i--)
- {
- if (strstr (list, sasl_mechanisms[i]) != NULL)
- {
- serv->sasl_mech = i;
- serv->retry_sasl = TRUE;
- tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[i]);
- return;
- }
- }
- }
-
- /* Abort, none supported */
- serv->sent_saslauth = TRUE;
- tcp_sendf (serv, "AUTHENTICATE *\r\n");
- return;
-}
-
void
inbound_sasl_authenticate (server *serv, char *data)
{
char *user, *pass = NULL;
const char *mech = sasl_mechanisms[serv->sasl_mech];
- /* Got a list of supported mechanisms from inspircd */
+ /* Got a list of supported mechanisms from outdated inspircd
+ * just ignore it as it goes against spec */
if (strchr (data, ',') != NULL)
- {
- inbound_sasl_supportedmechs (serv, data);
return;
- }
if (net->user && !(net->flags & FLAG_USE_GLOBAL))
user = net->user;
pass = encode_sasl_pass_plain (user, serv->password);
break;
#ifdef USE_OPENSSL
- case MECH_BLOWFISH:
- pass = encode_sasl_pass_blowfish (user, serv->password, data);
- break;
- case MECH_AES:
- pass = encode_sasl_pass_aes (user, serv->password, data);
- break;
case MECH_EXTERNAL:
pass = g_strdup ("+");
break;
if (pass == NULL)
{
/* something went wrong abort */
- serv->sent_saslauth = TRUE; /* prevent trying PLAIN */
tcp_sendf (serv, "AUTHENTICATE *\r\n");
return;
}
- serv->sent_saslauth = TRUE;
tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass);
g_free (pass);
NULL, NULL, 0, 0);
}
-int
+void
inbound_sasl_error (server *serv)
{
- if (serv->retry_sasl && !serv->sent_saslauth)
- return 1;
-
- /* If server sent 904 before we sent password,
- * mech not support so fallback to next mech */
- if (!serv->sent_saslauth && serv->sasl_mech != MECH_EXTERNAL && serv->sasl_mech != MECH_PLAIN)
- {
- serv->sasl_mech -= 1;
- tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[serv->sasl_mech]);
- return 1;
- }
- return 0;
+ /* Just abort, not much we can do */
+ tcp_sendf (serv, "AUTHENTICATE *\r\n");
}
void inbound_cap_del (server *serv, char *nick, char *extensions,
const message_tags_data *tags_data);
void inbound_sasl_authenticate (server *serv, char *data);
-int inbound_sasl_error (server *serv);
-void inbound_sasl_supportedmechs (server *serv, char *list);
+void inbound_sasl_error (server *serv);
void do_dns (session *sess, char *nick, char *host,
const message_tags_data *tags_data);
gboolean alert_match_word (char *word, char *masks);
word_eol[6]+1, word[1], word[2], NULL, 0,
tags_data->timestamp);
break;
- case 903: /* successful SASL auth */
case 904: /* failed SASL auth */
- if (inbound_sasl_error (serv))
- break; /* might retry */
+ inbound_sasl_error (serv);
+ case 903: /* successful SASL auth */
case 905: /* failed SASL auth */
case 906: /* aborted */
case 907: /* attempting to re-auth after a successful auth */
}
break;
case 908: /* Supported SASL Mechs */
- inbound_sasl_supportedmechs (serv, word[4]);
+ /* ignored for now, SASL 3.2 is a better solution and we only do PLAIN atm */
break;
default:
serv->chanmodes = g_strdup ("beI,k,l");
serv->nick_prefixes = g_strdup ("@%+");
serv->nick_modes = g_strdup ("ohv");
+ serv->sasl_mech = MECH_PLAIN;
server_set_encoding (serv, "UTF-8");
#ifdef USE_OPENSSL
#include <openssl/bn.h>
#include <openssl/rand.h>
-#include <openssl/blowfish.h>
-#include <openssl/aes.h>
#ifndef WIN32
#include <netinet/in.h>
#endif
return encoded;
}
-#ifdef USE_OPENSSL
-/* Adapted from ZNC's SASL module */
-
-static int
-parse_dh (char *str, DH **dh_out, unsigned char **secret_out, int *keysize_out)
-{
- DH *dh;
- guchar *data, *decoded_data;
- guchar *secret = NULL;
- gsize data_len;
- guint size;
- guint16 size16;
- BIGNUM *pubkey;
- gint key_size;
-
- dh = DH_new();
- data = decoded_data = g_base64_decode (str, &data_len);
- if (data_len < 2)
- goto fail;
-
- /* prime number */
- memcpy (&size16, data, sizeof(size16));
- size = ntohs (size16);
- data += 2;
- data_len -= 2;
-
- if (size > data_len)
- goto fail;
-
- dh->p = BN_bin2bn (data, size, NULL);
- data += size;
-
- /* Generator */
- if (data_len < 2)
- goto fail;
-
- memcpy (&size16, data, sizeof(size16));
- size = ntohs (size16);
- data += 2;
- data_len -= 2;
-
- if (size > data_len)
- goto fail;
-
- dh->g = BN_bin2bn (data, size, NULL);
- data += size;
-
- /* pub key */
- if (data_len < 2)
- goto fail;
-
- memcpy (&size16, data, sizeof(size16));
- size = ntohs(size16);
- data += 2;
- data_len -= 2;
-
- pubkey = BN_bin2bn (data, size, NULL);
- if (!(DH_generate_key (dh)))
- goto fail;
-
- secret = g_malloc (DH_size (dh));
- key_size = DH_compute_key (secret, pubkey, dh);
- if (key_size == -1)
- goto fail;
-
- g_free (decoded_data);
-
- *dh_out = dh;
- *secret_out = secret;
- *keysize_out = key_size;
- return 1;
-
-fail:
- g_free (secret);
- g_free (decoded_data);
-
- return 0;
-}
-
-char *
-encode_sasl_pass_blowfish (char *user, char *pass, char *data)
-{
- DH *dh;
- char *response, *ret = NULL;
- unsigned char *secret;
- unsigned char *encrypted_pass;
- char *plain_pass;
- BF_KEY key;
- int key_size, length;
- int pass_len = strlen (pass) + (8 - (strlen (pass) % 8));
- int user_len = strlen (user);
- guint16 size16;
- char *in_ptr, *out_ptr;
-
- if (!parse_dh (data, &dh, &secret, &key_size))
- return NULL;
- BF_set_key (&key, key_size, secret);
-
- encrypted_pass = g_malloc0 (pass_len);
- plain_pass = g_malloc0 (pass_len);
- memcpy (plain_pass, pass, strlen(pass));
- out_ptr = (char*)encrypted_pass;
- in_ptr = (char*)plain_pass;
-
- for (length = pass_len; length; length -= 8, in_ptr += 8, out_ptr += 8)
- BF_ecb_encrypt ((unsigned char*)in_ptr, (unsigned char*)out_ptr, &key, BF_ENCRYPT);
-
- /* Create response */
- length = 2 + BN_num_bytes (dh->pub_key) + pass_len + user_len + 1;
- response = g_malloc0 (length);
- out_ptr = response;
-
- /* our key */
- size16 = htons ((guint16)BN_num_bytes (dh->pub_key));
- memcpy (out_ptr, &size16, sizeof(size16));
- out_ptr += 2;
- BN_bn2bin (dh->pub_key, (guchar*)out_ptr);
- out_ptr += BN_num_bytes (dh->pub_key);
-
- /* username */
- memcpy (out_ptr, user, user_len + 1);
- out_ptr += user_len + 1;
-
- /* pass */
- memcpy (out_ptr, encrypted_pass, pass_len);
-
- ret = g_base64_encode ((const guchar*)response, length);
-
- g_free (response);
-
- DH_free(dh);
- g_free (plain_pass);
- g_free (encrypted_pass);
- g_free (secret);
-
- return ret;
-}
-
-char *
-encode_sasl_pass_aes (char *user, char *pass, char *data)
-{
- DH *dh;
- AES_KEY key;
- char *response = NULL;
- char *out_ptr, *ret = NULL;
- unsigned char *secret, *ptr;
- unsigned char *encrypted_userpass, *plain_userpass;
- int key_size, length;
- guint16 size16;
- unsigned char iv[16], iv_copy[16];
- int user_len = strlen (user) + 1;
- int pass_len = strlen (pass) + 1;
- int len = user_len + pass_len;
- int padlen = 16 - (len % 16);
- int userpass_len = len + padlen;
-
- if (!parse_dh (data, &dh, &secret, &key_size))
- return NULL;
-
- encrypted_userpass = g_malloc0 (userpass_len);
- plain_userpass = g_malloc0 (userpass_len);
-
- /* create message */
- /* format of: <username>\0<password>\0<padding> */
- ptr = plain_userpass;
- memcpy (ptr, user, user_len);
- ptr += user_len;
- memcpy (ptr, pass, pass_len);
- ptr += pass_len;
- if (padlen)
- {
- /* Padding */
- unsigned char randbytes[16];
- if (!RAND_bytes (randbytes, padlen))
- goto end;
-
- memcpy (ptr, randbytes, padlen);
- }
-
- if (!RAND_bytes (iv, sizeof (iv)))
- goto end;
-
- memcpy (iv_copy, iv, sizeof(iv));
-
- /* Encrypt */
- AES_set_encrypt_key (secret, key_size * 8, &key);
- AES_cbc_encrypt(plain_userpass, encrypted_userpass, userpass_len, &key, iv_copy, AES_ENCRYPT);
-
- /* Create response */
- /* format of: <size pubkey><pubkey><iv (always 16 bytes)><ciphertext> */
- length = 2 + key_size + sizeof(iv) + userpass_len;
- response = g_malloc (length);
- out_ptr = response;
-
- /* our key */
- size16 = htons ((guint16)key_size);
- memcpy (out_ptr, &size16, sizeof(size16));
- out_ptr += 2;
- BN_bn2bin (dh->pub_key, (guchar*)out_ptr);
- out_ptr += key_size;
-
- /* iv */
- memcpy (out_ptr, iv, sizeof(iv));
- out_ptr += sizeof(iv);
-
- /* userpass */
- memcpy (out_ptr, encrypted_userpass, userpass_len);
-
- ret = g_base64_encode ((const guchar*)response, length);
-
-end:
- DH_free (dh);
- g_free (plain_userpass);
- g_free (encrypted_userpass);
- g_free (secret);
- g_free (response);
-
- return ret;
-}
-#endif
-
#ifdef USE_OPENSSL
static char *
str_sha256hash (char *string)
int portable_mode (void);
int unity_mode (void);
char *encode_sasl_pass_plain (char *user, char *pass);
-char *encode_sasl_pass_blowfish (char *user, char *pass, char *data);
-char *encode_sasl_pass_aes (char *user, char *pass, char *data);
char *challengeauth_response (char *username, char *password, char *challenge);
size_t strftime_validated (char *dest, size_t destsize, const char *format, const struct tm *time);
gsize strftime_utf8 (char *dest, gsize destsize, const char *format, time_t time);