WL#6791: Redefine client --ssl option to imply enforced encryption

Affects: Server-5.7   —   Status: Complete   —   Priority: Medium

Currently if a MySQL client wants to connect to the server and use the --ssl
option a connection can still happen without encryption being enforced. This is
because under the current definition the --ssl option only provide the ability
to /disable/ encryption by setting --ssl=0. Setting --ssl=1 guarantees nothing.

This worklog redefines --ssl to enforce encryption from the client side.

There will be no guarantee for server certificate validation unless --ssl-ca
also is specified, even if a client certificate also is provided. On the other
hand, if --ssl-ca is used and server doesn't provide a certificate, a client
using --ssl=1 should be guaranteed to fail with an error. Likewise should happen
if any user supplied encryption attributes contradicts the available encryption
options under --ssl=1.


User Documentation
==================

http://dev.mysql.com/doc/refman/5.7/en/ssl-options.html
http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-3.html
1. The SSL client option


1.1 --ssl

Per default the client always attempts to connect using SSL. If it fails it
falls back on an unencrypted connection if the server allows it. This option
changes the default behaviour and if set to 1 (or used with no arguments) the
client will reject any authentication attempts if SSL can't be established. 

If set used as --ssl=0 (or --skip-ssl) then the SSL shall be turned off.
Authentication shall happen using an unencrypted connection if the server allows 
it.

As a recommended set of options to enable SSL connections, use at least
--ssl-cert and --ssl-key on the server side and --ssl-ca on the client side.

--ssl is not implied by other --ssl-xxx options as indicated in the descriptions
for those options. To enforce SSL by the client you must use --ssl. To avoid
attempting to use SSL you must explicitly use --ssl=0. The --ssl-option shall
always take precedence even if other --ssl-XX options are used at the same time.

If the --ssl option isn't used an account can still be configured securely by
specifying a REQUIRE SSL clause in the GRANT statement. In this case,
connections for that account will be rejected unless MySQL supports SSL
connections and the server and client have been started with the proper SSL
options.

If the server isn't configured properly with SSL certificate and key and the
--ssl option isn't used by the connecting client, authentication shall still be
attempted and the client may under certain circumstances communicate a plain
text or hashed password over the network.

1.1.1 Summary of --ssl option combinations
  Behaviours:
  * enforce_ssl := SSL is enforced if set to true. SSL isn't enforced when set
to false. 
  * use_ssl := attempt to establish SSL will be made if set to true. SSL will
not be attempted if set to false. (If enforce_ssl for some reason is true then
no connection will be possible).

  Combinations:
  a) --ssl or -ssl=1 implies that enforce_ssl=true, use_ssl=true
  b) --ssl=0 or --skip-ssl implies that enforce_ssl= false and use_ssl= false
  c) Default setting (ie no --ssl start up option is used) is enforce_ssl=
false, use_ssl= true.


1.2 MYSQL_OPT_SSL_ENFORCE

This new client option works as ssl=1 above when set using mysql_options()

1.3. --ssl-cipher

Per default the server shall attempt to use the cipher "DHE-RSA-AES256-SHA"
unless something else has been specified. The purpose is to make it easier to
setup an encrypted client connection.

2. Backward compatibility

2.1 Applications using any previously specified client protocol options
MYSQL_OPT_SSL_* shall cause the client to attempt to establish a client
connection. This is not a change from previous behaviour.

2.2 Scripts using the mysql client programs with the --ssl option shall fail to
connect unless a SSL connection actually can be established. This is a change of
behaviour.

2.3 Client programs which are explicitly configured to skip SSL using either
--skip-ssl or --ssl=0 shall not change from previous behaviour.

2.4 Client programs which does not specify if SSL should be used shall change
behaviour and start using SSL if the server is configured to use SSL. 
UPDATE 25 Oct 2013: since this will break backward compatibility it's not 
implemented. Need to double-check if it's a requirement.


2.5 PHP mysqlnd has a SSL connection flag which enforce SSL connection per
default. If SSL isn't created then the DB connection will fail. If no cipher is
specified, a default cipher will be used. Client side doesn't have to specify
certificate or key for the SSL connection to work. 

2.6 Slave server behave as any other client and shall attempt to use encryption
per default just as any other client, then fall back on unencrypted
communication if the former fails. To change the slave servers behaviour the
user can specify MASTER_SSL just as the client --ssl command line option.
UPDATE 25 Oct 2013: replication will try SSL only if explicitly requested. 
In this case it will also insist on SSL. No changes in the default behavior. See 
2.4 as to why.

3. Implemented a new C API function mysql_get_option() that will return the 
current value of an applicable mysql_options() option.  See the LLD for 
signature and implementation details.
3. Affected files.
3.1 client/mysql.cc
3.2 include/mysql.h
3.3 include/sslopt-longopts.h
3.4 include/sslopt-vars.h
3.5 sql-common/client.c

4. Affected subsystems

4.1 Client authentication is affected which means PHP, C/C++ connectors and
client programs.

4.2 Server is not affected.

5. Example pseudo-code specification

enum mysql_option 
{
[..]
 MYSQL_OPT_SSL_ENFORCE
};
[..]

send_client_reply(..) {

[..]
  if ((mysql->server_capabilities & CLIENT_SSL && mysql->options.use_ssl) ||
      mysql->options.ssl_enforce)
  {
    if (mysql->options.ssl_cipher == 0 && mysql->options.ssl_enforce)
    {
     char *cipher= my_strdup(DEFAULT_SSL_CIPHER,MYF(MY_WME));
     mysql->options.ssl_cipher= cipher;
    }
[..]
   if (!(ssl_fd= new_VioSSLConnectorFd(options->ssl_key, [..]
[..]

6. Flaws

It would be nice to get SSL to work when the server lacks cert/key but the
client still have it. For some reason I get SSL_ERROR_SYSCALL when I attempt 
this.


7. mysql_options() signature and explanation. 

From the source file : 

787 +  Return the current values for the options settable through mysql_options
    ()
788 +
789 +  Returns the current values for all of the connection options.
790 +  Callers should not manipulate the returned data !
791 +  Data are valid at the time of returning them until the next C API CALL
792 +  arg should always be a pointer to a variable of the appropriate type.
793 +  type of variable, based on the parameter:
794 +
795 +  uint
796 +    MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_OPT_READ_TIMEOUT, MYSQL_OPT_WRITE_TIM
    EOUT,
797 +    MYSQL_OPT_PROTOCOL
798 +
799 +  my_bool
800 +    MYSQL_OPT_COMPRESS, MYSQL_OPT_LOCAL_INFILE, MYSQL_OPT_USE_REMOTE_CONNE
    CTION,
801 +    MYSQL_OPT_USE_EMBEDDED_CONNECTION, MYSQL_OPT_GUESS_CONNECTION,
802 +    MYSQL_SECURE_AUTH, MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT,
803 +    MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQL_OPT_SSL_ENFORCE,
804 +    MYSQL_ENABLE_CLEARTEXT_PLUGIN, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS
805 +
806 +  const char *
807 +    MYSQL_READ_DEFAULT_FILE, MYSQL_READ_DEFAULT_GROUP,
808 +    MYSQL_SET_CHARSET_DIR, MYSQL_SET_CHARSET_NAME, MYSQL_SHARED_MEMORY_BAS
    E_NAME,
809 +    MYSQL_SET_CLIENT_IP, MYSQL_OPT_BIND, MYSQL_PLUGIN_DIR, MYSQL_DEFAULT_A
    UTH,
810 +    MYSQL_OPT_SSL_KEY, MYSQL_OPT_SSL_CERT, MYSQL_OPT_SSL_CA, MYSQL_OPT_SSL
    _CAPATH,
811 +    MYSQL_OPT_SSL_CIPHER, MYSQL_OPT_SSL_CRL, MYSQL_OPT_SSL_CRLPATH,
812 +    MYSQL_SERVER_PUBLIC_KEY
813 +
814 +  <none, error returned>
815 +    MYSQL_OPT_NAMED_PIPE, MYSQL_OPT_CONNECT_ATTR_RESET,
816 +    MYSQL_OPT_CONNECT_ATTR_DELETE, MYSQL_INIT_COMMAND
817 +
818 +  @param      mysql       The MYSQL connection to operate on
819 +  @param      option      The option to return the value for
820 +  @param  out arg         Must be non-null. Receives the current value.
821 +  @return status
822 +  @retval 0 SUCCESS
823 +*/
824 +
825 +int STDCALL
826 +mysql_get_option(MYSQL *mysql, enum mysql_option option, const void *arg)