2 * Copyright (c) 1999-2001,2005-2014 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 * sslTransport.c - SSL transport layer
29 #include "sslMemory.h"
30 #include "sslContext.h"
31 #include "sslRecord.h"
33 #include "sslCipherSpecs.h"
39 #include <utilities/SecIOFormat.h>
42 static inline void sslIoTrace(
49 sslLogRecordIo("[%p] ===%s: req %4lu moved %4lu status %d",
50 ctx
, op
, req
, moved
, (int)stat
);
53 #define sslIoTrace(ctx, op, req, moved, stat)
56 extern int kSplitDefaultValue
;
58 static OSStatus
SSLProcessProtocolMessage(SSLRecord
*rec
, SSLContext
*ctx
);
59 static OSStatus
SSLHandshakeProceed(SSLContext
*ctx
);
60 //static OSStatus SSLInitConnection(SSLContext *ctx);
67 size_t *bytesWritten
) /* RETURNED */
71 size_t dataLen
, processed
;
73 sslLogRecordIo("[%p] SSLWrite top", ctx
);
74 if((ctx
== NULL
) || (bytesWritten
== NULL
)) {
78 processed
= 0; /* Initialize in case we return with errSSLWouldBlock */
82 case SSL_HdskStateGracefulClose
:
83 err
= errSSLClosedGraceful
;
85 case SSL_HdskStateErrorClose
:
86 err
= errSSLClosedAbort
;
88 case SSL_HdskStateReady
:
90 case SSL_HdskStateUninit
:
91 /* not ready for I/O, and handshake not in progress */
92 sslIoTrace(ctx
, "SSLWrite(1)", dataLength
, 0, errSecBadReq
);
95 /* handshake in progress or done. Will call SSLHandshakeProceed below if necessary */
99 /* First, we have to wait until the session is ready to send data,
100 so the encryption keys and such have been established. */
102 while (!(ctx
->writeCipher_ready
))
103 { if ((err
= SSLHandshakeProceed(ctx
)) != 0)
107 /* Attempt to empty the write queue before queueing more data */
108 if ((err
= SSLServiceWriteQueue(ctx
)) != 0)
113 /* Skip empty writes, fragmentation is done at the coreTLS layer */
115 rec
.contentType
= SSL_RecordTypeAppData
;
116 rec
.protocolVersion
= ctx
->negProtocolVersion
;
117 rec
.contents
.data
= ((uint8_t *)data
) + processed
;
118 rec
.contents
.length
= dataLen
;
119 if ((err
= SSLWriteRecord(rec
, ctx
)) != 0)
121 processed
+= rec
.contents
.length
;
124 /* All the data has been advanced to the write queue */
125 *bytesWritten
= processed
;
126 if ((err
= SSLServiceWriteQueue(ctx
)) == 0) {
132 case errSSLWouldBlock
:
133 case errSSLUnexpectedRecord
:
134 case errSSLServerAuthCompleted
: /* == errSSLClientAuthCompleted */
135 case errSSLClientCertRequested
:
136 case errSSLClosedGraceful
:
139 sslErrorLog("SSLWrite: going to state errorClose due to err %d\n",
141 SSLChangeHdskState(ctx
, SSL_HdskStateErrorClose
);
145 sslIoTrace(ctx
, "SSLWrite(2)", dataLength
, *bytesWritten
, err
);
154 size_t *processed
) /* RETURNED */
158 size_t bufSize
, remaining
, count
;
161 sslLogRecordIo("[%p] SSLRead top (dataLength=%ld)", ctx
, dataLength
);
162 if((ctx
== NULL
) || (data
== NULL
) || (processed
== NULL
)) {
165 bufSize
= dataLength
;
166 *processed
= 0; /* Initialize in case we return with errSSLWouldBlock */
169 /* first handle cases in which we know we're finished */
171 case SSL_HdskStateGracefulClose
:
172 err
= errSSLClosedGraceful
;
174 case SSL_HdskStateErrorClose
:
175 err
= errSSLClosedAbort
;
177 case SSL_HdskStateNoNotifyClose
:
178 err
= errSSLClosedNoNotify
;
184 /* First, we have to wait until the session is ready to receive data,
185 so the encryption keys and such have been established. */
187 while (ctx
->readCipher_ready
== 0) {
188 if ((err
= SSLHandshakeProceed(ctx
)) != 0) {
193 /* Attempt to service the write queue */
194 if ((err
= SSLServiceWriteQueue(ctx
)) != 0) {
195 if (err
!= errSSLWouldBlock
) {
198 err
= errSecSuccess
; /* Write blocking shouldn't stop attempts to read */
202 charPtr
= (uint8_t *)data
;
203 if (ctx
->receivedDataBuffer
.data
)
204 { count
= ctx
->receivedDataBuffer
.length
- ctx
->receivedDataPos
;
207 memcpy(data
, ctx
->receivedDataBuffer
.data
+ ctx
->receivedDataPos
, count
);
211 ctx
->receivedDataPos
+= count
;
214 assert(ctx
->receivedDataPos
<= ctx
->receivedDataBuffer
.length
);
215 assert(*processed
+ remaining
== bufSize
);
216 assert(charPtr
== ((uint8_t *)data
) + *processed
);
218 if (ctx
->receivedDataBuffer
.data
!= 0 &&
219 ctx
->receivedDataPos
>= ctx
->receivedDataBuffer
.length
)
220 { SSLFreeBuffer(&ctx
->receivedDataBuffer
);
221 ctx
->receivedDataBuffer
.data
= 0;
222 ctx
->receivedDataPos
= 0;
226 * This while statement causes a hang when using nonblocking low-level I/O!
227 while (remaining > 0 && ctx->state != SSL_HdskStateGracefulClose)
228 ..what we really have to do is just return as soon as we read one
229 record. A performance hit in the nonblocking case, but that is
230 the only way this code can work in both modes...
232 if (remaining
> 0 && ctx
->state
!= SSL_HdskStateGracefulClose
)
233 { assert(ctx
->receivedDataBuffer
.data
== 0);
234 if ((err
= SSLReadRecord(&rec
, ctx
)) != 0) {
237 if (rec
.contentType
== SSL_RecordTypeAppData
||
238 rec
.contentType
== SSL_RecordTypeV2_0
)
239 { if (rec
.contents
.length
<= remaining
)
240 { memcpy(charPtr
, rec
.contents
.data
, rec
.contents
.length
);
241 remaining
-= rec
.contents
.length
;
242 charPtr
+= rec
.contents
.length
;
243 *processed
+= rec
.contents
.length
;
245 if ((err
= SSLFreeRecord(rec
, ctx
))) {
251 { memcpy(charPtr
, rec
.contents
.data
, remaining
);
252 charPtr
+= remaining
;
253 *processed
+= remaining
;
254 ctx
->receivedDataBuffer
= rec
.contents
;
255 ctx
->receivedDataPos
= remaining
;
260 if ((err
= SSLProcessProtocolMessage(&rec
, ctx
)) != 0) {
261 /* This may not make much sense, but this is required so that we
262 process the write queue. This replicate exactly the behavior
263 before the coreTLS adoption */
264 if(err
== errSSLClosedGraceful
) {
270 if ((err
= SSLFreeRecord(rec
, ctx
))) {
279 /* test for renegotiate: loop until something useful happens */
280 if(((err
== errSecSuccess
) && (*processed
== 0) && dataLength
) || (err
== errSSLUnexpectedRecord
)) {
281 sslLogNegotiateDebug("SSLRead recursion");
284 /* shut down on serious errors */
287 case errSSLWouldBlock
:
288 case errSSLUnexpectedRecord
:
289 case errSSLServerAuthCompleted
: /* == errSSLClientAuthCompleted */
290 case errSSLClientCertRequested
:
291 case errSSLClosedGraceful
:
292 case errSSLClosedNoNotify
:
295 sslErrorLog("SSLRead: going to state errorClose due to err %d\n",
297 SSLChangeHdskState(ctx
, SSL_HdskStateErrorClose
);
301 sslIoTrace(ctx
, "SSLRead returns", dataLength
, *processed
, err
);
306 #include "sslCrypto.h"
310 SSLHandshake(SSLContext
*ctx
)
317 if (ctx
->state
== SSL_HdskStateGracefulClose
)
318 return errSSLClosedGraceful
;
319 if (ctx
->state
== SSL_HdskStateErrorClose
)
320 return errSSLClosedAbort
;
322 if(ctx
->validCipherSuites
== NULL
) {
323 /* build list of legal cipherSpecs */
324 err
= sslBuildCipherSuiteArray(ctx
);
332 if(ctx
->isDTLS
&& ctx
->timeout_deadline
) {
333 CFAbsoluteTime current
= CFAbsoluteTimeGetCurrent();
335 if (ctx
->timeout_deadline
<current
) {
336 sslDebugLog("%p, retransmition deadline expired\n", ctx
);
337 err
= tls_handshake_retransmit_timer_expired(ctx
->hdsk
);
344 while (ctx
->readCipher_ready
== 0 || ctx
->writeCipher_ready
== 0)
346 err
= SSLHandshakeProceed(ctx
);
347 if((err
!= 0) && (err
!= errSSLUnexpectedRecord
))
351 /* one more flush at completion of successful handshake */
352 if ((err
= SSLServiceWriteQueue(ctx
)) != 0) {
356 return errSecSuccess
;
359 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
361 #include <AggregateDictionary/ADClient.h>
363 typedef void (*type_ADClientAddValueForScalarKey
)(CFStringRef key
, int64_t value
);
364 static type_ADClientAddValueForScalarKey gADClientAddValueForScalarKey
= NULL
;
365 static dispatch_once_t gADFunctionPointersSet
= 0;
366 static CFBundleRef gAggdBundleRef
= NULL
;
368 static bool InitializeADFunctionPointers()
370 if (gADClientAddValueForScalarKey
)
375 dispatch_once(&gADFunctionPointersSet
,
377 CFStringRef path_to_aggd_framework
= CFSTR("/System/Library/PrivateFrameworks/AggregateDictionary.framework");
379 CFURLRef aggd_url
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, path_to_aggd_framework
, kCFURLPOSIXPathStyle
, true);
381 if (NULL
!= aggd_url
)
383 gAggdBundleRef
= CFBundleCreate(kCFAllocatorDefault
, aggd_url
);
384 if (NULL
!= gAggdBundleRef
)
386 gADClientAddValueForScalarKey
= (type_ADClientAddValueForScalarKey
)
387 CFBundleGetFunctionPointerForName(gAggdBundleRef
, CFSTR("ADClientAddValueForScalarKey"));
393 return (gADClientAddValueForScalarKey
!=NULL
);
396 static void ad_log_SecureTransport_early_fail(long signature
)
398 if (InitializeADFunctionPointers()) {
400 CFStringRef key
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("com.apple.SecureTransport.early_fail.%ld"), signature
);
403 gADClientAddValueForScalarKey(key
, 1);
412 #if (!TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
414 #include <msgtracer_client.h>
416 static void mt_log_SecureTransport_early_fail(long signature
)
418 char signature_string
[16];
420 snprintf(signature_string
, sizeof(signature_string
), "%ld", signature
);
422 msgtracer_log_with_keys("com.apple.SecureTransport.early_fail", ASL_LEVEL_NOTICE
,
423 "com.apple.message.signature", signature_string
,
424 "com.apple.message.summarize", "YES",
431 static void log_SecureTransport_early_fail(long signature
)
433 #if (!TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
434 mt_log_SecureTransport_early_fail(signature
);
437 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
438 ad_log_SecureTransport_early_fail(signature
);
443 SSLHandshakeProceed(SSLContext
*ctx
)
448 if(ctx
->state
==SSL_HdskStateUninit
) {
449 /* If we are the client, we start the negotiation */
450 if(ctx
->protocolSide
== kSSLClientSide
) {
451 err
= tls_handshake_negotiate(ctx
->hdsk
, &ctx
->peerID
);
455 SSLChangeHdskState(ctx
, SSL_HdskStatePending
);
458 if ((err
= tls_handshake_continue(ctx
->hdsk
)) != 0)
461 if ((err
= SSLServiceWriteQueue(ctx
)) != 0)
466 err
= SSLReadRecord(&rec
, ctx
);
469 sslDebugLog("%p going to process a record (rec.len=%zd, ct=%d)\n", ctx
, rec
.contents
.length
, rec
.contentType
);
470 err
= tls_handshake_process(ctx
->hdsk
, rec
.contents
, rec
.contentType
);
471 sslDebugLog("%p processed a record (rec.len=%zd, ct=%d, err=%d)\n", ctx
, rec
.contents
.length
, rec
.contentType
, (int)err
);
472 SSLFreeRecord(rec
, ctx
);
473 } else if(err
!=errSSLWouldBlock
) {
474 sslDebugLog("%p Read error err=%d\n\n", ctx
, (int)err
);
477 if(ctx
->protocolSide
== kSSLClientSide
&&
478 ctx
->dheEnabled
== false &&
479 !ctx
->serverHelloReceived
&&
480 err
&& err
!= errSSLWouldBlock
)
482 log_SecureTransport_early_fail(err
);
489 SSLProcessProtocolMessage(SSLRecord
*rec
, SSLContext
*ctx
)
491 return tls_handshake_process(ctx
->hdsk
, rec
->contents
, rec
->contentType
);
495 SSLClose(SSLContext
*ctx
)
497 OSStatus err
= errSecSuccess
;
499 sslHdskStateDebug("SSLClose");
504 err
= tls_handshake_close(ctx
->hdsk
);
507 err
= SSLServiceWriteQueue(ctx
);
509 SSLChangeHdskState(ctx
, SSL_HdskStateGracefulClose
);
511 err
= errSecSuccess
; /* Ignore errors related to closed streams */
516 * Determine how much data the client can be guaranteed to
517 * obtain via SSLRead() without blocking or causing any low-level
518 * read operations to occur.
520 * Implemented here because the relevant info in SSLContext (receivedDataBuffer
521 * and receivedDataPos) are only used in this file.
524 SSLGetBufferedReadSize(SSLContextRef ctx
,
525 size_t *bufSize
) /* RETURNED */
530 if(ctx
->receivedDataBuffer
.data
== NULL
) {
534 assert(ctx
->receivedDataBuffer
.length
>= ctx
->receivedDataPos
);
535 *bufSize
= ctx
->receivedDataBuffer
.length
- ctx
->receivedDataPos
;
537 return errSecSuccess
;