2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
19 * Copyright 2015 Cloudius Systems
24 #include <unordered_set>
26 #include <boost/any.hpp>
28 #include <seastar/core/future.hh>
29 #include <seastar/core/sstring.hh>
30 #include <seastar/core/shared_ptr.hh>
31 #include <seastar/net/socket_defs.hh>
32 #include <seastar/util/std-compat.hh>
33 #include <seastar/net/api.hh>
34 #include "../core/internal/api-level.hh"
41 class connected_socket;
45 * Relatively thin SSL wrapper for socket IO.
46 * (Can be expanded to other IO forms).
48 * The current underlying mechanism is
49 * gnutls, however, all interfaces are kept
50 * agnostic, so in theory it could be replaced
51 * with OpenSSL or similar.
55 enum class x509_crt_format {
60 typedef std::basic_string_view<char> blob;
64 class server_credentials;
65 class certificate_credentials;
66 class credentials_builder;
69 * Diffie-Hellman parameters for
81 dh_params(level = level::LEGACY);
82 // loads a key from data
83 dh_params(const blob&, x509_crt_format);
86 dh_params(dh_params&&) noexcept;
87 dh_params& operator=(dh_params&&) noexcept;
89 dh_params(const dh_params&) = delete;
90 dh_params& operator=(const dh_params&) = delete;
92 /** loads a key from file */
93 static future<dh_params> from_file(const sstring&, x509_crt_format);
96 friend class server_credentials;
97 friend class certificate_credentials;
98 std::unique_ptr<impl> _impl;
102 x509_cert(const blob&, x509_crt_format);
104 static future<x509_cert> from_file(const sstring&, x509_crt_format);
107 x509_cert(shared_ptr<impl>);
108 shared_ptr<impl> _impl;
111 class abstract_credentials {
113 virtual ~abstract_credentials() {};
115 virtual void set_x509_trust(const blob&, x509_crt_format) = 0;
116 virtual void set_x509_crl(const blob&, x509_crt_format) = 0;
117 virtual void set_x509_key(const blob& cert, const blob& key, x509_crt_format) = 0;
119 virtual void set_simple_pkcs12(const blob&, x509_crt_format, const sstring& password) = 0;
121 virtual future<> set_x509_trust_file(const sstring& cafile, x509_crt_format);
122 virtual future<> set_x509_crl_file(const sstring& crlfile, x509_crt_format);
123 virtual future<> set_x509_key_file(const sstring& cf, const sstring& kf, x509_crt_format);
125 virtual future<> set_simple_pkcs12_file(const sstring& pkcs12file, x509_crt_format, const sstring& password);
128 template<typename Base>
129 class reloadable_credentials;
132 * Enum like tls::session::type but independent of gnutls headers
134 * \warning Uses a different internal encoding than tls::session::type
136 enum class session_type {
141 * Callback prototype for receiving Distinguished Name (DN) information
143 * \param type Our own role in the TLS handshake (client vs. server)
144 * \param subject The subject DN string
145 * \param issuer The issuer DN string
147 using dn_callback = noncopyable_function<void(session_type type, sstring subject, sstring issuer)>;
150 * Holds certificates and keys.
152 * Typically, credentials are shared for multiple client/server
153 * sessions. Changes to the credentials object will affect all
154 * sessions instantiated with it.
155 * You should probably set it up once, before starting client/server
158 class certificate_credentials : public abstract_credentials {
160 certificate_credentials();
161 ~certificate_credentials();
163 certificate_credentials(certificate_credentials&&) noexcept;
164 certificate_credentials& operator=(certificate_credentials&&) noexcept;
166 certificate_credentials(const certificate_credentials&) = delete;
167 certificate_credentials& operator=(const certificate_credentials&) = delete;
169 void set_x509_trust(const blob&, x509_crt_format) override;
170 void set_x509_crl(const blob&, x509_crt_format) override;
171 void set_x509_key(const blob& cert, const blob& key, x509_crt_format) override;
172 void set_simple_pkcs12(const blob&, x509_crt_format, const sstring& password) override;
175 * Loads default system cert trust file
178 future<> set_system_trust();
180 // TODO add methods for certificate verification
183 * TLS handshake priority string. See gnutls docs and syntax at
184 * https://gnutls.org/manual/html_node/Priority-Strings.html
186 * Allows specifying order and allowance for handshake alg.
188 void set_priority_string(const sstring&);
191 * Register a callback for receiving Distinguished Name (DN) information
192 * during the TLS handshake, extracted from the certificate as sent by the peer.
194 * The callback is not invoked in case the peer did not send a certificate.
195 * (This could e.g. happen when we are the server, and a client connects while
196 * client_auth is not set to REQUIRE.)
198 * If, based upon the extracted DN information, you want to abort the handshake,
199 * then simply throw an exception (e.g., from the callback) like verification_error.
201 * Registering this callback does not bypass the 'standard' certificate verification
202 * procedure; instead it merely extracts the DN information from the peer certificate
203 * (i.e., the 'leaf' certificate from the chain of certificates sent by the peer)
204 * and allows for extra checks.
206 * To keep the API simple, you can unregister the callback by means of registering
207 * an empty callback, i.e. dn_callback{}
209 * The callback prototype is documented in the dn_callback typedef.
211 void set_dn_verification_callback(dn_callback);
215 friend class session;
216 friend class server_session;
217 friend class server_credentials;
218 friend class credentials_builder;
219 template<typename Base>
220 friend class reloadable_credentials;
221 shared_ptr<impl> _impl;
224 /** Exception thrown on certificate validation error */
225 class verification_error : public std::runtime_error {
227 using runtime_error::runtime_error;
230 enum class client_auth {
231 NONE, REQUEST, REQUIRE
235 * Extending certificates and keys for server usage.
236 * More probably goes in here...
238 class server_credentials : public certificate_credentials {
240 server_credentials();
241 server_credentials(shared_ptr<dh_params>);
242 server_credentials(const dh_params&);
244 server_credentials(server_credentials&&) noexcept;
245 server_credentials& operator=(server_credentials&&) noexcept;
247 server_credentials(const server_credentials&) = delete;
248 server_credentials& operator=(const server_credentials&) = delete;
250 void set_client_auth(client_auth);
253 class reloadable_credentials_base;
255 using reload_callback = std::function<void(const std::unordered_set<sstring>&, std::exception_ptr)>;
258 * Intentionally "primitive", and more importantly, copyable
259 * container for certificate credentials options.
260 * The intendend use case is to be able to use across shards,
261 * at, say, initialization of tls objects
263 * Note that loading invalid objects (malformed certs etc) will
264 * _not_ generate exceptions until, earliest, the build functions
267 class credentials_builder : public abstract_credentials {
269 void set_dh_level(dh_params::level = dh_params::level::LEGACY);
271 void set_x509_trust(const blob&, x509_crt_format) override ;
272 void set_x509_crl(const blob&, x509_crt_format) override;
273 void set_x509_key(const blob& cert, const blob& key, x509_crt_format) override;
274 void set_simple_pkcs12(const blob&, x509_crt_format, const sstring& password) override;
276 future<> set_x509_trust_file(const sstring& cafile, x509_crt_format) override;
277 future<> set_x509_crl_file(const sstring& crlfile, x509_crt_format) override;
278 future<> set_x509_key_file(const sstring& cf, const sstring& kf, x509_crt_format) override;
279 future<> set_simple_pkcs12_file(const sstring& pkcs12file, x509_crt_format, const sstring& password) override;
281 future<> set_system_trust();
282 void set_client_auth(client_auth);
283 void set_priority_string(const sstring&);
285 void apply_to(certificate_credentials&) const;
287 shared_ptr<certificate_credentials> build_certificate_credentials() const;
288 shared_ptr<server_credentials> build_server_credentials() const;
290 // same as above, but any files used for certs/keys etc will be watched
291 // for modification and reloaded if changed
292 future<shared_ptr<certificate_credentials>> build_reloadable_certificate_credentials(reload_callback = {}, std::optional<std::chrono::milliseconds> tolerance = {}) const;
293 future<shared_ptr<server_credentials>> build_reloadable_server_credentials(reload_callback = {}, std::optional<std::chrono::milliseconds> tolerance = {}) const;
295 friend class reloadable_credentials_base;
297 std::multimap<sstring, boost::any> _blobs;
298 client_auth _client_auth = client_auth::NONE;
303 * Creates a TLS client connection using the default network stack and the
304 * supplied credentials.
305 * Typically these should contain enough information
306 * to validate the remote certificate (i.e. trust info).
308 * \param name An optional expected server name for the remote end point
311 future<connected_socket> connect(shared_ptr<certificate_credentials>, socket_address, sstring name = {});
312 future<connected_socket> connect(shared_ptr<certificate_credentials>, socket_address, socket_address local, sstring name = {});
316 * Creates a socket through which a TLS client connection can be created,
317 * using the default network stack and the supplied credentials.
318 * Typically these should contain enough information
319 * to validate the remote certificate (i.e. trust info).
321 * \param name An optional expected server name for the remote end point
324 ::seastar::socket socket(shared_ptr<certificate_credentials>, sstring name = {});
327 /** Wraps an existing connection in SSL/TLS. */
329 future<connected_socket> wrap_client(shared_ptr<certificate_credentials>, connected_socket&&, sstring name = {});
330 future<connected_socket> wrap_server(shared_ptr<server_credentials>, connected_socket&&);
334 * Creates a server socket that accepts SSL/TLS clients using default network stack
335 * and the supplied credentials.
336 * The credentials object should contain certificate info
337 * for the server and optionally trust/crl data.
340 server_socket listen(shared_ptr<server_credentials>, socket_address sa, listen_options opts = listen_options());
341 // Wraps an existing server socket in SSL
342 server_socket listen(shared_ptr<server_credentials>, server_socket);