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