blob: d36e2bfbeaa9825e2ce62e6fc914bbf4a31e8c52 [file] [log] [blame]
// 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/util/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