Skip to content

Commit 1287c90

Browse files
committed
TLS/SSL changes (major rework)
Peer certificate validation: Since version 3.4 peer certificate verification is enabled by default. It can be disabled via `mysql_optionsv`, using option MYSQL_OPT_SSL_VERIFY_SERVER_CERT: my_bool verify= 0; mysql_options(mariadb, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); Self signed certificates If the client obtained a self signed peer certificate from MariaDB server the verification will fail, with the following exceptions: * If the connection between client and server is considered to be secure:, e.g. * a unix_socket is used for client server communication * hostname is localhost (Windows operating system), 127.0.0.1 or ::1 * a specified fingerprint matches the fingerprint of the peer certificate (see below) * a client can verify the certificate using account password, it's possible if * account has a password * authentication plugin is "secure without TLS", that is, one of mysql_native_password, ed25519 or parsec. Fingerprint verification of the peer certificate A fingerprint is a cryptographic hash (SHA-256, SHA-384 or SHA-512) of the peer certificate's binary data. Even if the fingerprint matches, an expired or revoked certificate will not be accepted. For security reasons support for MD5 and SHA1 has been removed. Technical details: ================== - Peer certificate verification call was removed from ma_tls_connect, instead it will be called directly after the handshake succeeded (my_auth.c) - mysql->net.tls_self_signed_error was replaced by mysql->net.tls_verify_status which contains the result of the peer certfificate verification: The verification status can be obtained with mariadb_get_infov using new parameter MARIADB_TLS_VERIFY_STATUS. unsigned int tls_verify_status; mariadb_get_infov(mysql, MARIADB_TLS_VERIFY_STATUS, &tls_verify_status); The result is a combination of the following flags: MARIADB_TLS_VERIFY_OK 0 MARIADB_TLS_VERIFY_TRUST 1 MARIADB_TLS_VERIFY_HOST 2 MARIADB_TLS_VERIFY_PERIOD 4 MARIADB_TLS_VERIFY_FINGERPRINT 8 MARIADB_TLS_VERIFY_REVOKED 16 MARIADB_TLS_VERIFY_UNKNOWN 32 - GnuTLS peer certificate verification callback was removed and replaced by gnutls_verify_peers2() api function, so the peer certificate validation will happen after handshake. - OpenSSL implementation will no longer use SSL_verify_result to check the validity of the peer certificate. Instead a callback function will be called during the handshake, which collects all certificate validation errors. - If the peer certificate is not trusted, hostname verification will be skipped. - Testing Added new test tls, which implements a python based dummy server, which allows to set different certificates and TLS options. Please note. that tests are expected to fail, since the server doesn't support further steps like user authentication etc. after the handshake. Prerequisite for running the tls test is Python3.
1 parent 5386f1a commit 1287c90

33 files changed

+1938
-419
lines changed

.travis.yml

+10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ cache:
77
directories:
88
- $HOME/docker
99

10+
before_install:
11+
- |-
12+
if [ -z "$server_branch" ] ; then
13+
case $TRAVIS_OS_NAME in
14+
windows)
15+
choco install python --version=3.12.0
16+
;;
17+
esac
18+
fi
19+
1020
env:
1121
global: local=0 DB=testc CLEAR_TEXT=0
1222

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ SET(CC_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
3737

3838
SET(CPACK_PACKAGE_VERSION_MAJOR 3)
3939
SET(CPACK_PACKAGE_VERSION_MINOR 4)
40-
SET(CPACK_PACKAGE_VERSION_PATCH 0)
40+
SET(CPACK_PACKAGE_VERSION_PATCH 1)
4141
SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
4242
MATH(EXPR MARIADB_PACKAGE_VERSION_ID "${CPACK_PACKAGE_VERSION_MAJOR} * 10000 +
4343
${CPACK_PACKAGE_VERSION_MINOR} * 100 +

include/ma_common.h

+4-10
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,9 @@ typedef struct st_mariadb_field_extension
131131
MARIADB_CONST_STRING metadata[MARIADB_FIELD_ATTR_LAST+1]; /* 10.5 */
132132
} MA_FIELD_EXTENSION;
133133

134-
#if defined(HAVE_SCHANNEL) || defined(HAVE_GNUTLS)
135-
#define reset_tls_self_signed_error(mysql) \
136-
do { \
137-
free((char*)mysql->net.tls_self_signed_error); \
138-
mysql->net.tls_self_signed_error= 0; \
139-
} while(0)
140-
#else
141-
#define reset_tls_self_signed_error(mysql) \
142-
do { \
143-
mysql->net.tls_self_signed_error= 0; \
134+
#ifdef HAVE_TLS
135+
#define reset_tls_error(mysql) \
136+
do { \
137+
mysql->net.tls_verify_status= 0; \
144138
} while(0)
145139
#endif

include/ma_tls.h

+13-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ enum enum_pvio_tls_type {
2121
#define PROTOCOL_MAX PROTOCOL_TLS_1_3
2222

2323
#define TLS_VERSION_LENGTH 64
24+
25+
#define have_fingerprint(m) \
26+
((m)->options.extension) && \
27+
(((m)->options.extension->tls_fp && (m)->options.extension->tls_fp[0]) ||\
28+
((m)->options.extension->tls_fp_list && (m)->options.extension->tls_fp_list[0]))
29+
2430
extern char tls_library_version[TLS_VERSION_LENGTH];
2531

2632
typedef struct st_ma_pvio_tls {
@@ -111,11 +117,12 @@ my_bool ma_tls_close(MARIADB_TLS *ctls);
111117
validation check of server certificate
112118
Parameter:
113119
MARIADB_TLS MariaDB SSL container
120+
flags verification flags
114121
Returns:
115-
ß success
122+
0 success
116123
1 error
117124
*/
118-
int ma_tls_verify_server_cert(MARIADB_TLS *ctls);
125+
int ma_tls_verify_server_cert(MARIADB_TLS *ctls, unsigned int flags);
119126

120127
/* ma_tls_get_cipher
121128
returns cipher for current ssl connection
@@ -134,6 +141,7 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ssl);
134141
hash_type hash_type as defined in ma_hash.h
135142
fp buffer for fingerprint
136143
fp_len buffer length
144+
my_bool verify_period
137145
138146
Returns:
139147
actual size of finger print
@@ -150,7 +158,7 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp
150158
int ma_tls_get_protocol_version(MARIADB_TLS *ctls);
151159
const char *ma_pvio_tls_get_protocol_version(MARIADB_TLS *ctls);
152160
int ma_pvio_tls_get_protocol_version_id(MARIADB_TLS *ctls);
153-
unsigned int ma_tls_get_peer_cert_info(MARIADB_TLS *ctls);
161+
unsigned int ma_tls_get_peer_cert_info(MARIADB_TLS *ctls, unsigned int size);
154162
void ma_tls_set_connection(MYSQL *mysql);
155163

156164
/* Function prototypes */
@@ -159,12 +167,12 @@ my_bool ma_pvio_tls_connect(MARIADB_TLS *ctls);
159167
ssize_t ma_pvio_tls_read(MARIADB_TLS *ctls, const uchar *buffer, size_t length);
160168
ssize_t ma_pvio_tls_write(MARIADB_TLS *ctls, const uchar *buffer, size_t length);
161169
my_bool ma_pvio_tls_close(MARIADB_TLS *ctls);
162-
int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls);
170+
int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls, unsigned int flags);
163171
const char *ma_pvio_tls_cipher(MARIADB_TLS *ctls);
164172
my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_list);
165173
my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio);
166174
void ma_pvio_tls_set_connection(MYSQL *mysql);
167175
void ma_pvio_tls_end();
168-
unsigned int ma_pvio_tls_get_peer_cert_info(MARIADB_TLS *ctls);
176+
unsigned int ma_pvio_tls_get_peer_cert_info(MARIADB_TLS *ctls, unsigned int size);
169177

170178
#endif /* _ma_tls_h_ */

include/mariadb_com.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,10 @@ typedef struct st_net {
297297
unsigned char reading_or_writing;
298298
char save_char;
299299
char unused_1;
300-
my_bool unused_2;
300+
my_bool tls_verify_status;
301301
my_bool compress;
302-
my_bool unused_3;
303-
const char *tls_self_signed_error;
302+
my_bool unused_2;
303+
char *unused_3;
304304
unsigned int last_errno;
305305
unsigned char error;
306306
my_bool unused_5;

include/mysql.h

+12-1
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ extern const char *SQLSTATE_UNKNOWN;
300300
MARIADB_CONNECTION_BYTES_READ,
301301
MARIADB_CONNECTION_BYTES_SENT,
302302
MARIADB_TLS_PEER_CERT_INFO,
303+
MARIADB_TLS_VERIFY_STATUS
303304
};
304305

305306
enum mysql_status { MYSQL_STATUS_READY,
@@ -448,6 +449,16 @@ typedef struct st_mysql_time
448449
#define MYSQL_WAIT_EXCEPT 4
449450
#define MYSQL_WAIT_TIMEOUT 8
450451

452+
#define MARIADB_TLS_VERIFY_OK 0
453+
#define MARIADB_TLS_VERIFY_TRUST 1
454+
#define MARIADB_TLS_VERIFY_HOST 2
455+
#define MARIADB_TLS_VERIFY_PERIOD 4
456+
#define MARIADB_TLS_VERIFY_FINGERPRINT 8
457+
#define MARIADB_TLS_VERIFY_REVOKED 16
458+
#define MARIADB_TLS_VERIFY_UNKNOWN 32
459+
#define MARIADB_TLS_VERIFY_ERROR 128 /* last */
460+
461+
451462
typedef struct character_set
452463
{
453464
unsigned int number; /* character set number */
@@ -497,7 +508,7 @@ typedef struct
497508
int version;
498509
char *issuer;
499510
char *subject;
500-
char fingerprint[65];
511+
char fingerprint[129];
501512
struct tm not_before;
502513
struct tm not_after;
503514
} MARIADB_X509_INFO;

libmariadb/ma_pvio.c

-67
Original file line numberDiff line numberDiff line change
@@ -522,47 +522,6 @@ my_bool ma_pvio_has_data(MARIADB_PVIO *pvio, ssize_t *data_len)
522522
/* }}} */
523523

524524
#ifdef HAVE_TLS
525-
/**
526-
Checks if self-signed certificate error should be ignored.
527-
*/
528-
static my_bool ignore_self_signed_cert_error(MARIADB_PVIO *pvio)
529-
{
530-
const char *hostname= pvio->mysql->host;
531-
const char *local_host_names[]= {
532-
#ifdef _WIN32
533-
/*
534-
On Unix, we consider TCP connections with "localhost"
535-
an insecure transport, for the single reason to run tests for
536-
insecure transport on CI.This is artificial, but should be ok.
537-
Default client connections use unix sockets anyway, so it
538-
would not hurt much.
539-
540-
On Windows, the situation is quite different.
541-
Default connections type is TCP, default host name is "localhost",
542-
non-password plugin gssapi is common (every installation)
543-
In this environment, there would be a lot of faux/disruptive
544-
"self-signed certificates" errors there. Thus, "localhost" TCP
545-
needs to be considered secure transport.
546-
*/
547-
"localhost",
548-
#endif
549-
"127.0.0.1", "::1", NULL};
550-
int i;
551-
if (pvio->type != PVIO_TYPE_SOCKET)
552-
{
553-
return TRUE;
554-
}
555-
if (!hostname)
556-
return FALSE;
557-
for (i= 0; local_host_names[i]; i++)
558-
{
559-
if (strcmp(hostname, local_host_names[i]) == 0)
560-
{
561-
return TRUE;
562-
}
563-
}
564-
return FALSE;
565-
}
566525

567526
/* {{{ my_bool ma_pvio_start_ssl */
568527
my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio)
@@ -580,32 +539,6 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio)
580539
pvio->ctls= NULL;
581540
return 1;
582541
}
583-
584-
/* default behaviour:
585-
1. peer certificate verification
586-
2. verify CN (requires option ssl_verify_check)
587-
3. verrify finger print
588-
*/
589-
if (!pvio->mysql->options.extension->tls_allow_invalid_server_cert &&
590-
!pvio->mysql->net.tls_self_signed_error &&
591-
ma_pvio_tls_verify_server_cert(pvio->ctls))
592-
return 1;
593-
594-
if (pvio->mysql->options.extension &&
595-
((pvio->mysql->options.extension->tls_fp && pvio->mysql->options.extension->tls_fp[0]) ||
596-
(pvio->mysql->options.extension->tls_fp_list && pvio->mysql->options.extension->tls_fp_list[0])))
597-
{
598-
if (ma_pvio_tls_check_fp(pvio->ctls,
599-
pvio->mysql->options.extension->tls_fp,
600-
pvio->mysql->options.extension->tls_fp_list))
601-
return 1;
602-
reset_tls_self_signed_error(pvio->mysql); // validated
603-
return 0;
604-
}
605-
606-
if (pvio->mysql->net.tls_self_signed_error && ignore_self_signed_cert_error(pvio))
607-
reset_tls_self_signed_error(pvio->mysql);
608-
609542
return 0;
610543
}
611544
/* }}} */

libmariadb/ma_tls.c

+50-8
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,54 @@ my_bool ma_pvio_tls_close(MARIADB_TLS *ctls)
103103
return ma_tls_close(ctls);
104104
}
105105

106-
int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls)
106+
int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls, unsigned int flags)
107107
{
108-
return ma_tls_verify_server_cert(ctls);
108+
MYSQL *mysql;
109+
int rc;
110+
111+
if (!ctls || !ctls->pvio || !ctls->pvio->mysql)
112+
return 0;
113+
114+
mysql= ctls->pvio->mysql;
115+
116+
/* Skip peer certificate verification */
117+
if (ctls->pvio->mysql->options.extension->tls_allow_invalid_server_cert)
118+
{
119+
return 0;
120+
}
121+
122+
rc= ma_tls_verify_server_cert(ctls, flags);
123+
124+
/* Set error messages */
125+
if (!mysql->net.last_errno)
126+
{
127+
if (mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_PERIOD)
128+
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
129+
ER(CR_SSL_CONNECTION_ERROR),
130+
"Certificate not yet valid or expired");
131+
else if (mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_FINGERPRINT)
132+
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
133+
ER(CR_SSL_CONNECTION_ERROR),
134+
"Fingerprint validation of peer certificate failed");
135+
else if (mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_REVOKED)
136+
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
137+
ER(CR_SSL_CONNECTION_ERROR),
138+
"Certificate revoked");
139+
else if (mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_HOST)
140+
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
141+
ER(CR_SSL_CONNECTION_ERROR),
142+
"Hostname verification failed");
143+
else if (mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_UNKNOWN)
144+
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
145+
ER(CR_SSL_CONNECTION_ERROR),
146+
"Peer certificate verification failed");
147+
else if (mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_TRUST)
148+
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
149+
ER(CR_SSL_CONNECTION_ERROR),
150+
"Peer certificate is not trusted");
151+
}
152+
153+
return rc;
109154
}
110155

111156
const char *ma_pvio_tls_cipher(MARIADB_TLS *ctls)
@@ -150,8 +195,7 @@ static signed char ma_hex2int(char c)
150195

151196
static my_bool ma_pvio_tls_compare_fp(MARIADB_TLS *ctls,
152197
const char *cert_fp,
153-
unsigned int cert_fp_len
154-
)
198+
unsigned int cert_fp_len)
155199
{
156200
const char fp[EVP_MAX_MD_SIZE];
157201
unsigned int fp_len= EVP_MAX_MD_SIZE;
@@ -206,8 +250,6 @@ static my_bool ma_pvio_tls_compare_fp(MARIADB_TLS *ctls,
206250
signed char d1, d2;
207251
if (*p == ':')
208252
p++;
209-
if (p - cert_fp > (int)fp_len - 1)
210-
return 1;
211253
if ((d1 = ma_hex2int(*p)) == -1 ||
212254
(d2 = ma_hex2int(*(p + 1))) == -1 ||
213255
(char)(d1 * 16 + d2) != *c)
@@ -270,8 +312,8 @@ void ma_pvio_tls_set_connection(MYSQL *mysql)
270312
ma_tls_set_connection(mysql);
271313
}
272314

273-
unsigned int ma_pvio_tls_get_peer_cert_info(MARIADB_TLS *ctls)
315+
unsigned int ma_pvio_tls_get_peer_cert_info(MARIADB_TLS *ctls, unsigned int size)
274316
{
275-
return ma_tls_get_peer_cert_info(ctls);
317+
return ma_tls_get_peer_cert_info(ctls, size);
276318
}
277319
#endif /* HAVE_TLS */

libmariadb/mariadb_lib.c

+10-4
Original file line numberDiff line numberDiff line change
@@ -1295,7 +1295,7 @@ mysql_init(MYSQL *mysql)
12951295
}
12961296
else
12971297
{
1298-
memset((char*) (mysql), 0, sizeof(*(mysql)));
1298+
memset(mysql, 0, sizeof(MYSQL));
12991299
mysql->net.pvio= 0;
13001300
mysql->free_me= 0;
13011301
mysql->net.extension= 0;
@@ -1462,7 +1462,7 @@ mysql_real_connect(MYSQL *mysql, const char *host, const char *user,
14621462
if (!mysql->options.extension || !mysql->options.extension->status_callback)
14631463
mysql_optionsv(mysql, MARIADB_OPT_STATUS_CALLBACK, NULL, NULL);
14641464

1465-
reset_tls_self_signed_error(mysql);
1465+
reset_tls_error(mysql);
14661466

14671467
/* if host contains a semicolon, we need to parse connection string */
14681468
if (host && strchr(host, ';'))
@@ -2468,7 +2468,7 @@ mysql_close(MYSQL *mysql)
24682468
mysql_close_memory(mysql);
24692469
mysql_close_options(mysql);
24702470
ma_clear_session_state(mysql);
2471-
reset_tls_self_signed_error(mysql);
2471+
reset_tls_error(mysql);
24722472

24732473
if (mysql->net.extension)
24742474
{
@@ -4542,12 +4542,18 @@ my_bool mariadb_get_infov(MYSQL *mysql, enum mariadb_value value, void *arg, ...
45424542
case MARIADB_TLS_PEER_CERT_INFO:
45434543
if (mysql->net.pvio->ctls)
45444544
{
4545-
if (!ma_pvio_tls_get_peer_cert_info(mysql->net.pvio->ctls))
4545+
unsigned int size;
4546+
4547+
size= va_arg(ap, unsigned int);
4548+
if (!ma_pvio_tls_get_peer_cert_info(mysql->net.pvio->ctls, size))
45464549
*((MARIADB_X509_INFO **)arg)= (MARIADB_X509_INFO *)&mysql->net.pvio->ctls->cert_info;
45474550
return 0;
45484551
}
45494552
*((MARIADB_X509_INFO **)arg)= NULL;
45504553
break;
4554+
case MARIADB_TLS_VERIFY_STATUS:
4555+
*((unsigned int *)arg)= (unsigned int)mysql->net.tls_verify_status;
4556+
break;
45514557
#endif
45524558
case MARIADB_MAX_ALLOWED_PACKET:
45534559
*((size_t *)arg)= (size_t)max_allowed_packet;

0 commit comments

Comments
 (0)