diff --git a/doc/sphinx_source/tutorials/firststeps.rst b/doc/sphinx_source/tutorials/firststeps.rst index 368d8605e..17eb4f9c2 100644 --- a/doc/sphinx_source/tutorials/firststeps.rst +++ b/doc/sphinx_source/tutorials/firststeps.rst @@ -16,9 +16,9 @@ You can either telnet to the bot, or connect to the bot using DCC Chat. To telne You can find the IP and port the bot is listening on by a) remembering what you set in the config file ;) or b) reading the display the bot presented when it started up. Look for a line that looks similar to this:: - Listening for telnet connections on 2.4.6.9:3183 (all). + Listening for telnet connections on 192.0.2.1:3183 (all). -This tells you that the bot is listening on IP 2.4.6.9, port 3183. If you see 0.0.0.0 listed, that means Eggdrop is listening on all available IPs on that particular host. +This tells you that the bot is listening on IP 192.0.2.1, port 3183. If you see 0.0.0.0 listed, that means Eggdrop is listening on all available IPs on that particular host. If you choose not to telnet to connect to the partyline, you can either ``/dcc chat BotNick`` or ``/ctcp BotNick chat``. If one of those methods does not work for you, try the other. Once you're on the bot for the first time, type ``.help`` for a short list of available commands, or ``.help all`` for a more thorough list. @@ -150,15 +150,19 @@ Simple Authentication and Security Layer (SASL) is becoming a prevalant method o * **PLAIN**: To use this method, set sasl-mechanism to 0. This method passes the username and password (set in the sasl-username and sasl-password config file settings) to the IRC server in plaintext. If you only connect to the IRC server using a connection protected by SSL/TLS this is a generally safe method of authentication; however you probably want to avoid this method if you connect to a server on a non-protected port as the exchange itself is not encrypted. -* **ECDSA-NIST256P-CHALLENGE**: To use this method, set sasl-mechanism to 1. This method uses a public/private keypair to authenticate, so no username/password is required. Not all servers support this method. If your server does support this, you you must generate a certificate pair using:: +* **ECDSA-NIST256P-CHALLENGE**: To use this method, set sasl-mechanism to 1. This method uses a public/private keypair to authenticate, so no username/password is required. Not all servers support this method. If your server does support this, you must generate a certificate pair using:: openssl ecparam -genkey -name prime256v1 -out eggdrop-ecdsa.pem You will need to determine your public key fingerprint by using:: - openssl ec -noout -text -conv_form compressed -in eggdrop-ecdsa.pem | grep '^pub:' -A 3 | tail -n 3 | tr -d ' \n:' | xxd -r -p | base64 + openssl ec -noout -text -conv_form compressed -in eggdrop-ecdsa.pem 2>/dev/null | grep '^pub:' -A 3 | tail -n 3 | tr -d ' \n:' | xxd -r -p | base64 - Then, authenticate with your NickServ service and register your public certificate with NickServ. You can view your public key On Libera for example, it is done by:: + If error "xxd: command not found" you could install vim, because xxd is a part of vim, or you could try python:: + + openssl ec -noout -text -conv_form compressed -in eggdrop-ecdsa.pem 2>/dev/null| grep '^pub:' -A 3 | tail -n 3 | tr -d ' \n:' | python -c "import base64,sys;print(base64.b64encode(bytearray.fromhex(sys.stdin.readline())).decode())" + + Then, authenticate with your NickServ service and register your public certificate with NickServ. On Libera for example, it is done by:: /msg NickServ set pubkey @@ -166,10 +170,14 @@ Simple Authentication and Security Layer (SASL) is becoming a prevalant method o openssl req -new -x509 -nodes -keyout eggdrop.key -out eggdrop.crt -You will need to determine your public key fingerprint by using:: + You will need to determine your public key fingerprint by using:: openssl x509 -in eggdrop.crt -outform der | sha1sum -b | cut -d' ' -f1 -Then, ensure you have those keys loaded in the ssl-privatekey and ssl-certificate settings in the config file. Finally, to add this certificate to your NickServ account, type:: + Then, ensure you have those keys loaded in the ssl-privatekey and ssl-certificate settings in the config file. Finally, to add this certificate to your NickServ account, type:: /msg NickServ cert add + + Alternatively you could connect via ssl and if NickServ supports it, make it automatically determine and add your fingerprint in just the right format: + + /msg NickServ cert add 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..64ca828c7 --- /dev/null +++ b/src/mod/server.mod/sasl.c @@ -0,0 +1,701 @@ +/* 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 */ +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]; +const EVP_MD *digest; +char salted_password[EVP_MAX_MD_SIZE]; +int digest_len, auth_message_len; +char auth_message[3069]; + +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; +} + +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; + } + sig = nmalloc(ECDSA_size(eckey)); /* TODO: free()? */ + ret = ECDSA_sign(0, server_msg_plain, server_msg_plain_len, 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; +} + +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; +} + +/* 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, server_msg_plain_len; + #ifndef MAX + #define MAX(a,b) (((a)>(b))?(a):(b)) + #endif + char client_msg_b64[((MAX((sizeof client_msg_plain), 400) + 2) / 3) << 2] = ""; + char server_msg_plain[1024]; + char error_msg[1050]; /* snprintf() truncation should be tolerable */ + + putlog(LOG_DEBUG, "*", "SASL: got AUTHENTICATE %s", msg); + fixcolon(msg); /* Because Inspircd does its own thing */ + if (*msg == '+') { + 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); + } + switch (sasl_mechanism) { + case SASL_MECHANISM_PLAIN: + client_msg_plain_len = sasl_plain(client_msg_plain); + 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; + 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() ? */ + } + } 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; + } 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; + } + } + 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"; +#ifndef 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 */ +#ifndef HAVE_EVP_PKEY_GET1_EC_KEY + if (sasl_mechanism == SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE) + return "NIST256P functionality missing from your TLS libs, please choose a" + " different SASL method"; +#endif /* HAVE_EVP_PKEY_GET1_EC_KEY */ + 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..56a6d3282 100644 --- a/src/mod/server.mod/servmsg.c +++ b/src/mod/server.mod/servmsg.c @@ -20,10 +20,6 @@ * 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 @@ -47,6 +43,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 +1333,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 +1344,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 +1511,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 +1552,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 +1634,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 +1648,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 +1664,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 +1889,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} }; diff --git a/src/modules.c b/src/modules.c index 0fd005760..5c703a298 100644 --- a/src/modules.c +++ b/src/modules.c @@ -695,7 +695,6 @@ int module_register(char *name, Function *funcs, int major, int minor) const char *module_load(char *name) { - size_t len; module_entry *p; char *e; Function f; @@ -704,6 +703,7 @@ const char *module_load(char *name) #endif #ifndef STATIC + size_t len; char workbuf[PATH_MAX]; # ifdef MOD_USE_SHL shl_t hand;