diff options
Diffstat (limited to 'libmariadb/plugins/auth/my_auth.c')
-rw-r--r-- | libmariadb/plugins/auth/my_auth.c | 124 |
1 files changed, 106 insertions, 18 deletions
diff --git a/libmariadb/plugins/auth/my_auth.c b/libmariadb/plugins/auth/my_auth.c index 72773079..faea968c 100644 --- a/libmariadb/plugins/auth/my_auth.c +++ b/libmariadb/plugins/auth/my_auth.c @@ -3,16 +3,29 @@ #include <errmsg.h> #include <string.h> #include <ma_common.h> +#include <ma_crypt.h> #include <mysql/client_plugin.h> typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t; static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, size_t); static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); +static int native_password_hash(MYSQL *mysql, unsigned char *out, size_t *outlen); static int dummy_fallback_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql __attribute__((unused))); extern void read_user_name(char *name); extern char *ma_send_connect_attr(MYSQL *mysql, unsigned char *buffer); extern int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length); extern unsigned char *mysql_net_store_length(unsigned char *packet, ulonglong length); +extern const char *disabled_plugins; + +#define hashing(p) (p->interface_version >= 0x0101 && p->hash_password_bin) + +static int set_error_from_tls_self_signed_error(MYSQL *mysql) +{ + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), mysql->net.tls_self_signed_error); + reset_tls_self_signed_error(mysql); + return 1; +} typedef struct { int (*read_packet)(struct st_plugin_vio *vio, uchar **buf); @@ -44,13 +57,14 @@ auth_plugin_t mysql_native_password_client_plugin= native_password_plugin_name, "R.J.Silk, Sergei Golubchik", "Native MySQL authentication", - {1, 0, 0}, + {1, 0, 1}, "LGPL", NULL, NULL, NULL, NULL, - native_password_auth_client + native_password_auth_client, + native_password_hash }; @@ -97,6 +111,22 @@ static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) return CR_OK; } +static int native_password_hash(MYSQL *mysql, unsigned char *out, size_t *out_length) +{ + unsigned char digest[MA_SHA1_HASH_SIZE]; + + if (*out_length < MA_SHA1_HASH_SIZE) + return 1; + *out_length= MA_SHA1_HASH_SIZE; + + /* would it be better to reuse instead of recalculating here? see ed25519 */ + ma_hash(MA_HASH_SHA1, (unsigned char*)mysql->passwd, strlen(mysql->passwd), + digest); + ma_hash(MA_HASH_SHA1, digest, sizeof(digest), out); + + return 0; +} + auth_plugin_t dummy_fallback_client_plugin= { MYSQL_CLIENT_AUTHENTICATION_PLUGIN, @@ -110,7 +140,8 @@ auth_plugin_t dummy_fallback_client_plugin= NULL, NULL, NULL, - dummy_fallback_auth_client + dummy_fallback_auth_client, + NULL }; @@ -223,7 +254,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, if (mysql->options.ssl_key || mysql->options.ssl_cert || mysql->options.ssl_ca || mysql->options.ssl_capath || mysql->options.ssl_cipher || mysql->options.use_ssl || - mysql->options.extension->tls_verify_server_cert) + !mysql->options.extension->tls_allow_invalid_server_cert) mysql->options.use_ssl= 1; if (mysql->options.use_ssl) mysql->client_flag|= CLIENT_SSL; @@ -243,15 +274,16 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, mysql->net.pvio->type == PVIO_TYPE_SHAREDMEM)) { mysql->server_capabilities &= ~(CLIENT_SSL); + mysql->options.extension->tls_allow_invalid_server_cert= 1; } /* if server doesn't support SSL and verification of server certificate was set to mandatory, we need to return an error */ if (mysql->options.use_ssl && !(mysql->server_capabilities & CLIENT_SSL)) { - if (mysql->options.extension->tls_verify_server_cert || - (mysql->options.extension && (mysql->options.extension->tls_fp || - mysql->options.extension->tls_fp_list))) + if (!mysql->options.extension->tls_allow_invalid_server_cert || + mysql->options.extension->tls_fp || + mysql->options.extension->tls_fp_list) { my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_SSL_CONNECTION_ERROR), @@ -307,9 +339,11 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, if (!(mysql->server_capabilities & CLIENT_MYSQL)) { uint server_extended_cap= mysql->extension->mariadb_server_capabilities; - uint client_extended_cap= (uint)(MARIADB_CLIENT_SUPPORTED_FLAGS >> 32); + ulonglong client_extended_flag = CLIENT_DEFAULT_EXTENDED_FLAGS; + if (mysql->options.extension && mysql->options.extension->bulk_unit_results) + client_extended_flag|= MARIADB_CLIENT_BULK_UNIT_RESULTS; mysql->extension->mariadb_client_flag= - server_extended_cap & client_extended_cap; + server_extended_cap & (long)(client_extended_flag >> 32); int4store(buff + 28, mysql->extension->mariadb_client_flag); } end= buff+32; @@ -350,6 +384,13 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, } if (ma_pvio_start_ssl(mysql->net.pvio)) goto error; + if (mysql->net.tls_self_signed_error && + (!mysql->passwd || !mysql->passwd[0] || !hashing(mpvio->plugin))) + { + /* cannot use auth to validate the cert */ + set_error_from_tls_self_signed_error(mysql); + goto error; + } } #endif /* HAVE_TLS */ @@ -658,13 +699,13 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, retry: mpvio.plugin= auth_plugin; - if (auth_plugin_name && - mysql->options.extension && - mysql->options.extension->restricted_auth) + if (auth_plugin_name) { - if (!strstr(mysql->options.extension->restricted_auth, auth_plugin_name)) + if ((mysql->options.extension && mysql->options.extension->restricted_auth) + ? !strstr(mysql->options.extension->restricted_auth, auth_plugin_name) + : strstr(disabled_plugins, auth_plugin_name) != NULL) { - my_set_error(mysql, CR_PLUGIN_NOT_ALLOWED, SQLSTATE_UNKNOWN, 0, data_plugin); + my_set_error(mysql, CR_PLUGIN_NOT_ALLOWED, SQLSTATE_UNKNOWN, 0, auth_plugin_name); return 1; } } @@ -727,15 +768,62 @@ retry: auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) auth_plugin= &dummy_fallback_client_plugin; + /* can we use this plugin with this tls server cert ? */ + if (mysql->net.tls_self_signed_error && !hashing(auth_plugin)) + return set_error_from_tls_self_signed_error(mysql); goto retry; - } /* net->read_pos[0] should always be 0 here if the server implements the protocol correctly */ - if (mysql->net.read_pos[0] == 0) - return ma_read_ok_packet(mysql, mysql->net.read_pos + 1, pkt_length); - return 1; + if (mysql->net.read_pos[0] != 0) + return 1; + if (ma_read_ok_packet(mysql, mysql->net.read_pos + 1, pkt_length)) + return -1; + + if (!mysql->net.tls_self_signed_error) + return 0; + + assert(mysql->options.use_ssl); + assert(!mysql->options.extension->tls_allow_invalid_server_cert); + assert(!mysql->options.ssl_ca); + assert(!mysql->options.ssl_capath); + assert(!mysql->options.extension->tls_fp); + assert(!mysql->options.extension->tls_fp_list); + assert(hashing(auth_plugin)); + assert(mysql->passwd[0]); + if (mysql->info && mysql->info[0] == '\1') + { + MA_HASH_CTX *ctx = NULL; + unsigned char buf[1024], digest[MA_SHA256_HASH_SIZE]; + char fp[128], hexdigest[sizeof(digest)*2+1], *hexsig= mysql->info + 1; + size_t buflen= sizeof(buf) - 1, fplen; + + mysql->info= NULL; /* no need to confuse the client with binary info */ + + if (!(fplen= ma_tls_get_finger_print(mysql->net.pvio->ctls, MA_HASH_SHA256, + fp, sizeof(fp)))) + return 1; /* error is already set */ + + if (auth_plugin->hash_password_bin(mysql, buf, &buflen) || + !(ctx= ma_hash_new(MA_HASH_SHA256))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return 1; + } + + ma_hash_input(ctx, (unsigned char*)buf, buflen); + ma_hash_input(ctx, (unsigned char*)mysql->scramble_buff, SCRAMBLE_LENGTH); + ma_hash_input(ctx, (unsigned char*)fp, fplen); + ma_hash_result(ctx, digest); + ma_hash_free(ctx); + + mysql_hex_string(hexdigest, (char*)digest, sizeof(digest)); + if (strcmp(hexdigest, hexsig) == 0) + return 0; /* phew. self-signed certificate is validated! */ + } + + return set_error_from_tls_self_signed_error(mysql); } |