blob: da703316108fa53896f3a3619cb2edbe7d5ac0db [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 <utility>
#include <glog/logging.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 use using TlsContext::InitiateHandshake.
class TlsHandshake {
public:
TlsHandshake() = default;
~TlsHandshake() = default;
// 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;
// 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 class TlsContext;
bool has_started_ = false;
TlsVerificationMode verification_mode_ = TlsVerificationMode::VERIFY_REMOTE_CERT_AND_HOST;
// Set the verification mode on the underlying SSL object.
void SetSSLVerify();
// Set the SSL to use during the handshake. Called once by
// TlsContext::InitiateHandshake before starting the handshake processes.
void adopt_ssl(c_unique_ptr<SSL> ssl) {
CHECK(!ssl_);
ssl_ = std::move(ssl);
}
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;
// Owned SSL handle.
c_unique_ptr<SSL> ssl_;
Cert local_cert_;
Cert remote_cert_;
};
} // namespace security
} // namespace kudu