blob: b8d8c94a6c5c8cda997ddb4274e4ed806dca5f97 [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 Deymo39910dc2015-11-09 17:04:30 -080017#include "update_engine/common/certificate_checker.h"
Bruno Rocha7f9aea22011-09-12 14:31:24 -070018
19#include <string>
20
Alex Deymoc1c17b42015-11-23 03:53:15 -030021#include <base/lazy_instance.h>
Alex Deymo39910dc2015-11-09 17:04:30 -080022#include <base/logging.h>
Alex Vakulenko75039d72014-03-25 12:36:28 -070023#include <base/strings/string_number_conversions.h>
24#include <base/strings/string_util.h>
25#include <base/strings/stringprintf.h>
Alex Deymoc1c17b42015-11-23 03:53:15 -030026#include <base/threading/thread_local.h>
Bruno Rocha7f9aea22011-09-12 14:31:24 -070027#include <curl/curl.h>
Bruno Rocha7f9aea22011-09-12 14:31:24 -070028#include <openssl/evp.h>
29#include <openssl/ssl.h>
30
Alex Deymo39910dc2015-11-09 17:04:30 -080031#include "update_engine/common/constants.h"
32#include "update_engine/common/prefs_interface.h"
33#include "update_engine/common/utils.h"
Bruno Rocha7f9aea22011-09-12 14:31:24 -070034
35using std::string;
36
37namespace chromeos_update_engine {
38
39namespace {
Alex Deymoc1c17b42015-11-23 03:53:15 -030040// A lazily created thread local storage for passing the current certificate
41// checker to the openssl callback.
42base::LazyInstance<base::ThreadLocalPointer<CertificateChecker>>::Leaky
43 lazy_tls_ptr;
Alex Vakulenkod2779df2014-06-16 13:19:00 -070044} // namespace
Bruno Rocha7f9aea22011-09-12 14:31:24 -070045
46bool OpenSSLWrapper::GetCertificateDigest(X509_STORE_CTX* x509_ctx,
47 int* out_depth,
48 unsigned int* out_digest_length,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -080049 uint8_t* out_digest) const {
Bruno Rocha7f9aea22011-09-12 14:31:24 -070050 TEST_AND_RETURN_FALSE(out_digest);
51 X509* certificate = X509_STORE_CTX_get_current_cert(x509_ctx);
52 TEST_AND_RETURN_FALSE(certificate);
53 int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
54 if (out_depth)
55 *out_depth = depth;
56
57 unsigned int len;
58 const EVP_MD* digest_function = EVP_sha256();
59 bool success = X509_digest(certificate, digest_function, out_digest, &len);
60
61 if (success && out_digest_length)
62 *out_digest_length = len;
63 return success;
64}
65
Alex Deymoc1c17b42015-11-23 03:53:15 -030066CertificateChecker::CertificateChecker(PrefsInterface* prefs,
67 OpenSSLWrapper* openssl_wrapper,
68 ServerToCheck server_to_check)
69 : prefs_(prefs),
70 openssl_wrapper_(openssl_wrapper),
71 server_to_check_(server_to_check) {
72}
Bruno Rocha7f9aea22011-09-12 14:31:24 -070073
74// static
75CURLcode CertificateChecker::ProcessSSLContext(CURL* curl_handle,
76 SSL_CTX* ssl_ctx,
77 void* ptr) {
Alex Deymoc1c17b42015-11-23 03:53:15 -030078 CertificateChecker* cert_checker = reinterpret_cast<CertificateChecker*>(ptr);
79
Bruno Rocha7f9aea22011-09-12 14:31:24 -070080 // From here we set the SSL_CTX to another callback, from the openssl library,
81 // which will be called after each server certificate is validated. However,
82 // since openssl does not allow us to pass our own data pointer to the
Alex Deymoc1c17b42015-11-23 03:53:15 -030083 // callback, the certificate check will have to be done statically. To pass
84 // the pointer to this instance, we use a thread-safe pointer in lazy_tls_ptr
85 // during the calls and clear them after it.
86 CHECK(cert_checker != nullptr);
87 lazy_tls_ptr.Pointer()->Set(cert_checker);
88 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, VerifySSLCallback);
89 // Sanity check: We should not re-enter this method during certificate
90 // checking.
91 CHECK(lazy_tls_ptr.Pointer()->Get() == cert_checker);
92 lazy_tls_ptr.Pointer()->Set(nullptr);
Bruno Rocha7f9aea22011-09-12 14:31:24 -070093
94 return CURLE_OK;
95}
96
97// static
Alex Deymoc1c17b42015-11-23 03:53:15 -030098int CertificateChecker::VerifySSLCallback(int preverify_ok,
99 X509_STORE_CTX* x509_ctx) {
100 CertificateChecker* cert_checker = lazy_tls_ptr.Pointer()->Get();
101 CHECK(cert_checker != nullptr);
102 return cert_checker->CheckCertificateChange(preverify_ok, x509_ctx) ? 1 : 0;
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700103}
104
Alex Deymoc1c17b42015-11-23 03:53:15 -0300105bool CertificateChecker::CheckCertificateChange(int preverify_ok,
106 X509_STORE_CTX* x509_ctx) {
107 TEST_AND_RETURN_FALSE(prefs_ != nullptr);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700108
109 // If pre-verification failed, we are not interested in the current
110 // certificate. We store a report to UMA and just propagate the fail result.
111 if (!preverify_ok) {
Alex Deymoc1c17b42015-11-23 03:53:15 -0300112 NotifyCertificateChecked(CertificateCheckResult::kFailed);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700113 return false;
114 }
115
116 int depth;
117 unsigned int digest_length;
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800118 uint8_t digest[EVP_MAX_MD_SIZE];
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700119
120 if (!openssl_wrapper_->GetCertificateDigest(x509_ctx,
121 &depth,
122 &digest_length,
123 digest)) {
124 LOG(WARNING) << "Failed to generate digest of X509 certificate "
125 << "from update server.";
Alex Deymoc1c17b42015-11-23 03:53:15 -0300126 NotifyCertificateChecked(CertificateCheckResult::kValid);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700127 return true;
128 }
129
130 // We convert the raw bytes of the digest to an hex string, for storage in
131 // prefs.
132 string digest_string = base::HexEncode(digest, digest_length);
133
Alex Deymoc1c17b42015-11-23 03:53:15 -0300134 string storage_key =
135 base::StringPrintf("%s-%d-%d", kPrefsUpdateServerCertificate,
136 static_cast<int>(server_to_check_), depth);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700137 string stored_digest;
138 // If there's no stored certificate, we just store the current one and return.
Alex Deymoc1c17b42015-11-23 03:53:15 -0300139 if (!prefs_->GetString(storage_key, &stored_digest)) {
140 if (!prefs_->SetString(storage_key, digest_string)) {
141 LOG(WARNING) << "Failed to store server certificate on storage key "
142 << storage_key;
143 }
144 NotifyCertificateChecked(CertificateCheckResult::kValid);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700145 return true;
146 }
147
148 // Certificate changed, we store a report to UMA and store the most recent
149 // certificate.
150 if (stored_digest != digest_string) {
Alex Deymoc1c17b42015-11-23 03:53:15 -0300151 if (!prefs_->SetString(storage_key, digest_string)) {
152 LOG(WARNING) << "Failed to store server certificate on storage key "
153 << storage_key;
154 }
155 NotifyCertificateChecked(CertificateCheckResult::kValidChanged);
156 return true;
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700157 }
158
Alex Deymoc1c17b42015-11-23 03:53:15 -0300159 NotifyCertificateChecked(CertificateCheckResult::kValid);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700160 // Since we don't perform actual SSL verification, we return success.
161 return true;
162}
163
Alex Deymoc1c17b42015-11-23 03:53:15 -0300164void CertificateChecker::NotifyCertificateChecked(
165 CertificateCheckResult result) {
166 if (observer_)
167 observer_->CertificateChecked(server_to_check_, result);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700168}
169
170} // namespace chromeos_update_engine