]>
Commit | Line | Data |
---|---|---|
bac41a7b A |
1 | /* |
2 | * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. | |
3 | * | |
4 | * The contents of this file constitute Original Code as defined in and are | |
5 | * subject to the Apple Public Source License Version 1.2 (the 'License'). | |
6 | * You may not use this file except in compliance with the License. Please obtain | |
7 | * a copy of the License at http://www.apple.com/publicsource and read it before | |
8 | * using this file. | |
9 | * | |
10 | * This Original Code and all software distributed under the License are | |
11 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS | |
12 | * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT | |
13 | * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | |
14 | * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the | |
15 | * specific language governing rights and limitations under the License. | |
16 | */ | |
17 | ||
18 | ||
bac41a7b | 19 | #include "ssl.h" |
5a719ac8 A |
20 | #include "sslContext.h" |
21 | #include "sslSession.h" | |
22 | #include "sslMemory.h" | |
23 | #include "sslUtils.h" | |
bac41a7b | 24 | #include "sslDebug.h" |
bac41a7b | 25 | #include "cipherSpecs.h" |
bac41a7b | 26 | #include "appleSession.h" |
5a719ac8 | 27 | |
29654253 | 28 | #include <assert.h> |
bac41a7b A |
29 | #include <string.h> |
30 | #include <stddef.h> | |
31 | ||
32 | typedef struct | |
33 | { int sessionIDLen; | |
34 | UInt8 sessionID[32]; | |
35 | SSLProtocolVersion protocolVersion; | |
36 | UInt16 cipherSuite; | |
29654253 | 37 | UInt16 padding; /* so remainder is word aligned */ |
bac41a7b A |
38 | UInt8 masterSecret[48]; |
39 | int certCount; | |
40 | UInt8 certs[1]; /* Actually, variable length */ | |
41 | } ResumableSession; | |
42 | ||
43 | /* | |
44 | * Cook up a (private) resumable session blob, based on the | |
45 | * specified ctx, store it with ctx->peerID as the key. | |
5a719ac8 | 46 | * NOTE: This is contrary to the SSL v3 spec, which claims that |
bac41a7b A |
47 | * servers store resumable sessions using ctx->sessionID as the key. |
48 | * I don' think this is an issue...is it? | |
49 | */ | |
5a719ac8 | 50 | OSStatus |
29654253 | 51 | SSLAddSessionData(const SSLContext *ctx) |
5a719ac8 | 52 | { OSStatus err; |
bac41a7b A |
53 | uint32 sessionIDLen; |
54 | SSLBuffer sessionID; | |
55 | ResumableSession *session; | |
56 | int certCount; | |
57 | SSLCertificate *cert; | |
58 | uint8 *certDest; | |
59 | ||
60 | /* If we don't know who the peer is, we can't store a session */ | |
61 | if (ctx->peerID.data == 0) | |
5a719ac8 | 62 | return errSSLSessionNotFound; |
bac41a7b A |
63 | |
64 | sessionIDLen = offsetof(ResumableSession, certs); | |
65 | cert = ctx->peerCert; | |
66 | certCount = 0; | |
67 | while (cert) | |
68 | { ++certCount; | |
69 | sessionIDLen += 4 + cert->derCert.length; | |
70 | cert = cert->next; | |
71 | } | |
72 | ||
5a719ac8 | 73 | if ((err = SSLAllocBuffer(sessionID, sessionIDLen, ctx)) != 0) |
bac41a7b A |
74 | return err; |
75 | ||
76 | session = (ResumableSession*)sessionID.data; | |
77 | ||
78 | session->sessionIDLen = ctx->sessionID.length; | |
79 | memcpy(session->sessionID, ctx->sessionID.data, session->sessionIDLen); | |
80 | session->protocolVersion = ctx->negProtocolVersion; | |
81 | session->cipherSuite = ctx->selectedCipher; | |
82 | memcpy(session->masterSecret, ctx->masterSecret, 48); | |
83 | session->certCount = certCount; | |
29654253 A |
84 | session->padding = 0; |
85 | ||
bac41a7b A |
86 | certDest = session->certs; |
87 | cert = ctx->peerCert; | |
88 | while (cert) | |
89 | { certDest = SSLEncodeInt(certDest, cert->derCert.length, 4); | |
90 | memcpy(certDest, cert->derCert.data, cert->derCert.length); | |
91 | certDest += cert->derCert.length; | |
92 | cert = cert->next; | |
93 | } | |
94 | ||
29654253 | 95 | err = sslAddSession(ctx->peerID, sessionID); |
5a719ac8 | 96 | SSLFreeBuffer(sessionID, ctx); |
bac41a7b A |
97 | |
98 | return err; | |
99 | } | |
100 | ||
101 | /* | |
102 | * Retrieve resumable session data, from key ctx->peerID. | |
103 | */ | |
5a719ac8 | 104 | OSStatus |
29654253 | 105 | SSLGetSessionData(SSLBuffer *sessionData, const SSLContext *ctx) |
5a719ac8 | 106 | { OSStatus err; |
bac41a7b A |
107 | |
108 | if (ctx->peerID.data == 0) | |
5a719ac8 | 109 | return errSSLSessionNotFound; |
bac41a7b A |
110 | |
111 | sessionData->data = 0; | |
112 | ||
29654253 | 113 | err = sslGetSession(ctx->peerID, sessionData); |
bac41a7b | 114 | if (sessionData->data == 0) |
5a719ac8 | 115 | return errSSLSessionNotFound; |
bac41a7b A |
116 | |
117 | return err; | |
118 | } | |
119 | ||
5a719ac8 | 120 | OSStatus |
29654253 | 121 | SSLDeleteSessionData(const SSLContext *ctx) |
5a719ac8 | 122 | { OSStatus err; |
bac41a7b A |
123 | |
124 | if (ctx->peerID.data == 0) | |
5a719ac8 | 125 | return errSSLSessionNotFound; |
bac41a7b | 126 | |
29654253 | 127 | err = sslDeleteSession(ctx->peerID); |
bac41a7b A |
128 | return err; |
129 | } | |
130 | ||
131 | /* | |
132 | * Given a sessionData blob, obtain the associated sessionID (NOT the key...). | |
133 | */ | |
5a719ac8 | 134 | OSStatus |
29654253 | 135 | SSLRetrieveSessionID( |
bac41a7b A |
136 | const SSLBuffer sessionData, |
137 | SSLBuffer *identifier, | |
138 | const SSLContext *ctx) | |
5a719ac8 | 139 | { OSStatus err; |
bac41a7b A |
140 | ResumableSession *session; |
141 | ||
142 | session = (ResumableSession*) sessionData.data; | |
5a719ac8 | 143 | if ((err = SSLAllocBuffer(*identifier, session->sessionIDLen, ctx)) != 0) |
bac41a7b A |
144 | return err; |
145 | memcpy(identifier->data, session->sessionID, session->sessionIDLen); | |
5a719ac8 | 146 | return noErr; |
bac41a7b A |
147 | } |
148 | ||
149 | /* | |
150 | * Obtain the protocol version associated with a specified resumable session blob. | |
151 | */ | |
5a719ac8 | 152 | OSStatus |
29654253 A |
153 | SSLRetrieveSessionProtocolVersion( |
154 | const SSLBuffer sessionData, | |
bac41a7b A |
155 | SSLProtocolVersion *version, |
156 | const SSLContext *ctx) | |
157 | { ResumableSession *session; | |
158 | ||
29654253 | 159 | session = (ResumableSession*) sessionData.data; |
bac41a7b | 160 | *version = session->protocolVersion; |
5a719ac8 | 161 | return noErr; |
bac41a7b A |
162 | } |
163 | ||
164 | /* | |
29654253 A |
165 | * Retrieve session state from specified sessionData blob, install into |
166 | * ctx. Presumably, ctx->sessionID and | |
bac41a7b A |
167 | * ctx->negProtocolVersion are already init'd (from the above two functions). |
168 | */ | |
29654253 A |
169 | |
170 | /* | |
171 | * Netscape Enterprise Server is known to change cipherspecs upon session resumption. | |
172 | * For example, connecting to cdnow.com with all ciphersuites enabled results in | |
173 | * CipherSuite 4 (SSL_RSA_WITH_RC4_128_MD5) being selected on the first session, | |
174 | * and CipherSuite 10 (SSL_RSA_WITH_3DES_EDE_CBC_SHA) being selected on subsequent | |
175 | * sessions. This is contrary to the SSL3.0 spec, sesion 7.6.1.3, describing the | |
176 | * Server Hello message. | |
177 | * | |
178 | * This anomaly does not occur if only RC4 ciphers are enabled in the Client Hello | |
179 | * message. It also does not happen in SSL V2. | |
180 | */ | |
181 | #define ALLOW_CIPHERSPEC_CHANGE 1 | |
182 | ||
5a719ac8 | 183 | OSStatus |
29654253 | 184 | SSLInstallSessionFromData(const SSLBuffer sessionData, SSLContext *ctx) |
5a719ac8 | 185 | { OSStatus err; |
bac41a7b A |
186 | ResumableSession *session; |
187 | uint8 *storedCertProgress; | |
188 | SSLCertificate *cert, *lastCert; | |
bac41a7b A |
189 | int certCount; |
190 | uint32 certLen; | |
191 | ||
192 | session = (ResumableSession*)sessionData.data; | |
29654253 A |
193 | |
194 | /* | |
195 | * For SSLv3 and TLSv1, we know that selectedCipher has already been specified in | |
196 | * SSLProcessServerHello(). An SSLv2 server hello message with a session | |
197 | * ID hit contains no CipherKind field so we set it here. | |
198 | */ | |
199 | if(ctx->negProtocolVersion == SSL_Version_2_0) { | |
200 | if(ctx->protocolSide == SSL_ClientSide) { | |
201 | assert(ctx->selectedCipher == 0); | |
202 | ctx->selectedCipher = session->cipherSuite; | |
203 | } | |
204 | else { | |
205 | /* | |
206 | * Else...what if they don't match? Could never happen, right? | |
207 | * Wouldn't that mean the client is trying to switch ciphers on us? | |
208 | */ | |
209 | if(ctx->selectedCipher != session->cipherSuite) { | |
5a719ac8 | 210 | sslErrorLog("+++SSL2: CipherSpec change from %d to %d on session " |
29654253 A |
211 | "resume\n", |
212 | session->cipherSuite, ctx->selectedCipher); | |
5a719ac8 | 213 | return errSSLProtocol; |
29654253 A |
214 | } |
215 | } | |
216 | } | |
217 | else { | |
218 | assert(ctx->selectedCipher != 0); | |
219 | if(ctx->selectedCipher != session->cipherSuite) { | |
220 | #if ALLOW_CIPHERSPEC_CHANGE | |
5a719ac8 A |
221 | sslErrorLog("+++WARNING: CipherSpec change from %d to %d " |
222 | "on session resume\n", | |
29654253 A |
223 | session->cipherSuite, ctx->selectedCipher); |
224 | #else | |
5a719ac8 | 225 | sslErrorLog("+++SSL: CipherSpec change from %d to %d on session resume\n", |
29654253 | 226 | session->cipherSuite, ctx->selectedCipher); |
5a719ac8 | 227 | return errSSLProtocol; |
29654253 A |
228 | #endif |
229 | } | |
230 | } | |
bac41a7b A |
231 | if ((err = FindCipherSpec(ctx)) != 0) { |
232 | return err; | |
233 | } | |
234 | memcpy(ctx->masterSecret, session->masterSecret, 48); | |
235 | ||
236 | lastCert = 0; | |
237 | storedCertProgress = session->certs; | |
238 | certCount = session->certCount; | |
239 | ||
240 | while (certCount--) | |
241 | { | |
bac41a7b A |
242 | cert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate)); |
243 | if(cert == NULL) { | |
5a719ac8 | 244 | return memFullErr; |
bac41a7b | 245 | } |
bac41a7b A |
246 | cert->next = 0; |
247 | certLen = SSLDecodeInt(storedCertProgress, 4); | |
248 | storedCertProgress += 4; | |
5a719ac8 | 249 | if ((err = SSLAllocBuffer(cert->derCert, certLen, ctx)) != 0) |
bac41a7b | 250 | { |
bac41a7b | 251 | sslFree(cert); |
bac41a7b A |
252 | return err; |
253 | } | |
254 | memcpy(cert->derCert.data, storedCertProgress, certLen); | |
255 | storedCertProgress += certLen; | |
bac41a7b A |
256 | if (lastCert == 0) |
257 | ctx->peerCert = cert; | |
258 | else | |
259 | lastCert->next = cert; | |
260 | lastCert = cert; | |
261 | } | |
262 | ||
5a719ac8 | 263 | return noErr; |
bac41a7b | 264 | } |