]> git.saurik.com Git - apple/security.git/blob - libsecurity_ssl/lib/sslSession.c
Security-55163.44.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 "cipherSpecs.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 <Security/SecInternal.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", ix+1, 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, ctx)) != 0)
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
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 (!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 ix, certCount-1, certLength, (const uintptr_t)certBytes);
143 err = paramErr; /* 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 if (!err) {
154 err = sslAddSession(ctx->peerID, sessionID, ctx->sessionCacheTimeout);
155 }
156 SSLFreeBuffer(&sessionID, ctx);
157
158 return err;
159 }
160
161 /*
162 * Retrieve resumable session data, from key ctx->peerID.
163 */
164 OSStatus
165 SSLGetSessionData(SSLBuffer *sessionData, const SSLContext *ctx)
166 { OSStatus err;
167
168 if (ctx->peerID.data == 0)
169 return errSSLSessionNotFound;
170
171 sessionData->data = 0;
172
173 err = sslGetSession(ctx->peerID, sessionData);
174 if (sessionData->data == 0)
175 return errSSLSessionNotFound;
176
177 return err;
178 }
179
180 OSStatus
181 SSLDeleteSessionData(const SSLContext *ctx)
182 { OSStatus err;
183
184 if (ctx->peerID.data == 0)
185 return errSSLSessionNotFound;
186
187 err = sslDeleteSession(ctx->peerID);
188 return err;
189 }
190
191 /*
192 * Given a sessionData blob, obtain the associated sessionID (NOT the key...).
193 */
194 OSStatus
195 SSLRetrieveSessionID(
196 const SSLBuffer sessionData,
197 SSLBuffer *identifier,
198 const SSLContext *ctx)
199 { OSStatus err;
200 ResumableSession *session;
201
202 session = (ResumableSession*) sessionData.data;
203 if ((err = SSLAllocBuffer(identifier, session->sessionIDLen, ctx)) != 0)
204 return err;
205 memcpy(identifier->data, session->sessionID, session->sessionIDLen);
206 return noErr;
207 }
208
209 /*
210 * Obtain the protocol version associated with a specified resumable session blob.
211 */
212 OSStatus
213 SSLRetrieveSessionProtocolVersion(
214 const SSLBuffer sessionData,
215 SSLProtocolVersion *version,
216 const SSLContext *ctx)
217 { ResumableSession *session;
218
219 session = (ResumableSession*) sessionData.data;
220 *version = session->protocolVersion;
221 return noErr;
222 }
223
224 /*
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).
228 */
229
230 /*
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.
237 *
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.
240 */
241 #define ALLOW_CIPHERSPEC_CHANGE 1
242
243 OSStatus
244 SSLInstallSessionFromData(const SSLBuffer sessionData, SSLContext *ctx)
245 { OSStatus err;
246 ResumableSession *session;
247 uint8_t *storedCertProgress;
248 #ifdef USE_SSLCERTIFICATE
249 SSLCertificate *cert;
250 SSLCertificate *lastCert = NULL;
251 #else
252 SecCertificateRef cert;
253 CFMutableArrayRef certChain = NULL;
254 #endif
255 size_t certCount;
256 size_t certLen;
257
258 session = (ResumableSession*)sessionData.data;
259
260 /*
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.
264 */
265 if(ctx->negProtocolVersion == SSL_Version_2_0) {
266 if(ctx->protocolSide == kSSLClientSide) {
267 assert(ctx->selectedCipher == 0);
268 ctx->selectedCipher = session->cipherSuite;
269 }
270 else {
271 /*
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?
274 */
275 if(ctx->selectedCipher != session->cipherSuite) {
276 sslErrorLog("+++SSL2: CipherSpec change from %d to %d on session "
277 "resume\n",
278 session->cipherSuite, ctx->selectedCipher);
279 return errSSLProtocol;
280 }
281 }
282 }
283 else {
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);
290 #else
291 sslErrorLog("+++SSL: CipherSpec change from %d to %d on session resume\n",
292 session->cipherSuite, ctx->selectedCipher);
293 return errSSLProtocol;
294 #endif
295 }
296 }
297 if ((err = FindCipherSpec(ctx)) != 0) {
298 return err;
299 }
300 memcpy(ctx->masterSecret, session->masterSecret, 48);
301
302 storedCertProgress = session->certs;
303 certCount = session->certCount;
304
305 while (certCount--)
306 {
307 #ifdef USE_SSLCERTIFICATE
308 cert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate));
309 if(cert == NULL) {
310 return memFullErr;
311 }
312 cert->next = 0;
313 certLen = SSLDecodeInt(storedCertProgress, 4);
314 storedCertProgress += 4;
315 if ((err = SSLAllocBuffer(&cert->derCert, certLen, ctx)) != 0)
316 {
317 sslFree(cert);
318 return err;
319 }
320 memcpy(cert->derCert.data, storedCertProgress, certLen);
321 storedCertProgress += certLen;
322 if (lastCert == 0)
323 ctx->peerCert = cert;
324 else
325 lastCert->next = cert;
326 lastCert = cert;
327 #else
328 certLen = SSLDecodeInt(storedCertProgress, 4);
329 storedCertProgress += 4;
330 cert = SecCertificateCreateWithBytes(NULL, storedCertProgress, certLen);
331 #if SSL_DEBUG
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");
336 }
337 #endif
338 if(cert == NULL) {
339 return memFullErr;
340 }
341 storedCertProgress += certLen;
342 /* @@@ This is almost the same code as in sslCert.c: SSLProcessCertificate() */
343 if (!certChain) {
344 certChain = CFArrayCreateMutable(kCFAllocatorDefault,
345 session->certCount, &kCFTypeArrayCallBacks);
346 if (!certChain) {
347 CFRelease(cert);
348 return memFullErr;
349 }
350 ctx->peerCert = certChain;
351 }
352
353 CFArrayAppendValue(certChain, cert);
354 CFRelease(cert);
355 #endif
356 }
357
358 return noErr;
359 }