2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
22 Contains: SSL transport layer
24 Written by: Doug Mitchell
26 Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
30 #include "sslMemory.h"
31 #include "sslContext.h"
32 #include "sslRecord.h"
33 #include "sslAlertMessage.h"
34 #include "sslSession.h"
37 #include "cipherSpecs.h"
40 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
46 static void inline sslIoTrace(
52 sslLogRecordIo("===%s: req %4lu moved %4lu status %ld",
53 op
, req
, moved
, stat
);
56 #define sslIoTrace(op, req, moved, stat)
59 static OSStatus
SSLProcessProtocolMessage(SSLRecord
&rec
, SSLContext
*ctx
);
60 static OSStatus
SSLHandshakeProceed(SSLContext
*ctx
);
61 static OSStatus
SSLInitConnection(SSLContext
*ctx
);
62 static OSStatus
SSLServiceWriteQueue(SSLContext
*ctx
);
69 UInt32
*bytesWritten
) /* RETURNED */
73 UInt32 dataLen
, processed
;
75 sslLogRecordIo("SSLWrite top");
76 if((ctx
== NULL
) || (bytesWritten
== NULL
)) {
80 processed
= 0; /* Initialize in case we return with errSSLWouldBlock */
84 case SSL_HdskStateGracefulClose
:
85 err
= errSSLClosedGraceful
;
87 case SSL_HdskStateErrorClose
:
88 err
= errSSLClosedAbort
;
91 /* FIXME - original code didn't check for pending handshake -
94 sslIoTrace("SSLWrite", dataLength
, 0, badReqErr
);
96 case SSL_HdskStateServerReady
:
97 case SSL_HdskStateClientReady
:
101 /* First, we have to wait until the session is ready to send data,
102 so the encryption keys and such have been established. */
104 while (ctx
->writeCipher
.ready
== 0)
105 { if ((err
= SSLHandshakeProceed(ctx
)) != 0)
109 /* Attempt to empty the write queue before queueing more data */
110 if ((err
= SSLServiceWriteQueue(ctx
)) != 0)
115 * Fragment, package and encrypt the data and queue the resulting data
119 { rec
.contentType
= SSL_RecordTypeAppData
;
120 rec
.protocolVersion
= ctx
->negProtocolVersion
;
121 rec
.contents
.data
= ((UInt8
*)data
) + processed
;
123 if (dataLen
< MAX_RECORD_LENGTH
)
124 rec
.contents
.length
= dataLen
;
126 rec
.contents
.length
= MAX_RECORD_LENGTH
;
128 assert(ctx
->sslTslCalls
!= NULL
);
129 if ((err
= ctx
->sslTslCalls
->writeRecord(rec
, ctx
)) != 0)
131 processed
+= rec
.contents
.length
;
132 dataLen
-= rec
.contents
.length
;
135 /* All the data has been advanced to the write queue */
136 *bytesWritten
= processed
;
137 if ((err
= SSLServiceWriteQueue(ctx
)) == 0) {
141 if (err
!= 0 && err
!= errSSLWouldBlock
&& err
!= errSSLClosedGraceful
) {
142 sslErrorLog("SSLWrite: going to state errorCLose due to err %d\n",
144 SSLChangeHdskState(ctx
, SSL_HdskStateErrorClose
);
147 sslIoTrace("SSLWrite", dataLength
, *bytesWritten
, err
);
156 UInt32
*processed
) /* RETURNED */
160 UInt32 bufSize
, remaining
, count
;
163 sslLogRecordIo("SSLRead top");
164 if((ctx
== NULL
) || (processed
== NULL
)) {
167 bufSize
= dataLength
;
168 *processed
= 0; /* Initialize in case we return with errSSLWouldBlock */
171 /* first handle cases in which we know we're finished */
173 case SSL_HdskStateGracefulClose
:
174 err
= errSSLClosedGraceful
;
176 case SSL_HdskStateErrorClose
:
177 err
= errSSLClosedAbort
;
179 case SSL_HdskStateNoNotifyClose
:
180 err
= errSSLClosedNoNotify
;
186 /* First, we have to wait until the session is ready to receive data,
187 so the encryption keys and such have been established. */
189 while (ctx
->readCipher
.ready
== 0) {
190 if ((err
= SSLHandshakeProceed(ctx
)) != 0) {
195 /* Attempt to service the write queue */
196 if ((err
= SSLServiceWriteQueue(ctx
)) != 0) {
197 if (err
!= errSSLWouldBlock
) {
200 err
= noErr
; /* Write blocking shouldn't stop attempts to read */
204 charPtr
= (UInt8
*)data
;
205 if (ctx
->receivedDataBuffer
.data
)
206 { count
= ctx
->receivedDataBuffer
.length
- ctx
->receivedDataPos
;
209 memcpy(data
, ctx
->receivedDataBuffer
.data
+ ctx
->receivedDataPos
, count
);
213 ctx
->receivedDataPos
+= count
;
216 assert(ctx
->receivedDataPos
<= ctx
->receivedDataBuffer
.length
);
217 assert(*processed
+ remaining
== bufSize
);
218 assert(charPtr
== ((UInt8
*)data
) + *processed
);
220 if (ctx
->receivedDataBuffer
.data
!= 0 &&
221 ctx
->receivedDataPos
>= ctx
->receivedDataBuffer
.length
)
222 { SSLFreeBuffer(ctx
->receivedDataBuffer
, ctx
);
223 ctx
->receivedDataBuffer
.data
= 0;
224 ctx
->receivedDataPos
= 0;
228 * This while statement causes a hang when using nonblocking low-level I/O!
229 while (remaining > 0 && ctx->state != SSL_HdskStateGracefulClose)
230 ..what we really have to do is just return as soon as we read one
231 record. A performance hit in the nonblocking case, but that is
232 the only way this code can work in both modes...
234 if (remaining
> 0 && ctx
->state
!= SSL_HdskStateGracefulClose
)
235 { assert(ctx
->receivedDataBuffer
.data
== 0);
236 if ((err
= SSLReadRecord(rec
, ctx
)) != 0) {
239 if (rec
.contentType
== SSL_RecordTypeAppData
||
240 rec
.contentType
== SSL_RecordTypeV2_0
)
241 { if (rec
.contents
.length
<= remaining
)
242 { memcpy(charPtr
, rec
.contents
.data
, rec
.contents
.length
);
243 remaining
-= rec
.contents
.length
;
244 charPtr
+= rec
.contents
.length
;
245 *processed
+= rec
.contents
.length
;
248 * if ((err = SSLFreeBuffer(rec.contents, ctx)) != 0)
249 * passes the address of rec to SSLFreeBuffer, not the address
250 * of the contents field (which should be offset 8 from the start
254 SSLBuffer
*b
= &rec
.contents
;
255 if ((err
= SSLFreeBuffer(*b
, ctx
)) != 0) {
261 { memcpy(charPtr
, rec
.contents
.data
, remaining
);
262 charPtr
+= remaining
;
263 *processed
+= remaining
;
264 ctx
->receivedDataBuffer
= rec
.contents
;
265 ctx
->receivedDataPos
= remaining
;
270 if ((err
= SSLProcessProtocolMessage(rec
, ctx
)) != 0) {
273 if ((err
= SSLFreeBuffer(rec
.contents
, ctx
)) != 0) {
282 /* test for renegotiate: loop until something happens */
283 if((err
== noErr
) && (*processed
== 0)) {
284 sslLogNegotiateDebug("SSLRead recursion");
287 /* shut down on serious errors */
290 case errSSLWouldBlock
:
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("SSLRead ", dataLength
, *processed
, err
);
306 #include "appleCdsa.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
->validCipherSpecs
== NULL
) {
323 /* build list of legal cipherSpecs */
324 err
= sslBuildCipherSpecArray(ctx
);
330 while (ctx
->readCipher
.ready
== 0 || ctx
->writeCipher
.ready
== 0)
331 { if ((err
= SSLHandshakeProceed(ctx
)) != 0)
335 /* one more flush at completion of successful handshake */
336 if ((err
= SSLServiceWriteQueue(ctx
)) != 0) {
344 SSLHandshakeProceed(SSLContext
*ctx
)
348 if (ctx
->state
== SSL_HdskStateUninit
)
349 if ((err
= SSLInitConnection(ctx
)) != 0)
351 if ((err
= SSLServiceWriteQueue(ctx
)) != 0)
353 assert(ctx
->readCipher
.ready
== 0);
354 if ((err
= SSLReadRecord(rec
, ctx
)) != 0)
356 if ((err
= SSLProcessProtocolMessage(rec
, ctx
)) != 0)
357 { SSLFreeBuffer(rec
.contents
, ctx
);
360 if ((err
= SSLFreeBuffer(rec
.contents
, ctx
)) != 0)
367 SSLInitConnection(SSLContext
*ctx
)
368 { OSStatus err
= noErr
;
370 if (ctx
->protocolSide
== SSL_ClientSide
) {
371 SSLChangeHdskState(ctx
, SSL_HdskStateClientUninit
);
374 { assert(ctx
->protocolSide
== SSL_ServerSide
);
375 SSLChangeHdskState(ctx
, SSL_HdskStateServerUninit
);
378 if (ctx
->peerID
.data
!= 0)
379 { SSLGetSessionData(&ctx
->resumableSession
, ctx
);
380 /* Ignore errors; just treat as uncached session */
384 * If we have a cached resumable session, blow it off if it's a version
385 * which is not currently enabled.
387 Boolean cachedV3OrTls1
= false;
389 if (ctx
->resumableSession
.data
!= 0) {
391 SSLProtocolVersion savedVersion
;
394 if ((err
= SSLRetrieveSessionProtocolVersion(ctx
->resumableSession
,
395 &savedVersion
, ctx
)) != 0) {
398 switch(savedVersion
) {
399 case SSL_Version_2_0
:
400 enable
= ctx
->versionSsl2Enable
;
402 case SSL_Version_3_0
:
403 enable
= ctx
->versionSsl3Enable
;
404 cachedV3OrTls1
= true; // avoid V2 hello
406 case TLS_Version_1_0
:
407 enable
= ctx
->versionTls1Enable
;
408 cachedV3OrTls1
= true;
412 return errSSLInternal
;
415 sslLogResumSessDebug("===Resumable session protocol mismatch");
416 SSLFreeBuffer(ctx
->resumableSession
, ctx
);
417 cachedV3OrTls1
= false;
420 sslLogResumSessDebug("===attempting to resume session");
425 * If we're the client & handshake hasn't yet begun, start it by
426 * pretending we just received a hello request
428 if (ctx
->state
== SSL_HdskStateClientUninit
&& ctx
->writeCipher
.ready
== 0)
430 assert(ctx
->negProtocolVersion
== SSL_Version_Undetermined
);
431 if(ctx
->versionSsl2Enable
&& !cachedV3OrTls1
) {
432 /* SSL2 client hello with possible upgrade */
433 err
= SSL2AdvanceHandshake(SSL2_MsgKickstart
, ctx
);
436 err
= SSLAdvanceHandshake(SSL_HdskHelloRequest
, ctx
);
444 SSLServiceWriteQueue(SSLContext
*ctx
)
445 { OSStatus err
= noErr
, werr
= noErr
;
447 SSLBuffer buf
, recBuf
;
450 while (!werr
&& ((rec
= ctx
->recordWriteQueue
) != 0))
451 { buf
.data
= rec
->data
.data
+ rec
->sent
;
452 buf
.length
= rec
->data
.length
- rec
->sent
;
453 werr
= sslIoWrite(buf
, &written
, ctx
);
454 rec
->sent
+= written
;
455 if (rec
->sent
>= rec
->data
.length
)
456 { assert(rec
->sent
== rec
->data
.length
);
458 err
= SSLFreeBuffer(rec
->data
, ctx
);
460 recBuf
.data
= (UInt8
*)rec
;
461 recBuf
.length
= sizeof(WaitingRecord
);
462 ctx
->recordWriteQueue
= rec
->next
;
463 err
= SSLFreeBuffer(recBuf
, ctx
);
474 SSLProcessProtocolMessage(SSLRecord
&rec
, SSLContext
*ctx
)
477 switch (rec
.contentType
)
478 { case SSL_RecordTypeHandshake
:
479 sslLogRxProtocolDebug("Handshake");
480 err
= SSLProcessHandshakeRecord(rec
, ctx
);
482 case SSL_RecordTypeAlert
:
483 sslLogRxProtocolDebug("Alert");
484 err
= SSLProcessAlert(rec
, ctx
);
486 case SSL_RecordTypeChangeCipher
:
487 sslLogRxProtocolDebug("ChangeCipher");
488 err
= SSLProcessChangeCipherSpec(rec
, ctx
);
490 case SSL_RecordTypeV2_0
:
491 sslLogRxProtocolDebug("RecordTypeV2_0");
492 err
= SSL2ProcessMessage(rec
, ctx
);
495 sslLogRxProtocolDebug("Bad msg");
496 return errSSLProtocol
;
503 SSLClose(SSLContext
*ctx
)
505 OSStatus err
= noErr
;
507 sslHdskStateDebug("SSLClose");
511 if (ctx
->negProtocolVersion
>= SSL_Version_3_0
)
512 err
= SSLSendAlert(SSL_AlertLevelWarning
, SSL_AlertCloseNotify
, ctx
);
514 err
= SSLServiceWriteQueue(ctx
);
515 SSLChangeHdskState(ctx
, SSL_HdskStateGracefulClose
);
517 err
= noErr
; /* Ignore errors related to closed streams */
522 * Determine how much data the client can be guaranteed to
523 * obtain via SSLRead() without blocking or causing any low-level
524 * read operations to occur.
526 * Implemented here because the relevant info in SSLContext (receivedDataBuffer
527 * and receivedDataPos) are only used in this file.
530 SSLGetBufferedReadSize(SSLContextRef ctx
,
531 size_t *bufSize
) /* RETURNED */
536 if(ctx
->receivedDataBuffer
.data
== NULL
) {
540 assert(ctx
->receivedDataBuffer
.length
>= ctx
->receivedDataPos
);
541 *bufSize
= ctx
->receivedDataBuffer
.length
- ctx
->receivedDataPos
;