blob: 72b276365e17991bd3040f085adeec673a257b30 [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.
#ifndef IMPALA_RPC_AUTH_PROVIDER_H
#define IMPALA_RPC_AUTH_PROVIDER_H
#include <string>
#include <boost/thread/mutex.hpp>
#include <sasl/sasl.h>
#include "common/status.h"
#include "rpc/thrift-server.h"
#include "util/openssl-util.h"
#include "util/promise.h"
namespace sasl { class TSasl; }
namespace impala {
/// An AuthProvider creates Thrift transports that are set up to authenticate themselves
/// using a protocol such as Kerberos or PLAIN/SASL. Both server and client transports are
/// provided by this class, using slightly different mechanisms (servers use a factory,
/// clients wrap a pre-provided transport)
class AuthProvider {
public:
/// Initialises any state required to perform authentication using this provider.
virtual Status Start() WARN_UNUSED_RESULT = 0;
/// Creates a new Thrift transport factory in the out parameter that performs
/// authorisation per this provider's protocol. The type of the transport returned is
/// determined by 'underlying_transport_type' and there may be multiple levels of
/// wrapped transports, eg. a TBufferedTransport around a TSaslServerTransport.
virtual Status GetServerTransportFactory(
ThriftServer::TransportType underlying_transport_type,
const std::string& server_name, MetricGroup* metrics,
boost::shared_ptr<apache::thrift::transport::TTransportFactory>* factory)
WARN_UNUSED_RESULT = 0;
/// Called by Thrift clients to wrap a raw transport with any intermediate transport
/// that an auth protocol requires.
/// TODO: Return the correct clients for HTTP base transport. At this point, no clients
/// for HTTP endpoints are internal to the Impala service, so it should be OK.
virtual Status WrapClientTransport(const std::string& hostname,
boost::shared_ptr<apache::thrift::transport::TTransport> raw_transport,
const std::string& service_name,
boost::shared_ptr<apache::thrift::transport::TTransport>* wrapped_transport)
WARN_UNUSED_RESULT = 0;
/// Setup 'connection_ptr' to get its username with the given transports and sets
/// 'network_address' based on the underlying socket. The transports should be generated
/// by the factory returned by GetServerTransportFactory() when called with the same
/// 'underlying_transport_type'.
virtual void SetupConnectionContext(
const boost::shared_ptr<ThriftServer::ConnectionContext>& connection_ptr,
ThriftServer::TransportType underlying_transport_type,
apache::thrift::transport::TTransport* input_transport,
apache::thrift::transport::TTransport* output_transport) = 0;
/// Returns true if this provider uses Sasl at the transport layer.
virtual bool is_secure() = 0;
virtual ~AuthProvider() { }
};
/// Used if either (or both) Kerberos and LDAP auth are desired. For BINARY connections we
/// use Sasl for the communication, and for HTTP connections we use Basic or SPNEGO auth.
/// This "wraps" the underlying communication, in thrift-speak. This is used for both
/// client and server contexts; there is one for internal and one for external
/// communication.
class SecureAuthProvider : public AuthProvider {
public:
SecureAuthProvider(bool is_internal)
: has_ldap_(false), is_internal_(is_internal), needs_kinit_(false) {}
/// Performs initialization of external state. Kinit if configured to use kerberos.
/// If we're using ldap, set up appropriate certificate usage.
virtual Status Start();
/// Wrap the client transport with a new TSaslClientTransport. This is only for
/// internal connections. Since, as a daemon, we only do Kerberos and not LDAP,
/// we can go straight to Kerberos.
/// This is only applicable to Thrift connections and not KRPC connections.
virtual Status WrapClientTransport(const std::string& hostname,
boost::shared_ptr<apache::thrift::transport::TTransport> raw_transport,
const std::string& service_name,
boost::shared_ptr<apache::thrift::transport::TTransport>* wrapped_transport);
/// This sets up a mapping between auth types (PLAIN and GSSAPI) and callbacks.
/// When a connection comes in, thrift will see one of the above on the wire, do
/// a table lookup, and associate the appropriate callbacks with the connection.
/// Then presto! You've got authentication for the connection.
/// This is only applicable to Thrift connections and not KRPC connections.
virtual Status GetServerTransportFactory(
ThriftServer::TransportType underlying_transport_type,
const std::string& server_name, MetricGroup* metrics,
boost::shared_ptr<apache::thrift::transport::TTransportFactory>* factory);
/// IF sasl was used, the username will be available from the handshake, and we set it
/// here. If HTTP Basic or SPNEGO auth was used, the username won't be available until
/// one or more packets are received, so we register a callback with the transport that
/// will set the username when it's available.
virtual void SetupConnectionContext(
const boost::shared_ptr<ThriftServer::ConnectionContext>& connection_ptr,
ThriftServer::TransportType underlying_transport_type,
apache::thrift::transport::TTransport* input_transport,
apache::thrift::transport::TTransport* output_transport);
virtual bool is_secure() { return true; }
/// Initializes kerberos items and checks for sanity. Failures can occur on a
/// malformed principal or when setting some environment variables. Called
/// prior to Start().
Status InitKerberos(const std::string& principal, const std::string& keytab_path);
/// Initializes ldap - just record that we're going to use it. Called prior to
/// Start().
void InitLdap() { has_ldap_ = true; }
/// Used for testing
const std::string& principal() const { return principal_; }
const std::string& service_name() const { return service_name_; }
const std::string& hostname() const { return hostname_; }
const std::string& realm() const { return realm_; }
bool has_ldap() { return has_ldap_; }
private:
/// Do we (the server side only) support ldap for this connnection?
bool has_ldap_;
/// Hostname of this machine - if kerberos, derived from principal. If there
/// is no kerberos, but LDAP is used, then acquired via GetHostname().
std::string hostname_;
/// True if internal, false if external.
bool is_internal_;
/// All the rest of these private items are Kerberos-specific.
/// The Kerberos principal. If is_internal_ is true and --be_principal was
/// supplied, this is --be_principal. In all other cases this is --principal.
std::string principal_;
/// The full path to the keytab where the above principal can be found.
std::string keytab_file_;
/// The service name, deduced from the principal. Used by servers to indicate
/// what service a principal must have a ticket for in order to be granted
/// access to this service.
std::string service_name_;
/// Principal's realm, again derived from principal.
std::string realm_;
/// True if tickets for this principal should be obtained. This is true if
/// we're an auth provider for an "internal" connection, because we may
/// function as a client.
bool needs_kinit_;
/// Used to generate and verify signatures for cookies.
AuthenticationHash hash_;
/// One-time kerberos-specific environment variable setup. Called by InitKerberos().
Status InitKerberosEnv() WARN_UNUSED_RESULT;
};
/// This provider implements no authentication, so any connection is immediately
/// successful. There's no Sasl in the picture.
class NoAuthProvider : public AuthProvider {
public:
NoAuthProvider() { }
virtual Status Start() { return Status::OK(); }
virtual Status GetServerTransportFactory(
ThriftServer::TransportType underlying_transport_type,
const std::string& server_name, MetricGroup* metrics,
boost::shared_ptr<apache::thrift::transport::TTransportFactory>* factory);
virtual Status WrapClientTransport(const std::string& hostname,
boost::shared_ptr<apache::thrift::transport::TTransport> raw_transport,
const std::string& service_name,
boost::shared_ptr<apache::thrift::transport::TTransport>* wrapped_transport);
/// If there is no auth, then we don't have a username available.
virtual void SetupConnectionContext(
const boost::shared_ptr<ThriftServer::ConnectionContext>& connection_ptr,
ThriftServer::TransportType underlying_transport_type,
apache::thrift::transport::TTransport* input_transport,
apache::thrift::transport::TTransport* output_transport);
virtual bool is_secure() { return false; }
};
/// The first entry point to the authentication subsystem. Performs initialization
/// of Sasl, the global AuthManager, and the two authentication providers. Appname
/// should generally be argv[0]. Normally, InitAuth() should only be called once.
/// In certain test cases, we may call it more than once. It's important that InitAuth()
/// is called with the same 'appname' if it's called more than once. Otherwise, error
/// status will be returned.
Status InitAuth(const std::string& appname);
}
#endif