2 * Copyright (c) 1999-2001,2005-2007,2010-2012 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * sslRecord.c - Encryption, decryption and MACing of data
30 #include "sslRecord.h"
31 #include "sslMemory.h"
32 #include "cryptType.h"
33 #include "sslContext.h"
34 #include "sslAlertMessage.h"
37 #include "sslDigests.h"
43 * Lots of servers fail to provide closure alerts when they disconnect.
44 * For now we'll just accept it as long as it occurs on a clean record boundary
45 * (and the handshake is complete).
47 #define SSL_ALLOW_UNNOTICED_DISCONNECT 1
50 * Attempt to read & decrypt an SSL record.
53 SSLReadRecord(SSLRecord
*rec
, SSLContext
*ctx
)
55 size_t len
, contentLen
;
57 SSLBuffer readData
, cipherFragment
;
66 if (!ctx
->partialReadBuffer
.data
|| ctx
->partialReadBuffer
.length
< head
)
67 { if (ctx
->partialReadBuffer
.data
)
68 if ((err
= SSLFreeBuffer(&ctx
->partialReadBuffer
, ctx
)) != 0)
69 { SSLFatalSessionAlert(SSL_AlertInternalError
, ctx
);
72 if ((err
= SSLAllocBuffer(&ctx
->partialReadBuffer
,
73 DEFAULT_BUFFER_SIZE
, ctx
)) != 0)
74 { SSLFatalSessionAlert(SSL_AlertInternalError
, ctx
);
79 if (ctx
->negProtocolVersion
== SSL_Version_Undetermined
) {
80 if (ctx
->amountRead
< 1)
81 { readData
.length
= 1 - ctx
->amountRead
;
82 readData
.data
= ctx
->partialReadBuffer
.data
+ ctx
->amountRead
;
83 len
= readData
.length
;
84 err
= sslIoRead(readData
, &len
, ctx
);
86 { if (err
== errSSLWouldBlock
) {
87 ctx
->amountRead
+= len
;
92 err
= errSSLClosedAbort
;
93 if((ctx
->protocolSide
== kSSLClientSide
) &&
94 (ctx
->amountRead
== 0) &&
97 * Detect "server refused to even try to negotiate"
98 * error, when the server drops the connection before
99 * sending a single byte.
102 case SSL_HdskStateServerHello
:
103 case SSL_HdskStateServerHelloUnknownVersion
:
104 sslHdskStateDebug("Server dropped initial connection\n");
105 err
= errSSLConnectionRefused
;
111 SSLFatalSessionAlert(SSL_AlertCloseNotify
, ctx
);
115 ctx
->amountRead
+= len
;
119 if (ctx
->amountRead
< head
)
120 { readData
.length
= head
- ctx
->amountRead
;
121 readData
.data
= ctx
->partialReadBuffer
.data
+ ctx
->amountRead
;
122 len
= readData
.length
;
123 err
= sslIoRead(readData
, &len
, ctx
);
127 case errSSLWouldBlock
:
128 ctx
->amountRead
+= len
;
130 #if SSL_ALLOW_UNNOTICED_DISCONNECT
131 case errSSLClosedGraceful
:
132 /* legal if we're on record boundary and we've gotten past
134 if((ctx
->amountRead
== 0) && /* nothing pending */
135 (len
== 0) && /* nothing new */
136 (ctx
->state
== SSL_HdskStateClientReady
)) { /* handshake done */
138 * This means that the server has disconnected without
139 * sending a closure alert notice. This is technically
140 * illegal per the SSL3 spec, but about half of the
141 * servers out there do it, so we report it as a separate
142 * error which most clients - including (currently)
143 * URLAccess - ignore by treating it the same as
144 * a errSSLClosedGraceful error. Paranoid
145 * clients can detect it and handle it however they
148 SSLChangeHdskState(ctx
, SSL_HdskStateNoNotifyClose
);
149 err
= errSSLClosedNoNotify
;
153 /* illegal disconnect */
154 err
= errSSLClosedAbort
;
155 /* and drop thru to default: fatal alert */
157 #endif /* SSL_ALLOW_UNNOTICED_DISCONNECT */
159 SSLFatalSessionAlert(SSL_AlertCloseNotify
, ctx
);
164 ctx
->amountRead
+= len
;
167 assert(ctx
->amountRead
>= head
);
169 charPtr
= ctx
->partialReadBuffer
.data
;
170 rec
->contentType
= *charPtr
++;
171 if (rec
->contentType
< SSL_RecordTypeV3_Smallest
||
172 rec
->contentType
> SSL_RecordTypeV3_Largest
)
173 return errSSLProtocol
;
175 rec
->protocolVersion
= (SSLProtocolVersion
)SSLDecodeInt(charPtr
, 2);
179 if(rec
->protocolVersion
== DTLS_Version_1_0
)
182 SSLDecodeUInt64(charPtr
, 8, &seqNum
);
184 sslLogRecordIo("Read DTLS Record %08x_%08x (seq is: %08x_%08x)",
185 seqNum
.high
, seqNum
.low
,
186 ctx
->readCipher
.sequenceNum
.high
,ctx
->readCipher
.sequenceNum
.low
);
188 /* if the epoch of the record is different of current read cipher, just drop it */
189 if((seqNum
.high
>>8)!=(ctx
->readCipher
.sequenceNum
.high
>>8)) {
192 ctx
->readCipher
.sequenceNum
.high
=seqNum
.high
;
193 ctx
->readCipher
.sequenceNum
.low
=seqNum
.low
;
198 contentLen
= SSLDecodeInt(charPtr
, 2);
200 if (contentLen
> (16384 + 2048)) /* Maximum legal length of an
201 * SSLCipherText payload */
202 { SSLFatalSessionAlert(SSL_AlertRecordOverflow
, ctx
);
203 return errSSLProtocol
;
206 /* Dont check this if we are going to drop an old packet:
207 we dont know if the digestSize is the correct one */
208 if (!skipit
&& contentLen
< ctx
->readCipher
.macRef
->hash
->digestSize
)
210 SSLFatalSessionAlert(SSL_AlertInternalError
, ctx
);
211 return errSSLClosedAbort
;
214 if (ctx
->partialReadBuffer
.length
< head
+ contentLen
)
215 { if ((err
= SSLReallocBuffer(&ctx
->partialReadBuffer
, head
+ contentLen
, ctx
)) != 0)
216 { SSLFatalSessionAlert(SSL_AlertInternalError
, ctx
);
221 if (ctx
->amountRead
< head
+ contentLen
)
222 { readData
.length
= head
+ contentLen
- ctx
->amountRead
;
223 readData
.data
= ctx
->partialReadBuffer
.data
+ ctx
->amountRead
;
224 len
= readData
.length
;
225 err
= sslIoRead(readData
, &len
, ctx
);
227 { if (err
== errSSLWouldBlock
)
228 ctx
->amountRead
+= len
;
230 SSLFatalSessionAlert(SSL_AlertCloseNotify
, ctx
);
233 ctx
->amountRead
+= len
;
236 assert(ctx
->amountRead
>= head
+ contentLen
);
238 cipherFragment
.data
= ctx
->partialReadBuffer
.data
+ head
;
239 cipherFragment
.length
= contentLen
;
241 ctx
->amountRead
= 0; /* We've used all the data in the cache */
243 /* We dont decrypt if we were told to skip this record */
246 return errSSLWouldBlock
;
249 * Decrypt the payload & check the MAC, modifying the length of the
250 * buffer to indicate the amount of plaintext data after adjusting
251 * for the block size and removing the MAC (this function generates
254 assert(ctx
->sslTslCalls
!= NULL
);
255 if ((err
= ctx
->sslTslCalls
->decryptRecord(rec
->contentType
,
256 &cipherFragment
, ctx
)) != 0)
260 * We appear to have sucessfully received a record; increment the
263 IncrementUInt64(&ctx
->readCipher
.sequenceNum
);
265 /* Allocate a buffer to return the plaintext in and return it */
266 if ((err
= SSLAllocBuffer(&rec
->contents
, cipherFragment
.length
, ctx
)) != 0)
267 { SSLFatalSessionAlert(SSL_AlertInternalError
, ctx
);
270 memcpy(rec
->contents
.data
, cipherFragment
.data
, cipherFragment
.length
);
276 /* common for sslv3 and tlsv1, except for the computeMac callout */
277 OSStatus
SSLVerifyMac(
284 UInt8 macData
[SSL_MAX_DIGEST_LEN
];
285 SSLBuffer secret
, mac
;
287 secret
.data
= ctx
->readCipher
.macSecret
;
288 secret
.length
= ctx
->readCipher
.macRef
->hash
->digestSize
;
290 mac
.length
= ctx
->readCipher
.macRef
->hash
->digestSize
;
292 assert(ctx
->sslTslCalls
!= NULL
);
293 if ((err
= ctx
->sslTslCalls
->computeMac(type
,
297 ctx
->readCipher
.sequenceNum
,
301 if ((memcmp(mac
.data
, compareMAC
, mac
.length
)) != 0) {
302 sslErrorLog("ssl3VerifyMac: Mac verify failure\n");
303 return errSSLProtocol
;