]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_ssl/lib/securetransport++.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_ssl / lib / securetransport++.cpp
diff --git a/libsecurity_ssl/lib/securetransport++.cpp b/libsecurity_ssl/lib/securetransport++.cpp
new file mode 100644 (file)
index 0000000..cb6b51a
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2000-2001,2005-2007,2010-2012 Apple Inc. All Rights Reserved.
+ *
+ * The contents of this file constitute Original Code as defined in and are
+ * subject to the Apple Public Source License Version 1.2 (the 'License').
+ * You may not use this file except in compliance with the License. Please obtain
+ * a copy of the License at http://www.apple.com/publicsource and read it before
+ * using this file.
+ *
+ * This Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
+ * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
+ * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
+ * specific language governing rights and limitations under the License.
+ */
+
+
+//
+// securetransport++ - C++ interface to Apple's Secure Transport layer
+//
+#include "securetransport++.h"
+#include <security_utilities/debugging.h>
+
+
+namespace Security {
+namespace IPPlusPlus {
+
+
+//
+// Construct a core object.
+// This creates the Context object and sets the I/O functions.
+//
+SecureTransportCore::SecureTransportCore() : mAtEnd(false)
+{
+    MacOSError::check(SSLNewContext(false, &mContext));
+    try {
+       MacOSError::check(SSLSetIOFuncs(mContext, sslReadFunc, sslWriteFunc));
+        MacOSError::check(SSLSetConnection(mContext, this));
+        secdebug("ssl", "%p constructed", this);
+    } catch (...) {
+        SSLDisposeContext(mContext);
+        throw;
+    }
+}
+
+
+//
+// On destruction, we force a close and destroy the Context.
+//
+SecureTransportCore::~SecureTransportCore()
+{
+    SSLDisposeContext(mContext);       // ignore error (can't do anything if error)
+    secdebug("ssl", "%p destroyed", this);
+}
+
+
+//
+// Open initiates or continues the SSL handshake.
+// In nonblocking mode, open may return while handshake is still in
+// Progress. Keep calling open until state() != errSSLWouldBlock, or
+// go directly to I/O.
+//
+void SecureTransportCore::open()
+{
+    switch (OSStatus err = SSLHandshake(mContext)) {
+    case noErr:
+    case errSSLWouldBlock:
+        secdebug("ssl", "%p open, state=%d", this, state());
+        return;
+    default:
+        MacOSError::throwMe(err);
+    }
+}
+
+
+//
+// Close the SSL layer if needed.
+// Note that this does nothing to the underlying I/O layer.
+//
+void SecureTransportCore::close()
+{
+    switch (state()) {
+    case kSSLHandshake:
+    case kSSLConnected:
+        secdebug("ssl", "%p closed", this);
+        SSLClose(mContext);
+        break;
+    default:
+        break;
+    }
+}
+
+
+//
+// Read bytes from the SSL layer. This is the standard FileDescoid
+// read function.
+// Note that if the connection is still handshaking, handshake will proceed
+// and no bytes will be read (yet).
+//
+size_t SecureTransportCore::read(void *data, size_t length)
+{
+    if (continueHandshake())
+        return 0;
+    size_t bytesRead;
+    switch (OSStatus err = SSLRead(mContext, data, length, &bytesRead)) {
+    case noErr:                                        // full read
+    case errSSLWouldBlock:             // partial read
+        return bytesRead;              // (may be zero in non-blocking scenarios)
+    case errSSLClosedGraceful: // means end-of-data, but we may still return some
+    case errSSLClosedNoNotify: // peer closed abruptly (not sending SSL layer shutdown)
+        if (bytesRead == 0)
+            mAtEnd = true;             // no more data - set final end-of-data flag
+        return bytesRead;
+    default:
+        MacOSError::throwMe(err);
+    }
+}
+
+
+//
+// Write bytes to the SSL layer. This is the standard FileDescoid write function.
+// Note that if the connection is still handshaking, handshake will proceed
+// and no bytes will be written (yet).
+//
+size_t SecureTransportCore::write(const void *data, size_t length)
+{
+    if (continueHandshake())
+        return 0;
+    size_t bytesWritten;
+    switch (OSStatus err = SSLWrite(mContext, data, length, &bytesWritten)) {
+    case noErr:
+        return bytesWritten;
+    case errSSLWouldBlock:
+        return 0;      // no data, no error, no fuss
+    default:
+        MacOSError::throwMe(err);
+    }
+}
+
+
+//
+// Continue handshake processing if necessary.
+// Returns true if handshake is in Progress and not yet complete.
+//
+bool SecureTransportCore::continueHandshake()
+{
+    if (state() == kSSLHandshake) {
+        // still in handshake mode; prod it along
+        secdebug("ssl", "%p continuing handshake", this);
+        switch (OSStatus err = SSLHandshake(mContext)) {
+        case noErr:
+        case errSSLWouldBlock:
+            break;
+        default:
+            MacOSError::throwMe(err);
+        }
+        IFDEBUG(if (state() != kSSLHandshake) secdebug("ssl", "%p handshake complete", this));
+        return state() == kSSLHandshake;
+    } else
+        return false;
+}
+
+
+//
+// State access methods
+//
+SSLSessionState SecureTransportCore::state() const
+{
+    SSLSessionState state;
+    MacOSError::check(SSLGetSessionState(mContext, &state));
+    return state;
+}
+
+SSLProtocol SecureTransportCore::version() const
+{
+    SSLProtocol version;
+    MacOSError::check(SSLGetProtocolVersion(mContext, &version));
+    return version;
+}
+
+void SecureTransportCore::version(SSLProtocol version)
+{
+    MacOSError::check(SSLSetProtocolVersion(mContext, version));
+}
+
+size_t SecureTransportCore::numSupportedCiphers() const
+{
+       size_t numCiphers;
+    MacOSError::check(SSLGetNumberSupportedCiphers(mContext, &numCiphers));
+    return numCiphers;
+}
+
+void SecureTransportCore::supportedCiphers(
+       SSLCipherSuite *ciphers,
+       size_t &numCiphers) const
+{
+    MacOSError::check(SSLGetSupportedCiphers(mContext, ciphers, &numCiphers));
+}
+
+size_t SecureTransportCore::numEnabledCiphers() const
+{
+       size_t numCiphers;
+    MacOSError::check(SSLGetNumberEnabledCiphers(mContext, &numCiphers));
+    return numCiphers;
+}
+
+void SecureTransportCore::enabledCiphers(
+       SSLCipherSuite *ciphers,
+       size_t &numCiphers) const
+{
+    MacOSError::check(SSLGetEnabledCiphers(mContext, ciphers, &numCiphers));
+}
+
+void SecureTransportCore::enabledCiphers(
+       SSLCipherSuite *ciphers,
+       size_t numCiphers)
+{
+    MacOSError::check(SSLSetEnabledCiphers(mContext, ciphers, numCiphers));
+}
+
+bool SecureTransportCore::allowsExpiredCerts() const
+{
+    Boolean allow;
+    MacOSError::check(SSLGetAllowsExpiredCerts(mContext, &allow));
+    return allow;
+}
+
+void SecureTransportCore::allowsExpiredCerts(bool allow)
+{
+    MacOSError::check(SSLSetAllowsExpiredCerts(mContext, allow));
+}
+
+bool SecureTransportCore::allowsUnknownRoots() const
+{
+    Boolean allow;
+    MacOSError::check(SSLGetAllowsAnyRoot(mContext, &allow));
+    return allow;
+}
+
+void SecureTransportCore::allowsUnknownRoots(bool allow)
+{
+    MacOSError::check(SSLSetAllowsAnyRoot(mContext, allow));
+}
+
+void SecureTransportCore::peerId(const void *id, size_t length)
+{
+    MacOSError::check(SSLSetPeerID(mContext, id, length));
+}
+
+
+//
+// Implement SecureTransport's read/write transport functions.
+// Note that this API is very un-UNIX in that error codes (errSSLClosedGraceful, errSSLWouldBlock)
+// are returned even though data has been produced.
+//
+OSStatus SecureTransportCore::sslReadFunc(SSLConnectionRef connection,
+    void *data, size_t *length)
+{
+    const SecureTransportCore *stc = reinterpret_cast<const SecureTransportCore *>(connection);
+    try {
+        size_t lengthRequested = *length;
+        *length = stc->ioRead(data, lengthRequested);
+        secdebug("sslconio", "%p read %lu of %lu bytes", stc, *length, lengthRequested);
+        if (*length == lengthRequested)        // full deck
+            return noErr;
+        else if (stc->ioAtEnd()) {
+            secdebug("sslconio", "%p end of source input, returning %lu bytes",
+                stc, *length);
+            return errSSLClosedGraceful;
+        } else
+            return errSSLWouldBlock;
+    } catch (const UnixError &err) {
+        *length = 0;
+        if (err.error == ECONNRESET)
+            return errSSLClosedGraceful;
+        throw;
+    } catch (const CommonError &err) {
+        *length = 0;
+        return err.osStatus();
+    } catch (...) {
+        *length = 0;
+        return -1;     //@@@ generic internal error?
+    }
+}
+
+OSStatus SecureTransportCore::sslWriteFunc(SSLConnectionRef connection,
+    const void *data, size_t *length)
+{
+    const SecureTransportCore *stc = reinterpret_cast<const SecureTransportCore *>(connection);
+    try {
+        size_t lengthRequested = *length;
+        *length = stc->ioWrite(data, lengthRequested);
+        secdebug("sslconio", "%p wrote %lu of %lu bytes", stc, *length, lengthRequested);
+        return *length == lengthRequested ? OSStatus(noErr) : OSStatus(errSSLWouldBlock);
+    } catch (const CommonError &err) {
+        *length = 0;
+        return err.osStatus();
+    } catch (...) {
+        *length = 0;
+        return -1;     //@@@ generic internal error?
+    }
+}
+
+
+}      // end namespace IPPlusPlus
+}      // end namespace Security