]> git.saurik.com Git - apple/security.git/blob - libsecurity_ssl/lib/SSLRecordInternal.c
Security-55471.14.tar.gz
[apple/security.git] / libsecurity_ssl / lib / SSLRecordInternal.c
1 //
2 // SSLRecordInternal.c
3 // Security
4 //
5 // Created by Fabrice Gautier on 10/25/11.
6 // Copyright (c) 2011 Apple, Inc. All rights reserved.
7 //
8
9 /* THIS FILE CONTAINS KERNEL CODE */
10
11 #include "sslBuildFlags.h"
12 #include "SSLRecordInternal.h"
13 #include "sslDebug.h"
14 #include "cipherSpecs.h"
15 #include "symCipher.h"
16 #include "sslUtils.h"
17 #include "tls_record.h"
18
19 #include <AssertMacros.h>
20 #include <string.h>
21
22 #include <inttypes.h>
23
24 #define DEFAULT_BUFFER_SIZE 4096
25
26
27 /*
28 * Redirect SSLBuffer-based I/O call to user-supplied I/O.
29 */
30 static
31 int sslIoRead(SSLBuffer buf,
32 size_t *actualLength,
33 struct SSLRecordInternalContext *ctx)
34 {
35 size_t dataLength = buf.length;
36 int ortn;
37
38 *actualLength = 0;
39 ortn = (ctx->read)(ctx->ioRef,
40 buf.data,
41 &dataLength);
42 *actualLength = dataLength;
43 return ortn;
44 }
45
46 static
47 int sslIoWrite(SSLBuffer buf,
48 size_t *actualLength,
49 struct SSLRecordInternalContext *ctx)
50 {
51 size_t dataLength = buf.length;
52 int ortn;
53
54 *actualLength = 0;
55 ortn = (ctx->write)(ctx->ioRef,
56 buf.data,
57 &dataLength);
58 *actualLength = dataLength;
59 return ortn;
60 }
61
62
63 static int
64 SSLDisposeCipherSuite(CipherContext *cipher, struct SSLRecordInternalContext *ctx)
65 { int err;
66
67 /* symmetric encryption context */
68 if(cipher->symCipher) {
69 if ((err = cipher->symCipher->finish(cipher->cipherCtx)) != 0) {
70 return err;
71 }
72 }
73
74 /* per-record hash/hmac context */
75 ctx->sslTslCalls->freeMac(cipher);
76
77 return 0;
78 }
79
80
81
82 /* common for sslv3 and tlsv1, except for the computeMac callout */
83 int SSLVerifyMac(uint8_t type,
84 SSLBuffer *data,
85 uint8_t *compareMAC,
86 struct SSLRecordInternalContext *ctx)
87 {
88 int err;
89 uint8_t macData[SSL_MAX_DIGEST_LEN];
90 SSLBuffer secret, mac;
91
92 secret.data = ctx->readCipher.macSecret;
93 secret.length = ctx->readCipher.macRef->hash->digestSize;
94 mac.data = macData;
95 mac.length = ctx->readCipher.macRef->hash->digestSize;
96
97 check(ctx->sslTslCalls != NULL);
98 if ((err = ctx->sslTslCalls->computeMac(type,
99 *data,
100 mac,
101 &ctx->readCipher,
102 ctx->readCipher.sequenceNum,
103 ctx)) != 0)
104 return err;
105
106 if ((memcmp(mac.data, compareMAC, mac.length)) != 0) {
107 sslErrorLog("SSLVerifyMac: Mac verify failure\n");
108 return errSSLRecordProtocol;
109 }
110 return 0;
111 }
112
113 #include "cipherSpecs.h"
114 #include "symCipher.h"
115
116 static const HashHmacReference *sslCipherSuiteGetHashHmacReference(uint16_t selectedCipher)
117 {
118 HMAC_Algs alg = sslCipherSuiteGetMacAlgorithm(selectedCipher);
119
120 switch (alg) {
121 case HA_Null:
122 return &HashHmacNull;
123 case HA_MD5:
124 return &HashHmacMD5;
125 case HA_SHA1:
126 return &HashHmacSHA1;
127 case HA_SHA256:
128 return &HashHmacSHA256;
129 case HA_SHA384:
130 return &HashHmacSHA384;
131 default:
132 sslErrorLog("Invalid hashAlgorithm %d", alg);
133 check(0);
134 return &HashHmacNull;
135 }
136 }
137
138 static const SSLSymmetricCipher *sslCipherSuiteGetSymmetricCipher(uint16_t selectedCipher)
139 {
140
141 SSL_CipherAlgorithm alg = sslCipherSuiteGetSymmetricCipherAlgorithm(selectedCipher);
142 switch(alg) {
143 case SSL_CipherAlgorithmNull:
144 return &SSLCipherNull;
145 #if ENABLE_RC2
146 case SSL_CipherAlgorithmRC2_128:
147 return &SSLCipherRC2_128;
148 #endif
149 #if ENABLE_RC4
150 case SSL_CipherAlgorithmRC4_128:
151 return &SSLCipherRC4_128;
152 #endif
153 #if ENABLE_DES
154 case SSL_CipherAlgorithmDES_CBC:
155 return &SSLCipherDES_CBC;
156 #endif
157 case SSL_CipherAlgorithm3DES_CBC:
158 return &SSLCipher3DES_CBC;
159 case SSL_CipherAlgorithmAES_128_CBC:
160 return &SSLCipherAES_128_CBC;
161 case SSL_CipherAlgorithmAES_256_CBC:
162 return &SSLCipherAES_256_CBC;
163 #if ENABLE_AES_GCM
164 case SSL_CipherAlgorithmAES_128_GCM:
165 return &SSLCipherAES_128_GCM;
166 case SSL_CipherAlgorithmAES_256_GCM:
167 return &SSLCipherAES_256_GCM;
168 #endif
169 default:
170 check(0);
171 return &SSLCipherNull;
172 }
173 }
174
175 static void InitCipherSpec(struct SSLRecordInternalContext *ctx, uint16_t selectedCipher)
176 {
177 SSLRecordCipherSpec *dst = &ctx->selectedCipherSpec;
178
179 ctx->selectedCipher = selectedCipher;
180 dst->cipher = sslCipherSuiteGetSymmetricCipher(selectedCipher);
181 dst->macAlgorithm = sslCipherSuiteGetHashHmacReference(selectedCipher);
182 };
183
184 /* Entry points to Record Layer */
185
186 static int SSLRecordReadInternal(SSLRecordContextRef ref, SSLRecord *rec)
187 { int err;
188 size_t len, contentLen;
189 uint8_t *charPtr;
190 SSLBuffer readData, cipherFragment;
191 size_t head=5;
192 int skipit=0;
193 struct SSLRecordInternalContext *ctx = ref;
194
195 if(ctx->isDTLS)
196 head+=8;
197
198 if (!ctx->partialReadBuffer.data || ctx->partialReadBuffer.length < head)
199 { if (ctx->partialReadBuffer.data)
200 if ((err = SSLFreeBuffer(&ctx->partialReadBuffer)) != 0)
201 {
202 return err;
203 }
204 if ((err = SSLAllocBuffer(&ctx->partialReadBuffer,
205 DEFAULT_BUFFER_SIZE)) != 0)
206 {
207 return err;
208 }
209 }
210
211 if (ctx->negProtocolVersion == SSL_Version_Undetermined) {
212 if (ctx->amountRead < 1)
213 { readData.length = 1 - ctx->amountRead;
214 readData.data = ctx->partialReadBuffer.data + ctx->amountRead;
215 len = readData.length;
216 err = sslIoRead(readData, &len, ctx);
217 if(err != 0)
218 { if (err == errSSLRecordWouldBlock) {
219 ctx->amountRead += len;
220 return err;
221 }
222 else {
223 /* abort */
224 err = errSSLRecordClosedAbort;
225 #if 0 // TODO: revisit this in the transport layer
226 if((ctx->protocolSide == kSSLClientSide) &&
227 (ctx->amountRead == 0) &&
228 (len == 0)) {
229 /*
230 * Detect "server refused to even try to negotiate"
231 * error, when the server drops the connection before
232 * sending a single byte.
233 */
234 switch(ctx->state) {
235 case SSL_HdskStateServerHello:
236 sslHdskStateDebug("Server dropped initial connection\n");
237 err = errSSLConnectionRefused;
238 break;
239 default:
240 break;
241 }
242 }
243 #endif
244 return err;
245 }
246 }
247 ctx->amountRead += len;
248 }
249 }
250
251 if (ctx->amountRead < head)
252 { readData.length = head - ctx->amountRead;
253 readData.data = ctx->partialReadBuffer.data + ctx->amountRead;
254 len = readData.length;
255 err = sslIoRead(readData, &len, ctx);
256 if(err != 0)
257 {
258 switch(err) {
259 case errSSLRecordWouldBlock:
260 ctx->amountRead += len;
261 break;
262 #if SSL_ALLOW_UNNOTICED_DISCONNECT
263 case errSSLClosedGraceful:
264 /* legal if we're on record boundary and we've gotten past
265 * the handshake */
266 if((ctx->amountRead == 0) && /* nothing pending */
267 (len == 0) && /* nothing new */
268 (ctx->state == SSL_HdskStateClientReady)) { /* handshake done */
269 /*
270 * This means that the server has disconnected without
271 * sending a closure alert notice. This is technically
272 * illegal per the SSL3 spec, but about half of the
273 * servers out there do it, so we report it as a separate
274 * error which most clients - including (currently)
275 * URLAccess - ignore by treating it the same as
276 * a errSSLClosedGraceful error. Paranoid
277 * clients can detect it and handle it however they
278 * want to.
279 */
280 SSLChangeHdskState(ctx, SSL_HdskStateNoNotifyClose);
281 err = errSSLClosedNoNotify;
282 break;
283 }
284 else {
285 /* illegal disconnect */
286 err = errSSLClosedAbort;
287 /* and drop thru to default: fatal alert */
288 }
289 #endif /* SSL_ALLOW_UNNOTICED_DISCONNECT */
290 default:
291 break;
292 }
293 return err;
294 }
295 ctx->amountRead += len;
296 }
297
298 check(ctx->amountRead >= head);
299
300 charPtr = ctx->partialReadBuffer.data;
301 rec->contentType = *charPtr++;
302 if (rec->contentType < SSL_RecordTypeV3_Smallest ||
303 rec->contentType > SSL_RecordTypeV3_Largest)
304 return errSSLRecordProtocol;
305
306 rec->protocolVersion = (SSLProtocolVersion)SSLDecodeInt(charPtr, 2);
307 charPtr += 2;
308
309 if(rec->protocolVersion == DTLS_Version_1_0)
310 {
311 sslUint64 seqNum;
312 SSLDecodeUInt64(charPtr, 8, &seqNum);
313 charPtr += 8;
314 sslLogRecordIo("Read DTLS Record %016llx (seq is: %016llx)",
315 seqNum, ctx->readCipher.sequenceNum);
316
317 /* if the epoch of the record is different of current read cipher, just drop it */
318 if((seqNum>>48)!=(ctx->readCipher.sequenceNum>>48)) {
319 skipit=1;
320 } else {
321 ctx->readCipher.sequenceNum=seqNum;
322 }
323 }
324
325 contentLen = SSLDecodeInt(charPtr, 2);
326 charPtr += 2;
327 if (contentLen > (16384 + 2048)) /* Maximum legal length of an
328 * SSLCipherText payload */
329 {
330 return errSSLRecordRecordOverflow;
331 }
332
333 if (ctx->partialReadBuffer.length < head + contentLen)
334 { if ((err = SSLReallocBuffer(&ctx->partialReadBuffer, head + contentLen)) != 0)
335 {
336 return err;
337 }
338 }
339
340 if (ctx->amountRead < head + contentLen)
341 { readData.length = head + contentLen - ctx->amountRead;
342 readData.data = ctx->partialReadBuffer.data + ctx->amountRead;
343 len = readData.length;
344 err = sslIoRead(readData, &len, ctx);
345 if(err != 0)
346 { if (err == errSSLRecordWouldBlock)
347 ctx->amountRead += len;
348 return err;
349 }
350 ctx->amountRead += len;
351 }
352
353 check(ctx->amountRead >= head + contentLen);
354
355 cipherFragment.data = ctx->partialReadBuffer.data + head;
356 cipherFragment.length = contentLen;
357
358 ctx->amountRead = 0; /* We've used all the data in the cache */
359
360 /* We dont decrypt if we were told to skip this record */
361 if(skipit) {
362 return errSSLRecordUnexpectedRecord;
363 }
364 /*
365 * Decrypt the payload & check the MAC, modifying the length of the
366 * buffer to indicate the amount of plaintext data after adjusting
367 * for the block size and removing the MAC */
368 check(ctx->sslTslCalls != NULL);
369 if ((err = ctx->sslTslCalls->decryptRecord(rec->contentType,
370 &cipherFragment, ctx)) != 0)
371 return err;
372
373 /*
374 * We appear to have sucessfully received a record; increment the
375 * sequence number
376 */
377 IncrementUInt64(&ctx->readCipher.sequenceNum);
378
379 /* Allocate a buffer to return the plaintext in and return it */
380 if ((err = SSLAllocBuffer(&rec->contents, cipherFragment.length)) != 0)
381 {
382 return err;
383 }
384 memcpy(rec->contents.data, cipherFragment.data, cipherFragment.length);
385
386
387 return 0;
388 }
389
390 static int SSLRecordWriteInternal(SSLRecordContextRef ref, SSLRecord rec)
391 {
392 int err;
393 struct SSLRecordInternalContext *ctx = ref;
394
395 err=ctx->sslTslCalls->writeRecord(rec, ctx);
396
397 check_noerr(err);
398
399 return err;
400 }
401
402 /* Record Layer Entry Points */
403
404 static int
405 SSLRollbackInternalRecordLayerWriteCipher(SSLRecordContextRef ref)
406 {
407 int err;
408 struct SSLRecordInternalContext *ctx = ref;
409
410 if ((err = SSLDisposeCipherSuite(&ctx->writePending, ctx)) != 0)
411 return err;
412
413 ctx->writePending = ctx->writeCipher;
414 ctx->writeCipher = ctx->prevCipher;
415
416 /* Zero out old data */
417 memset(&ctx->prevCipher, 0, sizeof(CipherContext));
418
419 return 0;
420 }
421
422 static int
423 SSLAdvanceInternalRecordLayerWriteCipher(SSLRecordContextRef ref)
424 {
425 int err;
426 struct SSLRecordInternalContext *ctx = ref;
427
428 if ((err = SSLDisposeCipherSuite(&ctx->prevCipher, ctx)) != 0)
429 return err;
430
431 ctx->prevCipher = ctx->writeCipher;
432 ctx->writeCipher = ctx->writePending;
433
434 /* Zero out old data */
435 memset(&ctx->writePending, 0, sizeof(CipherContext));
436
437 return 0;
438 }
439
440 static int
441 SSLAdvanceInternalRecordLayerReadCipher(SSLRecordContextRef ref)
442 {
443 struct SSLRecordInternalContext *ctx = ref;
444 int err;
445
446 if ((err = SSLDisposeCipherSuite(&ctx->readCipher, ctx)) != 0)
447 return err;
448
449 ctx->readCipher = ctx->readPending;
450 memset(&ctx->readPending, 0, sizeof(CipherContext)); /* Zero out old data */
451
452 return 0;
453 }
454
455 static int
456 SSLInitInternalRecordLayerPendingCiphers(SSLRecordContextRef ref, uint16_t selectedCipher, bool isServer, SSLBuffer key)
457 { int err;
458 uint8_t *keyDataProgress, *keyPtr, *ivPtr;
459 CipherContext *serverPending, *clientPending;
460
461 struct SSLRecordInternalContext *ctx = ref;
462
463 InitCipherSpec(ctx, selectedCipher);
464
465 ctx->readPending.macRef = ctx->selectedCipherSpec.macAlgorithm;
466 ctx->writePending.macRef = ctx->selectedCipherSpec.macAlgorithm;
467 ctx->readPending.symCipher = ctx->selectedCipherSpec.cipher;
468 ctx->writePending.symCipher = ctx->selectedCipherSpec.cipher;
469 /* This need to be reinitialized because the whole thing is zeroed sometimes */
470 ctx->readPending.encrypting = 0;
471 ctx->writePending.encrypting = 1;
472
473 if(ctx->negProtocolVersion == DTLS_Version_1_0)
474 {
475 ctx->readPending.sequenceNum = (ctx->readPending.sequenceNum & (0xffffULL<<48)) + (1ULL<<48);
476 ctx->writePending.sequenceNum = (ctx->writePending.sequenceNum & (0xffffULL<<48)) + (1ULL<<48);
477 } else {
478 ctx->writePending.sequenceNum = 0;
479 ctx->readPending.sequenceNum = 0;
480 }
481
482 if (isServer)
483 { serverPending = &ctx->writePending;
484 clientPending = &ctx->readPending;
485 }
486 else
487 { serverPending = &ctx->readPending;
488 clientPending = &ctx->writePending;
489 }
490
491 /* Check the size of the 'key' buffer - <rdar://problem/11204357> */
492 if(key.length != ctx->selectedCipherSpec.macAlgorithm->hash->digestSize*2
493 + ctx->selectedCipherSpec.cipher->params->keySize*2
494 + ctx->selectedCipherSpec.cipher->params->ivSize*2)
495 {
496 return errSSLRecordInternal;
497 }
498
499 keyDataProgress = key.data;
500 memcpy(clientPending->macSecret, keyDataProgress,
501 ctx->selectedCipherSpec.macAlgorithm->hash->digestSize);
502 keyDataProgress += ctx->selectedCipherSpec.macAlgorithm->hash->digestSize;
503 memcpy(serverPending->macSecret, keyDataProgress,
504 ctx->selectedCipherSpec.macAlgorithm->hash->digestSize);
505 keyDataProgress += ctx->selectedCipherSpec.macAlgorithm->hash->digestSize;
506
507 if (ctx->selectedCipherSpec.cipher->params->cipherType == aeadCipherType)
508 goto skipInit;
509
510 /* init the reusable-per-record MAC contexts */
511 err = ctx->sslTslCalls->initMac(clientPending);
512 if(err) {
513 goto fail;
514 }
515 err = ctx->sslTslCalls->initMac(serverPending);
516 if(err) {
517 goto fail;
518 }
519
520 keyPtr = keyDataProgress;
521 keyDataProgress += ctx->selectedCipherSpec.cipher->params->keySize;
522 /* Skip server write key to get to IV */
523 ivPtr = keyDataProgress + ctx->selectedCipherSpec.cipher->params->keySize;
524 if ((err = ctx->selectedCipherSpec.cipher->c.cipher.initialize(clientPending->symCipher->params, clientPending->encrypting, keyPtr, ivPtr,
525 &clientPending->cipherCtx)) != 0)
526 goto fail;
527 keyPtr = keyDataProgress;
528 keyDataProgress += ctx->selectedCipherSpec.cipher->params->keySize;
529 /* Skip client write IV to get to server write IV */
530 ivPtr = keyDataProgress + ctx->selectedCipherSpec.cipher->params->ivSize;
531 if ((err = ctx->selectedCipherSpec.cipher->c.cipher.initialize(serverPending->symCipher->params, serverPending->encrypting, keyPtr, ivPtr,
532 &serverPending->cipherCtx)) != 0)
533 goto fail;
534
535 skipInit:
536 /* Ciphers are ready for use */
537 ctx->writePending.ready = 1;
538 ctx->readPending.ready = 1;
539
540 /* Ciphers get swapped by sending or receiving a change cipher spec message */
541 err = 0;
542
543 fail:
544 return err;
545 }
546
547 static int
548 SSLSetInternalRecordLayerProtocolVersion(SSLRecordContextRef ref, SSLProtocolVersion negVersion)
549 {
550 struct SSLRecordInternalContext *ctx = ref;
551
552 switch(negVersion) {
553 case SSL_Version_3_0:
554 ctx->sslTslCalls = &Ssl3RecordCallouts;
555 break;
556 case TLS_Version_1_0:
557 case TLS_Version_1_1:
558 case DTLS_Version_1_0:
559 case TLS_Version_1_2:
560 ctx->sslTslCalls = &Tls1RecordCallouts;
561 break;
562 case SSL_Version_2_0:
563 case SSL_Version_Undetermined:
564 default:
565 return errSSLRecordNegotiation;
566 }
567 ctx->negProtocolVersion = negVersion;
568
569 return 0;
570 }
571
572 static int
573 SSLRecordFreeInternal(SSLRecordContextRef ref, SSLRecord rec)
574 {
575 return SSLFreeBuffer(&rec.contents);
576 }
577
578 static int
579 SSLRecordServiceWriteQueueInternal(SSLRecordContextRef ref)
580 {
581 int err = 0, werr = 0;
582 size_t written = 0;
583 SSLBuffer buf;
584 WaitingRecord *rec;
585 struct SSLRecordInternalContext *ctx= ref;
586
587 while (!werr && ((rec = ctx->recordWriteQueue) != 0))
588 { buf.data = rec->data + rec->sent;
589 buf.length = rec->length - rec->sent;
590 werr = sslIoWrite(buf, &written, ctx);
591 rec->sent += written;
592 if (rec->sent >= rec->length)
593 {
594 check(rec->sent == rec->length);
595 check(err == 0);
596 ctx->recordWriteQueue = rec->next;
597 sslFree(rec);
598 }
599 if (err) {
600 check_noerr(err);
601 return err;
602 }
603 }
604
605 return werr;
606 }
607
608 /***** Internal Record Layer APIs *****/
609
610 SSLRecordContextRef
611 SSLCreateInternalRecordLayer(bool dtls)
612 {
613 struct SSLRecordInternalContext *ctx;
614
615 ctx = sslMalloc(sizeof(struct SSLRecordInternalContext));
616 if(ctx==NULL)
617 return NULL;
618
619 memset(ctx, 0, sizeof(struct SSLRecordInternalContext));
620
621 ctx->negProtocolVersion = SSL_Version_Undetermined;
622
623 ctx->sslTslCalls = &Ssl3RecordCallouts;
624 ctx->recordWriteQueue = NULL;
625
626 InitCipherSpec(ctx, TLS_NULL_WITH_NULL_NULL);
627
628 ctx->writeCipher.macRef = ctx->selectedCipherSpec.macAlgorithm;
629 ctx->readCipher.macRef = ctx->selectedCipherSpec.macAlgorithm;
630 ctx->readCipher.symCipher = ctx->selectedCipherSpec.cipher;
631 ctx->writeCipher.symCipher = ctx->selectedCipherSpec.cipher;
632 ctx->readCipher.encrypting = 0;
633 ctx->writeCipher.encrypting = 1;
634
635 ctx->isDTLS = dtls;
636
637 return ctx;
638
639 }
640
641 int
642 SSLSetInternalRecordLayerIOFuncs(
643 SSLRecordContextRef ref,
644 SSLIOReadFunc readFunc,
645 SSLIOWriteFunc writeFunc)
646 {
647 struct SSLRecordInternalContext *ctx = ref;
648
649 ctx->read = readFunc;
650 ctx->write = writeFunc;
651
652 return 0;
653 }
654
655 int
656 SSLSetInternalRecordLayerConnection(
657 SSLRecordContextRef ref,
658 SSLIOConnectionRef ioRef)
659 {
660 struct SSLRecordInternalContext *ctx = ref;
661
662 ctx->ioRef = ioRef;
663
664 return 0;
665 }
666
667 void
668 SSLDestroyInternalRecordLayer(SSLRecordContextRef ref)
669 {
670 struct SSLRecordInternalContext *ctx = ref;
671 WaitingRecord *waitRecord, *next;
672
673 /* RecordContext cleanup : */
674 SSLFreeBuffer(&ctx->partialReadBuffer);
675 waitRecord = ctx->recordWriteQueue;
676 while (waitRecord)
677 { next = waitRecord->next;
678 sslFree(waitRecord);
679 waitRecord = next;
680 }
681
682
683 /* Cleanup cipher structs */
684 SSLDisposeCipherSuite(&ctx->readCipher, ctx);
685 SSLDisposeCipherSuite(&ctx->writeCipher, ctx);
686 SSLDisposeCipherSuite(&ctx->readPending, ctx);
687 SSLDisposeCipherSuite(&ctx->writePending, ctx);
688 SSLDisposeCipherSuite(&ctx->prevCipher, ctx);
689
690 sslFree(ctx);
691
692 }
693
694 struct SSLRecordFuncs SSLRecordLayerInternal =
695 {
696 .read = SSLRecordReadInternal,
697 .write = SSLRecordWriteInternal,
698 .initPendingCiphers = SSLInitInternalRecordLayerPendingCiphers,
699 .advanceWriteCipher = SSLAdvanceInternalRecordLayerWriteCipher,
700 .advanceReadCipher = SSLAdvanceInternalRecordLayerReadCipher,
701 .rollbackWriteCipher = SSLRollbackInternalRecordLayerWriteCipher,
702 .setProtocolVersion = SSLSetInternalRecordLayerProtocolVersion,
703 .free = SSLRecordFreeInternal,
704 .serviceWriteQueue = SSLRecordServiceWriteQueueInternal,
705 };
706