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.
20 // securetransport++ - C++ interface to Apple's Secure Transport layer
22 #include "securetransport++.h"
23 #include <Security/debugging.h>
27 namespace IPPlusPlus
{
31 // Construct a core object.
32 // This creates the Context object and sets the I/O functions.
34 SecureTransportCore::SecureTransportCore() : mAtEnd(false)
36 MacOSError::check(SSLNewContext(false, &mContext
));
38 MacOSError::check(SSLSetIOFuncs(mContext
, sslReadFunc
, sslWriteFunc
));
39 MacOSError::check(SSLSetConnection(mContext
, this));
40 debug("ssl", "%p constructed", this);
42 SSLDisposeContext(mContext
);
49 // On destruction, we force a close and destroy the Context.
51 SecureTransportCore::~SecureTransportCore()
53 SSLDisposeContext(mContext
); // ignore error (can't do anything if error)
54 debug("ssl", "%p destroyed", this);
59 // Open initiates or continues the SSL handshake.
60 // In nonblocking mode, open may return while handshake is still in
61 // Progress. Keep calling open until state() != errSSLWouldBlock, or
62 // go directly to I/O.
64 void SecureTransportCore::open()
66 switch (OSStatus err
= SSLHandshake(mContext
)) {
68 case errSSLWouldBlock
:
69 debug("ssl", "%p open, state=%d", this, state());
72 MacOSError::throwMe(err
);
78 // Close the SSL layer if needed.
79 // Note that this does nothing to the underlying I/O layer.
81 void SecureTransportCore::close()
86 debug("ssl", "%p closed", this);
96 // Read bytes from the SSL layer. This is the standard FileDescoid
98 // Note that if the connection is still handshaking, handshake will proceed
99 // and no bytes will be read (yet).
101 size_t SecureTransportCore::read(void *data
, size_t length
)
103 if (continueHandshake())
106 switch (OSStatus err
= SSLRead(mContext
, data
, length
, &bytesRead
)) {
107 case noErr
: // full read
108 case errSSLWouldBlock
: // partial read
109 return bytesRead
; // (may be zero in non-blocking scenarios)
110 case errSSLClosedGraceful
: // means end-of-data, but we may still return some
111 case errSSLClosedNoNotify
: // peer closed abruptly (not sending SSL layer shutdown)
113 mAtEnd
= true; // no more data - set final end-of-data flag
116 MacOSError::throwMe(err
);
122 // Write bytes to the SSL layer. This is the standard FileDescoid write function.
123 // Note that if the connection is still handshaking, handshake will proceed
124 // and no bytes will be written (yet).
126 size_t SecureTransportCore::write(const void *data
, size_t length
)
128 if (continueHandshake())
131 switch (OSStatus err
= SSLWrite(mContext
, data
, length
, &bytesWritten
)) {
134 case errSSLWouldBlock
:
135 return 0; // no data, no error, no fuss
137 MacOSError::throwMe(err
);
143 // Continue handshake processing if necessary.
144 // Returns true if handshake is in Progress and not yet complete.
146 bool SecureTransportCore::continueHandshake()
148 if (state() == kSSLHandshake
) {
149 // still in handshake mode; prod it along
150 debug("ssl", "%p continuing handshake", this);
151 switch (OSStatus err
= SSLHandshake(mContext
)) {
153 case errSSLWouldBlock
:
156 MacOSError::throwMe(err
);
158 IFDEBUG(if (state() != kSSLHandshake
) debug("ssl", "%p handshake complete", this));
159 return state() == kSSLHandshake
;
166 // State access methods
168 SSLSessionState
SecureTransportCore::state() const
170 SSLSessionState state
;
171 MacOSError::check(SSLGetSessionState(mContext
, &state
));
175 SSLProtocol
SecureTransportCore::version() const
178 MacOSError::check(SSLGetProtocolVersion(mContext
, &version
));
182 void SecureTransportCore::version(SSLProtocol version
)
184 MacOSError::check(SSLSetProtocolVersion(mContext
, version
));
187 UInt32
SecureTransportCore::numSupportedCiphers() const
190 MacOSError::check(SSLGetNumberSupportedCiphers(mContext
, &numCiphers
));
194 void SecureTransportCore::supportedCiphers(
195 SSLCipherSuite
*ciphers
,
196 UInt32
&numCiphers
) const
198 MacOSError::check(SSLGetSupportedCiphers(mContext
, ciphers
, &numCiphers
));
201 UInt32
SecureTransportCore::numEnabledCiphers() const
204 MacOSError::check(SSLGetNumberEnabledCiphers(mContext
, &numCiphers
));
208 void SecureTransportCore::enabledCiphers(
209 SSLCipherSuite
*ciphers
,
210 UInt32
&numCiphers
) const
212 MacOSError::check(SSLGetEnabledCiphers(mContext
, ciphers
, &numCiphers
));
215 void SecureTransportCore::enabledCiphers(
216 SSLCipherSuite
*ciphers
,
219 MacOSError::check(SSLSetEnabledCiphers(mContext
, ciphers
, numCiphers
));
222 bool SecureTransportCore::allowsExpiredCerts() const
225 MacOSError::check(SSLGetAllowsExpiredCerts(mContext
, &allow
));
229 void SecureTransportCore::allowsExpiredCerts(bool allow
)
231 MacOSError::check(SSLSetAllowsExpiredCerts(mContext
, allow
));
234 bool SecureTransportCore::allowsUnknownRoots() const
237 MacOSError::check(SSLGetAllowsAnyRoot(mContext
, &allow
));
241 void SecureTransportCore::allowsUnknownRoots(bool allow
)
243 MacOSError::check(SSLSetAllowsAnyRoot(mContext
, allow
));
246 void SecureTransportCore::peerId(const void *id
, size_t length
)
248 MacOSError::check(SSLSetPeerID(mContext
, id
, length
));
253 // Implement SecureTransport's read/write transport functions.
254 // Note that this API is very un-UNIX in that error codes (errSSLClosedGraceful, errSSLWouldBlock)
255 // are returned even though data has been produced.
257 OSStatus
SecureTransportCore::sslReadFunc(SSLConnectionRef connection
,
258 void *data
, UInt32
*length
)
260 const SecureTransportCore
*stc
= reinterpret_cast<const SecureTransportCore
*>(connection
);
262 size_t lengthRequested
= *length
;
263 *length
= stc
->ioRead(data
, lengthRequested
);
264 debug("sslconio", "%p read %ld of %ld bytes", stc
, *length
, lengthRequested
);
265 if (*length
== lengthRequested
) // full deck
267 else if (stc
->ioAtEnd()) {
268 debug("sslconio", "%p end of source input, returning %ld bytes",
270 return errSSLClosedGraceful
;
272 return errSSLWouldBlock
;
273 } catch (const UnixError
&err
) {
275 if (err
.error
== ECONNRESET
)
276 return errSSLClosedGraceful
;
278 } catch (const CssmCommonError
&err
) {
280 return err
.osStatus();
283 return -1; //@@@ generic internal error?
287 OSStatus
SecureTransportCore::sslWriteFunc(SSLConnectionRef connection
,
288 const void *data
, UInt32
*length
)
290 const SecureTransportCore
*stc
= reinterpret_cast<const SecureTransportCore
*>(connection
);
292 size_t lengthRequested
= *length
;
293 *length
= stc
->ioWrite(data
, lengthRequested
);
294 debug("sslconio", "%p wrote %ld of %ld bytes", stc
, *length
, lengthRequested
);
295 return *length
== lengthRequested
? OSStatus(noErr
) : OSStatus(errSSLWouldBlock
);
296 } catch (const CssmCommonError
&err
) {
298 return err
.osStatus();
301 return -1; //@@@ generic internal error?
306 } // end namespace IPPlusPlus
307 } // end namespace Security