diff options
author | andrey <andrey@php.net> | 2012-09-25 14:04:36 +0200 |
---|---|---|
committer | andrey <andrey@php.net> | 2012-09-25 14:04:36 +0200 |
commit | 571b46bff68925f15f578147278b43c6f88083f0 (patch) | |
tree | 101cf8e3f35f89acb3290e07ca5712b93a8aae09 /ext/mysqlnd/mysqlnd_auth.c | |
parent | 8a930c93dd673b9f13350590a7b6e3a1a5ce4431 (diff) | |
download | php-git-571b46bff68925f15f578147278b43c6f88083f0.tar.gz |
Add SHA256 authentication support - password hashing to mysqlnd
Automatic switchover to SSL with plain-text password is not part of this
Diffstat (limited to 'ext/mysqlnd/mysqlnd_auth.c')
-rw-r--r-- | ext/mysqlnd/mysqlnd_auth.c | 188 |
1 files changed, 185 insertions, 3 deletions
diff --git a/ext/mysqlnd/mysqlnd_auth.c b/ext/mysqlnd/mysqlnd_auth.c index 10c932a968..295b6a338b 100644 --- a/ext/mysqlnd/mysqlnd_auth.c +++ b/ext/mysqlnd/mysqlnd_auth.c @@ -360,7 +360,9 @@ mysqlnd_native_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self size_t * auth_data_len, MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd, const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len, - const MYSQLND_OPTIONS * const options, unsigned long mysql_flags + const MYSQLND_OPTIONS * const options, + const MYSQLND_NET_OPTIONS * const net_options, + unsigned long mysql_flags TSRMLS_DC) { zend_uchar * ret = NULL; @@ -418,7 +420,9 @@ mysqlnd_pam_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self, size_t * auth_data_len, MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd, const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len, - const MYSQLND_OPTIONS * const options, unsigned long mysql_flags + const MYSQLND_OPTIONS * const options, + const MYSQLND_NET_OPTIONS * const net_options, + unsigned long mysql_flags TSRMLS_DC) { zend_uchar * ret = NULL; @@ -442,7 +446,7 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin MYSQLND_VERSION_ID, MYSQLND_VERSION, "PHP License 3.01", - "Andrey Hristov <andrey@mysql.com>, Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>", + "Andrey Hristov <andrey@php.net>, Ulf Wendel <uw@php.net>, Georg Richter <georg@php.net>", { NULL, /* no statistics , will be filled later if there are some */ NULL, /* no statistics */ @@ -457,12 +461,190 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin }; +/******************************************* SHA256 Password ***********************************/ +#ifdef MYSQLND_HAVE_SSL +static void +mysqlnd_xor_string(char * dst, const size_t dst_len, const char * xor_str, const size_t xor_str_len) +{ + unsigned int i; + for (i = 0; i <= dst_len; ++i) { + dst[i] ^= xor_str[i % xor_str_len]; + } +} + + +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/err.h> + + +/* {{{ mysqlnd_sha256_get_rsa_key */ +static RSA * +mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn, + const MYSQLND_OPTIONS * const options, + const MYSQLND_NET_OPTIONS * const net_options + TSRMLS_DC) +{ + RSA * ret = NULL; + int len; + const char * fname = (net_options->sha256_server_public_key && net_options->sha256_server_public_key[0] != '\0')? + net_options->sha256_server_public_key: + MYSQLND_G(sha256_server_public_key); + php_stream * stream; + DBG_ENTER("mysqlnd_sha256_get_rsa_key"); + + if (!fname || fname[0] == '\0') { + MYSQLND_PACKET_SHA256_PK_REQUEST * pk_req_packet = NULL; + MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE * pk_resp_packet = NULL; + + do { + DBG_INF("requesting the public key from the server"); + pk_req_packet = conn->protocol->m.get_sha256_pk_request_packet(conn->protocol, FALSE TSRMLS_CC); + if (!pk_req_packet) { + SET_OOM_ERROR(*conn->error_info); + break; + } + pk_resp_packet = conn->protocol->m.get_sha256_pk_request_response_packet(conn->protocol, FALSE TSRMLS_CC); + if (!pk_resp_packet) { + SET_OOM_ERROR(*conn->error_info); + PACKET_FREE(pk_req_packet); + break; + } + + if (! PACKET_WRITE(pk_req_packet, conn)) { + DBG_ERR_FMT("Error while sending public key request packet"); + php_error(E_WARNING, "Error while sending public key request packet. PID=%d", getpid()); + CONN_SET_STATE(conn, CONN_QUIT_SENT); + break; + } + if (FAIL == PACKET_READ(pk_resp_packet, conn) || NULL == pk_resp_packet->public_key) { + DBG_ERR_FMT("Error while receiving public key"); + php_error(E_WARNING, "Error while receiving public key. PID=%d", getpid()); + CONN_SET_STATE(conn, CONN_QUIT_SENT); + break; + } + DBG_INF_FMT("Public key(%d):\n%s", pk_resp_packet->public_key_len, pk_resp_packet->public_key); + /* now extract the public key */ + { + BIO * bio = BIO_new_mem_buf(pk_resp_packet->public_key, pk_resp_packet->public_key_len); + ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); + BIO_free(bio); + } + } while (0); + PACKET_FREE(pk_req_packet); + PACKET_FREE(pk_resp_packet); + + DBG_INF_FMT("ret=%p", ret); + DBG_RETURN(ret); + + SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, + "sha256_server_public_key is not set for the connection or as mysqlnd.sha256_server_public_key"); + DBG_ERR("server_public_key is not set"); + DBG_RETURN(NULL); + } else { + char * key_str = NULL; + stream = php_stream_open_wrapper((char *) fname, "rb", REPORT_ERRORS, NULL); + + if (stream) { + if ((len = php_stream_copy_to_mem(stream, &key_str, PHP_STREAM_COPY_ALL, 0)) >= 0 ) { + BIO * bio = BIO_new_mem_buf(key_str, len); + ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (key_str) { + DBG_INF_FMT("Public key:%*.s", len, key_str); + efree(key_str); + } + } + php_stream_free(stream, PHP_STREAM_FREE_CLOSE); + } + DBG_RETURN(ret) +} +/* }}} */ + + +/* {{{ mysqlnd_sha256_auth_get_auth_data */ +static zend_uchar * +mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self, + size_t * auth_data_len, + MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd, + const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len, + const MYSQLND_OPTIONS * const options, + const MYSQLND_NET_OPTIONS * const net_options, + unsigned long mysql_flags + TSRMLS_DC) +{ + RSA * server_public_key; + zend_uchar * ret = NULL; + DBG_ENTER("mysqlnd_sha256_auth_get_auth_data"); + DBG_INF_FMT("salt(%d)=[%.*s]", auth_plugin_data_len, auth_plugin_data_len, auth_plugin_data); + + *auth_data_len = 0; + + server_public_key = mysqlnd_sha256_get_rsa_key(conn, options, net_options TSRMLS_CC); + + if (server_public_key) { + int server_public_key_len; + char xor_str[passwd_len + 1]; + memcpy(xor_str, passwd, passwd_len); + xor_str[passwd_len] = '\0'; + mysqlnd_xor_string(xor_str, passwd_len, (char *) auth_plugin_data, auth_plugin_data_len); + + server_public_key_len = RSA_size(server_public_key); + /* + Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len. + RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here: + http://www.openssl.org/docs/crypto/RSA_public_encrypt.html + */ + if ((size_t) server_public_key_len - 41 <= passwd_len) { + /* password message is to long */ + SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long"); + DBG_ERR("password is too long"); + DBG_RETURN(NULL); + } + + *auth_data_len = server_public_key_len; + ret = malloc(*auth_data_len); + RSA_public_encrypt(passwd_len + 1, (zend_uchar *) xor_str, ret, server_public_key, RSA_PKCS1_OAEP_PADDING); + } + + DBG_RETURN(ret); +} +/* }}} */ + + +static struct st_mysqlnd_authentication_plugin mysqlnd_sha256_authentication_plugin = +{ + { + MYSQLND_PLUGIN_API_VERSION, + "auth_plugin_sha256_password", + MYSQLND_VERSION_ID, + MYSQLND_VERSION, + "PHP License 3.01", + "Andrey Hristov <andrey@mysql.com>, Ulf Wendel <uwendel@mysql.com>", + { + NULL, /* no statistics , will be filled later if there are some */ + NULL, /* no statistics */ + }, + { + NULL /* plugin shutdown */ + } + }, + {/* methods */ + mysqlnd_sha256_auth_get_auth_data + } +}; +#endif + /* {{{ mysqlnd_register_builtin_authentication_plugins */ void mysqlnd_register_builtin_authentication_plugins(TSRMLS_D) { mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_native_auth_plugin TSRMLS_CC); mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_pam_authentication_plugin TSRMLS_CC); +#ifdef MYSQLND_HAVE_SSL + mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_sha256_authentication_plugin TSRMLS_CC); +#endif } /* }}} */ |