From b2d5243e97b7395d38bd6afc4767199703cf7f7c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 Mar 2006 16:41:14 +0100 Subject: [PATCH 1/3] Cleanup SSL implementation Remove duplicate code Merge common functions Enforce MySQL coding standard include/violite.h: Cleanup SSL implementation sql-common/client.c: Cleanup SSL implementation sql/mysql_priv.h: Cleanup SSL implementation sql/mysqld.cc: Cleanup SSL implementation sql/sql_acl.cc: Cleanup SSL implementation vio/vio.c: Cleanup SSL implementation vio/vio_priv.h: Cleanup SSL implementation vio/viossl.c: Cleanup SSL implementation vio/viosslfactories.c: Cleanup SSL implementation --- include/violite.h | 21 +-- sql-common/client.c | 106 ++++++++++--- sql/mysql_priv.h | 2 +- sql/mysqld.cc | 7 +- sql/sql_acl.cc | 4 +- vio/vio.c | 16 +- vio/vio_priv.h | 20 +-- vio/viossl.c | 342 ++++++++++++------------------------------ vio/viosslfactories.c | 307 ++++++++++++++++--------------------- 9 files changed, 336 insertions(+), 489 deletions(-) diff --git a/include/violite.h b/include/violite.h index b48f3724f5b..b2a5f1640a5 100644 --- a/include/violite.h +++ b/include/violite.h @@ -105,33 +105,22 @@ void vio_timeout(Vio *vio,uint which, uint timeout); #include #include -struct st_VioSSLAcceptorFd +struct st_VioSSLFd { SSL_CTX *ssl_context; - SSL_METHOD *ssl_method; - struct st_VioSSLAcceptorFd *session_id_context; }; -/* One copy for client */ -struct st_VioSSLConnectorFd -{ - SSL_CTX *ssl_context; - /* function pointers which are only once for SSL client */ - SSL_METHOD *ssl_method; -}; +int sslaccept(struct st_VioSSLFd*, Vio *, long timeout); +int sslconnect(struct st_VioSSLFd*, Vio *, long timeout); -int sslaccept(struct st_VioSSLAcceptorFd*, Vio *, long timeout); -int sslconnect(struct st_VioSSLConnectorFd*, Vio *, long timeout); - -struct st_VioSSLConnectorFd +struct st_VioSSLFd *new_VioSSLConnectorFd(const char *key_file, const char *cert_file, const char *ca_file, const char *ca_path, const char *cipher); -struct st_VioSSLAcceptorFd +struct st_VioSSLFd *new_VioSSLAcceptorFd(const char *key_file, const char *cert_file, const char *ca_file,const char *ca_path, const char *cipher); -Vio *new_VioSSL(struct st_VioSSLAcceptorFd *fd, Vio *sd, int state); #endif /* HAVE_OPENSSL */ #ifdef HAVE_SMEM diff --git a/sql-common/client.c b/sql-common/client.c index 2d826df0662..21eb58adf01 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1514,8 +1514,7 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) , static void mysql_ssl_free(MYSQL *mysql __attribute__((unused))) { - struct st_VioSSLConnectorFd *st= - (struct st_VioSSLConnectorFd*) mysql->connector_fd; + struct st_VioSSLFd *ssl_fd= (struct st_VioSSLFd*) mysql->connector_fd; DBUG_ENTER("mysql_ssl_free"); my_free(mysql->options.ssl_key, MYF(MY_ALLOW_ZERO_PTR)); @@ -1523,8 +1522,8 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused))) my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->options.ssl_cipher, MYF(MY_ALLOW_ZERO_PTR)); - if (st) - SSL_CTX_free(st->ssl_context); + if (ssl_fd) + SSL_CTX_free(ssl_fd->ssl_context); my_free(mysql->connector_fd,MYF(MY_ALLOW_ZERO_PTR)); mysql->options.ssl_key = 0; mysql->options.ssl_cert = 0; @@ -1568,6 +1567,63 @@ static MYSQL_METHODS client_methods= #endif }; +int ssl_verify_server_cert(Vio *vio, const char* server_host) +{ + SSL *ssl; + X509 *server_cert; + char *cp1, *cp2; + char buf[256]; + DBUG_ENTER("ssl_verify_server_cert"); + DBUG_PRINT("enter", ("server_host: %s", server_host)); + + if (!(ssl= (SSL*)vio->ssl_arg)) + { + DBUG_PRINT("error", ("No SSL pointer found")); + return 1; + } + + if (!server_host) + { + DBUG_PRINT("error", ("No server hostname supplied")); + return 1; + } + + if (!(server_cert= SSL_get_peer_certificate(ssl))) + { + DBUG_PRINT("error", ("Could not get server certificate")); + return 1; + } + + /* + We already know that the certificate exchanged was valid; the SSL library + handled that. Now we need to verify that the contents of the certificate + are what we expect. + */ + + X509_NAME_oneline(X509_get_subject_name(server_cert), buf, sizeof(buf)); + X509_free (server_cert); + +// X509_NAME_get_text_by_NID(x509_get_subject_name(server_cert), NID_commonName, buf, sizeof(buf));... does the same thing + + DBUG_PRINT("info", ("hostname in cert: %s", buf)); + cp1 = strstr(buf, "/CN="); + if (cp1) + { + cp1 += 4; // Skip the "/CN=" that we found + cp2 = strchr(cp1, '/'); + if (cp2) + *cp2 = '\0'; + DBUG_PRINT("info", ("Server hostname in cert: ", cp1)); + if (!strcmp(cp1, server_host)) + { + /* Success */ + DBUG_RETURN(0); + } + } + DBUG_PRINT("error", ("SSL certificate validation failure")); + DBUG_RETURN(1); +} + MYSQL * CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, @@ -2013,37 +2069,53 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, mysql->client_flag=client_flag; #ifdef HAVE_OPENSSL - /* - Oops.. are we careful enough to not send ANY information without - encryption? - */ if (client_flag & CLIENT_SSL) { + /* Do the SSL layering. */ struct st_mysql_options *options= &mysql->options; + struct st_VioSSLFd *ssl_fd; + + /* + Send client_flag, max_packet_size - unencrypted otherwise + the server does not know we want to do SSL + */ if (my_net_write(net,buff,(uint) (end-buff)) || net_flush(net)) { set_mysql_error(mysql, CR_SERVER_LOST, unknown_sqlstate); goto error; } - /* Do the SSL layering. */ - if (!(mysql->connector_fd= - (gptr) new_VioSSLConnectorFd(options->ssl_key, - options->ssl_cert, - options->ssl_ca, - options->ssl_capath, - options->ssl_cipher))) + + /* Create the VioSSLConnectorFd - init SSL and load certs */ + if (!(ssl_fd= new_VioSSLConnectorFd(options->ssl_key, + options->ssl_cert, + options->ssl_ca, + options->ssl_capath, + options->ssl_cipher))) { set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate); goto error; } + mysql->connector_fd= (void*)ssl_fd; + + /* Connect to the server */ DBUG_PRINT("info", ("IO layer change in progress...")); - if (sslconnect((struct st_VioSSLConnectorFd*)(mysql->connector_fd), - mysql->net.vio, (long) (mysql->options.connect_timeout))) + if (sslconnect(ssl_fd, mysql->net.vio, + (long) (mysql->options.connect_timeout))) { set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate); goto error; } DBUG_PRINT("info", ("IO layer change done!")); + +#if 0 + /* Verify server cert */ + if (mysql->options.ssl_verify_cert && + ssl_verify_server_cert(mysql->net.vio, mysql->host)) + { + set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate); + goto error; + } +#endif } #endif /* HAVE_OPENSSL */ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2c817ae54c2..dfb5d6d22cc 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1258,7 +1258,7 @@ extern pthread_t signal_thread; #endif #ifdef HAVE_OPENSSL -extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd; +extern struct st_VioSSLFd * ssl_acceptor_fd; #endif /* HAVE_OPENSSL */ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 37a135fa063..e0be383a9ad 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -599,7 +599,7 @@ static void openssl_lock(int, openssl_lock_t *, const char *, int); static unsigned long openssl_id_function(); #endif char *des_key_file; -struct st_VioSSLAcceptorFd *ssl_acceptor_fd; +struct st_VioSSLFd *ssl_acceptor_fd; #endif /* HAVE_OPENSSL */ @@ -1110,7 +1110,10 @@ void clean_up(bool print_message) #endif #ifdef HAVE_OPENSSL if (ssl_acceptor_fd) - my_free((gptr) ssl_acceptor_fd, MYF(MY_ALLOW_ZERO_PTR)); + { + SSL_CTX_free(ssl_acceptor_fd->ssl_context); + my_free((gptr) ssl_acceptor_fd, MYF(0)); + } #endif /* HAVE_OPENSSL */ #ifdef USE_REGEX my_regex_end(); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 88c3c86aacb..28d9fe0c532 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -858,8 +858,8 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, if (acl_user->x509_issuer) { DBUG_PRINT("info",("checkpoint 3")); - char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); - DBUG_PRINT("info",("comparing issuers: '%s' and '%s'", + char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); + DBUG_PRINT("info",("comparing issuers: '%s' and '%s'", acl_user->x509_issuer, ptr)); if (strcmp(acl_user->x509_issuer, ptr)) { diff --git a/vio/vio.c b/vio/vio.c index bc286b2d2bb..21a824a4016 100644 --- a/vio/vio.c +++ b/vio/vio.c @@ -88,19 +88,19 @@ static void vio_init(Vio* vio, enum enum_vio_type type, if (type == VIO_TYPE_SSL) { vio->viodelete =vio_delete; - vio->vioerrno =vio_ssl_errno; + vio->vioerrno =vio_errno; vio->read =vio_ssl_read; vio->write =vio_ssl_write; - vio->fastsend =vio_ssl_fastsend; - vio->viokeepalive =vio_ssl_keepalive; - vio->should_retry =vio_ssl_should_retry; - vio->was_interrupted=vio_ssl_was_interrupted; + vio->fastsend =vio_fastsend; + vio->viokeepalive =vio_keepalive; + vio->should_retry =vio_should_retry; + vio->was_interrupted=vio_was_interrupted; vio->vioclose =vio_ssl_close; - vio->peer_addr =vio_ssl_peer_addr; - vio->in_addr =vio_ssl_in_addr; + vio->peer_addr =vio_peer_addr; + vio->in_addr =vio_in_addr; vio->vioblocking =vio_ssl_blocking; vio->is_blocking =vio_is_blocking; - vio->timeout =vio_ssl_timeout; + vio->timeout =vio_timeout; } else /* default is VIO_TYPE_TCPIP */ #endif /* HAVE_OPENSSL */ diff --git a/vio/vio_priv.h b/vio/vio_priv.h index eb495025ddd..db331abdea8 100644 --- a/vio/vio_priv.h +++ b/vio/vio_priv.h @@ -30,28 +30,10 @@ void vio_ignore_timeout(Vio *vio, uint which, uint timeout); int vio_ssl_read(Vio *vio,gptr buf, int size); int vio_ssl_write(Vio *vio,const gptr buf,int size); -void vio_ssl_timeout(Vio *vio, uint which, uint timeout); -/* setsockopt TCP_NODELAY at IPPROTO_TCP level, when possible. */ -int vio_ssl_fastsend(Vio *vio); -/* setsockopt SO_KEEPALIVE at SOL_SOCKET level, when possible. */ -int vio_ssl_keepalive(Vio *vio, my_bool onoff); -/* Whenever we should retry the last read/write operation. */ -my_bool vio_ssl_should_retry(Vio *vio); -/* Check that operation was timed out */ -my_bool vio_ssl_was_interrupted(Vio *vio); /* When the workday is over... */ int vio_ssl_close(Vio *vio); -/* Return last error number */ -int vio_ssl_errno(Vio *vio); -my_bool vio_ssl_peer_addr(Vio *vio, char *buf, uint16 *port); -void vio_ssl_in_addr(Vio *vio, struct in_addr *in); + int vio_ssl_blocking(Vio *vio, my_bool set_blocking_mode, my_bool *old_mode); -/* Single copy for server */ -enum vio_ssl_acceptorfd_state -{ - state_connect = 1, - state_accept = 2 -}; #endif /* HAVE_OPENSSL */ diff --git a/vio/viossl.c b/vio/viossl.c index 1273814c551..33b8f716b9e 100644 --- a/vio/viossl.c +++ b/vio/viossl.c @@ -70,12 +70,6 @@ report_errors() } -int vio_ssl_errno(Vio *vio __attribute__((unused))) -{ - return socket_errno; /* On Win32 this mapped to WSAGetLastError() */ -} - - int vio_ssl_read(Vio * vio, gptr buf, int size) { int r; @@ -107,183 +101,51 @@ int vio_ssl_write(Vio * vio, const gptr buf, int size) } -int vio_ssl_fastsend(Vio * vio __attribute__((unused))) -{ - int r=0; - DBUG_ENTER("vio_ssl_fastsend"); - -#if defined(IPTOS_THROUGHPUT) && !defined(__EMX__) - { - int tos= IPTOS_THROUGHPUT; - r= setsockopt(vio->sd, IPPROTO_IP, IP_TOS, (void *) &tos, sizeof(tos)); - } -#endif /* IPTOS_THROUGHPUT && !__EMX__ */ - if (!r) - { -#ifdef __WIN__ - BOOL nodelay= 1; - r= setsockopt(vio->sd, IPPROTO_TCP, TCP_NODELAY, (const char*) &nodelay, - sizeof(nodelay)); -#else - int nodelay= 1; - r= setsockopt(vio->sd, IPPROTO_TCP, TCP_NODELAY, (void*) &nodelay, - sizeof(nodelay)); -#endif /* __WIN__ */ - } - if (r) - { - DBUG_PRINT("warning", ("Couldn't set socket option for fast send")); - r= -1; - } - DBUG_PRINT("exit", ("%d", r)); - DBUG_RETURN(r); -} - - -int vio_ssl_keepalive(Vio* vio, my_bool set_keep_alive) -{ - int r=0; - DBUG_ENTER("vio_ssl_keepalive"); - DBUG_PRINT("enter", ("sd: %d, set_keep_alive: %d", vio->sd, (int) - set_keep_alive)); - if (vio->type != VIO_TYPE_NAMEDPIPE) - { - uint opt = (set_keep_alive) ? 1 : 0; - r= setsockopt(vio->sd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, - sizeof(opt)); - } - DBUG_RETURN(r); -} - - -my_bool -vio_ssl_should_retry(Vio * vio __attribute__((unused))) -{ - int en = socket_errno; - return (en == SOCKET_EAGAIN || en == SOCKET_EINTR || - en == SOCKET_EWOULDBLOCK); -} - - -my_bool -vio_ssl_was_interrupted(Vio *vio __attribute__((unused))) -{ - int en= socket_errno; - return (en == SOCKET_EAGAIN || en == SOCKET_EINTR || - en == SOCKET_EWOULDBLOCK || en == SOCKET_ETIMEDOUT); -} - - int vio_ssl_close(Vio * vio) { - int r; + int r= 0; + SSL* ssl= (SSL*)vio->ssl_arg; DBUG_ENTER("vio_ssl_close"); - r=0; - if ((SSL*) vio->ssl_arg) + + if (ssl) { - r = SSL_shutdown((SSL*) vio->ssl_arg); - SSL_free((SSL*) vio->ssl_arg); + switch ((r= SSL_shutdown(ssl))) + { + case 1: /* Shutdown successful */ + break; + case 0: /* Shutdown not yet finished, call it again */ + if ((r= SSL_shutdown(ssl) >= 0)) + break; + /* Fallthrough */ + default: /* Shutdown failed */ + DBUG_PRINT("vio_error", ("SSL_shutdown() failed, error: %s", + SSL_get_error(ssl, r))); + break; + } + SSL_free(ssl); vio->ssl_arg= 0; } - if (vio->sd >= 0) - { - if (shutdown(vio->sd, 2)) - r= -1; - if (closesocket(vio->sd)) - r= -1; - } - if (r) - { - DBUG_PRINT("error", ("close() failed, error: %d",socket_errno)); - report_errors(); - /* FIXME: error handling (not critical for MySQL) */ - } - vio->type= VIO_CLOSED; - vio->sd= -1; - DBUG_RETURN(r); + DBUG_RETURN(vio_close(vio)); } -const char *vio_ssl_description(Vio * vio) +int sslaccept(struct st_VioSSLFd* ptr, Vio* vio, long timeout) { - return vio->desc; -} - -enum enum_vio_type vio_ssl_type(Vio* vio) -{ - return vio->type; -} - -my_socket vio_ssl_fd(Vio* vio) -{ - return vio->sd; -} - - -my_bool vio_ssl_peer_addr(Vio * vio, char *buf, uint16 *port) -{ - DBUG_ENTER("vio_ssl_peer_addr"); - DBUG_PRINT("enter", ("sd: %d", vio->sd)); - if (vio->localhost) - { - strmov(buf,"127.0.0.1"); - *port=0; - } - else - { - size_socket addrLen = sizeof(struct sockaddr); - if (getpeername(vio->sd, (struct sockaddr *) (& (vio->remote)), - &addrLen) != 0) - { - DBUG_PRINT("exit", ("getpeername, error: %d", socket_errno)); - DBUG_RETURN(1); - } -#ifdef TO_BE_FIXED - my_inet_ntoa(vio->remote.sin_addr,buf); - *port= 0; -#else - strmov(buf, "unknown"); - *port= 0; -#endif - } - DBUG_PRINT("exit", ("addr: %s", buf)); - DBUG_RETURN(0); -} - - -void vio_ssl_in_addr(Vio *vio, struct in_addr *in) -{ - DBUG_ENTER("vio_ssl_in_addr"); - if (vio->localhost) - bzero((char*) in, sizeof(*in)); - else - *in=vio->remote.sin_addr; - DBUG_VOID_RETURN; -} - - -/* - TODO: Add documentation -*/ - -int sslaccept(struct st_VioSSLAcceptorFd* ptr, Vio* vio, long timeout) -{ - char *str; - char buf[1024]; + SSL *ssl; X509* client_cert; my_bool unused; my_bool net_blocking; enum enum_vio_type old_type; DBUG_ENTER("sslaccept"); - DBUG_PRINT("enter", ("sd: %d ptr: Ox%p, timeout: %d", + DBUG_PRINT("enter", ("sd: %d ptr: %p, timeout: %d", vio->sd, ptr, timeout)); old_type= vio->type; - net_blocking = vio_is_blocking(vio); + net_blocking= vio_is_blocking(vio); vio_blocking(vio, 1, &unused); /* Must be called before reset */ - vio_reset(vio,VIO_TYPE_SSL,vio->sd,0,FALSE); - vio->ssl_arg= 0; - if (!(vio->ssl_arg= (void*) SSL_new(ptr->ssl_context))) + vio_reset(vio, VIO_TYPE_SSL, vio->sd, 0, FALSE); + + if (!(ssl= SSL_new(ptr->ssl_context))) { DBUG_PRINT("error", ("SSL_new failure")); report_errors(); @@ -291,121 +153,114 @@ int sslaccept(struct st_VioSSLAcceptorFd* ptr, Vio* vio, long timeout) vio_blocking(vio, net_blocking, &unused); DBUG_RETURN(1); } - DBUG_PRINT("info", ("ssl_: Ox%p timeout: %ld", - (SSL*) vio->ssl_arg, timeout)); - SSL_clear((SSL*) vio->ssl_arg); - SSL_SESSION_set_timeout(SSL_get_session((SSL*) vio->ssl_arg), timeout); - SSL_set_fd((SSL*) vio->ssl_arg,vio->sd); - SSL_set_accept_state((SSL*) vio->ssl_arg); - if (SSL_do_handshake((SSL*) vio->ssl_arg) < 1) + vio->ssl_arg= (void*)ssl; + DBUG_PRINT("info", ("ssl_: %p timeout: %ld", ssl, timeout)); + SSL_clear(ssl); + SSL_SESSION_set_timeout(SSL_get_session(ssl), timeout); + SSL_set_fd(ssl, vio->sd); + SSL_set_accept_state(ssl); + if (SSL_do_handshake(ssl) < 1) { DBUG_PRINT("error", ("SSL_do_handshake failure")); report_errors(); - SSL_free((SSL*) vio->ssl_arg); + SSL_free(ssl); vio->ssl_arg= 0; vio_reset(vio, old_type,vio->sd,0,FALSE); vio_blocking(vio, net_blocking, &unused); DBUG_RETURN(1); } + #ifndef DBUG_OFF - DBUG_PRINT("info",("SSL_get_cipher_name() = '%s'" - ,SSL_get_cipher_name((SSL*) vio->ssl_arg))); - client_cert = SSL_get_peer_certificate ((SSL*) vio->ssl_arg); - if (client_cert != NULL) { - DBUG_PRINT("info",("Client certificate:")); - str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0); - DBUG_PRINT("info",("\t subject: %s", str)); - free (str); + char buf[1024]; + DBUG_PRINT("info",("cipher_name= '%s'", SSL_get_cipher_name(ssl))); - str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0); - DBUG_PRINT("info",("\t issuer: %s", str)); - free (str); + if ((client_cert= SSL_get_peer_certificate (ssl))) + { + DBUG_PRINT("info",("Client certificate:")); + X509_NAME_oneline (X509_get_subject_name (client_cert), + buf, sizeof(buf)); + DBUG_PRINT("info",("\t subject: %s", buf)); - X509_free (client_cert); + X509_NAME_oneline (X509_get_issuer_name (client_cert), + buf, sizeof(buf)); + DBUG_PRINT("info",("\t issuer: %s", buf)); + + X509_free (client_cert); + } + else + DBUG_PRINT("info",("Client does not have certificate.")); + + if (SSL_get_shared_ciphers(ssl, buf, sizeof(buf))) + { + DBUG_PRINT("info",("shared_ciphers: '%s'", buf)); + } + else + DBUG_PRINT("info",("no shared ciphers!")); } - else - DBUG_PRINT("info",("Client does not have certificate.")); - - str=SSL_get_shared_ciphers((SSL*) vio->ssl_arg, buf, sizeof(buf)); - if (str) - { - DBUG_PRINT("info",("SSL_get_shared_ciphers() returned '%s'",str)); - } - else - { - DBUG_PRINT("info",("no shared ciphers!")); - } - #endif + DBUG_RETURN(0); } -int sslconnect(struct st_VioSSLConnectorFd* ptr, Vio* vio, long timeout) +int sslconnect(struct st_VioSSLFd* ptr, Vio* vio, long timeout) { - char *str; - X509* server_cert; + SSL *ssl; + X509 *server_cert; my_bool unused; my_bool net_blocking; - enum enum_vio_type old_type; + enum enum_vio_type old_type; + DBUG_ENTER("sslconnect"); - DBUG_PRINT("enter", ("sd: %d ptr: 0x%p ctx: 0x%p", - vio->sd,ptr,ptr->ssl_context)); + DBUG_PRINT("enter", ("sd: %d, ptr: %p, ctx: %p", + vio->sd, ptr, ptr->ssl_context)); old_type= vio->type; - net_blocking = vio_is_blocking(vio); + net_blocking= vio_is_blocking(vio); vio_blocking(vio, 1, &unused); /* Must be called before reset */ - vio_reset(vio,VIO_TYPE_SSL,vio->sd,0,FALSE); - vio->ssl_arg= 0; - if (!(vio->ssl_arg = SSL_new(ptr->ssl_context))) + vio_reset(vio, VIO_TYPE_SSL, vio->sd, 0, FALSE); + if (!(ssl= SSL_new(ptr->ssl_context))) { DBUG_PRINT("error", ("SSL_new failure")); report_errors(); - vio_reset(vio, old_type,vio->sd,0,FALSE); - vio_blocking(vio, net_blocking, &unused); + vio_reset(vio, old_type, vio->sd, 0, FALSE); + vio_blocking(vio, net_blocking, &unused); DBUG_RETURN(1); } - DBUG_PRINT("info", ("ssl_: 0x%p timeout: %ld", - (SSL*) vio->ssl_arg, timeout)); - SSL_clear((SSL*) vio->ssl_arg); - SSL_SESSION_set_timeout(SSL_get_session((SSL*) vio->ssl_arg), timeout); - SSL_set_fd ((SSL*) vio->ssl_arg, vio_ssl_fd(vio)); - SSL_set_connect_state((SSL*) vio->ssl_arg); - if (SSL_do_handshake((SSL*) vio->ssl_arg) < 1) + vio->ssl_arg= (void*)ssl; + DBUG_PRINT("info", ("ssl: %p, timeout: %ld", ssl, timeout)); + SSL_clear(ssl); + SSL_SESSION_set_timeout(SSL_get_session(ssl), timeout); + SSL_set_fd(ssl, vio->sd); + SSL_set_connect_state(ssl); + if (SSL_do_handshake(ssl) < 1) { DBUG_PRINT("error", ("SSL_do_handshake failure")); report_errors(); - SSL_free((SSL*) vio->ssl_arg); + SSL_free(ssl); vio->ssl_arg= 0; - vio_reset(vio, old_type,vio->sd,0,FALSE); + vio_reset(vio, old_type, vio->sd, 0, FALSE); vio_blocking(vio, net_blocking, &unused); DBUG_RETURN(1); - } + } #ifndef DBUG_OFF - DBUG_PRINT("info",("SSL_get_cipher_name() = '%s'" - ,SSL_get_cipher_name((SSL*) vio->ssl_arg))); - server_cert = SSL_get_peer_certificate ((SSL*) vio->ssl_arg); - if (server_cert != NULL) + DBUG_PRINT("info",("cipher_name: '%s'" , SSL_get_cipher_name(ssl))); + + if ((server_cert= SSL_get_peer_certificate (ssl))) { + char buf[256]; DBUG_PRINT("info",("Server certificate:")); - str = X509_NAME_oneline (X509_get_subject_name (server_cert), 0, 0); - DBUG_PRINT("info",("\t subject: %s", str)); - free(str); - - str = X509_NAME_oneline (X509_get_issuer_name (server_cert), 0, 0); - DBUG_PRINT("info",("\t issuer: %s", str)); - free(str); - - /* - We could do all sorts of certificate verification stuff here before - deallocating the certificate. - */ + X509_NAME_oneline(X509_get_subject_name(server_cert), buf, sizeof(buf)); + DBUG_PRINT("info",("\t subject: %s", buf)); + X509_NAME_oneline (X509_get_issuer_name(server_cert), buf, sizeof(buf)); + DBUG_PRINT("info",("\t issuer: %s", buf)); X509_free (server_cert); } else DBUG_PRINT("info",("Server does not have certificate.")); #endif + DBUG_RETURN(0); } @@ -414,21 +269,10 @@ int vio_ssl_blocking(Vio * vio __attribute__((unused)), my_bool set_blocking_mode, my_bool *old_mode) { + /* Mode is always blocking */ + *old_mode= 1; /* Return error if we try to change to non_blocking mode */ - *old_mode=1; /* Mode is always blocking */ - return set_blocking_mode ? 0 : 1; + return (set_blocking_mode ? 0 : 1); } - -void vio_ssl_timeout(Vio *vio __attribute__((unused)), - uint which __attribute__((unused)), - uint timeout __attribute__((unused))) -{ -#ifdef __WIN__ - ulong wait_timeout= (ulong) timeout * 1000; - (void) setsockopt(vio->sd, SOL_SOCKET, - which ? SO_SNDTIMEO : SO_RCVTIMEO, (char*) &wait_timeout, - sizeof(wait_timeout)); -#endif /* __WIN__ */ -} #endif /* HAVE_OPENSSL */ diff --git a/vio/viosslfactories.c b/vio/viosslfactories.c index 4ee27f1e491..c58fa449374 100644 --- a/vio/viosslfactories.c +++ b/vio/viosslfactories.c @@ -21,7 +21,6 @@ static bool ssl_algorithms_added = FALSE; static bool ssl_error_strings_loaded= FALSE; static int verify_depth = 0; -static int verify_error = X509_V_OK; static unsigned char dh512_p[]= { @@ -82,30 +81,31 @@ vio_set_cert_stuff(SSL_CTX *ctx, const char *cert_file, const char *key_file) DBUG_ENTER("vio_set_cert_stuff"); DBUG_PRINT("enter", ("ctx: %p, cert_file: %s, key_file: %s", ctx, cert_file, key_file)); - if (cert_file != NULL) + if (cert_file) { - if (SSL_CTX_use_certificate_file(ctx,cert_file,SSL_FILETYPE_PEM) <= 0) + if (SSL_CTX_use_certificate_file(ctx, cert_file, SSL_FILETYPE_PEM) <= 0) { - DBUG_PRINT("error",("unable to get certificate from '%s'\n",cert_file)); + DBUG_PRINT("error",("unable to get certificate from '%s'\n", cert_file)); /* FIX stderr */ fprintf(stderr,"Error when connection to server using SSL:"); ERR_print_errors_fp(stderr); fprintf(stderr,"Unable to get certificate from '%s'\n", cert_file); fflush(stderr); - DBUG_RETURN(0); + DBUG_RETURN(1); } - if (key_file == NULL) - key_file = cert_file; - if (SSL_CTX_use_PrivateKey_file(ctx,key_file, - SSL_FILETYPE_PEM) <= 0) + + if (!key_file) + key_file= cert_file; + + if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) <= 0) { - DBUG_PRINT("error", ("unable to get private key from '%s'\n",key_file)); + DBUG_PRINT("error", ("unable to get private key from '%s'\n", key_file)); /* FIX stderr */ fprintf(stderr,"Error when connection to server using SSL:"); ERR_print_errors_fp(stderr); fprintf(stderr,"Unable to get private key from '%s'\n", cert_file); - fflush(stderr); - DBUG_RETURN(0); + fflush(stderr); + DBUG_RETURN(1); } /* @@ -116,45 +116,45 @@ vio_set_cert_stuff(SSL_CTX *ctx, const char *cert_file, const char *key_file) { DBUG_PRINT("error", ("Private key does not match the certificate public key\n")); - DBUG_RETURN(0); + DBUG_RETURN(1); } } - DBUG_RETURN(1); + DBUG_RETURN(0); } static int vio_verify_callback(int ok, X509_STORE_CTX *ctx) { - char buf[256]; - X509* err_cert; - int err,depth; + char buf[256]; + X509 *err_cert; DBUG_ENTER("vio_verify_callback"); - DBUG_PRINT("enter", ("ok: %d, ctx: 0x%p", ok, ctx)); - err_cert=X509_STORE_CTX_get_current_cert(ctx); - err= X509_STORE_CTX_get_error(ctx); - depth= X509_STORE_CTX_get_error_depth(ctx); + DBUG_PRINT("enter", ("ok: %d, ctx: %p", ok, ctx)); - X509_NAME_oneline(X509_get_subject_name(err_cert),buf,sizeof(buf)); + err_cert= X509_STORE_CTX_get_current_cert(ctx); + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); + DBUG_PRINT("info", ("cert: %s", buf)); if (!ok) { - DBUG_PRINT("error",("verify error: num: %d : '%s'\n",err, + int err, depth; + err= X509_STORE_CTX_get_error(ctx); + depth= X509_STORE_CTX_get_error_depth(ctx); + + DBUG_PRINT("error",("verify error: %d, '%s'",err, X509_verify_cert_error_string(err))); + /* + Approve cert if depth is greater then "verify_depth", currently + verify_depth is always 0 and there is no way to increase it. + */ if (verify_depth >= depth) - { - ok=1; - verify_error=X509_V_OK; - } - else - { - verify_error=X509_V_ERR_CERT_CHAIN_TOO_LONG; - } + ok= 1; } - switch (ctx->error) { + switch (ctx->error) + { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: - X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert),buf,256); - DBUG_PRINT("info",("issuer= %s\n",buf)); + X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256); + DBUG_PRINT("info",("issuer= %s\n", buf)); break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: @@ -198,193 +198,150 @@ static void netware_ssl_init() #endif /* __NETWARE__ */ -/************************ VioSSLConnectorFd **********************************/ -/* - TODO: - Add option --verify to mysql to be able to change verification mode -*/ - -struct st_VioSSLConnectorFd * -new_VioSSLConnectorFd(const char* key_file, - const char* cert_file, - const char* ca_file, - const char* ca_path, - const char* cipher) +static void check_ssl_init() { - int verify = SSL_VERIFY_NONE; - struct st_VioSSLConnectorFd* ptr; - int result; - DH *dh; - DBUG_ENTER("new_VioSSLConnectorFd"); - - if (!(ptr=((struct st_VioSSLConnectorFd*) - my_malloc(sizeof(struct st_VioSSLConnectorFd),MYF(0))))) - DBUG_RETURN(0); - - ptr->ssl_context= 0; - ptr->ssl_method= 0; - /* FIXME: constants! */ - if (!ssl_algorithms_added) { - DBUG_PRINT("info", ("todo: OpenSSL_add_all_algorithms()")); - ssl_algorithms_added = TRUE; + ssl_algorithms_added= TRUE; SSL_library_init(); OpenSSL_add_all_algorithms(); + } + #ifdef __NETWARE__ + /* MASV, should it be done everytime? */ netware_ssl_init(); #endif if (!ssl_error_strings_loaded) { - DBUG_PRINT("info", ("todo:SSL_load_error_strings()")); - ssl_error_strings_loaded = TRUE; + ssl_error_strings_loaded= TRUE; SSL_load_error_strings(); } - ptr->ssl_method = TLSv1_client_method(); - ptr->ssl_context = SSL_CTX_new(ptr->ssl_method); - DBUG_PRINT("info", ("ssl_context: %p",ptr->ssl_context)); - if (ptr->ssl_context == 0) +} + +/************************ VioSSLFd **********************************/ +struct st_VioSSLFd * +new_VioSSLFd(const char *key_file, const char *cert_file, + const char *ca_file, const char *ca_path, + const char *cipher, SSL_METHOD *method) +{ + DH *dh; + struct st_VioSSLFd *ssl_fd; + DBUG_ENTER("new_VioSSLFd"); + + check_ssl_init(); + + if (!(ssl_fd= ((struct st_VioSSLFd*) + my_malloc(sizeof(struct st_VioSSLFd),MYF(0))))) + DBUG_RETURN(0); + + if (!(ssl_fd->ssl_context= SSL_CTX_new(method))) { DBUG_PRINT("error", ("SSL_CTX_new failed")); report_errors(); - goto ctor_failure; + my_free((void*)ssl_fd,MYF(0)); + DBUG_RETURN(0); } - /* - SSL_CTX_set_options - SSL_CTX_set_info_callback - */ - if (cipher) + + /* Set the ciphers that can be used */ + if (cipher && SSL_CTX_set_cipher_list(ssl_fd->ssl_context, cipher)) { - result=SSL_CTX_set_cipher_list(ptr->ssl_context, cipher); - DBUG_PRINT("info",("SSL_set_cipher_list() returned %d",result)); + DBUG_PRINT("error", ("failed to set ciphers to use")); + report_errors(); + my_free((void*)ssl_fd,MYF(0)); + DBUG_RETURN(0); } - SSL_CTX_set_verify(ptr->ssl_context, verify, vio_verify_callback); - if (vio_set_cert_stuff(ptr->ssl_context, cert_file, key_file) == -1) + + if (vio_set_cert_stuff(ssl_fd->ssl_context, cert_file, key_file)) { DBUG_PRINT("error", ("vio_set_cert_stuff failed")); report_errors(); - goto ctor_failure; + my_free((void*)ssl_fd,MYF(0)); + DBUG_RETURN(0); } - if (SSL_CTX_load_verify_locations( ptr->ssl_context, ca_file,ca_path) == 0) + + if (SSL_CTX_load_verify_locations(ssl_fd->ssl_context, ca_file, ca_path) == 0) { DBUG_PRINT("warning", ("SSL_CTX_load_verify_locations failed")); - if (SSL_CTX_set_default_verify_paths(ptr->ssl_context) == 0) + if (SSL_CTX_set_default_verify_paths(ssl_fd->ssl_context) == 0) { DBUG_PRINT("error", ("SSL_CTX_set_default_verify_paths failed")); report_errors(); - goto ctor_failure; + my_free((void*)ssl_fd,MYF(0)); + DBUG_RETURN(0); } - } + } /* DH stuff */ dh=get_dh512(); - SSL_CTX_set_tmp_dh(ptr->ssl_context,dh); + SSL_CTX_set_tmp_dh(ssl_fd->ssl_context, dh); DH_free(dh); - DBUG_RETURN(ptr); -ctor_failure: - DBUG_PRINT("exit", ("there was an error")); - my_free((gptr)ptr,MYF(0)); - DBUG_RETURN(0); + DBUG_PRINT("exit", ("OK 1")); + + DBUG_RETURN(ssl_fd); +} + + +/************************ VioSSLConnectorFd **********************************/ +struct st_VioSSLFd * +new_VioSSLConnectorFd(const char *key_file, const char *cert_file, + const char *ca_file, const char *ca_path, + const char *cipher) +{ + struct st_VioSSLFd *ssl_fd; + int verify= SSL_VERIFY_NONE; + if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file, + ca_path, cipher, TLSv1_client_method()))) + { + return 0; + } + /* Init the the VioSSLFd as a "connector" ie. the client side */ + + /* + The verify_callback function is used to control the behaviour + when the SSL_VERIFY_PEER flag is set. Here it is SSL_VERIFY_NONE + and thus callback is set to NULL + */ + SSL_CTX_set_verify(ssl_fd->ssl_context, verify, NULL); + + return ssl_fd; } /************************ VioSSLAcceptorFd **********************************/ -/* - TODO: - Add option --verify to mysqld to be able to change verification mode -*/ -struct st_VioSSLAcceptorFd* -new_VioSSLAcceptorFd(const char *key_file, - const char *cert_file, - const char *ca_file, - const char *ca_path, +struct st_VioSSLFd* +new_VioSSLAcceptorFd(const char *key_file, const char *cert_file, + const char *ca_file, const char *ca_path, const char *cipher) { - int verify = (SSL_VERIFY_PEER | - SSL_VERIFY_CLIENT_ONCE); - struct st_VioSSLAcceptorFd* ptr; - int result; - DH *dh; - DBUG_ENTER("new_VioSSLAcceptorFd"); - - ptr= ((struct st_VioSSLAcceptorFd*) - my_malloc(sizeof(struct st_VioSSLAcceptorFd),MYF(0))); - ptr->ssl_context=0; - ptr->ssl_method=0; - /* FIXME: constants! */ - ptr->session_id_context= ptr; - - if (!ssl_algorithms_added) + struct st_VioSSLFd *ssl_fd; + int verify= SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; + if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file, + ca_path, cipher, TLSv1_server_method()))) { - DBUG_PRINT("info", ("todo: OpenSSL_add_all_algorithms()")); - ssl_algorithms_added = TRUE; - SSL_library_init(); - OpenSSL_add_all_algorithms(); + return 0; + } + /* Init the the VioSSLFd as a "acceptor" ie. the server side */ - } -#ifdef __NETWARE__ - netware_ssl_init(); -#endif - - if (!ssl_error_strings_loaded) - { - DBUG_PRINT("info", ("todo: SSL_load_error_strings()")); - ssl_error_strings_loaded = TRUE; - SSL_load_error_strings(); - } - ptr->ssl_method= TLSv1_server_method(); - ptr->ssl_context= SSL_CTX_new(ptr->ssl_method); - if (ptr->ssl_context == 0) - { - DBUG_PRINT("error", ("SSL_CTX_new failed")); - report_errors(); - goto ctor_failure; - } - if (cipher) - { - result=SSL_CTX_set_cipher_list(ptr->ssl_context, cipher); - DBUG_PRINT("info",("SSL_set_cipher_list() returned %d",result)); - } - /* SSL_CTX_set_quiet_shutdown(ctx,1); */ - SSL_CTX_sess_set_cache_size(ptr->ssl_context,128); - - /* DH? */ - SSL_CTX_set_verify(ptr->ssl_context, verify, vio_verify_callback); - SSL_CTX_set_session_id_context(ptr->ssl_context, - (const uchar*) &(ptr->session_id_context), - sizeof(ptr->session_id_context)); + /* Set max number of cached sessions, returns the previous size */ + SSL_CTX_sess_set_cache_size(ssl_fd->ssl_context, 128); /* - SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(CAfile)); + The verify_callback function is used to control the behaviour + when the SSL_VERIFY_PEER flag is set. */ - if (vio_set_cert_stuff(ptr->ssl_context, cert_file, key_file) == -1) - { - DBUG_PRINT("error", ("vio_set_cert_stuff failed")); - report_errors(); - goto ctor_failure; - } - if (SSL_CTX_load_verify_locations( ptr->ssl_context, ca_file, ca_path) == 0) - { - DBUG_PRINT("warning", ("SSL_CTX_load_verify_locations failed")); - if (SSL_CTX_set_default_verify_paths(ptr->ssl_context)==0) - { - DBUG_PRINT("error", ("SSL_CTX_set_default_verify_paths failed")); - report_errors(); - goto ctor_failure; - } - } - /* DH stuff */ - dh=get_dh512(); - SSL_CTX_set_tmp_dh(ptr->ssl_context,dh); - DH_free(dh); - DBUG_RETURN(ptr); + SSL_CTX_set_verify(ssl_fd->ssl_context, verify, vio_verify_callback); -ctor_failure: - DBUG_PRINT("exit", ("there was an error")); - my_free((gptr) ptr,MYF(0)); - DBUG_RETURN(0); + /* + Set session_id - an identifier for this server session + Use the ssl_fd pointer + */ + SSL_CTX_set_session_id_context(ssl_fd->ssl_context, + ssl_fd, + sizeof(ssl_fd)); + + return ssl_fd; } #endif /* HAVE_OPENSSL */ From c3babee88a1384a8fa4eee64a3eab610ef728661 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 12 Apr 2006 14:06:05 +0200 Subject: [PATCH 2/3] Fix up patch sql-common/client.c: Remove the ssl_verify_server_cert will be added in separate patch vio/viossl.c: Fix coding standard vio/viosslfactories.c: Remove comment --- sql-common/client.c | 68 +------------------------------------------ vio/viossl.c | 24 +++++++-------- vio/viosslfactories.c | 1 - 3 files changed, 13 insertions(+), 80 deletions(-) diff --git a/sql-common/client.c b/sql-common/client.c index da38d022c4d..b24b92014d6 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1567,64 +1567,6 @@ static MYSQL_METHODS client_methods= #endif }; -int ssl_verify_server_cert(Vio *vio, const char* server_host) -{ - SSL *ssl; - X509 *server_cert; - char *cp1, *cp2; - char buf[256]; - DBUG_ENTER("ssl_verify_server_cert"); - DBUG_PRINT("enter", ("server_host: %s", server_host)); - - if (!(ssl= (SSL*)vio->ssl_arg)) - { - DBUG_PRINT("error", ("No SSL pointer found")); - return 1; - } - - if (!server_host) - { - DBUG_PRINT("error", ("No server hostname supplied")); - return 1; - } - - if (!(server_cert= SSL_get_peer_certificate(ssl))) - { - DBUG_PRINT("error", ("Could not get server certificate")); - return 1; - } - - /* - We already know that the certificate exchanged was valid; the SSL library - handled that. Now we need to verify that the contents of the certificate - are what we expect. - */ - - X509_NAME_oneline(X509_get_subject_name(server_cert), buf, sizeof(buf)); - X509_free (server_cert); - -// X509_NAME_get_text_by_NID(x509_get_subject_name(server_cert), NID_commonName, buf, sizeof(buf));... does the same thing - - DBUG_PRINT("info", ("hostname in cert: %s", buf)); - cp1 = strstr(buf, "/CN="); - if (cp1) - { - cp1 += 4; // Skip the "/CN=" that we found - cp2 = strchr(cp1, '/'); - if (cp2) - *cp2 = '\0'; - DBUG_PRINT("info", ("Server hostname in cert: ", cp1)); - if (!strcmp(cp1, server_host)) - { - /* Success */ - DBUG_RETURN(0); - } - } - DBUG_PRINT("error", ("SSL certificate validation failure")); - DBUG_RETURN(1); -} - - MYSQL * CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, const char *passwd, const char *db, @@ -2107,15 +2049,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, } DBUG_PRINT("info", ("IO layer change done!")); -#if 0 - /* Verify server cert */ - if (mysql->options.ssl_verify_cert && - ssl_verify_server_cert(mysql->net.vio, mysql->host)) - { - set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate); - goto error; - } -#endif + /* TODO Verify server cert */ } #endif /* HAVE_OPENSSL */ diff --git a/vio/viossl.c b/vio/viossl.c index 33b8f716b9e..aa4cdda9f01 100644 --- a/vio/viossl.c +++ b/vio/viossl.c @@ -54,12 +54,12 @@ static void report_errors() { unsigned long l; - const char* file; - const char* data; - int line,flags; + const char *file; + const char *data; + int line,flags; DBUG_ENTER("report_errors"); - while ((l=ERR_get_error_line_data(&file,&line,&data,&flags))) + while ((l= ERR_get_error_line_data(&file,&line,&data,&flags))) { char buf[512]; DBUG_PRINT("error", ("OpenSSL: %s:%s:%d:%s\n", ERR_error_string(l,buf), @@ -70,7 +70,7 @@ report_errors() } -int vio_ssl_read(Vio * vio, gptr buf, int size) +int vio_ssl_read(Vio *vio, gptr buf, int size) { int r; DBUG_ENTER("vio_ssl_read"); @@ -88,7 +88,7 @@ int vio_ssl_read(Vio * vio, gptr buf, int size) } -int vio_ssl_write(Vio * vio, const gptr buf, int size) +int vio_ssl_write(Vio *vio, const gptr buf, int size) { int r; DBUG_ENTER("vio_ssl_write"); @@ -101,10 +101,10 @@ int vio_ssl_write(Vio * vio, const gptr buf, int size) } -int vio_ssl_close(Vio * vio) +int vio_ssl_close(Vio *vio) { int r= 0; - SSL* ssl= (SSL*)vio->ssl_arg; + SSL *ssl= (SSL*)vio->ssl_arg; DBUG_ENTER("vio_ssl_close"); if (ssl) @@ -129,10 +129,10 @@ int vio_ssl_close(Vio * vio) } -int sslaccept(struct st_VioSSLFd* ptr, Vio* vio, long timeout) +int sslaccept(struct st_VioSSLFd *ptr, Vio *vio, long timeout) { SSL *ssl; - X509* client_cert; + X509 *client_cert; my_bool unused; my_bool net_blocking; enum enum_vio_type old_type; @@ -204,7 +204,7 @@ int sslaccept(struct st_VioSSLFd* ptr, Vio* vio, long timeout) } -int sslconnect(struct st_VioSSLFd* ptr, Vio* vio, long timeout) +int sslconnect(struct st_VioSSLFd *ptr, Vio *vio, long timeout) { SSL *ssl; X509 *server_cert; @@ -265,7 +265,7 @@ int sslconnect(struct st_VioSSLFd* ptr, Vio* vio, long timeout) } -int vio_ssl_blocking(Vio * vio __attribute__((unused)), +int vio_ssl_blocking(Vio *vio __attribute__((unused)), my_bool set_blocking_mode, my_bool *old_mode) { diff --git a/vio/viosslfactories.c b/vio/viosslfactories.c index c58fa449374..d6356f1adca 100644 --- a/vio/viosslfactories.c +++ b/vio/viosslfactories.c @@ -209,7 +209,6 @@ static void check_ssl_init() } #ifdef __NETWARE__ - /* MASV, should it be done everytime? */ netware_ssl_init(); #endif From c1d64ccc1b227e862f5cbaa991ce5525c59ab2ef Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 18 Apr 2006 17:58:27 +0200 Subject: [PATCH 3/3] Bug#17208 SSL: client does not verify server certificate - Add new function 'ssl_verify_server_cert' which is used if we are connecting to the server with SSL. It will compare the hostname in the server's cert against the hostname that we used when connecting to the server. Will reject the connection if hostname does not match. - Add new option "OPT_SSL_VERIFY_SERVER_CERT" to be passed to mysql_options which will turn on checking of servers cert. - Add new argument "ssl-verify-server-cert" to all mysql* clients which will activate the above option. - Generate a new server cert with 1024 bits that has "localhost" as the server name. SSL/server-cert.pem: Generate a new server cert that has "localhost" as CN, so that we can test to verify the hostname we connected against with the hostname in the cert client/client_priv.h: Add OPT_SSL_VERIFY_CERT client/mysql.cc: Pass the variable "opt_ssl_verify_server_cert" to the mysql_options function. It's processed/included by include/sslopt*.h files client/mysqladmin.cc: Pass the variable "opt_ssl_verify_server_cert" to the mysql_options function. It's processed/included by include/sslopt*.h files client/mysqldump.c: Pass the variable "opt_ssl_verify_server_cert" to the mysql_options function. It's processed/included by include/sslopt*.h files client/mysqlimport.c: Pass the variable "opt_ssl_verify_server_cert" to the mysql_options function. It's processed/included by include/sslopt*.h files client/mysqlshow.c: Pass the variable "opt_ssl_verify_server_cert" to the mysql_options function. It's processed/included by include/sslopt*.h files client/mysqltest.c: Always set opt_ssl_verify_server_cert on in mysqltest if we are using SSL include/mysql.h: Add variable ssl_verify_cerver_cert include/sslopt-longopts.h: Add ssl-verify-server-cert options to all clients. include/sslopt-vars.h: Add opt_ssl_varify_server_cert to all clients. sql-common/client.c: Add ssl_vertify_server_cert function which is executed if user has set the option ssl_verify_cerver_cert vio/viosslfactories.c: Ask the SSL library to verify servers cert by setting the SSL_VERIFY_PEER flag --- SSL/server-cert.pem | 78 +++++++++++++++++------------------ client/client_priv.h | 2 +- client/mysql.cc | 2 + client/mysqladmin.cc | 2 + client/mysqldump.c | 2 + client/mysqlimport.c | 2 + client/mysqlshow.c | 2 + client/mysqltest.c | 11 ++++- include/mysql.h | 4 +- include/sslopt-longopts.h | 7 +++- include/sslopt-vars.h | 3 ++ sql-common/client.c | 86 ++++++++++++++++++++++++++++++++++++++- vio/viosslfactories.c | 8 ++-- 13 files changed, 159 insertions(+), 50 deletions(-) diff --git a/SSL/server-cert.pem b/SSL/server-cert.pem index debf7026e3c..4ef70ed5095 100644 --- a/SSL/server-cert.pem +++ b/SSL/server-cert.pem @@ -1,13 +1,14 @@ Certificate: Data: Version: 3 (0x2) - Serial Number: 2 (0x2) + Serial Number: + e9:07:d1:01:94:ee:66:ca Signature Algorithm: md5WithRSAEncryption - Issuer: C=SE, L=Uppsala, O=MySQL AB, CN=Abstract MySQL Developer/Email=abstract.mysql.developer@mysql.com + Issuer: C=SE, ST=Uppsala, L=Uppsala, O=MySQL AB, CN=localhost/emailAddress=abstract.mysql.developer@mysql.com Validity - Not Before: Sep 12 16:22:06 2003 GMT - Not After : Sep 9 16:22:06 2013 GMT - Subject: C=SE, L=Uppsala, O=MySQL AB, CN=MySQL Server/Email=abstract.mysql.developer@mysql.com + Not Before: Apr 18 15:35:37 2006 GMT + Not After : Jan 12 15:35:37 2009 GMT + Subject: C=SE, ST=Uppsala, L=Uppsala, O=MySQL AB, CN=localhost/emailAddress=abstract.mysql.developer@mysql.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) @@ -23,45 +24,42 @@ Certificate: 3d:0e:4d:2a:a8:b8:ca:99:8d Exponent: 65537 (0x10001) X509v3 extensions: - X509v3 Basic Constraints: - CA:FALSE - Netscape Comment: - OpenSSL Generated Certificate X509v3 Subject Key Identifier: 6E:E4:9B:6A:C5:EA:E4:E6:C7:EF:D7:1E:C8:63:45:60:2B:1B:D4:D4 X509v3 Authority Key Identifier: - keyid:88:98:65:D9:F3:F2:8B:03:1D:66:60:61:23:FA:AD:73:6D:D3:68:92 - DirName:/C=SE/L=Uppsala/O=MySQL AB/CN=Abstract MySQL Developer/Email=abstract.mysql.developer@mysql.com - serial:00 + keyid:6E:E4:9B:6A:C5:EA:E4:E6:C7:EF:D7:1E:C8:63:45:60:2B:1B:D4:D4 + DirName:/C=SE/ST=Uppsala/L=Uppsala/O=MySQL AB/CN=localhost/emailAddress=abstract.mysql.developer@mysql.com + serial:E9:07:D1:01:94:EE:66:CA + X509v3 Basic Constraints: + CA:TRUE Signature Algorithm: md5WithRSAEncryption - 31:77:69:b9:bd:ab:29:f3:fc:5a:09:16:6f:5d:42:ea:ba:01: - 55:69:e3:75:cf:b8:d1:b7:b9:bf:da:63:85:8c:48:92:06:60: - 76:97:e0:00:78:4b:ad:da:ab:6a:90:6d:8b:03:a8:b1:e9:09: - 78:e1:29:98:56:12:60:6b:42:fe:e8:a7:c4:f8:d6:15:07:e8: - 2b:c2:d8:8a:e5:1b:2e:51:08:9b:56:e3:b3:7a:4c:3e:e5:be: - 4a:4d:f8:65:7b:a8:21:e0:ca:fe:8b:ab:d7:ec:f2:2d:f7:d0: - bf:d7:c5:23:1c:08:d8:aa:57:c7:f3:5f:ba:33:3f:78:d1:f4: - 8e:5e + 1f:03:59:6e:ff:1f:9d:c7:19:9e:8e:b2:1a:c0:0b:9e:ee:94: + 35:77:2a:93:04:ea:d5:a8:fc:36:5a:5b:e3:1c:02:b8:cf:04: + 6e:21:b0:27:f6:96:6e:d6:8f:cd:02:cf:23:f3:e7:ff:6a:ee: + a9:09:c5:c9:07:81:b6:d2:bc:bd:13:47:0d:7b:76:f6:8a:c4: + 76:24:f8:4c:4e:26:fc:d8:c0:1f:3d:40:19:43:8e:41:ab:99: + 3a:99:9b:24:7c:ae:78:f3:df:2f:a2:ed:8f:27:0a:0a:0b:04: + bf:25:74:88:87:96:c8:68:d5:bc:5b:a0:ef:14:aa:53:6e:c4: + a3:e3 -----BEGIN CERTIFICATE----- -MIIDkTCCAvqgAwIBAgIBAjANBgkqhkiG9w0BAQQFADCBiDELMAkGA1UEBhMCU0Ux -EDAOBgNVBAcTB1VwcHNhbGExETAPBgNVBAoTCE15U1FMIEFCMSEwHwYDVQQDExhB -YnN0cmFjdCBNeVNRTCBEZXZlbG9wZXIxMTAvBgkqhkiG9w0BCQEWImFic3RyYWN0 -Lm15c3FsLmRldmVsb3BlckBteXNxbC5jb20wHhcNMDMwOTEyMTYyMjA2WhcNMTMw -OTA5MTYyMjA2WjB8MQswCQYDVQQGEwJTRTEQMA4GA1UEBxMHVXBwc2FsYTERMA8G -A1UEChMITXlTUUwgQUIxFTATBgNVBAMTDE15U1FMIFNlcnZlcjExMC8GCSqGSIb3 -DQEJARYiYWJzdHJhY3QubXlzcWwuZGV2ZWxvcGVyQG15c3FsLmNvbTCBnzANBgkq -hkiG9w0BAQEFAAOBjQAwgYkCgYEA6YZ6VYSITL6k+JJzMBJJC3qFhzk0OQ19C40Y -wheVE1LSP1UQV8g/WvWy+ovQZ0nMqoL8n84AtHPzNtI608KwDhTD1LIhdKHwMYFg -h5hzXBDBsRpN8fOwmD/w15ebK/3VIXmyL+tkFcmbnfyeLdT4BFvqqXVLQsM9Dk0q -qLjKmY0CAwEAAaOCARQwggEQMAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9w -ZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRu5Jtqxerk5sfv -1x7IY0VgKxvU1DCBtQYDVR0jBIGtMIGqgBSImGXZ8/KLAx1mYGEj+q1zbdNokqGB -jqSBizCBiDELMAkGA1UEBhMCU0UxEDAOBgNVBAcTB1VwcHNhbGExETAPBgNVBAoT -CE15U1FMIEFCMSEwHwYDVQQDExhBYnN0cmFjdCBNeVNRTCBEZXZlbG9wZXIxMTAv -BgkqhkiG9w0BCQEWImFic3RyYWN0Lm15c3FsLmRldmVsb3BlckBteXNxbC5jb22C -AQAwDQYJKoZIhvcNAQEEBQADgYEAMXdpub2rKfP8WgkWb11C6roBVWnjdc+40be5 -v9pjhYxIkgZgdpfgAHhLrdqrapBtiwOosekJeOEpmFYSYGtC/uinxPjWFQfoK8LY -iuUbLlEIm1bjs3pMPuW+Sk34ZXuoIeDK/our1+zyLffQv9fFIxwI2KpXx/NfujM/ -eNH0jl4= +MIIDijCCAvOgAwIBAgIJAOkH0QGU7mbKMA0GCSqGSIb3DQEBBAUAMIGLMQswCQYD +VQQGEwJTRTEQMA4GA1UECBMHVXBwc2FsYTEQMA4GA1UEBxMHVXBwc2FsYTERMA8G +A1UEChMITXlTUUwgQUIxEjAQBgNVBAMTCWxvY2FsaG9zdDExMC8GCSqGSIb3DQEJ +ARYiYWJzdHJhY3QubXlzcWwuZGV2ZWxvcGVyQG15c3FsLmNvbTAeFw0wNjA0MTgx +NTM1MzdaFw0wOTAxMTIxNTM1MzdaMIGLMQswCQYDVQQGEwJTRTEQMA4GA1UECBMH +VXBwc2FsYTEQMA4GA1UEBxMHVXBwc2FsYTERMA8GA1UEChMITXlTUUwgQUIxEjAQ +BgNVBAMTCWxvY2FsaG9zdDExMC8GCSqGSIb3DQEJARYiYWJzdHJhY3QubXlzcWwu +ZGV2ZWxvcGVyQG15c3FsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA +6YZ6VYSITL6k+JJzMBJJC3qFhzk0OQ19C40YwheVE1LSP1UQV8g/WvWy+ovQZ0nM +qoL8n84AtHPzNtI608KwDhTD1LIhdKHwMYFgh5hzXBDBsRpN8fOwmD/w15ebK/3V +IXmyL+tkFcmbnfyeLdT4BFvqqXVLQsM9Dk0qqLjKmY0CAwEAAaOB8zCB8DAdBgNV +HQ4EFgQUbuSbasXq5ObH79ceyGNFYCsb1NQwgcAGA1UdIwSBuDCBtYAUbuSbasXq +5ObH79ceyGNFYCsb1NShgZGkgY4wgYsxCzAJBgNVBAYTAlNFMRAwDgYDVQQIEwdV +cHBzYWxhMRAwDgYDVQQHEwdVcHBzYWxhMREwDwYDVQQKEwhNeVNRTCBBQjESMBAG +A1UEAxMJbG9jYWxob3N0MTEwLwYJKoZIhvcNAQkBFiJhYnN0cmFjdC5teXNxbC5k +ZXZlbG9wZXJAbXlzcWwuY29tggkA6QfRAZTuZsowDAYDVR0TBAUwAwEB/zANBgkq +hkiG9w0BAQQFAAOBgQAfA1lu/x+dxxmejrIawAue7pQ1dyqTBOrVqPw2WlvjHAK4 +zwRuIbAn9pZu1o/NAs8j8+f/au6pCcXJB4G20ry9E0cNe3b2isR2JPhMTib82MAf +PUAZQ45Bq5k6mZskfK54898vou2PJwoKCwS/JXSIh5bIaNW8W6DvFKpTbsSj4w== -----END CERTIFICATE----- diff --git a/client/client_priv.h b/client/client_priv.h index a9d5364df49..9e011144836 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -51,5 +51,5 @@ enum options_client #endif OPT_TRIGGERS, OPT_IGNORE_TABLE,OPT_INSERT_IGNORE,OPT_SHOW_WARNINGS,OPT_DROP_DATABASE, - OPT_TZ_UTC, OPT_AUTO_CLOSE + OPT_TZ_UTC, OPT_AUTO_CLOSE, OPT_SSL_VERIFY_SERVER_CERT }; diff --git a/client/mysql.cc b/client/mysql.cc index cd4cbf49918..7c1e70cfda2 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -3123,6 +3123,8 @@ sql_real_connect(char *host,char *database,char *user,char *password, if (opt_use_ssl) mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); + mysql_options(&mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + (char*)&opt_ssl_verify_server_cert); #endif if (opt_protocol) mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index 5b52d524f8e..57ab4e071fb 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -340,6 +340,8 @@ int main(int argc,char *argv[]) if (opt_use_ssl) mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); + mysql_options(&mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + (char*)&opt_ssl_verify_server_cert); #endif if (opt_protocol) mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); diff --git a/client/mysqldump.c b/client/mysqldump.c index 78f12593f46..ee6d7b9d12b 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -905,6 +905,8 @@ static int dbConnect(char *host, char *user,char *passwd) if (opt_use_ssl) mysql_ssl_set(&mysql_connection, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); + mysql_options(&mysql_connection,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + (char*)&opt_ssl_verify_server_cert); #endif if (opt_protocol) mysql_options(&mysql_connection,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); diff --git a/client/mysqlimport.c b/client/mysqlimport.c index 8694093f06b..1f9b96f91be 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -384,6 +384,8 @@ static MYSQL *db_connect(char *host, char *database, char *user, char *passwd) if (opt_use_ssl) mysql_ssl_set(&mysql_connection, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); + mysql_options(&mysql_connection,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + (char*)&opt_ssl_verify_server_cert); #endif if (opt_protocol) mysql_options(&mysql_connection,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); diff --git a/client/mysqlshow.c b/client/mysqlshow.c index 504f0d9844b..d090495ff81 100644 --- a/client/mysqlshow.c +++ b/client/mysqlshow.c @@ -109,6 +109,8 @@ int main(int argc, char **argv) if (opt_use_ssl) mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); + mysql_options(&mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + (char*)&opt_ssl_verify_server_cert); #endif if (opt_protocol) mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); diff --git a/client/mysqltest.c b/client/mysqltest.c index e51d83270b5..7257958311f 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -108,7 +108,7 @@ enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD, OPT_MANAGER_PORT,OPT_MANAGER_WAIT_TIMEOUT, OPT_SKIP_SAFEMALLOC, OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH, OPT_SSL_CIPHER,OPT_PS_PROTOCOL,OPT_SP_PROTOCOL,OPT_CURSOR_PROTOCOL, - OPT_VIEW_PROTOCOL}; + OPT_VIEW_PROTOCOL, OPT_SSL_VERIFY_SERVER_CERT}; /* ************************************************************************ */ /* @@ -2378,8 +2378,12 @@ int do_connect(struct st_query *q) #ifdef HAVE_OPENSSL if (opt_use_ssl || con_ssl) + { mysql_ssl_set(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); + mysql_options(&next_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + &opt_ssl_verify_server_cert); + } #endif if (con_sock && !free_con_sock && *con_sock && *con_sock != FN_LIBCHAR) con_sock=fn_format(buff, con_sock, TMPDIR, "",0); @@ -4604,9 +4608,14 @@ int main(int argc, char **argv) mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name); #ifdef HAVE_OPENSSL + opt_ssl_verify_server_cert= TRUE; /* Always on in mysqltest */ if (opt_use_ssl) + { mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); + mysql_options(&cur_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + &opt_ssl_verify_server_cert); + } #endif if (!(cur_con->name = my_strdup("default", MYF(MY_WME)))) diff --git a/include/mysql.h b/include/mysql.h index 925a4525378..7149224dcdd 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -149,7 +149,8 @@ enum mysql_option MYSQL_OPT_WRITE_TIMEOUT, MYSQL_OPT_USE_RESULT, MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION, MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH, - MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT + MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT, + MYSQL_OPT_SSL_VERIFY_SERVER_CERT }; struct st_mysql_options { @@ -164,6 +165,7 @@ struct st_mysql_options { char *ssl_ca; /* PEM CA file */ char *ssl_capath; /* PEM directory of CA-s? */ char *ssl_cipher; /* cipher to use */ + my_bool ssl_verify_server_cert; /* if to verify server cert */ char *shared_memory_base_name; unsigned long max_allowed_packet; my_bool use_ssl; /* if to use SSL or not */ diff --git a/include/sslopt-longopts.h b/include/sslopt-longopts.h index dc3b0922327..f444a7eb7ce 100644 --- a/include/sslopt-longopts.h +++ b/include/sslopt-longopts.h @@ -37,5 +37,10 @@ {"ssl-cipher", OPT_SSL_CIPHER, "SSL cipher to use (implies --ssl).", (gptr*) &opt_ssl_cipher, (gptr*) &opt_ssl_cipher, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - +#ifdef MYSQL_CLIENT + {"ssl-verify-server-cert", OPT_SSL_VERIFY_SERVER_CERT, + "Verify servers \"Common Name\" in it's cert against hostname used when connecting. This option is disabled by default.", + (gptr*) &opt_ssl_verify_server_cert, (gptr*) &opt_ssl_verify_server_cert, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, +#endif #endif /* HAVE_OPENSSL */ diff --git a/include/sslopt-vars.h b/include/sslopt-vars.h index 164cf541381..8e5f3434396 100644 --- a/include/sslopt-vars.h +++ b/include/sslopt-vars.h @@ -21,4 +21,7 @@ static char *opt_ssl_cert = 0; static char *opt_ssl_ca = 0; static char *opt_ssl_capath = 0; static char *opt_ssl_cipher = 0; +#ifdef MYSQL_CLIENT +static my_bool opt_ssl_verify_server_cert= 0; +#endif #endif diff --git a/sql-common/client.c b/sql-common/client.c index b24b92014d6..13a816b05b9 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1500,6 +1500,7 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) , mysql->options.ssl_ca= strdup_if_not_null(ca); mysql->options.ssl_capath= strdup_if_not_null(capath); mysql->options.ssl_cipher= strdup_if_not_null(cipher); + mysql->options.ssl_verify_server_cert= FALSE; /* Off by default */ #endif /* HAVE_OPENSSL */ DBUG_RETURN(0); } @@ -1521,7 +1522,7 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused))) my_free(mysql->options.ssl_cert, MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_cipher, MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.ssl_cipher, MYF(MY_ALLOW_ZERO_PTR)); if (ssl_fd) SSL_CTX_free(ssl_fd->ssl_context); my_free(mysql->connector_fd,MYF(MY_ALLOW_ZERO_PTR)); @@ -1534,6 +1535,77 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused))) mysql->connector_fd = 0; DBUG_VOID_RETURN; } + +/* + Check the server's (subject) Common Name against the + hostname we connected to + + SYNOPSIS + ssl_verify_server_cert() + vio pointer to a SSL connected vio + server_hostname name of the server that we connected to + + RETURN VALUES + 0 Success + 1 Failed to validate server + + */ +static int ssl_verify_server_cert(Vio *vio, const char* server_hostname) +{ + SSL *ssl; + X509 *server_cert; + char *cp1, *cp2; + char buf[256]; + DBUG_ENTER("ssl_verify_server_cert"); + DBUG_PRINT("enter", ("server_hostname: %s", server_hostname)); + + if (!(ssl= (SSL*)vio->ssl_arg)) + { + DBUG_PRINT("error", ("No SSL pointer found")); + DBUG_RETURN(1); + } + + if (!server_hostname) + { + DBUG_PRINT("error", ("No server hostname supplied")); + DBUG_RETURN(1); + } + + if (!(server_cert= SSL_get_peer_certificate(ssl))) + { + DBUG_PRINT("error", ("Could not get server certificate")); + DBUG_RETURN(1); + } + + /* + We already know that the certificate exchanged was valid; the SSL library + handled that. Now we need to verify that the contents of the certificate + are what we expect. + */ + + X509_NAME_oneline(X509_get_subject_name(server_cert), buf, sizeof(buf)); + X509_free (server_cert); + + DBUG_PRINT("info", ("hostname in cert: %s", buf)); + cp1 = strstr(buf, "/CN="); + if (cp1) + { + cp1 += 4; // Skip the "/CN=" that we found + // Search for next / which might be the delimiter for email + cp2 = strchr(cp1, '/'); + if (cp2) + *cp2 = '\0'; + DBUG_PRINT("info", ("Server hostname in cert: %s", cp1)); + if (!strcmp(cp1, server_hostname)) + { + /* Success */ + DBUG_RETURN(0); + } + } + DBUG_PRINT("error", ("SSL certificate validation failure")); + DBUG_RETURN(1); +} + #endif /* HAVE_OPENSSL */ @@ -2049,7 +2121,14 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, } DBUG_PRINT("info", ("IO layer change done!")); - /* TODO Verify server cert */ + /* Verify server cert */ + if (mysql->options.ssl_verify_server_cert && + ssl_verify_server_cert(mysql->net.vio, mysql->host)) + { + set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate); + goto error; + } + } #endif /* HAVE_OPENSSL */ @@ -2789,6 +2868,9 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const char *arg) case MYSQL_OPT_RECONNECT: mysql->reconnect= *(my_bool *) arg; break; + case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: + mysql->options.ssl_verify_server_cert= *(my_bool *) arg; + break; default: DBUG_RETURN(1); } diff --git a/vio/viosslfactories.c b/vio/viosslfactories.c index d6356f1adca..2b3e80a98e4 100644 --- a/vio/viosslfactories.c +++ b/vio/viosslfactories.c @@ -290,20 +290,20 @@ new_VioSSLConnectorFd(const char *key_file, const char *cert_file, const char *cipher) { struct st_VioSSLFd *ssl_fd; - int verify= SSL_VERIFY_NONE; + int verify= SSL_VERIFY_PEER; if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file, ca_path, cipher, TLSv1_client_method()))) { return 0; } + /* Init the the VioSSLFd as a "connector" ie. the client side */ /* The verify_callback function is used to control the behaviour - when the SSL_VERIFY_PEER flag is set. Here it is SSL_VERIFY_NONE - and thus callback is set to NULL + when the SSL_VERIFY_PEER flag is set. */ - SSL_CTX_set_verify(ssl_fd->ssl_context, verify, NULL); + SSL_CTX_set_verify(ssl_fd->ssl_context, verify, vio_verify_callback); return ssl_fd; }