darwinssl: add support for native Mac OS X/iOS SSL
authorNick Zitzmann <nick@chronosnet.com>
Tue, 26 Jun 2012 12:01:51 +0000 (14:01 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 26 Jun 2012 12:04:15 +0000 (14:04 +0200)
configure.ac
lib/Makefile.inc
lib/curl_darwinssl.c [new file with mode: 0644]
lib/curl_darwinssl.h [new file with mode: 0644]
lib/setup.h
lib/sslgen.c
lib/urldata.h

index 1774b3a..3c792cd 100644 (file)
@@ -1371,6 +1371,29 @@ else
   AC_MSG_RESULT(no)
 fi
 
+OPT_DARWINSSL=no
+AC_ARG_WITH(darwinssl,dnl
+AC_HELP_STRING([--with-darwinssl],[enable iOS/Mac OS X native SSL/TLS])
+AC_HELP_STRING([--without-darwinssl], [disable iOS/Mac OS X native SSL/TLS]),
+  OPT_DARWINSSL=$withval)
+
+AC_MSG_CHECKING([whether to enable iOS/Mac OS X native SSL/TLS])
+if test "$curl_ssl_msg" = "$init_ssl_msg"; then
+  if test "x$OPT_DARWINSSL" != "xno" &&
+     test -d "/System/Library/Frameworks/Security.framework"; then
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(USE_DARWINSSL, 1, [to enable iOS/Mac OS X native SSL/TLS support])
+    AC_SUBST(USE_DARWINSSL, [1])
+    curl_ssl_msg="enabled (iOS/Mac OS X-native)"
+    DARWINSSL_ENABLED=1
+    LDFLAGS="$LDFLAGS -framework CoreFoundation -framework Security"
+  else
+    AC_MSG_RESULT(no)
+  fi
+else
+  AC_MSG_RESULT(no)
+fi
+
 dnl **********************************************************************
 dnl Check for the presence of SSL libraries and headers
 dnl **********************************************************************
@@ -2233,7 +2256,7 @@ if test "$curl_ssl_msg" = "$init_ssl_msg"; then
   fi
 fi
 
-if test "x$OPENSSL_ENABLED$GNUTLS_ENABLED$NSS_ENABLED$POLARSSL_ENABLED$AXTLS_ENABLED$CYASSL_ENABLED$WINSSL_ENABLED" = "x"; then
+if test "x$OPENSSL_ENABLED$GNUTLS_ENABLED$NSS_ENABLED$POLARSSL_ENABLED$AXTLS_ENABLED$CYASSL_ENABLED$WINSSL_ENABLED$DARWINSSL_ENABLED" = "x"; then
   AC_MSG_WARN([SSL disabled, you will not be able to use HTTPS, FTPS, NTLM and more.])
   AC_MSG_WARN([Use --with-ssl, --with-gnutls, --with-polarssl, --with-cyassl, --with-nss, --with-axtls or --with-winssl to address this.])
 else
@@ -2510,6 +2533,8 @@ AC_HELP_STRING([--disable-versioned-symbols], [Disable versioned symbols in shar
           versioned_symbols_flavour="AXTLS_"
         elif test "x$WINSSL_ENABLED" == "x1"; then
           versioned_symbols_flavour="WINSSL_"
+        elif test "x$DARWINSSL_ENABLED" == "x1"; then
+          versioned_symbols_flavour="DARWINSSL_"
         else
           versioned_symbols_flavour=""
         fi
index 4e36dc6..e0044ec 100644 (file)
@@ -24,7 +24,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c   \
   idn_win32.c http_negotiate_sspi.c cyassl.c http_proxy.c non-ascii.c  \
   asyn-ares.c asyn-thread.c curl_gssapi.c curl_ntlm.c curl_ntlm_wb.c   \
   curl_ntlm_core.c curl_ntlm_msgs.c curl_sasl.c curl_schannel.c        \
-  curl_multibyte.c
+  curl_multibyte.c curl_darwinssl.c
 
 HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h      \
   progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h     \
@@ -41,4 +41,4 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h     \
   warnless.h curl_hmac.h polarssl.h curl_rtmp.h curl_gethostname.h     \
   gopher.h axtls.h cyassl.h http_proxy.h non-ascii.h asyn.h curl_ntlm.h        \
   curl_gssapi.h curl_ntlm_wb.h curl_ntlm_core.h curl_ntlm_msgs.h       \
-  curl_sasl.h curl_schannel.h curl_multibyte.h
+  curl_sasl.h curl_schannel.h curl_multibyte.h curl_darwinssl.h
diff --git a/lib/curl_darwinssl.c b/lib/curl_darwinssl.c
new file mode 100644 (file)
index 0000000..b21cc46
--- /dev/null
@@ -0,0 +1,661 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012, Nick Zitzmann, <nickzman@gmail.com>.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all SecureTransport-specific code for the TLS/SSL layer.
+ * No code but sslgen.c should ever call or use these functions.
+ */
+
+#include "setup.h"
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef USE_DARWINSSL
+#include <Security/Security.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "inet_pton.h"
+#include "connect.h"
+#include "select.h"
+#include "sslgen.h"
+#include "curl_darwinssl.h"
+
+/* From MacTypes.h (which we can't include because it isn't present in iOS: */
+#define ioErr -36
+
+/* The following two functions were ripped from Apple sample code,
+ * with some modifications: */
+static OSStatus SocketRead(SSLConnectionRef connection,
+                           void *data,          /* owned by
+                                                 * caller, data
+                                                 * RETURNED */
+                           size_t *dataLength)  /* IN/OUT */
+{
+  UInt32 bytesToGo = *dataLength;
+  UInt32 initLen = bytesToGo;
+  UInt8 *currData = (UInt8 *)data;
+  int sock = *(int *)connection;
+  OSStatus rtn = noErr;
+  UInt32 bytesRead;
+  int rrtn;
+  int theErr;
+
+  *dataLength = 0;
+
+  for(;;) {
+    bytesRead = 0;
+    rrtn = read(sock, currData, bytesToGo);
+    if(rrtn <= 0) {
+      /* this is guesswork... */
+      theErr = errno;
+      if((rrtn == 0) && (theErr == 0)) {
+        /* try fix for iSync */
+        rtn = errSSLClosedGraceful;
+      }
+      else /* do the switch */
+        switch(theErr) {
+          case ENOENT:
+            /* connection closed */
+            rtn = errSSLClosedGraceful;
+            break;
+          case ECONNRESET:
+            rtn = errSSLClosedAbort;
+            break;
+          case EAGAIN:
+            rtn = errSSLWouldBlock;
+            break;
+          default:
+            rtn = ioErr;
+            break;
+        }
+      break;
+    }
+    else {
+      bytesRead = rrtn;
+    }
+    bytesToGo -= bytesRead;
+    currData  += bytesRead;
+
+    if(bytesToGo == 0) {
+      /* filled buffer with incoming data, done */
+      break;
+    }
+  }
+  *dataLength = initLen - bytesToGo;
+
+  return rtn;
+}
+
+static OSStatus SocketWrite(SSLConnectionRef connection,
+                            const void *data,
+                            size_t *dataLength)  /* IN/OUT */
+{
+  UInt32 bytesSent = 0;
+  int sock = *(int *)connection;
+  int length;
+  UInt32 dataLen = *dataLength;
+  const UInt8 *dataPtr = (UInt8 *)data;
+  OSStatus ortn;
+  int theErr;
+
+  *dataLength = 0;
+
+  do {
+    length = write(sock,
+                   (char*)dataPtr + bytesSent,
+                   dataLen - bytesSent);
+  } while((length > 0) &&
+           ( (bytesSent += length) < dataLen) );
+
+  if(length <= 0) {
+    theErr = errno;
+    if(theErr == EAGAIN) {
+      ortn = errSSLWouldBlock;
+    }
+    else {
+      ortn = ioErr;
+    }
+  }
+  else {
+    ortn = noErr;
+  }
+  *dataLength = bytesSent;
+  return ortn;
+}
+
+static CURLcode st_connect_step1(struct connectdata *conn,
+                                 int sockindex)
+{
+  struct SessionHandle *data = conn->data;
+  curl_socket_t sockfd = conn->sock[sockindex];
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  bool sni = true;
+#ifdef ENABLE_IPV6
+  struct in6_addr addr;
+#else
+  struct in_addr addr;
+#endif
+  SSLConnectionRef ssl_connection;
+  OSStatus err = noErr;
+
+  if(connssl->ssl_ctx)
+    (void)SSLDisposeContext(connssl->ssl_ctx);
+  err = SSLNewContext(false, &(connssl->ssl_ctx));
+  if(err != noErr) {
+    failf(data, "SSL: couldn't create a context: OSStatus %d", err);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  /* check to see if we've been told to use an explicit SSL/TLS version */
+  (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, kSSLProtocolAll, false);
+  switch(data->set.ssl.version) {
+    default:
+    case CURL_SSLVERSION_DEFAULT:
+      (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
+                                         kSSLProtocol3,
+                                         true);
+      (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
+                                         kTLSProtocol1,
+                                         true);
+      break;
+    case CURL_SSLVERSION_TLSv1:
+      (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
+                                         kTLSProtocol1,
+                                         true);
+      break;
+    case CURL_SSLVERSION_SSLv2:
+      (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
+                                         kSSLProtocol2,
+                                         true);
+      break;
+    case CURL_SSLVERSION_SSLv3:
+      (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
+                                         kSSLProtocol3,
+                                         true);
+      break;
+  }
+
+  /* No need to load certificates here. SecureTransport uses the Keychain
+   * (which is also part of the Security framework) to evaluate trust. */
+
+  /* SSL always tries to verify the peer, this only says whether it should
+   * fail to connect if the verification fails, or if it should continue
+   * anyway. In the latter case the result of the verification is checked with
+   * SSL_get_verify_result() below. */
+  err = SSLSetEnableCertVerify(connssl->ssl_ctx,
+                               data->set.ssl.verifypeer?true:false);
+  if(err != noErr) {
+    failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err);
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  if((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) &&
+#ifdef ENABLE_IPV6
+     (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) &&
+#endif
+     sni) {
+    err = SSLSetPeerDomainName(connssl->ssl_ctx, conn->host.name,
+                               strlen(conn->host.name));
+    if(err != noErr) {
+      infof(data, "WARNING: SSL: SSLSetPeerDomainName() failed: OSStatus %d",
+            err);
+    }
+    else
+      infof(data, "WARNING: failed to configure "
+            "server name indication (SNI) TLS extension\n");
+  }
+
+  err = SSLSetIOFuncs(connssl->ssl_ctx, SocketRead, SocketWrite);
+  if(err != noErr) {
+    failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err);
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  /* pass the raw socket into the SSL layers */
+  /* We need to store the FD in a constant memory address, because
+   * SSLSetConnection() will not copy that address. I've found that
+   * conn->sock[sockindex] may change on its own. */
+  connssl->ssl_sockfd = sockfd;
+  ssl_connection = &(connssl->ssl_sockfd);
+  err = SSLSetConnection(connssl->ssl_ctx, ssl_connection);
+  if(err != noErr) {
+    failf(data, "SSL: SSLSetConnection() failed: %d", err);
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  connssl->connecting_state = ssl_connect_2;
+  return CURLE_OK;
+}
+
+static CURLcode
+st_connect_step2(struct connectdata *conn, int sockindex)
+{
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  OSStatus err;
+  SSLCipherSuite cipher;
+
+  DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
+              || ssl_connect_2_reading == connssl->connecting_state
+              || ssl_connect_2_writing == connssl->connecting_state
+              || ssl_connect_2_wouldblock == connssl->connecting_state);
+
+  /* Here goes nothing: */
+  err = SSLHandshake(connssl->ssl_ctx);
+
+  if(err != noErr) {
+    switch (err) {
+      case errSSLWouldBlock:  /* they're not done with us yet */
+        connssl->connecting_state = ssl_connect_2_wouldblock;
+        return CURLE_OK;
+        break;
+
+      case errSSLServerAuthCompleted:
+        /* the documentation says we need to call SSLHandshake() again */
+        return st_connect_step2(conn, sockindex);
+
+      case errSSLXCertChainInvalid:
+      case errSSLUnknownRootCert:
+      case errSSLNoRootCert:
+      case errSSLCertExpired:
+        failf(data, "SSL certificate problem: OSStatus %d", err);
+        return CURLE_SSL_CACERT;
+        break;
+
+      default:
+        failf(data, "Unknown SSL protocol error in connection to %s:%d",
+              conn->host.name, err);
+        return CURLE_SSL_CONNECT_ERROR;
+        break;
+    }
+  }
+  else {
+    /* we have been connected fine, we're not waiting for anything else. */
+    connssl->connecting_state = ssl_connect_3;
+
+    /* Informational message */
+    (void)SSLGetNegotiatedCipher(connssl->ssl_ctx, &cipher);
+    infof (data, "SSL connection using cipher %u\n", cipher);
+
+    return CURLE_OK;
+  }
+}
+
+static CURLcode
+st_connect_step3(struct connectdata *conn,
+                 int sockindex)
+{
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  CFStringRef server_cert_summary;
+  char server_cert_summary_c[128];
+  CFArrayRef server_certs;
+  SecCertificateRef server_cert;
+  OSStatus err;
+  CFIndex i, count;
+
+  /* There is no step 3!
+   * Well, okay, if verbose mode is on, let's print the details of the
+   * server certificates. */
+  err = SSLCopyPeerCertificates(connssl->ssl_ctx, &server_certs);
+  if(err == noErr) {
+    count = CFArrayGetCount(server_certs);
+    for(i = 0L ; i < count ; i++) {
+      server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i);
+
+      server_cert_summary = SecCertificateCopySubjectSummary(server_cert);
+      memset(server_cert_summary_c, 0, 128);
+      if(CFStringGetCString(server_cert_summary,
+                            server_cert_summary_c,
+                            128,
+                            kCFStringEncodingUTF8)) {
+        infof(data, "Server certificate: %s\n", server_cert_summary_c);
+      }
+      CFRelease(server_cert_summary);
+    }
+    CFRelease(server_certs);
+  }
+
+  connssl->connecting_state = ssl_connect_done;
+  return CURLE_OK;
+}
+
+static Curl_recv st_recv;
+static Curl_send st_send;
+
+static CURLcode
+st_connect_common(struct connectdata *conn,
+                  int sockindex,
+                  bool nonblocking,
+                  bool *done)
+{
+  CURLcode retcode;
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  curl_socket_t sockfd = conn->sock[sockindex];
+  long timeout_ms;
+  int what;
+
+  /* check if the connection has already been established */
+  if(ssl_connection_complete == connssl->state) {
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
+  if(ssl_connect_1==connssl->connecting_state) {
+    /* Find out how much more time we're allowed */
+    timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+    if(timeout_ms < 0) {
+      /* no need to continue if time already is up */
+      failf(data, "SSL connection timeout");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+    retcode = st_connect_step1(conn, sockindex);
+    if(retcode)
+      return retcode;
+  }
+
+  while(ssl_connect_2 == connssl->connecting_state ||
+        ssl_connect_2_reading == connssl->connecting_state ||
+        ssl_connect_2_writing == connssl->connecting_state ||
+        ssl_connect_2_wouldblock == connssl->connecting_state) {
+
+    /* check allowed time left */
+    timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+    if(timeout_ms < 0) {
+      /* no need to continue if time already is up */
+      failf(data, "SSL connection timeout");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+
+    /* if ssl is expecting something, check if it's available. */
+    if(connssl->connecting_state == ssl_connect_2_reading
+       || connssl->connecting_state == ssl_connect_2_writing
+       || connssl->connecting_state == ssl_connect_2_wouldblock) {
+
+      curl_socket_t writefd = ssl_connect_2_writing
+      || ssl_connect_2_wouldblock ==
+      connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+      curl_socket_t readfd = ssl_connect_2_reading
+      || ssl_connect_2_wouldblock ==
+      connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+
+      what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
+      if(what < 0) {
+        /* fatal error */
+        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+        return CURLE_SSL_CONNECT_ERROR;
+      }
+      else if(0 == what) {
+        if(nonblocking) {
+          *done = FALSE;
+          return CURLE_OK;
+        }
+        else {
+          /* timeout */
+          failf(data, "SSL connection timeout");
+          return CURLE_OPERATION_TIMEDOUT;
+        }
+      }
+      /* socket is readable or writable */
+    }
+
+    /* Run transaction, and return to the caller if it failed or if this
+     * connection is done nonblocking and this loop would execute again. This
+     * permits the owner of a multi handle to abort a connection attempt
+     * before step2 has completed while ensuring that a client using select()
+     * or epoll() will always have a valid fdset to wait on.
+     */
+    retcode = st_connect_step2(conn, sockindex);
+    if(retcode || (nonblocking &&
+                   (ssl_connect_2 == connssl->connecting_state ||
+                    ssl_connect_2_reading == connssl->connecting_state ||
+                    ssl_connect_2_writing == connssl->connecting_state)))
+      return retcode;
+
+  } /* repeat step2 until all transactions are done. */
+
+
+  if(ssl_connect_3==connssl->connecting_state) {
+    retcode = st_connect_step3(conn, sockindex);
+    if(retcode)
+      return retcode;
+  }
+
+  if(ssl_connect_done==connssl->connecting_state) {
+    connssl->state = ssl_connection_complete;
+    conn->recv[sockindex] = st_recv;
+    conn->send[sockindex] = st_send;
+    *done = TRUE;
+  }
+  else
+    *done = FALSE;
+
+  /* Reset our connect state machine */
+  connssl->connecting_state = ssl_connect_1;
+
+  return CURLE_OK;
+}
+
+CURLcode
+Curl_st_connect_nonblocking(struct connectdata *conn,
+                            int sockindex,
+                            bool *done)
+{
+  return st_connect_common(conn, sockindex, TRUE, done);
+}
+
+CURLcode
+Curl_st_connect(struct connectdata *conn,
+                int sockindex)
+{
+  CURLcode retcode;
+  bool done = FALSE;
+
+  retcode = st_connect_common(conn, sockindex, FALSE, &done);
+
+  if(retcode)
+    return retcode;
+
+  DEBUGASSERT(done);
+
+  return CURLE_OK;
+}
+
+void Curl_st_close(struct connectdata *conn, int sockindex)
+{
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+  (void)SSLClose(connssl->ssl_ctx);
+  (void)SSLDisposeContext(connssl->ssl_ctx);
+  connssl->ssl_ctx = NULL;
+  connssl->ssl_sockfd = 0;
+}
+
+void Curl_st_close_all(struct SessionHandle *data)
+{
+  /* SecureTransport doesn't separate sessions from contexts, so... */
+  (void)data;
+}
+
+int Curl_st_shutdown(struct connectdata *conn, int sockindex)
+{
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct SessionHandle *data = conn->data;
+  ssize_t nread;
+  int what;
+  int rc;
+  char buf[120];
+
+  if(!connssl->ssl_ctx)
+    return 0;
+
+  if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE)
+    return 0;
+
+  Curl_st_close(conn, sockindex);
+
+  rc = 0;
+
+  what = Curl_socket_ready(conn->sock[sockindex],
+                           CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
+
+  for(;;) {
+    if(what < 0) {
+      /* anything that gets here is fatally bad */
+      failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+      rc = -1;
+      break;
+    }
+
+    if(!what) {                                /* timeout */
+      failf(data, "SSL shutdown timeout");
+      break;
+    }
+
+    /* Something to read, let's do it and hope that it is the close
+     notify alert from the server. No way to SSL_Read now, so use read(). */
+
+    nread = read(conn->sock[sockindex], buf, sizeof(buf));
+
+    if(nread < 0) {
+      failf(data, "read: %s", strerror(errno));
+      rc = -1;
+    }
+
+    if(nread <= 0)
+      break;
+
+    what = Curl_socket_ready(conn->sock[sockindex], CURL_SOCKET_BAD, 0);
+  }
+
+  return rc;
+}
+
+size_t Curl_st_version(char *buffer, size_t size)
+{
+  return snprintf(buffer, size, "SecureTransport");
+}
+
+/*
+ * This function uses SSLGetSessionState to determine connection status.
+ *
+ * Return codes:
+ *     1 means the connection is still in place
+ *     0 means the connection has been closed
+ *    -1 means the connection status is unknown
+ */
+int Curl_st_check_cxn(struct connectdata *conn)
+{
+  struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
+  OSStatus err;
+  SSLSessionState state;
+
+  if(connssl->ssl_ctx) {
+    err = SSLGetSessionState(connssl->ssl_ctx, &state);
+    if(err == noErr)
+      return state == kSSLConnected || state == kSSLHandshake;
+    return -1;
+  }
+  return 0;
+}
+
+bool Curl_st_data_pending(const struct connectdata *conn, int connindex)
+{
+  const struct ssl_connect_data *connssl = &conn->ssl[connindex];
+  OSStatus err;
+  size_t buffer;
+
+  if(connssl->ssl_ctx) {  /* SSL is in use */
+    err = SSLGetBufferedReadSize(connssl->ssl_ctx, &buffer);
+    if(err == noErr)
+      return buffer > 0UL;
+    return false;
+  }
+  else
+    return false;
+}
+
+static ssize_t st_send(struct connectdata *conn,
+                       int sockindex,
+                       const void *mem,
+                       size_t len,
+                       CURLcode *curlcode)
+{
+  /*struct SessionHandle *data = conn->data;*/
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  size_t processed;
+  OSStatus err = SSLWrite(connssl->ssl_ctx, mem, len, &processed);
+
+  if(err != noErr) {
+    switch (err) {
+      case errSSLWouldBlock:  /* we're not done yet; keep sending */
+        *curlcode = CURLE_AGAIN;
+        return -1;
+        break;
+
+      default:
+        failf(conn->data, "SSLWrite() return error %d", err);
+        *curlcode = CURLE_SEND_ERROR;
+        return -1;
+        break;
+    }
+  }
+  return (ssize_t)processed;
+}
+
+static ssize_t st_recv(struct connectdata *conn, /* connection data */
+                       int num,                  /* socketindex */
+                       char *buf,                /* store read data here */
+                       size_t buffersize,        /* max amount to read */
+                       CURLcode *curlcode)
+{
+  /*struct SessionHandle *data = conn->data;*/
+  struct ssl_connect_data *connssl = &conn->ssl[num];
+  size_t processed;
+  OSStatus err = SSLRead(connssl->ssl_ctx, buf, buffersize, &processed);
+
+  if(err != noErr) {
+    switch (err) {
+      case errSSLWouldBlock:  /* we're not done yet; keep reading */
+        *curlcode = CURLE_AGAIN;
+        return -1;
+        break;
+
+      default:
+        failf(conn->data, "SSLRead() return error %d", err);
+        *curlcode = CURLE_RECV_ERROR;
+        return -1;
+        break;
+    }
+  }
+  return (ssize_t)processed;
+}
+
+#endif /* USE_DARWINSSL */
diff --git a/lib/curl_darwinssl.h b/lib/curl_darwinssl.h
new file mode 100644 (file)
index 0000000..6eb20cc
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef HEADER_CURL_DARWINSSL_H
+#define HEADER_CURL_DARWINSSL_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012, Nick Zitzmann, <nickzman@gmail.com>.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#include "setup.h"
+
+#ifdef USE_DARWINSSL
+
+CURLcode Curl_st_connect(struct connectdata *conn, int sockindex);
+
+CURLcode Curl_st_connect_nonblocking(struct connectdata *conn,
+                                     int sockindex,
+                                     bool *done);
+
+/* this function doesn't actually do anything */
+void Curl_st_close_all(struct SessionHandle *data);
+
+/* close a SSL connection */
+void Curl_st_close(struct connectdata *conn, int sockindex);
+
+size_t Curl_st_version(char *buffer, size_t size);
+int Curl_st_shutdown(struct connectdata *conn, int sockindex);
+int Curl_st_check_cxn(struct connectdata *conn);
+bool Curl_st_data_pending(const struct connectdata *conn, int connindex);
+
+/* API setup for SecureTransport */
+#define curlssl_init() (1)
+#define curlssl_cleanup() Curl_nop_stmt
+#define curlssl_connect Curl_st_connect
+#define curlssl_connect_nonblocking Curl_st_connect_nonblocking
+#define curlssl_session_free(x) Curl_nop_stmt
+#define curlssl_close_all Curl_st_close_all
+#define curlssl_close Curl_st_close
+#define curlssl_shutdown(x,y) 0
+#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN)
+#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN)
+#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL)
+#define curlssl_version Curl_st_version
+#define curlssl_check_cxn Curl_st_check_cxn
+#define curlssl_data_pending(x,y) Curl_st_data_pending(x, y)
+
+#endif /* USE_DARWINSSL */
+#endif /* HEADER_CURL_DARWINSSL_H */
\ No newline at end of file
index 4b48761..a2d0b28 100644 (file)
@@ -581,7 +581,8 @@ int netware_init(void);
 
 #if defined(USE_GNUTLS) || defined(USE_SSLEAY) || defined(USE_NSS) || \
     defined(USE_QSOSSL) || defined(USE_POLARSSL) || defined(USE_AXTLS) || \
-    defined(USE_CYASSL) || defined(USE_SCHANNEL)
+    defined(USE_CYASSL) || defined(USE_SCHANNEL) || \
+    defined(USE_DARWINSSL)
 #define USE_SSL    /* SSL support has been enabled */
 #endif
 
index 28326dd..8cf91f0 100644 (file)
@@ -34,6 +34,7 @@
    Curl_polarssl_ - prefix for PolarSSL ones
    Curl_cyassl_ - prefix for CyaSSL ones
    Curl_schannel_ - prefix for Schannel SSPI ones
+   Curl_st_ - prefix for SecureTransport (Darwin) ones
 
    Note that this source code uses curlssl_* functions, and they are all
    defines/macros #defined by the lib-specific header files.
@@ -59,6 +60,7 @@
 #include "axtls.h"  /* axTLS versions */
 #include "cyassl.h"  /* CyaSSL versions */
 #include "curl_schannel.h" /* Schannel SSPI version */
+#include "curl_darwinssl.h" /* SecureTransport (Darwin) version */
 #include "sendf.h"
 #include "rawstr.h"
 #include "url.h"
index ae6326a..dfb4fab 100644 (file)
 #include <schannel.h>
 #endif
 
+#ifdef USE_DARWINSSL
+#include <Security/Security.h>
+#endif
+
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #endif
@@ -242,7 +246,12 @@ typedef enum {
   ssl_connect_2_reading,
   ssl_connect_2_writing,
   ssl_connect_3,
+#ifdef USE_DARWINSSL
+  ssl_connect_done,
+  ssl_connect_2_wouldblock
+#else
   ssl_connect_done
+#endif /* USE_DARWINSSL */
 } ssl_connect_state;
 
 typedef enum {
@@ -313,6 +322,11 @@ struct ssl_connect_data {
   unsigned char *encdata_buffer, *decdata_buffer;
   unsigned long req_flags, ret_flags;
 #endif /* USE_SCHANNEL */
+#ifdef USE_DARWINSSL
+  SSLContextRef ssl_ctx;
+  curl_socket_t ssl_sockfd;
+  ssl_connect_state connecting_state;
+#endif /* USE_DARWINSSL */
 };
 
 struct ssl_config_data {