1 // 2 // Copyright (C) 2012 The Android Open Source Project 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 #include "update_engine/certificate_checker.h" 18 19 #include <string> 20 21 #include <base/logging.h> 22 #include <base/strings/string_number_conversions.h> 23 #include <base/strings/string_util.h> 24 #include <base/strings/stringprintf.h> 25 #include <curl/curl.h> 26 #include <openssl/evp.h> 27 #include <openssl/ssl.h> 28 29 #include "update_engine/common/constants.h" 30 #include "update_engine/common/prefs_interface.h" 31 #include "update_engine/common/utils.h" 32 33 using std::string; 34 35 namespace chromeos_update_engine { 36 37 bool OpenSSLWrapper::GetCertificateDigest(X509_STORE_CTX* x509_ctx, 38 int* out_depth, 39 unsigned int* out_digest_length, 40 uint8_t* out_digest) const { 41 TEST_AND_RETURN_FALSE(out_digest); 42 X509* certificate = X509_STORE_CTX_get_current_cert(x509_ctx); 43 TEST_AND_RETURN_FALSE(certificate); 44 int depth = X509_STORE_CTX_get_error_depth(x509_ctx); 45 if (out_depth) 46 *out_depth = depth; 47 48 unsigned int len; 49 const EVP_MD* digest_function = EVP_sha256(); 50 bool success = X509_digest(certificate, digest_function, out_digest, &len); 51 52 if (success && out_digest_length) 53 *out_digest_length = len; 54 return success; 55 } 56 57 // static 58 CertificateChecker* CertificateChecker::cert_checker_singleton_ = nullptr; 59 60 CertificateChecker::CertificateChecker(PrefsInterface* prefs, 61 OpenSSLWrapper* openssl_wrapper) 62 : prefs_(prefs), openssl_wrapper_(openssl_wrapper) {} 63 64 CertificateChecker::~CertificateChecker() { 65 if (cert_checker_singleton_ == this) 66 cert_checker_singleton_ = nullptr; 67 } 68 69 void CertificateChecker::Init() { 70 CHECK(cert_checker_singleton_ == nullptr); 71 cert_checker_singleton_ = this; 72 } 73 74 // static 75 CURLcode CertificateChecker::ProcessSSLContext(CURL* curl_handle, 76 SSL_CTX* ssl_ctx, 77 void* ptr) { 78 ServerToCheck* server_to_check = reinterpret_cast<ServerToCheck*>(ptr); 79 80 if (!cert_checker_singleton_) { 81 DLOG(WARNING) << "No CertificateChecker singleton initialized."; 82 return CURLE_FAILED_INIT; 83 } 84 85 // From here we set the SSL_CTX to another callback, from the openssl library, 86 // which will be called after each server certificate is validated. However, 87 // since openssl does not allow us to pass our own data pointer to the 88 // callback, the certificate check will have to be done statically. Since we 89 // need to know which update server we are using in order to check the 90 // certificate, we hardcode Chrome OS's two known update servers here, and 91 // define a different static callback for each. Since this code should only 92 // run in official builds, this should not be a problem. However, if an update 93 // server different from the ones listed here is used, the check will not 94 // take place. 95 int (*verify_callback)(int, X509_STORE_CTX*); 96 switch (*server_to_check) { 97 case ServerToCheck::kDownload: 98 verify_callback = &CertificateChecker::VerifySSLCallbackDownload; 99 break; 100 case ServerToCheck::kUpdate: 101 verify_callback = &CertificateChecker::VerifySSLCallbackUpdate; 102 break; 103 case ServerToCheck::kNone: 104 verify_callback = nullptr; 105 break; 106 } 107 108 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, verify_callback); 109 return CURLE_OK; 110 } 111 112 // static 113 int CertificateChecker::VerifySSLCallbackDownload(int preverify_ok, 114 X509_STORE_CTX* x509_ctx) { 115 return VerifySSLCallback(preverify_ok, x509_ctx, ServerToCheck::kDownload); 116 } 117 118 // static 119 int CertificateChecker::VerifySSLCallbackUpdate(int preverify_ok, 120 X509_STORE_CTX* x509_ctx) { 121 return VerifySSLCallback(preverify_ok, x509_ctx, ServerToCheck::kUpdate); 122 } 123 124 // static 125 int CertificateChecker::VerifySSLCallback(int preverify_ok, 126 X509_STORE_CTX* x509_ctx, 127 ServerToCheck server_to_check) { 128 CHECK(cert_checker_singleton_ != nullptr); 129 return cert_checker_singleton_->CheckCertificateChange( 130 preverify_ok, x509_ctx, server_to_check) 131 ? 1 132 : 0; 133 } 134 135 bool CertificateChecker::CheckCertificateChange(int preverify_ok, 136 X509_STORE_CTX* x509_ctx, 137 ServerToCheck server_to_check) { 138 TEST_AND_RETURN_FALSE(prefs_ != nullptr); 139 140 // If pre-verification failed, we are not interested in the current 141 // certificate. We store a report to UMA and just propagate the fail result. 142 if (!preverify_ok) { 143 NotifyCertificateChecked(server_to_check, CertificateCheckResult::kFailed); 144 return false; 145 } 146 147 int depth; 148 unsigned int digest_length; 149 uint8_t digest[EVP_MAX_MD_SIZE]; 150 151 if (!openssl_wrapper_->GetCertificateDigest( 152 x509_ctx, &depth, &digest_length, digest)) { 153 LOG(WARNING) << "Failed to generate digest of X509 certificate " 154 << "from update server."; 155 NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid); 156 return true; 157 } 158 159 // We convert the raw bytes of the digest to an hex string, for storage in 160 // prefs. 161 string digest_string = base::HexEncode(digest, digest_length); 162 163 string storage_key = base::StringPrintf("%s-%d-%d", 164 kPrefsUpdateServerCertificate, 165 static_cast<int>(server_to_check), 166 depth); 167 string stored_digest; 168 // If there's no stored certificate, we just store the current one and return. 169 if (!prefs_->GetString(storage_key, &stored_digest)) { 170 if (!prefs_->SetString(storage_key, digest_string)) { 171 LOG(WARNING) << "Failed to store server certificate on storage key " 172 << storage_key; 173 } 174 NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid); 175 return true; 176 } 177 178 // Certificate changed, we store a report to UMA and store the most recent 179 // certificate. 180 if (stored_digest != digest_string) { 181 if (!prefs_->SetString(storage_key, digest_string)) { 182 LOG(WARNING) << "Failed to store server certificate on storage key " 183 << storage_key; 184 } 185 LOG(INFO) << "Certificate changed from " << stored_digest << " to " 186 << digest_string << "."; 187 NotifyCertificateChecked(server_to_check, 188 CertificateCheckResult::kValidChanged); 189 return true; 190 } 191 192 NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid); 193 // Since we don't perform actual SSL verification, we return success. 194 return true; 195 } 196 197 void CertificateChecker::NotifyCertificateChecked( 198 ServerToCheck server_to_check, CertificateCheckResult result) { 199 if (observer_) 200 observer_->CertificateChecked(server_to_check, result); 201 } 202 203 } // namespace chromeos_update_engine 204