blob: 87f984820924d0c0c0179e8f67dcb2744e883206 [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 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>
23#include <base/strings/string_util.h>
24#include <base/strings/stringprintf.h>
Bruno Rocha7f9aea22011-09-12 14:31:24 -070025#include <curl/curl.h>
Bruno Rocha7f9aea22011-09-12 14:31:24 -070026#include <openssl/evp.h>
27#include <openssl/ssl.h>
28
Gilad Arnold1f847232014-04-07 12:07:49 -070029#include "metrics/metrics_library.h"
Alex Deymo39910dc2015-11-09 17:04:30 -080030#include "update_engine/common/constants.h"
31#include "update_engine/common/prefs_interface.h"
32#include "update_engine/common/utils.h"
Bruno Rocha7f9aea22011-09-12 14:31:24 -070033
34using std::string;
35
36namespace chromeos_update_engine {
37
38namespace {
39// This should be in the same order of CertificateChecker::ServerToCheck, with
40// the exception of kNone.
41static const char* kReportToSendKey[2] =
42 {kPrefsCertificateReportToSendUpdate,
43 kPrefsCertificateReportToSendDownload};
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
66// static
Alex Vakulenko88b591f2014-08-28 16:48:57 -070067SystemState* CertificateChecker::system_state_ = nullptr;
Bruno Rocha7f9aea22011-09-12 14:31:24 -070068
69// static
Alex Vakulenko88b591f2014-08-28 16:48:57 -070070OpenSSLWrapper* CertificateChecker::openssl_wrapper_ = nullptr;
Bruno Rocha7f9aea22011-09-12 14:31:24 -070071
72// static
73CURLcode CertificateChecker::ProcessSSLContext(CURL* curl_handle,
74 SSL_CTX* ssl_ctx,
75 void* ptr) {
76 // From here we set the SSL_CTX to another callback, from the openssl library,
77 // which will be called after each server certificate is validated. However,
78 // since openssl does not allow us to pass our own data pointer to the
79 // callback, the certificate check will have to be done statically. Since we
80 // need to know which update server we are using in order to check the
81 // certificate, we hardcode Chrome OS's two known update servers here, and
82 // define a different static callback for each. Since this code should only
83 // run in official builds, this should not be a problem. However, if an update
84 // server different from the ones listed here is used, the check will not
85 // take place.
86 ServerToCheck* server_to_check = reinterpret_cast<ServerToCheck*>(ptr);
87
88 // We check which server to check and set the appropriate static callback.
89 if (*server_to_check == kUpdate)
90 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, VerifySSLCallbackUpdateCheck);
91 if (*server_to_check == kDownload)
92 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, VerifySSLCallbackDownload);
93
94 return CURLE_OK;
95}
96
97// static
98int CertificateChecker::VerifySSLCallbackUpdateCheck(int preverify_ok,
99 X509_STORE_CTX* x509_ctx) {
100 return CertificateChecker::CheckCertificateChange(
101 kUpdate, preverify_ok, x509_ctx) ? 1 : 0;
102}
103
104// static
105int CertificateChecker::VerifySSLCallbackDownload(int preverify_ok,
106 X509_STORE_CTX* x509_ctx) {
107 return CertificateChecker::CheckCertificateChange(
108 kDownload, preverify_ok, x509_ctx) ? 1 : 0;
109}
110
111// static
112bool CertificateChecker::CheckCertificateChange(
113 ServerToCheck server_to_check, int preverify_ok,
114 X509_STORE_CTX* x509_ctx) {
115 static const char kUMAActionCertChanged[] =
116 "Updater.ServerCertificateChanged";
117 static const char kUMAActionCertFailed[] = "Updater.ServerCertificateFailed";
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700118 TEST_AND_RETURN_FALSE(system_state_ != nullptr);
119 TEST_AND_RETURN_FALSE(system_state_->prefs() != nullptr);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700120 TEST_AND_RETURN_FALSE(server_to_check != kNone);
121
122 // If pre-verification failed, we are not interested in the current
123 // certificate. We store a report to UMA and just propagate the fail result.
124 if (!preverify_ok) {
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800125 LOG_IF(WARNING, !system_state_->prefs()->SetString(
126 kReportToSendKey[server_to_check], kUMAActionCertFailed))
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700127 << "Failed to store UMA report on a failure to validate "
128 << "certificate from update server.";
129 return false;
130 }
131
132 int depth;
133 unsigned int digest_length;
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800134 uint8_t digest[EVP_MAX_MD_SIZE];
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700135
136 if (!openssl_wrapper_->GetCertificateDigest(x509_ctx,
137 &depth,
138 &digest_length,
139 digest)) {
140 LOG(WARNING) << "Failed to generate digest of X509 certificate "
141 << "from update server.";
142 return true;
143 }
144
145 // We convert the raw bytes of the digest to an hex string, for storage in
146 // prefs.
147 string digest_string = base::HexEncode(digest, digest_length);
148
Alex Vakulenko75039d72014-03-25 12:36:28 -0700149 string storage_key = base::StringPrintf("%s-%d-%d",
150 kPrefsUpdateServerCertificate,
151 server_to_check,
152 depth);
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700153 string stored_digest;
154 // If there's no stored certificate, we just store the current one and return.
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800155 if (!system_state_->prefs()->GetString(storage_key, &stored_digest)) {
156 LOG_IF(WARNING, !system_state_->prefs()->SetString(storage_key,
157 digest_string))
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700158 << "Failed to store server certificate on storage key " << storage_key;
159 return true;
160 }
161
162 // Certificate changed, we store a report to UMA and store the most recent
163 // certificate.
164 if (stored_digest != digest_string) {
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800165 LOG_IF(WARNING, !system_state_->prefs()->SetString(
166 kReportToSendKey[server_to_check], kUMAActionCertChanged))
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700167 << "Failed to store UMA report on a change on the "
168 << "certificate from update server.";
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800169 LOG_IF(WARNING, !system_state_->prefs()->SetString(storage_key,
170 digest_string))
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700171 << "Failed to store server certificate on storage key " << storage_key;
172 }
173
174 // Since we don't perform actual SSL verification, we return success.
175 return true;
176}
177
178// static
179void CertificateChecker::FlushReport() {
180 // This check shouldn't be needed, but it is useful for testing.
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800181 TEST_AND_RETURN(system_state_);
182 TEST_AND_RETURN(system_state_->metrics_lib());
183 TEST_AND_RETURN(system_state_->prefs());
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700184
185 // We flush reports for both servers.
186 for (size_t i = 0; i < arraysize(kReportToSendKey); i++) {
187 string report_to_send;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800188 if (system_state_->prefs()->GetString(kReportToSendKey[i], &report_to_send)
189 && !report_to_send.empty()) {
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700190 // There is a report to be sent. We send it and erase it.
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800191 LOG(INFO) << "Found report #" << i << ". Sending it";
192 LOG_IF(WARNING, !system_state_->metrics_lib()->SendUserActionToUMA(
193 report_to_send))
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700194 << "Failed to send server certificate report to UMA: "
195 << report_to_send;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800196 LOG_IF(WARNING, !system_state_->prefs()->Delete(kReportToSendKey[i]))
Bruno Rocha7f9aea22011-09-12 14:31:24 -0700197 << "Failed to erase server certificate report to be sent to UMA";
198 }
199 }
200}
201
202} // namespace chromeos_update_engine