diff --git a/CMakeLists.txt b/CMakeLists.txt index 1dbdc8a0..b8e45c18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,6 @@ IF(CMAKE_VERSION VERSION_GREATER "2.9.9") CMAKE_POLICY(SET CMP0045 OLD) ENDIF() - SET(MARIADB_CONNECTOR_C_COPYRIGHT "2013-2015 MariaDB Corporation Ab") ### Options ### @@ -296,10 +295,10 @@ IF(GIT_BUILD_SRCPKG) STRING(REGEX REPLACE "\\[|\\]" "" GIT_BRANCH ${git_branch}) MESSAGE(STATUS "${GIT_BRANCH}") IF(WIN32) - EXECUTE_PROCESS(COMMAND git archive ${GIT_BRANCH} --format=zip --output=${CPACK_SOURCE_PACKAGE_FILE_NAME}.zip) + EXECUTE_PROCESS(COMMAND git archive ${GIT_BRANCH} --format=zip --prefix=${CPACK_SOURCE_PACKAGE_FILE_NAME}/ --output=${CPACK_SOURCE_PACKAGE_FILE_NAME}.zip) ELSE() - EXECUTE_PROCESS(COMMAND git archive ${GIT_BRANCH} --format=zip --output=${CPACK_SOURCE_PACKAGE_FILE_NAME}.zip) - EXECUTE_PROCESS(COMMAND git archive ${GIT_BRANCH} --format=tar --output=${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar) + EXECUTE_PROCESS(COMMAND git archive ${GIT_BRANCH} --format=zip --prefix=${CPACK_SOURCE_PACKAGE_FILE_NAME}/ --output=${CPACK_SOURCE_PACKAGE_FILE_NAME}.zip) + EXECUTE_PROCESS(COMMAND git archive ${GIT_BRANCH} --format=tar --prefix=${CPACK_SOURCE_PACKAGE_FILE_NAME}/ --output=${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar) EXECUTE_PROCESS(COMMAND gzip -9 -f ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar) ENDIF() ENDIF() diff --git a/include/mysql.h b/include/mysql.h index 3c000741..b1feac36 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -214,7 +214,8 @@ extern unsigned int mariadb_deinitialize_ssl; 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 + MARIADB_OPT_CONNECTION_READ_ONLY, + MYSQL_OPT_CONNECT_ATTRS /* for mysql_get_optionv */ }; enum mysql_status { MYSQL_STATUS_READY, @@ -487,6 +488,8 @@ CHARSET_INFO * STDCALL mariadb_get_charset_by_nr(unsigned int csnr); size_t STDCALL mariadb_convert_string(const char *from, size_t *from_len, CHARSET_INFO *from_cs, char *to, size_t *to_len, CHARSET_INFO *to_cs, int *errorcode); int STDCALL mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...); +int STDCALL mysql_get_optionv(MYSQL *mysql, enum mysql_option option, void *arg, ...); +int STDCALL mysql_get_option(MYSQL *mysql, enum mysql_option option, void *arg); MYSQL_PARAMETERS *STDCALL mysql_get_parameters(void); unsigned long STDCALL mysql_hex_string(char *to, const char *from, size_t len); my_socket STDCALL mysql_get_socket(const MYSQL *mysql); diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index 7f0ba6e8..32d1f451 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -81,6 +81,8 @@ SET(EXPORT_SYMBOLS mysql_get_client_info mysql_get_client_version mysql_get_host_info + mysql_get_option + mysql_get_optionv mysql_get_parameters mysql_get_proto_info mysql_get_server_info diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index bab2a079..7eaa7b42 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -1229,15 +1229,13 @@ uchar *ma_send_connect_attr(MYSQL *mysql, uchar *buffer) size_t len; uchar *p= hash_element(&mysql->options.extension->connect_attrs, i); - len= *(size_t *)p; + len= strlen(p); buffer= mysql_net_store_length(buffer, len); - p+= sizeof(size_t); memcpy(buffer, p, len); - buffer+= len; - p+= len; - len= *(size_t *)p; + buffer+= (len); + p+= (len + 1); + len= strlen(p); buffer= mysql_net_store_length(buffer, len); - p+= sizeof(size_t); memcpy(buffer, p, len); buffer+= len; } @@ -2597,21 +2595,17 @@ static size_t get_store_length(size_t length) return 9; } -uchar *ma_get_hash_key(const uchar *hash_entry, +uchar *ma_get_hash_keyval(const uchar *hash_entry, unsigned int *length, my_bool not_used __attribute__((unused))) { /* Hash entry has the following format: - Offset: 0 key length - sizeof(size_t) key - key_length + - sizeof(size_t) value length - value + Offset: 0 key (\0 terminated) + key_length + 1 valu (\0 terminated) */ uchar *p= (uchar *)hash_entry; - size_t len=*((size_t*)p); - p+= sizeof(size_t); - *length= (uint)len; + size_t len= strlen(p); + *length= len; return p; } @@ -2809,19 +2803,19 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) break; case MYSQL_OPT_CONNECT_ATTR_DELETE: { - uchar *p; + uchar *h; CHECK_OPT_EXTENSION_SET(&mysql->options); if (hash_inited(&mysql->options.extension->connect_attrs) && - (p= (uchar *)hash_search(&mysql->options.extension->connect_attrs, (uchar *)arg1, + (h= (uchar *)hash_search(&mysql->options.extension->connect_attrs, (uchar *)arg1, arg1 ? (uint)strlen((char *)arg1) : 0))) { - size_t key_len= *(size_t *)p; - mysql->options.extension->connect_attrs_len-= key_len; - mysql->options.extension->connect_attrs_len-= get_store_length(key_len); - key_len= *(size_t *)(p + sizeof(size_t) + key_len); - mysql->options.extension->connect_attrs_len-= key_len; - mysql->options.extension->connect_attrs_len-= get_store_length(key_len); - hash_delete(&mysql->options.extension->connect_attrs, p); + uchar *p= h; + size_t key_len= strlen(p); + mysql->options.extension->connect_attrs_len-= key_len + get_store_length(key_len); + p+= key_len + 1; + key_len= strlen(p); + mysql->options.extension->connect_attrs_len-= key_len + get_store_length(key_len); + hash_delete(&mysql->options.extension->connect_attrs, h); } } @@ -2843,6 +2837,11 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) size_t storage_len= key_len + value_len + get_store_length(key_len) + get_store_length(value_len); + + /* since we store terminating zero character in hash, we need + * to increase lengths */ + key_len++; + value_len++; CHECK_OPT_EXTENSION_SET(&mysql->options); if (!key_len || @@ -2855,24 +2854,20 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) if (!hash_inited(&mysql->options.extension->connect_attrs)) { if (_hash_init(&mysql->options.extension->connect_attrs, - 0, 0, 0, ma_get_hash_key, ma_hash_free, 0)) + 0, 0, 0, ma_get_hash_keyval, ma_hash_free, 0)) { SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); goto end; } } - if ((buffer= (uchar *)my_malloc(2 * sizeof(size_t) + key_len + value_len, + if ((buffer= (uchar *)my_malloc(key_len + value_len, MYF(MY_WME | MY_ZEROFILL)))) { uchar *p= buffer; - *((size_t *)p)= key_len; - p+= sizeof(size_t); - memcpy(p, arg1, key_len); - p+= key_len; - *((size_t *)p)= value_len; - p+= sizeof(size_t); + strcpy(p, arg1); + p+= (strlen(arg1) + 1); if (arg2) - memcpy(p, arg2, value_len); + strcpy(p, arg2); if (hash_insert(&mysql->options.extension->connect_attrs, buffer)) { @@ -2921,6 +2916,176 @@ end: DBUG_RETURN(1); } +int STDCALL +mysql_get_optionv(MYSQL *mysql, enum mysql_option option, void *arg, ...) +{ + va_list ap; + + DBUG_ENTER("mariadb_get_optionv"); + DBUG_PRINT("enter",("option: %d",(int) option)); + + va_start(ap, arg); + + switch(option) { + case MYSQL_OPT_CONNECT_TIMEOUT: + *((uint *)arg)= mysql->options.connect_timeout; + break; + case MYSQL_OPT_COMPRESS: + *((my_bool *)arg)= mysql->options.compress; + break; + case MYSQL_OPT_NAMED_PIPE: + *((my_bool *)arg)= mysql->options.named_pipe; + break; + case MYSQL_OPT_LOCAL_INFILE: /* Allow LOAD DATA LOCAL ?*/ + *((uint *)arg)= test(mysql->options.client_flag & CLIENT_LOCAL_FILES); + break; + case MYSQL_INIT_COMMAND: + /* mysql_get_optionsv(mysql, MYSQL_INIT_COMMAND, commands, elements) */ + { + unsigned int *elements; + if (arg) + *((char **)arg)= mysql->options.init_command ? mysql->options.init_command->buffer : NULL; + if ((elements= va_arg(ap, unsigned int *))) + *elements= mysql->options.init_command ? mysql->options.init_command->elements : 0; + } + break; + case MYSQL_READ_DEFAULT_FILE: + *((char **)arg)= mysql->options.my_cnf_file; + break; + case MYSQL_READ_DEFAULT_GROUP: + *((char **)arg)= mysql->options.my_cnf_group; + break; + case MYSQL_SET_CHARSET_DIR: + /* not supported in this version. Since all character sets + are internally available, we don't throw an error */ + *((char **)arg)= NULL; + break; + case MYSQL_SET_CHARSET_NAME: + *((char **)arg)= mysql->options.charset_name; + break; + case MYSQL_OPT_RECONNECT: + *((uint *)arg)= mysql->reconnect; + break; + case MYSQL_OPT_PROTOCOL: + *((uint *)arg)= mysql->options.protocol; + break; + case MYSQL_OPT_READ_TIMEOUT: + *((uint *)arg)= mysql->options.read_timeout; + break; + case MYSQL_OPT_WRITE_TIMEOUT: + *((uint *)arg)= mysql->options.write_timeout; + break; + case MYSQL_REPORT_DATA_TRUNCATION: + *((uint *)arg)= mysql->options.report_data_truncation; + break; + case MYSQL_PROGRESS_CALLBACK: + *((void (**)(const MYSQL *, uint, uint, double, const char *, uint))arg)= + mysql->options.extension ? mysql->options.extension->report_progress : NULL; + break; + case MYSQL_PLUGIN_DIR: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->plugin_dir : NULL; + break; + case MYSQL_DEFAULT_AUTH: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->default_auth : NULL; + break; + case MYSQL_OPT_NONBLOCK: + *((my_bool *)arg)= test(mysql->options.extension && mysql->options.extension->async_context); + break; + case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: + *((my_bool *)arg)= test(mysql->options.client_flag & CLIENT_SSL_VERIFY_SERVER_CERT); + break; + case MYSQL_OPT_SSL_KEY: + *((char **)arg)= mysql->options.ssl_key; + break; + case MYSQL_OPT_SSL_CERT: + *((char **)arg)= mysql->options.ssl_cert; + break; + case MYSQL_OPT_SSL_CA: + *((char **)arg)= mysql->options.ssl_ca; + break; + case MYSQL_OPT_SSL_CAPATH: + *((char **)arg)= mysql->options.ssl_capath; + break; + case MYSQL_OPT_SSL_CIPHER: + *((char **)arg)= mysql->options.ssl_cipher; + break; + case MYSQL_OPT_SSL_CRL: + *((char **)arg)= mysql->options.extension ? mysql->options.ssl_cipher : NULL; + break; + case MYSQL_OPT_SSL_CRLPATH: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->ssl_crlpath : NULL; + break; + case MYSQL_OPT_CONNECT_ATTRS: + /* mysql_get_optionsv(mysql, MYSQL_OPT_CONNECT_ATTRS, keys, vals, elements) */ + { + int i, *elements; + char **key= (char **)arg; + char **val; + + if (!elements) + goto error; + + val= va_arg(ap, char **); + + if (!(elements= va_arg(ap, unsigned int *))) + goto error; + + *elements= 0; + + if (!mysql->options.extension || + !hash_inited(&mysql->options.extension->connect_attrs)) + break; + + *elements= mysql->options.extension->connect_attrs.records; + + if (val || key) + { + for (i=0; i < *elements; i++) + { + uchar *p= hash_element(&mysql->options.extension->connect_attrs, i); + if (key) + key[i]= p; + p+= strlen(p) + 1; + if (val) + val[i]= p; + } + } + } + break; + case MYSQL_SECURE_AUTH: + *((my_bool *)arg)= mysql->options.secure_auth; + break; + case MYSQL_OPT_BIND: + *((char **)arg)= mysql->options.bind_address; + break; + case MARIADB_OPT_SSL_FP: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->ssl_fp : NULL; + break; + case MARIADB_OPT_SSL_FP_LIST: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->ssl_fp_list : NULL; + break; + case MARIADB_OPT_SSL_PASSWORD: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->ssl_pw : NULL; + break; + /* todo + case MARIADB_OPT_CONNECTION_READ_ONLY: + break; + */ + default: + va_end(ap); + DBUG_RETURN(-1); + } + va_end(ap); + DBUG_RETURN(0); +error: + va_end(ap); + DBUG_RETURN(-1); +} + +int STDCALL mysql_get_option(MYSQL *mysql, enum mysql_option option, void *arg) +{ + return mysql_get_optionv(mysql, option, arg); +} int STDCALL mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index 37256efc..d4164403 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -464,9 +464,13 @@ int ma_ssl_verify_server_cert(MARIADB_SSL *cssl) { X509 *cert; MYSQL *mysql; - MARIADB_PVIO *pvio; + X509_NAME *x509sn; + int cn_pos; + X509_NAME_ENTRY *cn_entry; + ASN1_STRING *cn_asn1; + const char *cn_str; SSL *ssl; - char *p1, *p2, buf[256]; + MARIADB_PVIO *pvio; if (!cssl || !cssl->ssl) return 1; @@ -477,33 +481,46 @@ int ma_ssl_verify_server_cert(MARIADB_SSL *cssl) if (!mysql->host) { - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, - "Invalid (empty) hostname"); + pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), "Invalid (empty) hostname"); return 1; } if (!(cert= SSL_get_peer_certificate(ssl))) { - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, - "Unable to get server certificate"); + pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), "Unable to get server certificate"); return 1; } - X509_NAME_oneline(X509_get_subject_name(cert), buf, 256); + x509sn= X509_get_subject_name(cert); + + if ((cn_pos= X509_NAME_get_index_by_NID(x509sn, NID_commonName, -1)) < 0) + goto error; + + if (!(cn_entry= X509_NAME_get_entry(x509sn, cn_pos))) + goto error; + + if (!(cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry))) + goto error; + + cn_str = (char *)ASN1_STRING_data(cn_asn1); + + /* Make sure there is no embedded \0 in the CN */ + if ((size_t)ASN1_STRING_length(cn_asn1) != strlen(cn_str)) + goto error; + + if (strcmp(cn_str, mysql->host)) + goto error; + X509_free(cert); - /* Extract the server name from buffer: - Format: ....CN=/hostname/.... */ - if ((p1= strstr(buf, "/CN="))) - { - p1+= 4; - if ((p2= strchr(p1, '/'))) - *p2= 0; - if (!strcmp(mysql->host, p1)) - return(0); - } - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, - "Validation of SSL server certificate failed"); + return 0; +error: + X509_free(cert); + + pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), "Validation of SSL server certificate failed"); return 1; } diff --git a/mariadb_config/libmariadb.pc.in b/mariadb_config/libmariadb.pc.in new file mode 100644 index 00000000..e9161554 --- /dev/null +++ b/mariadb_config/libmariadb.pc.in @@ -0,0 +1,19 @@ +# +# pkg_config.pc.in +# +# pkg_config configuration file +# For a detailled description of options, please visit +# Dan Nicholson’s Guide to pkg-config (http://www.freedesktop.org/wiki/Software/pkg-config/) +# + +includedir=@PREFIX_INSTALL_DIR@/@INCLUDE_INSTALL_DIR@/@SUFFIX_INSTALL_DIR@ +libdir=@PREFIX_INSTALL_DIR@/@INCLUDE_INSTALL_DIR@/@SUFFIX_INSTALL_DIR@ +prefix=@PREFIX_INSTALL_DIR@ + +Name: libmariadb +Version: @LIBMARIADB_VERSION@ +Description: MariaDB Connector/C dynamic library +Cflags: -I@PREFIX_INSTALL_DIR@/@INCLUDE_INSTALL_DIR@/@SUFFIX_INSTALL_DIR@ @CMAKE_C_FLAGS@ +Libs: -L@PREFIX_INSTALL_DIR@/@LIB_INSTALL_DIR@/@SUFFIX_INSTALL_DIR@ -lmariadb @extra_dynamic_LDFLAGS@ + + diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index e40b05e0..1b6c6d7f 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -744,7 +744,82 @@ static int test_bind_address(MYSQL *my) return OK; } +static int test_get_options(MYSQL *my) +{ + MYSQL *mysql= mysql_init(NULL); + int options_int[]= {MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_LOCAL_INFILE, + MYSQL_OPT_RECONNECT, MYSQL_OPT_PROTOCOL, MYSQL_OPT_READ_TIMEOUT, MYSQL_OPT_WRITE_TIMEOUT, 0}; + my_bool options_bool[]= {MYSQL_OPT_COMPRESS, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQL_SECURE_AUTH, +#ifdef _WIN32 + MYSQL_OPT_NAMED_PIPE, +#endif + 0}; + int options_char[]= {MYSQL_READ_DEFAULT_FILE, MYSQL_READ_DEFAULT_GROUP, MYSQL_SET_CHARSET_NAME, + MYSQL_OPT_SSL_KEY, MYSQL_OPT_SSL_CA, MYSQL_OPT_SSL_CERT, MYSQL_OPT_SSL_CAPATH, + MYSQL_OPT_SSL_CIPHER, MYSQL_OPT_BIND, MARIADB_OPT_SSL_FP, MARIADB_OPT_SSL_FP_LIST, + MARIADB_OPT_SSL_PASSWORD, 0}; + + char *init_command[3]= {"SET @a:=1", "SET @b:=2", "SET @c:=3"}; + int elements= 0; + char **command; + + + int intval[2]= {1, 0}; + my_bool boolval[2]= {1, 0}; + char *char1= "test", *char2; + int i; + char *attr_key[] = {"foo1", "foo2", "foo3"}; + char *attr_val[] = {"bar1", "bar2", "bar3"}; + char *key[3], *val[3]; + + for (i=0; options_int[i]; i++) + { + mysql_options(mysql, options_int[i], &intval[0]); + intval[1]= 0; + mysql_get_optionv(mysql, options_int[i], &intval[1]); + FAIL_IF(intval[0] != intval[1], "mysql_get_optionv (int) failed"); + } + for (i=0; options_bool[i]; i++) + { + mysql_options(mysql, options_bool[i], &boolval[0]); + intval[1]= 0; + mysql_get_optionv(mysql, options_bool[i], &boolval[1]); + FAIL_IF(boolval[0] != boolval[1], "mysql_get_optionv (my_bool) failed"); + } + for (i=0; options_char[i]; i++) + { + mysql_options(mysql, options_char[i], char1); + char2= NULL; + mysql_get_optionv(mysql, options_char[i], (void *)&char2); + FAIL_IF(strcmp(char1, char2), "mysql_get_optionv (char) failed"); + } + + for (i=0; i < 3; i++) + mysql_options(mysql, MYSQL_INIT_COMMAND, init_command[i]); + + mysql_get_optionv(mysql, MYSQL_INIT_COMMAND, &command, &elements); + FAIL_IF(elements != 3, "expected 3 elements"); + for (i=0; i < 3; i++) + FAIL_IF(strcmp(init_command[i], command[i]), "wrong init command"); + + for (i=0; i < 3; i++) + mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, attr_key[i], attr_val[i]); + + mysql_get_optionv(mysql, MYSQL_OPT_CONNECT_ATTRS, NULL, NULL, &elements); + FAIL_IF(elements != 3, "expected 3 connection attributes"); + + mysql_get_optionv(mysql, MYSQL_OPT_CONNECT_ATTRS, &key, &val, &elements); + for (i=0; i < elements; i++) + { + diag("%s => %s", key[i], val[i]); + } + + mysql_close(mysql); + return OK; +} + struct my_tests_st my_tests[] = { + {"test_get_options", test_get_options, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_wrong_bind_address", test_wrong_bind_address, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_bind_address", test_bind_address, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc118", test_conc118, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},