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 "cipherSpecs.h"
31 #include "appleSession.h"
36 #include <Security/SecCertificate.h>
37 #include <Security/SecCertificatePriv.h>
38 #include <Security/SecInternal.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", ix
+1, 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
, ctx
)) != 0)
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
);
131 /* print cert name when debugging; leave disabled otherwise */
132 CFStringRef certName
= NULL
;
133 OSStatus status
= SecCertificateInferLabel(certRef
, &certName
);
135 if (!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 ix
, certCount
-1, certLength
, (const uintptr_t)certBytes
);
143 err
= paramErr
; /* 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
;
154 err
= sslAddSession(ctx
->peerID
, sessionID
, ctx
->sessionCacheTimeout
);
156 SSLFreeBuffer(&sessionID
, ctx
);
162 * Retrieve resumable session data, from key ctx->peerID.
165 SSLGetSessionData(SSLBuffer
*sessionData
, const SSLContext
*ctx
)
168 if (ctx
->peerID
.data
== 0)
169 return errSSLSessionNotFound
;
171 sessionData
->data
= 0;
173 err
= sslGetSession(ctx
->peerID
, sessionData
);
174 if (sessionData
->data
== 0)
175 return errSSLSessionNotFound
;
181 SSLDeleteSessionData(const SSLContext
*ctx
)
184 if (ctx
->peerID
.data
== 0)
185 return errSSLSessionNotFound
;
187 err
= sslDeleteSession(ctx
->peerID
);
192 * Given a sessionData blob, obtain the associated sessionID (NOT the key...).
195 SSLRetrieveSessionID(
196 const SSLBuffer sessionData
,
197 SSLBuffer
*identifier
,
198 const SSLContext
*ctx
)
200 ResumableSession
*session
;
202 session
= (ResumableSession
*) sessionData
.data
;
203 if ((err
= SSLAllocBuffer(identifier
, session
->sessionIDLen
, ctx
)) != 0)
205 memcpy(identifier
->data
, session
->sessionID
, session
->sessionIDLen
);
210 * Obtain the protocol version associated with a specified resumable session blob.
213 SSLRetrieveSessionProtocolVersion(
214 const SSLBuffer sessionData
,
215 SSLProtocolVersion
*version
,
216 const SSLContext
*ctx
)
217 { ResumableSession
*session
;
219 session
= (ResumableSession
*) sessionData
.data
;
220 *version
= session
->protocolVersion
;
225 * Retrieve session state from specified sessionData blob, install into
226 * ctx. Presumably, ctx->sessionID and
227 * ctx->negProtocolVersion are already init'd (from the above two functions).
231 * Netscape Enterprise Server is known to change cipherspecs upon session resumption.
232 * For example, connecting to cdnow.com with all ciphersuites enabled results in
233 * CipherSuite 4 (SSL_RSA_WITH_RC4_128_MD5) being selected on the first session,
234 * and CipherSuite 10 (SSL_RSA_WITH_3DES_EDE_CBC_SHA) being selected on subsequent
235 * sessions. This is contrary to the SSL3.0 spec, sesion 7.6.1.3, describing the
236 * Server Hello message.
238 * This anomaly does not occur if only RC4 ciphers are enabled in the Client Hello
239 * message. It also does not happen in SSL V2.
241 #define ALLOW_CIPHERSPEC_CHANGE 1
244 SSLInstallSessionFromData(const SSLBuffer sessionData
, SSLContext
*ctx
)
246 ResumableSession
*session
;
247 uint8_t *storedCertProgress
;
248 #ifdef USE_SSLCERTIFICATE
249 SSLCertificate
*cert
;
250 SSLCertificate
*lastCert
= NULL
;
252 SecCertificateRef cert
;
253 CFMutableArrayRef certChain
= NULL
;
258 session
= (ResumableSession
*)sessionData
.data
;
261 * For SSLv3 and TLSv1, we know that selectedCipher has already been specified in
262 * SSLProcessServerHello(). An SSLv2 server hello message with a session
263 * ID hit contains no CipherKind field so we set it here.
265 if(ctx
->negProtocolVersion
== SSL_Version_2_0
) {
266 if(ctx
->protocolSide
== kSSLClientSide
) {
267 assert(ctx
->selectedCipher
== 0);
268 ctx
->selectedCipher
= session
->cipherSuite
;
272 * Else...what if they don't match? Could never happen, right?
273 * Wouldn't that mean the client is trying to switch ciphers on us?
275 if(ctx
->selectedCipher
!= session
->cipherSuite
) {
276 sslErrorLog("+++SSL2: CipherSpec change from %d to %d on session "
278 session
->cipherSuite
, ctx
->selectedCipher
);
279 return errSSLProtocol
;
284 assert(ctx
->selectedCipher
!= 0);
285 if(ctx
->selectedCipher
!= session
->cipherSuite
) {
286 #if ALLOW_CIPHERSPEC_CHANGE
287 sslErrorLog("+++WARNING: CipherSpec change from %d to %d "
288 "on session resume\n",
289 session
->cipherSuite
, ctx
->selectedCipher
);
291 sslErrorLog("+++SSL: CipherSpec change from %d to %d on session resume\n",
292 session
->cipherSuite
, ctx
->selectedCipher
);
293 return errSSLProtocol
;
297 if ((err
= FindCipherSpec(ctx
)) != 0) {
300 memcpy(ctx
->masterSecret
, session
->masterSecret
, 48);
302 storedCertProgress
= session
->certs
;
303 certCount
= session
->certCount
;
307 #ifdef USE_SSLCERTIFICATE
308 cert
= (SSLCertificate
*)sslMalloc(sizeof(SSLCertificate
));
313 certLen
= SSLDecodeInt(storedCertProgress
, 4);
314 storedCertProgress
+= 4;
315 if ((err
= SSLAllocBuffer(&cert
->derCert
, certLen
, ctx
)) != 0)
320 memcpy(cert
->derCert
.data
, storedCertProgress
, certLen
);
321 storedCertProgress
+= certLen
;
323 ctx
->peerCert
= cert
;
325 lastCert
->next
= cert
;
328 certLen
= SSLDecodeInt(storedCertProgress
, 4);
329 storedCertProgress
+= 4;
330 cert
= SecCertificateCreateWithBytes(NULL
, storedCertProgress
, certLen
);
332 sslDebugLog("SSLInstallSessionFromData: creating cert with bytes=%p len=%lu\n",
333 (uintptr_t)storedCertProgress
, certLen
);
334 if (!cert
|| CFGetTypeID(cert
) != SecCertificateGetTypeID()) {
335 sslErrorLog("SSLInstallSessionFromData: SecCertificateCreateWithBytes failed\n");
341 storedCertProgress
+= certLen
;
342 /* @@@ This is almost the same code as in sslCert.c: SSLProcessCertificate() */
344 certChain
= CFArrayCreateMutable(kCFAllocatorDefault
,
345 session
->certCount
, &kCFTypeArrayCallBacks
);
350 ctx
->peerCert
= certChain
;
353 CFArrayAppendValue(certChain
, cert
);