Repository: qpid-proton
Updated Branches:
  refs/heads/master 2f8a70a3b -> 04271c198


PROTON-697: SChannel functionality: basic server support


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/04271c19
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/04271c19
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/04271c19

Branch: refs/heads/master
Commit: 04271c1982b9bbf4c59770ef7cfad337ed4c316b
Parents: 2f8a70a
Author: Clifford Jansen <[email protected]>
Authored: Mon Dec 8 12:58:52 2014 -0800
Committer: Clifford Jansen <[email protected]>
Committed: Mon Dec 8 12:58:52 2014 -0800

----------------------------------------------------------------------
 proton-c/include/proton/ssl.h   |  15 +-
 proton-c/src/windows/schannel.c | 471 +++++++++++++++++++++++++++++++----
 2 files changed, 436 insertions(+), 50 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/04271c19/proton-c/include/proton/ssl.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/ssl.h b/proton-c/include/proton/ssl.h
index 09d386b..e8ab1e5 100644
--- a/proton-c/include/proton/ssl.h
+++ b/proton-c/include/proton/ssl.h
@@ -127,17 +127,20 @@ PN_EXTERN void pn_ssl_domain_free( pn_ssl_domain_t 
*domain );
  * previous setting.
  *
  * @param[in] domain the ssl domain that will use this certificate.
- * @param[in] certificate_file path to file/database containing the identifying
- * certificate.
- * @param[in] private_key_file path to file/database containing the private 
key used to
- * sign the certificate
+ * @param[in] credential_1 specifier for the file/database containing the 
identifying
+ * certificate. For Openssl users, this is a PEM file. For Windows SChannel 
users, this is
+ * the PKCS#12 file or system store.
+ * @param[in] credential_2 an optional key to access the identifying 
certificate. For
+ * Openssl users, this is an optional PEM file containing the private key used 
to sign the
+ * certificate. For Windows SChannel users, this is the friendly name of the
+ * self-identifying certificate if there are multiple certificates in the 
store.
  * @param[in] password the password used to sign the key, else NULL if key is 
not
  * protected.
  * @return 0 on success
  */
 PN_EXTERN int pn_ssl_domain_set_credentials( pn_ssl_domain_t *domain,
-                                             const char *certificate_file,
-                                             const char *private_key_file,
+                                             const char *credential_1,
+                                             const char *credential_2,
                                              const char *password);
 
 /** Configure the set of trusted CA certificates used by this domain to verify 
peers.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/04271c19/proton-c/src/windows/schannel.c
----------------------------------------------------------------------
diff --git a/proton-c/src/windows/schannel.c b/proton-c/src/windows/schannel.c
index e7ef5fd..3cc7b9f 100644
--- a/proton-c/src/windows/schannel.c
+++ b/proton-c/src/windows/schannel.c
@@ -54,6 +54,230 @@
  * This file contains an SChannel-based implemention of the SSL/TLS API for 
Windows platforms.
  */
 
+static void ssl_log_error(const char *fmt, ...);
+static void ssl_log(pn_ssl_t *ssl, const char *fmt, ...);
+static void ssl_log_error_status(HRESULT status, const char *fmt, ...);
+
+/*
+ * win_credential_t: SChannel context that must accompany TLS connections.
+ *
+ * SChannel attempts session resumption for shared CredHandle objects.
+ * To mimic openssl behavior, server CredHandle handles must be shared
+ * by derived connections, client CredHandle handles must be unique
+ * when app's session_id is null and tracked for reuse otherwise
+ * (TODO).
+ *
+ * Ref counted by parent ssl_domain_t and each derived connection.
+ */
+struct win_credential_t {
+  pn_ssl_mode_t mode;
+  PCCERT_CONTEXT cert_context;  // Particulars of the certificate (if any)
+  CredHandle cred_handle;       // Bound session parameters, certificate, CAs, 
verification_mode
+};
+
+#define win_credential_compare NULL
+#define win_credential_inspect NULL
+#define win_credential_hashcode NULL
+
+static void win_credential_initialize(void *object)
+{
+  win_credential_t *c = (win_credential_t *) object;
+  SecInvalidateHandle(&c->cred_handle);
+  c->cert_context = 0;
+}
+
+static void win_credential_finalize(void *object)
+{
+  win_credential_t *c = (win_credential_t *) object;
+  if (SecIsValidHandle(&c->cred_handle))
+    FreeCredentialsHandle(&c->cred_handle);
+  if (c->cert_context)
+    CertFreeCertificateContext(c->cert_context);
+}
+
+static win_credential_t *win_credential(pn_ssl_mode_t m)
+{
+  static const pn_cid_t CID_win_credential = CID_pn_void;
+  static const pn_class_t clazz = PN_CLASS(win_credential);
+  win_credential_t *c = (win_credential_t *) pn_class_new(&clazz, 
sizeof(win_credential_t));
+  c->mode = m;
+  return c;
+}
+
+static int win_credential_load_cert(win_credential_t *cred, const char 
*store_name, const char *cert_name, const char *passwd)
+{
+  if (!store_name)
+    return -2;
+
+  char *buf = NULL;
+  DWORD sys_store_type = 0;
+  HCERTSTORE cert_store = 0;
+
+  if (store_name) {
+    if (strncmp(store_name, "ss:", 3) == 0) {
+      store_name += 3;
+      sys_store_type = CERT_SYSTEM_STORE_CURRENT_USER;
+    }
+    else if (strncmp(store_name, "lmss:", 5) == 0) {
+      store_name += 5;
+      sys_store_type = CERT_SYSTEM_STORE_LOCAL_MACHINE;
+    }
+  }
+
+  if (sys_store_type) {
+    // Opening a system store, names are not case sensitive.
+    // Map confusing GUI name to actual registry store name.
+    if (pni_eq_nocase(store_name, "personal"))
+      store_name= "my";
+    cert_store = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, NULL,
+                                CERT_STORE_OPEN_EXISTING_FLAG | 
CERT_STORE_READONLY_FLAG |
+                                sys_store_type, store_name);
+    if (!cert_store) {
+      ssl_log_error_status(GetLastError(), "Failed to open system certificate 
store %s", store_name);
+      return -3;
+    }
+  } else {
+    // PKCS#12 file
+    HANDLE cert_file = CreateFile(store_name, GENERIC_READ, 0, NULL, 
OPEN_EXISTING,
+                                  FILE_ATTRIBUTE_NORMAL, NULL);
+    if (INVALID_HANDLE_VALUE == cert_file) {
+      HRESULT status = GetLastError();
+      ssl_log_error_status(status, "Failed to open the file holding the 
private key: %s", store_name);
+      return -4;
+    }
+    DWORD nread = 0L;
+    const DWORD file_size = GetFileSize(cert_file, NULL);
+    if (INVALID_FILE_SIZE != file_size)
+      buf = (char *) malloc(file_size);
+    if (!buf || !ReadFile(cert_file, buf, file_size, &nread, NULL)
+        || file_size != nread) {
+      HRESULT status = GetLastError();
+      CloseHandle(cert_file);
+      free(buf);
+      ssl_log_error_status(status, "Reading the private key from file failed 
%s", store_name);
+      return -5;
+    }
+    CloseHandle(cert_file);
+
+    CRYPT_DATA_BLOB blob;
+    blob.cbData = nread;
+    blob.pbData = (BYTE *) buf;
+
+    wchar_t *pwUCS2 = NULL;
+    int pwlen = 0;
+    if (passwd) {
+      // convert passwd to null terminated wchar_t (Windows UCS2)
+      pwlen = strlen(passwd);
+      pwUCS2 = (wchar_t *) calloc(pwlen + 1, sizeof(wchar_t));
+      int nwc = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, passwd, 
pwlen, &pwUCS2[0], pwlen);
+      if (!nwc) {
+        ssl_log_error_status(GetLastError(), "Error converting password from 
UTF8");
+        free(buf);
+        free(pwUCS2);
+        return -6;
+      }
+    }
+
+    cert_store = PFXImportCertStore(&blob, pwUCS2, 0);
+    if (pwUCS2) {
+      SecureZeroMemory(pwUCS2, pwlen * sizeof(wchar_t));
+      free(pwUCS2);
+    }
+    if (cert_store == NULL) {
+      ssl_log_error_status(GetLastError(), "Failed to import the file based 
certificate store");
+      free(buf);
+      return -7;
+    }
+  }
+
+  // find friendly name that matches cert_name, or sole certificate
+  PCCERT_CONTEXT tmpctx = NULL;
+  PCCERT_CONTEXT found_ctx = NULL;
+  int cert_count = 0;
+  int name_len = cert_name ? strlen(cert_name) : 0;
+  char *fn = name_len ? (char *) malloc(name_len + 1) : 0;
+  while (tmpctx = CertEnumCertificatesInStore(cert_store, tmpctx)) {
+    cert_count++;
+    if (cert_name) {
+      DWORD len = CertGetNameString(tmpctx, CERT_NAME_FRIENDLY_DISPLAY_TYPE,
+                                    0, NULL, NULL, 0);
+      if (len != name_len + 1)
+        continue;
+      CertGetNameString(tmpctx, CERT_NAME_FRIENDLY_DISPLAY_TYPE,
+                        0, NULL, fn, len);
+      if (!strcmp(cert_name, fn)) {
+        found_ctx = tmpctx;
+        tmpctx= NULL;
+        break;
+      }
+    } else {
+      // Test for single certificate
+      if (cert_count == 1) {
+        found_ctx = CertDuplicateCertificateContext(tmpctx);
+      } else {
+        ssl_log_error("Multiple certificates to choose from certificate store 
%s\n", store_name);
+        found_ctx = NULL;
+        break;
+      }
+    }
+  }
+
+  if (tmpctx) {
+    CertFreeCertificateContext(tmpctx);
+    tmpctx = false;
+  }
+  if (!found_ctx && cert_name && cert_count == 1)
+    ssl_log_error("Could not find certificate %s in store %s\n", cert_name, 
store_name);
+  cred->cert_context = found_ctx;
+
+  free(buf);
+  free(fn);
+  CertCloseStore(cert_store, 0);
+  return found_ctx ? 0 : -8;
+}
+
+
+static CredHandle win_credential_cred_handle(win_credential_t *cred, 
pn_ssl_verify_mode_t verify_mode,
+                                             const char *session_id, 
SECURITY_STATUS *status)
+{
+  if (cred->mode == PN_SSL_MODE_SERVER && 
SecIsValidHandle(&cred->cred_handle)) {
+    *status = SEC_E_OK;
+    return cred->cred_handle;  // Server always reuses cached value
+  }
+  // TODO: if (is_client && session_id != NULL) create or use cached value 
based on
+  // session_id+server_host_name (per domain? reclaimed after X hours?)
+
+  CredHandle tmp_handle;
+  SecInvalidateHandle(&tmp_handle);
+  TimeStamp expiry;  // Not used
+  SCHANNEL_CRED descriptor;
+  memset(&descriptor, 0, sizeof(descriptor));
+
+  descriptor.dwVersion = SCHANNEL_CRED_VERSION;
+  descriptor.dwFlags = SCH_CRED_NO_DEFAULT_CREDS;
+  if (cred->mode == PN_SSL_MODE_CLIENT && verify_mode == PN_SSL_ANONYMOUS_PEER)
+    descriptor.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
+  if (cred->cert_context != NULL) {
+    // assign the certificate into the credentials
+    descriptor.paCred = &cred->cert_context;
+    descriptor.cCreds = 1;
+  }
+
+  ULONG direction = (cred->mode == PN_SSL_MODE_SERVER) ? SECPKG_CRED_INBOUND : 
SECPKG_CRED_OUTBOUND;
+  *status = AcquireCredentialsHandle(NULL, UNISP_NAME, direction, NULL,
+                                               &descriptor, NULL, NULL, 
&tmp_handle, &expiry);
+  if (cred->mode == PN_SSL_MODE_SERVER && *status == SEC_E_OK)
+    cred->cred_handle = tmp_handle;
+
+  return tmp_handle;
+}
+
+static bool win_credential_has_certificate(win_credential_t *cred)
+{
+  if (!cred) return false;
+  return (cred->cert_context != NULL);
+}
+
 #define SSL_DATA_SIZE 16384
 #define SSL_BUF_SIZE (SSL_DATA_SIZE + 5 + 2048 + 32)
 
@@ -64,17 +288,9 @@ struct pn_ssl_domain_t {
   int ref_count;
   pn_ssl_mode_t mode;
   bool has_ca_db;       // true when CA database configured
-  bool has_certificate; // true when certificate configured
-  char *keyfile_pw;
-
-  // settings used for all connections
   pn_ssl_verify_mode_t verify_mode;
   bool allow_unsecured;
-
-  // SChannel
-  HCERTSTORE cert_store;
-  PCCERT_CONTEXT cert_context;
-  SCHANNEL_CRED credential;
+  win_credential_t *cred;
 };
 
 typedef enum { CREATED, CLIENT_HELLO, NEGOTIATING,
@@ -124,6 +340,7 @@ struct pni_ssl_t {
   CredHandle cred_handle;
   CtxtHandle ctxt_handle;
   SecPkgContext_StreamSizes sc_sizes;
+  win_credential_t *cred;
 };
 
 static inline pn_transport_t *get_transport_internal(pn_ssl_t *ssl)
@@ -193,7 +410,7 @@ static void ssl_log_error_status(HRESULT status, const char 
*fmt, ...)
 
   if (FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM,
                     0, status, 0, buf, sizeof(buf), 0))
-    ssl_log_error("%s\n", buf);
+    ssl_log_error(" : %s\n", buf);
   else
     fprintf(stderr, "pn internal Windows error: %lu\n", GetLastError());
 
@@ -269,19 +486,11 @@ pn_ssl_domain_t *pn_ssl_domain( pn_ssl_mode_t mode )
   pn_ssl_domain_t *domain = (pn_ssl_domain_t *) calloc(1, 
sizeof(pn_ssl_domain_t));
   if (!domain) return NULL;
 
-  memset(domain, 0, sizeof(domain));
-  domain->credential.dwVersion = SCHANNEL_CRED_VERSION;
-  domain->credential.dwFlags = SCH_CRED_NO_DEFAULT_CREDS;
-
   domain->ref_count = 1;
   domain->mode = mode;
   switch(mode) {
   case PN_SSL_MODE_CLIENT:
-    // TODO
-    break;
-
   case PN_SSL_MODE_SERVER:
-    // TODO
     break;
 
   default:
@@ -289,7 +498,7 @@ pn_ssl_domain_t *pn_ssl_domain( pn_ssl_mode_t mode )
     free(domain);
     return NULL;
   }
-
+  domain->cred = win_credential(mode);
   return domain;
 }
 
@@ -298,12 +507,7 @@ void pn_ssl_domain_free( pn_ssl_domain_t *domain )
   if (!domain) return;
 
   if (--domain->ref_count == 0) {
-    if (domain->cert_context)
-      CertFreeCertificateContext(domain->cert_context);
-    if (domain->cert_store)
-      CertCloseStore(domain->cert_store, CERT_CLOSE_STORE_FORCE_FLAG);
-
-    if (domain->keyfile_pw) free(domain->keyfile_pw);
+    pn_decref(domain->cred);
     free(domain);
   }
 }
@@ -316,9 +520,14 @@ int pn_ssl_domain_set_credentials( pn_ssl_domain_t *domain,
 {
   if (!domain) return -1;
 
-  // TODO:
-
-  return 0;
+  if (win_credential_has_certificate(domain->cred)) {
+    // Need a new win_credential_t to hold new certificate
+    pn_decref(domain->cred);
+    domain->cred = win_credential(domain->mode);
+    if (!domain->cred)
+      return -1;
+  }
+  return win_credential_load_cert(domain->cred, certificate_file, 
private_key_file, password);
 }
 
 
@@ -326,8 +535,24 @@ int pn_ssl_domain_set_trusted_ca_db(pn_ssl_domain_t 
*domain,
                                     const char *certificate_db)
 {
   if (!domain) return -1;
-  // TODO: support for alternate ca db? or just return -1
-  domain->has_ca_db = true;
+
+  if (certificate_db && !pni_eq_nocase(certificate_db, "ss:root") &&
+      !pni_eq_nocase(certificate_db, "lmss:root"))
+    // TODO: handle more than just the main system trust store
+    return -1;
+  if (!certificate_db && !domain->has_ca_db)
+    return 0;   // No change
+  if (certificate_db && domain->has_ca_db)
+    return 0;   // NO change
+
+  win_credential_t *new_cred = win_credential(domain->mode);
+  if (!new_cred)
+    return -1;
+  new_cred->cert_context = 
CertDuplicateCertificateContext(domain->cred->cert_context);
+  pn_decref(domain->cred);
+  domain->cred = new_cred;
+
+  domain->has_ca_db = (certificate_db != NULL);
   return 0;
 }
 
@@ -340,20 +565,37 @@ int pn_ssl_domain_set_peer_authentication(pn_ssl_domain_t 
*domain,
 
   switch (mode) {
   case PN_SSL_VERIFY_PEER:
+    ssl_log_error("Error: optional peer name checking not yet implemented\n"); 
 // But coming soon
+    return -1;
+
   case PN_SSL_VERIFY_PEER_NAME:
-    // TODO
+    if (!domain->has_ca_db) {
+      ssl_log_error("Error: cannot verify peer without a trusted CA 
configured.\n"
+                    "       Use pn_ssl_domain_set_trusted_ca_db()\n");
+      return -1;
+    }
+    if (domain->mode == PN_SSL_MODE_SERVER) {
+      ssl_log_error("Client certificates not yet supported.\n"); // But coming 
soon
+      return -1;
+    }
     break;
 
   case PN_SSL_ANONYMOUS_PEER:   // hippie free love mode... :)
-    // TODO
     break;
 
   default:
-    ssl_log_error( "Invalid peer authentication mode given.\n" );
+    ssl_log_error("Invalid peer authentication mode given.\n");
     return -1;
   }
 
   domain->verify_mode = mode;
+  win_credential_t *new_cred = win_credential(domain->mode);
+  if (!new_cred)
+    return -1;
+  new_cred->cert_context = 
CertDuplicateCertificateContext(domain->cred->cert_context);
+  pn_decref(domain->cred);
+  domain->cred = new_cred;
+
   return 0;
 }
 
@@ -397,12 +639,14 @@ int pn_ssl_init(pn_ssl_t *ssl0, pn_ssl_domain_t *domain, 
const char *session_id)
   if (session_id && domain->mode == PN_SSL_MODE_CLIENT)
     ssl->session_id = pn_strdup(session_id);
 
-  TimeStamp cred_expiry;
-  SECURITY_STATUS status = AcquireCredentialsHandle(NULL, UNISP_NAME, 
SECPKG_CRED_OUTBOUND,
-                               NULL, &domain->credential, NULL, NULL, 
&ssl->cred_handle,
-                               &cred_expiry);
+  ssl->cred = domain->cred;
+  pn_incref(domain->cred);
+
+  SECURITY_STATUS status = SEC_E_OK;
+  ssl->cred_handle = win_credential_cred_handle(ssl->cred, 
ssl->domain->verify_mode,
+                                                ssl->session_id, &status);
   if (status != SEC_E_OK) {
-    ssl_log_error_status(status, "AcquireCredentialsHandle");
+    ssl_log_error_status(status, "Credentials handle failure");
     return -1;
   }
 
@@ -451,8 +695,14 @@ void pn_ssl_free( pn_transport_t *transport)
   // clean up Windows per TLS session data before releasing the domain count
   if (SecIsValidHandle(&ssl->ctxt_handle))
     DeleteSecurityContext(&ssl->ctxt_handle);
-  if (SecIsValidHandle(&ssl->cred_handle))
-    FreeCredentialsHandle(&ssl->cred_handle);
+  if (ssl->cred) {
+    if (ssl->domain->mode == PN_SSL_MODE_CLIENT && ssl->session_id == NULL) {
+      // Responsible for unshared handle
+      if (SecIsValidHandle(&ssl->cred_handle))
+        FreeCredentialsHandle(&ssl->cred_handle);
+    }
+    pn_decref(ssl->cred);
+  }
 
   if (ssl->domain) pn_ssl_domain_free(ssl->domain);
   if (ssl->session_id) free((void *)ssl->session_id);
@@ -460,6 +710,7 @@ void pn_ssl_free( pn_transport_t *transport)
   if (ssl->sc_inbuf) free((void *)ssl->sc_inbuf);
   if (ssl->sc_outbuf) free((void *)ssl->sc_outbuf);
   if (ssl->inbuf2) pn_buffer_free(ssl->inbuf2);
+
   free(ssl);
 }
 
@@ -612,7 +863,7 @@ static bool ssl_decrypt(pn_transport_t *transport)
   buff_desc.ulVersion = SECBUFFER_VERSION;
   buff_desc.cBuffers = 4;
   buff_desc.pBuffers = recv_buffs;
-  SECURITY_STATUS status = ::DecryptMessage(&ssl->ctxt_handle, &buff_desc, 0, 
NULL);
+  SECURITY_STATUS status = DecryptMessage(&ssl->ctxt_handle, &buff_desc, 0, 
NULL);
 
   if (status == SEC_E_INCOMPLETE_MESSAGE) {
     // Less than a full Record, come back later with more network data
@@ -810,17 +1061,149 @@ static void client_handshake( pn_transport_t* 
transport) {
     ssl_failed(transport, 0);
     break;
   }
+
+  if (token_buffs[1].BufferType == SECBUFFER_EXTRA && token_buffs[1].cbBuffer 
> 0 &&
+      !ssl->ssl_closed) {
+    // remaining data after the consumed TLS record(s)
+    ssl->extra_count = token_buffs[1].cbBuffer;
+    ssl->inbuf_extra = ssl->sc_inbuf + (ssl->sc_in_count - ssl->extra_count);
+  }
+
   ssl->decrypting = false;
   rewind_sc_inbuf(ssl);
 }
 
 
+static void server_handshake(pn_transport_t* transport)
+{
+  pni_ssl_t *ssl = transport->ssl;
+  // Feed SChannel ongoing handshake records from the client until the 
handshake is complete.
+  ULONG ctxt_requested = ASC_REQ_STREAM;
+  // TODO:  if server and verifying client certificate, ctxtRequested |= 
ASC_REQ_MUTUAL_AUTH;
+  ULONG ctxt_attrs;
+  size_t max = 0;
+
+  // token_buffs describe the buffer that's coming in. It should have
+  // a token from the SSL client except if shutting down or renegotiating.
+  bool shutdown = ssl->state == SHUTTING_DOWN;
+  SecBuffer token_buffs[2];
+  token_buffs[0].cbBuffer = shutdown ? 0 : ssl->sc_in_count;
+  token_buffs[0].BufferType = SECBUFFER_TOKEN;
+  token_buffs[0].pvBuffer = shutdown ? 0 : ssl->sc_inbuf;
+  token_buffs[1].cbBuffer = 0;
+  token_buffs[1].BufferType = SECBUFFER_EMPTY;
+  token_buffs[1].pvBuffer = 0;
+  SecBufferDesc token_buff_desc;
+  token_buff_desc.ulVersion = SECBUFFER_VERSION;
+  token_buff_desc.cBuffers = 2;
+  token_buff_desc.pBuffers = token_buffs;
+
+  // send_buffs will hold information to forward to the peer.
+  SecBuffer send_buffs[2];
+  send_buffs[0].cbBuffer = ssl->sc_out_size;
+  send_buffs[0].BufferType = SECBUFFER_TOKEN;
+  send_buffs[0].pvBuffer = ssl->sc_outbuf;
+  send_buffs[1].cbBuffer = 0;
+  send_buffs[1].BufferType = SECBUFFER_EMPTY;
+  send_buffs[1].pvBuffer = 0;
+  SecBufferDesc send_buff_desc;
+  send_buff_desc.ulVersion = SECBUFFER_VERSION;
+  send_buff_desc.cBuffers = 2;
+  send_buff_desc.pBuffers = send_buffs;
+  PCtxtHandle ctxt_handle_ptr = (SecIsValidHandle(&ssl->ctxt_handle)) ? 
&ssl->ctxt_handle : 0;
+
+  SECURITY_STATUS status = AcceptSecurityContext(&ssl->cred_handle, 
ctxt_handle_ptr,
+                               &token_buff_desc, ctxt_requested, 0, 
&ssl->ctxt_handle,
+                               &send_buff_desc, &ctxt_attrs, NULL);
+
+  bool outbound_token = false;
+  switch(status) {
+  case SEC_E_INCOMPLETE_MESSAGE:
+    // Not enough - get more data from the client then try again.
+    // Leave input buffers untouched.
+    ssl_log(ssl, "server handshake: incomplete record\n");
+    ssl->sc_in_incomplete = true;
+    return;
+
+  case SEC_I_CONTINUE_NEEDED:
+    outbound_token = true;
+    break;
+
+  case SEC_E_OK:
+    // Handshake complete.
+    if (shutdown) {
+      if (send_buffs[0].cbBuffer > 0) {
+        ssl->sc_out_count = send_buffs[0].cbBuffer;
+        // the token is the whole quantity to send
+        ssl->network_out_pending = ssl->sc_out_count;
+        ssl->network_outp = ssl->sc_outbuf;
+        ssl_log(ssl, "server shutdown token %d bytes\n", 
ssl->network_out_pending);
+      } else {
+        ssl->state = SSL_CLOSED;
+      }
+      // we didn't touch sc_inbuf, no need to reset
+      return;
+    }
+    if (const char *err = tls_version_check(ssl)) {
+      ssl_failed(transport, err);
+      break;
+    }
+    // Handshake complete.
+    // TODO: manual check of certificate chain here, if bad: ssl_failed() + 
break;
+    QueryContextAttributes(&ssl->ctxt_handle,
+                             SECPKG_ATTR_STREAM_SIZES, &ssl->sc_sizes);
+    max = ssl->sc_sizes.cbMaximumMessage + ssl->sc_sizes.cbHeader + 
ssl->sc_sizes.cbTrailer;
+    if (max > ssl->sc_out_size) {
+      ssl_log_error("Buffer size mismatch have %d, need %d\n", (int) 
ssl->sc_out_size, (int) max);
+      ssl->state = SHUTTING_DOWN;
+      ssl->app_input_closed = ssl->app_output_closed = PN_ERR;
+      start_ssl_shutdown(transport);
+      pn_do_error(transport, "amqp:connection:framing-error", "SSL Failure: 
buffer size");
+      break;
+    }
+
+    if (send_buffs[0].cbBuffer != 0)
+      outbound_token = true;
+
+    ssl->state = RUNNING;
+    ssl->max_data_size = max - ssl->sc_sizes.cbHeader - 
ssl->sc_sizes.cbTrailer;
+    ssl_log(ssl, "server handshake successful %d max record size\n", max);
+    break;
+
+  case SEC_I_CONTEXT_EXPIRED:
+    // ended before we got going
+  default:
+    ssl_log(ssl, "server handshake failed %d\n", (int) status);
+    ssl_failed(transport, 0);
+    break;
+  }
+
+  if (outbound_token) {
+    // Successful handshake step, requiring data to be sent to peer.
+    assert(ssl->network_out_pending == 0);
+    ssl->sc_out_count = send_buffs[0].cbBuffer;
+    // the token is the whole quantity to send
+    ssl->network_out_pending = ssl->sc_out_count;
+    ssl->network_outp = ssl->sc_outbuf;
+    ssl_log(ssl, "server handshake token %d bytes\n", 
ssl->network_out_pending);
+  }
+
+  if (token_buffs[1].BufferType == SECBUFFER_EXTRA && token_buffs[1].cbBuffer 
> 0 &&
+      !ssl->ssl_closed) {
+    // remaining data after the consumed TLS record(s)
+    ssl->extra_count = token_buffs[1].cbBuffer;
+    ssl->inbuf_extra = ssl->sc_inbuf + (ssl->sc_in_count - ssl->extra_count);
+  }
+
+  ssl->decrypting = false;
+  rewind_sc_inbuf(ssl);
+}
+
 static void ssl_handshake(pn_transport_t* transport) {
   if (transport->ssl->domain->mode == PN_SSL_MODE_CLIENT)
     client_handshake(transport);
   else {
-    ssl_log(transport->ssl, "TODO: server handshake.\n" );
-    ssl_failed(transport, "internal runtime error, not yet implemented");
+    server_handshake(transport);
   }
 }
 
@@ -876,7 +1259,7 @@ static void start_ssl_shutdown(pn_transport_t *transport)
   desc.ulVersion = SECBUFFER_VERSION;
   desc.cBuffers = 1;
   desc.pBuffers = &shutBuff;
-  ::ApplyControlToken(&ssl->ctxt_handle, &desc);
+  ApplyControlToken(&ssl->ctxt_handle, &desc);
 
   // Next handshake will generate the shudown alert token
   ssl_handshake(transport);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to