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