blob: 8ffa596b858b6333723420de3caf3bd88dd805b5 [file] [log] [blame]
Alex Deymoaea4c1c2015-08-19 20:24:43 -07001//
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//
Bruno Rocha7f9aea22011-09-12 14:31:24 -070016
Alex Deymo14c0da82016-07-20 16:45:45 -070017#include "update_engine/certificate_checker.h"
Bruno Rocha7f9aea22011-09-12 14:31:24 -070018
19#include <string>
20
Alex Deymo39910dc2015-11-09 17:04:30 -080021#include <base/logging.h>
Alex Vakulenko75039d72014-03-25 12:36:28 -070022#include <base/strings/string_number_conversions.h>
Kelvin Zhangb9a9aa22024-10-15 10:38:35 -070023#include <android-base/stringprintf.h>
Bruno Rocha7f9aea22011-09-12 14:31:24 -070024#include <curl/curl.h>
Bruno Rocha7f9aea22011-09-12 14:31:24 -070025#include <openssl/evp.h>
26#include <openssl/ssl.h>
27
Alex Deymo39910dc2015-11-09 17:04:30 -080028#include "update_engine/common/constants.h"
29#include "update_engine/common/prefs_interface.h"
30#include "update_engine/common/utils.h"
Bruno Rocha7f9aea22011-09-12 14:31:24 -070031
32using std::string;
33
34namespace chromeos_update_engine {
35
Bruno Rocha7f9aea22011-09-12 14:31:24 -070036bool OpenSSLWrapper::GetCertificateDigest(X509_STORE_CTX* x509_ctx,
37 int* out_depth,
38 unsigned int* out_digest_length,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -080039 uint8_t* out_digest) const {
Bruno Rocha7f9aea22011-09-12 14:31:24 -070040 TEST_AND_RETURN_FALSE(out_digest);
41 X509* certificate = X509_STORE_CTX_get_current_cert(x509_ctx);
42 TEST_AND_RETURN_FALSE(certificate);
43 int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
44 if (out_depth)
45 *out_depth = depth;
46
47 unsigned int len;
48 const EVP_MD* digest_function = EVP_sha256();
49 bool success = X509_digest(certificate, digest_function, out_digest, &len);
50
51 if (success && out_digest_length)
52 *out_digest_length = len;
53 return success;
54}
55
Alex Deymo33e91e72015-12-01 18:26:08 -030056// static
57CertificateChecker* CertificateChecker::cert_checker_singleton_ = nullptr;
58
Alex Deymoc1c17b42015-11-23 03:53:15 -030059CertificateChecker::CertificateChecker(PrefsInterface* prefs,
Alex Deymo33e91e72015-12-01 18:26:08 -030060 OpenSSLWrapper* openssl_wrapper)
Amin Hassani7cc8bb02019-01-14 16:29:47 -080061 : prefs_(prefs), openssl_wrapper_(openssl_wrapper) {}
Alex Deymo33e91e72015-12-01 18:26:08 -030062
63CertificateChecker::~CertificateChecker() {
64 if (cert_checker_singleton_ == this)
65 cert_checker_singleton_ = nullptr;
66}
67
68void CertificateChecker::Init() {
69 CHECK(cert_checker_singleton_ == nullptr);
70 cert_checker_singleton_ = this;
Alex Deymoc1c17b42015-11-23 03:53:15 -030071}
Bruno Rocha7f9aea22011-09-12 14:31:24 -070072
73// static
74CURLcode CertificateChecker::ProcessSSLContext(CURL* curl_handle,
75 SSL_CTX* ssl_ctx,
76 void* ptr) {
Alex Deymo33e91e72015-12-01 18:26:08 -030077 ServerToCheck* server_to_check = reinterpret_cast<ServerToCheck*>(ptr);
78
79 if (!cert_checker_singleton_) {
80 DLOG(WARNING) << "No CertificateChecker singleton initialized.";
81 return CURLE_FAILED_INIT;
82 }
Alex Deymoc1c17b42015-11-23 03:53:15 -030083
Bruno Rocha7f9aea22011-09-12 14:31:24 -070084 // From here we set the SSL_CTX to another callback, from the openssl library,
85 // which will be called after each server certificate is validated. However,
86 // since openssl does not allow us to pass our own data pointer to the
Alex Deymo33e91e72015-12-01 18:26:08 -030087 // callback, the certificate check will have to be done statically. Since we
88 // need to know which update server we are using in order to check the
89 // certificate, we hardcode Chrome OS's two known update servers here, and
90 // define a different static callback for each. Since this code should only
91 // run in official builds, this should not be a problem. However, if an update
92 // server different from the ones listed here is used, the check will not
93 // take place.
94 int (*verify_callback)(int, X509_STORE_CTX*);
95 switch (*server_to_check) {
96 case ServerToCheck::kDownload:
97 verify_callback = &CertificateChecker::VerifySSLCallbackDownload;
98 break;
99 case ServerToCheck::kUpdate:
100 verify_callback = &CertificateChecker::VerifySSLCallbackUpdate;
101 break;
102 case ServerToCheck::kNone:
103 verify_callback = nullptr;
104 break;
105 }
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700106
Alex Deymo33e91e72015-12-01 18:26:08 -0300107 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, verify_callback);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700108 return CURLE_OK;
109}
110
111// static
Alex Deymo33e91e72015-12-01 18:26:08 -0300112int CertificateChecker::VerifySSLCallbackDownload(int preverify_ok,
113 X509_STORE_CTX* x509_ctx) {
114 return VerifySSLCallback(preverify_ok, x509_ctx, ServerToCheck::kDownload);
115}
116
117// static
118int CertificateChecker::VerifySSLCallbackUpdate(int preverify_ok,
119 X509_STORE_CTX* x509_ctx) {
120 return VerifySSLCallback(preverify_ok, x509_ctx, ServerToCheck::kUpdate);
121}
122
123// static
Alex Deymoc1c17b42015-11-23 03:53:15 -0300124int CertificateChecker::VerifySSLCallback(int preverify_ok,
Alex Deymo33e91e72015-12-01 18:26:08 -0300125 X509_STORE_CTX* x509_ctx,
126 ServerToCheck server_to_check) {
127 CHECK(cert_checker_singleton_ != nullptr);
128 return cert_checker_singleton_->CheckCertificateChange(
Amin Hassani7cc8bb02019-01-14 16:29:47 -0800129 preverify_ok, x509_ctx, server_to_check)
130 ? 1
131 : 0;
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700132}
133
Alex Deymoc1c17b42015-11-23 03:53:15 -0300134bool CertificateChecker::CheckCertificateChange(int preverify_ok,
Alex Deymo33e91e72015-12-01 18:26:08 -0300135 X509_STORE_CTX* x509_ctx,
136 ServerToCheck server_to_check) {
Alex Deymoc1c17b42015-11-23 03:53:15 -0300137 TEST_AND_RETURN_FALSE(prefs_ != nullptr);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700138
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) {
Alex Deymo33e91e72015-12-01 18:26:08 -0300142 NotifyCertificateChecked(server_to_check, CertificateCheckResult::kFailed);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700143 return false;
144 }
145
146 int depth;
147 unsigned int digest_length;
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800148 uint8_t digest[EVP_MAX_MD_SIZE];
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700149
Amin Hassani7cc8bb02019-01-14 16:29:47 -0800150 if (!openssl_wrapper_->GetCertificateDigest(
151 x509_ctx, &depth, &digest_length, digest)) {
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700152 LOG(WARNING) << "Failed to generate digest of X509 certificate "
153 << "from update server.";
Alex Deymo33e91e72015-12-01 18:26:08 -0300154 NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700155 return true;
156 }
157
158 // We convert the raw bytes of the digest to an hex string, for storage in
159 // prefs.
160 string digest_string = base::HexEncode(digest, digest_length);
161
Kelvin Zhangb9a9aa22024-10-15 10:38:35 -0700162 string storage_key =
163 android::base::StringPrintf("%s-%d-%d",
164 kPrefsUpdateServerCertificate,
165 static_cast<int>(server_to_check),
166 depth);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700167 string stored_digest;
168 // If there's no stored certificate, we just store the current one and return.
Alex Deymoc1c17b42015-11-23 03:53:15 -0300169 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 }
Alex Deymo33e91e72015-12-01 18:26:08 -0300174 NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700175 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) {
Alex Deymoc1c17b42015-11-23 03:53:15 -0300181 if (!prefs_->SetString(storage_key, digest_string)) {
182 LOG(WARNING) << "Failed to store server certificate on storage key "
183 << storage_key;
184 }
Alex Deymo33e91e72015-12-01 18:26:08 -0300185 LOG(INFO) << "Certificate changed from " << stored_digest << " to "
186 << digest_string << ".";
187 NotifyCertificateChecked(server_to_check,
188 CertificateCheckResult::kValidChanged);
Alex Deymoc1c17b42015-11-23 03:53:15 -0300189 return true;
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700190 }
191
Alex Deymo33e91e72015-12-01 18:26:08 -0300192 NotifyCertificateChecked(server_to_check, CertificateCheckResult::kValid);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700193 // Since we don't perform actual SSL verification, we return success.
194 return true;
195}
196
Alex Deymoc1c17b42015-11-23 03:53:15 -0300197void CertificateChecker::NotifyCertificateChecked(
Amin Hassani7cc8bb02019-01-14 16:29:47 -0800198 ServerToCheck server_to_check, CertificateCheckResult result) {
Alex Deymoc1c17b42015-11-23 03:53:15 -0300199 if (observer_)
Alex Deymo33e91e72015-12-01 18:26:08 -0300200 observer_->CertificateChecked(server_to_check, result);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700201}
202
203} // namespace chromeos_update_engine