]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
1 | /* |
2 | * Copyright (c) 2006-2014 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 | /* | |
25 | * sslCrypto.c - interface between SSL and crypto libraries | |
26 | */ | |
27 | ||
28 | #include "sslCrypto.h" | |
29 | #include "sslContext.h" | |
30 | #include "sslMemory.h" | |
31 | #include "sslUtils.h" | |
32 | #include "sslDebug.h" | |
33 | ||
34 | #include <string.h> | |
35 | #include <stdlib.h> | |
36 | #include <assert.h> | |
37 | ||
5c19dc3a | 38 | #include <Security/SecTrustPriv.h> |
d8f41ccd A |
39 | #include <Security/SecPolicy.h> |
40 | #include <Security/SecCertificate.h> | |
41 | ||
42 | #include <AssertMacros.h> | |
43 | #include "utilities/SecCFRelease.h" | |
44 | ||
45 | #if TARGET_OS_IPHONE | |
46 | #include <Security/SecRSAKey.h> | |
47 | #include <Security/SecECKey.h> | |
48 | #endif | |
49 | ||
50 | /* | |
51 | * Get algorithm id for a SSLPubKey object. | |
52 | */ | |
53 | CFIndex sslPubKeyGetAlgorithmID(SecKeyRef pubKey) | |
54 | { | |
55 | #if TARGET_OS_IPHONE | |
56 | return SecKeyGetAlgorithmID(pubKey); | |
57 | #else | |
58 | return SecKeyGetAlgorithmId(pubKey); | |
59 | #endif | |
60 | } | |
61 | ||
62 | /* | |
63 | * Get algorithm id for a SSLPrivKey object. | |
64 | */ | |
65 | CFIndex sslPrivKeyGetAlgorithmID(SecKeyRef privKey) | |
66 | { | |
67 | #if TARGET_OS_IPHONE | |
68 | return SecKeyGetAlgorithmID(privKey); | |
69 | #else | |
70 | return SecKeyGetAlgorithmId(privKey); | |
71 | #endif | |
72 | } | |
73 | ||
5c19dc3a A |
74 | static |
75 | OSStatus sslCreateCFArrayFromList(const tls_buffer_list_t *list, CFArrayRef *cfArray) | |
76 | { | |
77 | int err; | |
78 | CFMutableArrayRef array = NULL; | |
79 | CFDataRef data = NULL; | |
80 | ||
81 | err = errSSLInternal; | |
82 | ||
83 | array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); | |
84 | require(array, out); | |
85 | ||
86 | while(list) { | |
87 | require((data = CFDataCreate(kCFAllocatorDefault, list->buffer.data, list->buffer.length)), out); | |
88 | CFArrayAppendValue(array, data); | |
89 | CFReleaseNull(data); | |
90 | list=list->next; | |
91 | } | |
92 | ||
93 | *cfArray = array; | |
94 | return errSecSuccess; | |
95 | ||
96 | out: | |
97 | CFReleaseSafe(data); | |
98 | CFReleaseSafe(array); | |
99 | return err; | |
100 | } | |
d8f41ccd A |
101 | |
102 | OSStatus | |
103 | sslCreateSecTrust( | |
104 | SSLContext *ctx, | |
105 | CFArrayRef certChain, | |
d8f41ccd A |
106 | SecTrustRef *pTrust) /* RETURNED */ |
107 | { | |
108 | OSStatus status = errSecAllocate; | |
109 | CFStringRef peerDomainName = NULL; | |
110 | CFTypeRef policies = NULL; | |
111 | SecTrustRef trust = NULL; | |
112 | const char *peerDomainNameData = NULL; | |
113 | size_t peerDomainNameLen = 0; | |
114 | ||
115 | if(ctx->protocolSide==kSSLClientSide) { | |
116 | tls_handshake_get_peer_hostname(ctx->hdsk, &peerDomainNameData, &peerDomainNameLen); | |
117 | } | |
118 | ||
119 | if (CFArrayGetCount(certChain) == 0) { | |
120 | status = errSSLBadCert; | |
121 | goto errOut; | |
122 | } | |
123 | ||
5c19dc3a A |
124 | if (peerDomainNameLen && peerDomainNameData) { |
125 | CFIndex len = peerDomainNameLen; | |
126 | if (peerDomainNameData[len - 1] == 0) { | |
127 | len--; | |
128 | //secwarning("peerDomainName is zero terminated!"); | |
129 | } | |
130 | /* @@@ Double check that this is the correct encoding. */ | |
131 | require(peerDomainName = CFStringCreateWithBytes(kCFAllocatorDefault, | |
132 | (const UInt8 *)peerDomainNameData, len, | |
133 | kCFStringEncodingUTF8, false), errOut); | |
134 | } | |
135 | ||
d8f41ccd A |
136 | /* If we are the client, our peer certificates must satisfy the |
137 | ssl server policy. */ | |
5c19dc3a A |
138 | bool use_server_policy = (ctx->protocolSide == kSSLClientSide); |
139 | require(policies = SecPolicyCreateSSL(use_server_policy, peerDomainName), errOut); | |
d8f41ccd A |
140 | |
141 | require_noerr(status = SecTrustCreateWithCertificates(certChain, policies, | |
142 | &trust), errOut); | |
143 | ||
5c19dc3a A |
144 | /* If we are the client, let's see if we have OCSP responses and SCTs in the TLS handshake */ |
145 | if(ctx->protocolSide == kSSLClientSide) { | |
146 | const tls_buffer_list_t *sct_list = tls_handshake_get_peer_sct_list(ctx->hdsk); | |
147 | const tls_buffer *ocsp_response = tls_handshake_get_peer_ocsp_response(ctx->hdsk); | |
148 | ||
149 | if(ocsp_response) { | |
150 | CFDataRef responseData = CFDataCreate(kCFAllocatorDefault, ocsp_response->data, ocsp_response->length); | |
151 | status = SecTrustSetOCSPResponse(trust, responseData); | |
152 | CFReleaseSafe(responseData); | |
153 | require_noerr(status, errOut); | |
154 | } | |
155 | ||
156 | if(sct_list) { | |
157 | CFArrayRef sctArray = NULL; | |
158 | require_noerr(status = sslCreateCFArrayFromList(sct_list, &sctArray), errOut); | |
159 | #if TARGET_OS_IPHONE | |
160 | status = SecTrustSetSignedCertificateTimestamps(trust, sctArray); | |
161 | #else | |
162 | status = noErr; | |
163 | #endif | |
164 | CFReleaseSafe(sctArray); | |
165 | require_noerr(status, errOut); | |
166 | } | |
167 | } | |
168 | ||
d8f41ccd A |
169 | /* If we have trustedAnchors we set them here. */ |
170 | if (ctx->trustedCerts) { | |
171 | require_noerr(status = SecTrustSetAnchorCertificates(trust, | |
172 | ctx->trustedCerts), errOut); | |
173 | require_noerr(status = SecTrustSetAnchorCertificatesOnly(trust, | |
174 | ctx->trustedCertsOnly), errOut); | |
175 | } | |
176 | ||
177 | status = errSecSuccess; | |
178 | ||
179 | errOut: | |
180 | CFReleaseSafe(peerDomainName); | |
181 | CFReleaseSafe(policies); | |
182 | ||
183 | *pTrust = trust; | |
184 | ||
185 | return status; | |
186 | } | |
187 | ||
188 | /* Return the first certificate reference from the supplied array | |
189 | * whose data matches the given certificate, or NULL if none match. | |
190 | */ | |
191 | static | |
192 | SecCertificateRef | |
193 | sslGetMatchingCertInArray( | |
194 | SecCertificateRef certRef, | |
195 | CFArrayRef certArray) | |
196 | { | |
197 | SecCertificateRef matchedCert = NULL; | |
198 | ||
199 | if (certRef == NULL || certArray == NULL) { | |
200 | return NULL; | |
201 | } | |
202 | ||
203 | CFDataRef certData = SecCertificateCopyData(certRef); | |
204 | if (certData) { | |
205 | CFIndex idx, count = CFArrayGetCount(certArray); | |
206 | for(idx=0; idx<count; idx++) { | |
207 | SecCertificateRef aCert = (SecCertificateRef)CFArrayGetValueAtIndex(certArray, idx); | |
208 | CFDataRef aData = SecCertificateCopyData(aCert); | |
209 | if (aData && CFEqual(aData, certData)) { | |
210 | matchedCert = aCert; | |
211 | } | |
212 | CFReleaseSafe(aData); | |
213 | if (matchedCert) | |
214 | break; | |
215 | } | |
216 | CFReleaseSafe(certData); | |
217 | } | |
218 | ||
219 | return matchedCert; | |
220 | } | |
221 | ||
222 | /* | |
223 | * Verify a chain of DER-encoded certs. | |
d8f41ccd A |
224 | */ |
225 | static OSStatus sslVerifyCertChain( | |
226 | SSLContext *ctx, | |
5c19dc3a | 227 | CFArrayRef certChain) |
d8f41ccd A |
228 | { |
229 | OSStatus status; | |
230 | SecTrustRef trust = NULL; | |
231 | ||
5c19dc3a A |
232 | /* renegotiate - start with a new SecTrustRef */ |
233 | CFReleaseNull(ctx->peerSecTrust); | |
d8f41ccd | 234 | |
d87e1158 | 235 | if(certChain==NULL) { |
5c19dc3a A |
236 | if(ctx->protocolSide == kSSLClientSide) { |
237 | /* No cert chain is always a trust failure on the server side */ | |
238 | status = errSSLXCertChainInvalid; | |
239 | sslErrorLog("***Error: NULL server cert chain\n"); | |
240 | } else { | |
241 | /* No cert chain on the client side is ok unless using kAlwaysAuthenticate */ | |
242 | if(ctx->clientAuth == kAlwaysAuthenticate) { | |
243 | sslErrorLog("***Error: NULL client cert chain\n"); | |
244 | status = errSSLXCertChainInvalid; | |
245 | } else { | |
246 | status = noErr; | |
247 | } | |
248 | } | |
d87e1158 A |
249 | goto errOut; |
250 | } | |
251 | ||
5c19dc3a | 252 | status = sslCreateSecTrust(ctx, certChain, &trust); |
d8f41ccd A |
253 | |
254 | if (!ctx->enableCertVerify) { | |
255 | /* trivial case, this is caller's responsibility */ | |
256 | status = errSecSuccess; | |
257 | goto errOut; | |
258 | } | |
259 | ||
260 | SecTrustResultType secTrustResult; | |
261 | require_noerr(status = SecTrustEvaluate(trust, &secTrustResult), errOut); | |
262 | switch (secTrustResult) { | |
263 | case kSecTrustResultUnspecified: | |
264 | /* cert chain valid, no special UserTrust assignments */ | |
265 | case kSecTrustResultProceed: | |
266 | /* cert chain valid AND user explicitly trusts this */ | |
267 | status = errSecSuccess; | |
268 | break; | |
269 | case kSecTrustResultDeny: | |
270 | case kSecTrustResultConfirm: | |
271 | case kSecTrustResultRecoverableTrustFailure: | |
272 | default: | |
273 | if(ctx->allowAnyRoot) { | |
274 | sslErrorLog("***Warning: accepting unverified cert chain\n"); | |
275 | status = errSecSuccess; | |
276 | } | |
277 | else { | |
278 | /* | |
279 | * If the caller provided a list of trusted leaf certs, check them here | |
280 | */ | |
281 | if(ctx->trustedLeafCerts) { | |
282 | if (sslGetMatchingCertInArray((SecCertificateRef)CFArrayGetValueAtIndex(certChain, 0), | |
283 | ctx->trustedLeafCerts)) { | |
284 | status = errSecSuccess; | |
285 | goto errOut; | |
286 | } | |
287 | } | |
288 | status = errSSLXCertChainInvalid; | |
289 | } | |
290 | /* Do we really need to return things like: | |
291 | errSSLNoRootCert | |
292 | errSSLUnknownRootCert | |
293 | errSSLCertExpired | |
294 | errSSLCertNotYetValid | |
295 | errSSLHostNameMismatch | |
296 | for our client to see what went wrong, or should we just always | |
297 | return | |
298 | errSSLXCertChainInvalid | |
299 | when something is wrong? */ | |
300 | break; | |
301 | } | |
302 | ||
303 | errOut: | |
5c19dc3a | 304 | ctx->peerSecTrust = trust; |
d8f41ccd A |
305 | |
306 | return status; | |
307 | } | |
308 | ||
309 | /* Extract public SecKeyRef from Certificate Chain */ | |
310 | static | |
311 | int sslCopyPeerPubKey(const SSLCertificate *certchain, | |
312 | SecKeyRef *pubKey) | |
313 | { | |
314 | int err; | |
315 | check(pubKey); | |
316 | SecTrustRef trust = NULL; | |
317 | const SSLCertificate *cert; | |
318 | CFMutableArrayRef certArray = NULL; | |
319 | CFDataRef certData = NULL; | |
320 | SecCertificateRef cfCert = NULL; | |
321 | ||
322 | err = errSSLInternal; | |
323 | ||
324 | certArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); | |
325 | cert = certchain; | |
326 | while(cert) { | |
327 | require((certData = CFDataCreate(kCFAllocatorDefault, cert->derCert.data, cert->derCert.length)), out); | |
5c19dc3a | 328 | require_action((cfCert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)), out, err=errSSLBadCert); |
d8f41ccd A |
329 | CFArrayAppendValue(certArray, cfCert); |
330 | CFReleaseNull(cfCert); | |
331 | CFReleaseNull(certData); | |
332 | cert=cert->next; | |
333 | } | |
334 | ||
335 | require_noerr((err=SecTrustCreateWithCertificates(certArray, NULL, &trust)), out); | |
336 | SecKeyRef key = SecTrustCopyPublicKey(trust); | |
5c19dc3a | 337 | require_action(key, out, err=errSSLBadCert); |
d8f41ccd A |
338 | |
339 | *pubKey = key; | |
340 | ||
341 | err = errSecSuccess; | |
342 | ||
343 | out: | |
344 | CFReleaseSafe(certData); | |
345 | CFReleaseSafe(cfCert); | |
346 | CFReleaseSafe(trust); | |
347 | CFReleaseSafe(certArray); | |
348 | ||
349 | return err; | |
350 | } | |
351 | ||
352 | /* Extract the pubkey from a cert chain, and send it to the tls_handshake context */ | |
5c19dc3a | 353 | int tls_set_peer_pubkey(SSLContext *ctx) |
d8f41ccd A |
354 | { |
355 | int err; | |
356 | CFIndex algId; | |
357 | SecKeyRef pubkey = NULL; | |
358 | CFDataRef modulus = NULL; | |
359 | CFDataRef exponent = NULL; | |
360 | CFDataRef ecpubdata = NULL; | |
5c19dc3a A |
361 | const SSLCertificate *certchain = NULL; |
362 | ||
363 | certchain = tls_handshake_get_peer_certificates(ctx->hdsk); | |
364 | CFReleaseNull(ctx->peerCert); | |
365 | ||
366 | /* If there is no certchain, then we don't need to set the pubkey in coreTLS */ | |
367 | /* We should really set it to "NULL" or none, but we need to fix the coreTLS API */ | |
368 | /* See: <rdar://problem/19723662> coreTLS: replace tls_handshake_set_peer_rsa_public_key and tls_handshake_set_peer_ec_public_key with a common function */ | |
369 | if(!certchain) | |
370 | return 0; | |
371 | ||
372 | ctx->peerCert = tls_get_peer_certs(certchain); | |
d8f41ccd A |
373 | |
374 | #if 0 | |
375 | { /* dump certs */ | |
376 | int i=0; | |
377 | int j; | |
378 | const SSLCertificate *tmp = certchain; | |
379 | while(tmp) { | |
380 | printf("cert%d[] = {", i); | |
381 | for(j=0; j<tmp->derCert.length; j++) { | |
382 | if((j&0xf)==0) | |
383 | printf("\n"); | |
384 | printf("0x%02x, ", tmp->derCert.data[j]); | |
385 | } | |
386 | printf("}\n"); | |
387 | tmp=tmp->next; | |
388 | i++; | |
389 | } | |
390 | } | |
391 | #endif | |
392 | ||
393 | require_noerr((err=sslCopyPeerPubKey(certchain, &pubkey)), errOut); | |
394 | ||
395 | #if TARGET_OS_IPHONE | |
396 | algId = SecKeyGetAlgorithmID(pubkey); | |
397 | #else | |
398 | algId = SecKeyGetAlgorithmId(pubkey); | |
399 | #endif | |
400 | ||
401 | err = errSSLCrypto; | |
402 | ||
403 | switch(algId) { | |
404 | case kSecRSAAlgorithmID: | |
405 | { | |
406 | require((modulus = SecKeyCopyModulus(pubkey)), errOut); | |
407 | require((exponent = SecKeyCopyExponent(pubkey)), errOut); | |
408 | ||
409 | tls_buffer mod; | |
410 | tls_buffer exp; | |
411 | ||
412 | mod.data = (uint8_t *)CFDataGetBytePtr(modulus); | |
413 | mod.length = CFDataGetLength(modulus); | |
414 | ||
415 | exp.data = (uint8_t *)CFDataGetBytePtr(exponent); | |
416 | exp.length = CFDataGetLength(exponent); | |
417 | ||
5c19dc3a | 418 | err = tls_handshake_set_peer_rsa_public_key(ctx->hdsk, &mod, &exp); |
d8f41ccd A |
419 | break; |
420 | } | |
421 | case kSecECDSAAlgorithmID: | |
422 | { | |
423 | tls_named_curve curve = SecECKeyGetNamedCurve(pubkey); | |
424 | require((ecpubdata = SecECKeyCopyPublicBits(pubkey)), errOut); | |
425 | ||
426 | tls_buffer pubdata; | |
427 | pubdata.data = (uint8_t *)CFDataGetBytePtr(ecpubdata); | |
428 | pubdata.length = CFDataGetLength(ecpubdata); | |
429 | ||
5c19dc3a | 430 | err = tls_handshake_set_peer_ec_public_key(ctx->hdsk, curve, &pubdata); |
d8f41ccd A |
431 | |
432 | break; | |
433 | } | |
434 | default: | |
435 | break; | |
436 | } | |
437 | ||
438 | errOut: | |
439 | CFReleaseSafe(pubkey); | |
440 | CFReleaseSafe(modulus); | |
441 | CFReleaseSafe(exponent); | |
442 | CFReleaseSafe(ecpubdata); | |
443 | ||
444 | return err; | |
445 | } | |
446 | ||
447 | /* Convert cert in DER format into an CFArray of SecCertificateRef */ | |
448 | CFArrayRef | |
449 | tls_get_peer_certs(const SSLCertificate *certs) | |
450 | { | |
451 | const SSLCertificate *cert; | |
452 | ||
453 | CFMutableArrayRef certArray = NULL; | |
454 | CFDataRef certData = NULL; | |
455 | SecCertificateRef cfCert = NULL; | |
456 | ||
457 | certArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); | |
458 | require(certArray, out); | |
459 | cert = certs; | |
460 | while(cert) { | |
461 | require((certData = CFDataCreate(kCFAllocatorDefault, cert->derCert.data, cert->derCert.length)), out); | |
462 | require((cfCert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)), out); | |
463 | CFArrayAppendValue(certArray, cfCert); | |
464 | CFReleaseNull(cfCert); | |
465 | CFReleaseNull(certData); | |
466 | cert=cert->next; | |
467 | } | |
468 | ||
469 | return certArray; | |
470 | ||
471 | out: | |
472 | CFReleaseNull(cfCert); | |
473 | CFReleaseNull(certData); | |
474 | CFReleaseNull(certArray); | |
475 | return NULL; | |
476 | } | |
477 | ||
478 | int | |
479 | tls_verify_peer_cert(SSLContext *ctx) | |
480 | { | |
5c19dc3a A |
481 | int err = 0; |
482 | OSStatus st; | |
d8f41ccd | 483 | |
5c19dc3a A |
484 | /* Note: A verification failure here does not cause the function to return an error. |
485 | This will allow the handshake to continue, coreTLS will eventually returns an error, | |
486 | after sending the appropriate alert messages, based on the trust value set with the | |
487 | call to tls_handshake_set_peer_trust(). In some case a verification failure here is | |
488 | normal, for example if there is no cert (eg: PSK and Anon DH ciphersuites) */ | |
d8f41ccd | 489 | |
5c19dc3a | 490 | st = sslVerifyCertChain(ctx, ctx->peerCert); |
d8f41ccd | 491 | tls_handshake_trust_t trust; |
5c19dc3a | 492 | switch (st) { |
d8f41ccd A |
493 | case errSecSuccess: |
494 | trust = tls_handshake_trust_ok; | |
495 | break; | |
496 | case errSSLUnknownRootCert: | |
497 | case errSSLNoRootCert: | |
498 | trust = tls_handshake_trust_unknown_root; | |
499 | break; | |
500 | case errSSLCertExpired: | |
501 | case errSSLCertNotYetValid: | |
502 | trust = tls_handshake_trust_cert_expired; | |
503 | break; | |
504 | case errSSLXCertChainInvalid: | |
505 | default: | |
506 | trust = tls_handshake_trust_cert_invalid; | |
507 | break; | |
508 | } | |
509 | ||
510 | tls_handshake_set_peer_trust(ctx->hdsk, trust); | |
511 | ||
5c19dc3a A |
512 | /* Now that trust has been (possibly) evaluated, |
513 | we check if we need to break out of the handshake */ | |
d8f41ccd A |
514 | if(ctx->protocolSide == kSSLServerSide) { |
515 | /* | |
516 | * Schedule return to the caller to verify the client's identity. | |
5c19dc3a | 517 | * This will return even if there was no client cert sent. |
d8f41ccd A |
518 | */ |
519 | if (ctx->breakOnClientAuth) { | |
520 | err = errSSLClientAuthCompleted; | |
521 | } | |
5c19dc3a | 522 | } else if(ctx->peerCert) { |
d8f41ccd A |
523 | /* |
524 | * Schedule return to the caller to verify the server's identity. | |
5c19dc3a A |
525 | * This will only return if a server cert was sent. In other cases |
526 | * such as PSK and AnonDH, we don't want to break out of the handshake. | |
d8f41ccd A |
527 | */ |
528 | if (ctx->breakOnServerAuth) { | |
529 | err = errSSLServerAuthCompleted; | |
530 | } | |
531 | } | |
532 | ||
d8f41ccd A |
533 | return err; |
534 | } | |
535 | ||
536 | /* | |
537 | * After ciphersuite negotiation is complete, verify that we have | |
538 | * the capability of actually performing the selected cipher. | |
539 | * Currently we just verify that we have a cert and private signing | |
540 | * key, if needed, and that the signing key's algorithm matches the | |
541 | * expected key exchange method. | |
542 | * | |
543 | * This is currently called from FindCipherSpec(), after it sets | |
544 | * ctx->selectedCipherSpec to a (supposedly) valid value, and from | |
545 | * sslBuildCipherSpecArray(), in server mode (pre-negotiation) only. | |
546 | */ | |
547 | ||
548 | #if 0 | |
549 | OSStatus sslVerifySelectedCipher(SSLContext *ctx) | |
550 | { | |
551 | ||
552 | if(ctx->protocolSide == kSSLClientSide) { | |
553 | return errSecSuccess; | |
554 | } | |
555 | #if SSL_PAC_SERVER_ENABLE | |
556 | if((ctx->masterSecretCallback != NULL) && | |
557 | (ctx->sessionTicket.data != NULL)) { | |
558 | /* EAP via PAC resumption; we can do it */ | |
559 | return errSecSuccess; | |
560 | } | |
561 | #endif /* SSL_PAC_SERVER_ENABLE */ | |
562 | ||
563 | CFIndex requireAlg; | |
564 | switch (ctx->selectedCipherSpecParams.keyExchangeMethod) { | |
565 | case SSL_RSA: | |
566 | case SSL_RSA_EXPORT: | |
567 | case SSL_DH_RSA: | |
568 | case SSL_DH_RSA_EXPORT: | |
569 | case SSL_DHE_RSA: | |
570 | case SSL_DHE_RSA_EXPORT: | |
571 | requireAlg = kSecRSAAlgorithmID; | |
572 | break; | |
573 | case SSL_DHE_DSS: | |
574 | case SSL_DHE_DSS_EXPORT: | |
575 | case SSL_DH_DSS: | |
576 | case SSL_DH_DSS_EXPORT: | |
577 | requireAlg = kSecDSAAlgorithmID; | |
578 | break; | |
579 | case SSL_DH_anon: | |
580 | case SSL_DH_anon_EXPORT: | |
581 | case TLS_PSK: | |
582 | requireAlg = kSecNullAlgorithmID; /* no signing key */ | |
583 | break; | |
584 | /* | |
585 | * When SSL_ECDSA_SERVER is true and we support ECDSA on the server side, | |
586 | * we'll need to add some logic here... | |
587 | */ | |
588 | #if SSL_ECDSA_SERVER | |
589 | case SSL_ECDHE_ECDSA: | |
590 | case SSL_ECDHE_RSA: | |
591 | case SSL_ECDH_ECDSA: | |
592 | case SSL_ECDH_RSA: | |
593 | case SSL_ECDH_anon: | |
594 | requireAlg = kSecECDSAAlgorithmID; | |
595 | break; | |
596 | #endif | |
597 | ||
598 | default: | |
599 | /* needs update per cipherSpecs.c */ | |
600 | assert(0); | |
601 | sslErrorLog("sslVerifySelectedCipher: unknown key exchange method\n"); | |
602 | return errSSLInternal; | |
603 | } | |
604 | ||
605 | if(requireAlg == kSecNullAlgorithmID) { | |
606 | return errSecSuccess; | |
607 | } | |
608 | ||
609 | /* private signing key required */ | |
610 | if(ctx->signingPrivKeyRef == NULL) { | |
611 | sslErrorLog("sslVerifySelectedCipher: no signing key\n"); | |
612 | return errSSLBadConfiguration; | |
613 | } | |
614 | ||
615 | /* Check the alg of our signing key. */ | |
616 | CFIndex keyAlg = sslPrivKeyGetAlgorithmID(ctx->signingPrivKeyRef); | |
617 | if (requireAlg != keyAlg) { | |
618 | sslErrorLog("sslVerifySelectedCipher: signing key alg mismatch\n"); | |
619 | return errSSLBadConfiguration; | |
620 | } | |
621 | ||
622 | return errSecSuccess; | |
623 | } | |
624 | ||
625 | #endif |