]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
1 | /* |
2 | * Copyright (c) 2011-2014 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 | ||
25 | /* THIS FILE CONTAINS KERNEL CODE */ | |
26 | ||
27 | #include "sslBuildFlags.h" | |
28 | #include "SSLRecordInternal.h" | |
29 | #include "sslDebug.h" | |
30 | #include "cipherSpecs.h" | |
d8f41ccd A |
31 | #include "tls_record_internal.h" |
32 | ||
33 | #include <AssertMacros.h> | |
34 | #include <string.h> | |
35 | ||
36 | #include <inttypes.h> | |
37 | #include <stddef.h> | |
38 | ||
39 | /* Maximum encrypted record size, defined in TLS 1.2 RFC, section 6.2.3 */ | |
40 | #define DEFAULT_BUFFER_SIZE (16384 + 2048) | |
41 | ||
42 | ||
43 | /* | |
44 | * Redirect SSLBuffer-based I/O call to user-supplied I/O. | |
45 | */ | |
46 | static | |
47 | int sslIoRead(SSLBuffer buf, | |
48 | size_t *actualLength, | |
49 | struct SSLRecordInternalContext *ctx) | |
50 | { | |
d8f41ccd | 51 | int ortn; |
5c19dc3a | 52 | SSLContextRef sslCtx = ctx->sslCtx; |
d8f41ccd | 53 | |
5c19dc3a A |
54 | *actualLength = buf.length; |
55 | ||
56 | ortn = sslCtx->ioCtx.read(sslCtx->ioCtx.ioRef, buf.data, actualLength); | |
57 | ||
58 | /* We may need to translate error codes at this layer */ | |
59 | if(ortn==errSSLWouldBlock) { | |
60 | ortn=errSSLRecordWouldBlock; | |
61 | } | |
d8f41ccd A |
62 | |
63 | sslLogRecordIo("sslIoRead: [%p] req %4lu actual %4lu status %d", | |
5c19dc3a | 64 | ctx, buf.length, *actualLength, (int)ortn); |
d8f41ccd A |
65 | |
66 | return ortn; | |
67 | } | |
68 | ||
69 | static | |
70 | int sslIoWrite(SSLBuffer buf, | |
71 | size_t *actualLength, | |
72 | struct SSLRecordInternalContext *ctx) | |
73 | { | |
d8f41ccd | 74 | int ortn; |
5c19dc3a | 75 | SSLContextRef sslCtx = ctx->sslCtx; |
d8f41ccd | 76 | |
5c19dc3a A |
77 | *actualLength = buf.length; |
78 | ||
79 | ortn = sslCtx->ioCtx.write(sslCtx->ioCtx.ioRef, buf.data, actualLength); | |
80 | ||
81 | /* We may need to translate error codes at this layer */ | |
82 | if(ortn==errSSLWouldBlock) { | |
83 | ortn=errSSLRecordWouldBlock; | |
84 | } | |
d8f41ccd A |
85 | |
86 | sslLogRecordIo("sslIoWrite: [%p] req %4lu actual %4lu status %d", | |
5c19dc3a | 87 | ctx, buf.length, *actualLength, (int)ortn); |
d8f41ccd A |
88 | |
89 | return ortn; | |
90 | } | |
91 | ||
92 | /* Entry points to Record Layer */ | |
93 | ||
94 | static int SSLRecordReadInternal(SSLRecordContextRef ref, SSLRecord *rec) | |
95 | { | |
96 | struct SSLRecordInternalContext *ctx = ref; | |
97 | ||
98 | int err; | |
99 | size_t len, contentLen; | |
100 | SSLBuffer readData; | |
101 | ||
102 | size_t head=tls_record_get_header_size(ctx->filter); | |
103 | ||
104 | if (ctx->amountRead < head) | |
105 | { | |
106 | readData.length = head - ctx->amountRead; | |
107 | readData.data = ctx->partialReadBuffer.data + ctx->amountRead; | |
108 | len = readData.length; | |
109 | err = sslIoRead(readData, &len, ctx); | |
110 | if(err != 0) | |
111 | { | |
112 | switch(err) { | |
113 | case errSSLRecordWouldBlock: | |
114 | ctx->amountRead += len; | |
115 | break; | |
116 | default: | |
117 | /* Any other error but errSSLWouldBlock is translated to errSSLRecordClosedAbort */ | |
118 | err = errSSLRecordClosedAbort; | |
119 | break; | |
120 | } | |
121 | return err; | |
122 | } | |
123 | ctx->amountRead += len; | |
124 | ||
125 | check(ctx->amountRead == head); | |
126 | } | |
127 | ||
128 | ||
129 | tls_buffer header; | |
130 | header.data=ctx->partialReadBuffer.data; | |
131 | header.length=head; | |
132 | ||
133 | uint8_t content_type; | |
134 | ||
135 | tls_record_parse_header(ctx->filter, header, &contentLen, &content_type); | |
136 | ||
137 | if(content_type&0x80) { | |
fa7225c8 | 138 | sslDebugLog("Detected SSL2 record in SSLReadRecordInternal"); |
d8f41ccd A |
139 | // Looks like SSL2 record, reset expectations. |
140 | head = 2; | |
141 | err=tls_record_parse_ssl2_header(ctx->filter, header, &contentLen, &content_type); | |
142 | if(err!=0) return errSSLRecordUnexpectedRecord; | |
143 | } | |
144 | ||
145 | check(ctx->partialReadBuffer.length>=head+contentLen); | |
146 | ||
fa7225c8 A |
147 | if(head+contentLen>ctx->partialReadBuffer.length) { |
148 | sslDebugLog("overflow in SSLReadRecordInternal"); | |
d8f41ccd | 149 | return errSSLRecordRecordOverflow; |
fa7225c8 | 150 | } |
d8f41ccd A |
151 | |
152 | if (ctx->amountRead < head + contentLen) | |
fa7225c8 A |
153 | { |
154 | readData.length = head + contentLen - ctx->amountRead; | |
d8f41ccd A |
155 | readData.data = ctx->partialReadBuffer.data + ctx->amountRead; |
156 | len = readData.length; | |
157 | err = sslIoRead(readData, &len, ctx); | |
158 | if(err != 0) | |
fa7225c8 A |
159 | { |
160 | if (err == errSSLRecordWouldBlock) | |
161 | { | |
162 | ctx->amountRead += len; | |
163 | } | |
d8f41ccd A |
164 | return err; |
165 | } | |
166 | ctx->amountRead += len; | |
167 | } | |
168 | ||
169 | check(ctx->amountRead == head + contentLen); | |
170 | ||
171 | tls_buffer record; | |
172 | record.data = ctx->partialReadBuffer.data; | |
173 | record.length = ctx->amountRead; | |
174 | ||
175 | rec->contentType = content_type; | |
176 | ||
177 | ctx->amountRead = 0; /* We've used all the data in the cache */ | |
178 | ||
179 | if(content_type==tls_record_type_SSL2) { | |
180 | /* Just copy the SSL2 record, dont decrypt since this is only for SSL2 Client Hello */ | |
181 | return SSLCopyBuffer(&record, &rec->contents); | |
182 | } else { | |
183 | size_t sz = tls_record_decrypted_size(ctx->filter, record.length); | |
184 | ||
185 | /* There was an underflow - For TLS, we return errSSLRecordClosedAbort for historical reason - see ssl-44-crashes test */ | |
186 | if(sz==0) { | |
187 | sslErrorLog("underflow in SSLReadRecordInternal"); | |
fa7225c8 | 188 | if(ctx->sslCtx->isDTLS) { |
d8f41ccd A |
189 | // For DTLS, we should just drop it. |
190 | return errSSLRecordUnexpectedRecord; | |
191 | } else { | |
192 | // For TLS, we are going to close the connection. | |
193 | return errSSLRecordClosedAbort; | |
194 | } | |
195 | } | |
196 | ||
197 | /* Allocate a buffer for the plaintext */ | |
198 | if ((err = SSLAllocBuffer(&rec->contents, sz))) | |
199 | { | |
200 | return err; | |
201 | } | |
202 | ||
203 | return tls_record_decrypt(ctx->filter, record, &rec->contents, NULL); | |
204 | } | |
205 | } | |
206 | ||
207 | static int SSLRecordWriteInternal(SSLRecordContextRef ref, SSLRecord rec) | |
208 | { | |
209 | int err; | |
210 | struct SSLRecordInternalContext *ctx = ref; | |
211 | WaitingRecord *queue, *out; | |
212 | tls_buffer data; | |
213 | tls_buffer content; | |
214 | size_t len; | |
215 | ||
216 | err = errSSLRecordInternal; /* FIXME: allocation error */ | |
217 | len=tls_record_encrypted_size(ctx->filter, rec.contentType, rec.contents.length); | |
218 | ||
219 | require((out = (WaitingRecord *)sslMalloc(offsetof(WaitingRecord, data) + len)), fail); | |
220 | out->next = NULL; | |
221 | out->sent = 0; | |
222 | out->length = len; | |
223 | ||
224 | data.data=&out->data[0]; | |
225 | data.length=out->length; | |
226 | ||
227 | content.data = rec.contents.data; | |
228 | content.length = rec.contents.length; | |
229 | ||
230 | require_noerr((err=tls_record_encrypt(ctx->filter, content, rec.contentType, &data)), fail); | |
231 | ||
232 | out->length = data.length; // This should not be needed if tls_record_encrypted_size works properly. | |
233 | ||
234 | /* Enqueue the record to be written from the idle loop */ | |
235 | if (ctx->recordWriteQueue == 0) | |
236 | ctx->recordWriteQueue = out; | |
237 | else | |
238 | { queue = ctx->recordWriteQueue; | |
239 | while (queue->next != 0) | |
240 | queue = queue->next; | |
241 | queue->next = out; | |
242 | } | |
243 | ||
244 | return 0; | |
245 | fail: | |
246 | if(out) | |
247 | sslFree(out); | |
248 | return err; | |
249 | } | |
250 | ||
251 | /* Record Layer Entry Points */ | |
252 | ||
253 | static int | |
254 | SSLRollbackInternalRecordLayerWriteCipher(SSLRecordContextRef ref) | |
255 | { | |
256 | struct SSLRecordInternalContext *ctx = ref; | |
257 | return tls_record_rollback_write_cipher(ctx->filter); | |
258 | } | |
259 | ||
260 | static int | |
261 | SSLAdvanceInternalRecordLayerWriteCipher(SSLRecordContextRef ref) | |
262 | { | |
263 | struct SSLRecordInternalContext *ctx = ref; | |
264 | return tls_record_advance_write_cipher(ctx->filter); | |
265 | } | |
266 | ||
267 | static int | |
268 | SSLAdvanceInternalRecordLayerReadCipher(SSLRecordContextRef ref) | |
269 | { | |
270 | struct SSLRecordInternalContext *ctx = ref; | |
271 | return tls_record_advance_read_cipher(ctx->filter); | |
272 | } | |
273 | ||
274 | static int | |
275 | SSLInitInternalRecordLayerPendingCiphers(SSLRecordContextRef ref, uint16_t selectedCipher, bool isServer, SSLBuffer key) | |
276 | { | |
277 | struct SSLRecordInternalContext *ctx = ref; | |
278 | return tls_record_init_pending_ciphers(ctx->filter, selectedCipher, isServer, key); | |
279 | } | |
280 | ||
281 | static int | |
282 | SSLSetInternalRecordLayerProtocolVersion(SSLRecordContextRef ref, SSLProtocolVersion negVersion) | |
283 | { | |
284 | struct SSLRecordInternalContext *ctx = ref; | |
6b200bc3 | 285 | return tls_record_set_protocol_version(ctx->filter, (tls_protocol_version) negVersion); |
d8f41ccd A |
286 | } |
287 | ||
288 | static int | |
289 | SSLRecordFreeInternal(SSLRecordContextRef ref, SSLRecord rec) | |
290 | { | |
291 | return SSLFreeBuffer(&rec.contents); | |
292 | } | |
293 | ||
294 | static int | |
295 | SSLRecordServiceWriteQueueInternal(SSLRecordContextRef ref) | |
296 | { | |
5c19dc3a | 297 | int werr = 0; |
d8f41ccd A |
298 | size_t written = 0; |
299 | SSLBuffer buf; | |
300 | WaitingRecord *rec; | |
301 | struct SSLRecordInternalContext *ctx= ref; | |
302 | ||
303 | while (!werr && ((rec = ctx->recordWriteQueue) != 0)) | |
304 | { buf.data = rec->data + rec->sent; | |
305 | buf.length = rec->length - rec->sent; | |
306 | werr = sslIoWrite(buf, &written, ctx); | |
307 | rec->sent += written; | |
308 | if (rec->sent >= rec->length) | |
309 | { | |
310 | check(rec->sent == rec->length); | |
d8f41ccd A |
311 | ctx->recordWriteQueue = rec->next; |
312 | sslFree(rec); | |
313 | } | |
d8f41ccd A |
314 | } |
315 | ||
316 | return werr; | |
317 | } | |
318 | ||
319 | static int | |
320 | SSLRecordSetOption(SSLRecordContextRef ref, SSLRecordOption option, bool value) | |
321 | { | |
322 | struct SSLRecordInternalContext *ctx = (struct SSLRecordInternalContext *)ref; | |
323 | switch (option) { | |
324 | case kSSLRecordOptionSendOneByteRecord: | |
325 | return tls_record_set_record_splitting(ctx->filter, value); | |
d8f41ccd A |
326 | default: |
327 | return 0; | |
d8f41ccd A |
328 | } |
329 | } | |
330 | ||
331 | /***** Internal Record Layer APIs *****/ | |
332 | ||
333 | #include <CommonCrypto/CommonRandomSPI.h> | |
334 | #define CCRNGSTATE ccDRBGGetRngState() | |
335 | ||
336 | SSLRecordContextRef | |
5c19dc3a | 337 | SSLCreateInternalRecordLayer(SSLContextRef sslCtx) |
d8f41ccd A |
338 | { |
339 | struct SSLRecordInternalContext *ctx; | |
340 | ||
341 | ctx = sslMalloc(sizeof(struct SSLRecordInternalContext)); | |
342 | if(ctx==NULL) | |
343 | return NULL; | |
344 | ||
345 | memset(ctx, 0, sizeof(struct SSLRecordInternalContext)); | |
346 | ||
5c19dc3a | 347 | require((ctx->filter=tls_record_create(sslCtx->isDTLS, CCRNGSTATE)), fail); |
d8f41ccd A |
348 | require_noerr(SSLAllocBuffer(&ctx->partialReadBuffer, |
349 | DEFAULT_BUFFER_SIZE), fail); | |
350 | ||
5c19dc3a | 351 | ctx->sslCtx = sslCtx; |
d8f41ccd A |
352 | return ctx; |
353 | ||
354 | fail: | |
355 | if(ctx->filter) | |
356 | tls_record_destroy(ctx->filter); | |
357 | sslFree(ctx); | |
358 | return NULL; | |
359 | } | |
360 | ||
d8f41ccd A |
361 | void |
362 | SSLDestroyInternalRecordLayer(SSLRecordContextRef ref) | |
363 | { | |
364 | struct SSLRecordInternalContext *ctx = ref; | |
365 | WaitingRecord *waitRecord, *next; | |
366 | ||
367 | /* RecordContext cleanup : */ | |
368 | SSLFreeBuffer(&ctx->partialReadBuffer); | |
369 | waitRecord = ctx->recordWriteQueue; | |
370 | while (waitRecord) | |
371 | { next = waitRecord->next; | |
372 | sslFree(waitRecord); | |
373 | waitRecord = next; | |
374 | } | |
375 | ||
376 | if(ctx->filter) | |
377 | tls_record_destroy(ctx->filter); | |
378 | ||
379 | sslFree(ctx); | |
380 | ||
381 | } | |
382 | ||
383 | struct SSLRecordFuncs SSLRecordLayerInternal = | |
384 | { | |
385 | .read = SSLRecordReadInternal, | |
386 | .write = SSLRecordWriteInternal, | |
387 | .initPendingCiphers = SSLInitInternalRecordLayerPendingCiphers, | |
388 | .advanceWriteCipher = SSLAdvanceInternalRecordLayerWriteCipher, | |
389 | .advanceReadCipher = SSLAdvanceInternalRecordLayerReadCipher, | |
390 | .rollbackWriteCipher = SSLRollbackInternalRecordLayerWriteCipher, | |
391 | .setProtocolVersion = SSLSetInternalRecordLayerProtocolVersion, | |
392 | .free = SSLRecordFreeInternal, | |
393 | .serviceWriteQueue = SSLRecordServiceWriteQueueInternal, | |
394 | .setOption = SSLRecordSetOption, | |
395 | }; | |
396 |