1/*
2 * Copyright (c) 2000-2001,2005-2007,2010-2012 Apple Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19//
20// securetransport++ - C++ interface to Apple's Secure Transport layer
21//
22#include "securetransport++.h"
23#include <security_utilities/debugging.h>
24
25
26namespace Security {
27namespace IPPlusPlus {
28
29
30//
31// Construct a core object.
32// This creates the Context object and sets the I/O functions.
33//
34SecureTransportCore::SecureTransportCore() : mAtEnd(false)
35{
36    MacOSError::check(SSLNewContext(false, &mContext));
37    try {
38    	MacOSError::check(SSLSetIOFuncs(mContext, sslReadFunc, sslWriteFunc));
39        MacOSError::check(SSLSetConnection(mContext, this));
40        secdebug("ssl", "%p constructed", this);
41    } catch (...) {
42        SSLDisposeContext(mContext);
43        throw;
44    }
45}
46
47
48//
49// On destruction, we force a close and destroy the Context.
50//
51SecureTransportCore::~SecureTransportCore()
52{
53    SSLDisposeContext(mContext);	// ignore error (can't do anything if error)
54    secdebug("ssl", "%p destroyed", this);
55}
56
57
58//
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.
63//
64void SecureTransportCore::open()
65{
66    switch (OSStatus err = SSLHandshake(mContext)) {
67    case errSecSuccess:
68    case errSSLWouldBlock:
69        secdebug("ssl", "%p open, state=%d", this, state());
70        return;
71    default:
72        MacOSError::throwMe(err);
73    }
74}
75
76
77//
78// Close the SSL layer if needed.
79// Note that this does nothing to the underlying I/O layer.
80//
81void SecureTransportCore::close()
82{
83    switch (state()) {
84    case kSSLHandshake:
85    case kSSLConnected:
86        secdebug("ssl", "%p closed", this);
87        SSLClose(mContext);
88        break;
89    default:
90        break;
91    }
92}
93
94
95//
96// Read bytes from the SSL layer. This is the standard FileDescoid
97// read function.
98// Note that if the connection is still handshaking, handshake will proceed
99// and no bytes will be read (yet).
100//
101size_t SecureTransportCore::read(void *data, size_t length)
102{
103    if (continueHandshake())
104        return 0;
105    size_t bytesRead;
106    switch (OSStatus err = SSLRead(mContext, data, length, &bytesRead)) {
107    case errSecSuccess:					// 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)
112        if (bytesRead == 0)
113            mAtEnd = true;		// no more data - set final end-of-data flag
114        return bytesRead;
115    default:
116        MacOSError::throwMe(err);
117    }
118}
119
120
121//
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).
125//
126size_t SecureTransportCore::write(const void *data, size_t length)
127{
128    if (continueHandshake())
129        return 0;
130    size_t bytesWritten;
131    switch (OSStatus err = SSLWrite(mContext, data, length, &bytesWritten)) {
132    case errSecSuccess:
133        return bytesWritten;
134    case errSSLWouldBlock:
135        return 0;	// no data, no error, no fuss
136    default:
137        MacOSError::throwMe(err);
138    }
139}
140
141
142//
143// Continue handshake processing if necessary.
144// Returns true if handshake is in Progress and not yet complete.
145//
146bool SecureTransportCore::continueHandshake()
147{
148    if (state() == kSSLHandshake) {
149        // still in handshake mode; prod it along
150        secdebug("ssl", "%p continuing handshake", this);
151        switch (OSStatus err = SSLHandshake(mContext)) {
152        case errSecSuccess:
153        case errSSLWouldBlock:
154            break;
155        default:
156            MacOSError::throwMe(err);
157        }
158        IFDEBUG(if (state() != kSSLHandshake) secdebug("ssl", "%p handshake complete", this));
159        return state() == kSSLHandshake;
160    } else
161        return false;
162}
163
164
165//
166// State access methods
167//
168SSLSessionState SecureTransportCore::state() const
169{
170    SSLSessionState state;
171    MacOSError::check(SSLGetSessionState(mContext, &state));
172    return state;
173}
174
175SSLProtocol SecureTransportCore::version() const
176{
177    SSLProtocol version;
178    MacOSError::check(SSLGetProtocolVersion(mContext, &version));
179    return version;
180}
181
182void SecureTransportCore::version(SSLProtocol version)
183{
184    MacOSError::check(SSLSetProtocolVersion(mContext, version));
185}
186
187size_t SecureTransportCore::numSupportedCiphers() const
188{
189	size_t numCiphers;
190    MacOSError::check(SSLGetNumberSupportedCiphers(mContext, &numCiphers));
191    return numCiphers;
192}
193
194void SecureTransportCore::supportedCiphers(
195	SSLCipherSuite *ciphers,
196	size_t &numCiphers) const
197{
198    MacOSError::check(SSLGetSupportedCiphers(mContext, ciphers, &numCiphers));
199}
200
201size_t SecureTransportCore::numEnabledCiphers() const
202{
203	size_t numCiphers;
204    MacOSError::check(SSLGetNumberEnabledCiphers(mContext, &numCiphers));
205    return numCiphers;
206}
207
208void SecureTransportCore::enabledCiphers(
209	SSLCipherSuite *ciphers,
210	size_t &numCiphers) const
211{
212    MacOSError::check(SSLGetEnabledCiphers(mContext, ciphers, &numCiphers));
213}
214
215void SecureTransportCore::enabledCiphers(
216	SSLCipherSuite *ciphers,
217	size_t numCiphers)
218{
219    MacOSError::check(SSLSetEnabledCiphers(mContext, ciphers, numCiphers));
220}
221
222bool SecureTransportCore::allowsExpiredCerts() const
223{
224    Boolean allow;
225    MacOSError::check(SSLGetAllowsExpiredCerts(mContext, &allow));
226    return allow;
227}
228
229void SecureTransportCore::allowsExpiredCerts(bool allow)
230{
231    MacOSError::check(SSLSetAllowsExpiredCerts(mContext, allow));
232}
233
234bool SecureTransportCore::allowsUnknownRoots() const
235{
236    Boolean allow;
237    MacOSError::check(SSLGetAllowsAnyRoot(mContext, &allow));
238    return allow;
239}
240
241void SecureTransportCore::allowsUnknownRoots(bool allow)
242{
243    MacOSError::check(SSLSetAllowsAnyRoot(mContext, allow));
244}
245
246void SecureTransportCore::peerId(const void *id, size_t length)
247{
248    MacOSError::check(SSLSetPeerID(mContext, id, length));
249}
250
251
252//
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.
256//
257OSStatus SecureTransportCore::sslReadFunc(SSLConnectionRef connection,
258    void *data, size_t *length)
259{
260    const SecureTransportCore *stc = reinterpret_cast<const SecureTransportCore *>(connection);
261    try {
262        size_t lengthRequested = *length;
263        *length = stc->ioRead(data, lengthRequested);
264        secdebug("sslconio", "%p read %lu of %lu bytes", stc, *length, lengthRequested);
265        if (*length == lengthRequested)	// full deck
266            return errSecSuccess;
267        else if (stc->ioAtEnd()) {
268            secdebug("sslconio", "%p end of source input, returning %lu bytes",
269                stc, *length);
270            return errSSLClosedGraceful;
271        } else
272            return errSSLWouldBlock;
273    } catch (const UnixError &err) {
274        *length = 0;
275        if (err.error == ECONNRESET)
276            return errSSLClosedGraceful;
277        throw;
278    } catch (const CommonError &err) {
279        *length = 0;
280        return err.osStatus();
281    } catch (...) {
282        *length = 0;
283        return -1;	//@@@ generic internal error?
284    }
285}
286
287OSStatus SecureTransportCore::sslWriteFunc(SSLConnectionRef connection,
288    const void *data, size_t *length)
289{
290    const SecureTransportCore *stc = reinterpret_cast<const SecureTransportCore *>(connection);
291    try {
292        size_t lengthRequested = *length;
293        *length = stc->ioWrite(data, lengthRequested);
294        secdebug("sslconio", "%p wrote %lu of %lu bytes", stc, *length, lengthRequested);
295        return *length == lengthRequested ? OSStatus(errSecSuccess) : OSStatus(errSSLWouldBlock);
296    } catch (const CommonError &err) {
297        *length = 0;
298        return err.osStatus();
299    } catch (...) {
300        *length = 0;
301        return -1;	//@@@ generic internal error?
302    }
303}
304
305
306}	// end namespace IPPlusPlus
307}	// end namespace Security
308