1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chromeos/attestation/attestation_flow.h" 6 7 #include "base/bind.h" 8 #include "chromeos/cryptohome/async_method_caller.h" 9 #include "chromeos/dbus/cryptohome_client.h" 10 11 namespace chromeos { 12 namespace attestation { 13 14 namespace { 15 16 // Redirects to one of three callbacks based on a boolean value and dbus call 17 // status. 18 // 19 // Parameters 20 // on_true - Called when status=succes and value=true. 21 // on_false - Called when status=success and value=false. 22 // on_fail - Called when status=failure. 23 // status - The D-Bus operation status. 24 // value - The value returned by the D-Bus operation. 25 void DBusBoolRedirectCallback(const base::Closure& on_true, 26 const base::Closure& on_false, 27 const base::Closure& on_fail, 28 DBusMethodCallStatus status, 29 bool value) { 30 if (status != DBUS_METHOD_CALL_SUCCESS) { 31 LOG(ERROR) << "Attestation: Failed to query enrollment state."; 32 if (!on_fail.is_null()) 33 on_fail.Run(); 34 return; 35 } 36 const base::Closure& task = value ? on_true : on_false; 37 if (!task.is_null()) 38 task.Run(); 39 } 40 41 void DBusDataMethodCallback( 42 const AttestationFlow::CertificateCallback& callback, 43 DBusMethodCallStatus status, 44 bool result, 45 const std::string& data) { 46 if (status != DBUS_METHOD_CALL_SUCCESS) { 47 LOG(ERROR) << "Attestation: DBus data operation failed."; 48 if (!callback.is_null()) 49 callback.Run(false, ""); 50 return; 51 } 52 if (!callback.is_null()) 53 callback.Run(result, data); 54 } 55 56 AttestationKeyType GetKeyTypeForProfile( 57 AttestationCertificateProfile profile) { 58 switch (profile) { 59 case PROFILE_ENTERPRISE_MACHINE_CERTIFICATE: 60 return KEY_DEVICE; 61 case PROFILE_ENTERPRISE_USER_CERTIFICATE: 62 case PROFILE_CONTENT_PROTECTION_CERTIFICATE: 63 return KEY_USER; 64 } 65 NOTREACHED(); 66 return KEY_USER; 67 } 68 69 std::string GetKeyNameForProfile(AttestationCertificateProfile profile, 70 const std::string& origin) { 71 switch (profile) { 72 case PROFILE_ENTERPRISE_MACHINE_CERTIFICATE: 73 return kEnterpriseMachineKey; 74 case PROFILE_ENTERPRISE_USER_CERTIFICATE: 75 return kEnterpriseUserKey; 76 case PROFILE_CONTENT_PROTECTION_CERTIFICATE: 77 return std::string(kContentProtectionKeyPrefix) + origin; 78 } 79 NOTREACHED(); 80 return ""; 81 } 82 83 } // namespace 84 85 AttestationFlow::AttestationFlow(cryptohome::AsyncMethodCaller* async_caller, 86 CryptohomeClient* cryptohome_client, 87 scoped_ptr<ServerProxy> server_proxy) 88 : async_caller_(async_caller), 89 cryptohome_client_(cryptohome_client), 90 server_proxy_(server_proxy.Pass()), 91 weak_factory_(this) { 92 } 93 94 AttestationFlow::~AttestationFlow() { 95 } 96 97 void AttestationFlow::GetCertificate( 98 AttestationCertificateProfile certificate_profile, 99 const std::string& user_id, 100 const std::string& request_origin, 101 bool force_new_key, 102 const CertificateCallback& callback) { 103 // If this device has not enrolled with the Privacy CA, we need to do that 104 // first. Once enrolled we can proceed with the certificate request. 105 base::Closure do_cert_request = base::Bind( 106 &AttestationFlow::StartCertificateRequest, 107 weak_factory_.GetWeakPtr(), 108 certificate_profile, 109 user_id, 110 request_origin, 111 force_new_key, 112 callback); 113 base::Closure on_enroll_failure = base::Bind(callback, false, ""); 114 base::Closure do_enroll = base::Bind(&AttestationFlow::StartEnroll, 115 weak_factory_.GetWeakPtr(), 116 on_enroll_failure, 117 do_cert_request); 118 cryptohome_client_->TpmAttestationIsEnrolled(base::Bind( 119 &DBusBoolRedirectCallback, 120 do_cert_request, // If enrolled, proceed with cert request. 121 do_enroll, // If not enrolled, initiate enrollment. 122 on_enroll_failure)); 123 } 124 125 void AttestationFlow::StartEnroll(const base::Closure& on_failure, 126 const base::Closure& next_task) { 127 // Get the attestation service to create a Privacy CA enrollment request. 128 async_caller_->AsyncTpmAttestationCreateEnrollRequest(base::Bind( 129 &AttestationFlow::SendEnrollRequestToPCA, 130 weak_factory_.GetWeakPtr(), 131 on_failure, 132 next_task)); 133 } 134 135 void AttestationFlow::SendEnrollRequestToPCA(const base::Closure& on_failure, 136 const base::Closure& next_task, 137 bool success, 138 const std::string& data) { 139 if (!success) { 140 LOG(ERROR) << "Attestation: Failed to create enroll request."; 141 if (!on_failure.is_null()) 142 on_failure.Run(); 143 return; 144 } 145 146 // Send the request to the Privacy CA. 147 server_proxy_->SendEnrollRequest( 148 data, 149 base::Bind(&AttestationFlow::SendEnrollResponseToDaemon, 150 weak_factory_.GetWeakPtr(), 151 on_failure, 152 next_task)); 153 } 154 155 void AttestationFlow::SendEnrollResponseToDaemon( 156 const base::Closure& on_failure, 157 const base::Closure& next_task, 158 bool success, 159 const std::string& data) { 160 if (!success) { 161 LOG(ERROR) << "Attestation: Enroll request failed."; 162 if (!on_failure.is_null()) 163 on_failure.Run(); 164 return; 165 } 166 167 // Forward the response to the attestation service to complete enrollment. 168 async_caller_->AsyncTpmAttestationEnroll( 169 data, 170 base::Bind(&AttestationFlow::OnEnrollComplete, 171 weak_factory_.GetWeakPtr(), 172 on_failure, 173 next_task)); 174 } 175 176 void AttestationFlow::OnEnrollComplete(const base::Closure& on_failure, 177 const base::Closure& next_task, 178 bool success, 179 cryptohome::MountError /*not_used*/) { 180 if (!success) { 181 LOG(ERROR) << "Attestation: Failed to complete enrollment."; 182 if (!on_failure.is_null()) 183 on_failure.Run(); 184 return; 185 } 186 187 // Enrollment has successfully completed, we can move on to whatever is next. 188 if (!next_task.is_null()) 189 next_task.Run(); 190 } 191 192 void AttestationFlow::StartCertificateRequest( 193 AttestationCertificateProfile certificate_profile, 194 const std::string& user_id, 195 const std::string& request_origin, 196 bool generate_new_key, 197 const CertificateCallback& callback) { 198 AttestationKeyType key_type = GetKeyTypeForProfile(certificate_profile); 199 std::string key_name = GetKeyNameForProfile(certificate_profile, 200 request_origin); 201 if (generate_new_key) { 202 // Get the attestation service to create a Privacy CA certificate request. 203 async_caller_->AsyncTpmAttestationCreateCertRequest( 204 certificate_profile, 205 user_id, 206 request_origin, 207 base::Bind(&AttestationFlow::SendCertificateRequestToPCA, 208 weak_factory_.GetWeakPtr(), 209 key_type, 210 user_id, 211 key_name, 212 callback)); 213 } else { 214 // If the key already exists, query the existing certificate. 215 base::Closure on_key_exists = base::Bind( 216 &AttestationFlow::GetExistingCertificate, 217 weak_factory_.GetWeakPtr(), 218 key_type, 219 user_id, 220 key_name, 221 callback); 222 // If the key does not exist, call this method back with |generate_new_key| 223 // set to true. 224 base::Closure on_key_not_exists = base::Bind( 225 &AttestationFlow::StartCertificateRequest, 226 weak_factory_.GetWeakPtr(), 227 certificate_profile, 228 user_id, 229 request_origin, 230 true, 231 callback); 232 cryptohome_client_->TpmAttestationDoesKeyExist( 233 key_type, 234 user_id, 235 key_name, 236 base::Bind(&DBusBoolRedirectCallback, 237 on_key_exists, 238 on_key_not_exists, 239 base::Bind(callback, false, ""))); 240 } 241 } 242 243 void AttestationFlow::SendCertificateRequestToPCA( 244 AttestationKeyType key_type, 245 const std::string& user_id, 246 const std::string& key_name, 247 const CertificateCallback& callback, 248 bool success, 249 const std::string& data) { 250 if (!success) { 251 LOG(ERROR) << "Attestation: Failed to create certificate request."; 252 if (!callback.is_null()) 253 callback.Run(false, ""); 254 return; 255 } 256 257 // Send the request to the Privacy CA. 258 server_proxy_->SendCertificateRequest( 259 data, 260 base::Bind(&AttestationFlow::SendCertificateResponseToDaemon, 261 weak_factory_.GetWeakPtr(), 262 key_type, 263 user_id, 264 key_name, 265 callback)); 266 } 267 268 void AttestationFlow::SendCertificateResponseToDaemon( 269 AttestationKeyType key_type, 270 const std::string& user_id, 271 const std::string& key_name, 272 const CertificateCallback& callback, 273 bool success, 274 const std::string& data) { 275 if (!success) { 276 LOG(ERROR) << "Attestation: Certificate request failed."; 277 if (!callback.is_null()) 278 callback.Run(false, ""); 279 return; 280 } 281 282 // Forward the response to the attestation service to complete the operation. 283 async_caller_->AsyncTpmAttestationFinishCertRequest(data, 284 key_type, 285 user_id, 286 key_name, 287 base::Bind(callback)); 288 } 289 290 void AttestationFlow::GetExistingCertificate( 291 AttestationKeyType key_type, 292 const std::string& user_id, 293 const std::string& key_name, 294 const CertificateCallback& callback) { 295 cryptohome_client_->TpmAttestationGetCertificate( 296 key_type, 297 user_id, 298 key_name, 299 base::Bind(&DBusDataMethodCallback, callback)); 300 } 301 302 } // namespace attestation 303 } // namespace chromeos 304