]> git.saurik.com Git - apple/security.git/blob - libsecurity_ssl/lib/sslSession.c
Security-55471.14.tar.gz
[apple/security.git] / libsecurity_ssl / lib / sslSession.c
1 /*
2 * Copyright (c) 2000-2001,2005-2008,2010-2012 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 "ssl.h"
25 #include "sslContext.h"
26 #include "sslSession.h"
27 #include "sslMemory.h"
28 #include "sslUtils.h"
29 #include "sslDebug.h"
30 #include "sslCipherSpecs.h"
31 #include "appleSession.h"
32
33 #include <assert.h>
34 #include <string.h>
35 #include <stddef.h>
36 #include <Security/SecCertificate.h>
37 #include <Security/SecCertificatePriv.h>
38 #include "utilities/SecCFRelease.h"
39
40 typedef struct
41 { size_t sessionIDLen;
42 UInt8 sessionID[32];
43 SSLProtocolVersion protocolVersion;
44 UInt16 cipherSuite;
45 UInt16 padding; /* so remainder is word aligned */
46 UInt8 masterSecret[48];
47 size_t certCount;
48 UInt8 certs[1]; /* Actually, variable length */
49 } ResumableSession;
50
51 /*
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?
57 */
58 OSStatus
59 SSLAddSessionData(const SSLContext *ctx)
60 { OSStatus err;
61 size_t sessionIDLen;
62 SSLBuffer sessionID;
63 ResumableSession *session;
64 size_t certCount;
65 #ifdef USE_SSLCERTIFICATE
66 SSLCertificate *cert;
67 #else
68 CFArrayRef certChain;
69 size_t ix;
70 #endif
71 uint8_t *certDest;
72
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;
76
77 sessionIDLen = offsetof(ResumableSession, certs);
78 #ifdef USE_SSLCERTIFICATE
79 cert = ctx->peerCert;
80 certCount = 0;
81 while (cert)
82 { ++certCount;
83 sessionIDLen += 4 + cert->derCert.length;
84 cert = cert->next;
85 }
86 #else
87 certChain = ctx->peerCert;
88 certCount = certChain ? CFArrayGetCount(certChain) : 0;
89 for (ix = 0; ix < certCount; ++ix) {
90 SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, ix);
91 #if SSL_DEBUG
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");
95 }
96 #endif
97 sessionIDLen += 4 + (size_t)SecCertificateGetLength(cert);
98 }
99 #endif
100
101 if ((err = SSLAllocBuffer(&sessionID, sessionIDLen)))
102 return err;
103
104 session = (ResumableSession*)sessionID.data;
105
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;
113
114 certDest = session->certs;
115
116 #ifdef USE_SSLCERTIFICATE
117 cert = ctx->peerCert;
118 while (cert)
119 { certDest = SSLEncodeInt(certDest, cert->derCert.length, 4);
120 memcpy(certDest, cert->derCert.data, cert->derCert.length);
121 certDest += cert->derCert.length;
122 cert = cert->next;
123 }
124 #else
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);
129
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);
134 char buf[1024];
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);
138 #endif
139
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 */
144 }
145 else {
146 certDest = SSLEncodeSize(certDest, certLength, 4);
147 memcpy(certDest, certBytes, certLength);
148 certDest += certLength;
149 }
150 }
151 #endif
152
153 err = sslAddSession(ctx->peerID, sessionID, ctx->sessionCacheTimeout);
154 SSLFreeBuffer(&sessionID);
155
156 return err;
157 }
158
159 /*
160 * Retrieve resumable session data, from key ctx->peerID.
161 */
162 OSStatus
163 SSLGetSessionData(SSLBuffer *sessionData, const SSLContext *ctx)
164 { OSStatus err;
165
166 if (ctx->peerID.data == 0)
167 return errSSLSessionNotFound;
168
169 sessionData->data = 0;
170
171 err = sslGetSession(ctx->peerID, sessionData);
172 if (sessionData->data == 0)
173 return errSSLSessionNotFound;
174
175 return err;
176 }
177
178 OSStatus
179 SSLDeleteSessionData(const SSLContext *ctx)
180 { OSStatus err;
181
182 if (ctx->peerID.data == 0)
183 return errSSLSessionNotFound;
184
185 err = sslDeleteSession(ctx->peerID);
186 return err;
187 }
188
189 /*
190 * Given a sessionData blob, obtain the associated sessionID (NOT the key...).
191 */
192 OSStatus
193 SSLRetrieveSessionID(
194 const SSLBuffer sessionData,
195 SSLBuffer *identifier,
196 const SSLContext *ctx)
197 { OSStatus err;
198 ResumableSession *session;
199
200 session = (ResumableSession*) sessionData.data;
201 if ((err = SSLAllocBuffer(identifier, session->sessionIDLen)))
202 return err;
203 memcpy(identifier->data, session->sessionID, session->sessionIDLen);
204 return errSecSuccess;
205 }
206
207 /*
208 * Obtain the protocol version associated with a specified resumable session blob.
209 */
210 OSStatus
211 SSLRetrieveSessionProtocolVersion(
212 const SSLBuffer sessionData,
213 SSLProtocolVersion *version,
214 const SSLContext *ctx)
215 { ResumableSession *session;
216
217 session = (ResumableSession*) sessionData.data;
218 *version = session->protocolVersion;
219 return errSecSuccess;
220 }
221
222 /*
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).
226 */
227
228 /*
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.
235 *
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.
238 */
239 #define ALLOW_CIPHERSPEC_CHANGE 1
240
241 OSStatus
242 SSLInstallSessionFromData(const SSLBuffer sessionData, SSLContext *ctx)
243 { OSStatus err;
244 ResumableSession *session;
245 uint8_t *storedCertProgress;
246 #ifdef USE_SSLCERTIFICATE
247 SSLCertificate *cert;
248 SSLCertificate *lastCert = NULL;
249 #else
250 SecCertificateRef cert;
251 CFMutableArrayRef certChain = NULL;
252 #endif
253 size_t certCount;
254 size_t certLen;
255
256 session = (ResumableSession*)sessionData.data;
257
258 /*
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.
262 */
263 if(ctx->negProtocolVersion == SSL_Version_2_0) {
264 if(ctx->protocolSide == kSSLClientSide) {
265 assert(ctx->selectedCipher == 0);
266 ctx->selectedCipher = session->cipherSuite;
267 }
268 else {
269 /*
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?
272 */
273 if(ctx->selectedCipher != session->cipherSuite) {
274 sslErrorLog("+++SSL2: CipherSpec change from %d to %d on session "
275 "resume\n",
276 session->cipherSuite, ctx->selectedCipher);
277 return errSSLProtocol;
278 }
279 }
280 }
281 else {
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);
288 #else
289 sslErrorLog("+++SSL: CipherSpec change from %d to %d on session resume\n",
290 session->cipherSuite, ctx->selectedCipher);
291 return errSSLProtocol;
292 #endif
293 }
294 }
295 if ((err = FindCipherSpec(ctx)) != 0) {
296 return err;
297 }
298 memcpy(ctx->masterSecret, session->masterSecret, 48);
299
300 storedCertProgress = session->certs;
301 certCount = session->certCount;
302
303 while (certCount--)
304 {
305 #ifdef USE_SSLCERTIFICATE
306 cert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate));
307 if(cert == NULL) {
308 return errSecAllocate;
309 }
310 cert->next = 0;
311 certLen = SSLDecodeInt(storedCertProgress, 4);
312 storedCertProgress += 4;
313 if ((err = SSLAllocBuffer(&cert->derCert, certLen)
314 {
315 sslFree(cert);
316 return err;
317 }
318 memcpy(cert->derCert.data, storedCertProgress, certLen);
319 storedCertProgress += certLen;
320 if (lastCert == 0)
321 ctx->peerCert = cert;
322 else
323 lastCert->next = cert;
324 lastCert = cert;
325 #else
326 certLen = SSLDecodeInt(storedCertProgress, 4);
327 storedCertProgress += 4;
328 cert = SecCertificateCreateWithBytes(NULL, storedCertProgress, certLen);
329 #if SSL_DEBUG
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");
334 }
335 #endif
336 if(cert == NULL) {
337 return errSecAllocate;
338 }
339 storedCertProgress += certLen;
340 /* @@@ This is almost the same code as in sslCert.c: SSLProcessCertificate() */
341 if (!certChain) {
342 certChain = CFArrayCreateMutable(kCFAllocatorDefault,
343 session->certCount, &kCFTypeArrayCallBacks);
344 if (!certChain) {
345 CFRelease(cert);
346 return errSecAllocate;
347 }
348 if (ctx->peerCert) {
349 sslDebugLog("SSLInstallSessionFromData: releasing existing cert chain\n");
350 CFRelease(ctx->peerCert);
351 }
352 ctx->peerCert = certChain;
353 }
354
355 CFArrayAppendValue(certChain, cert);
356 CFRelease(cert);
357 #endif
358 }
359
360 return errSecSuccess;
361 }