1 /* 2 * Copyright (c) Facebook, Inc. and its affiliates. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <list> 20 #include <mutex> 21 22 #include <folly/String.h> 23 #include <folly/io/async/SSLContext.h> 24 #include <folly/ssl/OpenSSLPtrTypes.h> 25 26 namespace folly { 27 class AsyncSSLSocket; 28 } 29 30 namespace wangle { 31 32 /** 33 * SSL session establish/resume status 34 * 35 * changing these values will break logging pipelines 36 */ 37 enum class SSLResumeEnum : uint8_t { 38 HANDSHAKE = 0, 39 RESUME_SESSION_ID = 1, 40 RESUME_TICKET = 3, 41 NA = 2, 42 }; 43 44 enum class SSLErrorEnum { 45 NO_ERROR, 46 TIMEOUT, 47 DROPPED, 48 }; 49 50 class SSLException : public std::runtime_error { 51 public: 52 SSLException( 53 SSLErrorEnum error, 54 const std::chrono::milliseconds& latency, 55 uint64_t bytesRead); 56 getError()57 SSLErrorEnum getError() const { 58 return error_; 59 } getLatency()60 std::chrono::milliseconds getLatency() const { 61 return latency_; 62 } getBytesRead()63 uint64_t getBytesRead() const { 64 return bytesRead_; 65 } 66 67 private: 68 SSLErrorEnum error_{SSLErrorEnum::NO_ERROR}; 69 std::chrono::milliseconds latency_; 70 uint64_t bytesRead_{0}; 71 }; 72 73 class SSLUtil { 74 private: 75 static std::mutex sIndexLock_; 76 77 /* 78 * This helper function was taken and modified from: 79 * https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption 80 */ 81 static std::string decrypt( 82 folly::ByteRange ciphertext, 83 folly::ByteRange key, 84 folly::ByteRange iv, 85 const EVP_CIPHER* cipher); 86 87 public: 88 /** 89 * Ensures only one caller will allocate an ex_data index for a given static 90 * or global. 91 */ getSSLCtxExIndex(int * pindex)92 static void getSSLCtxExIndex(int* pindex) { 93 std::lock_guard<std::mutex> g(sIndexLock_); 94 if (*pindex < 0) { 95 *pindex = SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); 96 } 97 } 98 getRSAExIndex(int * pindex)99 static void getRSAExIndex(int* pindex) { 100 std::lock_guard<std::mutex> g(sIndexLock_); 101 if (*pindex < 0) { 102 *pindex = RSA_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); 103 } 104 } 105 106 private: 107 // The following typedefs are needed for compatibility across various OpenSSL 108 // versions since each change the dup function param types ever so slightly 109 #if FOLLY_OPENSSL_IS_110 || defined(OPENSSL_IS_BORINGSSL) 110 using ex_data_dup_from_arg_t = const CRYPTO_EX_DATA*; 111 #else 112 using ex_data_dup_from_arg_t = CRYPTO_EX_DATA*; 113 #endif 114 115 #if defined(OPENSSL_IS_BORINGSSL) || FOLLY_OPENSSL_PREREQ(3, 0, 0) 116 using ex_data_dup_ptr_arg_t = void**; 117 #else 118 using ex_data_dup_ptr_arg_t = void*; 119 #endif 120 121 public: 122 // ex data string dup func exDataStdStringDup(CRYPTO_EX_DATA *,ex_data_dup_from_arg_t,ex_data_dup_ptr_arg_t ptr,int,long,void *)123 static int exDataStdStringDup( 124 CRYPTO_EX_DATA* /* to */, 125 ex_data_dup_from_arg_t /* from */, 126 ex_data_dup_ptr_arg_t ptr, 127 int /* idx */, 128 long /* argl */, 129 void* /* argp */) { 130 // TODO: With OpenSSL, ptr is passed in as a void* but is actually a void** 131 // So we need to convert it and then set to the duped data. 132 // see int_dup_ex_data in ex_data.c 133 // BoringSSL is saner and uses a void** 134 void** dataPtr = reinterpret_cast<void**>(ptr); 135 std::string* strData = reinterpret_cast<std::string*>(*dataPtr); 136 if (strData) { 137 *dataPtr = new std::string(*strData); 138 } 139 return 1; 140 } 141 142 // ex data string free func exDataStdStringFree(void *,void * ptr,CRYPTO_EX_DATA *,int,long,void *)143 static void exDataStdStringFree( 144 void* /* parent */, 145 void* ptr, 146 CRYPTO_EX_DATA* /* ad */, 147 int /* idx */, 148 long /* argl */, 149 void* /* argp */) { 150 if (ptr) { 151 auto strPtr = reinterpret_cast<std::string*>(ptr); 152 delete strPtr; 153 } 154 } 155 // get an index that will store a std::string* getSSLSessionExStrIndex(int * pindex)156 static void getSSLSessionExStrIndex(int* pindex) { 157 std::lock_guard<std::mutex> g(sIndexLock_); 158 if (*pindex < 0) { 159 *pindex = SSL_SESSION_get_ex_new_index( 160 0, nullptr, nullptr, exDataStdStringDup, exDataStdStringFree); 161 } 162 } 163 hexlify(const std::string & binary)164 static inline std::string hexlify(const std::string& binary) { 165 std::string hex; 166 folly::hexlify<std::string, std::string>(binary, hex); 167 168 return hex; 169 } 170 hexlify(const std::string & binary,std::string & hex)171 static inline const std::string& hexlify( 172 const std::string& binary, 173 std::string& hex) { 174 folly::hexlify<std::string, std::string>(binary, hex); 175 176 return hex; 177 } 178 179 /** 180 * Return the SSL resume type for the given socket. 181 */ 182 static SSLResumeEnum getResumeState(folly::AsyncSSLSocket* sslSocket); 183 184 /** 185 * Get the Common Name from an X.509 certificate 186 * @param cert certificate to inspect 187 * @return common name, or null if an error occurs 188 */ 189 static std::unique_ptr<std::string> getCommonName(const X509* cert); 190 191 /** 192 * Get the Subject Alternative Name value(s) from an X.509 certificate 193 * @param cert certificate to inspect 194 * @return set of zero or more alternative names, or null if 195 * an error occurs 196 */ 197 static std::unique_ptr<std::list<std::string>> getSubjectAltName( 198 const X509* cert); 199 200 /** 201 * Parse an X509 out of a certificate buffer (usually read from the cert file) 202 * @param certificateData Buffer containing certificate data from file 203 * @return unique_ptr to X509 (may be null) 204 */ 205 static folly::ssl::X509UniquePtr getX509FromCertificate( 206 const std::string& certificateData); 207 208 /** 209 * Same as decryptOpenSSLEncFilePassFile except the password is a string 210 * instead of being stored in a file. 211 */ 212 static folly::Optional<std::string> decryptOpenSSLEncFilePassString( 213 const std::string& filename, 214 const std::string& password, 215 const EVP_CIPHER* cipher, 216 const EVP_MD* digest); 217 218 /** 219 * Function for decrypting files encrypted with openssl enc as: 220 * 221 * openssl enc -e -md sha256 -aes-256-cbc -pass file:<passwordFile> 222 */ 223 static folly::Optional<std::string> decryptOpenSSLEncFilePassFile( 224 const std::string& filename, 225 const folly::PasswordCollector& pwdCollector, 226 const EVP_CIPHER* cipher, 227 const EVP_MD* digest); 228 }; 229 230 } // namespace wangle 231