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\n",
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 if((ctx
== NULL
) || (bytesWritten
== NULL
)) {
79 processed
= 0; /* Initialize in case we return with errSSLWouldBlock */
83 case SSL_HdskStateGracefulClose
:
84 err
= errSSLClosedGraceful
;
86 case SSL_HdskStateErrorClose
:
87 err
= errSSLClosedAbort
;
90 /* FIXME - original code didn't check for pending handshake -
93 sslIoTrace("SSLWrite", dataLength
, 0, badReqErr
);
95 case SSL2_HdskStateServerReady
:
96 case SSL2_HdskStateClientReady
:
100 /* First, we have to wait until the session is ready to send data,
101 so the encryption keys and such have been established. */
103 while (ctx
->writeCipher
.ready
== 0)
104 { if ((err
= SSLHandshakeProceed(ctx
)) != 0)
108 /* Attempt to empty the write queue before queueing more data */
109 if ((err
= SSLServiceWriteQueue(ctx
)) != 0)
114 * Fragment, package and encrypt the data and queue the resulting data
118 { rec
.contentType
= SSL_RecordTypeAppData
;
119 rec
.protocolVersion
= ctx
->negProtocolVersion
;
120 rec
.contents
.data
= ((UInt8
*)data
) + processed
;
122 if (dataLen
< MAX_RECORD_LENGTH
)
123 rec
.contents
.length
= dataLen
;
125 rec
.contents
.length
= MAX_RECORD_LENGTH
;
127 assert(ctx
->sslTslCalls
!= NULL
);
128 if ((err
= ctx
->sslTslCalls
->writeRecord(rec
, ctx
)) != 0)
130 processed
+= rec
.contents
.length
;
131 dataLen
-= rec
.contents
.length
;
134 /* All the data has been advanced to the write queue */
135 *bytesWritten
= processed
;
136 if ((err
= SSLServiceWriteQueue(ctx
)) == 0) {
140 if (err
!= 0 && err
!= errSSLWouldBlock
&& err
!= errSSLClosedGraceful
) {
141 sslErrorLog("SSLWrite: going to state errorCLose due to err %d\n",
143 SSLChangeHdskState(ctx
, SSL_HdskStateErrorClose
);
146 sslIoTrace("SSLWrite", dataLength
, *bytesWritten
, err
);
155 UInt32
*processed
) /* RETURNED */
159 UInt32 bufSize
, remaining
, count
;
162 if((ctx
== NULL
) || (processed
== NULL
)) {
165 bufSize
= dataLength
;
166 *processed
= 0; /* Initialize in case we return with errSSLWouldBlock */
168 /* first handle cases in which we know we're finished */
170 case SSL_HdskStateGracefulClose
:
171 err
= errSSLClosedGraceful
;
173 case SSL_HdskStateErrorClose
:
174 err
= errSSLClosedAbort
;
176 case SSL_HdskStateNoNotifyClose
:
177 err
= errSSLClosedNoNotify
;
183 /* First, we have to wait until the session is ready to receive data,
184 so the encryption keys and such have been established. */
186 while (ctx
->readCipher
.ready
== 0) {
187 if ((err
= SSLHandshakeProceed(ctx
)) != 0) {
192 /* Attempt to service the write queue */
193 if ((err
= SSLServiceWriteQueue(ctx
)) != 0) {
194 if (err
!= errSSLWouldBlock
) {
197 err
= noErr
; /* Write blocking shouldn't stop attempts to read */
201 charPtr
= (UInt8
*)data
;
202 if (ctx
->receivedDataBuffer
.data
)
203 { count
= ctx
->receivedDataBuffer
.length
- ctx
->receivedDataPos
;
206 memcpy(data
, ctx
->receivedDataBuffer
.data
+ ctx
->receivedDataPos
, count
);
210 ctx
->receivedDataPos
+= count
;
213 assert(ctx
->receivedDataPos
<= ctx
->receivedDataBuffer
.length
);
214 assert(*processed
+ remaining
== bufSize
);
215 assert(charPtr
== ((UInt8
*)data
) + *processed
);
217 if (ctx
->receivedDataBuffer
.data
!= 0 &&
218 ctx
->receivedDataPos
>= ctx
->receivedDataBuffer
.length
)
219 { SSLFreeBuffer(ctx
->receivedDataBuffer
, ctx
);
220 ctx
->receivedDataBuffer
.data
= 0;
221 ctx
->receivedDataPos
= 0;
225 * This while statement causes a hang when using nonblocking low-level I/O!
226 while (remaining > 0 && ctx->state != SSL_HdskStateGracefulClose)
227 ..what we really have to do is just return as soon as we read one
228 record. A performance hit in the nonblocking case, but that is
229 the only way this code can work in both modes...
231 if (remaining
> 0 && ctx
->state
!= SSL_HdskStateGracefulClose
)
232 { assert(ctx
->receivedDataBuffer
.data
== 0);
233 if ((err
= SSLReadRecord(rec
, ctx
)) != 0) {
236 if (rec
.contentType
== SSL_RecordTypeAppData
||
237 rec
.contentType
== SSL_RecordTypeV2_0
)
238 { if (rec
.contents
.length
<= remaining
)
239 { memcpy(charPtr
, rec
.contents
.data
, rec
.contents
.length
);
240 remaining
-= rec
.contents
.length
;
241 charPtr
+= rec
.contents
.length
;
242 *processed
+= rec
.contents
.length
;
245 * if ((err = SSLFreeBuffer(rec.contents, ctx)) != 0)
246 * passes the address of rec to SSLFreeBuffer, not the address
247 * of the contents field (which should be offset 8 from the start
251 SSLBuffer
*b
= &rec
.contents
;
252 if ((err
= SSLFreeBuffer(*b
, ctx
)) != 0) {
258 { memcpy(charPtr
, rec
.contents
.data
, remaining
);
259 charPtr
+= remaining
;
260 *processed
+= remaining
;
261 ctx
->receivedDataBuffer
= rec
.contents
;
262 ctx
->receivedDataPos
= remaining
;
267 if ((err
= SSLProcessProtocolMessage(rec
, ctx
)) != 0) {
270 if ((err
= SSLFreeBuffer(rec
.contents
, ctx
)) != 0) {
279 /* shut down on serious errors */
282 case errSSLWouldBlock
:
283 case errSSLClosedGraceful
:
284 case errSSLClosedNoNotify
:
287 sslErrorLog("SSLRead: going to state errorClose due to err %d\n",
289 SSLChangeHdskState(ctx
, SSL_HdskStateErrorClose
);
293 sslIoTrace("SSLRead ", dataLength
, *processed
, err
);
298 #include "appleCdsa.h"
302 SSLHandshake(SSLContext
*ctx
)
309 if (ctx
->state
== SSL_HdskStateGracefulClose
)
310 return errSSLClosedGraceful
;
311 if (ctx
->state
== SSL_HdskStateErrorClose
)
312 return errSSLClosedAbort
;
314 if(ctx
->protocolSide
== SSL_ServerSide
) {
315 /* some things the caller really has to have done by now... */
316 if((ctx
->localCert
== NULL
) ||
317 (ctx
->signingPrivKey
== NULL
) ||
318 (ctx
->signingPubKey
== NULL
) ||
319 (ctx
->signingKeyCsp
== 0)) {
320 sslErrorLog("SSLHandshake: insufficient init\n");
324 if(ctx
->validCipherSpecs
== NULL
) {
325 /* build list of legal cipherSpecs */
326 err
= sslBuildCipherSpecArray(ctx
);
332 while (ctx
->readCipher
.ready
== 0 || ctx
->writeCipher
.ready
== 0)
333 { if ((err
= SSLHandshakeProceed(ctx
)) != 0)
337 /* one more flush at completion of successful handshake */
338 if ((err
= SSLServiceWriteQueue(ctx
)) != 0) {
346 SSLHandshakeProceed(SSLContext
*ctx
)
350 if (ctx
->state
== SSL_HdskStateUninit
)
351 if ((err
= SSLInitConnection(ctx
)) != 0)
353 if ((err
= SSLServiceWriteQueue(ctx
)) != 0)
355 assert(ctx
->readCipher
.ready
== 0);
356 if ((err
= SSLReadRecord(rec
, ctx
)) != 0)
358 if ((err
= SSLProcessProtocolMessage(rec
, ctx
)) != 0)
359 { SSLFreeBuffer(rec
.contents
, ctx
);
362 if ((err
= SSLFreeBuffer(rec
.contents
, ctx
)) != 0)
369 SSLInitConnection(SSLContext
*ctx
)
372 if (ctx
->protocolSide
== SSL_ClientSide
) {
373 SSLChangeHdskState(ctx
, SSL_HdskStateClientUninit
);
376 { assert(ctx
->protocolSide
== SSL_ServerSide
);
377 SSLChangeHdskState(ctx
, SSL_HdskStateServerUninit
);
380 if (ctx
->peerID
.data
!= 0)
381 { SSLGetSessionData(&ctx
->resumableSession
, ctx
);
382 /* Ignore errors; just treat as uncached session */
386 * If we have a cached resumable session, blow it off if it's a higher
387 * version than the max currently allowed. Note that this means that once
388 * a process negotiates a given version with a given server/port, it won't
389 * be able to negotiate a higher version. We might want to revisit this.
391 if (ctx
->resumableSession
.data
!= 0) {
393 SSLProtocolVersion savedVersion
;
395 if ((err
= SSLRetrieveSessionProtocolVersion(ctx
->resumableSession
,
396 &savedVersion
, ctx
)) != 0) {
399 if(savedVersion
> ctx
->maxProtocolVersion
) {
400 sslLogResumSessDebug("===Resumable session protocol mismatch");
401 SSLFreeBuffer(ctx
->resumableSession
, ctx
);
404 sslLogResumSessDebug("===attempting to resume session");
406 * A bit of a special case for server side here. If currently
407 * configged to allow for SSL3/TLS1 with an SSL2 hello, we
408 * don't want to preclude the possiblity of an SSL2 hello...
409 * so we'll just leave the negProtocolVersion alone in the server case.
411 if(ctx
->protocolSide
== SSL_ClientSide
) {
412 ctx
->negProtocolVersion
= savedVersion
;
418 * If we're the client & handshake hasn't yet begun, start it by
419 * pretending we just received a hello request
421 if (ctx
->state
== SSL_HdskStateClientUninit
&& ctx
->writeCipher
.ready
== 0)
422 { switch (ctx
->negProtocolVersion
)
423 { case SSL_Version_Undetermined
:
424 case SSL_Version_3_0_With_2_0_Hello
:
425 case SSL_Version_2_0
:
426 if ((err
= SSL2AdvanceHandshake(
427 SSL2_MsgKickstart
, ctx
)) != 0)
430 case SSL_Version_3_0_Only
:
431 case SSL_Version_3_0
:
432 case TLS_Version_1_0_Only
:
433 case TLS_Version_1_0
:
434 if ((err
= SSLAdvanceHandshake(SSL_HdskHelloRequest
, ctx
)) != 0)
438 sslErrorLog("Bad protocol version\n");
439 return errSSLInternal
;
447 SSLServiceWriteQueue(SSLContext
*ctx
)
448 { OSStatus err
= noErr
, werr
= noErr
;
450 SSLBuffer buf
, recBuf
;
453 while (!werr
&& ((rec
= ctx
->recordWriteQueue
) != 0))
454 { buf
.data
= rec
->data
.data
+ rec
->sent
;
455 buf
.length
= rec
->data
.length
- rec
->sent
;
456 werr
= sslIoWrite(buf
, &written
, ctx
);
457 rec
->sent
+= written
;
458 if (rec
->sent
>= rec
->data
.length
)
459 { assert(rec
->sent
== rec
->data
.length
);
461 err
= SSLFreeBuffer(rec
->data
, ctx
);
463 recBuf
.data
= (UInt8
*)rec
;
464 recBuf
.length
= sizeof(WaitingRecord
);
465 ctx
->recordWriteQueue
= rec
->next
;
466 err
= SSLFreeBuffer(recBuf
, ctx
);
471 assert(ctx
->recordWriteQueue
== 0 || ctx
->recordWriteQueue
->sent
== 0);
478 SSLProcessProtocolMessage(SSLRecord
&rec
, SSLContext
*ctx
)
481 switch (rec
.contentType
)
482 { case SSL_RecordTypeHandshake
:
483 sslLogRxProtocolDebug("Handshake");
484 err
= SSLProcessHandshakeRecord(rec
, ctx
);
486 case SSL_RecordTypeAlert
:
487 sslLogRxProtocolDebug("Alert");
488 err
= SSLProcessAlert(rec
, ctx
);
490 case SSL_RecordTypeChangeCipher
:
491 sslLogRxProtocolDebug("ChangeCipher");
492 err
= SSLProcessChangeCipherSpec(rec
, ctx
);
494 case SSL_RecordTypeV2_0
:
495 sslLogRxProtocolDebug("RecordTypeV2_0");
496 err
= SSL2ProcessMessage(rec
, ctx
);
499 sslLogRxProtocolDebug("Bad msg");
500 return errSSLProtocol
;
507 SSLClose(SSLContext
*ctx
)
509 OSStatus err
= noErr
;
514 if (ctx
->negProtocolVersion
>= SSL_Version_3_0
)
515 err
= SSLSendAlert(SSL_AlertLevelWarning
, SSL_AlertCloseNotify
, ctx
);
517 err
= SSLServiceWriteQueue(ctx
);
518 SSLChangeHdskState(ctx
, SSL_HdskStateGracefulClose
);
520 err
= noErr
; /* Ignore errors related to closed streams */
525 * Determine how much data the client can be guaranteed to
526 * obtain via SSLRead() without blocking or causing any low-level
527 * read operations to occur.
529 * Implemented here because the relevant info in SSLContext (receivedDataBuffer
530 * and receivedDataPos) are only used in this file.
533 SSLGetBufferedReadSize(SSLContextRef ctx
,
534 size_t *bufSize
) /* RETURNED */
539 if(ctx
->receivedDataBuffer
.data
== NULL
) {
543 assert(ctx
->receivedDataBuffer
.length
>= ctx
->receivedDataPos
);
544 *bufSize
= ctx
->receivedDataBuffer
.length
- ctx
->receivedDataPos
;