]>
Commit | Line | Data |
---|---|---|
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 | ||
19 | #include "ssl.h" | |
20 | #include "sslContext.h" | |
21 | #include "sslSession.h" | |
22 | #include "sslMemory.h" | |
23 | #include "sslUtils.h" | |
24 | #include "sslDebug.h" | |
25 | #include "cipherSpecs.h" | |
26 | #include "appleSession.h" | |
27 | ||
28 | #include <assert.h> | |
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; | |
37 | UInt16 padding; /* so remainder is word aligned */ | |
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. | |
46 | * NOTE: This is contrary to the SSL v3 spec, which claims that | |
47 | * servers store resumable sessions using ctx->sessionID as the key. | |
48 | * I don' think this is an issue...is it? | |
49 | */ | |
50 | OSStatus | |
51 | SSLAddSessionData(const SSLContext *ctx) | |
52 | { OSStatus err; | |
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) | |
62 | return errSSLSessionNotFound; | |
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 | ||
73 | if ((err = SSLAllocBuffer(sessionID, sessionIDLen, ctx)) != 0) | |
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; | |
84 | session->padding = 0; | |
85 | ||
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 | ||
95 | err = sslAddSession(ctx->peerID, sessionID); | |
96 | SSLFreeBuffer(sessionID, ctx); | |
97 | ||
98 | return err; | |
99 | } | |
100 | ||
101 | /* | |
102 | * Retrieve resumable session data, from key ctx->peerID. | |
103 | */ | |
104 | OSStatus | |
105 | SSLGetSessionData(SSLBuffer *sessionData, const SSLContext *ctx) | |
106 | { OSStatus err; | |
107 | ||
108 | if (ctx->peerID.data == 0) | |
109 | return errSSLSessionNotFound; | |
110 | ||
111 | sessionData->data = 0; | |
112 | ||
113 | err = sslGetSession(ctx->peerID, sessionData); | |
114 | if (sessionData->data == 0) | |
115 | return errSSLSessionNotFound; | |
116 | ||
117 | return err; | |
118 | } | |
119 | ||
120 | OSStatus | |
121 | SSLDeleteSessionData(const SSLContext *ctx) | |
122 | { OSStatus err; | |
123 | ||
124 | if (ctx->peerID.data == 0) | |
125 | return errSSLSessionNotFound; | |
126 | ||
127 | err = sslDeleteSession(ctx->peerID); | |
128 | return err; | |
129 | } | |
130 | ||
131 | /* | |
132 | * Given a sessionData blob, obtain the associated sessionID (NOT the key...). | |
133 | */ | |
134 | OSStatus | |
135 | SSLRetrieveSessionID( | |
136 | const SSLBuffer sessionData, | |
137 | SSLBuffer *identifier, | |
138 | const SSLContext *ctx) | |
139 | { OSStatus err; | |
140 | ResumableSession *session; | |
141 | ||
142 | session = (ResumableSession*) sessionData.data; | |
143 | if ((err = SSLAllocBuffer(*identifier, session->sessionIDLen, ctx)) != 0) | |
144 | return err; | |
145 | memcpy(identifier->data, session->sessionID, session->sessionIDLen); | |
146 | return noErr; | |
147 | } | |
148 | ||
149 | /* | |
150 | * Obtain the protocol version associated with a specified resumable session blob. | |
151 | */ | |
152 | OSStatus | |
153 | SSLRetrieveSessionProtocolVersion( | |
154 | const SSLBuffer sessionData, | |
155 | SSLProtocolVersion *version, | |
156 | const SSLContext *ctx) | |
157 | { ResumableSession *session; | |
158 | ||
159 | session = (ResumableSession*) sessionData.data; | |
160 | *version = session->protocolVersion; | |
161 | return noErr; | |
162 | } | |
163 | ||
164 | /* | |
165 | * Retrieve session state from specified sessionData blob, install into | |
166 | * ctx. Presumably, ctx->sessionID and | |
167 | * ctx->negProtocolVersion are already init'd (from the above two functions). | |
168 | */ | |
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 | ||
183 | OSStatus | |
184 | SSLInstallSessionFromData(const SSLBuffer sessionData, SSLContext *ctx) | |
185 | { OSStatus err; | |
186 | ResumableSession *session; | |
187 | uint8 *storedCertProgress; | |
188 | SSLCertificate *cert, *lastCert; | |
189 | int certCount; | |
190 | uint32 certLen; | |
191 | ||
192 | session = (ResumableSession*)sessionData.data; | |
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) { | |
210 | sslErrorLog("+++SSL2: CipherSpec change from %d to %d on session " | |
211 | "resume\n", | |
212 | session->cipherSuite, ctx->selectedCipher); | |
213 | return errSSLProtocol; | |
214 | } | |
215 | } | |
216 | } | |
217 | else { | |
218 | assert(ctx->selectedCipher != 0); | |
219 | if(ctx->selectedCipher != session->cipherSuite) { | |
220 | #if ALLOW_CIPHERSPEC_CHANGE | |
221 | sslErrorLog("+++WARNING: CipherSpec change from %d to %d " | |
222 | "on session resume\n", | |
223 | session->cipherSuite, ctx->selectedCipher); | |
224 | #else | |
225 | sslErrorLog("+++SSL: CipherSpec change from %d to %d on session resume\n", | |
226 | session->cipherSuite, ctx->selectedCipher); | |
227 | return errSSLProtocol; | |
228 | #endif | |
229 | } | |
230 | } | |
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 | { | |
242 | cert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate)); | |
243 | if(cert == NULL) { | |
244 | return memFullErr; | |
245 | } | |
246 | cert->next = 0; | |
247 | certLen = SSLDecodeInt(storedCertProgress, 4); | |
248 | storedCertProgress += 4; | |
249 | if ((err = SSLAllocBuffer(cert->derCert, certLen, ctx)) != 0) | |
250 | { | |
251 | sslFree(cert); | |
252 | return err; | |
253 | } | |
254 | memcpy(cert->derCert.data, storedCertProgress, certLen); | |
255 | storedCertProgress += certLen; | |
256 | if (lastCert == 0) | |
257 | ctx->peerCert = cert; | |
258 | else | |
259 | lastCert->next = cert; | |
260 | lastCert = cert; | |
261 | } | |
262 | ||
263 | return noErr; | |
264 | } |