diff options
Diffstat (limited to 'plugin/auth_ed25519/server_ed25519.c')
-rw-r--r-- | plugin/auth_ed25519/server_ed25519.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/plugin/auth_ed25519/server_ed25519.c b/plugin/auth_ed25519/server_ed25519.c new file mode 100644 index 00000000..b789bd34 --- /dev/null +++ b/plugin/auth_ed25519/server_ed25519.c @@ -0,0 +1,171 @@ +/* + Copyright (c) 2017, 2021, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <mysql/plugin_auth.h> +#include <mysqld_error.h> +#include "common.h" + +#if !defined(__attribute__) && !defined(__GNUC__) +#define __attribute__(A) +#endif + +#define PASSWORD_LEN_BUF 44 /* base64 of 32 bytes */ +#define PASSWORD_LEN 43 /* we won't store the last byte, padding '=' */ + +#define CRYPTO_LONGS (CRYPTO_BYTES/sizeof(long)) +#define NONCE_LONGS (NONCE_BYTES/sizeof(long)) + +/************************** SERVER *************************************/ + +static int loaded= 0; + +static int auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) +{ + int pkt_len; + unsigned long nonce[CRYPTO_LONGS + NONCE_LONGS]; + unsigned char *pkt, *reply= (unsigned char*)nonce; + + info->password_used= PASSWORD_USED_YES; + + /* prepare random nonce */ + if (my_random_bytes((unsigned char *)nonce, (int)sizeof(nonce))) + return CR_ERROR; // eh? OpenSSL error + + /* send it */ + if (vio->write_packet(vio, reply + CRYPTO_BYTES, NONCE_BYTES)) + return CR_AUTH_HANDSHAKE; + + /* read the signature */ + if ((pkt_len= vio->read_packet(vio, &pkt)) != CRYPTO_BYTES) + return CR_AUTH_HANDSHAKE; + memcpy(reply, pkt, CRYPTO_BYTES); + + if (crypto_sign_open(reply, CRYPTO_BYTES + NONCE_BYTES, + (unsigned char*)info->auth_string)) + return CR_AUTH_USER_CREDENTIALS; // wrong password provided by the user + + return CR_OK; +} + +static int compute_password_digest(const char *pw, size_t pwlen, + char *d, size_t *dlen) +{ + unsigned char pk[CRYPTO_PUBLICKEYBYTES]; + if (*dlen < PASSWORD_LEN || pwlen == 0) + return 1; + *dlen= PASSWORD_LEN; + crypto_sign_keypair(pk, (unsigned char*)pw, pwlen); + my_base64_encode(pk, CRYPTO_PUBLICKEYBYTES, d); + return 0; +} + +static int digest_to_binary(const char *d, size_t dlen, + unsigned char *b, size_t *blen) +{ + char pw[PASSWORD_LEN_BUF]; + + if (*blen < CRYPTO_PUBLICKEYBYTES || dlen != PASSWORD_LEN) + { + my_printf_error(ER_PASSWD_LENGTH, "Password hash should be %d characters long", 0, PASSWORD_LEN); + return 1; + } + + *blen= CRYPTO_PUBLICKEYBYTES; + memcpy(pw, d, PASSWORD_LEN); + pw[PASSWORD_LEN]= '='; + if (my_base64_decode(pw, PASSWORD_LEN_BUF, b, 0, 0) == CRYPTO_PUBLICKEYBYTES) + return 0; + my_printf_error(ER_PASSWD_LENGTH, "Password hash should be base64 encoded", 0); + return 1; +} + +static struct st_mysql_auth info = +{ + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + "client_ed25519", + auth, + compute_password_digest, + digest_to_binary +}; + +static int init(void *p __attribute__((unused))) +{ + loaded= 1; + return 0; +} + +static int deinit(void *p __attribute__((unused))) +{ + loaded= 0; + return 0; +} + +maria_declare_plugin(ed25519) +{ + MYSQL_AUTHENTICATION_PLUGIN, + &info, + "ed25519", + "Sergei Golubchik", + "Elliptic curve ED25519 based authentication", + PLUGIN_LICENSE_GPL, + init, + deinit, + 0x0101, + NULL, + NULL, + "1.1", + MariaDB_PLUGIN_MATURITY_STABLE +} +maria_declare_plugin_end; + +/************************** UDF ****************************************/ +MYSQL_PLUGIN_EXPORT +char *ed25519_password(UDF_INIT *initid __attribute__((unused)), + UDF_ARGS *args, char *result, unsigned long *length, + char *is_null, char *error __attribute__((unused))) +{ + unsigned char pk[CRYPTO_PUBLICKEYBYTES]; + + if ((*is_null= !args->args[0])) + return NULL; + + *length= PASSWORD_LEN; + crypto_sign_keypair(pk, (unsigned char*)args->args[0], args->lengths[0]); + my_base64_encode(pk, CRYPTO_PUBLICKEYBYTES, result); + return result; +} + +/* + At least one of _init/_deinit is needed unless the server is started + with --allow_suspicious_udfs. +*/ +MYSQL_PLUGIN_EXPORT +my_bool ed25519_password_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) + { + strcpy(message,"Wrong arguments to ed25519_password()"); + return 1; + } + if (!loaded) + { + /* cannot work unless the plugin is loaded, we need services. */ + strcpy(message,"Authentication plugin ed25519 is not loaded"); + return 1; + } + initid->max_length= PASSWORD_LEN_BUF; + return 0; +} |