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 65 CertificateChecker::~CertificateChecker() { 66 if (cert_checker_singleton_ == this) 67 cert_checker_singleton_ = nullptr; 68 } 69 70 void CertificateChecker::Init() { 71 CHECK(cert_checker_singleton_ == nullptr); 72 cert_checker_singleton_ = this; 73 } 74 75 // static 76 CURLcode CertificateChecker::ProcessSSLContext(CURL* curl_handle, 77 SSL_CTX* ssl_ctx, 78 void* ptr) { 79 ServerToCheck* server_to_check = reinterpret_cast<ServerToCheck*>(ptr); 80 81 if (!cert_checker_singleton_) { 82 DLOG(WARNING) << "No CertificateChecker singleton initialized."; 83 return CURLE_FAILED_INIT; 84 } 85 86 // From here we set the SSL_CTX to another callback, from the openssl library, 87 // which will be called after each server certificate is validated. However, 88 // since openssl does not allow us to pass our own data pointer to the 89 // callback, the certificate check will have to be done statically. Since we 90 // need to know which update server we are using in order to check the 91 // certificate, we hardcode Chrome OS's two known update servers here, and 92 // define a different static callback for each. Since this code should only 93 // run in official builds, this should not be a problem. However, if an update 94 // server different from the ones listed here is used, the check will not 95 // take place. 96 int (*verify_callback)(int, X509_STORE_CTX*); 97 switch (*server_to_check) { 98 case ServerToCheck::kDownload: 99 verify_callback = &CertificateChecker::VerifySSLCallbackDownload; 100 break; 101 case ServerToCheck::kUpdate: 102 verify_callback = &CertificateChecker::VerifySSLCallbackUpdate; 103 break; 104 case ServerToCheck::kNone: 105 verify_callback = nullptr; 106 break; 107 } 108 109 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, verify_callback); 110 return CURLE_OK; 111 } 112 113 // static 114 int CertificateChecker::VerifySSLCallbackDownload(int preverify_ok, 115 X509_STORE_CTX* x509_ctx) { 116 return VerifySSLCallback(preverify_ok, x509_ctx, ServerToCheck::kDownload); 117 } 118 119 // static 120 int CertificateChecker::VerifySSLCallbackUpdate(int preverify_ok, 121 X509_STORE_CTX* x509_ctx) { 122 return VerifySSLCallback(preverify_ok, x509_ctx, ServerToCheck::kUpdate); 123 } 124 125 // static 126 int CertificateChecker::VerifySSLCallback(int preverify_ok, 127 X509_STORE_CTX* x509_ctx, 128 ServerToCheck server_to_check) { 129 CHECK(cert_checker_singleton_ != nullptr); 130 return cert_checker_singleton_->CheckCertificateChange( 131 preverify_ok, x509_ctx, server_to_check) ? 1 : 0; 132 } 133 134 bool CertificateChecker::CheckCertificateChange(int preverify_ok, 135 X509_STORE_CTX* x509_ctx, 136 ServerToCheck server_to_check) { 137 TEST_AND_RETURN_FALSE(prefs_ != nullptr); 138 139 // If pre-verification failed, we are not interested in the current 140 // certificate. We store a report to UMA and just propagate the fail result. 141 if (!preverify_ok) { 142 NotifyCertificateChecked(server_to_check, CertificateCheckResult::kFailed); 143 return false; 144 } 145 146 int depth; 147 unsigned int digest_length; 148 uint8_t digest[EVP_MAX_MD_SIZE]; 149 150 if (!openssl_wrapper_->GetCertificateDigest(x509_ctx, 151 &depth, 152 &digest_length, 153 digest)) { 154 LOG(WARNING) << "Failed to generate digest of X509 certificate " 155 << "from update server."; 156 NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid); 157 return true; 158 } 159 160 // We convert the raw bytes of the digest to an hex string, for storage in 161 // prefs. 162 string digest_string = base::HexEncode(digest, digest_length); 163 164 string storage_key = 165 base::StringPrintf("%s-%d-%d", kPrefsUpdateServerCertificate, 166 static_cast<int>(server_to_check), 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, 199 CertificateCheckResult result) { 200 if (observer_) 201 observer_->CertificateChecked(server_to_check, result); 202 } 203 204 } // namespace chromeos_update_engine 205