Skip to content

Commit 98fa081

Browse files
committed
WL#15130 Socket-level TLS patch mysql#1: class NdbSocket
Introduce class NdbSocket, which includes both an ndb_socket_t and an SSL *, and wraps all socket operations that might use TLS. Change-Id: I20d7aeb4854cdb11cfd0b256270ab3648b067efa
1 parent 0f40d86 commit 98fa081

File tree

8 files changed

+913
-17
lines changed

8 files changed

+913
-17
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* Copyright (c) 2022, Oracle and/or its affiliates.
2+
3+
This program is free software; you can redistribute it and/or modify
4+
it under the terms of the GNU General Public License, version 2.0,
5+
as published by the Free Software Foundation.
6+
7+
This program is also distributed with certain software (including
8+
but not limited to OpenSSL) that is licensed under separate terms,
9+
as designated in a particular file or component or in included license
10+
documentation. The authors of MySQL hereby grant you an additional
11+
permission to link the program and your derivative works with the
12+
separately licensed software that they have included with MySQL.
13+
14+
This program is distributed in the hope that it will be useful,
15+
but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
GNU General Public License, version 2.0, for more details.
18+
19+
You should have received a copy of the GNU General Public License
20+
along with this program; if not, write to the Free Software
21+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22+
*/
23+
24+
/* NDB requires a certain minimum OpenSSL version for TLS: 1.1.1d, Sep 2019
25+
26+
In general, OpenSSL 1.1 is required for the availability of TLS 1.3.
27+
1.1.1d is chosen as a 1.1 release that is mature, but not unreasonably new
28+
(about three years old at the time the TLS feature is released).
29+
*/
30+
31+
#define NDB_TLS_MINIMUM_OPENSSL 0x1010104fL
32+
33+
/* NDB_OPENSSL_TOO_OLD is used as an error return value from functions
34+
to report a OpenSSL library version less than NDB_TLS_MINIMUM_OPENSSL
35+
at runtime.
36+
*/
37+
#define NDB_OPENSSL_TOO_OLD -100

storage/ndb/include/portlib/ndb_socket_win32.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ struct iovec {
122122
};
123123

124124
static inline
125-
ssize_t ndb_socket_writev(ndb_socket_t s, struct iovec *iov, int iovcnt)
125+
ssize_t ndb_socket_writev(ndb_socket_t s, const struct iovec *iov, int iovcnt)
126126
{
127127
DWORD rv=0;
128128
if (WSASend(s.s,(LPWSABUF)iov,iovcnt,&rv,0,0,0) == SOCKET_ERROR)

storage/ndb/include/util/NdbSocket.h

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
/*
2+
Copyright (c) 2022, Oracle and/or its affiliates.
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License, version 2.0,
6+
as published by the Free Software Foundation.
7+
8+
This program is also distributed with certain software (including
9+
but not limited to OpenSSL) that is licensed under separate terms,
10+
as designated in a particular file or component or in included license
11+
documentation. The authors of MySQL hereby grant you an additional
12+
permission to link the program and your derivative works with the
13+
separately licensed software that they have included with MySQL.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU General Public License, version 2.0, for more details.
19+
20+
You should have received a copy of the GNU General Public License
21+
along with this program; if not, write to the Free Software
22+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23+
*/
24+
25+
#ifndef NDB_UTIL_SECURE_SOCKET_H
26+
#define NDB_UTIL_SECURE_SOCKET_H
27+
28+
#include "portlib/ndb_socket.h"
29+
#include "portlib/ndb_socket_poller.h"
30+
#include "portlib/NdbMutex.h"
31+
32+
static constexpr int TLS_BUSY_TRY_AGAIN = -2;
33+
34+
class NdbSocket {
35+
public:
36+
enum class From { New, Existing };
37+
38+
NdbSocket() = default;
39+
NdbSocket(ndb_socket_t ndbsocket, From /*fromType*/) {
40+
// ssl = socket_table_get_ssl(ndbsocket.s, (fromType == From::Existing));
41+
init_from_native(ndbsocket.s);
42+
}
43+
/* NdbSockets cannot be copied, except using NdbSocket::transfer() */
44+
NdbSocket(const NdbSocket &) = delete; // do not copy
45+
~NdbSocket() { disable_locking(); }
46+
47+
void init_from_new(ndb_socket_t);
48+
void init_from_native(socket_t fd) { ndb_socket_init_from_native(s, fd); }
49+
void invalidate();
50+
int is_valid() const { return ndb_socket_valid(s); }
51+
bool has_tls() const { return ssl; }
52+
ndb_socket_t ndb_socket() const { return s; }
53+
socket_t native_socket() const { return ndb_socket_get_native(s); }
54+
std::string to_string() const;
55+
56+
/* Copy all properties of the original socket to the new one,
57+
* then invalidate the original.
58+
*/
59+
static void transfer(NdbSocket & newSocket, NdbSocket & original);
60+
61+
/* Get an SSL for client-side TLS.
62+
* Returns pointer to SSL. Returns null if CTX is null, or SSL_new() fails,
63+
* or if the OpenSSL version is not supported for NDB TLS.
64+
* When ready to switch over, call associate().
65+
*/
66+
static struct ssl_st * get_client_ssl(struct ssl_ctx_st *);
67+
68+
/* Get an SSL for for server-side TLS.
69+
* Returns pointer to SSL. Returns null if CTX is null, or SSL_new() fails,
70+
* or if the OpenSSL version is not supported for NDB TLS.
71+
* When ready to switch over, call associate().
72+
*/
73+
static struct ssl_st * get_server_ssl(struct ssl_ctx_st *);
74+
75+
/* Free an SSL returned by get_client_ssl() or get_server_ssl(), in the case
76+
* when an error has prevented it from being associated with a socket.
77+
*/
78+
static void free_ssl(struct ssl_st *);
79+
80+
/* Associate a socket with an SSL.
81+
* Returns true on success.
82+
* Returns false if socket already has an SSL association,
83+
* or on failure from the SSL socket table,
84+
* or on failure from SSL_set_fd().
85+
*/
86+
bool associate(struct ssl_st *);
87+
88+
/* Enable or disable mutex locking around SSL read and write calls.
89+
* Return true on success.
90+
*/
91+
bool enable_locking();
92+
bool disable_locking();
93+
94+
/* Run TLS handshake.
95+
* This must be done synchronously on a blocking socket.
96+
*
97+
* Returns:
98+
* true on success.
99+
* true if the socket does not have TLS enabled.
100+
* false on failure; the socket has been invalidated and closed.
101+
* false if socket is non-blocking (based on SSL mode flags)
102+
*/
103+
bool do_tls_handshake() { return ssl ? ssl_handshake() : true; }
104+
105+
/* Update keys (TLS 1.3).
106+
* If request_peer_update is set to true, request the peer to also update.
107+
* Returns true on success. If TLS 1.3 is not in use, returns false.
108+
*/
109+
bool update_keys(bool request_peer_update=false) const;
110+
111+
/* Renegotiate (TLS 1.2)
112+
* Like handshake, renegotiation is supported only as a synchronous action
113+
* on a blocking socket. Returns true if renegotiation has been scheduled;
114+
* caller should then call do_tls_handshake().
115+
* Returns false if connection is plaintext or is using TLS 1.3.
116+
*/
117+
bool renegotiate();
118+
119+
/* A single key_update_pending() function supports both TLS 1.2 and 1.3.
120+
* Returns true if a key update is pending (TLS 1.3).
121+
* Returns true if renegotiation is pending (TLS 1.2).
122+
*/
123+
bool key_update_pending() const;
124+
125+
/* Get peer's TLS certificate
126+
*
127+
* Returns nullptr if socket does not have TLS enabled.
128+
*/
129+
struct x509_st * peer_certificate() const;
130+
131+
/* Set socket behavior to blocking or non-blocking.
132+
For SSL sockets, this should be done after associate().
133+
This call also sets the appropriate TLS segment size and SSL mode flags
134+
for the socket.
135+
* blocking SSL sockets have mode flag SSL_MODE_AUTO_RETRY.
136+
* non-blocking SSL sockets have mode flags SSL_MODE_ENABLE_PARTIAL_WRITE
137+
and SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER.
138+
See SSL_set_mode(3) for reference on flags.
139+
*/
140+
int set_nonblocking(int on);
141+
142+
/* ndb_socket.h */
143+
ssize_t recv(char * buf, size_t len, int peek = 0) const;
144+
ssize_t send(const char * buf, size_t len) const;
145+
ssize_t writev(const struct iovec *vec, int nvec) const;
146+
147+
/* socket_io.h */
148+
/* read() reads with a timeout. write() writes with a timeout.
149+
150+
readln() reads a whole line, with timeout, and with optional unlocking
151+
of a held mutex while polling the socket. On success, it reads a whole
152+
null-terminated line, including the newline delimiter, into buf; it
153+
rewrites line \r\n to \n; and it resets *time to 0.
154+
It returns the length read, including the final null.
155+
readln() provides whole lines only; it returns -1 on timeout, end
156+
of file, or buffer full, even if a partial line would be available.
157+
*/
158+
int read(int timeout, char *buf, int len) const;
159+
int readln(int timeout, int *time, char *buf, int len, NdbMutex *) const;
160+
int write(int timeout, int *, const char * buf, int len) const;
161+
162+
/* For SSL sockets, close() removes the SSL from the SSL Socket Table */
163+
int close();
164+
void close_with_reset(bool with_reset);
165+
166+
/* ndb_socket_poller.h */
167+
uint add_readable(ndb_socket_poller *) const;
168+
uint add_writable(ndb_socket_poller *) const;
169+
int poll_readable(int timeout_msec) const;
170+
int poll_writable(int timeout_msec) const;
171+
bool check_hup() const;
172+
173+
private:
174+
NdbSocket & operator= (const NdbSocket &) = default;
175+
ssize_t ssl_recv(char * buf, size_t len) const;
176+
ssize_t ssl_peek(char * buf, size_t len) const;
177+
ssize_t ssl_send(const char * buf, size_t len) const;
178+
bool ssl_send(const char * buf, size_t len, size_t * written) const;
179+
ssize_t ssl_writev(const struct iovec *vec, int nvec) const;
180+
181+
int ssl_read(int timeout, char *buf, int len) const;
182+
int ssl_readln(int timeout, int *, char * buf, int len, NdbMutex *) const;
183+
int ssl_write(int timeout, int *, const char * buf, int len) const;
184+
185+
bool ssl_handshake();
186+
void ssl_close();
187+
188+
int send_several_iov(const struct iovec *, const int, int) const;
189+
int send_one_iov(const char * base, size_t len) const;
190+
int consolidate(const struct iovec *, const int, const int) const;
191+
192+
friend class TlsLineReader;
193+
194+
private:
195+
struct ssl_st * ssl{nullptr};
196+
NdbMutex * mutex{nullptr};
197+
ndb_socket_t s{INVALID_SOCKET};
198+
};
199+
200+
inline
201+
void NdbSocket::init_from_new(ndb_socket_t ndbsocket) {
202+
// assert(! socket_table_get_ssl(ndbsocket.s, false));
203+
init_from_native(ndbsocket.s);
204+
}
205+
206+
inline
207+
void NdbSocket::transfer(NdbSocket & newSocket, NdbSocket & original) {
208+
assert(! newSocket.is_valid());
209+
newSocket = original; // invokes the private copy operator
210+
original.invalidate();
211+
}
212+
213+
inline
214+
void NdbSocket::invalidate() {
215+
ssl = nullptr;
216+
mutex = nullptr;
217+
ndb_socket_invalidate(&s);
218+
}
219+
220+
inline
221+
std::string NdbSocket::to_string() const {
222+
std::string str = ndb_socket_to_string(s);
223+
if(ssl) str += " [ssl]";
224+
return str;
225+
}
226+
227+
/* ndb_socket.h */
228+
inline
229+
ssize_t NdbSocket::recv(char * buf, size_t len, int peek) const {
230+
if(ssl) {
231+
assert(!peek); // could be supported but is not expected
232+
return ssl_recv(buf, len);
233+
}
234+
return ndb_recv(s, buf, len, peek);
235+
}
236+
237+
inline
238+
ssize_t NdbSocket::send(const char * buf, size_t len) const {
239+
if(ssl) return ssl_send(buf, len);
240+
return ndb_send(s, buf, len, 0);
241+
}
242+
243+
inline
244+
ssize_t NdbSocket::writev(const struct iovec *vec, int nvec) const {
245+
if(ssl) return ssl_writev(vec, nvec);
246+
return ndb_socket_writev(s, vec, nvec);
247+
}
248+
249+
inline
250+
int NdbSocket::close() {
251+
if(ssl) ssl_close();
252+
disable_locking();
253+
return ndb_socket_close(s);
254+
}
255+
256+
inline
257+
void NdbSocket::close_with_reset(bool with_reset) {
258+
if(ssl) ssl_close();
259+
ndb_socket_close_with_reset(s, with_reset);
260+
}
261+
262+
/* ndb_socket_poller.h */
263+
inline
264+
uint NdbSocket::add_readable(ndb_socket_poller * poller) const {
265+
return poller->add_readable(s);
266+
}
267+
268+
inline
269+
uint NdbSocket::add_writable(ndb_socket_poller * poller) const {
270+
return poller->add_writable(s);
271+
}
272+
273+
inline
274+
int NdbSocket::poll_readable(int timeout) const {
275+
ndb_socket_poller poller;
276+
poller.add_readable(s);
277+
return poller.poll(timeout);
278+
}
279+
280+
inline
281+
int NdbSocket::poll_writable(int timeout) const {
282+
ndb_socket_poller poller;
283+
poller.add_writable(s);
284+
return poller.poll(timeout);
285+
}
286+
287+
inline
288+
bool NdbSocket::check_hup() const {
289+
ndb_socket_poller poller;
290+
poller.add_readable(s);
291+
if(poller.poll_unsafe(0) > 0 && poller.has_hup(0))
292+
return true;
293+
return false;
294+
}
295+
296+
#endif
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
Copyright (c) 2022, Oracle and/or its affiliates.
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License, version 2.0,
6+
as published by the Free Software Foundation.
7+
8+
This program is also distributed with certain software (including
9+
but not limited to OpenSSL) that is licensed under separate terms,
10+
as designated in a particular file or component or in included license
11+
documentation. The authors of MySQL hereby grant you an additional
12+
permission to link the program and your derivative works with the
13+
separately licensed software that they have included with MySQL.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU General Public License, version 2.0, for more details.
19+
20+
You should have received a copy of the GNU General Public License
21+
along with this program; if not, write to the Free Software
22+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23+
*/
24+
25+
/* Include this file *after* ndb_socket.h and socket_io.h.
26+
It will redefine the functions that should be replaced with NdbSocket
27+
method calls, so that the compiler catches them as errors.
28+
29+
The error message will be along the lines of "error: expected expression".
30+
*/
31+
32+
#define ndb_socket_close(A)
33+
#define ndb_socket_nonblock(A, B)
34+
#define ndb_recv(A, B, C, D)
35+
#define ndb_send(A, B, C, D)
36+
#define ndb_socket_writev(A, B, C)
37+
#define read_socket(A, B, C)
38+
#define readln_socket(A, B, C, D, E, F)
39+
#define write_socket(A, B, C, D, E)
40+

0 commit comments

Comments
 (0)