| /* | 
 |  * 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 _THRIFT_TRANSPORT_TSSLSOCKET_H_ | 
 | #define _THRIFT_TRANSPORT_TSSLSOCKET_H_ 1 | 
 |  | 
 | #include <string> | 
 | #include <boost/shared_ptr.hpp> | 
 | #include <openssl/ssl.h> | 
 | #include "concurrency/Mutex.h" | 
 | #include "TSocket.h" | 
 |  | 
 | namespace apache { namespace thrift { namespace transport { | 
 |  | 
 | class AccessManager; | 
 | class SSLContext; | 
 |  | 
 | /** | 
 |  * OpenSSL implementation for SSL socket interface. | 
 |  */ | 
 | class TSSLSocket: public TSocket { | 
 |  public: | 
 |  ~TSSLSocket(); | 
 |   /** | 
 |    * TTransport interface. | 
 |    */ | 
 |   bool     isOpen(); | 
 |   bool     peek(); | 
 |   void     open(); | 
 |   void     close(); | 
 |   uint32_t read(uint8_t* buf, uint32_t len); | 
 |   void     write(const uint8_t* buf, uint32_t len); | 
 |   void     flush(); | 
 |    /** | 
 |    * Set whether to use client or server side SSL handshake protocol. | 
 |    * | 
 |    * @param flag  Use server side handshake protocol if true. | 
 |    */ | 
 |   void server(bool flag) { server_ = flag; } | 
 |   /** | 
 |    * Determine whether the SSL socket is server or client mode. | 
 |    */ | 
 |   bool server() const { return server_; } | 
 |   /** | 
 |    * Set AccessManager. | 
 |    * | 
 |    * @param manager  Instance of AccessManager | 
 |    */ | 
 |   virtual void access(boost::shared_ptr<AccessManager> manager) { | 
 |     access_ = manager; | 
 |   } | 
 | protected: | 
 |   /** | 
 |    * Constructor. | 
 |    */ | 
 |   TSSLSocket(boost::shared_ptr<SSLContext> ctx); | 
 |   /** | 
 |    * Constructor, create an instance of TSSLSocket given an existing socket. | 
 |    * | 
 |    * @param socket An existing socket | 
 |    */ | 
 |   TSSLSocket(boost::shared_ptr<SSLContext> ctx, int socket); | 
 |   /** | 
 |    * Constructor. | 
 |    * | 
 |    * @param host  Remote host name | 
 |    * @param port  Remote port number | 
 |    */ | 
 |   TSSLSocket(boost::shared_ptr<SSLContext> ctx, | 
 |                                std::string host, | 
 |                                        int port); | 
 |   /** | 
 |    * Authorize peer access after SSL handshake completes. | 
 |    */ | 
 |   virtual void authorize(); | 
 |   /** | 
 |    * Initiate SSL handshake if not already initiated. | 
 |    */ | 
 |   void checkHandshake(); | 
 |  | 
 |   bool server_; | 
 |   SSL* ssl_; | 
 |   boost::shared_ptr<SSLContext> ctx_; | 
 |   boost::shared_ptr<AccessManager> access_; | 
 |   friend class TSSLSocketFactory; | 
 | }; | 
 |  | 
 | /** | 
 |  * SSL socket factory. SSL sockets should be created via SSL factory. | 
 |  */ | 
 | class TSSLSocketFactory { | 
 |  public: | 
 |   /** | 
 |    * Constructor/Destructor | 
 |    */ | 
 |   TSSLSocketFactory(); | 
 |   virtual ~TSSLSocketFactory(); | 
 |   /** | 
 |    * Create an instance of TSSLSocket with a fresh new socket. | 
 |    */ | 
 |   virtual boost::shared_ptr<TSSLSocket> createSocket(); | 
 |   /** | 
 |    * Create an instance of TSSLSocket with the given socket. | 
 |    * | 
 |    * @param socket An existing socket. | 
 |    */ | 
 |   virtual boost::shared_ptr<TSSLSocket> createSocket(int socket); | 
 |    /** | 
 |    * Create an instance of TSSLSocket. | 
 |    * | 
 |    * @param host  Remote host to be connected to | 
 |    * @param port  Remote port to be connected to | 
 |    */ | 
 |   virtual boost::shared_ptr<TSSLSocket> createSocket(const std::string& host, | 
 |                                                      int port); | 
 |   /** | 
 |    * Set ciphers to be used in SSL handshake process. | 
 |    * | 
 |    * @param ciphers  A list of ciphers | 
 |    */ | 
 |   virtual void ciphers(const std::string& enable); | 
 |   /** | 
 |    * Enable/Disable authentication. | 
 |    * | 
 |    * @param required Require peer to present valid certificate if true | 
 |    */ | 
 |   virtual void authenticate(bool required); | 
 |   /** | 
 |    * Load server certificate. | 
 |    * | 
 |    * @param path   Path to the certificate file | 
 |    * @param format Certificate file format | 
 |    */ | 
 |   virtual void loadCertificate(const char* path, const char* format = "PEM"); | 
 |   /** | 
 |    * Load private key. | 
 |    * | 
 |    * @param path   Path to the private key file | 
 |    * @param format Private key file format | 
 |    */ | 
 |   virtual void loadPrivateKey(const char* path, const char* format = "PEM"); | 
 |   /** | 
 |    * Load trusted certificates from specified file. | 
 |    * | 
 |    * @param path Path to trusted certificate file | 
 |    */ | 
 |   virtual void loadTrustedCertificates(const char* path); | 
 |   /** | 
 |    * Default randomize method. | 
 |    */ | 
 |   virtual void randomize(); | 
 |   /** | 
 |    * Override default OpenSSL password callback with getPassword(). | 
 |    */ | 
 |   void overrideDefaultPasswordCallback(); | 
 |   /** | 
 |    * Set/Unset server mode. | 
 |    * | 
 |    * @param flag  Server mode if true | 
 |    */ | 
 |   virtual void server(bool flag) { server_ = flag; } | 
 |   /** | 
 |    * Determine whether the socket is in server or client mode. | 
 |    * | 
 |    * @return true, if server mode, or, false, if client mode | 
 |    */ | 
 |   virtual bool server() const { return server_; } | 
 |   /** | 
 |    * Set AccessManager. | 
 |    * | 
 |    * @param manager  The AccessManager instance | 
 |    */ | 
 |   virtual void access(boost::shared_ptr<AccessManager> manager) { | 
 |     access_ = manager; | 
 |   } | 
 |  protected: | 
 |   boost::shared_ptr<SSLContext> ctx_; | 
 |  | 
 |   static void initializeOpenSSL(); | 
 |   static void cleanupOpenSSL(); | 
 |   /** | 
 |    * Override this method for custom password callback. It may be called | 
 |    * multiple times at any time during a session as necessary. | 
 |    * | 
 |    * @param password Pass collected password to OpenSSL | 
 |    * @param size     Maximum length of password including NULL character | 
 |    */ | 
 |   virtual void getPassword(std::string& /* password */, int /* size */) {} | 
 |  private: | 
 |   bool server_; | 
 |   boost::shared_ptr<AccessManager> access_; | 
 |   static bool initialized; | 
 |   static concurrency::Mutex mutex_; | 
 |   static uint64_t count_; | 
 |   void setup(boost::shared_ptr<TSSLSocket> ssl); | 
 |   static int passwordCallback(char* password, int size, int, void* data); | 
 | }; | 
 |  | 
 | /** | 
 |  * SSL exception. | 
 |  */ | 
 | class TSSLException: public TTransportException { | 
 |  public: | 
 |   TSSLException(const std::string& message): | 
 |     TTransportException(TTransportException::INTERNAL_ERROR, message) {} | 
 |  | 
 |   virtual const char* what() const throw() { | 
 |     if (message_.empty()) { | 
 |       return "TSSLException"; | 
 |     } else { | 
 |       return message_.c_str(); | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | /** | 
 |  * Wrap OpenSSL SSL_CTX into a class. | 
 |  */ | 
 | class SSLContext { | 
 |  public: | 
 |   SSLContext(); | 
 |   virtual ~SSLContext(); | 
 |   SSL* createSSL(); | 
 |   SSL_CTX* get() { return ctx_; } | 
 |  private: | 
 |   SSL_CTX* ctx_; | 
 | }; | 
 |  | 
 | /** | 
 |  * Callback interface for access control. It's meant to verify the remote host. | 
 |  * It's constructed when application starts and set to TSSLSocketFactory | 
 |  * instance. It's passed onto all TSSLSocket instances created by this factory | 
 |  * object. | 
 |  */ | 
 | class AccessManager { | 
 |  public: | 
 |   enum Decision { | 
 |     DENY   = -1,    // deny access | 
 |     SKIP   =  0,    // cannot make decision, move on to next (if any) | 
 |     ALLOW  =  1     // allow access | 
 |   }; | 
 |  /** | 
 |   * Destructor | 
 |   */ | 
 |  virtual ~AccessManager() {} | 
 |  /** | 
 |   * Determine whether the peer should be granted access or not. It's called | 
 |   * once after the SSL handshake completes successfully, before peer certificate | 
 |   * is examined. | 
 |   * | 
 |   * If a valid decision (ALLOW or DENY) is returned, the peer certificate is | 
 |   * not to be verified. | 
 |   * | 
 |   * @param  sa Peer IP address | 
 |   * @return True if the peer is trusted, false otherwise | 
 |   */ | 
 |  virtual Decision verify(const sockaddr_storage& /* sa */ ) throw() { return DENY; } | 
 |  /** | 
 |   * Determine whether the peer should be granted access or not. It's called | 
 |   * every time a DNS subjectAltName/common name is extracted from peer's | 
 |   * certificate. | 
 |   * | 
 |   * @param  host Client mode: host name returned by TSocket::getHost() | 
 |   *              Server mode: host name returned by TSocket::getPeerHost() | 
 |   * @param  name SubjectAltName or common name extracted from peer certificate | 
 |   * @param  size Length of name | 
 |   * @return True if the peer is trusted, false otherwise | 
 |   * | 
 |   * Note: The "name" parameter may be UTF8 encoded. | 
 |   */ | 
 |  virtual Decision verify(const std::string& /* host */, const char* /* name */, int /* size */) | 
 |    throw() { return DENY; } | 
 |  /** | 
 |   * Determine whether the peer should be granted access or not. It's called | 
 |   * every time an IP subjectAltName is extracted from peer's certificate. | 
 |   * | 
 |   * @param  sa   Peer IP address retrieved from the underlying socket | 
 |   * @param  data IP address extracted from certificate | 
 |   * @param  size Length of the IP address | 
 |   * @return True if the peer is trusted, false otherwise | 
 |   */ | 
 |  virtual Decision verify(const sockaddr_storage& /* sa */, const char* /* data */, int /* size */) | 
 |    throw() { return DENY; } | 
 | }; | 
 |  | 
 | typedef AccessManager::Decision Decision; | 
 |  | 
 | class DefaultClientAccessManager: public AccessManager { | 
 |  public: | 
 |   // AccessManager interface | 
 |   Decision verify(const sockaddr_storage& sa) throw(); | 
 |   Decision verify(const std::string& host, const char* name, int size) throw(); | 
 |   Decision verify(const sockaddr_storage& sa, const char* data, int size) throw(); | 
 | }; | 
 |  | 
 |  | 
 | }}} | 
 |  | 
 | #endif |