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