2 * Copyright (c) 2000-2001,2005-2008,2010-2012 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 #include "sslContext.h"
26 #include "sslSession.h"
27 #include "sslMemory.h"
30 #include "sslCipherSpecs.h"
31 #include "appleSession.h"
36 #include <Security/SecCertificate.h>
37 #include <Security/SecCertificatePriv.h>
38 #include "utilities/SecCFRelease.h"
41 { size_t sessionIDLen
;
43 SSLProtocolVersion protocolVersion
;
45 UInt16 padding
; /* so remainder is word aligned */
46 UInt8 masterSecret
[48];
48 UInt8 certs
[1]; /* Actually, variable length */
52 * Cook up a (private) resumable session blob, based on the
53 * specified ctx, store it with ctx->peerID as the key.
54 * NOTE: This is contrary to the SSL v3 spec, which claims that
55 * servers store resumable sessions using ctx->sessionID as the key.
56 * I don' think this is an issue...is it?
59 SSLAddSessionData(const SSLContext
*ctx
)
63 ResumableSession
*session
;
65 #ifdef USE_SSLCERTIFICATE
73 /* If we don't know who the peer is, we can't store a session */
74 if (ctx
->peerID
.data
== 0)
75 return errSSLSessionNotFound
;
77 sessionIDLen
= offsetof(ResumableSession
, certs
);
78 #ifdef USE_SSLCERTIFICATE
83 sessionIDLen
+= 4 + cert
->derCert
.length
;
87 certChain
= ctx
->peerCert
;
88 certCount
= certChain
? CFArrayGetCount(certChain
) : 0;
89 for (ix
= 0; ix
< certCount
; ++ix
) {
90 SecCertificateRef cert
= (SecCertificateRef
)CFArrayGetValueAtIndex(certChain
, ix
);
92 sslDebugLog("SSLAddSessionData: got cert %d of %d\n", (int)ix
+1, (int)certCount
);
93 if (!cert
|| CFGetTypeID(cert
) != SecCertificateGetTypeID()) {
94 sslErrorLog("SSLAddSessionData: non-cert in peerCert array!\n");
97 sessionIDLen
+= 4 + (size_t)SecCertificateGetLength(cert
);
101 if ((err
= SSLAllocBuffer(&sessionID
, sessionIDLen
)))
104 session
= (ResumableSession
*)sessionID
.data
;
106 session
->sessionIDLen
= ctx
->sessionID
.length
;
107 memcpy(session
->sessionID
, ctx
->sessionID
.data
, session
->sessionIDLen
);
108 session
->protocolVersion
= ctx
->negProtocolVersion
;
109 session
->cipherSuite
= ctx
->selectedCipher
;
110 memcpy(session
->masterSecret
, ctx
->masterSecret
, 48);
111 session
->certCount
= certCount
;
112 session
->padding
= 0;
114 certDest
= session
->certs
;
116 #ifdef USE_SSLCERTIFICATE
117 cert
= ctx
->peerCert
;
119 { certDest
= SSLEncodeInt(certDest
, cert
->derCert
.length
, 4);
120 memcpy(certDest
, cert
->derCert
.data
, cert
->derCert
.length
);
121 certDest
+= cert
->derCert
.length
;
125 for (ix
= 0; ix
< certCount
; ++ix
) {
126 SecCertificateRef certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(certChain
, ix
);
127 size_t certLength
= (size_t)SecCertificateGetLength(certRef
);
128 const uint8_t *certBytes
= SecCertificateGetBytePtr(certRef
);
130 #if SSL_DEBUG && !TARGET_OS_IPHONE
131 /* print cert name when debugging; leave disabled otherwise */
132 CFStringRef certName
= NULL
;
133 OSStatus status
= SecCertificateInferLabel(certRef
, &certName
);
135 if (status
|| !certName
|| !CFStringGetCString(certName
, buf
, 1024-1, kCFStringEncodingUTF8
)) { buf
[0]=0; }
136 sslDebugLog("SSLAddSessionData: flattening \"%s\" (%ld bytes)\n", buf
, certLength
);
137 CFReleaseSafe(certName
);
140 if (!certBytes
|| !certLength
) {
141 sslErrorLog("SSLAddSessionData: invalid certificate at index %d of %d (length=%ld, data=%p)\n",
142 (int)ix
, (int)certCount
-1, certLength
, certBytes
);
143 err
= errSecParam
; /* if we have a bad cert, don't add session to cache */
146 certDest
= SSLEncodeSize(certDest
, certLength
, 4);
147 memcpy(certDest
, certBytes
, certLength
);
148 certDest
+= certLength
;
153 err
= sslAddSession(ctx
->peerID
, sessionID
, ctx
->sessionCacheTimeout
);
154 SSLFreeBuffer(&sessionID
);
160 * Retrieve resumable session data, from key ctx->peerID.
163 SSLGetSessionData(SSLBuffer
*sessionData
, const SSLContext
*ctx
)
166 if (ctx
->peerID
.data
== 0)
167 return errSSLSessionNotFound
;
169 sessionData
->data
= 0;
171 err
= sslGetSession(ctx
->peerID
, sessionData
);
172 if (sessionData
->data
== 0)
173 return errSSLSessionNotFound
;
179 SSLDeleteSessionData(const SSLContext
*ctx
)
182 if (ctx
->peerID
.data
== 0)
183 return errSSLSessionNotFound
;
185 err
= sslDeleteSession(ctx
->peerID
);
190 * Given a sessionData blob, obtain the associated sessionID (NOT the key...).
193 SSLRetrieveSessionID(
194 const SSLBuffer sessionData
,
195 SSLBuffer
*identifier
,
196 const SSLContext
*ctx
)
198 ResumableSession
*session
;
200 session
= (ResumableSession
*) sessionData
.data
;
201 if ((err
= SSLAllocBuffer(identifier
, session
->sessionIDLen
)))
203 memcpy(identifier
->data
, session
->sessionID
, session
->sessionIDLen
);
204 return errSecSuccess
;
208 * Obtain the protocol version associated with a specified resumable session blob.
211 SSLRetrieveSessionProtocolVersion(
212 const SSLBuffer sessionData
,
213 SSLProtocolVersion
*version
,
214 const SSLContext
*ctx
)
215 { ResumableSession
*session
;
217 session
= (ResumableSession
*) sessionData
.data
;
218 *version
= session
->protocolVersion
;
219 return errSecSuccess
;
223 * Retrieve session state from specified sessionData blob, install into
224 * ctx. Presumably, ctx->sessionID and
225 * ctx->negProtocolVersion are already init'd (from the above two functions).
229 * Netscape Enterprise Server is known to change cipherspecs upon session resumption.
230 * For example, connecting to cdnow.com with all ciphersuites enabled results in
231 * CipherSuite 4 (SSL_RSA_WITH_RC4_128_MD5) being selected on the first session,
232 * and CipherSuite 10 (SSL_RSA_WITH_3DES_EDE_CBC_SHA) being selected on subsequent
233 * sessions. This is contrary to the SSL3.0 spec, sesion 7.6.1.3, describing the
234 * Server Hello message.
236 * This anomaly does not occur if only RC4 ciphers are enabled in the Client Hello
237 * message. It also does not happen in SSL V2.
239 #define ALLOW_CIPHERSPEC_CHANGE 1
242 SSLInstallSessionFromData(const SSLBuffer sessionData
, SSLContext
*ctx
)
244 ResumableSession
*session
;
245 uint8_t *storedCertProgress
;
246 #ifdef USE_SSLCERTIFICATE
247 SSLCertificate
*cert
;
248 SSLCertificate
*lastCert
= NULL
;
250 SecCertificateRef cert
;
251 CFMutableArrayRef certChain
= NULL
;
256 session
= (ResumableSession
*)sessionData
.data
;
259 * For SSLv3 and TLSv1, we know that selectedCipher has already been specified in
260 * SSLProcessServerHello(). An SSLv2 server hello message with a session
261 * ID hit contains no CipherKind field so we set it here.
263 if(ctx
->negProtocolVersion
== SSL_Version_2_0
) {
264 if(ctx
->protocolSide
== kSSLClientSide
) {
265 assert(ctx
->selectedCipher
== 0);
266 ctx
->selectedCipher
= session
->cipherSuite
;
270 * Else...what if they don't match? Could never happen, right?
271 * Wouldn't that mean the client is trying to switch ciphers on us?
273 if(ctx
->selectedCipher
!= session
->cipherSuite
) {
274 sslErrorLog("+++SSL2: CipherSpec change from %d to %d on session "
276 session
->cipherSuite
, ctx
->selectedCipher
);
277 return errSSLProtocol
;
282 assert(ctx
->selectedCipher
!= 0);
283 if(ctx
->selectedCipher
!= session
->cipherSuite
) {
284 #if ALLOW_CIPHERSPEC_CHANGE
285 sslErrorLog("+++WARNING: CipherSpec change from %d to %d "
286 "on session resume\n",
287 session
->cipherSuite
, ctx
->selectedCipher
);
289 sslErrorLog("+++SSL: CipherSpec change from %d to %d on session resume\n",
290 session
->cipherSuite
, ctx
->selectedCipher
);
291 return errSSLProtocol
;
295 if ((err
= FindCipherSpec(ctx
)) != 0) {
298 memcpy(ctx
->masterSecret
, session
->masterSecret
, 48);
300 storedCertProgress
= session
->certs
;
301 certCount
= session
->certCount
;
305 #ifdef USE_SSLCERTIFICATE
306 cert
= (SSLCertificate
*)sslMalloc(sizeof(SSLCertificate
));
308 return errSecAllocate
;
311 certLen
= SSLDecodeInt(storedCertProgress
, 4);
312 storedCertProgress
+= 4;
313 if ((err
= SSLAllocBuffer(&cert
->derCert
, certLen
)
318 memcpy(cert
->derCert
.data
, storedCertProgress
, certLen
);
319 storedCertProgress
+= certLen
;
321 ctx
->peerCert
= cert
;
323 lastCert
->next
= cert
;
326 certLen
= SSLDecodeInt(storedCertProgress
, 4);
327 storedCertProgress
+= 4;
328 cert
= SecCertificateCreateWithBytes(NULL
, storedCertProgress
, certLen
);
330 sslDebugLog("SSLInstallSessionFromData: creating cert with bytes=%p len=%lu\n",
331 storedCertProgress
, certLen
);
332 if (!cert
|| CFGetTypeID(cert
) != SecCertificateGetTypeID()) {
333 sslErrorLog("SSLInstallSessionFromData: SecCertificateCreateWithBytes failed\n");
337 return errSecAllocate
;
339 storedCertProgress
+= certLen
;
340 /* @@@ This is almost the same code as in sslCert.c: SSLProcessCertificate() */
342 certChain
= CFArrayCreateMutable(kCFAllocatorDefault
,
343 session
->certCount
, &kCFTypeArrayCallBacks
);
346 return errSecAllocate
;
349 sslDebugLog("SSLInstallSessionFromData: releasing existing cert chain\n");
350 CFRelease(ctx
->peerCert
);
352 ctx
->peerCert
= certChain
;
355 CFArrayAppendValue(certChain
, cert
);
360 return errSecSuccess
;