Skip to content

Commit 4ba9baa

Browse files
committed
WL#15524 Patch #1 "START TLS" for management API
Add an MGM protocol command to turn a plaintext mgm api session into a TLS session. Add three new MGM API functions: ndb_mgm_set_ssl_ctx() ndb_mgm_start_tls() ndb_mgm_connect_tls() Define two client TLS requirement levels: CLIENT_TLS_RELAXED, CLIENT_TLS_STRICT This adds a new test: testMgmd -n StartTls Change-Id: Ib46faacd9198c474558e46c3fa0538c7e759f3fb
1 parent 8948014 commit 4ba9baa

File tree

7 files changed

+265
-16
lines changed

7 files changed

+265
-16
lines changed

storage/ndb/include/mgmapi/mgmapi.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,6 +1573,60 @@ extern "C" {
15731573
ndb_mgm_dump_events(NdbMgmHandle handle, enum Ndb_logevent_type type,
15741574
int no_of_nodes, const int * node_list);
15751575

1576+
/** @} *********************************************************************/
1577+
/**
1578+
* @name Functions: Transport Layer Security (TLS)
1579+
* @{
1580+
*/
1581+
1582+
/**
1583+
* Set an SSL CTX for a handle.
1584+
*
1585+
* @param handle Management handle
1586+
* @param ctx SSL_ctx to be used for TLS and HTTPS connections
1587+
*
1588+
* @return True on success, false if ctx has already been set
1589+
*
1590+
*/
1591+
bool ndb_mgm_set_ssl_ctx(NdbMgmHandle handle, struct ssl_ctx_st *ctx);
1592+
1593+
/**
1594+
* Start TLS. Upgrade an open, unencrypted connection to a secure one.
1595+
*
1596+
* @param handle Management handle
1597+
*
1598+
* @return 0 on success.
1599+
*/
1600+
int ndb_mgm_start_tls(NdbMgmHandle handle);
1601+
1602+
/**
1603+
* Connects to a management server. This function wraps a call to
1604+
* ndb_mgm_connect(), followed by a call to ndb_mgm_start_tls().
1605+
*
1606+
* In order to attempt TLS, the user must first have called
1607+
* ndb_mgm_set_ssl_ctx().
1608+
*
1609+
* If tls_req_level is CLIENT_TLS_RELAXED, TLS authentication failures still
1610+
* result in errors, but a missing certificate or server refusal will result
1611+
* in a succesful cleartext connection.
1612+
*
1613+
* If tls_req_level is CLIENT_TLS_STRICT, then any failure to establish TLS
1614+
* will be treated as an error, and the connection will be closed.
1615+
*
1616+
* @param handle Management handle.
1617+
* @param no_retries Number of retries to connect
1618+
* (0 means connect once).
1619+
* @param retry_delay_in_seconds
1620+
* How long to wait until retry is performed.
1621+
* @param verbose Make printout regarding connect retries.
1622+
* @param tls_req_level TLS requirement level
1623+
*
1624+
* @return 0 on success, -1 on failure.
1625+
*/
1626+
int ndb_mgm_connect_tls(NdbMgmHandle handle, int no_retries,
1627+
int retry_delay_in_seconds, int verbose,
1628+
int tls_req_level);
1629+
15761630
#ifdef __cplusplus
15771631
}
15781632
#endif

storage/ndb/include/mgmapi/mgmapi_config_parameters.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,4 +390,7 @@
390390
#define OPERATION_REDO_PROBLEM_ACTION_ABORT 0
391391
#define OPERATION_REDO_PROBLEM_ACTION_QUEUE 1
392392

393+
#define CLIENT_TLS_RELAXED 0
394+
#define CLIENT_TLS_STRICT 1
395+
393396
#endif

storage/ndb/include/mgmapi/mgmapi_error.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,13 @@ extern "C" {
5656
NDB_MGM_BIND_ADDRESS = 1012,
5757
/** Supplied bind-address is illegal */
5858
NDB_MGM_ILLEGAL_BIND_ADDRESS = 1013,
59-
/** Cannot convert TLS MGM connection to transporter */
60-
NDB_MGM_CANNOT_CONVERT_TO_TRANSPORTER = 1014,
61-
59+
/** TLS is not available; client-side error */
60+
NDB_MGM_TLS_ERROR = 1014,
61+
/** Server refused to upgrade connection to TLS */
62+
NDB_MGM_TLS_REFUSED = 1015,
63+
/** TLS handshake failed; connection closed */
64+
NDB_MGM_TLS_HANDSHAKE_FAILED = 1016,
65+
6266
/* Alloc node id failures */
6367
/** Generic error, retry may succeed */
6468
NDB_MGM_ALLOCID_ERROR = 1101,

storage/ndb/src/mgmapi/mgmapi.cpp

Lines changed: 102 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include <EventLogger.hpp>
4848
#include <memory>
4949
#include "portlib/ndb_sockaddr.h"
50+
#include "mgmcommon/NdbMgm.hpp"
5051

5152
//#define MGMAPI_LOG
5253
#define MGM_CMD(name, fun, desc) \
@@ -113,6 +114,7 @@ struct ndb_mgm_handle {
113114
int mgmd_version_major;
114115
int mgmd_version_minor;
115116
int mgmd_version_build;
117+
struct ssl_ctx_st * ssl_ctx;
116118

117119
int mgmd_version(void) const {
118120
// Must be connected
@@ -252,6 +254,7 @@ ndb_mgm_create_handle()
252254
h->m_bindaddress = nullptr;
253255
h->m_bindaddress_port = 0;
254256
h->ignore_sigpipe = true;
257+
h->ssl_ctx = nullptr;
255258

256259
strncpy(h->last_error_desc, "No error", NDB_MGM_MAX_ERR_DESC_SIZE);
257260

@@ -2521,21 +2524,25 @@ ndb_mgm_listen_event_internal(NdbMgmHandle handle, const int filter[],
25212524
}
25222525

25232526
{
2524-
ndb_mgm_handle * tmp_handle = ndb_mgm_create_handle();
2527+
ndb_mgm::handle_ptr tmp_handle(ndb_mgm_create_handle());
25252528
tmp_handle->socket.init_from_new(sockfd);
25262529

2530+
if(handle->ssl_ctx)
2531+
{
2532+
ndb_mgm_set_ssl_ctx(tmp_handle.get(), handle->ssl_ctx);
2533+
ndb_mgm_start_tls(tmp_handle.get());
2534+
}
2535+
25272536
const Properties *reply;
2528-
reply = ndb_mgm_call(tmp_handle, stat_reply, "listen event", &args);
2537+
reply = ndb_mgm_call(tmp_handle.get(), stat_reply, "listen event", &args);
25292538

25302539
if(reply == nullptr) {
25312540
ndb_socket_close(sockfd);
2532-
CHECK_REPLY(tmp_handle, reply, -1)
2541+
CHECK_REPLY(tmp_handle.get(), reply, -1)
25332542
} else {
25342543
delete reply;
2535-
tmp_handle->connected = 0; // so that destroy_handle() doesn't close it.
2544+
tmp_handle.get()->connected = 0; // so that destructor doesn't close it.
25362545
}
2537-
2538-
ndb_mgm_destroy_handle(&tmp_handle);
25392546
}
25402547

25412548
*sock= sockfd;
@@ -2607,6 +2614,95 @@ ndb_mgm_get_configuration_from_node(NdbMgmHandle handle,
26072614
NDB_MGM_NODE_TYPE_UNKNOWN, nodeid);
26082615
}
26092616

2617+
extern "C"
2618+
bool
2619+
ndb_mgm_set_ssl_ctx(NdbMgmHandle handle, struct ssl_ctx_st *ctx)
2620+
{
2621+
if(handle && (handle->ssl_ctx == nullptr)) {
2622+
handle->ssl_ctx = ctx;
2623+
return true;
2624+
}
2625+
return false;
2626+
}
2627+
2628+
extern "C"
2629+
int
2630+
ndb_mgm_start_tls(NdbMgmHandle handle)
2631+
{
2632+
bool server_ok = false;
2633+
2634+
if(handle->ssl_ctx == nullptr) {
2635+
SET_ERROR(handle, NDB_MGM_TLS_ERROR, "SSL CTX required");
2636+
return -1;
2637+
}
2638+
2639+
if(handle->socket.has_tls()) {
2640+
SET_ERROR(handle, NDB_MGM_TLS_ERROR, "Socket already has TLS");
2641+
return -2;
2642+
}
2643+
2644+
const ParserRow<ParserDummy> start_tls_reply[] = {
2645+
MGM_CMD("start tls reply", NULL, ""),
2646+
MGM_ARG("result", String, Mandatory, "Error message"),
2647+
MGM_END()
2648+
};
2649+
2650+
Properties args;
2651+
const Properties * reply =
2652+
ndb_mgm_call(handle, start_tls_reply, "start tls", &args);
2653+
2654+
if(reply) {
2655+
BaseString result;
2656+
reply->get("result", result);
2657+
if(strcmp(result.c_str(), "Ok") == 0) server_ok = true;
2658+
delete reply;
2659+
}
2660+
2661+
if(! server_ok) {
2662+
SET_ERROR(handle, NDB_MGM_TLS_REFUSED, "Server refused upgrade");
2663+
return -3;
2664+
}
2665+
2666+
struct ssl_st * ssl = NdbSocket::get_client_ssl(handle->ssl_ctx);
2667+
if(! (ssl && handle->socket.associate(ssl))) {
2668+
SET_ERROR(handle, NDB_MGM_TLS_ERROR, "Failed in client");
2669+
NdbSocket::free_ssl(ssl);
2670+
return -4;
2671+
}
2672+
2673+
/* Now run handshake */
2674+
bool r = handle->socket.do_tls_handshake();
2675+
if(r) return 0; // success
2676+
2677+
SET_ERROR(handle, NDB_MGM_TLS_HANDSHAKE_FAILED, "Handshake failed");
2678+
return -5;
2679+
}
2680+
2681+
extern "C"
2682+
int
2683+
ndb_mgm_connect_tls(NdbMgmHandle handle, int retries,
2684+
int retry_delay, int verbose, int tls_level)
2685+
{
2686+
if(tls_level < CLIENT_TLS_RELAXED || tls_level > CLIENT_TLS_STRICT) {
2687+
SET_ERROR(handle, NDB_MGM_USAGE_ERROR, "Invalid TLS level");
2688+
return -1;
2689+
}
2690+
2691+
int r = ndb_mgm_connect(handle, retries, retry_delay, verbose);
2692+
if(r != 0)
2693+
return r;
2694+
2695+
r = ndb_mgm_start_tls(handle);
2696+
2697+
if(r == 0)
2698+
return 0; // TLS started successfully
2699+
2700+
if((tls_level == CLIENT_TLS_RELAXED) && (r != -5))
2701+
return 0; // -5 is fatal (handshake failed); otherwise okay.
2702+
2703+
return -1;
2704+
}
2705+
26102706
extern "C"
26112707
int
26122708
ndb_mgm_start_signallog(NdbMgmHandle handle, int nodeId,
@@ -3667,13 +3763,6 @@ ndb_mgm_convert_to_transporter(NdbMgmHandle *handle)
36673763
DBUG_RETURN(s);
36683764
}
36693765

3670-
if ((*handle)->socket.has_tls())
3671-
{
3672-
SET_ERROR(*handle, NDB_MGM_CANNOT_CONVERT_TO_TRANSPORTER, "");
3673-
ndb_socket_invalidate(&s);
3674-
DBUG_RETURN(s);
3675-
}
3676-
36773766
(*handle)->connected= 0; // we pretend we're disconnected
36783767
s= (*handle)->socket.ndb_socket();
36793768

storage/ndb/src/mgmsrv/Services.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ ParserRow<MgmApiSession> commands[] = {
245245

246246
MGM_CMD("start all", &MgmApiSession::startAll, ""),
247247

248+
MGM_CMD("start tls", &MgmApiSession::startTls, ""),
249+
248250
MGM_CMD("bye", &MgmApiSession::bye, ""),
249251

250252
MGM_CMD("end session", &MgmApiSession::endSession, ""),
@@ -1448,6 +1450,39 @@ MgmApiSession::startAll(Parser<MgmApiSession>::Context &,
14481450
m_output->println("%s", "");
14491451
}
14501452

1453+
void
1454+
MgmApiSession::startTls(Parser<MgmApiSession>::Context &,
1455+
Properties const &) {
1456+
struct ssl_ctx_st * ctx = nullptr;
1457+
struct ssl_st * ssl = nullptr;
1458+
const char * result = "Failed";
1459+
1460+
if(m_secure_socket.has_tls()) {
1461+
result = "Already Connected";
1462+
} else {
1463+
ctx = m_mgmsrv.theFacade->get_registry()->getTlsKeyManager()->ctx();
1464+
}
1465+
1466+
if(ctx)
1467+
ssl = NdbSocket::get_server_ssl(ctx);
1468+
1469+
if(ssl)
1470+
result = "Ok";
1471+
1472+
/* Send the reply to the client */
1473+
m_output->println("start tls reply");
1474+
m_output->println("result: %s", result);
1475+
m_output->println("%s", "");
1476+
m_output->flush();
1477+
1478+
/* Run the TLS handshake */
1479+
if(ssl) {
1480+
if(m_secure_socket.associate(ssl))
1481+
m_secure_socket.do_tls_handshake();
1482+
else
1483+
NdbSocket::free_ssl(ssl);
1484+
}
1485+
}
14511486

14521487
static bool
14531488
setEventLogFilter(int severity, int enable)

storage/ndb/src/mgmsrv/Services.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ class MgmApiSession : public SocketServer::Session
108108
void stopAll(Parser_t::Context &ctx, const class Properties &args);
109109
void start(Parser_t::Context &ctx, const class Properties &args);
110110
void startAll(Parser_t::Context &ctx, const class Properties &args);
111+
void startTls(Parser_t::Context &ctx, const class Properties &args);
111112
void bye(Parser_t::Context &ctx, const class Properties &args);
112113
void endSession(Parser_t::Context &ctx, const class Properties &args);
113114
void setLogLevel(Parser_t::Context &ctx, const class Properties &args);

storage/ndb/test/ndbapi/testMgmd.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,6 +2027,64 @@ runTestNdbdWithCert(NDBT_Context* ctx, NDBT_Step* step)
20272027
return NDBT_OK;
20282028
}
20292029

2030+
int runTestStartTls(NDBT_Context* ctx, NDBT_Step* step)
2031+
{
2032+
NDBT_Workingdir wd("test_tls"); // temporary working directory
2033+
TlsKeyManager tls_km;
2034+
int major, minor, build, r;
2035+
char ver[128];
2036+
static constexpr int len = sizeof(ver);
2037+
2038+
BaseString cfg_path = path(wd.path(), "config.ini", nullptr);
2039+
Properties config = ConfigFactory::create();
2040+
CHECK(ConfigFactory::write_config_ini(config, cfg_path.c_str()));
2041+
2042+
sign_tls_keys(wd);
2043+
2044+
Mgmd mgmd(1);
2045+
2046+
NdbProcess::Args mgmdArgs;
2047+
mgmd.common_args(mgmdArgs, wd.path());
2048+
mgmdArgs.add("--ndb-tls-search-path=", wd.path());
2049+
2050+
CHECK(mgmd.start(wd.path(), mgmdArgs)); // Start management node
2051+
CHECK(mgmd.connect(config)); // Connect to management node
2052+
CHECK(mgmd.wait_confirmed_config()); // Wait for configuration
2053+
2054+
tls_km.init(wd.path(), 0, NODE_TYPE_API, true);
2055+
CHECK(tls_km.ctx());
2056+
2057+
r = ndb_mgm_get_version(mgmd.handle(), &major, &minor, &build, len, ver);
2058+
CHECK(r == 1);
2059+
printf("Version: %d.%d.%d %s\n", major, minor, build, ver);
2060+
2061+
r = ndb_mgm_start_tls(mgmd.handle());
2062+
CHECK(r == -1); // -1 is "SSL CTX required"
2063+
CHECK(ndb_mgm_get_latest_error(mgmd.handle()) == NDB_MGM_TLS_ERROR);
2064+
2065+
r = ndb_mgm_set_ssl_ctx(mgmd.handle(), tls_km.ctx());
2066+
CHECK(r); // first time setting ctx succeeds
2067+
r = ndb_mgm_set_ssl_ctx(mgmd.handle(), nullptr);
2068+
CHECK(r == 0); // second time setting ctx fails
2069+
2070+
r = ndb_mgm_start_tls(mgmd.handle());
2071+
printf("ndb_mgm_start_tls(): %d\n", r);
2072+
CHECK(r == 0);
2073+
2074+
r = ndb_mgm_start_tls(mgmd.handle());
2075+
CHECK(r == -2); // -2 is "Socket already has TLS"
2076+
2077+
/* We have switched to TLS. Now run a command. */
2078+
r = ndb_mgm_get_version(mgmd.handle(), &major, &minor, &build, len, ver);
2079+
CHECK(r == 1);
2080+
2081+
/* And run another command. */
2082+
struct ndb_mgm_cluster_state *state = ndb_mgm_get_status(mgmd.handle());
2083+
CHECK(state != nullptr);
2084+
2085+
return NDBT_OK;
2086+
}
2087+
20302088
NDBT_TESTSUITE(testMgmd);
20312089
DRIVER(DummyDriver); /* turn off use of NdbApi */
20322090

@@ -2146,6 +2204,11 @@ TESTCASE("NdbdWithCertificate", "Test data node startup with certificate")
21462204
INITIALIZER(runTestNdbdWithCert)
21472205
}
21482206

2207+
TESTCASE("StartTls", "Test START TLS in MGM protocol")
2208+
{
2209+
INITIALIZER(runTestStartTls);
2210+
}
2211+
21492212
#endif
21502213

21512214
NDBT_TESTSUITE_END(testMgmd)

0 commit comments

Comments
 (0)