1aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// 2aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// Copyright (C) 2012 The Android Open Source Project 3aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// 4aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// Licensed under the Apache License, Version 2.0 (the "License"); 5aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// you may not use this file except in compliance with the License. 6aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// You may obtain a copy of the License at 7aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// 8aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// http://www.apache.org/licenses/LICENSE-2.0 9aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// 10aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// Unless required by applicable law or agreed to in writing, software 11aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// distributed under the License is distributed on an "AS IS" BASIS, 12aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// See the License for the specific language governing permissions and 14aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// limitations under the License. 15aea4c1cea20dda7ae7e85fc8924a2d784f70d806Alex Deymo// 167f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 1714c0da88a93aa7b1aa71d5e7e923b537f0d419f3Alex Deymo#include "update_engine/certificate_checker.h" 187f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 197f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha#include <string> 207f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 2139910dcd1d68987ccee7c3031dc269233a8490bbAlex Deymo#include <base/logging.h> 2275039d7397f03dff77bdf4e26398049ff88edc4cAlex Vakulenko#include <base/strings/string_number_conversions.h> 2375039d7397f03dff77bdf4e26398049ff88edc4cAlex Vakulenko#include <base/strings/string_util.h> 2475039d7397f03dff77bdf4e26398049ff88edc4cAlex Vakulenko#include <base/strings/stringprintf.h> 257f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha#include <curl/curl.h> 267f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha#include <openssl/evp.h> 277f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha#include <openssl/ssl.h> 287f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 2939910dcd1d68987ccee7c3031dc269233a8490bbAlex Deymo#include "update_engine/common/constants.h" 3039910dcd1d68987ccee7c3031dc269233a8490bbAlex Deymo#include "update_engine/common/prefs_interface.h" 3139910dcd1d68987ccee7c3031dc269233a8490bbAlex Deymo#include "update_engine/common/utils.h" 327f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 337f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rochausing std::string; 347f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 357f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rochanamespace chromeos_update_engine { 367f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 377f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rochabool OpenSSLWrapper::GetCertificateDigest(X509_STORE_CTX* x509_ctx, 387f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha int* out_depth, 397f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha unsigned int* out_digest_length, 40f68bbbc952aa9a71898e4939b5f36187fa564a50Alex Vakulenko uint8_t* out_digest) const { 417f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha TEST_AND_RETURN_FALSE(out_digest); 427f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha X509* certificate = X509_STORE_CTX_get_current_cert(x509_ctx); 437f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha TEST_AND_RETURN_FALSE(certificate); 447f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha int depth = X509_STORE_CTX_get_error_depth(x509_ctx); 457f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha if (out_depth) 467f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha *out_depth = depth; 477f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 487f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha unsigned int len; 497f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha const EVP_MD* digest_function = EVP_sha256(); 507f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha bool success = X509_digest(certificate, digest_function, out_digest, &len); 517f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 527f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha if (success && out_digest_length) 537f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha *out_digest_length = len; 547f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha return success; 557f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha} 567f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 5733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo// static 5833e91e78bfe98c063b0c3b6d590976e275685686Alex DeymoCertificateChecker* CertificateChecker::cert_checker_singleton_ = nullptr; 5933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo 60c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex DeymoCertificateChecker::CertificateChecker(PrefsInterface* prefs, 6133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo OpenSSLWrapper* openssl_wrapper) 6233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo : prefs_(prefs), openssl_wrapper_(openssl_wrapper) { 6333e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo} 6433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo 6533e91e78bfe98c063b0c3b6d590976e275685686Alex DeymoCertificateChecker::~CertificateChecker() { 6633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo if (cert_checker_singleton_ == this) 6733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo cert_checker_singleton_ = nullptr; 6833e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo} 6933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo 7033e91e78bfe98c063b0c3b6d590976e275685686Alex Deymovoid CertificateChecker::Init() { 7133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo CHECK(cert_checker_singleton_ == nullptr); 7233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo cert_checker_singleton_ = this; 73c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo} 747f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 757f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha// static 767f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno RochaCURLcode CertificateChecker::ProcessSSLContext(CURL* curl_handle, 777f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha SSL_CTX* ssl_ctx, 787f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha void* ptr) { 7933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo ServerToCheck* server_to_check = reinterpret_cast<ServerToCheck*>(ptr); 8033e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo 8133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo if (!cert_checker_singleton_) { 8233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo DLOG(WARNING) << "No CertificateChecker singleton initialized."; 8333e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo return CURLE_FAILED_INIT; 8433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo } 85c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo 867f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha // From here we set the SSL_CTX to another callback, from the openssl library, 877f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha // which will be called after each server certificate is validated. However, 887f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha // since openssl does not allow us to pass our own data pointer to the 8933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo // callback, the certificate check will have to be done statically. Since we 9033e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo // need to know which update server we are using in order to check the 9133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo // certificate, we hardcode Chrome OS's two known update servers here, and 9233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo // define a different static callback for each. Since this code should only 9333e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo // run in official builds, this should not be a problem. However, if an update 9433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo // server different from the ones listed here is used, the check will not 9533e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo // take place. 9633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo int (*verify_callback)(int, X509_STORE_CTX*); 9733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo switch (*server_to_check) { 9833e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo case ServerToCheck::kDownload: 9933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo verify_callback = &CertificateChecker::VerifySSLCallbackDownload; 10033e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo break; 10133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo case ServerToCheck::kUpdate: 10233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo verify_callback = &CertificateChecker::VerifySSLCallbackUpdate; 10333e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo break; 10433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo case ServerToCheck::kNone: 10533e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo verify_callback = nullptr; 10633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo break; 10733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo } 1087f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 10933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, verify_callback); 1107f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha return CURLE_OK; 1117f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha} 1127f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 1137f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha// static 11433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymoint CertificateChecker::VerifySSLCallbackDownload(int preverify_ok, 11533e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo X509_STORE_CTX* x509_ctx) { 11633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo return VerifySSLCallback(preverify_ok, x509_ctx, ServerToCheck::kDownload); 11733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo} 11833e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo 11933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo// static 12033e91e78bfe98c063b0c3b6d590976e275685686Alex Deymoint CertificateChecker::VerifySSLCallbackUpdate(int preverify_ok, 12133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo X509_STORE_CTX* x509_ctx) { 12233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo return VerifySSLCallback(preverify_ok, x509_ctx, ServerToCheck::kUpdate); 12333e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo} 12433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo 12533e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo// static 126c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymoint CertificateChecker::VerifySSLCallback(int preverify_ok, 12733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo X509_STORE_CTX* x509_ctx, 12833e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo ServerToCheck server_to_check) { 12933e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo CHECK(cert_checker_singleton_ != nullptr); 13033e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo return cert_checker_singleton_->CheckCertificateChange( 13133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo preverify_ok, x509_ctx, server_to_check) ? 1 : 0; 1327f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha} 1337f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 134c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymobool CertificateChecker::CheckCertificateChange(int preverify_ok, 13533e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo X509_STORE_CTX* x509_ctx, 13633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo ServerToCheck server_to_check) { 137c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo TEST_AND_RETURN_FALSE(prefs_ != nullptr); 1387f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 1397f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha // If pre-verification failed, we are not interested in the current 1407f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha // certificate. We store a report to UMA and just propagate the fail result. 1417f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha if (!preverify_ok) { 14233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo NotifyCertificateChecked(server_to_check, CertificateCheckResult::kFailed); 1437f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha return false; 1447f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha } 1457f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 1467f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha int depth; 1477f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha unsigned int digest_length; 148f68bbbc952aa9a71898e4939b5f36187fa564a50Alex Vakulenko uint8_t digest[EVP_MAX_MD_SIZE]; 1497f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 1507f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha if (!openssl_wrapper_->GetCertificateDigest(x509_ctx, 1517f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha &depth, 1527f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha &digest_length, 1537f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha digest)) { 1547f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha LOG(WARNING) << "Failed to generate digest of X509 certificate " 1557f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha << "from update server."; 15633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid); 1577f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha return true; 1587f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha } 1597f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 1607f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha // We convert the raw bytes of the digest to an hex string, for storage in 1617f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha // prefs. 1627f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha string digest_string = base::HexEncode(digest, digest_length); 1637f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 164c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo string storage_key = 165c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo base::StringPrintf("%s-%d-%d", kPrefsUpdateServerCertificate, 16633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo static_cast<int>(server_to_check), depth); 1677f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha string stored_digest; 1687f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha // If there's no stored certificate, we just store the current one and return. 169c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo if (!prefs_->GetString(storage_key, &stored_digest)) { 170c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo if (!prefs_->SetString(storage_key, digest_string)) { 171c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo LOG(WARNING) << "Failed to store server certificate on storage key " 172c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo << storage_key; 173c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo } 17433e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid); 1757f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha return true; 1767f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha } 1777f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 1787f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha // Certificate changed, we store a report to UMA and store the most recent 1797f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha // certificate. 1807f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha if (stored_digest != digest_string) { 181c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo if (!prefs_->SetString(storage_key, digest_string)) { 182c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo LOG(WARNING) << "Failed to store server certificate on storage key " 183c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo << storage_key; 184c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo } 18533e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo LOG(INFO) << "Certificate changed from " << stored_digest << " to " 18633e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo << digest_string << "."; 18733e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo NotifyCertificateChecked(server_to_check, 18833e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo CertificateCheckResult::kValidChanged); 189c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo return true; 1907f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha } 1917f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 19233e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid); 1937f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha // Since we don't perform actual SSL verification, we return success. 1947f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha return true; 1957f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha} 1967f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 197c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymovoid CertificateChecker::NotifyCertificateChecked( 19833e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo ServerToCheck server_to_check, 199c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo CertificateCheckResult result) { 200c1c17b4ed6a3896b6343e737fd89682fa0c8436bAlex Deymo if (observer_) 20133e91e78bfe98c063b0c3b6d590976e275685686Alex Deymo observer_->CertificateChecked(server_to_check, result); 2027f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha} 2037f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha 2047f9aea2748370b0a26e1d5c36db7d8bbf3ba1245Bruno Rocha} // namespace chromeos_update_engine 205