| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #pragma once |
| |
| #include <functional> |
| #include <memory> |
| #include <string> |
| |
| #include <glog/logging.h> |
| #include <gtest/gtest_prod.h> |
| |
| #include "kudu/gutil/port.h" |
| #include "kudu/security/cert.h" |
| #include "kudu/security/openssl_util.h" |
| #include "kudu/util/status.h" |
| |
| namespace kudu { |
| |
| class Socket; |
| |
| namespace security { |
| |
| enum class TlsHandshakeType { |
| // The local endpoint is the TLS client (initiator). |
| CLIENT, |
| // The local endpoint is the TLS server (acceptor). |
| SERVER, |
| }; |
| |
| // Mode for performing verification of the remote peer's identity during a handshake. |
| enum class TlsVerificationMode { |
| // SERVER: |
| // No certificate will be requested from the client, and no verification |
| // will be done. |
| // CLIENT: |
| // The server's certificate will be obtained but no verification will be done. |
| // (the server still requires a certificate, even if it is self-signed). |
| VERIFY_NONE, |
| |
| // BOTH: |
| // The remote peer is required to have a signed certificate. The certificate will |
| // be verified in two ways: |
| // 1) The certificate must be signed by a trusted CA (or chain of CAs). |
| // 2) Second, the hostname of the remote peer (as determined by reverse DNS of the |
| // socket address) must match the common name or one of the Subject Alternative |
| // Names stored in the certificate. |
| VERIFY_REMOTE_CERT_AND_HOST |
| }; |
| |
| // TlsHandshake manages an ongoing TLS handshake between a client and server. |
| // |
| // TlsHandshake instances are default constructed, but must be initialized |
| // before using: call the Init() method to initialize an instance. |
| class TlsHandshake { |
| public: |
| explicit TlsHandshake(TlsHandshakeType type); |
| ~TlsHandshake() = default; |
| |
| // Initialize the instance for the specified type of handshake |
| // using the given SSL handle. |
| Status Init(c_unique_ptr<SSL> s) WARN_UNUSED_RESULT; |
| |
| // Set the verification mode for this handshake. The default verification mode |
| // is VERIFY_REMOTE_CERT_AND_HOST. |
| // |
| // This must be called before the first call to Continue(). |
| void set_verification_mode(TlsVerificationMode mode) { |
| DCHECK(!has_started_); |
| verification_mode_ = mode; |
| } |
| |
| // Continue or start a new handshake. |
| // |
| // 'recv' should contain the input buffer from the remote end, or an empty |
| // string when the handshake is new. |
| // |
| // 'send' should contain the output buffer which must be sent to the remote |
| // end. |
| // |
| // Returns Status::OK when the handshake is complete, however the 'send' |
| // buffer may contain a message which must still be transmitted to the remote |
| // end. If the send buffer is empty after this call and the return is |
| // Status::OK, the socket should immediately be wrapped in the TLS channel |
| // using 'Finish'. If the send buffer is not empty, the message should be sent |
| // to the remote end, and then the socket should be wrapped using 'Finish'. |
| // |
| // Returns Status::Incomplete when the handshake must continue for another |
| // round of messages. |
| // |
| // Returns any other status code on error. |
| Status Continue(const std::string& recv, std::string* send) WARN_UNUSED_RESULT; |
| |
| // Whether an extra step of negotiation is needed at this point given the |
| // return status of a prior call to the Continue() method. |
| bool NeedsExtraStep(const Status& continue_status, |
| const std::string& token) const; |
| |
| // This method is used to store the data produced by the server's final TLS |
| // handshake message. It's not an application data and should be passed by the |
| // encrypted/raw side of the communication channel, not via the regular |
| // SSL_{read,write}() API. Once stored, the data is written to the underlying |
| // socket by the Finish() method upon transitioning from memory-based BIOs |
| // to the socket-based BIOs when the negotiation is complete. Once it's |
| // written to the socket, it will be sent over the wire along with next chunk |
| // of encryped data sent by the server to the client, prepending the encrypted |
| // server's response. |
| void StorePendingData(std::string data); |
| |
| // Finishes the handshake, wrapping the provided socket in the negotiated TLS |
| // channel. This 'TlsHandshake' instance should not be used again after |
| // calling this. |
| Status Finish(std::unique_ptr<Socket>* socket) WARN_UNUSED_RESULT; |
| |
| // Finish the handshake, using the provided socket to verify the remote peer, |
| // but without wrapping the socket. |
| Status FinishNoWrap(const Socket& socket) WARN_UNUSED_RESULT; |
| |
| // Retrieve the local certificate. This will return an error status if there |
| // is no local certificate. |
| // |
| // May only be called after 'Finish' or 'FinishNoWrap'. |
| Status GetLocalCert(Cert* cert) const WARN_UNUSED_RESULT; |
| |
| // Retrieve the remote peer's certificate. This will return an error status if |
| // there is no remote certificate. |
| // |
| // May only be called after 'Finish' or 'FinishNoWrap'. |
| Status GetRemoteCert(Cert* cert) const WARN_UNUSED_RESULT; |
| |
| // Retrieve the negotiated cipher suite. Only valid to call after the |
| // handshake is complete and before 'Finish()'. |
| std::string GetCipherSuite() const; |
| |
| // Retrieve the negotiated TLS protocol version. Only valid to call after the |
| // handshake is complete and before 'Finish()'. |
| std::string GetProtocol() const; |
| |
| // Retrive the description of the negotiated cipher. |
| // Only valid to call after the handshake is complete and before 'Finish()'. |
| std::string GetCipherDescription() const; |
| |
| private: |
| FRIEND_TEST(TestTlsHandshake, HandshakeSequenceNoTLSv1dot3); |
| FRIEND_TEST(TestTlsHandshake, HandshakeSequenceTLSv1dot3); |
| |
| // Set the verification mode on the underlying SSL object. |
| void SetSSLVerify(); |
| |
| SSL* ssl() { |
| return ssl_.get(); |
| } |
| |
| // Populates local_cert_ and remote_cert_. |
| Status GetCerts() WARN_UNUSED_RESULT; |
| |
| // Verifies that the handshake is valid for the provided socket. |
| Status Verify(const Socket& socket) const WARN_UNUSED_RESULT; |
| |
| // The type of TLS handshake this wrapper represents: client or server. |
| const TlsHandshakeType type_; |
| |
| // Owned SSL handle. |
| c_unique_ptr<SSL> ssl_; |
| |
| bool has_started_ = false; |
| TlsVerificationMode verification_mode_ = TlsVerificationMode::VERIFY_REMOTE_CERT_AND_HOST; |
| |
| Cert local_cert_; |
| Cert remote_cert_; |
| |
| // Data which is left pending in the write BIO after the last call to |
| // Continue(), i.e. the data left pending after completing the TLS handshake. |
| std::string rbio_pending_data_; |
| }; |
| |
| } // namespace security |
| } // namespace kudu |