|
| 1 | +/* Copyright (c) 2022, 2023, 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 | +#ifndef NDB_UTIL_TLS_KEY_MANAGER_H |
| 25 | +#define NDB_UTIL_TLS_KEY_MANAGER_H |
| 26 | + |
| 27 | +#include "ndb_limits.h" // MAX_NODES |
| 28 | + |
| 29 | +#include "portlib/NdbMutex.h" |
| 30 | +#include "util/NodeCertificate.hpp" |
| 31 | +#include "util/NdbSocket.h" |
| 32 | +#include "util/TlsKeyErrors.h" |
| 33 | + |
| 34 | +struct cert_table_entry { |
| 35 | + time_t expires; |
| 36 | + const char * name; |
| 37 | + const char * serial; |
| 38 | +}; |
| 39 | + |
| 40 | +class ClientAuthorization; |
| 41 | + |
| 42 | +class TlsKeyManager { |
| 43 | +public: |
| 44 | + enum TlsVersion { Tls12, Tls13 }; |
| 45 | + |
| 46 | + TlsKeyManager(); |
| 47 | + ~TlsKeyManager(); |
| 48 | + |
| 49 | + /* TlsKeyManager::init() for NDB nodes. |
| 50 | + All error and info messages are logged to g_EventLogger. |
| 51 | + You can test whether init() has succeeded by calling ctx(). |
| 52 | + */ |
| 53 | + void init(const char * tls_search_path, |
| 54 | + int node_id, int node_type, bool is_primary); |
| 55 | + |
| 56 | + /* init() for MGM Clients that will not have a node ID */ |
| 57 | + void init_mgm_client(const char * tls_search_path, |
| 58 | + Node::Type type = Node::Type::Client); |
| 59 | + |
| 60 | + /* Alternate versions of init() used for authentication testing */ |
| 61 | + void init(int node_id, const NodeCertificate *); |
| 62 | + void init(int node_id, struct stack_st_X509 *, struct evp_pkey_st *); |
| 63 | + |
| 64 | + /* Get SSL_CTX */ |
| 65 | + struct ssl_ctx_st * ctx() const { return m_ctx; } |
| 66 | + |
| 67 | + void cert_table_set(int node_id, struct x509_st *); |
| 68 | + void cert_table_clear(int node_id); |
| 69 | + bool iterate_cert_table(int & node_id, cert_table_entry *); |
| 70 | + |
| 71 | + // Check replacement date of our own node certificate |
| 72 | + // pct should be a number between 0.0 and 1.0, where 0 represents the |
| 73 | + // not-valid-before date 1 represents the not-valid-after date. |
| 74 | + // Returns true if the current time is strictly less than pct. |
| 75 | + bool check_replace_date(float pct); |
| 76 | + |
| 77 | + /* Class method: TLS verification callback */ |
| 78 | + static int on_verify(int result, struct x509_store_ctx_st *); |
| 79 | + |
| 80 | + /* Class methods: certificate hostname authorization checks. |
| 81 | +
|
| 82 | + The check of a server's certificate is a simple comparison between the |
| 83 | + hostnames in the cert and the name the client used in order to reach the |
| 84 | + server. |
| 85 | +
|
| 86 | + The check of a client's certificate requires a DNS lookup. It is divided |
| 87 | + into a "fast" part, in check_socket_for_auth(), and a "slow" (blocking) |
| 88 | + part, in perform_client_host_auth(). The API is designed to allow the |
| 89 | + slow part to run asynchronously if needed. |
| 90 | + */ |
| 91 | + |
| 92 | + // Client-side checks of server cert: |
| 93 | + static int check_server_host_auth(const NdbSocket &, const char *name); |
| 94 | + static int check_server_host_auth(struct x509_st *, const char *name); |
| 95 | + static int check_server_host_auth(const NodeCertificate &, const char *); |
| 96 | + |
| 97 | + /* Server-side check of client cert: |
| 98 | +
|
| 99 | + check_socket_for_auth() can return a non-zero TlsKeyError error code: |
| 100 | + auth2_no_cert if socket has no client certificate; |
| 101 | + auth2_bad_common_name if cert CN is not valid for NDB; |
| 102 | + auth2_bad_socket if getpeername() fails. |
| 103 | +
|
| 104 | + Otherwise it returns zero, and the caller should check the contents |
| 105 | + of pAuth. If *pAuth is null, the certificate is not bound to a |
| 106 | + hostname, so authorization is complete. If *pAuth is non-null, |
| 107 | + hostname authorization is required, and the user should call |
| 108 | + perform_client_host_auth(*pAuth). |
| 109 | + */ |
| 110 | + static int check_socket_for_auth(const NdbSocket & socket, |
| 111 | + ClientAuthorization ** pAuth); |
| 112 | + |
| 113 | + /* test harness */ |
| 114 | + static ClientAuthorization * test_client_auth(struct x509_st *, |
| 115 | + const struct addrinfo *); |
| 116 | + |
| 117 | + /* perform_client_host_auth() checks the socket peer against the certificate |
| 118 | + hostname, using DNS lookup. It will block, synchronously waiting for |
| 119 | + DNS. On return, the supplied ClientAuthorization will have been |
| 120 | + deleted. Returns a TlsKeyError code. |
| 121 | + */ |
| 122 | + static int perform_client_host_auth(ClientAuthorization *); |
| 123 | + |
| 124 | + |
| 125 | +protected: |
| 126 | + void initialize_context(); |
| 127 | + |
| 128 | + void log_error(TlsKeyError::code); |
| 129 | + void log_error() const; |
| 130 | + |
| 131 | + bool open_active_cert(); |
| 132 | + |
| 133 | + static constexpr Node::Type cert_type[3] = { |
| 134 | + /* indexed to NODE_TYPE_DB, NODE_TYPE_API, NODE_TYPE_MGM */ |
| 135 | + Node::Type::DB, Node::Type::Client, Node::Type::MGMD |
| 136 | + }; |
| 137 | + |
| 138 | +private: |
| 139 | + enum UserType { Primary, SecondaryApi, MgmClient }; |
| 140 | + |
| 141 | + PkiFile::PathName m_key_file, m_cert_file; |
| 142 | + char * m_path_string {nullptr}; |
| 143 | + TlsSearchPath * m_search_path {nullptr}; |
| 144 | + static constexpr size_t SN_buf_len = 30; |
| 145 | + static constexpr size_t CN_buf_len = 65; |
| 146 | + struct private_cert_table_entry { |
| 147 | + char serial[SN_buf_len]; |
| 148 | + char name[CN_buf_len]; |
| 149 | + time_t expires {0}; |
| 150 | + bool active {false}; |
| 151 | + } m_cert_table[MAX_NODES]; |
| 152 | + NodeCertificate m_node_cert; |
| 153 | + NdbMutex m_cert_table_mutex; |
| 154 | + int m_error {0}; |
| 155 | + int m_node_id; |
| 156 | + Node::Type m_type; |
| 157 | + struct ssl_ctx_st * m_ctx {nullptr}; |
| 158 | + |
| 159 | + void init(const char *, int, Node::Type, UserType); |
| 160 | + |
| 161 | + bool cert_table_get(const private_cert_table_entry &, |
| 162 | + cert_table_entry *) const; |
| 163 | + void free_path_strings(); |
| 164 | +}; |
| 165 | + |
| 166 | +inline void TlsKeyManager::init_mgm_client(const char * tls_search_path, |
| 167 | + Node::Type type) |
| 168 | +{ |
| 169 | + init(tls_search_path, 0, type, MgmClient); |
| 170 | +} |
| 171 | + |
| 172 | +#endif |
0 commit comments