From 4c0d051ff948e7eeccaab59892e5e7abe3162db4 Mon Sep 17 00:00:00 2001 From: Michael Ortmann Date: Fri, 26 Apr 2024 18:03:56 +0200 Subject: [PATCH 01/13] Implement SASL_MECHANISM_SCRAM_SHA_256 and SASL_MECHANISM_SCRAM_SHA_512 State machine for sasl scram Add Tcl_TraceVar() for sasl-mechanism Modularized sasl stuff into sasl.c / Refactor Updated doc Set sasl-username to username, if not set Enhance logging Leave got900() in servmsg.c instead of sasl.c Constant time memory comparison Update valid cap sasl mechanism list on server 908 Handle SASL AUTHENTICATE server error pre sasl mechanism ECDH-X25519-CHALLENGE --- eggdrop.conf | 8 +- src/mod/server.mod/Makefile | 4 +- src/mod/server.mod/sasl.c | 731 +++++++++++++++++++++++++++++++++++ src/mod/server.mod/server.c | 71 +--- src/mod/server.mod/server.h | 11 - src/mod/server.mod/servmsg.c | 352 ++--------------- 6 files changed, 787 insertions(+), 390 deletions(-) create mode 100644 src/mod/server.mod/sasl.c diff --git a/eggdrop.conf b/eggdrop.conf index 8ab6a1a72..93ea11253 100755 --- a/eggdrop.conf +++ b/eggdrop.conf @@ -1113,12 +1113,18 @@ server add ssl.example.net +7000 # 1 = ECDSA-NIST256P-CHALLENGE (Uses a certificate; usually requires a # public key to be registered with NickServ # or other similar service. Set certificate -# to use in sasl-ecdsa-key setting below) +# to use in sasl-ecdsa-key setting below. +# Beware: NIST curve could be backdoored, +# so please use EXTERNAL or SCRAM instead.) # # 2 = EXTERNAL (Some other method you set up. Certificates # used are defined in ssl-certificate and # ssl-privatekey settings in SSL section) # +# 3 = SCRAM-SHA-256 +# +# 4 = SCRAM-SHA-512 +# #set sasl-mechanism 0 # Set username to authenticate to IRC NickServ with diff --git a/src/mod/server.mod/Makefile b/src/mod/server.mod/Makefile index 1b26a1043..8f84cc8d8 100644 --- a/src/mod/server.mod/Makefile +++ b/src/mod/server.mod/Makefile @@ -40,5 +40,5 @@ distclean: clean .././server.mod/server.h .././server.mod/isupport.c \ .././server.mod/tclisupport.c .././server.mod/servmsg.c \ .././server.mod/../irc.mod/irc.h \ - .././server.mod/../channels.mod/channels.h .././server.mod/cmdsserv.c \ - .././server.mod/tclserv.c + .././server.mod/../channels.mod/channels.h .././server.mod/sasl.c \ + .././server.mod/cmdsserv.c .././server.mod/tclserv.c diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c new file mode 100644 index 000000000..9660999f0 --- /dev/null +++ b/src/mod/server.mod/sasl.c @@ -0,0 +1,731 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * sasl.c -- part of server.mod + * + * Written by Michael Ortmann + * + * Copyright (C) 2019 - 2024 Eggheads Development Team + */ + +#undef answer /* before resolv.h because it could collide with src/mod/module.h + * (dietlibc) */ +#include /* base64 encode b64_ntop() and base64 decode b64_pton() */ + +/* RFC 5802 - printable ASCII characters excluding ',' + * printable = %x21-2B / %x2D-7E + */ +#define CHARSET_SCRAM "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e" + +#define CLIENT_KEY "Client Key" +#define SERVER_KEY "Server Key" + +/* Available sasl mechanisms */ +enum { + SASL_MECHANISM_PLAIN, + SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE, + SASL_MECHANISM_EXTERNAL, + SASL_MECHANISM_SCRAM_SHA_256, + SASL_MECHANISM_SCRAM_SHA_512, + /* TODO: https://github.com/atheme/atheme/blob/master/modules/saslserv/ecdh-x25519-challenge.c */ + /* SASL_MECHANISM_ECDH_X25519_CHALLENGE, */ + SASL_MECHANISM_NUM +}; + +#define SASL_PASSWORD_MAX 120 +#define SASL_ECDSA_KEY_MAX 120 + +static int sasl_timeout_time = 0; +static int sasl_continue = 1; +static char sasl_username[NICKMAX + 1]; +static int sasl_mechanism = 0; +static char sasl_password[SASL_PASSWORD_MAX + 1]; +static char sasl_ecdsa_key[SASL_ECDSA_KEY_MAX + 1]; +static int sasl_timeout = 15; +int sasl = 0; + +/* Available sasl mechanisms. */ +static char const *SASL_MECHANISMS[SASL_MECHANISM_NUM] = { + [SASL_MECHANISM_PLAIN] = "PLAIN", + [SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE] = "ECDSA-NIST256P-CHALLENGE", + [SASL_MECHANISM_EXTERNAL] = "EXTERNAL", + [SASL_MECHANISM_SCRAM_SHA_256] = "SCRAM-SHA-256", + [SASL_MECHANISM_SCRAM_SHA_512] = "SCRAM-SHA-512", + /* [SASL_MECHANISM_ECDH_X25519_CHALLENGE] = "ECDH-X25519-CHALLENGE", */ +}; + +/* scram state */ +#ifdef TLS +#if OPENSSL_VERSION_NUMBER >= 0x10000000L /* 1.0.0 */ +const EVP_MD *digest; +char salted_password[EVP_MAX_MD_SIZE]; +static int step = 0; +char nonce[21]; /* atheme defines acceptable client nonce len min 8 max 512 chars + * nonce 128 bit = math.ceil(128 / math.log(93, 2)) = 20 chars + * 3 major irc clients and postgres use 18, looks like ripping is still a thing ;) + */ +char client_first_message[1024]; +int digest_len, auth_message_len; +char auth_message[3069]; +#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ +#endif /* TLS */ + +static void sasl_error(const char *msg) +{ + putlog(LOG_SERV, "*", "SASL: %s", msg); + dprintf(DP_MODE, "CAP END\n"); + sasl_timeout_time = 0; + if (!sasl_continue) { + putlog(LOG_DEBUG, "*", "SASL: Aborting connection and retrying"); + nuke_server("sasl"); + } +} + +static void sasl_secondly() +{ + if (!--sasl_timeout_time) + sasl_error("timeout"); +} + +/* Got 901: RPL_LOGGEDOUT, users account name is unset (whether by SASL or + * otherwise) + */ +static int got901(char *from, char *msg) +{ + newsplit(&msg); /* nick */ + newsplit(&msg); /* nick!ident@host */ + fixcolon(msg); + putlog(LOG_SERV, "*", "%s: %s", from, msg); + return 0; +} + +/* Got 902: ERR_NICKLOCKED, authentication fails b/c nick is unavailable + * Got 904: ERR_SASLFAIL, invalid credentials (or something not covered) + * Got 905: ERR_SASLTOOLONG, AUTHENTICATE command was too long (>400 bytes) + * Got 906: ERR_SASL_ABORTED, sent AUTHENTICATE command with * as parameter + * For easy grepping, this covers got902 got904 got905 got906 + */ +static int gotsasl90X(char *from, char *msg) +{ + newsplit(&msg); /* nick */ + fixcolon(msg); + sasl_error(msg); + return 0; +} + +/* Got 903: RPL_SASLSUCCESS, authentication successful */ +static int got903(char *from, char *msg) +{ + newsplit(&msg); /* nick */ + fixcolon(msg); + putlog(LOG_SERV, "*", "SASL: %s", msg); + dprintf(DP_MODE, "CAP END\n"); + sasl_timeout_time = 0; + return 0; +} + +/* Got 907: ERR_SASLALREADY, already authenticated */ +static int got907(char *from, char *msg) +{ + putlog(LOG_SERV, "*", "SASL: Already authenticated"); + return 0; +} + +/* Got 908: RPL_SASLMECHS, available mechanisms by network */ +static int got908(char *from, char *msg) +{ + char s[128]; + + newsplit(&msg); /* nick */ + fixcolon(msg); + putlog(LOG_SERV, "*", "SASL: Available mechanisms: %s", msg); + del_capability("sasl"); + snprintf(s, sizeof s, "sasl=%s", msg); + add_capabilities(s); + return 0; +} + +static int sasl_plain(char *client_msg_plain) +{ + /* Don't use snprintf() due to \0 inside */ + char *s = client_msg_plain; + s = stpcpy(s, sasl_username) + 1; + s = stpcpy(s, sasl_username) + 1; + s = stpcpy(s, sasl_password); + return s - client_msg_plain; +} + +#ifdef TLS +static int sasl_ecdsa_nist256p_challange_step_0(char *client_msg_plain) +{ + /* Don't use snprintf() due to \0 inside */ + char *s = client_msg_plain; + s = stpcpy(s, sasl_username) + 1; + s = stpcpy(s, sasl_username); + return s - client_msg_plain; +} + +static int sasl_ecdsa_nist256p_challange_step_1( + char *restrict client_msg_plain, char *restrict server_msg_plain, + int server_msg_plain_len) +{ + FILE *fp; + char error_msg[256]; /* snprintf() truncation should be tolerable */ + EVP_PKEY *pkey; + + if (!(fp = fopen(sasl_ecdsa_key, "r"))) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: could not open " + "file sasl_ecdsa_key %s: %s\n", sasl_ecdsa_key, strerror(errno)); + sasl_error(error_msg); + return -1; + } + if (!(pkey = PEM_read_PrivateKey(fp, NULL, 0, NULL))) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: " + "PEM_read_PrivateKey(): SSL error = %s\n", + ERR_error_string(ERR_get_error(), 0)); + sasl_error(error_msg); + fclose(fp); + return -1; + } + fclose(fp); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L /* 1.0.0 */ + EVP_PKEY_CTX *ctx; + size_t siglen; + + /* The EVP interface to digital signatures should almost always be used in + * preference to the low level interfaces. + */ + if (!(ctx = EVP_PKEY_CTX_new(pkey, NULL))) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: EVP_PKEY_CTX_new(): " + "SSL error = %s\n", ERR_error_string(ERR_get_error(), 0)); + sasl_error(error_msg); + return -1; + } + EVP_PKEY_free(pkey); + if (EVP_PKEY_sign_init(ctx) <= 0) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: EVP_PKEY_sign_init():" + "SSL error = %s\n", ERR_error_string(ERR_get_error(), 0)); + sasl_error(error_msg); + EVP_PKEY_CTX_free(ctx); + return -1; + } + if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: " + "EVP_PKEY_CTX_set_signature_md(): SSL error = %s\n", + ERR_error_string(ERR_get_error(), 0)); + sasl_error(error_msg); + EVP_PKEY_CTX_free(ctx); + return -1; + } + /* EVP_PKEY_sign() must be used instead of EVP_DigestSign*() and EVP_Sign*(), + * because EVP_PKEY_sign() does not hash the data to be signed. + * EVP_PKEY_sign() is for signing digests, EVP_DigestSign*() and EVP_Sign*() + * are for signing messages. + */ + if (EVP_PKEY_sign(ctx, NULL, &siglen, (unsigned char *) server_msg_plain, server_msg_plain_len) <= 0) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: EVP_PKEY_sign(): SSL " + "error = %s\n", ERR_error_string(ERR_get_error(), 0)); + sasl_error(error_msg); + EVP_PKEY_CTX_free(ctx); + return -1; + } + if (EVP_PKEY_sign(ctx, (unsigned char *) client_msg_plain, &siglen, (unsigned char *) server_msg_plain, server_msg_plain_len) <= 0) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: EVP_PKEY_sign(): SSL " + "error = %s\n", ERR_error_string(ERR_get_error(), 0)); + sasl_error(error_msg); + EVP_PKEY_CTX_free(ctx); + return -1; + } + EVP_PKEY_CTX_free(ctx); +#else + EC_KEY *eckey; + int ret; + unsigned int siglen; + + eckey = EVP_PKEY_get1_EC_KEY(pkey); + EVP_PKEY_free(pkey); + if (!eckey) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: " + "EVP_PKEY_get1_EC_KEY(): SSL error = %s\n", + ERR_error_string(ERR_get_error(), 0)); + sasl_error(error_msg); + return -1; + } + ret = ECDSA_sign(0, (const unsigned char *) server_msg_plain, + server_msg_plain_len, + (unsigned char *) client_msg_plain, &siglen, eckey); + EC_KEY_free(eckey); + if (!ret) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: ECDSA_sign() SSL " + "error = %s\n", ERR_error_string(ERR_get_error(), 0)); + sasl_error(error_msg); + return -1; + } +#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ + return siglen; +} + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L /* 1.0.0 */ +static int sasl_scram_step_0(char *client_msg_plain, int client_msg_plain_len) +{ + /* TODO: after sasl scram merged make_rand_str_from_chars() should be made + * return unbiased uniformed randoms + */ + make_rand_str_from_chars(nonce, (sizeof nonce) - 1, CHARSET_SCRAM); + return snprintf(client_msg_plain, client_msg_plain_len, "n,,n=%s,r=%s", + sasl_username, nonce); +} + +static int sasl_scram_step_1(char *restrict client_msg_plain, + int client_msg_plain_len, + char *restrict server_msg_plain) +{ + char server_first_message[1024]; + char *word, *brkb, *server_nonce = 0, *salt_b64 = 0, *i = 0; + char error_msg[128]; /* snprintf() truncation should be tolerable */ + int salt_plain_len, iter, j; + char salt_plain[64]; /* atheme: Valid values are 8 to 64 (inclusive) */ + char client_key[EVP_MAX_MD_SIZE]; + unsigned int client_key_len, stored_key_len; + unsigned char stored_key[EVP_MAX_MD_SIZE]; + char client_final_message_without_proof[1024]; + unsigned char client_signature[EVP_MAX_MD_SIZE]; + unsigned char client_proof[EVP_MAX_MD_SIZE]; + char client_proof_b64[1024]; + + strlcpy(server_first_message, server_msg_plain, sizeof server_first_message); + for (word = strtok_r(server_msg_plain, ",", &brkb); + word; + word = strtok_r(NULL, ",", &brkb)) { + switch (*word) { + case 'r': + if ( +#if OPENSSL_VERSION_NUMBER >= 0x1010008fL /* 1.1.0h */ + CRYPTO_memcmp +#else + memcmp +#endif + (word + 2, nonce, (sizeof nonce) - 1)) { + sasl_error("AUTHENTICATE error: server nonce != client nonce"); + return -1; + } + server_nonce = word + 2; + break; + case 's': + salt_b64 = word + 2; + break; + case 'i': + i = word + 2; + break; + case 'e': + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: server error: %s", word + 2); + sasl_error(error_msg); + return -1; + default: + putlog(LOG_SERV, "*", "SASL: AUTHENTICATE warning: SCRAM Attribute ignored: %s", word); + } + } + if (!server_nonce) { + sasl_error("AUTHENTICATE error: server nonce missing from SCRAM challenge"); + return -1; + } + if (!salt_b64) { + sasl_error("AUTHENTICATE error: salt missing from SCRAM challenge"); + return -1; + } + if (!i) { + sasl_error("AUTHENTICATE error: iteration count missing from SCRAM challenge"); + return -1; + } + /* TODO: normalize(password) + * Eggdrop doesnt have support for utf8 normalization yet + * tcl also doesnt have it in core yet, only in tcllib + * We could use glib or something + */ + + if ((salt_plain_len = b64_pton(salt_b64, (unsigned char*) salt_plain, sizeof salt_plain)) == -1) { + sasl_error("AUTHENTICATE error: could not base64 decode salt"); + return -1; + } + errno = 0; + iter = strtol(i, NULL, 10); + if (errno) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: strtol(%s): %s", i, strerror(errno)); + sasl_error(error_msg); + return -1; + } + + printf("DEBUG: server_nonce: >>>%s<<<\n", server_nonce); + printf("DEBUG: salt_b64: >>>%s<<<\n", salt_b64); + printf("DEBUG: iter: %i\n", iter); + printf("DEBUG: salt_plain_len: %i\n", salt_plain_len); + + if (sasl_mechanism == SASL_MECHANISM_SCRAM_SHA_256) + digest = EVP_sha256(); + else + digest = EVP_sha512(); + digest_len = EVP_MD_size(digest); + /* TODO: print time spent for pbkdf2 func */ + if (!PKCS5_PBKDF2_HMAC(sasl_password, strlen(sasl_password), + (const unsigned char *) salt_plain, salt_plain_len, + iter, digest, digest_len, + (unsigned char *) salted_password)) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: " + "PKCS5_PBKDF2_HMAC(): %s", ERR_error_string(ERR_get_error(), + NULL)); + sasl_error(error_msg); + return -1; + } + + printf("DEBUG: salted_password ready\n"); + + /* ClientKey := HMAC(SaltedPassword, "Client Key") */ + + if (!HMAC(digest, salted_password, digest_len, (unsigned char *) CLIENT_KEY, + strlen(CLIENT_KEY), (unsigned char *) client_key, + &client_key_len)) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", + ERR_error_string(ERR_get_error(), NULL)); + sasl_error(error_msg); + return -1; + } + + printf("DEBUG: client_key ready\n"); + + /* StoredKey := H(ClientKey) */ + + if (!EVP_Digest(client_key, client_key_len, stored_key, &stored_key_len, digest, NULL)) { + snprintf(error_msg, sizeof error_msg, + "AUTHENTICATE error: EVP_Digest(): %s", + ERR_error_string(ERR_get_error(), NULL)); + sasl_error(error_msg); + return -1; + } + + printf("DEBUG: stored_key ready\n"); + + /* AuthMessage := client-first-message-bare + "," + + * server-first-message + "," + + * client-final-message-without-proof + */ + + snprintf(client_final_message_without_proof, + sizeof client_final_message_without_proof, "c=biws,r=%s", + server_nonce); + + printf("DEBUG: client_final_message_without_proof = >>>%s<<<\n", client_final_message_without_proof); + + auth_message_len = snprintf(auth_message, sizeof auth_message, "%s,%s,%s", + client_first_message + 3, server_first_message, + client_final_message_without_proof); + + printf("DEBUG: auth_message ready: >>>%s<<<\n", auth_message); + + /* ClientSignature := HMAC(StoredKey, AuthMessage) */ + + printf("DEBUG: digestlen: %i auth_message_len: %i\n", digest_len, auth_message_len); + + if (!HMAC(digest, stored_key, digest_len, (unsigned char *) auth_message, + auth_message_len, client_signature, NULL)) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", + ERR_error_string(ERR_get_error(), NULL)); + sasl_error(error_msg); + return -1; + } + + printf("DEBUG: client_signature ready\n"); + + /* ClientProof := ClientKey XOR ClientSignature */ + + printf("DEBUG: client_key_len: %i\n", client_key_len); + + for (j = 0; j < client_key_len; j++) + client_proof[j] = client_key[j] ^ client_signature[j]; + + printf("DEBUG: client_proof ready\n"); + + if (b64_ntop(client_proof, client_key_len, client_proof_b64, sizeof client_proof_b64) == -1) { + sasl_error("AUTHENTICATE error: could not base64 encode"); + return -1; + } + + printf("DEBUG: base64-encoded client_proof ready\n"); + + printf("DEBUG: client_final_message_without_proof = >>>%s<<<\n", client_final_message_without_proof); + + return snprintf(client_msg_plain, client_msg_plain_len, "%s,p=%s", + client_final_message_without_proof, client_proof_b64); +} + +static void sasl_scram_step_2(char *restrict client_msg_plain, + int client_msg_plain_len, + char *restrict server_msg_plain) +{ + char server_key[EVP_MAX_MD_SIZE]; + unsigned int server_key_len; + char error_msg[128]; /* snprintf() truncation should be tolerable */ + unsigned char server_signature[EVP_MAX_MD_SIZE]; + char server_signature_b64[128]; + int server_signature_b64_len; + + /* ServerKey := HMAC(SaltedPassword, "Server Key") */ + + if (!HMAC(digest, salted_password, digest_len, (unsigned char *) SERVER_KEY, + strlen(SERVER_KEY), (unsigned char *) server_key, + &server_key_len)) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", + ERR_error_string(ERR_get_error(), NULL)); + sasl_error(error_msg); + return; + } + + printf("DEBUG: server_key ready\n"); + + /* ServerSignature := HMAC(ServerKey, AuthMessage) */ + + printf("DEBUG: digestlen: %i auth_message_len: %i\n", digest_len, auth_message_len); + + if (!HMAC(digest, server_key, digest_len, (unsigned char *) auth_message, + auth_message_len, server_signature, NULL)) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", + ERR_error_string(ERR_get_error(), NULL)); + sasl_error(error_msg); + return; + } + + printf("DEBUG: server_signature ready\n"); + + if ((server_signature_b64_len = b64_ntop(server_signature, digest_len, server_signature_b64, sizeof server_signature_b64)) == -1) { + sasl_error("AUTHENTICATE error: could not base64 encode"); + return; + } + + printf("DEBUG: base64-encoded server_signature ready\n"); + + printf("DEBUG: server_signature_b64 = >>>%s<<<\n", server_signature_b64); + + printf("DEBUG: server_signature_b64_len = %i\n", server_signature_b64_len); + + if ( +#if OPENSSL_VERSION_NUMBER >= 0x1010008fL /* 1.1.0h */ + CRYPTO_memcmp +#else + memcmp +#endif + (server_msg_plain + 2, server_signature_b64, server_signature_b64_len)) { + sasl_error("invalid server signature"); + return; + } + + putlog(LOG_SERV, "*", "SASL: authentication of server successful"); + dprintf(DP_MODE, "AUTHENTICATE +\n"); + sasl_timeout_time = 0; +} +#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ +#endif /* TLS */ + +/* TODO: + * modularize + * aim is final version <= 70 lines + * state machine, at least for scram + * guard sasl auth with timeout + * sasl-password should be sasl-password-file so we read the pass from file + * and keep it only in memory while we need it, + * we could also enable/disable all sasl raw bindings to minimize attack + * surface + * in the end, fuzzing would be nice, coze we do a lot of parsing here + * server_iter and rusage should be displayed for the function calling + * pbkdf2(server_iter) + * cache the client_key (assuming the Salt and hash iteration-count is stable) + * support authenticate split by 400 byte, like: + * https://github.com/ircv3/ircv3-specifications/commit/838ef397385065bbc5c29d934bbb407e5b5a5ce5 + * 400-byte chunk, see: https://ircv3.net/specs/extensions/sasl-3.1.html + * base64 padding + * The response is encoded in Base64 (RFC 4648), then split to + * 400-byte chunks, and each chunk is sent as a separate AUTHENTICATE + * command. + */ +static int gotauthenticate(char *from, char *msg) +{ + char client_msg_plain[1024]; + int client_msg_plain_len; +#ifdef TLS + char server_msg_plain[1024]; + char error_msg[1050]; /* snprintf() truncation should be tolerable */ + int server_msg_plain_len; +#endif + #ifndef MAX + #define MAX(a,b) (((a)>(b))?(a):(b)) + #endif + char client_msg_b64[((MAX((sizeof client_msg_plain), 400) + 2) / 3) << 2] = ""; + + + putlog(LOG_DEBUG, "*", "SASL: got AUTHENTICATE %s", msg); + fixcolon(msg); /* Because Inspircd does its own thing */ +#ifdef TLS + if (*msg == '+') { +#endif + if (!*sasl_username) { /* TODO: mind. fuer EXTERNAL muessen wir das nicht machen */ + putlog(LOG_SERV, "*", "SASL: sasl-username not set, setting it to " + "username %s", botname); + strlcpy(sasl_username, botuser, sizeof sasl_username); + } +#ifdef TLS + switch (sasl_mechanism) { + case SASL_MECHANISM_PLAIN: +#endif + client_msg_plain_len = sasl_plain(client_msg_plain); +#ifdef TLS + break; + case SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE: + client_msg_plain_len = sasl_ecdsa_nist256p_challange_step_0(client_msg_plain); + break; + case SASL_MECHANISM_EXTERNAL: + putlog(LOG_DEBUG, "*", "SASL: put AUTHENTICATE Response +"); + dprintf(DP_MODE, "AUTHENTICATE +\n"); + return 0; +#if OPENSSL_VERSION_NUMBER >= 0x10000000L /* 1.0.0 */ + case SASL_MECHANISM_SCRAM_SHA_256: + case SASL_MECHANISM_SCRAM_SHA_512: + client_msg_plain_len = sasl_scram_step_0(client_msg_plain, sizeof client_msg_plain); + strlcpy(client_first_message, client_msg_plain, + sizeof client_first_message); /* TODO: do this here or in sasl_scram_step_0() ? */ +#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ + } + } else { + if ((server_msg_plain_len = b64_pton(msg, (unsigned char*) server_msg_plain, sizeof server_msg_plain)) == -1) { + sasl_error("AUTHENTICATE: could not base64 decode line from server"); + return 0; + } + if (server_msg_plain_len < 2) { + sasl_error("AUTHENTICATE: server message too short"); + return 0; + } + if (*server_msg_plain == 'e') { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: server error: %s", server_msg_plain + 2); + sasl_error(error_msg); + return 0; + } + if (sasl_mechanism == SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE) { + if ((client_msg_plain_len = sasl_ecdsa_nist256p_challange_step_1(client_msg_plain, server_msg_plain, server_msg_plain_len)) < 0) + return 0; + } +#if OPENSSL_VERSION_NUMBER >= 0x10000000L /* 1.0.0 */ + else + if (step == 0) { + if ((client_msg_plain_len = sasl_scram_step_1(client_msg_plain, sizeof client_msg_plain, server_msg_plain)) < 0) + return 0; + step++; + } else { + sasl_scram_step_2(client_msg_plain, sizeof client_msg_plain, server_msg_plain); + return 0; + } +#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ + } +#endif /* TLS */ + if (b64_ntop((unsigned char *) client_msg_plain, client_msg_plain_len, client_msg_b64, sizeof client_msg_b64) == -1) { + sasl_error("AUTHENTICATE: could not base64 encode"); + return 0; + } + putlog(LOG_DEBUG, "*", "SASL: put AUTHENTICATE Response %s", client_msg_b64); + dprintf(DP_MODE, "AUTHENTICATE %s\n", client_msg_b64); + return 0; +} + +static char *traced_sasl_mechanism(ClientData cdata, Tcl_Interp *irp, + EGG_CONST char *name1, + EGG_CONST char *name2, int flags) +{ + if ((sasl_mechanism < 0) || (sasl_mechanism >= SASL_MECHANISM_NUM)) + return "sasl-mechanism is not set to an allowed value, please check it and" + " try again"; +#ifdef TLS +#ifndef HAVE_EVP_PKEY_GET1_EC_KEY + if (sasl_mechanism == SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE) + return "SASL NIST256P functionality missing from your TLS libs, please " + "choose a different SASL method"; +#endif /* HAVE_EVP_PKEY_GET1_EC_KEY */ +#if OPENSSL_VERSION_NUMBER < 0x10000000L /* 1.0.0 */ + if ((sasl_mechanism == SASL_MECHANISM_SCRAM_SHA_256) || + (sasl_mechanism == SASL_MECHANISM_SCRAM_SHA_512)) + return "SASL SCRAM functionality needs openssl version 1.0.0 or higher, " + "please choose a different SASL method"; +#endif /* OPENSSL_VERSION_NUMBER < 0x10000000L */ +#else /* TLS */ + if (sasl_mechanism != SASL_MECHANISM_PLAIN) + return "The selected SASL authentication method requires TLS libraries " + "which are not installed on this machine. Please choose the PLAIN " + "method."; +#endif /* TLS */ + return NULL; +} + +static cmd_t sasl_raw[] = { + {"901", "", (IntFunc) got901, NULL}, + {"902", "", (IntFunc) gotsasl90X, NULL}, + {"903", "", (IntFunc) got903, NULL}, + {"904", "", (IntFunc) gotsasl90X, NULL}, + {"905", "", (IntFunc) gotsasl90X, NULL}, + {"906", "", (IntFunc) gotsasl90X, NULL}, + {"907", "", (IntFunc) got907, NULL}, + {"908", "", (IntFunc) got908, NULL}, + {"AUTHENTICATE", "", (IntFunc) gotauthenticate, NULL}, + {NULL, NULL, NULL, NULL} +}; + +static tcl_ints sasl_tcl_ints[] = { + {"sasl", &sasl, 0}, + {"sasl-mechanism", &sasl_mechanism, 0}, + {"sasl-continue", &sasl_continue, 0}, + {"sasl-timeout", &sasl_timeout, 0}, + {NULL, NULL, 0} +}; + +static tcl_strings sasl_tcl_strings[] = { + {"sasl-username", sasl_username, NICKMAX, 0}, + {"sasl-password", sasl_password, SASL_PASSWORD_MAX, 0}, + {"sasl-ecdsa-key", sasl_ecdsa_key, SASL_ECDSA_KEY_MAX, 0}, + {NULL, NULL, 0, 0} +}; + +static void sasl_close() +{ + rem_builtins(H_raw, sasl_raw); + rem_tcl_ints(sasl_tcl_ints); + rem_tcl_strings(sasl_tcl_strings); + Tcl_UntraceVar(interp, "sasl-mechanism", TCL_TRACE_WRITES | TCL_TRACE_UNSETS, + traced_sasl_mechanism, NULL); +} + +static void sasl_start() +{ + Tcl_TraceVar(interp, "sasl-mechanism", TCL_TRACE_WRITES | TCL_TRACE_UNSETS, + traced_sasl_mechanism, NULL); + add_builtins(H_raw, sasl_raw); + add_tcl_ints(sasl_tcl_ints); + add_tcl_strings(sasl_tcl_strings); +} + +/* There are two forms of the AUTHENTICATE command: initial client message and + * later messages. The initial client message specifies the SASL mechanism to + * be used. +*/ +/* TODO: aktuell versucht eggdrop EXTERNAL ueber non-ssl verbindung, das kann + * doch nicht funktionieren, oder? also sollte eggdrop da eine warnung loggen + * und es gar nicht erst versuchen. + */ +int sasl_authenticate_initial(const struct cap_values *cap_value_list) +{ + char error_msg[128]; + putlog(LOG_DEBUG, "*", "SASL: Starting authentication process"); + if (!is_cap_value(cap_value_list, SASL_MECHANISMS[sasl_mechanism])) { + snprintf(error_msg, sizeof error_msg, + "authentication mechanism %s not supported by server", + SASL_MECHANISMS[sasl_mechanism]); /* TODO: report server supported mechanisms */ + sasl_error(error_msg); + return 1; + } + putlog(LOG_DEBUG, "*", "SASL: AUTHENTICATE %s", SASL_MECHANISMS[sasl_mechanism]); + dprintf(DP_MODE, "AUTHENTICATE %s\n", SASL_MECHANISMS[sasl_mechanism]); + sasl_timeout_time = sasl_timeout; + return 0; +} diff --git a/src/mod/server.mod/server.c b/src/mod/server.mod/server.c index 6aff0e488..5f12aa982 100644 --- a/src/mod/server.mod/server.c +++ b/src/mod/server.mod/server.c @@ -24,7 +24,6 @@ #define MODULE_NAME "server" #define MAKING_SERVER -#include #include "src/mod/module.h" #include "server.h" @@ -129,23 +128,16 @@ static int add_server(const char *, const char *, const char *); static int del_server(const char *, const char *); static void free_server(struct server_list *); -static int sasl = 0; static int away_notify = 0; static int invite_notify = 0; static int message_tags = 0; static char cap_request[CAPMAX - 9]; -static int sasl_mechanism = 0; -static char sasl_username[NICKMAX + 1]; -static char sasl_password[81]; -static int sasl_continue = 1; -static char sasl_ecdsa_key[121]; -static int sasl_timeout = 15; -static int sasl_timeout_time = 0; #include "isupport.c" #include "tclisupport.c" #include "servmsg.c" +#include "sasl.c" #define MAXPENALTY 10 @@ -157,13 +149,6 @@ static int burst; #include "cmdsserv.c" #include "tclserv.c" -/* Available sasl mechanisms. */ -char const *SASL_MECHANISMS[SASL_MECHANISM_NUM] = { - [SASL_MECHANISM_PLAIN] = "PLAIN", - [SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE] = "ECDSA-NIST256P-CHALLENGE", - [SASL_MECHANISM_EXTERNAL] = "EXTERNAL" -}; - static void write_to_server(char *s, unsigned int len) { char *s2 = nmalloc(len + 2); @@ -1734,19 +1719,16 @@ static char *traced_nicklen(ClientData cdata, Tcl_Interp *irp, } static tcl_strings my_tcl_strings[] = { - {"botnick", NULL, 0, STR_PROTECT}, - {"altnick", altnick, NICKMAX, 0}, - {"realname", botrealname, 80, 0}, - {"init-server", initserver, 120, 0}, - {"connect-server", connectserver, 120, 0}, - {"stackable-commands", stackablecmds, 510, 0}, - {"stackable2-commands", stackable2cmds, 510, 0}, - {"cap-request", cap_request, CAPMAX - 9, 0}, - {"sasl-username", sasl_username, NICKMAX, 0}, - {"sasl-password", sasl_password, 80, 0}, - {"sasl-ecdsa-key", sasl_ecdsa_key, 120, 0}, - {"net-type", net_type, 8, 0}, - {NULL, NULL, 0, 0} + {"botnick", NULL, 0, STR_PROTECT}, + {"altnick", altnick, NICKMAX, 0}, + {"realname", botrealname, 80, 0}, + {"init-server", initserver, 120, 0}, + {"connect-server", connectserver, 120, 0}, + {"stackable-commands", stackablecmds, 510, 0}, + {"stackable2-commands", stackable2cmds, 510, 0}, + {"cap-request", cap_request, CAPMAX - 9, 0}, + {"net-type", net_type, 8, 0}, + {NULL, NULL, 0, 0} }; static tcl_coups my_tcl_coups[] = { @@ -1784,10 +1766,6 @@ static tcl_ints my_tcl_ints[] = { #ifdef TLS {"ssl-verify-server", &tls_vfyserver, 0}, #endif - {"sasl", &sasl, 0}, - {"sasl-mechanism", &sasl_mechanism, 0}, - {"sasl-continue", &sasl_continue, 0}, - {"sasl-timeout", &sasl_timeout, 0}, {"away-notify", &away_notify, 0}, {"invite-notify", &invite_notify, 0}, {"message-tags", &message_tags, 0}, @@ -2034,8 +2012,8 @@ static void server_secondly() deq_msg(); if (!resolvserv && serv < 0) connect_server(); - if (!--sasl_timeout_time) - handle_sasl_timeout(); + else + sasl_secondly(); } static void server_5minutely() @@ -2310,6 +2288,7 @@ static char *server_close() del_hook(HOOK_PRE_REHASH, (Function) server_prerehash); del_hook(HOOK_REHASH, (Function) server_postrehash); del_hook(HOOK_DIE, (Function) server_die); + sasl_close(); module_undepend(MODULE_NAME); return NULL; } @@ -2508,27 +2487,6 @@ char *server_start(Function *global_funcs) my_tcl_strings[0].buf = botname; add_tcl_strings(my_tcl_strings); add_tcl_ints(my_tcl_ints); - if (sasl) { - if ((sasl_mechanism < 0) || (sasl_mechanism >= SASL_MECHANISM_NUM)) { - fatal("ERROR: sasl-mechanism is not set to an allowed value, please check" - " it and try again", 0); - } -#ifdef TLS -#ifndef HAVE_EVP_PKEY_GET1_EC_KEY - if (sasl_mechanism == SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE) { - fatal("ERROR: NIST256 functionality missing from your TLS libs, please " - "choose a different SASL method", 0); - } -#endif /* HAVE_EVP_PKEY_GET1_EC_KEY */ -#else /* TLS */ - if ((sasl_mechanism == SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE) || - (sasl_mechanism == SASL_MECHANISM_EXTERNAL)) { - fatal("ERROR: The selected SASL authentication method requires TLS " - "libraries which are not installed on this machine. Please " - "choose the PLAIN method in your config.", 0); - } -#endif /* TLS */ - } add_tcl_commands(my_tcl_cmds); add_tcl_coups(my_tcl_coups); add_hook(HOOK_SECONDLY, (Function) server_secondly); @@ -2548,5 +2506,6 @@ char *server_start(Function *global_funcs) curserv = 999; /* Because this reads the interp variable, the read trace MUST be after */ do_nettype(); + sasl_start(); return NULL; } diff --git a/src/mod/server.mod/server.h b/src/mod/server.mod/server.h index ac81a5422..17b3d9e80 100644 --- a/src/mod/server.mod/server.h +++ b/src/mod/server.mod/server.h @@ -154,15 +154,4 @@ enum { NETT_OTHER /* Others */ }; -/* Available sasl mechanisms. */ -enum { - SASL_MECHANISM_PLAIN, - SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE, - SASL_MECHANISM_EXTERNAL, - SASL_MECHANISM_NUM -}; - -/* Must be extern to avoid odr-violation and allow make static */ -extern char const *SASL_MECHANISMS[]; - #endif /* _EGG_MOD_SERVER_SERVER_H */ diff --git a/src/mod/server.mod/servmsg.c b/src/mod/server.mod/servmsg.c index c0e048a2b..25adf8924 100644 --- a/src/mod/server.mod/servmsg.c +++ b/src/mod/server.mod/servmsg.c @@ -20,15 +20,12 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#undef answer /* before resolv.h because it could collide with src/mod/module.h - * (dietlibc) */ -#include /* base64 encode b64_ntop() and base64 decode b64_pton() */ -#include #ifdef TLS #include #endif #include "../irc.mod/irc.h" #include "../channels.mod/channels.h" +#include #include "server.h" char *encode_msgtags(Tcl_Obj *msgtagdict); @@ -47,6 +44,9 @@ static void monitor_clear(); int account_notify = 1, extended_join = 1, account_tag = 0; Tcl_Obj *ncapeslist; +extern int sasl; +extern int sasl_authenticate_initial(const struct cap_values *); + /* We try to change to a preferred unique nick here. We always first try the * specified alternate nick. If that fails, we repeatedly modify the nick * until it gets accepted. @@ -1334,195 +1334,6 @@ static int gotsetname(char *from, char *msg) return 0; } -static int tryauthenticate(char *from, char *msg) -{ - char src[(sizeof sasl_username) + (sizeof sasl_username) + - (sizeof sasl_password)] = ""; - char *s; - /* 400-byte chunk, see: https://ircv3.net/specs/extensions/sasl-3.1.html - * base64 padding */ - #ifndef MAX - #define MAX(a,b) (((a)>(b))?(a):(b)) - #endif - unsigned char dst[((MAX((sizeof src), 400) + 2) / 3) << 2] = ""; -#ifdef HAVE_EVP_PKEY_GET1_EC_KEY - int olen; - FILE *fp; - EVP_PKEY *pkey; - unsigned char *sig; -#if OPENSSL_VERSION_NUMBER >= 0x10000000L /* 1.0.0 */ - EVP_PKEY_CTX *ctx; - size_t siglen; -#else - EC_KEY *eckey; - int ret; - unsigned int siglen; -#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ -#endif /* HAVE_EVP_PKEY_GET1_EC_KEY */ - putlog(LOG_DEBUG, "*", "SASL: got AUTHENTICATE %s", msg); - if (msg[0] == '+') { - s = src; - /* Don't use snprintf due to \0 inside */ - if (sasl_mechanism == SASL_MECHANISM_PLAIN) { - strcpy(s, sasl_username); - s += strlen(sasl_username) + 1; - strcpy(s, sasl_username); - s += strlen(sasl_username) + 1; - strcpy(s, sasl_password); - s += strlen(sasl_password); - dst[0] = 0; - if (b64_ntop((unsigned char *) src, s - src, (char *) dst, sizeof dst) == -1) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE error: could not base64 " - "encode"); - /* TODO: send cap end for all error cases in this function ? */ - return 1; - } - /* TODO: what about olen we used for mbedtls_base64_encode() ? */ - } - else if (sasl_mechanism == SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE) { -#ifdef HAVE_EVP_PKEY_GET1_EC_KEY - strcpy(s, sasl_username); - s += strlen(sasl_username) + 1; - strcpy(s, sasl_username); - s += strlen(sasl_username); - if (b64_ntop((unsigned char *) src, s - src, (char *) dst, sizeof dst) == -1) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE error: could not base64 " - "encode"); - return 1; - } - } -#else - putlog(LOG_DEBUG, "*", "SASL: TLS libs not present or missing EC support." - " Try the PLAIN or EXTERNAL method instead"); - return 1; - } -#endif - else { /* sasl_mechanism == SASL_MECHANISM_EXTERNAL */ -#ifdef TLS /* TLS required for EXTERNAL sasl */ - dst[0] = '+'; - dst[1] = 0; - } -#else - putlog(LOG_DEBUG, "*", "SASL: TLS libs required for EXTERNAL but are not " - "installed, try PLAIN method"); - return 1; - } -#endif /* TLS */ - putlog(LOG_DEBUG, "*", "SASL: put AUTHENTICATE %s", dst); - dprintf(DP_MODE, "AUTHENTICATE %s\n", dst); - } else { /* Only EC-challenges get extra auth messages w/o a + */ -#ifdef TLS -#ifdef HAVE_EVP_PKEY_GET1_EC_KEY - putlog(LOG_DEBUG, "*", "SASL: got AUTHENTICATE Challenge"); - olen = b64_pton(msg, dst, sizeof dst); - if (olen == -1) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE error: could not base64 decode " - "line from server"); - return 1; - } - fp = fopen(sasl_ecdsa_key, "r"); - if (!fp) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE error: could not open file " - "sasl_ecdsa_key %s: %s\n", sasl_ecdsa_key, strerror(errno)); - return 1; - } - pkey = PEM_read_PrivateKey(fp, NULL, 0, NULL); - if (!pkey) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE: PEM_read_PrivateKey(): SSL " - "error = %s\n", ERR_error_string(ERR_get_error(), 0)); - fclose(fp); - return 1; - } - fclose(fp); -#if OPENSSL_VERSION_NUMBER >= 0x10000000L /* 1.0.0 */ - /* The EVP interface to digital signatures should almost always be used in - * preference to the low level interfaces. */ - if (!(ctx = EVP_PKEY_CTX_new(pkey, NULL))) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE: EVP_PKEY_CTX_new(): SSL error = %s\n", - ERR_error_string(ERR_get_error(), 0)); - return 1; - } - EVP_PKEY_free(pkey); - if (EVP_PKEY_sign_init(ctx) <= 0) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE: EVP_PKEY_sign_init(): SSL error = %s\n", - ERR_error_string(ERR_get_error(), 0)); - EVP_PKEY_CTX_free(ctx); - return 1; - - } - if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE: EVP_PKEY_CTX_set_signature_md(): SSL error = %s\n", - ERR_error_string(ERR_get_error(), 0)); - EVP_PKEY_CTX_free(ctx); - return 1; - } - /* EVP_PKEY_sign() must be used instead of EVP_DigestSign*() and EVP_Sign*(), - * because EVP_PKEY_sign() does not hash the data to be signed. - * EVP_PKEY_sign() is for signing digests, EVP_DigestSign*() and EVP_Sign*() - * are for signing messages. - */ - if (EVP_PKEY_sign(ctx, NULL, &siglen, dst, olen) <= 0) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE: EVP_PKEY_sign(): SSL error = %s\n", - ERR_error_string(ERR_get_error(), 0)); - EVP_PKEY_CTX_free(ctx); - return 1; - } - sig = nmalloc(siglen); - if (EVP_PKEY_sign(ctx, sig, &siglen, dst, olen) <= 0) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE: EVP_PKEY_sign(): SSL error = %s\n", - ERR_error_string(ERR_get_error(), 0)); - nfree(sig); - EVP_PKEY_CTX_free(ctx); - return 1; - } - EVP_PKEY_CTX_free(ctx); -#else - eckey = EVP_PKEY_get1_EC_KEY(pkey); - EVP_PKEY_free(pkey); - if (!eckey) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE: EVP_PKEY_get1_EC_KEY(): SSL error = %s\n", - ERR_error_string(ERR_get_error(), 0)); - return 1; - } - sig = nmalloc(ECDSA_size(eckey)); - ret = ECDSA_sign(0, dst, olen, sig, &siglen, eckey); - EC_KEY_free(eckey); - if (!ret) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE: ECDSA_sign() SSL error = %s\n", - ERR_error_string(ERR_get_error(), 0)); - nfree(sig); - return 1; - } -#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ - if (b64_ntop(sig, siglen, (char *) dst, sizeof dst) == -1) { - putlog(LOG_SERV, "*", "SASL: AUTHENTICATE error: could not base64 encode"); - nfree(sig); - return 1; - } - nfree(sig); - putlog(LOG_DEBUG, "*", "SASL: put AUTHENTICATE Response %s", dst); - dprintf(DP_MODE, "AUTHENTICATE %s\n", dst); -#endif /* HAVE_EVP_PKEY_GET1_EC_KEY */ -#else /* TLS */ - putlog(LOG_SERV, "*", "SASL: Received EC message, but no TLS EC libs " - "present. Try PLAIN method"); - return 1; -#endif /* TLS */ - } - return 0; -} - -static int gotauthenticate(char *from, char *msg) -{ - fixcolon(msg); /* Because Inspircd does its own thing */ - if (tryauthenticate(from, msg) && !sasl_continue) { - putlog(LOG_SERV, "*", "SASL: Aborting connection and retrying"); - nuke_server("Quitting..."); - return 1; - } - return 0; -} - /* Got 900: RPL_LOGGEDIN, users account name is set (whether by SASL or otherwise) */ static int got900(char *from, char *msg) { @@ -1534,75 +1345,8 @@ static int got900(char *from, char *msg) return 0; } -/* Got 901: RPL_LOGGEDOUT, users account name is unset (whether by SASL or otherwise) */ -static int got901(char *from, char *msg) -{ - newsplit(&msg); /* nick */ - newsplit(&msg); /* nick!ident@host */ - fixcolon(msg); - putlog(LOG_SERV, "*", "%s: %s", from, msg); - return 0; -} - -static int sasl_error(char *msg) -{ - putlog(LOG_SERV, "*", "SASL: %s", msg); - dprintf(DP_MODE, "CAP END\n"); - sasl_timeout_time = 0; - if (!sasl_continue) { - putlog(LOG_DEBUG, "*", "SASL: Aborting connection and retrying"); - nuke_server("Quitting..."); - } - return 1; -} - -/* Got 902: ERR_NICKLOCKED, authentication fails b/c nick is unavailable - * Got 904: ERR_SASLFAIL, invalid credentials (or something not covered) - * Got 905: ERR_SASLTOOLONG, AUTHENTICATE command was too long (>400 bytes) - * Got 906: ERR_SASL_ABORTED, sent AUTHENTICATE command with * as parameter - * For easy grepping, this covers got902 got904 got905 got906 - */ -static int gotsasl90X(char *from, char *msg) -{ - newsplit(&msg); /* nick */ - fixcolon(msg); - return sasl_error(msg); -} - -/* Got 903: RPL_SASLSUCCESS, authentication successful */ -static int got903(char *from, char *msg) -{ - newsplit(&msg); /* nick */ - fixcolon(msg); - putlog(LOG_SERV, "*", "SASL: %s", msg); - dprintf(DP_MODE, "CAP END\n"); - sasl_timeout_time = 0; - return 0; -} - -/* Got 907: ERR_SASLALREADY, already authenticated */ -static int got907(char *from, char *msg) -{ - putlog(LOG_SERV, "*", "SASL: Already authenticated"); - return 0; -} - -/* Got 908: RPL_SASLMECHS, mechanisms supported by network */ -static int got908(char *from, char *msg) -{ - newsplit(&msg); /* nick */ - fixcolon(msg); - putlog(LOG_SERV, "*", "SASL: %s", msg); - return 0; -} - -static int handle_sasl_timeout() -{ - return sasl_error("timeout"); -} - /* - * 465 ERR_YOUREBANNEDCREEP :You are banned from this server + * 465 ERR_YOUREBANNEDCREEP :You are banned from this server */ static int got465(char *from, char *msg) { @@ -1768,9 +1512,11 @@ static int add_capabilities(char *msg) { } /* Helper function to see if given value exists for a capability */ -static int checkvalue(struct cap_values *caplist, const char *name) { - struct cap_values *current = caplist; +static int is_cap_value(const struct cap_values *cap_value_list, const char *name) { + const struct cap_values *current = cap_value_list; + if (!cap_value_list) + return 1; while (current != NULL) { if (!strcmp(name, current->name)) { return 1; @@ -1807,25 +1553,26 @@ static int gotcap(char *from, char *msg) { return 0; } current = cap; -/* CAP is supported, yay! If it is supported, lets load what we want to request */ + /* CAP is supported, yay! If it is supported, lets load what we want to request */ while (current != NULL) { - if (!strcmp(current->name, "sasl") && (sasl) && !(current->enabled)) { - add_req(current->name); - } else if (!strcmp(current->name, "account-notify") && (account_notify) - && (!current->enabled)) { - add_req(current->name); - } else if (!strcmp(current->name, "account-tag") && (account_tag) - && (!current->enabled)) { - add_req(current->name); - } else if (!strcmp(current->name, "extended-join") && (extended_join) - && (!current->enabled)) { - add_req(current->name); - } else if (!strcmp(current->name, "invite-notify") && (invite_notify) - && (!current->enabled)) { - add_req(current->name); - } else if (!strcmp(current->name, "message-tags") && (message_tags) - && (!current->enabled)) { - add_req(current->name); + if (!strcmp(current->name, "sasl")) { + if (sasl && !(current->enabled)) + add_req(current->name); + } else if (!strcmp(current->name, "account-notify")) { + if ((account_notify) && (!current->enabled)) + add_req(current->name); + } else if (!strcmp(current->name, "account-tag")) { + if ((account_tag) && (!current->enabled)) + add_req(current->name); + } else if (!strcmp(current->name, "extended-join")) { + if ((extended_join) && (!current->enabled)) + add_req(current->name); + } else if (!strcmp(current->name, "invite-notify")) { + if ((invite_notify) && (!current->enabled)) + add_req(current->name); + } else if (!strcmp(current->name, "message-tags")) { + if ((message_tags) && (!current->enabled)) + add_req(current->name); } /* Add any custom capes the user listed */ strlcpy(cape, cap_request, sizeof cape); @@ -1888,7 +1635,6 @@ static int gotcap(char *from, char *msg) { splitstr = strtok(NULL, " "); } } else if (!strcmp(cmd, "ACK")) { - buf[0] = 0; splitstr = strtok(msg, " "); while (splitstr != NULL) { current = cap; @@ -1903,35 +1649,9 @@ static int gotcap(char *from, char *msg) { current->enabled = 0; } else { current->enabled = 1; - } - - if ((sasl) && (!strcasecmp(current->name, "sasl")) && (current->enabled)) { - putlog(LOG_DEBUG, "*", "SASL: Starting authentication process"); - if (current->value && !checkvalue(current->value, SASL_MECHANISMS[sasl_mechanism])) { - snprintf(buf, sizeof buf, - "%s authentication method not supported", - SASL_MECHANISMS[sasl_mechanism]); - return sasl_error(buf); - } -#ifndef HAVE_EVP_PKEY_GET1_EC_KEY - if (sasl_mechanism != SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE) { -#endif - putlog(LOG_DEBUG, "*", "SASL: AUTHENTICATE %s", - SASL_MECHANISMS[sasl_mechanism]); - dprintf(DP_MODE, "AUTHENTICATE %s\n", SASL_MECHANISMS[sasl_mechanism]); - sasl_timeout_time = sasl_timeout; -#ifndef HAVE_EVP_PKEY_GET1_EC_KEY - } else { -#ifdef TLS - return sasl_error("TLS libs missing EC support, try PLAIN or EXTERNAL method, aborting authentication"); - } -#else /* TLS */ - if (sasl_mechanism != SASL_MECHANISM_PLAIN) { - return sasl_error("TLS libs not present, try PLAIN method, aborting authentication"); - } - } -#endif /* TLS */ -#endif /* HAVE_EVP_PKEY */ + if (sasl && !strcasecmp(current->name, "sasl") && + sasl_authenticate_initial(current->value)) + return 1; } } current = current->next; @@ -1945,6 +1665,7 @@ static int gotcap(char *from, char *msg) { dprintf(DP_MODE, "CAP END\n"); } current = cap; + buf[0] = 0; while (current != NULL) { if (current->enabled) { written += snprintf(buf + written, sizeof buf - written, " %s", current->name); @@ -2169,21 +1890,12 @@ static cmd_t my_raw_binds[] = { {"733", "", (IntFunc) got733, NULL}, {"734", "", (IntFunc) got734, NULL}, {"900", "", (IntFunc) got900, NULL}, - {"901", "", (IntFunc) got901, NULL}, - {"902", "", (IntFunc) gotsasl90X, NULL}, - {"903", "", (IntFunc) got903, NULL}, - {"904", "", (IntFunc) gotsasl90X, NULL}, - {"905", "", (IntFunc) gotsasl90X, NULL}, - {"906", "", (IntFunc) gotsasl90X, NULL}, - {"907", "", (IntFunc) got907, NULL}, - {"908", "", (IntFunc) got908, NULL}, {"NICK", "", (IntFunc) gotnick, NULL}, {"ERROR", "", (IntFunc) goterror, NULL}, /* ircu2.10.10 has a bug when a client is throttled ERROR is sent wrong */ {"ERROR:", "", (IntFunc) goterror, NULL}, {"KICK", "", (IntFunc) gotkick, NULL}, {"CAP", "", (IntFunc) gotcap, NULL}, - {"AUTHENTICATE", "", (IntFunc) gotauthenticate, NULL}, {"SETNAME", "", (IntFunc) gotsetname, NULL}, {NULL, NULL, NULL, NULL} }; From 63e8e7c143f2ff7e0a09d64b5f59fd7a31f9cdf8 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Tue, 29 Oct 2024 04:33:00 +0100 Subject: [PATCH 02/13] Add debug log for pbkdf2 rusage --- src/mod/server.mod/sasl.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c index 9660999f0..4569a8a33 100644 --- a/src/mod/server.mod/sasl.c +++ b/src/mod/server.mod/sasl.c @@ -282,7 +282,7 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, char server_first_message[1024]; char *word, *brkb, *server_nonce = 0, *salt_b64 = 0, *i = 0; char error_msg[128]; /* snprintf() truncation should be tolerable */ - int salt_plain_len, iter, j; + int salt_plain_len, iter, j, ret; char salt_plain[64]; /* atheme: Valid values are 8 to 64 (inclusive) */ char client_key[EVP_MAX_MD_SIZE]; unsigned int client_key_len, stored_key_len; @@ -291,6 +291,7 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, unsigned char client_signature[EVP_MAX_MD_SIZE]; unsigned char client_proof[EVP_MAX_MD_SIZE]; char client_proof_b64[1024]; + struct rusage ru1, ru2; strlcpy(server_first_message, server_msg_plain, sizeof server_first_message); for (word = strtok_r(server_msg_plain, ",", &brkb); @@ -364,7 +365,8 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, else digest = EVP_sha512(); digest_len = EVP_MD_size(digest); - /* TODO: print time spent for pbkdf2 func */ + + ret = getrusage(RUSAGE_SELF, &ru1); if (!PKCS5_PBKDF2_HMAC(sasl_password, strlen(sasl_password), (const unsigned char *) salt_plain, salt_plain_len, iter, digest, digest_len, @@ -375,6 +377,17 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, sasl_error(error_msg); return -1; } + if (!ret && !getrusage(RUSAGE_SELF, &ru2)) { + debug4("SASL: pbkdf2 digest %s iter %i, user %.3fms sys %.3fms", EVP_MD_name(digest), + iter, + (double) (ru2.ru_utime.tv_usec - ru1.ru_utime.tv_usec) / 1000 + + (double) (ru2.ru_utime.tv_sec - ru1.ru_utime.tv_sec ) * 1000, + (double) (ru2.ru_stime.tv_usec - ru1.ru_stime.tv_usec) / 1000 + + (double) (ru2.ru_stime.tv_sec - ru1.ru_stime.tv_sec ) * 1000); + } + else { + debug1("PBKDF2 error: getrusage(): %s", strerror(errno)); + } printf("DEBUG: salted_password ready\n"); @@ -533,8 +546,6 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, * we could also enable/disable all sasl raw bindings to minimize attack * surface * in the end, fuzzing would be nice, coze we do a lot of parsing here - * server_iter and rusage should be displayed for the function calling - * pbkdf2(server_iter) * cache the client_key (assuming the Salt and hash iteration-count is stable) * support authenticate split by 400 byte, like: * https://github.com/ircv3/ircv3-specifications/commit/838ef397385065bbc5c29d934bbb407e5b5a5ce5 From bfc1df563618c29bc8e316112f52d448a99cfe63 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Mon, 2 Dec 2024 04:48:06 +0100 Subject: [PATCH 03/13] Remove printf debug --- src/mod/server.mod/sasl.c | 44 +++------------------------------------ 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c index 4569a8a33..64fe51b63 100644 --- a/src/mod/server.mod/sasl.c +++ b/src/mod/server.mod/sasl.c @@ -267,7 +267,7 @@ static int sasl_ecdsa_nist256p_challange_step_1( #if OPENSSL_VERSION_NUMBER >= 0x10000000L /* 1.0.0 */ static int sasl_scram_step_0(char *client_msg_plain, int client_msg_plain_len) { - /* TODO: after sasl scram merged make_rand_str_from_chars() should be made + /* TODO: after merge of #1706 make_rand_str_from_chars() should be made * return unbiased uniformed randoms */ make_rand_str_from_chars(nonce, (sizeof nonce) - 1, CHARSET_SCRAM); @@ -340,7 +340,8 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, /* TODO: normalize(password) * Eggdrop doesnt have support for utf8 normalization yet * tcl also doesnt have it in core yet, only in tcllib - * We could use glib or something + * We could use glib or something, but we dont want dependency bloat just + * for one function */ if ((salt_plain_len = b64_pton(salt_b64, (unsigned char*) salt_plain, sizeof salt_plain)) == -1) { @@ -355,11 +356,6 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, return -1; } - printf("DEBUG: server_nonce: >>>%s<<<\n", server_nonce); - printf("DEBUG: salt_b64: >>>%s<<<\n", salt_b64); - printf("DEBUG: iter: %i\n", iter); - printf("DEBUG: salt_plain_len: %i\n", salt_plain_len); - if (sasl_mechanism == SASL_MECHANISM_SCRAM_SHA_256) digest = EVP_sha256(); else @@ -389,8 +385,6 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, debug1("PBKDF2 error: getrusage(): %s", strerror(errno)); } - printf("DEBUG: salted_password ready\n"); - /* ClientKey := HMAC(SaltedPassword, "Client Key") */ if (!HMAC(digest, salted_password, digest_len, (unsigned char *) CLIENT_KEY, @@ -402,8 +396,6 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, return -1; } - printf("DEBUG: client_key ready\n"); - /* StoredKey := H(ClientKey) */ if (!EVP_Digest(client_key, client_key_len, stored_key, &stored_key_len, digest, NULL)) { @@ -414,8 +406,6 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, return -1; } - printf("DEBUG: stored_key ready\n"); - /* AuthMessage := client-first-message-bare + "," + * server-first-message + "," + * client-final-message-without-proof @@ -425,18 +415,12 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, sizeof client_final_message_without_proof, "c=biws,r=%s", server_nonce); - printf("DEBUG: client_final_message_without_proof = >>>%s<<<\n", client_final_message_without_proof); - auth_message_len = snprintf(auth_message, sizeof auth_message, "%s,%s,%s", client_first_message + 3, server_first_message, client_final_message_without_proof); - printf("DEBUG: auth_message ready: >>>%s<<<\n", auth_message); - /* ClientSignature := HMAC(StoredKey, AuthMessage) */ - printf("DEBUG: digestlen: %i auth_message_len: %i\n", digest_len, auth_message_len); - if (!HMAC(digest, stored_key, digest_len, (unsigned char *) auth_message, auth_message_len, client_signature, NULL)) { snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", @@ -445,26 +429,16 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, return -1; } - printf("DEBUG: client_signature ready\n"); - /* ClientProof := ClientKey XOR ClientSignature */ - printf("DEBUG: client_key_len: %i\n", client_key_len); - for (j = 0; j < client_key_len; j++) client_proof[j] = client_key[j] ^ client_signature[j]; - printf("DEBUG: client_proof ready\n"); - if (b64_ntop(client_proof, client_key_len, client_proof_b64, sizeof client_proof_b64) == -1) { sasl_error("AUTHENTICATE error: could not base64 encode"); return -1; } - printf("DEBUG: base64-encoded client_proof ready\n"); - - printf("DEBUG: client_final_message_without_proof = >>>%s<<<\n", client_final_message_without_proof); - return snprintf(client_msg_plain, client_msg_plain_len, "%s,p=%s", client_final_message_without_proof, client_proof_b64); } @@ -491,12 +465,8 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, return; } - printf("DEBUG: server_key ready\n"); - /* ServerSignature := HMAC(ServerKey, AuthMessage) */ - printf("DEBUG: digestlen: %i auth_message_len: %i\n", digest_len, auth_message_len); - if (!HMAC(digest, server_key, digest_len, (unsigned char *) auth_message, auth_message_len, server_signature, NULL)) { snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", @@ -505,19 +475,11 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, return; } - printf("DEBUG: server_signature ready\n"); - if ((server_signature_b64_len = b64_ntop(server_signature, digest_len, server_signature_b64, sizeof server_signature_b64)) == -1) { sasl_error("AUTHENTICATE error: could not base64 encode"); return; } - printf("DEBUG: base64-encoded server_signature ready\n"); - - printf("DEBUG: server_signature_b64 = >>>%s<<<\n", server_signature_b64); - - printf("DEBUG: server_signature_b64_len = %i\n", server_signature_b64_len); - if ( #if OPENSSL_VERSION_NUMBER >= 0x1010008fL /* 1.1.0h */ CRYPTO_memcmp From 28a82cd34462ffd5db69e212d188942d91434b93 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Thu, 5 Dec 2024 08:28:03 +0100 Subject: [PATCH 04/13] Cleanup --- src/mod/server.mod/sasl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c index 64fe51b63..06e440ff1 100644 --- a/src/mod/server.mod/sasl.c +++ b/src/mod/server.mod/sasl.c @@ -501,7 +501,6 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, /* TODO: * modularize * aim is final version <= 70 lines - * state machine, at least for scram * guard sasl auth with timeout * sasl-password should be sasl-password-file so we read the pass from file * and keep it only in memory while we need it, From 283f2e524a1b52679a4b4698fd0bba4e4ae38f92 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Fri, 6 Dec 2024 17:43:02 +0100 Subject: [PATCH 05/13] Dont use stpcpy() for it is POSIX 2008 --- src/mod/server.mod/sasl.c | 45 ++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c index 06e440ff1..e8116a238 100644 --- a/src/mod/server.mod/sasl.c +++ b/src/mod/server.mod/sasl.c @@ -144,24 +144,39 @@ static int got908(char *from, char *msg) return 0; } -static int sasl_plain(char *client_msg_plain) +static int sasl_plain(char *dst, size_t dstsize) { - /* Don't use snprintf() due to \0 inside */ - char *s = client_msg_plain; - s = stpcpy(s, sasl_username) + 1; - s = stpcpy(s, sasl_username) + 1; - s = stpcpy(s, sasl_password); - return s - client_msg_plain; + /* Don't use snprintf() due to \0 inside + * and don't use stpcpy() for it is POSIX 2008 + */ + size_t n, y = 0; + + n = strlcpy(dst, sasl_username, dstsize); + dst = dst + n + 1; + dstsize = dstsize - n - 1; + y = n + 1; + n = strlcpy(dst, sasl_username, dstsize); + dst = dst + n + 1; + dstsize = dstsize - n - 1; + y = y + n + 1; + n = strlcpy(dst, sasl_password, dstsize); + return y + n; } #ifdef TLS -static int sasl_ecdsa_nist256p_challange_step_0(char *client_msg_plain) +static int sasl_ecdsa_nist256p_challange_step_0(char *dst, size_t dstsize) { - /* Don't use snprintf() due to \0 inside */ - char *s = client_msg_plain; - s = stpcpy(s, sasl_username) + 1; - s = stpcpy(s, sasl_username); - return s - client_msg_plain; + /* Don't use snprintf() due to \0 inside + * and don't use stpcpy() for it is POSIX 2008 + */ + size_t n, y = 0; + + n = strlcpy(dst, sasl_username, dstsize); + dst = dst + n + 1; + dstsize = dstsize - n - 1; + y = n + 1; + n = strlcpy(dst, sasl_username, dstsize); + return y + n; } static int sasl_ecdsa_nist256p_challange_step_1( @@ -545,11 +560,11 @@ static int gotauthenticate(char *from, char *msg) switch (sasl_mechanism) { case SASL_MECHANISM_PLAIN: #endif - client_msg_plain_len = sasl_plain(client_msg_plain); + client_msg_plain_len = sasl_plain(client_msg_plain, sizeof client_msg_plain); #ifdef TLS break; case SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE: - client_msg_plain_len = sasl_ecdsa_nist256p_challange_step_0(client_msg_plain); + client_msg_plain_len = sasl_ecdsa_nist256p_challange_step_0(client_msg_plain, sizeof client_msg_plain); break; case SASL_MECHANISM_EXTERNAL: putlog(LOG_DEBUG, "*", "SASL: put AUTHENTICATE Response +"); From dd3caa45e9f085be1569337d7fa1bec62ca12d23 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Fri, 6 Dec 2024 19:58:09 +0100 Subject: [PATCH 06/13] Cleanup and remove redundant debug log --- src/mod/server.mod/sasl.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c index e8116a238..ab884871d 100644 --- a/src/mod/server.mod/sasl.c +++ b/src/mod/server.mod/sasl.c @@ -286,8 +286,10 @@ static int sasl_scram_step_0(char *client_msg_plain, int client_msg_plain_len) * return unbiased uniformed randoms */ make_rand_str_from_chars(nonce, (sizeof nonce) - 1, CHARSET_SCRAM); - return snprintf(client_msg_plain, client_msg_plain_len, "n,,n=%s,r=%s", - sasl_username, nonce); + snprintf(client_msg_plain, client_msg_plain_len, "n,,n=%s,r=%s", + sasl_username, nonce); + return strlcpy(client_first_message, client_msg_plain, + sizeof client_first_message); } static int sasl_scram_step_1(char *restrict client_msg_plain, @@ -514,8 +516,6 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, #endif /* TLS */ /* TODO: - * modularize - * aim is final version <= 70 lines * guard sasl auth with timeout * sasl-password should be sasl-password-file so we read the pass from file * and keep it only in memory while we need it, @@ -545,8 +545,6 @@ static int gotauthenticate(char *from, char *msg) #endif char client_msg_b64[((MAX((sizeof client_msg_plain), 400) + 2) / 3) << 2] = ""; - - putlog(LOG_DEBUG, "*", "SASL: got AUTHENTICATE %s", msg); fixcolon(msg); /* Because Inspircd does its own thing */ #ifdef TLS if (*msg == '+') { @@ -567,15 +565,12 @@ static int gotauthenticate(char *from, char *msg) client_msg_plain_len = sasl_ecdsa_nist256p_challange_step_0(client_msg_plain, sizeof client_msg_plain); break; case SASL_MECHANISM_EXTERNAL: - putlog(LOG_DEBUG, "*", "SASL: put AUTHENTICATE Response +"); dprintf(DP_MODE, "AUTHENTICATE +\n"); return 0; #if OPENSSL_VERSION_NUMBER >= 0x10000000L /* 1.0.0 */ case SASL_MECHANISM_SCRAM_SHA_256: case SASL_MECHANISM_SCRAM_SHA_512: client_msg_plain_len = sasl_scram_step_0(client_msg_plain, sizeof client_msg_plain); - strlcpy(client_first_message, client_msg_plain, - sizeof client_first_message); /* TODO: do this here or in sasl_scram_step_0() ? */ #endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ } } else { @@ -613,7 +608,6 @@ static int gotauthenticate(char *from, char *msg) sasl_error("AUTHENTICATE: could not base64 encode"); return 0; } - putlog(LOG_DEBUG, "*", "SASL: put AUTHENTICATE Response %s", client_msg_b64); dprintf(DP_MODE, "AUTHENTICATE %s\n", client_msg_b64); return 0; } From 932cddcd93b6f780c43a151a1158b398f7b7f263 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Mon, 16 Dec 2024 22:58:36 +0100 Subject: [PATCH 07/13] sasl-timeout works fine --- src/mod/server.mod/sasl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c index ab884871d..a854dc7b7 100644 --- a/src/mod/server.mod/sasl.c +++ b/src/mod/server.mod/sasl.c @@ -516,7 +516,6 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, #endif /* TLS */ /* TODO: - * guard sasl auth with timeout * sasl-password should be sasl-password-file so we read the pass from file * and keep it only in memory while we need it, * we could also enable/disable all sasl raw bindings to minimize attack From a0aa82628961621b76e8b8ea8dde60f178550293 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Wed, 18 Dec 2024 03:36:27 +0100 Subject: [PATCH 08/13] Fix state machine for scram --- src/mod/server.mod/sasl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c index a854dc7b7..966260cf3 100644 --- a/src/mod/server.mod/sasl.c +++ b/src/mod/server.mod/sasl.c @@ -598,6 +598,7 @@ static int gotauthenticate(char *from, char *msg) step++; } else { sasl_scram_step_2(client_msg_plain, sizeof client_msg_plain, server_msg_plain); + step = 0; return 0; } #endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ From 117e9435e6a0899751ec6c2e0b1d5056fe6b08fc Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Wed, 18 Dec 2024 03:59:31 +0100 Subject: [PATCH 09/13] Cache client server key --- src/mod/server.mod/sasl.c | 127 +++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 56 deletions(-) diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c index 966260cf3..226161834 100644 --- a/src/mod/server.mod/sasl.c +++ b/src/mod/server.mod/sasl.c @@ -66,6 +66,15 @@ char nonce[21]; /* atheme defines acceptable client nonce len min 8 max 512 char char client_first_message[1024]; int digest_len, auth_message_len; char auth_message[3069]; +/* a client implementation MAY cache ClientKey&ServerKey */ +char last_sasl_password[sizeof sasl_password]; +char last_salt_b64[96] = ""; +char last_i[32] = ""; +char client_key[EVP_MAX_MD_SIZE]; +unsigned int client_key_len; +int use_cache; +char server_key[EVP_MAX_MD_SIZE]; +unsigned int server_key_len; #endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ #endif /* TLS */ @@ -301,8 +310,7 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, char error_msg[128]; /* snprintf() truncation should be tolerable */ int salt_plain_len, iter, j, ret; char salt_plain[64]; /* atheme: Valid values are 8 to 64 (inclusive) */ - char client_key[EVP_MAX_MD_SIZE]; - unsigned int client_key_len, stored_key_len; + unsigned int stored_key_len; unsigned char stored_key[EVP_MAX_MD_SIZE]; char client_final_message_without_proof[1024]; unsigned char client_signature[EVP_MAX_MD_SIZE]; @@ -361,57 +369,66 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, * for one function */ - if ((salt_plain_len = b64_pton(salt_b64, (unsigned char*) salt_plain, sizeof salt_plain)) == -1) { - sasl_error("AUTHENTICATE error: could not base64 decode salt"); - return -1; - } - errno = 0; - iter = strtol(i, NULL, 10); - if (errno) { - snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: strtol(%s): %s", i, strerror(errno)); - sasl_error(error_msg); - return -1; - } + /* ClientKey := HMAC(SaltedPassword, "Client Key") */ - if (sasl_mechanism == SASL_MECHANISM_SCRAM_SHA_256) - digest = EVP_sha256(); - else - digest = EVP_sha512(); - digest_len = EVP_MD_size(digest); - - ret = getrusage(RUSAGE_SELF, &ru1); - if (!PKCS5_PBKDF2_HMAC(sasl_password, strlen(sasl_password), - (const unsigned char *) salt_plain, salt_plain_len, - iter, digest, digest_len, - (unsigned char *) salted_password)) { - snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: " - "PKCS5_PBKDF2_HMAC(): %s", ERR_error_string(ERR_get_error(), - NULL)); - sasl_error(error_msg); - return -1; - } - if (!ret && !getrusage(RUSAGE_SELF, &ru2)) { - debug4("SASL: pbkdf2 digest %s iter %i, user %.3fms sys %.3fms", EVP_MD_name(digest), - iter, - (double) (ru2.ru_utime.tv_usec - ru1.ru_utime.tv_usec) / 1000 + - (double) (ru2.ru_utime.tv_sec - ru1.ru_utime.tv_sec ) * 1000, - (double) (ru2.ru_stime.tv_usec - ru1.ru_stime.tv_usec) / 1000 + - (double) (ru2.ru_stime.tv_sec - ru1.ru_stime.tv_sec ) * 1000); - } - else { - debug1("PBKDF2 error: getrusage(): %s", strerror(errno)); - } + use_cache = (!strcmp(sasl_password, last_sasl_password)) && (!strcmp(salt_b64, last_salt_b64)) && (!strcmp(i, last_i)); - /* ClientKey := HMAC(SaltedPassword, "Client Key") */ + if (!use_cache) { + if ((salt_plain_len = b64_pton(salt_b64, (unsigned char*) salt_plain, sizeof salt_plain)) == -1) { + sasl_error("AUTHENTICATE error: could not base64 decode salt"); + return -1; + } + errno = 0; + iter = strtol(i, NULL, 10); + if (errno) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: strtol(%s): %s", i, strerror(errno)); + sasl_error(error_msg); + return -1; + } - if (!HMAC(digest, salted_password, digest_len, (unsigned char *) CLIENT_KEY, - strlen(CLIENT_KEY), (unsigned char *) client_key, - &client_key_len)) { - snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", - ERR_error_string(ERR_get_error(), NULL)); - sasl_error(error_msg); - return -1; + if (sasl_mechanism == SASL_MECHANISM_SCRAM_SHA_256) + digest = EVP_sha256(); + else + digest = EVP_sha512(); + digest_len = EVP_MD_size(digest); + + ret = getrusage(RUSAGE_SELF, &ru1); + if (!PKCS5_PBKDF2_HMAC(sasl_password, strlen(sasl_password), + (const unsigned char *) salt_plain, salt_plain_len, + iter, digest, digest_len, + (unsigned char *) salted_password)) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: " + "PKCS5_PBKDF2_HMAC(): %s", ERR_error_string(ERR_get_error(), + NULL)); + sasl_error(error_msg); + return -1; + } + if (!ret && !getrusage(RUSAGE_SELF, &ru2)) { + debug4("SASL: pbkdf2 digest %s iter %i, user %.3fms sys %.3fms", EVP_MD_name(digest), + iter, + (double) (ru2.ru_utime.tv_usec - ru1.ru_utime.tv_usec) / 1000 + + (double) (ru2.ru_utime.tv_sec - ru1.ru_utime.tv_sec ) * 1000, + (double) (ru2.ru_stime.tv_usec - ru1.ru_stime.tv_usec) / 1000 + + (double) (ru2.ru_stime.tv_sec - ru1.ru_stime.tv_sec ) * 1000); + } + else { + debug1("PBKDF2 error: getrusage(): %s", strerror(errno)); + } + + if (!HMAC(digest, salted_password, digest_len, (unsigned char *) CLIENT_KEY, + strlen(CLIENT_KEY), (unsigned char *) client_key, + &client_key_len)) { + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", + ERR_error_string(ERR_get_error(), NULL)); + sasl_error(error_msg); + return -1; + } + strlcpy(last_sasl_password, sasl_password, sizeof last_sasl_password); + strlcpy(last_salt_b64, salt_b64, sizeof last_salt_b64); + strlcpy(last_i, i, sizeof last_i); } + else + debug0("SASL: using cached client and server key"); /* StoredKey := H(ClientKey) */ @@ -464,8 +481,6 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, int client_msg_plain_len, char *restrict server_msg_plain) { - char server_key[EVP_MAX_MD_SIZE]; - unsigned int server_key_len; char error_msg[128]; /* snprintf() truncation should be tolerable */ unsigned char server_signature[EVP_MAX_MD_SIZE]; char server_signature_b64[128]; @@ -473,9 +488,10 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, /* ServerKey := HMAC(SaltedPassword, "Server Key") */ - if (!HMAC(digest, salted_password, digest_len, (unsigned char *) SERVER_KEY, - strlen(SERVER_KEY), (unsigned char *) server_key, - &server_key_len)) { + if ((!use_cache) && + (!HMAC(digest, salted_password, digest_len, (unsigned char *) SERVER_KEY, + strlen(SERVER_KEY), (unsigned char *) server_key, + &server_key_len))) { snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", ERR_error_string(ERR_get_error(), NULL)); sasl_error(error_msg); @@ -521,7 +537,6 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, * we could also enable/disable all sasl raw bindings to minimize attack * surface * in the end, fuzzing would be nice, coze we do a lot of parsing here - * cache the client_key (assuming the Salt and hash iteration-count is stable) * support authenticate split by 400 byte, like: * https://github.com/ircv3/ircv3-specifications/commit/838ef397385065bbc5c29d934bbb407e5b5a5ce5 * 400-byte chunk, see: https://ircv3.net/specs/extensions/sasl-3.1.html @@ -598,7 +613,7 @@ static int gotauthenticate(char *from, char *msg) step++; } else { sasl_scram_step_2(client_msg_plain, sizeof client_msg_plain, server_msg_plain); - step = 0; + step = 0; return 0; } #endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ From 451c0c620f723686e0d41a5cc25244971fa3fc36 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Wed, 18 Dec 2024 04:02:48 +0100 Subject: [PATCH 10/13] Cleanup --- src/mod/server.mod/sasl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c index 226161834..571b0ddfd 100644 --- a/src/mod/server.mod/sasl.c +++ b/src/mod/server.mod/sasl.c @@ -613,7 +613,7 @@ static int gotauthenticate(char *from, char *msg) step++; } else { sasl_scram_step_2(client_msg_plain, sizeof client_msg_plain, server_msg_plain); - step = 0; + step = 0; return 0; } #endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ From 70a334e61daca659920621448f2d4723e8b346b7 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Thu, 19 Dec 2024 04:14:55 +0100 Subject: [PATCH 11/13] EXTERNAL does not need username, so do not check for it EXTERNAL not possible via non-ssl connection, so do not try but log Enhance error logging --- src/mod/server.mod/sasl.c | 46 ++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c index 571b0ddfd..175dc2117 100644 --- a/src/mod/server.mod/sasl.c +++ b/src/mod/server.mod/sasl.c @@ -80,7 +80,7 @@ unsigned int server_key_len; static void sasl_error(const char *msg) { - putlog(LOG_SERV, "*", "SASL: %s", msg); + putlog(LOG_SERV, "*", "SASL: error: %s", msg); dprintf(DP_MODE, "CAP END\n"); sasl_timeout_time = 0; if (!sasl_continue) { @@ -197,7 +197,7 @@ static int sasl_ecdsa_nist256p_challange_step_1( EVP_PKEY *pkey; if (!(fp = fopen(sasl_ecdsa_key, "r"))) { - snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: could not open " + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: could not open " "file sasl_ecdsa_key %s: %s\n", sasl_ecdsa_key, strerror(errno)); sasl_error(error_msg); return -1; @@ -331,7 +331,7 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, memcmp #endif (word + 2, nonce, (sizeof nonce) - 1)) { - sasl_error("AUTHENTICATE error: server nonce != client nonce"); + sasl_error("AUTHENTICATE: server nonce != client nonce"); return -1; } server_nonce = word + 2; @@ -343,7 +343,7 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, i = word + 2; break; case 'e': - snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: server error: %s", word + 2); + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: server error: %s", word + 2); sasl_error(error_msg); return -1; default: @@ -351,15 +351,15 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, } } if (!server_nonce) { - sasl_error("AUTHENTICATE error: server nonce missing from SCRAM challenge"); + sasl_error("AUTHENTICATE: server nonce missing from SCRAM challenge"); return -1; } if (!salt_b64) { - sasl_error("AUTHENTICATE error: salt missing from SCRAM challenge"); + sasl_error("AUTHENTICATE: salt missing from SCRAM challenge"); return -1; } if (!i) { - sasl_error("AUTHENTICATE error: iteration count missing from SCRAM challenge"); + sasl_error("AUTHENTICATE: iteration count missing from SCRAM challenge"); return -1; } /* TODO: normalize(password) @@ -375,13 +375,13 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, if (!use_cache) { if ((salt_plain_len = b64_pton(salt_b64, (unsigned char*) salt_plain, sizeof salt_plain)) == -1) { - sasl_error("AUTHENTICATE error: could not base64 decode salt"); + sasl_error("AUTHENTICATE: could not base64 decode salt"); return -1; } errno = 0; iter = strtol(i, NULL, 10); if (errno) { - snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: strtol(%s): %s", i, strerror(errno)); + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: strtol(%s): %s", i, strerror(errno)); sasl_error(error_msg); return -1; } @@ -397,7 +397,7 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, (const unsigned char *) salt_plain, salt_plain_len, iter, digest, digest_len, (unsigned char *) salted_password)) { - snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: " + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: " "PKCS5_PBKDF2_HMAC(): %s", ERR_error_string(ERR_get_error(), NULL)); sasl_error(error_msg); @@ -418,7 +418,7 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, if (!HMAC(digest, salted_password, digest_len, (unsigned char *) CLIENT_KEY, strlen(CLIENT_KEY), (unsigned char *) client_key, &client_key_len)) { - snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: HMAC(): %s", ERR_error_string(ERR_get_error(), NULL)); sasl_error(error_msg); return -1; @@ -434,7 +434,7 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, if (!EVP_Digest(client_key, client_key_len, stored_key, &stored_key_len, digest, NULL)) { snprintf(error_msg, sizeof error_msg, - "AUTHENTICATE error: EVP_Digest(): %s", + "AUTHENTICATE: EVP_Digest(): %s", ERR_error_string(ERR_get_error(), NULL)); sasl_error(error_msg); return -1; @@ -457,7 +457,7 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, if (!HMAC(digest, stored_key, digest_len, (unsigned char *) auth_message, auth_message_len, client_signature, NULL)) { - snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: HMAC(): %s", ERR_error_string(ERR_get_error(), NULL)); sasl_error(error_msg); return -1; @@ -469,7 +469,7 @@ static int sasl_scram_step_1(char *restrict client_msg_plain, client_proof[j] = client_key[j] ^ client_signature[j]; if (b64_ntop(client_proof, client_key_len, client_proof_b64, sizeof client_proof_b64) == -1) { - sasl_error("AUTHENTICATE error: could not base64 encode"); + sasl_error("AUTHENTICATE: could not base64 encode"); return -1; } @@ -492,7 +492,7 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, (!HMAC(digest, salted_password, digest_len, (unsigned char *) SERVER_KEY, strlen(SERVER_KEY), (unsigned char *) server_key, &server_key_len))) { - snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: HMAC(): %s", ERR_error_string(ERR_get_error(), NULL)); sasl_error(error_msg); return; @@ -502,14 +502,14 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, if (!HMAC(digest, server_key, digest_len, (unsigned char *) auth_message, auth_message_len, server_signature, NULL)) { - snprintf(error_msg, sizeof error_msg, "AUTHENTICATE error: HMAC(): %s", + snprintf(error_msg, sizeof error_msg, "AUTHENTICATE: HMAC(): %s", ERR_error_string(ERR_get_error(), NULL)); sasl_error(error_msg); return; } if ((server_signature_b64_len = b64_ntop(server_signature, digest_len, server_signature_b64, sizeof server_signature_b64)) == -1) { - sasl_error("AUTHENTICATE error: could not base64 encode"); + sasl_error("AUTHENTICATE: could not base64 encode"); return; } @@ -532,6 +532,7 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, #endif /* TLS */ /* TODO: + * * sasl-password should be sasl-password-file so we read the pass from file * and keep it only in memory while we need it, * we could also enable/disable all sasl raw bindings to minimize attack @@ -563,7 +564,7 @@ static int gotauthenticate(char *from, char *msg) #ifdef TLS if (*msg == '+') { #endif - if (!*sasl_username) { /* TODO: mind. fuer EXTERNAL muessen wir das nicht machen */ + if ((sasl_mechanism != SASL_MECHANISM_EXTERNAL) && (!*sasl_username)) { putlog(LOG_SERV, "*", "SASL: sasl-username not set, setting it to " "username %s", botname); strlcpy(sasl_username, botuser, sizeof sasl_username); @@ -705,14 +706,15 @@ static void sasl_start() * later messages. The initial client message specifies the SASL mechanism to * be used. */ -/* TODO: aktuell versucht eggdrop EXTERNAL ueber non-ssl verbindung, das kann - * doch nicht funktionieren, oder? also sollte eggdrop da eine warnung loggen - * und es gar nicht erst versuchen. - */ int sasl_authenticate_initial(const struct cap_values *cap_value_list) { char error_msg[128]; putlog(LOG_DEBUG, "*", "SASL: Starting authentication process"); + int servidx = findanyidx(serv); + if ((sasl_mechanism == SASL_MECHANISM_EXTERNAL) && !dcc[servidx].ssl) { + sasl_error("authentication mechanism EXTERNAL not possible via non-ssl connection"); + return 1; + } if (!is_cap_value(cap_value_list, SASL_MECHANISMS[sasl_mechanism])) { snprintf(error_msg, sizeof error_msg, "authentication mechanism %s not supported by server", From c1b4d8077408a5bf7f4ac4848051d8ee7b71ee2f Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Thu, 19 Dec 2024 07:47:50 +0100 Subject: [PATCH 12/13] Move TODOs for later to #832 --- src/mod/server.mod/sasl.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c index 175dc2117..df934f1b0 100644 --- a/src/mod/server.mod/sasl.c +++ b/src/mod/server.mod/sasl.c @@ -531,21 +531,6 @@ static void sasl_scram_step_2(char *restrict client_msg_plain, #endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */ #endif /* TLS */ -/* TODO: - * - * sasl-password should be sasl-password-file so we read the pass from file - * and keep it only in memory while we need it, - * we could also enable/disable all sasl raw bindings to minimize attack - * surface - * in the end, fuzzing would be nice, coze we do a lot of parsing here - * support authenticate split by 400 byte, like: - * https://github.com/ircv3/ircv3-specifications/commit/838ef397385065bbc5c29d934bbb407e5b5a5ce5 - * 400-byte chunk, see: https://ircv3.net/specs/extensions/sasl-3.1.html - * base64 padding - * The response is encoded in Base64 (RFC 4648), then split to - * 400-byte chunks, and each chunk is sent as a separate AUTHENTICATE - * command. - */ static int gotauthenticate(char *from, char *msg) { char client_msg_plain[1024]; From c7db41e4c52c7e8ace86eca0e349bfc37875b761 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Thu, 19 Dec 2024 07:54:14 +0100 Subject: [PATCH 13/13] Unbreak --disable-tls yet again --- src/mod/server.mod/sasl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod/server.mod/sasl.c b/src/mod/server.mod/sasl.c index df934f1b0..234b93116 100644 --- a/src/mod/server.mod/sasl.c +++ b/src/mod/server.mod/sasl.c @@ -695,11 +695,13 @@ int sasl_authenticate_initial(const struct cap_values *cap_value_list) { char error_msg[128]; putlog(LOG_DEBUG, "*", "SASL: Starting authentication process"); +#ifdef TLS int servidx = findanyidx(serv); if ((sasl_mechanism == SASL_MECHANISM_EXTERNAL) && !dcc[servidx].ssl) { sasl_error("authentication mechanism EXTERNAL not possible via non-ssl connection"); return 1; } +#endif if (!is_cap_value(cap_value_list, SASL_MECHANISMS[sasl_mechanism])) { snprintf(error_msg, sizeof error_msg, "authentication mechanism %s not supported by server",