• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /**
3  * Copyright 2021 Huawei Technologies Co., Ltd
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include "ps/core/communicator/ssl_client.h"
19 
20 #include <sys/time.h>
21 #include <openssl/pem.h>
22 #include <openssl/sha.h>
23 
24 #include <cstdio>
25 #include <cstring>
26 #include <cstdlib>
27 #include <vector>
28 #include <iomanip>
29 #include <sstream>
30 
31 namespace mindspore {
32 namespace ps {
33 namespace core {
SSLClient()34 SSLClient::SSLClient() : ssl_ctx_(nullptr), check_time_thread_(nullptr), running_(false), is_ready_(false) {
35   InitSSL();
36 }
37 
~SSLClient()38 SSLClient::~SSLClient() { CleanSSL(); }
39 
InitSSL()40 void SSLClient::InitSSL() {
41   CommUtil::InitOpensslLib();
42   ssl_ctx_ = SSL_CTX_new(SSLv23_client_method());
43   if (!ssl_ctx_) {
44     MS_LOG(EXCEPTION) << "SSL_CTX_new failed";
45   }
46   std::unique_ptr<Configuration> config_ =
47     std::make_unique<FileConfiguration>(PSContext::instance()->config_file_path());
48   MS_EXCEPTION_IF_NULL(config_);
49   if (!config_->Initialize()) {
50     MS_LOG(EXCEPTION) << "The config file is empty.";
51   }
52 
53   // 1.Parse the client's certificate and the ciphertext of key.
54   std::string path = CommUtil::ParseConfig(*config_, kClientCertPath);
55   if (!CommUtil::IsFileExists(path)) {
56     MS_LOG(EXCEPTION) << "The key:" << kClientCertPath << "'s value is not exist.";
57   }
58   std::string client_cert = path;
59   MS_LOG(INFO) << "client cert: " << client_cert;
60 
61   // 2. Parse the client password.
62   char *client_password = PSContext::instance()->client_password();
63   if (strlen(client_password) == 0) {
64     MS_LOG(EXCEPTION) << "The client password's value is empty.";
65   }
66   EVP_PKEY *pkey = nullptr;
67   X509 *cert = nullptr;
68   STACK_OF(X509) *ca_stack = nullptr;
69   BIO *bio = BIO_new_file(client_cert.c_str(), "rb");
70   if (bio == nullptr) {
71     PSContext::instance()->ClearClientPassword();
72     MS_LOG(EXCEPTION) << "Read client cert file failed.";
73   }
74   PKCS12 *p12 = d2i_PKCS12_bio(bio, nullptr);
75   BIO_free_all(bio);
76   if (p12 == nullptr) {
77     PSContext::instance()->ClearClientPassword();
78     MS_LOG(EXCEPTION) << "Create PKCS12 cert failed, please check whether the certificate is correct.";
79   }
80   if (PKCS12_parse(p12, client_password, &pkey, &cert, &ca_stack) == 0) {
81     if (ERR_GET_REASON(ERR_peek_last_error()) == PKCS12_R_MAC_VERIFY_FAILURE) {
82       PSContext::instance()->ClearClientPassword();
83       MS_LOG(EXCEPTION) << "The client password is invalid!";
84     }
85     PSContext::instance()->ClearClientPassword();
86     MS_LOG(EXCEPTION) << "PKCS12_parse failed, the reason is " << ERR_reason_error_string(ERR_peek_last_error());
87   }
88   PSContext::instance()->ClearClientPassword();
89 
90   PKCS12_free(p12);
91   MS_EXCEPTION_IF_NULL(cert);
92   MS_EXCEPTION_IF_NULL(pkey);
93   if (ca_stack != nullptr) {
94     MS_LOG(EXCEPTION) << "The cert is invalid: ca_stack should be empty.";
95   }
96 
97   // 3. load ca cert.
98   std::string ca_path = CommUtil::ParseConfig(*config_, kCaCertPath);
99   if (!CommUtil::IsFileExists(ca_path)) {
100     MS_LOG(EXCEPTION) << "The key:" << kCaCertPath << "'s value is not exist.";
101   }
102   BIO *ca_bio = BIO_new_file(ca_path.c_str(), "r");
103   if (ca_bio == nullptr) {
104     MS_LOG(EXCEPTION) << "Read CA cert file failed.";
105   }
106   X509 *caCert = PEM_read_bio_X509(ca_bio, nullptr, nullptr, nullptr);
107 
108   X509_CRL *crl = nullptr;
109   std::string crl_path = CommUtil::ParseConfig(*(config_), kCrlPath);
110   if (crl_path.empty()) {
111     MS_LOG(INFO) << "The crl path is empty.";
112   } else if (!CommUtil::checkCRLTime(crl_path)) {
113     MS_LOG(EXCEPTION) << "check crl time failed";
114   } else if (!CommUtil::VerifyCRL(caCert, crl_path, &crl)) {
115     MS_LOG(EXCEPTION) << "Verify crl failed.";
116   }
117 
118   CommUtil::verifyCertPipeline(caCert, cert);
119   InitSSLCtx(*config_, cert, pkey, crl, ca_path);
120   StartCheckCertTime(*config_, cert);
121 
122   EVP_PKEY_free(pkey);
123   X509_free(caCert);
124   X509_free(cert);
125   BIO_vfree(ca_bio);
126   if (crl != nullptr) {
127     X509_CRL_free(crl);
128   }
129 }
130 
InitSSLCtx(const Configuration & config,const X509 * cert,const EVP_PKEY * pkey,X509_CRL * crl,std::string ca_path)131 void SSLClient::InitSSLCtx(const Configuration &config, const X509 *cert, const EVP_PKEY *pkey, X509_CRL *crl,
132                            std::string ca_path) {
133   SSL_CTX_set_verify(ssl_ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
134   if (!SSL_CTX_load_verify_locations(ssl_ctx_, ca_path.c_str(), nullptr)) {
135     MS_LOG(EXCEPTION) << "SSL load ca location failed!";
136   }
137 
138   std::string default_cipher_list = CommUtil::ParseConfig(config, kCipherList);
139   std::vector<std::string> ciphers = CommUtil::Split(default_cipher_list, kColon);
140   if (!CommUtil::VerifyCipherList(ciphers)) {
141     MS_LOG(EXCEPTION) << "The cipher is wrong.";
142   }
143   if (!SSL_CTX_set_cipher_list(ssl_ctx_, default_cipher_list.c_str())) {
144     MS_LOG(EXCEPTION) << "SSL use set cipher list failed!";
145   }
146   if (!SSL_CTX_use_certificate(ssl_ctx_, const_cast<X509 *>(cert))) {
147     MS_LOG(EXCEPTION) << "SSL use certificate chain file failed!";
148   }
149 
150   if (!SSL_CTX_use_PrivateKey(ssl_ctx_, const_cast<EVP_PKEY *>(pkey))) {
151     MS_LOG(EXCEPTION) << "SSL use private key file failed!";
152   }
153 
154   if (!SSL_CTX_check_private_key(ssl_ctx_)) {
155     MS_LOG(EXCEPTION) << "SSL check private key file failed!";
156   }
157 
158   if (!SSL_CTX_set_options(ssl_ctx_, SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
159                                        SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1)) {
160     MS_LOG(EXCEPTION) << "SSL_CTX_set_options failed.";
161   }
162 
163   if (!SSL_CTX_set_mode(ssl_ctx_, SSL_MODE_AUTO_RETRY)) {
164     MS_LOG(EXCEPTION) << "SSL set mode auto retry failed!";
165   }
166 
167   if (crl != nullptr) {
168     // Load CRL into the `X509_STORE`
169     X509_STORE *x509_store = SSL_CTX_get_cert_store(ssl_ctx_);
170     if (X509_STORE_add_crl(x509_store, crl) != 1) {
171       MS_LOG(EXCEPTION) << "ssl client X509_STORE add crl failed!";
172     }
173 
174     // Enable CRL checking
175     X509_VERIFY_PARAM *param = SSL_CTX_get0_param(ssl_ctx_);
176     if (param == nullptr) {
177       MS_LOG(EXCEPTION) << "ssl client X509_VERIFY_PARAM is nullptr!";
178     }
179     if (X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK) != 1) {
180       MS_LOG(EXCEPTION) << "ssl client X509_VERIFY_PARAM set flag X509_V_FLAG_CRL_CHECK failed!";
181     }
182   }
183 
184   SSL_CTX_set_security_level(ssl_ctx_, kSecurityLevel);
185 }
186 
CleanSSL()187 void SSLClient::CleanSSL() {
188   if (ssl_ctx_ != nullptr) {
189     SSL_CTX_free(ssl_ctx_);
190   }
191   ERR_free_strings();
192   EVP_cleanup();
193   ERR_remove_thread_state(nullptr);
194   CRYPTO_cleanup_all_ex_data();
195   StopCheckCertTime();
196 }
197 
StartCheckCertTime(const Configuration & config,const X509 * cert)198 void SSLClient::StartCheckCertTime(const Configuration &config, const X509 *cert) {
199   MS_EXCEPTION_IF_NULL(cert);
200   MS_LOG(INFO) << "The client start check cert.";
201   int64_t interval = kCertCheckIntervalInHour;
202 
203   int64_t warning_time = kCertExpireWarningTimeInDay;
204   if (config.Exists(kCertExpireWarningTime)) {
205     int64_t res_time = config.GetInt(kCertExpireWarningTime, 0);
206     if (res_time < kMinWarningTime || res_time > kMaxWarningTime) {
207       MS_LOG(EXCEPTION) << "The Certificate expiration warning time should be [7, 180]";
208     }
209     warning_time = res_time;
210   }
211   MS_LOG(INFO) << "The interval time is:" << interval << ", the warning time is:" << warning_time;
212   running_ = true;
213   check_time_thread_ = std::make_unique<std::thread>([&, cert, interval, warning_time]() {
214     while (running_) {
215       if (!CommUtil::VerifyCertTime(cert, warning_time)) {
216         MS_LOG(WARNING) << "Verify cert time failed.";
217       }
218       std::unique_lock<std::mutex> lock(mutex_);
219       bool res = cond_.wait_for(lock, std::chrono::hours(interval), [&] {
220         bool result = is_ready_.load();
221         return result;
222       });
223       MS_LOG(INFO) << "Wait for res:" << res;
224     }
225   });
226   MS_EXCEPTION_IF_NULL(check_time_thread_);
227 }
228 
StopCheckCertTime()229 void SSLClient::StopCheckCertTime() {
230   running_ = false;
231   is_ready_ = true;
232   cond_.notify_all();
233   if (check_time_thread_ != nullptr) {
234     check_time_thread_->join();
235   }
236 }
237 
GetSSLCtx() const238 SSL_CTX *SSLClient::GetSSLCtx() const { return ssl_ctx_; }
239 }  // namespace core
240 }  // namespace ps
241 }  // namespace mindspore
242