diff --git a/cmake/sign.cmake b/cmake/sign.cmake index 024b861d..1461a415 100644 --- a/cmake/sign.cmake +++ b/cmake/sign.cmake @@ -2,9 +2,11 @@ MACRO(SIGN_TARGET target) IF(WITH_SIGNCODE) IF(WIN32) SET(target_file $) - MESSAGE(STATUS "TARGET_FILE: <${target_file}>") - ADD_CUSTOM_COMMAND(TARGET ${target} - COMMAND signtool ARGS sign ${SIGN_OPTIONS} ${target_file}) + IF(EXISTS "C:\tools\sign.bat") + ADD_CUSTOM_COMMAND(TARGET ${target} COMMAND c:\tools\sign.bat ${target_file}) + ELSEIF() + ADD_CUSTOM_COMMAND(TARGET ${target} COMMAND signtool ARGS sign ${SIGN_OPTIONS} ${target_file}) + ENDIF() ENDIF() ENDIF() ENDMACRO() diff --git a/include/ma_common.h b/include/ma_common.h index 1871c734..6a6a80a0 100644 --- a/include/ma_common.h +++ b/include/ma_common.h @@ -51,7 +51,10 @@ struct st_mysql_options_extension { MARIADB_DB_DRIVER *db_driver; char *ssl_fp; /* finger print of server certificate */ char *ssl_fp_list; /* white list of finger prints */ + char *ssl_pw; /* password for encrypted certificates */ }; +#define OPT_HAS_EXT_VAL(a,key) \ + ((a)->options.extension && (a)->options.extension->key) #endif diff --git a/include/mysql.h b/include/mysql.h index f86910d5..b61eb84f 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -213,6 +213,7 @@ extern unsigned int mariadb_deinitialize_ssl; MYSQL_DATABASE_DRIVER=7000, MARIADB_OPT_SSL_FP, /* single finger print for server certificate verification */ MARIADB_OPT_SSL_FP_LIST, /* finger print white list for server certificate verification */ + MARIADB_OPT_SSL_PASSWORD, /* password for encrypted certificates */ MARIADB_OPT_CONNECTION_READ_ONLY }; diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index 19066f8e..3c7f5192 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -622,7 +622,7 @@ static const char *default_options[]= "ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name", "multi-results", "multi-statements", "multi-queries", "secure-auth", "report-data-truncation", "plugin-dir", "default-auth", "database-type", - "ssl-fp", "ssl-fp-list", "bind-address", + "ssl-fp", "ssl-fp-list", "ssl_password", "bind-address", NULL }; @@ -637,7 +637,7 @@ enum option_val OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol, OPT_shared_memory_base_name, OPT_multi_results, OPT_multi_statements, OPT_multi_queries, OPT_secure_auth, OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth, OPT_db_type, - OPT_ssl_fp, OPT_ssl_fp_list, OPT_bind_address + OPT_ssl_fp, OPT_ssl_fp_list, OPT_ssl_pw, OPT_bind_address }; #define CHECK_OPT_EXTENSION_SET(OPTS)\ @@ -797,6 +797,9 @@ static void mysql_read_default_options(struct st_mysql_options *options, case OPT_ssl_fp_list: OPT_SET_EXTENDED_VALUE_STR(options, ssl_fp_list, opt_arg); break; + case OPT_ssl_pw: + OPT_SET_EXTENDED_VALUE_STR(options, ssl_pw, opt_arg); + break; #else case OPT_ssl_key: case OPT_ssl_cert: @@ -1149,8 +1152,6 @@ mysql_init(MYSQL *mysql) return mysql; } - - int STDCALL mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert, const char *ca, const char *capath, const char *cipher) @@ -1874,6 +1875,7 @@ static void mysql_close_options(MYSQL *mysql) my_free(mysql->options.ssl_ca); my_free(mysql->options.ssl_capath); my_free(mysql->options.ssl_cipher); + if (mysql->options.extension) { struct mysql_async_context *ctxt; @@ -1884,6 +1886,7 @@ static void mysql_close_options(MYSQL *mysql) my_free(mysql->options.extension->ssl_crlpath); my_free(mysql->options.extension->ssl_fp); my_free(mysql->options.extension->ssl_fp_list); + my_free(mysql->options.extension->ssl_pw); if(hash_inited(&mysql->options.extension->connect_attrs)) hash_free(&mysql->options.extension->connect_attrs); if ((ctxt = mysql->options.extension->async_context) != 0) @@ -2844,6 +2847,9 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) case MARIADB_OPT_SSL_FP_LIST: OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_fp_list, (char *)arg1); break; + case MARIADB_OPT_SSL_PASSWORD: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_pw, (char *)arg1); + break; case MARIADB_OPT_CONNECTION_READ_ONLY: if (mysql->net.conn_hdlr) DBUG_RETURN(mysql->net.conn_hdlr->plugin->options(mysql, MARIADB_OPT_CONNECTION_READ_ONLY, arg1)); diff --git a/libmariadb/secure/gnutls.c b/libmariadb/secure/gnutls.c index e76bd204..45ab5ead 100644 --- a/libmariadb/secure/gnutls.c +++ b/libmariadb/secure/gnutls.c @@ -174,9 +174,11 @@ static int ma_ssl_set_certs(MYSQL *mysql) /* set key */ if (certfile || keyfile) { - if ((ssl_error= gnutls_certificate_set_x509_key_file(GNUTLS_xcred, + if ((ssl_error= gnutls_certificate_set_x509_key_file2(GNUTLS_xcred, certfile, keyfile, - GNUTLS_X509_FMT_PEM)) < 0) + GNUTLS_X509_FMT_PEM, + OPT_HAS_EXT_VAL(mysql, ssl_pw) ? mysql->options.extension->ssl_pw : NULL, + 0)) < 0) goto error; } return 1; diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index 1873d344..37256efc 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -238,6 +238,15 @@ void ma_ssl_end() return; } +int ma_ssl_get_password(char *buf, int size, int rwflag, void *userdata) +{ + bzero(buf, size); + if (userdata) + strncpy(buf, (char *)userdata, size); + return strlen(buf); +} + + static int ma_ssl_set_certs(MYSQL *mysql) { char *certfile= mysql->options.ssl_cert, @@ -270,12 +279,24 @@ static int ma_ssl_set_certs(MYSQL *mysql) if (SSL_CTX_use_certificate_file(SSL_context, certfile, SSL_FILETYPE_PEM) != 1) goto error; - /* set key */ + /* If the private key file is encrypted, we need to register a callback function + * for providing password. */ + if (OPT_HAS_EXT_VAL(mysql, ssl_pw)) + { + SSL_CTX_set_default_passwd_cb_userdata(SSL_context, (void *)mysql->options.extension->ssl_pw); + SSL_CTX_set_default_passwd_cb(SSL_context, ma_ssl_get_password); + } + if (keyfile && keyfile[0]) { if (SSL_CTX_use_PrivateKey_file(SSL_context, keyfile, SSL_FILETYPE_PEM) != 1) goto error; } + if (OPT_HAS_EXT_VAL(mysql, ssl_pw)) + { + SSL_CTX_set_default_passwd_cb_userdata(SSL_context, NULL); + SSL_CTX_set_default_passwd_cb(SSL_context, NULL); + } /* verify key */ if (certfile && !SSL_CTX_check_private_key(SSL_context)) goto error; @@ -329,6 +350,7 @@ static int my_verify_callback(int ok, X509_STORE_CTX *ctx) return ok; } + void *ma_ssl_init(MYSQL *mysql) { int verify; diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index a42b183f..11452977 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -674,7 +674,7 @@ static int test_wrong_bind_address(MYSQL *my) char *bind_addr= "100.188.111.112"; MYSQL *mysql; - if (!strcmp(hostname, "localhost")) + if (!hostname || !strcmp(hostname, "localhost")) { diag("test doesn't work with unix sockets"); return SKIP; @@ -702,7 +702,7 @@ static int test_bind_address(MYSQL *my) char query[128]; int rc; - if (!strcmp(hostname, "localhost")) + if (!hostname || !strcmp(hostname, "localhost")) { diag("test doesn't work with unix sockets"); return SKIP; diff --git a/unittest/libmariadb/ssl.c.in b/unittest/libmariadb/ssl.c.in index 9dc931bd..d74a4e6a 100644 --- a/unittest/libmariadb/ssl.c.in +++ b/unittest/libmariadb/ssl.c.in @@ -28,13 +28,14 @@ static int skip_ssl= 1; const char *ssluser= "ssluser"; const char *sslpw= "sslpw"; +char sslhost[128]; pthread_mutex_t LOCK_test; int check_skip_ssl() { #ifndef HAVE_SSL - diag("client library built without SSL support -> skip"); + diag("client library built without OpenSSL support -> skip"); return 1; #endif if (skip_ssl) @@ -45,19 +46,58 @@ int check_skip_ssl() return 0; } +static int check_cipher(MYSQL *mysql) +{ + char *cipher= (char *)mysql_get_ssl_cipher(mysql); + if (!cipher) + return 1; +#ifdef HAVE_GNUTLS + return strcmp(cipher, "AES-128-GCM"); +#endif +#ifdef HAVE_OPENSSL + return strcmp(cipher, "DHE-RSA-AES256-SHA"); +#endif +} + +static int create_ssl_user(const char *ssluser, my_bool is_X509) +{ + int rc; + char query[1024]; + MYSQL *mysql= mysql_init(NULL); + + FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, + port, socketname, 0), mysql_error(mysql)); + + sprintf(query, "DROP USER IF EXISTS '%s'@'%s'", ssluser, sslhost); + rc= mysql_query(mysql, query); + check_mysql_rc(rc,mysql); + + sprintf(query, "CREATE USER '%s'@'%s' IDENTIFIED BY '%s'", ssluser, sslhost, sslpw); + rc= mysql_query(mysql, query); + check_mysql_rc(rc,mysql); + + sprintf(query, "GRANT ALL ON %s.* TO '%s'@'%s' REQUIRE %s", schema, ssluser, sslhost, is_X509 ? "X509" : "SSL"); + rc= mysql_query(mysql, query); + check_mysql_rc(rc,mysql); + rc= mysql_query(mysql, "FLUSH PRIVILEGES"); + check_mysql_rc(rc,mysql); + + return rc; +} + static int test_ssl(MYSQL *mysql) { int rc; MYSQL_RES *res; MYSQL_ROW row; - rc= mysql_query(mysql, "SELECT @@have_ssl"); + rc= mysql_query(mysql, "SELECT @@have_ssl UNION SELECT @@have_openssl"); check_mysql_rc(rc, mysql); res= mysql_store_result(mysql); FAIL_IF(!res, mysql_error(mysql)); - if ((row= mysql_fetch_row(res))) + while ((row= mysql_fetch_row(res))) { if (!strcmp(row[0], "YES")) skip_ssl= 0; @@ -65,12 +105,20 @@ static int test_ssl(MYSQL *mysql) } mysql_free_result(res); + sslhost[0]= 0; + if (!skip_ssl) { - rc= mysql_query(mysql, "DROP USER 'ssluser'@'%'"); - rc= mysql_query(mysql, "GRANT ALL ON test.* TO 'ssluser'@'%' IDENTIFIED BY 'sslpw' REQUIRE SSL"); - rc= mysql_query(mysql, "GRANT ALL ON test.* TO 'ssluser'@'localhost' IDENTIFIED BY 'sslpw' REQUIRE SSL"); - rc= mysql_query(mysql, "FLUSH PRVILEGES"); + char *p; + + rc= mysql_query(mysql, "SELECT CURRENT_USER()"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + diag("user: %s", row[0]); + if (p= strchr(row[0], '@')) + strcpy(sslhost, p+1); + mysql_free_result(res); } return OK; @@ -79,7 +127,6 @@ static int test_ssl(MYSQL *mysql) static int test_ssl_cipher(MYSQL *unused) { MYSQL *my; - char *cipher; if (check_skip_ssl()) return SKIP; @@ -92,8 +139,7 @@ static int test_ssl_cipher(MYSQL *unused) FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, port, socketname, 0), mysql_error(my)); - cipher= (char *)mysql_get_ssl_cipher(my); - FAIL_IF(cipher == NULL, "used cipher is NULL"); + FAIL_IF(check_cipher(my) != 0, "Invalid cipher"); mysql_close(my); return OK; } @@ -101,23 +147,16 @@ static int test_ssl_cipher(MYSQL *unused) static int test_conc95(MYSQL *my) { MYSQL *mysql; - int rc; if (check_skip_ssl()) return SKIP; - rc= mysql_query(my, "DROP USER 'ssluser1'@'%'"); - rc= mysql_query(my, "DROP USER 'ssluser1'@'localhost'"); - rc= mysql_query(my, "GRANT ALL ON test.* TO 'ssluser1'@'%' IDENTIFIED BY 'sslpw' REQUIRE X509"); - rc= mysql_query(my, "GRANT ALL ON test.* TO 'ssluser1'@'localhost' IDENTIFIED BY 'sslpw' REQUIRE X509"); - check_mysql_rc(rc, my); - rc= mysql_query(my, "FLUSH PRIVILEGES"); - check_mysql_rc(rc, my); + create_ssl_user("ssluser1", 1); mysql= mysql_init(NULL); mysql_ssl_set(mysql, - "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-key.pem", - "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-cert.pem", + "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/server-key.pem", + "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/server-cert.pem", "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", NULL, NULL); @@ -125,7 +164,6 @@ static int test_conc95(MYSQL *my) if (!mysql_real_connect(mysql, hostname, "ssluser1", sslpw, schema, port, socketname, 0)) { - diag("Error: %s", mysql_error(mysql)); mysql_close(mysql); diag("could not establish x509 connection"); return FAIL; @@ -137,7 +175,6 @@ static int test_conc95(MYSQL *my) static int test_multi_ssl_connections(MYSQL *unused) { MYSQL *mysql[50], *my; - char *cipher; int i, rc; int old_connections= 0, new_connections= 0; MYSQL_RES *res; @@ -149,6 +186,8 @@ static int test_multi_ssl_connections(MYSQL *unused) diag("Test doesn't work with yassl"); return SKIP; + create_ssl_user(ssluser, 0); + my= mysql_init(NULL); FAIL_IF(!my,"mysql_init() failed"); FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, @@ -177,8 +216,7 @@ static int test_multi_ssl_connections(MYSQL *unused) return FAIL; } - cipher= (char *)mysql_get_ssl_cipher(mysql[i]); - FAIL_IF(cipher == NULL, "used cipher is NULL"); + FAIL_IF(check_cipher(mysql[i]) != 0, "Invalid cipher"); } for (i=0; i < 50; i++) mysql_close(mysql[i]); @@ -307,6 +345,31 @@ static int test_phpbug51647(MYSQL *my) return OK; } +static int test_password_protected(MYSQL *my) +{ + MYSQL* mysql; + + if (check_skip_ssl()) + return SKIP; + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-key-enc.pem", + "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-cert.pem", + "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", 0, 0); + + mysql_options(mysql, MARIADB_OPT_SSL_PASSWORD, "qwerty"); + + FAIL_IF(!mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + port, socketname, 0), mysql_error(mysql)); + diag("%s", mysql_get_ssl_cipher(mysql)); + mysql_close(mysql); + + return OK; +} + + static int test_conc50(MYSQL *my) { MYSQL *mysql; @@ -394,28 +457,18 @@ static int test_conc127(MYSQL *my) static int test_conc50_3(MYSQL *my) { MYSQL *mysql; - int rc; - char query[256]; if (check_skip_ssl()) return SKIP; - mysql_query(my, "DROP USER 'ssltest'@'%'"); - - sprintf(query, "GRANT ALL ON %s.* TO 'ssltest'@'%' REQUIRE SSL", schema ? schema : "*"); - rc= mysql_query(my, query); - sprintf(query, "GRANT ALL ON %s.* TO 'ssltest'@'localhost' REQUIRE SSL", schema ? schema : "*"); - rc= mysql_query(my, query); - check_mysql_rc(rc, my); - rc= mysql_query(my, "FLUSH PRIVILEGES"); - check_mysql_rc(rc, my); + create_ssl_user(ssluser, 0); mysql= mysql_init(NULL); FAIL_IF(!mysql, "Can't allocate memory"); mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); - mysql_real_connect(mysql, hostname, (const char *)"ssltest", NULL, schema, + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, port, socketname, 0); FAIL_IF(!mysql_errno(mysql), "Error expected, SSL connection required!"); mysql_close(mysql); @@ -487,7 +540,7 @@ static int test_bug62743(MYSQL *my) mysql= mysql_init(NULL); FAIL_IF(!mysql, "Can't allocate memory"); - mysql_ssl_set(mysql, "dummykey", "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-cert.pem", NULL, NULL, NULL); + mysql_ssl_set(mysql, "dummykey", NULL, NULL, NULL, NULL); mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, port, socketname, 0); @@ -594,8 +647,6 @@ static int test_conc_102(MYSQL *mysql) rc= mysql_query(mysql, "INSERT INTO t_conc102 VALUES (0)"); check_mysql_rc(rc, mysql); - pthread_mutex_init(&LOCK_test, 0); - for (i=0; i < 50; i++) { #ifndef WIN32 @@ -613,8 +664,7 @@ static int test_conc_102(MYSQL *mysql) #else WaitForSingleObject(hthreads[i], INFINITE); #endif - } - pthread_mutex_destroy(&LOCK_test); + } rc= mysql_query(mysql, "SELECT a FROM t_conc102"); check_mysql_rc(rc, mysql); res= mysql_store_result(mysql); @@ -630,7 +680,6 @@ const char *ssl_cert_finger_print= "@SSL_CERT_FINGER_PRINT@"; static int test_ssl_fp(MYSQL *unused) { MYSQL *my; - char *cipher; if (check_skip_ssl()) return SKIP; @@ -645,8 +694,7 @@ static int test_ssl_fp(MYSQL *unused) FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, port, socketname, 0), mysql_error(my)); - cipher= (char *)mysql_get_ssl_cipher(my); - FAIL_IF(cipher == NULL, "used cipher is NULL"); + FAIL_IF(check_cipher(my) != 0, "Invalid cipher"); mysql_close(my); return OK; } @@ -654,7 +702,6 @@ static int test_ssl_fp(MYSQL *unused) static int test_ssl_fp_list(MYSQL *unused) { MYSQL *my; - char *cipher; if (check_skip_ssl()) return SKIP; @@ -669,47 +716,16 @@ static int test_ssl_fp_list(MYSQL *unused) FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, port, socketname, 0), mysql_error(my)); - cipher= (char *)mysql_get_ssl_cipher(my); - FAIL_IF(cipher == NULL, "used cipher is NULL"); + FAIL_IF(check_cipher(my) != 0, "Invalid cipher"); mysql_close(my); return OK; } -static int test_ssl_long_msg(MYSQL *unused) -{ - MYSQL *my; - char buffer[20000]; - int rc; - - if (check_skip_ssl()) - return SKIP; - my= mysql_init(NULL); - FAIL_IF(!my, "mysql_init() failed"); - - mysql_ssl_set(my,0, 0, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", 0, 0); - - mysql_options(my, MARIADB_OPT_SSL_FP, ssl_cert_finger_print); - - FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, - port, socketname, 0), mysql_error(my)); - - memset(buffer, 0, 20000); - strcpy(buffer, "SET @a:="); - memset(buffer + strlen(buffer), '0', 19000); - - rc= mysql_query(my, buffer); - check_mysql_rc(rc, my); - mysql_close(my); - - return OK; -} struct my_tests_st my_tests[] = { {"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL}, - /* conc127 must be the first test, otherwise it will fail!! */ {"test_conc127", test_conc127, TEST_CONNECTION_NEW, 0, NULL, NULL}, - {"test_ssl_long_msg", test_ssl_long_msg, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_ssl_fp", test_ssl_fp, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_ssl_fp_list", test_ssl_fp_list, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_conc50", test_conc50, TEST_CONNECTION_NEW, 0, NULL, NULL}, @@ -725,6 +741,7 @@ struct my_tests_st my_tests[] = { {"test_multi_ssl_connections", test_multi_ssl_connections, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_conc_102", test_conc_102, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_ssl_threads", test_ssl_threads, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_password_protected", test_password_protected, TEST_CONNECTION_NEW, 0, NULL, NULL}, {NULL, NULL, 0, 0, NULL, NULL} };