blob: b5c4dcb15f8b6f3f1b93af659c5f8ab7679fe399 [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 <sasl/sasl.h>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <thrift/transport/TTransportException.h>
#include <thrift/transport/TVirtualTransport.h>
#include "kudu/rpc/sasl_common.h"
#include "kudu/rpc/sasl_helper.h"
#include "kudu/util/faststring.h"
#include "kudu/util/slice.h"
#include "kudu/util/status.h"
namespace apache {
namespace thrift {
namespace transport {
class TTransport;
} // namespace transport
} // namespace thrift
} // namespace apache
namespace kudu {
namespace thrift {
// An exception representing a SASL or Kerberos failure.
class SaslException : public apache::thrift::transport::TTransportException {
public:
explicit SaslException(Status status)
: TTransportException(status.ToString()),
status_(std::move(status)) {
}
const Status& status() const {
return status_;
}
private:
Status status_;
};
// An enum describing the possible states of the SASL negotiation protocol.
enum NegotiationStatus {
TSASL_INVALID = -1,
TSASL_START = 1,
TSASL_OK = 2,
TSASL_BAD = 3,
TSASL_ERROR = 4,
TSASL_COMPLETE = 5
};
// A Thrift transport which uses SASL GSSAPI to authenticate as a client to a
// remote server.
//
// SaslClientTransport internally holds buffers, so it does not need the
// underlying transport to be buffered.
class SaslClientTransport
: public apache::thrift::transport::TVirtualTransport<SaslClientTransport> {
public:
SaslClientTransport(std::string service_principal,
const std::string& server_fqdn,
std::shared_ptr<TTransport> transport,
size_t max_recv_buf_size);
~SaslClientTransport() override = default;
bool isOpen() override;
bool peek() override;
void open() override;
void close() override;
uint32_t read(uint8_t* buf, uint32_t len);
void write(const uint8_t* buf, uint32_t len);
void flush() override;
int GetOptionCb(const char* plugin_name, const char* option,
const char** result, unsigned* len);
private:
// Runs SASL negotiation with the remote server.
void Negotiate();
// Sends a SASL negotiation message to the underlying transport.
//
// Send a SASL negotiation message using the Thrift framing protocol:
//
// - 1 byte of status
// - 4 bytes of remaining length
// - var-len payload
void SendSaslMessage(NegotiationStatus status, Slice payload);
// Receives a SASL negotiation message from the underlying transport.
//
// The returned negotiation status will be of type OK or COMPLETE, all
// other statuses result in an exception.
NegotiationStatus ReceiveSaslMessage(faststring* payload);
// Initializes SASL state.
void SetupSaslContext();
// Sends the initial SASL connection message.
void SendSaslStart();
// Reads a frame from the underlying transport, storing the payload into
// read_slice_. If the connection is using SASL auth-conf or auth-int
// protection the data is automatically decoded.
void ReadFrame();
// Resets the read buffer to empty, and deallocates its internal buffer.
void ResetReadBuf();
// Resets the write buffer to the size of a frame header, and deallocates its
// internal buffer.
void ResetWriteBuf();
// The underlying transport. Typically a TCP socket.
std::shared_ptr<TTransport> transport_;
// SASL state.
rpc::SaslHelper sasl_helper_;
std::unique_ptr<sasl_conn_t, rpc::SaslDeleter> sasl_conn_;
std::vector<sasl_callback_t> sasl_callbacks_;
// Whether the connection is using auth-int or auth-conf protection.
bool needs_wrap_;
// The negotiated SASL maximum buffer sizes. These correspond to the maximum
// sized frames that can be received or sent.
//
// Note: the Java implementation of the Thrift SASL transport does not respect
// the negotiated maximum buffer size (THRIFT-4483) and never splits a message
// into multiple frames, so we end up having to set the recv buf size to match
// the largest serialized Thrift message we want to be able to receive.
size_t max_recv_buf_size_;
size_t max_send_buf_size_;
// The read buffer and slice. The slice points to the remaining frame data
// which hasn't been read yet.
faststring read_buf_;
Slice read_slice_;
// The write buffer.
faststring write_buf_;
// The principal of the service being connected to.
std::string service_principal_;
};
} // namespace thrift
} // namespace kudu