1/* 2 * Copyright (c) 2008-2010 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include "SecSCEP.h" 25 26#include <Security/SecCMS.h> 27#include <Security/SecRandom.h> 28#include <Security/SecIdentityPriv.h> 29#include <string.h> 30#include <AssertMacros.h> 31#include <CommonCrypto/CommonDigest.h> 32#include <CommonCrypto/CommonDigestSPI.h> 33#include <Security/SecItem.h> 34#include <Security/SecInternal.h> 35#include <Security/SecCertificateInternal.h> 36#include <Security/SecKeyPriv.h> 37#include <Security/SecInternal.h> 38#include <libDER/DER_Encode.h> 39#include <uuid/uuid.h> 40#include <utilities/array_size.h> 41#include <utilities/debugging.h> 42#include <utilities/SecIOFormat.h> 43 44typedef enum { 45 messageType = 2, 46 pkiStatus = 3, 47 failInfo = 4, 48 senderNonce = 5, 49 recipientNonce = 6, 50 transId = 7 51} scep_attr_t; 52 53static CFDataRef scep_oid(scep_attr_t type) 54{ 55/* +-------------------+-----------------------------------------------+ 56 | Name | ASN.1 Definition | 57 +-------------------+-----------------------------------------------+ 58 | id-VeriSign | OBJECT_IDENTIFIER ::= {2 16 US(840) 1 | 59 | | VeriSign(113733)} | 60 | id-pki | OBJECT_IDENTIFIER ::= {id-VeriSign pki(1)} | 61 | id-attributes | OBJECT_IDENTIFIER ::= {id-pki attributes(9)} | 62 | id-messageType | OBJECT_IDENTIFIER ::= {id-attributes | 63 | | messageType(2)} | 64 | id-pkiStatus | OBJECT_IDENTIFIER ::= {id-attributes | 65 | | pkiStatus(3)} | 66 | id-failInfo | OBJECT_IDENTIFIER ::= {id-attributes | 67 | | failInfo(4)} | 68 | id-senderNonce | OBJECT_IDENTIFIER ::= {id-attributes | 69 | | senderNonce(5)} | 70 | id-recipientNonce | OBJECT_IDENTIFIER ::= {id-attributes | 71 | | recipientNonce(6)} | 72 | id-transId | OBJECT_IDENTIFIER ::= {id-attributes | 73 | | transId(7)} | 74 | id-extensionReq | OBJECT_IDENTIFIER ::= {id-attributes | 75 | | extensionReq(8)} | 76 +-------------------+-----------------------------------------------+ */ 77 uint8_t oid_scep_attrs[] = 78 { 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0 }; 79 /* messageType:2 pkiStatus:3 failInfo:4 senderNonce:5 recipientNonce:6 transId:7 */ 80 if ((type < messageType) || (type > transId)) 81 return NULL; 82 83 oid_scep_attrs[sizeof(oid_scep_attrs) - 1] = type; 84 return CFDataCreate(kCFAllocatorDefault, oid_scep_attrs, sizeof(oid_scep_attrs)); 85} 86 87static const char CertRep[] = "3"; 88static const char PKCSReq[] = "19"; 89static const char GetCertInitial[] = "20"; 90static const char GetCert[] = "21"; 91static const char GetCRL[] = "22"; 92static const char PKIStatusSUCCESS[] = "0"; 93static const char PKIStatusFAILURE[] = "2"; 94static const char PKIStatusPENDING[] = "3"; 95 96static CFDataRef 97printable_string_data(size_t length, const char *bytes) 98{ 99 DERSize der_length_len = DERLengthOfLength(length); 100 size_t value_length = sizeof(SecASN1PrintableString) + der_length_len + length; 101 CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, value_length); 102 CFDataSetLength(data, value_length); 103 uint8_t *ptr = (uint8_t *)CFDataGetBytePtr(data); 104 *ptr++ = SecASN1PrintableString; 105 DEREncodeLength(length, ptr, &der_length_len); 106 ptr += der_length_len; 107 memcpy(ptr, bytes, length); 108 return (CFDataRef)data; 109} 110 111#define scep_result(value) printable_string_data(sizeof(value)-1, value) 112 113static CFTypeRef 114dictionary_array_value_1(CFDictionaryRef attrs, CFTypeRef attr) 115{ 116 CFTypeRef value = NULL; 117 CFArrayRef attr_values = NULL; 118 119 require(attr_values = (CFArrayRef)CFDictionaryGetValue(attrs, attr), out); 120 require(CFArrayGetCount(attr_values) == 1, out); 121 value = CFArrayGetValueAtIndex(attr_values, 0); 122out: 123 return value; 124} 125 126/* @@@ consider splitting into function returning single value 127 and function creating printable string from c str */ 128static bool scep_attr_has_val(CFDictionaryRef attrs, scep_attr_t attr, const char *val) 129{ 130 bool result = false; 131 CFDataRef msgtype_value_data = printable_string_data(strlen(val), val); 132 CFArrayRef msgtype_value_datas = CFArrayCreate(kCFAllocatorDefault, 133 (const void **)&msgtype_value_data, 1, &kCFTypeArrayCallBacks); 134 CFRelease(msgtype_value_data); 135 CFDataRef msgtype_oid_data = scep_oid(attr); 136 CFArrayRef msgtype_values = (CFArrayRef)CFDictionaryGetValue(attrs, msgtype_oid_data); 137 CFRelease(msgtype_oid_data); 138 if (msgtype_values && CFEqual(msgtype_value_datas, msgtype_values)) 139 result = true; 140 CFRelease(msgtype_value_datas); 141 142 return result; 143} 144 145static CFDataRef hexencode(CFDataRef data) 146{ 147 CFIndex ix, length = CFDataGetLength(data); 148 const uint8_t *bin_data = CFDataGetBytePtr(data); 149 uint8_t *hex_data = calloc(1, 2*length + 1); 150 require(length && bin_data && hex_data, out); 151 152 for (ix = 0; ix < length; ix++) 153 snprintf((char *)&hex_data[2*ix], 3, "%02X", bin_data[ix]); 154 155 return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, hex_data, 156 2*length, kCFAllocatorMalloc); 157out: 158 if (hex_data) 159 free(hex_data); 160 return NULL; 161} 162 163static CFDataRef pubkeyhash(SecKeyRef key) 164{ 165 CFTypeRef key_type = NULL; 166 CFDictionaryRef pubkey_attrs = NULL; 167 CFDataRef hash_pubkey_data = NULL, pubkey_data = NULL; 168 uint8_t pubkey_hash[CC_SHA1_DIGEST_LENGTH]; 169 170 require(pubkey_attrs = SecKeyCopyAttributeDictionary(key), out); 171 require( (key_type = CFDictionaryGetValue(pubkey_attrs, kSecAttrKeyClass)) && 172 CFEqual(key_type, kSecAttrKeyClassPublic), out); 173 require(pubkey_data = CFDictionaryGetValue(pubkey_attrs, kSecValueData), out); 174 require((unsigned long)CFDataGetLength(pubkey_data)<=UINT32_MAX, out); /* Correct as long as CFIndex is long */ 175 CCDigest(kCCDigestSHA1, CFDataGetBytePtr(pubkey_data), (CC_LONG)CFDataGetLength(pubkey_data), pubkey_hash); 176 hash_pubkey_data = CFDataCreate(kCFAllocatorDefault, pubkey_hash, sizeof(pubkey_hash)); 177out: 178 CFReleaseSafe(pubkey_attrs); 179 return hash_pubkey_data; 180} 181 182static void generate_sender_nonce(CFMutableDictionaryRef dict) 183{ 184 /* random sender nonce, to be verified against recipient nonce in reply */ 185 CFDataRef senderNonce_oid_data = scep_oid(senderNonce); 186 uint8_t senderNonce_value[18] = { 4, 16, }; 187 SecRandomCopyBytes(kSecRandomDefault, sizeof(senderNonce_value) - 2, senderNonce_value + 2); 188 CFDataRef senderNonce_value_data = CFDataCreate(kCFAllocatorDefault, 189 senderNonce_value, sizeof(senderNonce_value)); 190 if (senderNonce_oid_data && senderNonce_value_data) 191 CFDictionarySetValue(dict, senderNonce_oid_data, senderNonce_value_data); 192 CFReleaseNull(senderNonce_oid_data); 193 CFReleaseNull(senderNonce_value_data); 194} 195 196SecIdentityRef SecSCEPCreateTemporaryIdentity(SecKeyRef publicKey, SecKeyRef privateKey) 197{ 198 int key_usage = kSecKeyUsageDigitalSignature | kSecKeyUsageKeyEncipherment; 199 CFDictionaryRef self_signed_parameters = NULL; 200 CFNumberRef key_usage_num = NULL; 201 SecCertificateRef self_signed_certificate = NULL; 202 SecIdentityRef self_signed_identity = NULL; 203 CFStringRef cn_uuid = NULL; 204 CFArrayRef cn_dn = NULL, cn_dns = NULL, unique_rdns = NULL; 205 206 key_usage_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &key_usage); 207 require(key_usage_num, out); 208 209 const void *key[] = { kSecCertificateKeyUsage }; 210 const void *val[] = { key_usage_num }; 211 self_signed_parameters = CFDictionaryCreate(kCFAllocatorDefault, 212 key, val, array_size(key), 213 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 214 require(self_signed_parameters, out); 215 216 char uuid_string[37] = {}; 217 uuid_t uuid; 218 uuid_generate_random(uuid); 219 uuid_unparse(uuid, uuid_string); 220 cn_uuid = CFStringCreateWithCString(kCFAllocatorDefault, uuid_string, kCFStringEncodingASCII); 221 require(cn_uuid, out); 222 const void * cn[] = { kSecOidCommonName, cn_uuid }; 223 cn_dn = CFArrayCreate(kCFAllocatorDefault, cn, 2, NULL); 224 require(cn_dn, out); 225 cn_dns = CFArrayCreate(kCFAllocatorDefault, (const void **)&cn_dn, 1, NULL); 226 require(cn_dns, out); 227 unique_rdns = CFArrayCreate(kCFAllocatorDefault, (const void **)&cn_dns, 1, NULL); 228 require(unique_rdns, out); 229 230 self_signed_certificate = SecGenerateSelfSignedCertificate(unique_rdns, self_signed_parameters, publicKey, privateKey); 231 require(self_signed_certificate, out); 232 self_signed_identity = SecIdentityCreate(kCFAllocatorDefault, self_signed_certificate, privateKey); 233 234out: 235 CFReleaseSafe(key_usage_num); 236 CFReleaseSafe(self_signed_parameters); 237 CFReleaseSafe(self_signed_certificate); 238 CFReleaseSafe(unique_rdns); 239 CFReleaseSafe(cn_dns); 240 CFReleaseSafe(cn_dn); 241 CFReleaseSafe(cn_uuid); 242 243 return self_signed_identity; 244} 245 246CFDataRef 247SecSCEPGenerateCertificateRequest(CFArrayRef subject, CFDictionaryRef parameters, 248 SecKeyRef publicKey, SecKeyRef privateKey, 249 SecIdentityRef signer, CFTypeRef recipients) 250{ 251 CFDataRef csr = NULL; 252 CFMutableDataRef enveloped_data = NULL; 253 CFMutableDictionaryRef simple_attr = NULL; 254 SecIdentityRef self_signed_identity = NULL; 255 CFMutableDataRef signed_request = NULL; 256 SecCertificateRef recipient = NULL; 257 258 if (CFGetTypeID(recipients) == SecCertificateGetTypeID()) { 259 recipient = (SecCertificateRef)recipients; 260 } else if (CFGetTypeID(recipients) == CFArrayGetTypeID()) { 261 CFIndex recipient_count = CFArrayGetCount(recipients); 262 if (recipient_count > 1) { 263 /* get the encryption cert */ 264 recipient = (SecCertificateRef)CFArrayGetValueAtIndex(recipients, 0); 265 } else if (recipient_count == 1) { 266 /* if there is at least one we'll assume it's sign+encrypt */ 267 recipient = (SecCertificateRef)CFArrayGetValueAtIndex(recipients, 0); 268 } 269 } 270 require(recipient, out); 271 272 require(csr = SecGenerateCertificateRequest(subject, parameters, publicKey, privateKey), out); 273 require(enveloped_data = CFDataCreateMutable(kCFAllocatorDefault, 0), out); 274 require_noerr(SecCMSCreateEnvelopedData(recipient, parameters, csr, enveloped_data), out); 275 CFReleaseNull(csr); 276 277 simple_attr = CFDictionaryCreateMutable(kCFAllocatorDefault, 3, 278 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 279 280 /* generate a transaction id: hex encoded pubkey hash */ 281 CFDataRef public_key_hash = pubkeyhash(publicKey); 282 CFDataRef public_key_hash_hex = hexencode(public_key_hash); 283 CFReleaseSafe(public_key_hash); 284 CFDataRef transid_oid_data = scep_oid(transId); 285 CFDataRef transid_data = printable_string_data(CFDataGetLength(public_key_hash_hex), 286 (const char *)CFDataGetBytePtr(public_key_hash_hex)); 287 CFReleaseSafe(public_key_hash_hex); 288 289 CFDictionarySetValue(simple_attr, transid_oid_data, transid_data); 290 CFReleaseNull(transid_oid_data); 291 CFReleaseNull(transid_data); 292 293 /* message type: PKCSReq (19) */ 294 CFDataRef msgtype_value_data = NULL; 295 CFDataRef msgtype_oid_data = NULL; 296 require(msgtype_oid_data = scep_oid(messageType), out); 297 require(msgtype_value_data = printable_string_data(strlen(PKCSReq), PKCSReq), out); 298 299 CFDictionarySetValue(simple_attr, msgtype_oid_data, msgtype_value_data); 300 CFReleaseNull(msgtype_oid_data); 301 CFReleaseNull(msgtype_value_data); 302 303 /* random sender nonce, to be verified against recipient nonce in reply */ 304 generate_sender_nonce(simple_attr); 305 306 /* XXX/cs remove auto-generation once managedconfig is no longer using this */ 307 if (signer) { 308 self_signed_identity = signer; 309 CFRetain(self_signed_identity); 310 } else { 311 self_signed_identity = SecSCEPCreateTemporaryIdentity(publicKey, privateKey); 312 313 /* Add our temporary cert to the keychain for CMS decryption of 314 the reply. If we happened to have picked an existing UUID 315 we fail. We should pick a different UUID and try again. */ 316 require(self_signed_identity, out); 317 CFDictionaryRef identity_add = CFDictionaryCreate(NULL, 318 &kSecValueRef, (const void **)&self_signed_identity, 1, NULL, NULL); 319 require_noerr_action(SecItemAdd(identity_add, NULL), out, 320 CFReleaseSafe(identity_add)); 321 CFReleaseSafe(identity_add); 322 } 323 require(self_signed_identity, out); 324 325 signed_request = CFDataCreateMutable(kCFAllocatorDefault, 0); 326 require_noerr_action(SecCMSCreateSignedData(self_signed_identity, enveloped_data, 327 parameters, simple_attr, signed_request), out, CFReleaseNull(signed_request)); 328 329 330out: 331 332 CFReleaseSafe(simple_attr); 333 CFReleaseSafe(self_signed_identity); 334 CFReleaseSafe(enveloped_data); 335 CFReleaseSafe(csr); 336 return signed_request; 337} 338 339 340CFDataRef 341SecSCEPCertifyRequest(CFDataRef request, SecIdentityRef ca_identity, CFDataRef serialno, bool pend_request) 342{ 343 CFDictionaryRef simple_attr = NULL; 344 SecCertificateRef ca_certificate = NULL; 345 SecKeyRef ca_public_key = NULL; 346 SecCertificateRef cert = NULL; 347 SecPolicyRef policy = NULL; 348 CFDataRef cert_pkcs7 = NULL; 349 CFMutableDataRef cert_msg = NULL; 350 CFMutableDataRef signed_reply = NULL; 351 SecTrustRef trust = NULL; 352 CFDataRef signed_content = NULL; 353 CFDictionaryRef signed_attributes = NULL; 354 SecCertificateRef signer_cert = NULL; 355 CFDataRef transid_oid_data = NULL, senderNonce_oid_data = NULL, transid_value = NULL; 356 CFDataRef subject = NULL, extensions = NULL, senderNonce_value = NULL; 357 CFStringRef challenge = NULL; 358 SecKeyRef tbsPublicKey = NULL; 359 CFMutableDataRef encrypted_content = NULL; 360 SecCertificateRef recipient = NULL; 361 CFDictionaryRef parameters = NULL; 362 363 require_noerr(SecIdentityCopyCertificate(ca_identity, &ca_certificate), out); 364 ca_public_key = SecCertificateCopyPublicKey(ca_certificate); /*@@@*/ 365 366 /* unwrap outer layer: */ 367 policy = SecPolicyCreateBasicX509(); 368 369 require_noerr(SecCMSVerifyCopyDataAndAttributes(request, NULL, 370 policy, &trust, &signed_content, &signed_attributes), out); 371 /* remember signer: is signer certified by us, then re-certify, no challenge needed */ 372 SecTrustResultType result; 373 require_noerr(SecTrustEvaluate(trust, &result), out); 374 require (signer_cert = SecTrustGetCertificateAtIndex(trust, 0), out); 375 bool recertify = !SecCertificateIsSignedBy(signer_cert, ca_public_key); 376 377 /* msgType should be certreq msg */ 378 require(scep_attr_has_val(signed_attributes, messageType, PKCSReq), out); 379 380 /* remember transaction id just for reuse */ 381 require(transid_oid_data = scep_oid(transId), out); 382 require(transid_value = 383 dictionary_array_value_1(signed_attributes, transid_oid_data), out); 384 385 /* senderNonce becomes recipientNonce */ 386 require(senderNonce_oid_data = scep_oid(senderNonce), out); 387 require(senderNonce_value = 388 dictionary_array_value_1(signed_attributes, senderNonce_oid_data), out); 389 390 /* decrypt the request */ 391 encrypted_content = CFDataCreateMutable(kCFAllocatorDefault, 0); 392 require_noerr(SecCMSDecryptEnvelopedData(signed_content, encrypted_content, &recipient), out); 393 require(recipient && CFEqual(ca_certificate, recipient), out); 394 395 /* verify CSR */ 396 require(SecVerifyCertificateRequest(encrypted_content, &tbsPublicKey, &challenge, &subject, &extensions), out); 397 CFReleaseNull(encrypted_content); 398 399 /* @@@ 400 // alternatively send a pending message 401 // pkistatus {{id-attributes pkiStatus(3)} "FAILURE"} 402 // failInfo {{id-attributes failInfo(4)} "the reason to reject"} 403 */ 404 405 /* verify challenge - this would need to be a callout that can determine 406 the challenge appropriate for the subject */ 407 if (!recertify) 408 require( challenge && (CFStringGetTypeID() == CFGetTypeID(challenge)) && 409 CFEqual(CFSTR("magic"), challenge), out); 410 411 require(cert_msg = CFDataCreateMutable(kCFAllocatorDefault, 0), out); 412 413 if (!pend_request) { 414 /* sign cert */ 415 cert = SecIdentitySignCertificate(ca_identity, serialno, 416 tbsPublicKey, subject, extensions); 417 418 /* degenerate cms with cert */ 419 require (cert_pkcs7 = SecCMSCreateCertificatesOnlyMessage(cert), out); 420 CFReleaseNull(cert); 421 422 /* envelope for client */ 423 require_noerr(SecCMSCreateEnvelopedData(signer_cert, NULL, cert_pkcs7, cert_msg), out); 424 CFReleaseNull(cert_pkcs7); 425 } 426 427 CFDataRef pki_status_oid = scep_oid(pkiStatus); 428 CFDataRef pki_status_value = pend_request ? scep_result(PKIStatusPENDING) : scep_result(PKIStatusSUCCESS); 429 CFDataRef message_type_oid = scep_oid(messageType), message_type_value = scep_result(CertRep); 430 const void *oid[] = { transid_oid_data, pki_status_oid, message_type_oid }; 431 const void *value[] = { transid_value, pki_status_value, message_type_value }; 432 simple_attr = CFDictionaryCreate(kCFAllocatorDefault, oid, value, array_size(oid), 433 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 434 CFReleaseSafe(pki_status_oid); CFReleaseSafe(pki_status_value); 435 CFReleaseSafe(message_type_oid); CFReleaseSafe(message_type_value); 436 437 /* sign with ra/ca cert and add attributes */ 438 signed_reply = CFDataCreateMutable(kCFAllocatorDefault, 0); 439 const void *signing_params[] = { kSecCMSCertChainMode }; 440 const void *signing_params_vals[] = { kSecCMSCertChainModeNone }; 441 parameters = CFDictionaryCreate(kCFAllocatorDefault, signing_params, signing_params_vals, array_size(signing_params), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 442 require_noerr_action(SecCMSCreateSignedData(ca_identity, cert_msg, parameters, simple_attr, signed_reply), out, CFReleaseNull(signed_reply)); 443 444out: 445 CFReleaseSafe(ca_certificate); 446 CFReleaseSafe(ca_public_key); 447 CFReleaseSafe(cert); 448 CFReleaseSafe(cert_pkcs7); 449 CFReleaseSafe(cert_msg); 450 CFReleaseSafe(trust); 451 CFReleaseSafe(policy); 452 CFReleaseSafe(signed_content); 453 CFReleaseSafe(signed_attributes); 454 CFReleaseSafe(transid_oid_data); 455 CFReleaseSafe(senderNonce_oid_data); 456 CFReleaseSafe(subject); 457 CFReleaseSafe(extensions); 458 CFReleaseSafe(challenge); 459 CFReleaseSafe(tbsPublicKey); 460 CFReleaseSafe(encrypted_content); 461 CFReleaseSafe(simple_attr); 462 CFReleaseSafe(recipient); 463 CFReleaseSafe(parameters); 464 465 return signed_reply; 466} 467 468static CFStringRef 469copy_signed_attr_printable_string_value(CFDictionaryRef signed_attributes, scep_attr_t attr) 470{ 471 CFStringRef printable_string = NULL; 472 CFDataRef key_oid = NULL; 473 474 key_oid = scep_oid(attr); 475 require(key_oid, out); 476 477 CFArrayRef values = (CFArrayRef)CFDictionaryGetValue(signed_attributes, key_oid); 478 require_quiet(values && (CFGetTypeID(values) == CFArrayGetTypeID()) 479 && (CFArrayGetCount(values) == 1), out); 480 CFDataRef value = CFArrayGetValueAtIndex(values, 0); 481 const uint8_t *bytes = CFDataGetBytePtr(value); 482 size_t length = CFDataGetLength(value); 483 require(length >= 2, out); 484 require(bytes[0] == 0x13, out); 485 /* no scep responses defined that are longer */ 486 require(!(bytes[1] & 0x80) && (bytes[1] == length-2), out); 487 printable_string = CFStringCreateWithBytes(kCFAllocatorDefault, 488 bytes + 2, length - 2, kCFStringEncodingASCII, false); 489out: 490 CFReleaseSafe(key_oid); 491 492 return printable_string; 493} 494 495CFArrayRef 496SecSCEPVerifyReply(CFDataRef request, CFDataRef reply, CFTypeRef ca_certificates, 497 CFErrorRef *server_error) 498{ 499 SecKeyRef ca_public_key = NULL; 500 SecCertificateRef cert = NULL; 501 SecPolicyRef policy = NULL; 502 CFDataRef cert_msg = NULL; 503 CFMutableDataRef enc_cert_msg = NULL; 504 SecTrustRef trust = NULL; 505 CFDataRef signed_content = NULL; 506 CFDictionaryRef signed_attributes = NULL; 507 CFDictionaryRef attributes = NULL; 508 SecCertificateRef signer_cert = NULL; 509 510 CFMutableDataRef encrypted_content = NULL; 511 SecCertificateRef recipient = NULL; 512 CFArrayRef certificates = NULL; 513 514 SecCertificateRef reply_signer = NULL; 515 516 CFStringRef msg_type = NULL; 517 CFStringRef pki_status = NULL; 518 519 if (CFGetTypeID(ca_certificates) == SecCertificateGetTypeID()) { 520 reply_signer = (SecCertificateRef)ca_certificates; 521 } else if (CFGetTypeID(ca_certificates) == CFArrayGetTypeID()) { 522 CFIndex reply_signer_count = CFArrayGetCount(ca_certificates); 523 if (reply_signer_count > 1) { 524 /* get the signer cert */ 525 reply_signer = (SecCertificateRef)CFArrayGetValueAtIndex(ca_certificates, 1); 526 } else if (reply_signer_count == 1) { 527 /* if there is at least one we'll assume it's sign+encrypt */ 528 reply_signer = (SecCertificateRef)CFArrayGetValueAtIndex(ca_certificates, 0); 529 } 530 } 531 require(reply_signer, out); 532 533 /* unwrap outer layer */ 534 policy = SecPolicyCreateBasicX509(); 535 CFArrayRef additional_certificates = CFArrayCreate(kCFAllocatorDefault, (const void **)&reply_signer, 1, &kCFTypeArrayCallBacks); 536 require_noerr(SecCMSVerifySignedData(reply, NULL, 537 policy, &trust, additional_certificates, &signed_content, &attributes), out); 538 CFReleaseSafe(additional_certificates); 539 if (attributes) 540 signed_attributes = CFDictionaryGetValue(attributes, kSecCMSSignedAttributes); 541 542 /* response should be signed by ra */ 543 SecTrustResultType result; 544 require_noerr(SecTrustEvaluate(trust, &result), out); 545 require(signer_cert = SecTrustGetCertificateAtIndex(trust, 0), out); 546 require(CFEqual(reply_signer, signer_cert), out); 547 548 /* msgType should be certreq msg */ 549 require(signed_attributes, out); 550 msg_type = copy_signed_attr_printable_string_value(signed_attributes, messageType); 551 pki_status = copy_signed_attr_printable_string_value(signed_attributes, pkiStatus); 552 553 if (msg_type || pki_status) { 554 require(msg_type && CFEqual(msg_type, CFSTR("3")), out); 555 556 require(pki_status, out); 557 if (CFEqual(pki_status, CFSTR("2"))) { 558 goto out; // FAILURE, the end (return NULL) 559 } else if (CFEqual(pki_status, CFSTR("3"))) { 560 CFDataRef transid_oid_data = NULL, transid_value = NULL; 561 require(transid_oid_data = scep_oid(transId), out); 562 require(transid_value = dictionary_array_value_1(signed_attributes, transid_oid_data), out); 563 CFDictionaryRef err_dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&transid_oid_data, (const void **)&transid_value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 564 *server_error = CFErrorCreate(kCFAllocatorDefault, 565 CFSTR("PENDING"), 3, err_dict); 566 CFReleaseSafe(err_dict); 567 CFReleaseSafe(transid_oid_data); 568 goto out; 569 } 570 require(CFEqual(pki_status, CFSTR("0")), out); 571 } 572 573 // can we decode the request? 574 encrypted_content = CFDataCreateMutable(kCFAllocatorDefault, 0); 575 require_noerr(SecCMSDecryptEnvelopedData(signed_content, encrypted_content, &recipient), out); 576 require(recipient, out); 577 // verify recipient belongs with our private key 578 579 // verify CSR: 580 require(certificates = SecCMSCertificatesOnlyMessageCopyCertificates(encrypted_content), out); 581 582 // recipient is either our temporary self-signed cert or the old cert we just used 583 // to recertify. if we have new certificates and have stored them successfully we 584 // can now get rid of the cert. 585 /* XXX/cs 586 This should move outside of thise function when we force a signer 587 to be passed in */ 588 CFDictionaryRef cert_delete = CFDictionaryCreate(NULL, 589 &kSecValueRef, (const void **)&recipient, 1, NULL, NULL); 590 require_noerr_action(SecItemDelete(cert_delete), out, 591 CFReleaseSafe(cert_delete)); 592 CFReleaseSafe(cert_delete); 593 594out: 595 CFReleaseSafe(ca_public_key); 596 CFReleaseSafe(cert); 597 CFReleaseSafe(cert_msg); 598 CFReleaseSafe(enc_cert_msg); 599 CFReleaseSafe(trust); 600 CFReleaseSafe(policy); 601 CFReleaseSafe(signed_content); 602 CFReleaseSafe(encrypted_content); 603 CFReleaseSafe(recipient); 604 CFReleaseSafe(msg_type); 605 CFReleaseSafe(pki_status); 606 CFReleaseSafe(attributes); 607 608 return certificates; 609} 610 611OSStatus SecSCEPValidateCACertMessage(CFArrayRef certs, 612 CFDataRef ca_fingerprint, 613 SecCertificateRef *ca_certificate, 614 SecCertificateRef *ra_signing_certificate, 615 SecCertificateRef *ra_encryption_certificate) 616{ 617 OSStatus status = errSecParam; 618 SecCertificateRef _ca_certificate = NULL, _ra_signing_certificate = NULL, 619 _ra_encryption_certificate = NULL, _ra_certificate = NULL; 620 621 CFIndex j, count = CFArrayGetCount(certs); 622 CFMutableArrayRef chain = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 623 SecPolicyRef policy = SecPolicyCreateBasicX509(); 624 SecTrustRef trust = NULL; 625 require(chain, out); 626 for (j=0; j<count; j++) { 627 const void *candidate_leaf = CFArrayGetValueAtIndex(certs, j); 628 CFArrayRemoveAllValues(chain); 629 CFArraySetValueAtIndex(chain, 0, candidate_leaf); 630 CFArrayAppendArray(chain, certs, CFRangeMake(0, count)); 631 CFArrayRemoveValueAtIndex(chain, 1 + j); 632 require_noerr(SecTrustCreateWithCertificates(chain, 633 policy, &trust), out); 634 SecTrustResultType trust_result; 635 SecTrustEvaluate(trust, &trust_result); 636 CFIndex chain_count = SecTrustGetCertificateCount(trust); 637 secdebug("scep", "candidate leaf: %@ forms chain of length %" PRIdCFIndex, candidate_leaf, chain_count); 638 if (chain_count > 1) { 639 SecCertificateRef leaf = SecTrustGetCertificateAtIndex(trust, 0); 640 SecCertificateRef ca_leaf = SecTrustGetCertificateAtIndex(trust, chain_count - 1); 641 if (!_ca_certificate) { 642 if (ca_fingerprint) { 643 secdebug("scep", "checking ca %@ against fingerprint %@", ca_leaf, ca_fingerprint); 644 uint8_t ca_hash[CC_SHA1_DIGEST_LENGTH]; /*max(md5,sha-1)*/ 645 CFDataRef ca_cert_data = SecCertificateCopyData(ca_leaf); 646 require(ca_cert_data, out); 647 size_t ca_data_len = CFDataGetLength(ca_cert_data); 648 size_t ca_fingerprint_len = CFDataGetLength(ca_fingerprint); 649 const uint8_t *ca_data = CFDataGetBytePtr(ca_cert_data); 650 require(ca_data_len && ca_data, out); 651 require(ca_data_len<UINT32_MAX, out); 652 switch (ca_fingerprint_len) { 653 case CC_MD5_DIGEST_LENGTH: 654 CC_MD5(ca_data, (CC_LONG)ca_data_len, ca_hash); 655 break; 656 657 case CC_SHA1_DIGEST_LENGTH: 658 CCDigest(kCCDigestSHA1, ca_data, (CC_LONG)ca_data_len, ca_hash); 659 break; 660 661 default: 662 goto out; 663 } 664 CFRelease(ca_cert_data); 665 CFDataRef ca_hash_cfdata = CFDataCreate(kCFAllocatorDefault, 666 ca_hash, ca_fingerprint_len); 667 require(ca_hash_cfdata, out); 668 require_action(CFEqual(ca_fingerprint, ca_hash_cfdata), 669 out, CFRelease(ca_hash_cfdata)); 670 CFRelease(ca_hash_cfdata); 671 } 672 _ca_certificate = ca_leaf; 673 CFRetain(ca_leaf); 674 } else { 675 // if ca_certificate is already set, this should be the same 676 require(CFEqual(_ca_certificate, ca_leaf), out); 677 } 678 679 // is leaf allowed to sign and/or encrypt? 680 SecKeyUsage key_usage = SecCertificateGetKeyUsage(leaf); 681 bool can_sign = (key_usage & kSecKeyUsageDigitalSignature); 682 bool can_enc = (key_usage & kSecKeyUsageKeyEncipherment); 683 if (!_ra_certificate && can_sign && can_enc) { 684 _ra_certificate = leaf; 685 CFRetain(leaf); 686 } 687 else if (!_ra_encryption_certificate && !can_sign && can_enc) { 688 _ra_encryption_certificate = leaf; 689 CFRetain(leaf); 690 } 691 else if (!_ra_signing_certificate && !can_enc && can_sign) { 692 _ra_signing_certificate = leaf; 693 CFRetain(leaf); 694 } 695 } 696 if (trust) { CFRelease(trust); trust = NULL; } 697 } 698 699 // we should have both a ca certificate and at least one ra certificate now 700 require(_ca_certificate, out); 701 require(_ra_certificate || 702 (_ra_signing_certificate && _ra_encryption_certificate), out); 703 704 if (ca_certificate) { 705 *ca_certificate = _ca_certificate; 706 _ca_certificate = NULL; 707 } 708 if (_ra_signing_certificate && _ra_encryption_certificate) { 709 if (ra_signing_certificate) { 710 *ra_signing_certificate = _ra_signing_certificate; 711 _ra_signing_certificate = NULL; 712 } 713 if (ra_encryption_certificate) { 714 *ra_encryption_certificate = _ra_encryption_certificate; 715 _ra_encryption_certificate = NULL; 716 } 717 } else if (_ra_certificate) { 718 if (ra_signing_certificate) { 719 *ra_signing_certificate = _ra_certificate; 720 _ra_certificate = NULL; 721 } 722 } 723 724 status = errSecSuccess; 725 726out: 727 if (_ra_encryption_certificate) CFRelease(_ra_encryption_certificate); 728 if (_ra_signing_certificate) CFRelease(_ra_signing_certificate); 729 if (_ca_certificate) CFRelease(_ca_certificate); 730 if (policy) CFRelease(policy); 731 if (trust) CFRelease(trust); 732 if (chain) CFRelease(chain); 733 return status; 734 735} 736 737 738/*! 739 @function SecSCEPGetCertInitial 740 @abstract generate a scep cert initial request, to be presented to 741 a scep server, in case the first request timed out 742 */ 743 744// XXX/cs pass CA/RA certificates as a CFTypeRef: one or more certificates for ca_certificate and recipient 745 746CFDataRef 747SecSCEPGetCertInitial(SecCertificateRef ca_certificate, CFArrayRef subject, CFDictionaryRef parameters, 748 CFDictionaryRef signed_attrs, SecIdentityRef signer, CFTypeRef recipient) 749{ 750 CFMutableDataRef signed_request = NULL; 751 CFMutableDictionaryRef simple_attr = NULL; 752 CFDataRef pki_message_contents = NULL; 753 CFMutableDataRef enveloped_data = NULL; 754 755 require(signed_attrs, out); 756 require(pki_message_contents = SecGenerateCertificateRequestSubject(ca_certificate, subject), out); 757 require(enveloped_data = CFDataCreateMutable(kCFAllocatorDefault, 0), out); 758 require_noerr(SecCMSCreateEnvelopedData(recipient, parameters, pki_message_contents, enveloped_data), out); 759 760 /* remember transaction id just for reuse */ 761 simple_attr = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 3, signed_attrs); 762 763 /* message type: GetCertInitial (20) */ 764 CFDataRef msgtype_value_data = NULL; 765 CFDataRef msgtype_oid_data = NULL; 766 require(msgtype_oid_data = scep_oid(messageType), out); 767 require(msgtype_value_data = printable_string_data(sizeof(GetCertInitial) - 1, GetCertInitial), out); 768 CFDictionarySetValue(simple_attr, msgtype_oid_data, msgtype_value_data); 769 CFReleaseNull(msgtype_oid_data); 770 CFReleaseNull(msgtype_value_data); 771 772 /* random sender nonce, to be verified against recipient nonce in reply */ 773 generate_sender_nonce(simple_attr); 774 signed_request = CFDataCreateMutable(kCFAllocatorDefault, 0); 775 require_noerr_action(SecCMSCreateSignedData(signer, enveloped_data, 776 parameters, simple_attr, signed_request), out, CFReleaseNull(signed_request)); 777 778out: 779 CFReleaseSafe(simple_attr); 780 CFReleaseSafe(pki_message_contents); 781 CFReleaseSafe(enveloped_data); 782 return signed_request; 783} 784 785 786/* 787 +----------------+-----------------+---------------------------+ 788 | Attribute | Encoding | Comment | 789 +----------------+-----------------+---------------------------+ 790 | transactionID | PrintableString | Decimal value as a string | 791 | messageType | PrintableString | Decimal value as a string | 792 | pkiStatus | PrintableString | Decimal value as a string | 793 | failInfo | PrintableString | Decimal value as a string | 794 | senderNonce | OctetString | | 795 | recipientNonce | OctetString | | 796 +----------------+-----------------+---------------------------+ 797 7984.2.1. transactionID 799 800 The transactionID is an attribute which uniquely identifies a 801 transaction. This attribute is required in all PKI messages. 802 803 Because the enrollment transaction could be interrupted by various 804 errors, including network connection errors or client reboot, the 805 SCEP client generates a transaction identifier by calculating a hash 806 on the public key value for which the enrollment is requested. This 807 retains the same transaction identifier throughout the enrollment 808 transaction, even if the client has rebooted or timed out, and issues 809 a new enrollment request for the same key pair. 810 811 It also provides the way for the CA to uniquely identify a 812 transaction in its database. At the requester side, it generates a 813 transaction identifier which is included in PKCSReq. If the CA 814 returns a response of PENDING, the requester will poll by 815 periodically sending out GetCertInitial with the same transaction 816 identifier until either a response other than PENDING is obtained, or 817 the configured maximum time has elapsed. 818 819 For non-enrollment message (for example GetCert and GetCRL), the 820 transactionID should be a number unique to the client. 821 822 8234.2.2. messageType 824 825 The messageType attribute specify the type of operation performed by 826 the transaction. This attribute is required in all PKI messages. 827 Currently, the following message types are defined: 828 829 o PKCSReq (19) -- PKCS#10 [RFC2986] certificate request 830 831 o CertRep (3) -- Response to certificate or CRL request 832 833 o GetCertInitial (20) -- Certificate polling in manual enrollment 834 835 o GetCert (21) -- Retrieve a certificate 836 837 o GetCRL (22) -- Retrieve a CRL 838 8394.2.3. pkiStatus 840 841 All response message will include transaction status information 842 which is defined as pkiStatus attribute: 843 844 o SUCCESS (0) -- request granted 845 846 o FAILURE (2) -- request rejected. This also requires a failInfo 847 attribute to be present, as defined in section 4.2.4. 848 849 o PENDING (3) -- request pending for manual approval 850 851 8524.2.4. failInfo 853 854 The failInfo attribute will contain one of the following failure 855 reasons: 856 857 o badAlg (0) -- Unrecognized or unsupported algorithm ident 858 859 o badMessageCheck (1) -- integrity check failed 860 861 o badRequest (2) -- transaction not permitted or supported 862 863 o badTime (3) -- Message time field was not sufficiently close to 864 the system time 865 866 o badCertId (4) -- No certificate could be identified matching the 867 provided criteria 868 8694.2.5. senderNonce and responderNonce 870 871 The attributes of senderNonce and recipientNonce are the 16 byte 872 random numbers generated for each transaction to prevent the replay 873 attack. 874 875 When a requester sends a PKI message to the server, a senderNonce is 876 included in the message. After the server processes the request, it 877 will send back the requester senderNonce as the recipientNonce and 878 generates another nonce as the senderNonce in the response message. 879 Because the proposed PKI protocol is a two-way communication 880 protocol, it is clear that the nonce can only be used by the 881 requester to prevent the replay. The server has to employ extra 882 state related information to prevent a replay attack. 883 884*/ 885