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
GetCertificateDigest(X509_STORE_CTX * x509_ctx,int * out_depth,unsigned int * out_digest_length,uint8_t * out_digest) const37 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
CertificateChecker(PrefsInterface * prefs,OpenSSLWrapper * openssl_wrapper)60 CertificateChecker::CertificateChecker(PrefsInterface* prefs,
61 OpenSSLWrapper* openssl_wrapper)
62 : prefs_(prefs), openssl_wrapper_(openssl_wrapper) {
63 }
64
~CertificateChecker()65 CertificateChecker::~CertificateChecker() {
66 if (cert_checker_singleton_ == this)
67 cert_checker_singleton_ = nullptr;
68 }
69
Init()70 void CertificateChecker::Init() {
71 CHECK(cert_checker_singleton_ == nullptr);
72 cert_checker_singleton_ = this;
73 }
74
75 // static
ProcessSSLContext(CURL * curl_handle,SSL_CTX * ssl_ctx,void * ptr)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
VerifySSLCallbackDownload(int preverify_ok,X509_STORE_CTX * x509_ctx)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
VerifySSLCallbackUpdate(int preverify_ok,X509_STORE_CTX * x509_ctx)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
VerifySSLCallback(int preverify_ok,X509_STORE_CTX * x509_ctx,ServerToCheck server_to_check)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
CheckCertificateChange(int preverify_ok,X509_STORE_CTX * x509_ctx,ServerToCheck server_to_check)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
NotifyCertificateChecked(ServerToCheck server_to_check,CertificateCheckResult result)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