| Notes on Thrift/SSL | 
 |  | 
 | Author: Ping Li <pingli@facebook.com> | 
 |  | 
 | 1. Scope | 
 |  | 
 |    This SSL only supports blocking mode socket I/O. It can only be used with | 
 |    TSimpleServer, TThreadedServer, and TThreadPoolServer. | 
 |  | 
 | 2. Implementation | 
 |  | 
 |    There're two main classes TSSLSocketFactory and TSSLSocket. Instances of | 
 |    TSSLSocket are always created from TSSLSocketFactory. | 
 |  | 
 |    PosixSSLThreadFactory creates PosixSSLThread. The only difference from the | 
 |    PthreadThread type is that it cleanups OpenSSL error queue upon exiting | 
 |    the thread. Ideally, OpenSSL APIs should only be called from PosixSSLThread. | 
 |  | 
 | 3. How to use SSL APIs | 
 |  | 
 |    // This is for demo. In real code, typically only one TSSLSocketFactory | 
 |    // instance is needed. | 
 |    shared_ptr<TSSLSocketFactory> getSSLSocketFactory() { | 
 |      shared_ptr<TSSLSocketFactory> factory(new TSSLSocketFactory()); | 
 |      // client: load trusted certificates | 
 |      factory->loadTrustedCertificates("my-trusted-ca-certificates.pem"); | 
 |      // client: optionally set your own access manager, otherwise, | 
 |      //         the default client access manager will be loaded. | 
 |  | 
 |      factory->loadCertificate("my-certificate-signed-by-ca.pem"); | 
 |      factory->loadPrivateKey("my-private-key.pem"); | 
 |      // server: optionally setup access manager | 
 |      // shared_ptr<AccessManager> accessManager(new MyAccessManager); | 
 |      // factory->access(accessManager); | 
 |      ... | 
 |    } | 
 |  | 
 |    // client code sample | 
 |    shared_ptr<TSSLSocketFactory> factory = getSSLSocketFactory(); | 
 |    shared_ptr<TSocket> socket = factory.createSocket(host, port); | 
 |    shared_ptr<TBufferedTransport> transport(new TBufferedTransport(socket)); | 
 |    ... | 
 |  | 
 |    // server code sample | 
 |    shared_ptr<TSSLSocketFactory> factory = getSSLSocketFactory(); | 
 |    shared_ptr<TSSLServerSocket> socket(new TSSLServerSocket(port, factory)); | 
 |    shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory)); | 
 |    ... | 
 |  | 
 | 4. AccessManager | 
 |  | 
 |    AccessManager defines a callback interface. It has three callback methods: | 
 |  | 
 |    (a) Decision verify(const sockaddr_storage& sa); | 
 |    (b) Decision verify(const string& host, const char* name, int size); | 
 |    (c) Decision verify(const sockaddr_storage& sa, const char* data, int size); | 
 |  | 
 |    After SSL handshake completes, additional checks are conducted. Application | 
 |    is given the chance to decide whether or not to continue the conversation | 
 |    with the remote. Application is queried through the above three "verify" | 
 |    method. They are called at different points of the verification process. | 
 |  | 
 |    Decisions can be one of ALLOW, DENY, and SKIP. ALLOW and DENY means the | 
 |    conversation should be continued or disconnected, respectively. ALLOW and | 
 |    DENY decision stops the verification process. SKIP means there's no decision | 
 |    based on the given input, continue the verification process. | 
 |  | 
 |    First, (a) is called with the remote IP. It is called once at the beginning. | 
 |    "sa" is the IP address of the remote peer. | 
 |  | 
 |    Then, the certificate of remote peer is loaded. SubjectAltName extensions | 
 |    are extracted and sent to application for verification. When a DNS | 
 |    subjectAltName field is extracted, (b) is called. When an IP subjectAltName | 
 |    field is extracted, (c) is called. | 
 |  | 
 |    The "host" in (b) is the value from TSocket::getHost() if this is a client | 
 |    side socket, or TSocket::getPeerHost() if this is a server side socket. The | 
 |    reason is client side socket initiates the connection. TSocket::getHost() | 
 |    is the remote host name. On server side, the remote host name is unknown | 
 |    unless it's retrieved through TSocket::getPeerHost(). Either way, "host" | 
 |    should be the remote host name. Keep in mind, if TSocket::getPeerHost() | 
 |    failed, it would return the remote host name in numeric format. | 
 |  | 
 |    If all subjectAltName extensions were "skipped", the common name field would | 
 |    be checked. It is sent to application through (c), where "sa" is the remote | 
 |    IP address. "data" is the IP address extracted from subjectAltName IP | 
 |    extension, and "size" is the length of the extension data. | 
 |  | 
 |    If any of the above "verify" methods returned a decision ALLOW or DENY, the | 
 |    verification process would be stopped. | 
 |  | 
 |    If any of the above "verify" methods returned SKIP, that decision would be | 
 |    ignored and the verification process would move on till the last item is | 
 |    examined. At that point, if there's still no decision, the connection is | 
 |    terminated. | 
 |  | 
 |    Thread safety, an access manager should not store state information if it's | 
 |    to be used by many SSL sockets. | 
 |  | 
 | 5. SIGPIPE signal | 
 |  | 
 |    Applications running OpenSSL over network connections may crash if SIGPIPE | 
 |    is not ignored. This happens when they receive a connection reset by remote | 
 |    peer exception, which somehow triggers a SIGPIPE signal. If not handled, | 
 |    this signal would kill the application. | 
 |  | 
 | 6. How to run test client/server in SSL mode | 
 |  | 
 |    The server expects the followings from the current working directory, | 
 |    - "server-certificate.pem" | 
 |    - "server-private-key.pem" | 
 |  | 
 |    The client loads "trusted-ca-certificate.pem" from current directory. | 
 |  | 
 |    The file names are hard coded in the source code. You need to create these | 
 |    certificates before you can run the test code in SSL mode. Make sure at least | 
 |    one of the followings is included in "server-certificate.pem", | 
 |    - subjectAltName, DNS localhost | 
 |    - subjectAltName, IP  127.0.0.1 | 
 |    - common name,    localhost | 
 |  | 
 |    Run, | 
 |    - "./test_server --ssl" to start server | 
 |    - "./test_client --ssl" to run client | 
 |  | 
 |    If "-h <host>" is used to run client, the above "localhost" in the above | 
 |    server-certificate.pem has to be replaced with that host name. | 
 |  | 
 | 7. TSSLSocketFactory::randomize() | 
 |  | 
 |    The default implementation of OpenSSLSocketFactory::randomize() simply calls | 
 |    OpenSSL's RAND_poll() when OpenSSL library is first initialized. | 
 |  | 
 |    The PRNG seed is key to the application security. This method should be | 
 |    overridden if it's not strong enough for you. |