]> git.saurik.com Git - apple/security.git/blob - SecureTransport/ssl2Record.cpp
6cfaafadd99af08d415e6c9b43387b324e9f053e
[apple/security.git] / SecureTransport / ssl2Record.cpp
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 /*
20 File: ssl2Record.cpp
21
22 Contains: Record encrypting/decrypting/MACing for SSL 2
23
24 Written by: Doug Mitchell
25
26 Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
27
28 */
29
30 #include "ssl2.h"
31 #include "sslRecord.h"
32 #include "sslMemory.h"
33 #include "sslContext.h"
34 #include "sslAlertMessage.h"
35 #include "sslDebug.h"
36 #include "sslUtils.h"
37 #include "sslDigests.h"
38
39 #include <string.h>
40
41 static OSStatus SSL2DecryptRecord(
42 SSLBuffer &payload,
43 SSLContext *ctx);
44 static OSStatus SSL2VerifyMAC(
45 SSLBuffer &content,
46 UInt8 *compareMAC,
47 SSLContext *ctx);
48 static OSStatus SSL2CalculateMAC(
49 SSLBuffer &secret,
50 SSLBuffer &content,
51 UInt32 seqNo,
52 const HashReference &hash,
53 SSLBuffer &mac,
54 SSLContext *ctx);
55
56
57 OSStatus
58 SSL2ReadRecord(SSLRecord &rec, SSLContext *ctx)
59 { OSStatus err;
60 UInt32 len, contentLen;
61 int padding, headerSize;
62 UInt8 *charPtr;
63 SSLBuffer readData, cipherFragment;
64
65 switch (ctx->negProtocolVersion)
66 { case SSL_Version_Undetermined:
67 case SSL_Version_3_0_With_2_0_Hello:
68 case SSL_Version_2_0:
69 break;
70 case SSL_Version_3_0: /* We've negotiated a 3.0 session;
71 * we can send an alert */
72 case TLS_Version_1_0:
73 SSLFatalSessionAlert(SSL_AlertUnexpectedMsg, ctx);
74 return errSSLProtocol;
75 case SSL_Version_3_0_Only: /* We haven't yet negotiated, but
76 * we don't want to support 2.0; just
77 * die without an alert */
78 return errSSLProtocol;
79 default:
80 sslErrorLog("bad protocolVersion in ctx->protocolVersion");
81 return errSSLInternal;
82 }
83
84 if (!ctx->partialReadBuffer.data || ctx->partialReadBuffer.length < 3)
85 { if (ctx->partialReadBuffer.data)
86 if ((err = SSLFreeBuffer(ctx->partialReadBuffer, ctx)) != 0)
87 { SSLFatalSessionAlert(SSL_AlertCloseNotify, ctx);
88 return err;
89 }
90 if ((err = SSLAllocBuffer(ctx->partialReadBuffer, DEFAULT_BUFFER_SIZE, ctx)) != 0)
91 { SSLFatalSessionAlert(SSL_AlertCloseNotify, ctx);
92 return err;
93 }
94 }
95
96 if (ctx->amountRead < 3)
97 { readData.length = 3 - ctx->amountRead;
98 readData.data = ctx->partialReadBuffer.data + ctx->amountRead;
99 len = readData.length;
100 err = sslIoRead(readData, &len, ctx);
101 if(err != 0)
102 { if (err == errSSLWouldBlock)
103 ctx->amountRead += len;
104 if (err == ioErr && ctx->amountRead == 0) /* If the session closes on a record boundary, it's graceful */
105 err = errSSLClosedGraceful;
106 return err;
107 }
108 ctx->amountRead += len;
109 }
110
111 rec.contentType = SSL_RecordTypeV2_0;
112 rec.protocolVersion = SSL_Version_2_0;
113 charPtr = ctx->partialReadBuffer.data;
114
115 if (((*charPtr) & 0x80) != 0) /* High bit on -> specifies 2-byte header */
116 { headerSize = 2;
117 contentLen = ((charPtr[0] & 0x7F) << 8) | charPtr[1];
118 padding = 0;
119 }
120 else if (((*charPtr) & 0x40) != 0) /* Bit 6 on -> specifies security escape */
121 { return errSSLProtocol; /* No security escapes are defined */
122 }
123 else /* 3-byte header */
124 { headerSize = 3;
125 contentLen = ((charPtr[0] & 0x3F) << 8) | charPtr[1];
126 padding = charPtr[2];
127 }
128
129 /*
130 * FIXME - what's the max record size?
131 * and why doesn't SSLReadRecord parse the 2 or 3 byte header?
132 * Note: I see contentLen of 0 coming back from www.cduniverse.com when
133 * it's only been given SSL_RSA_EXPORT_WITH_DES40_CBC_SHA.
134 */
135 if((contentLen == 0) || (contentLen > 0xffff)) {
136 return errSSLProtocol;
137 }
138
139 charPtr += headerSize;
140
141 if (ctx->partialReadBuffer.length < headerSize + contentLen)
142 { if ((err = SSLReallocBuffer(ctx->partialReadBuffer, 5 + contentLen, ctx)) != 0)
143 return err;
144 }
145
146 if (ctx->amountRead < headerSize + contentLen)
147 { readData.length = headerSize + contentLen - ctx->amountRead;
148 readData.data = ctx->partialReadBuffer.data + ctx->amountRead;
149 len = readData.length;
150 err = sslIoRead(readData, &len, ctx);
151 if(err != 0)
152 { if (err == errSSLWouldBlock)
153 ctx->amountRead += len;
154 return err;
155 }
156 ctx->amountRead += len;
157 }
158
159 cipherFragment.data = ctx->partialReadBuffer.data + headerSize;
160 cipherFragment.length = contentLen;
161 if ((err = SSL2DecryptRecord(cipherFragment, ctx)) != 0)
162 return err;
163
164 cipherFragment.length -= padding; /* Remove padding; MAC was removed
165 * by SSL2DecryptRecord */
166
167 IncrementUInt64(&ctx->readCipher.sequenceNum);
168
169 /* Allocate a buffer to return the plaintext in and return it */
170 if ((err = SSLAllocBuffer(rec.contents, cipherFragment.length, ctx)) != 0)
171 return err;
172 memcpy(rec.contents.data, cipherFragment.data, cipherFragment.length);
173
174 ctx->amountRead = 0; /* We've used all the data in the cache */
175
176 return noErr;
177 }
178
179 OSStatus
180 SSL2WriteRecord(SSLRecord &rec, SSLContext *ctx)
181 { OSStatus err;
182 int padding = 0, i, headerSize;
183 WaitingRecord *out, *queue;
184 SSLBuffer buf, content, payload, secret, mac;
185 UInt8 *charPtr;
186 UInt16 payloadSize, blockSize;
187
188 assert(rec.contents.length < 16384);
189
190 out = 0;
191 /* Allocate a WaitingRecord to store our ready-to-send record in */
192 if ((err = SSLAllocBuffer(buf, sizeof(WaitingRecord), ctx)) != 0)
193 return err;
194 out = (WaitingRecord*)buf.data;
195 out->next = 0;
196 out->sent = 0;
197
198 payloadSize = (UInt16)
199 (rec.contents.length + ctx->writeCipher.macRef->hash->digestSize);
200 blockSize = ctx->writeCipher.symCipher->blockSize;
201 if (blockSize > 0)
202 {
203 padding = blockSize - (payloadSize % blockSize);
204 if (padding == blockSize)
205 padding = 0;
206 payloadSize += padding;
207 headerSize = 3;
208 }
209 else
210 { padding = 0;
211 headerSize = 2;
212 }
213 out->data.data = 0;
214 if ((err = SSLAllocBuffer(out->data, headerSize + payloadSize, ctx)) != 0)
215 goto fail;
216 charPtr = out->data.data;
217
218 if (headerSize == 2)
219 charPtr = SSLEncodeInt(charPtr, payloadSize | 0x8000, 2);
220 else
221 { charPtr = SSLEncodeInt(charPtr, payloadSize, 2);
222 *charPtr++ = padding;
223 }
224
225 payload.data = charPtr;
226 payload.length = payloadSize;
227
228 mac.data = charPtr;
229 mac.length = ctx->writeCipher.macRef->hash->digestSize;
230 charPtr += mac.length;
231
232 content.data = charPtr;
233 content.length = rec.contents.length + padding;
234 memcpy(charPtr, rec.contents.data, rec.contents.length);
235 charPtr += rec.contents.length;
236 i = padding;
237 while (i--)
238 *charPtr++ = padding;
239
240 assert(charPtr == out->data.data + out->data.length);
241
242 secret.data = ctx->writeCipher.macSecret;
243 secret.length = ctx->writeCipher.symCipher->keySize;
244 if (mac.length > 0)
245 if ((err = SSL2CalculateMAC(secret, content,
246 ctx->writeCipher.sequenceNum.low,
247 *ctx->writeCipher.macRef->hash, mac, ctx)) != 0)
248 goto fail;
249
250 if ((err = ctx->writeCipher.symCipher->encrypt(payload,
251 payload,
252 &ctx->writeCipher,
253 ctx)) != 0)
254 goto fail;
255
256 /* Enqueue the record to be written from the idle loop */
257 if (ctx->recordWriteQueue == 0)
258 ctx->recordWriteQueue = out;
259 else
260 { queue = ctx->recordWriteQueue;
261 while (queue->next != 0)
262 queue = queue->next;
263 queue->next = out;
264 }
265
266 /* Increment the sequence number */
267 IncrementUInt64(&ctx->writeCipher.sequenceNum);
268
269 return noErr;
270
271 fail:
272 /*
273 * Only for if we fail between when the WaitingRecord is allocated and
274 * when it is queued
275 */
276 SSLFreeBuffer(out->data, 0);
277 buf.data = (UInt8*)out;
278 buf.length = sizeof(WaitingRecord);
279 SSLFreeBuffer(buf, ctx);
280 return err;
281 }
282
283 static OSStatus
284 SSL2DecryptRecord(SSLBuffer &payload, SSLContext *ctx)
285 { OSStatus err;
286 SSLBuffer content;
287
288 if (ctx->readCipher.symCipher->blockSize > 0)
289 if (payload.length % ctx->readCipher.symCipher->blockSize != 0)
290 return errSSLProtocol;
291
292 /* Decrypt in place */
293 if ((err = ctx->readCipher.symCipher->decrypt(payload,
294 payload,
295 &ctx->readCipher,
296 ctx)) != 0)
297 return err;
298
299 if (ctx->readCipher.macRef->hash->digestSize > 0)
300 /* Optimize away MAC for null case */
301 { content.data = payload.data + ctx->readCipher.macRef->hash->digestSize; /* Data is after MAC */
302 content.length = payload.length - ctx->readCipher.macRef->hash->digestSize;
303 if ((err = SSL2VerifyMAC(content, payload.data, ctx)) != 0)
304 return err;
305 /* Adjust payload to remove MAC; caller is still responsible
306 * for removing padding [if any] */
307 payload = content;
308 }
309
310 return noErr;
311 }
312
313 #define IGNORE_MAC_FAILURE 0
314
315 static OSStatus
316 SSL2VerifyMAC(SSLBuffer &content, UInt8 *compareMAC, SSLContext *ctx)
317 { OSStatus err;
318 UInt8 calculatedMAC[SSL_MAX_DIGEST_LEN];
319 SSLBuffer secret, mac;
320
321 secret.data = ctx->readCipher.macSecret;
322 secret.length = ctx->readCipher.symCipher->keySize;
323 mac.data = calculatedMAC;
324 mac.length = ctx->readCipher.macRef->hash->digestSize;
325 if ((err = SSL2CalculateMAC(secret, content, ctx->readCipher.sequenceNum.low,
326 *ctx->readCipher.macRef->hash, mac, ctx)) != 0)
327 return err;
328 if (memcmp(mac.data, compareMAC, mac.length) != 0) {
329 #if IGNORE_MAC_FAILURE
330 sslErrorLog("SSL2VerifyMAC: Mac verify failure\n");
331 return noErr;
332 #else
333 sslErrorLog("SSL2VerifyMAC: Mac verify failure\n");
334 return errSSLProtocol;
335 #endif
336 }
337 return noErr;
338 }
339
340 #define LOG_MAC_DATA 0
341 #if LOG_MAC_DATA
342 static void logMacData(
343 char *field,
344 SSLBuffer *data)
345 {
346 int i;
347
348 printf("%s: ", field);
349 for(i=0; i<data->length; i++) {
350 printf("%02X", data->data[i]);
351 if((i % 4) == 3) {
352 printf(" ");
353 }
354 }
355 printf("\n");
356 }
357 #else /* LOG_MAC_DATA */
358 #define logMacData(f, d)
359 #endif /* LOG_MAC_DATA */
360
361 /* For SSL 2, the MAC is hash ( secret || content || sequence# )
362 * where secret is the decryption key for the message, content is
363 * the record data plus any padding used to round out the record
364 * size to an even multiple of the block size and sequence# is
365 * a monotonically increasing 32-bit unsigned integer.
366 */
367 static OSStatus
368 SSL2CalculateMAC(
369 SSLBuffer &secret,
370 SSLBuffer &content,
371 UInt32 seqNo,
372 const HashReference &hash,
373 SSLBuffer &mac,
374 SSLContext *ctx)
375 { OSStatus err;
376 UInt8 sequenceNum[4];
377 SSLBuffer seqData, hashContext;
378
379 SSLEncodeInt(sequenceNum, seqNo, 4);
380 seqData.data = sequenceNum;
381 seqData.length = 4;
382
383 hashContext.data = 0;
384 if ((err = ReadyHash(hash, hashContext, ctx)) != 0)
385 return err;
386 if ((err = hash.update(hashContext, secret)) != 0)
387 goto fail;
388 if ((err = hash.update(hashContext, content)) != 0)
389 goto fail;
390 if ((err = hash.update(hashContext, seqData)) != 0)
391 goto fail;
392 if ((err = hash.final(hashContext, mac)) != 0)
393 goto fail;
394
395 logMacData("secret ", &secret);
396 logMacData("seqData", &seqData);
397 logMacData("mac ", &mac);
398
399 err = noErr;
400 fail:
401 SSLFreeBuffer(hashContext, ctx);
402 return err;
403 }
404
405 OSStatus
406 SSL2SendError(SSL2ErrorCode error, SSLContext *ctx)
407 { OSStatus err;
408 SSLRecord rec;
409 UInt8 errorData[3];
410
411 rec.contentType = SSL_RecordTypeV2_0;
412 rec.protocolVersion = SSL_Version_2_0;
413 rec.contents.data = errorData;
414 rec.contents.length = 3;
415 errorData[0] = SSL2_MsgError;
416 SSLEncodeInt(errorData + 1, error, 2);
417
418 err = SSL2WriteRecord(rec, ctx);
419 return err;
420 }