/*
- * "$Id$"
+ * TLS support for CUPS on Windows using the Security Support Provider
+ * Interface (SSPI).
*
- * TLS support for CUPS on Windows using SSPI.
+ * Copyright 2010-2018 by Apple Inc.
*
- * Copyright 2010-2013 by Apple Inc.
- *
- * These coded instructions, statements, and computer programs are the
- * property of Apple Inc. and are protected by Federal copyright
- * law. Distribution and use rights are outlined in the file "LICENSE.txt"
- * which should have been included with this file. If this file is
- * file is missing or damaged, see the license at "http://www.cups.org/".
- *
- * This file is subject to the Apple OS-Developed Software exception.
+ * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
*/
+/**** This file is included from tls.c ****/
+
/*
* Include necessary headers...
*/
-#include "sspi-private.h"
#include "debug-private.h"
-/* required to link this library for certificate functions */
+/*
+ * Include necessary libraries...
+ */
+
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "Secur32.lib")
#pragma comment(lib, "Ws2_32.lib")
-#if !defined(SECURITY_FLAG_IGNORE_UNKNOWN_CA)
+/*
+ * Constants...
+ */
+
+#ifndef SECURITY_FLAG_IGNORE_UNKNOWN_CA
# define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100 /* Untrusted root */
-#endif
+#endif /* SECURITY_FLAG_IGNORE_UNKNOWN_CA */
+
+#ifndef SECURITY_FLAG_IGNORE_CERT_CN_INVALID
+# define SECURITY_FLAG_IGNORE_CERT_CN_INVALID 0x00001000 /* Common name does not match */
+#endif /* !SECURITY_FLAG_IGNORE_CERT_CN_INVALID */
-#if !defined(SECURITY_FLAG_IGNORE_CERT_DATE_INVALID)
+#ifndef SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
# define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */
-#endif
+#endif /* !SECURITY_FLAG_IGNORE_CERT_DATE_INVALID */
+
+
+/*
+ * Local globals...
+ */
-static DWORD sspi_verify_certificate(PCCERT_CONTEXT serverCert,
- const CHAR *serverName,
- DWORD dwCertFlags);
+static int tls_options = -1,/* Options for TLS connections */
+ tls_min_version = _HTTP_TLS_1_0,
+ tls_max_version = _HTTP_TLS_MAX;
/*
- * 'http_tls_initialize()' - Initialize the TLS stack.
+ * Local functions...
*/
-static void
-http_tls_initialize(void)
-{
-#ifdef HAVE_GNUTLS
- /*
- * Initialize GNU TLS...
- */
+static _http_sspi_t *http_sspi_alloc(void);
+static int http_sspi_client(http_t *http, const char *hostname);
+static PCCERT_CONTEXT http_sspi_create_credential(http_credential_t *cred);
+static BOOL http_sspi_find_credentials(http_t *http, const LPWSTR containerName, const char *common_name);
+static void http_sspi_free(_http_sspi_t *sspi);
+static BOOL http_sspi_make_credentials(_http_sspi_t *sspi, const LPWSTR containerName, const char *common_name, _http_mode_t mode, int years);
+static int http_sspi_server(http_t *http, const char *hostname);
+static void http_sspi_set_allows_any_root(_http_sspi_t *sspi, BOOL allow);
+static void http_sspi_set_allows_expired_certs(_http_sspi_t *sspi, BOOL allow);
+static const char *http_sspi_strerror(char *buffer, size_t bufsize, DWORD code);
+static DWORD http_sspi_verify(PCCERT_CONTEXT cert, const char *common_name, DWORD dwCertFlags);
- gnutls_global_init();
-#elif defined(HAVE_LIBSSL)
- /*
- * Initialize OpenSSL...
- */
+/*
+ * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsMakeServerCredentials(
+ const char *path, /* I - Keychain path or @code NULL@ for default */
+ const char *common_name, /* I - Common name */
+ int num_alt_names, /* I - Number of subject alternate names */
+ const char **alt_names, /* I - Subject Alternate Names */
+ time_t expiration_date) /* I - Expiration date */
+{
+ _http_sspi_t *sspi; /* SSPI data */
+ int ret; /* Return value */
- SSL_load_error_strings();
- SSL_library_init();
- /*
- * Using the current time is a dubious random seed, but on some systems
- * it is the best we can do (on others, this seed isn't even used...)
- */
+ DEBUG_printf(("cupsMakeServerCredentials(path=\"%s\", common_name=\"%s\", num_alt_names=%d, alt_names=%p, expiration_date=%d)", path, common_name, num_alt_names, alt_names, (int)expiration_date));
+
+ (void)path;
+ (void)num_alt_names;
+ (void)alt_names;
- CUPS_SRAND(time(NULL));
+ sspi = http_sspi_alloc();
+ ret = http_sspi_make_credentials(sspi, L"ServerContainer", common_name, _HTTP_MODE_SERVER, (int)((expiration_date - time(NULL) + 86399) / 86400 / 365));
- for (i = 0; i < sizeof(data); i ++)
- data[i] = CUPS_RAND();
+ http_sspi_free(sspi);
- RAND_seed(data, sizeof(data));
-#endif /* HAVE_GNUTLS */
+ return (ret);
}
-#ifdef HAVE_SSL
/*
- * 'http_tls_read()' - Read from a SSL/TLS connection.
+ * 'cupsSetServerCredentials()' - Set the default server credentials.
+ *
+ * Note: The server credentials are used by all threads in the running process.
+ * This function is threadsafe.
+ *
+ * @since CUPS 2.0/OS 10.10@
*/
-static int /* O - Bytes read */
-http_tls_read(http_t *http, /* I - Connection to server */
- char *buf, /* I - Buffer to store data */
- int len) /* I - Length of buffer */
+int /* O - 1 on success, 0 on failure */
+cupsSetServerCredentials(
+ const char *path, /* I - Keychain path or @code NULL@ for default */
+ const char *common_name, /* I - Default common name for server */
+ int auto_create) /* I - 1 = automatically create self-signed certificates */
{
-# if defined(HAVE_LIBSSL)
- return (SSL_read((SSL *)(http->tls), buf, len));
+ DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
-# elif defined(HAVE_GNUTLS)
- ssize_t result; /* Return value */
+ (void)path;
+ (void)common_name;
+ (void)auto_create;
+ return (0);
+}
- result = gnutls_record_recv(http->tls, buf, len);
-
- if (result < 0 && !errno)
- {
- /*
- * Convert GNU TLS error to errno value...
- */
- switch (result)
- {
- case GNUTLS_E_INTERRUPTED :
- errno = EINTR;
- break;
+/*
+ * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
+ * an encrypted connection.
+ *
+ * @since CUPS 1.5/macOS 10.7@
+ */
- case GNUTLS_E_AGAIN :
- errno = EAGAIN;
- break;
+int /* O - Status of call (0 = success) */
+httpCopyCredentials(
+ http_t *http, /* I - Connection to server */
+ cups_array_t **credentials) /* O - Array of credentials */
+{
+ DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials));
- default :
- errno = EPIPE;
- break;
- }
+ if (!http || !http->tls || !http->tls->remoteCert || !credentials)
+ {
+ if (credentials)
+ *credentials = NULL;
- result = -1;
+ return (-1);
}
- return ((int)result);
-
-# elif defined(HAVE_CDSASSL)
- int result; /* Return value */
- OSStatus error; /* Error info */
- size_t processed; /* Number of bytes processed */
-
+ *credentials = cupsArrayNew(NULL, NULL);
+ httpAddCredential(*credentials, http->tls->remoteCert->pbCertEncoded, http->tls->remoteCert->cbCertEncoded);
- error = SSLRead(http->tls, buf, len, &processed);
- DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error,
- (int)processed));
- switch (error)
- {
- case 0 :
- result = (int)processed;
- break;
-
- case errSSLWouldBlock :
- if (processed)
- result = (int)processed;
- else
- {
- result = -1;
- errno = EINTR;
- }
- break;
+ return (0);
+}
- case errSSLClosedGraceful :
- default :
- if (processed)
- result = (int)processed;
- else
- {
- result = -1;
- errno = EPIPE;
- }
- break;
- }
- return (result);
+/*
+ * '_httpCreateCredentials()' - Create credentials in the internal format.
+ */
-# elif defined(HAVE_SSPISSL)
- return _sspiRead((_sspi_struct_t*) http->tls, buf, len);
-# endif /* HAVE_LIBSSL */
+http_tls_credentials_t /* O - Internal credentials */
+_httpCreateCredentials(
+ cups_array_t *credentials) /* I - Array of credentials */
+{
+ return (http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials)));
}
-#endif /* HAVE_SSL */
-#ifdef HAVE_SSL
/*
- * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
+ * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
+ *
+ * @since CUPS 2.0/OS 10.10@
*/
-static int /* O - 0 on success, -1 on failure */
-http_setup_ssl(http_t *http) /* I - Connection to server */
+int /* O - 1 if valid, 0 otherwise */
+httpCredentialsAreValidForName(
+ cups_array_t *credentials, /* I - Credentials */
+ const char *common_name) /* I - Name to check */
{
- char hostname[256], /* Hostname */
- *hostptr; /* Pointer into hostname */
+ int valid = 1; /* Valid name? */
+ PCCERT_CONTEXT cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
+ /* Certificate */
+ char cert_name[1024]; /* Name from certificate */
-# ifdef HAVE_LIBSSL
- SSL_CTX *context; /* Context for encryption */
- BIO *bio; /* BIO data */
- const char *message = NULL;/* Error message */
-# elif defined(HAVE_GNUTLS)
- int status; /* Status of handshake */
- gnutls_certificate_client_credentials *credentials;
- /* TLS credentials */
-# elif defined(HAVE_CDSASSL)
- _cups_globals_t *cg = _cupsGlobals();
- /* Pointer to library globals */
- OSStatus error; /* Error code */
- const char *message = NULL;/* Error message */
- cups_array_t *credentials; /* Credentials array */
- cups_array_t *names; /* CUPS distinguished names */
- CFArrayRef dn_array; /* CF distinguished names array */
- CFIndex count; /* Number of credentials */
- CFDataRef data; /* Certificate data */
- int i; /* Looping var */
- http_credential_t *credential; /* Credential data */
-# elif defined(HAVE_SSPISSL)
- TCHAR username[256]; /* Username returned from GetUserName() */
- TCHAR commonName[256];/* Common name for certificate */
- DWORD dwSize; /* 32 bit size */
-# endif /* HAVE_LIBSSL */
+ if (cert)
+ {
+ if (CertNameToStrA(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
+ {
+ /*
+ * Extract common name at end...
+ */
+
+ char *ptr = strrchr(cert_name, ',');
+ if (ptr && ptr[1])
+ _cups_strcpy(cert_name, ptr + 2);
+ }
+ else
+ strlcpy(cert_name, "unknown", sizeof(cert_name));
- DEBUG_printf(("7http_setup_ssl(http=%p)", http));
+ CertFreeCertificateContext(cert);
+ }
+ else
+ strlcpy(cert_name, "unknown", sizeof(cert_name));
/*
- * Get the hostname to use for SSL...
+ * Compare the common names...
*/
- if (httpAddrLocalhost(http->hostaddr))
- {
- strlcpy(hostname, "localhost", sizeof(hostname));
- }
- else
+ if (_cups_strcasecmp(common_name, cert_name))
{
/*
- * Otherwise make sure the hostname we have does not end in a trailing dot.
+ * Not an exact match for the common name, check for wildcard certs...
*/
- strlcpy(hostname, http->hostname, sizeof(hostname));
- if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
- *hostptr == '.')
- *hostptr = '\0';
+ const char *domain = strchr(common_name, '.');
+ /* Domain in common name */
+
+ if (strncmp(cert_name, "*.", 2) || !domain || _cups_strcasecmp(domain, cert_name + 1))
+ {
+ /*
+ * Not a wildcard match.
+ */
+
+ /* TODO: Check subject alternate names */
+ valid = 0;
+ }
}
-# ifdef HAVE_LIBSSL
- context = SSL_CTX_new(SSLv23_client_method());
+ return (valid);
+}
- SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
- bio = BIO_new(_httpBIOMethods());
- BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
+/*
+ * 'httpCredentialsGetTrust()' - Return the trust of credentials.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
- http->tls = SSL_new(context);
- SSL_set_bio(http->tls, bio, bio);
+http_trust_t /* O - Level of trust */
+httpCredentialsGetTrust(
+ cups_array_t *credentials, /* I - Credentials */
+ const char *common_name) /* I - Common name for trust lookup */
+{
+ http_trust_t trust = HTTP_TRUST_OK; /* Level of trust */
+ PCCERT_CONTEXT cert = NULL; /* Certificate to validate */
+ DWORD certFlags = 0; /* Cert verification flags */
+ _cups_globals_t *cg = _cupsGlobals(); /* Per-thread global data */
-# ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
- SSL_set_tlsext_host_name(http->tls, hostname);
-# endif /* HAVE_SSL_SET_TLSEXT_HOST_NAME */
- if (SSL_connect(http->tls) != 1)
- {
- unsigned long error; /* Error code */
+ if (!common_name)
+ return (HTTP_TRUST_UNKNOWN);
- while ((error = ERR_get_error()) != 0)
- {
- message = ERR_error_string(error, NULL);
- DEBUG_printf(("8http_setup_ssl: %s", message));
- }
+ cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
+ if (!cert)
+ return (HTTP_TRUST_UNKNOWN);
- SSL_CTX_free(context);
- SSL_free(http->tls);
- http->tls = NULL;
+ if (cg->any_root < 0)
+ _cupsSetDefaults();
-# ifdef WIN32
- http->error = WSAGetLastError();
-# else
- http->error = errno;
-# endif /* WIN32 */
- http->status = HTTP_STATUS_ERROR;
+ if (cg->any_root)
+ certFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
- if (!message)
- message = _("Unable to establish a secure connection to host.");
+ if (cg->expired_certs)
+ certFlags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
- _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
+ if (!cg->validate_certs)
+ certFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
- return (-1);
- }
+ if (http_sspi_verify(cert, common_name, certFlags) != SEC_E_OK)
+ trust = HTTP_TRUST_INVALID;
-# elif defined(HAVE_GNUTLS)
- credentials = (gnutls_certificate_client_credentials *)
- malloc(sizeof(gnutls_certificate_client_credentials));
- if (credentials == NULL)
- {
- DEBUG_printf(("8http_setup_ssl: Unable to allocate credentials: %s",
- strerror(errno)));
- http->error = errno;
- http->status = HTTP_STATUS_ERROR;
- _cupsSetHTTPError(HTTP_STATUS_ERROR);
+ CertFreeCertificateContext(cert);
- return (-1);
- }
+ return (trust);
+}
- gnutls_certificate_allocate_credentials(credentials);
- gnutls_init(&http->tls, GNUTLS_CLIENT);
- gnutls_set_default_priority(http->tls);
- gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname,
- strlen(hostname));
- gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
- gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr)http);
- gnutls_transport_set_pull_function(http->tls, _httpReadGNUTLS);
- gnutls_transport_set_push_function(http->tls, _httpWriteGNUTLS);
+/*
+ * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
+
+time_t /* O - Expiration date of credentials */
+httpCredentialsGetExpiration(
+ cups_array_t *credentials) /* I - Credentials */
+{
+ time_t expiration_date = 0; /* Expiration data of credentials */
+ PCCERT_CONTEXT cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
+ /* Certificate */
- while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
+ if (cert)
{
- DEBUG_printf(("8http_setup_ssl: gnutls_handshake returned %d (%s)",
- status, gnutls_strerror(status)));
+ SYSTEMTIME systime; /* System time */
+ struct tm tm; /* UNIX date/time */
- if (gnutls_error_is_fatal(status))
- {
- http->error = EIO;
- http->status = HTTP_STATUS_ERROR;
+ FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime);
- _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
+ tm.tm_year = systime.wYear - 1900;
+ tm.tm_mon = systime.wMonth - 1;
+ tm.tm_mday = systime.wDay;
+ tm.tm_hour = systime.wHour;
+ tm.tm_min = systime.wMinute;
+ tm.tm_sec = systime.wSecond;
- gnutls_deinit(http->tls);
- gnutls_certificate_free_credentials(*credentials);
- free(credentials);
- http->tls = NULL;
+ expiration_date = mktime(&tm);
- return (-1);
- }
+ CertFreeCertificateContext(cert);
}
- http->tls_credentials = credentials;
+ return (expiration_date);
+}
-# elif defined(HAVE_CDSASSL)
- if ((http->tls = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide,
- kSSLStreamType)) == NULL)
- {
- DEBUG_puts("4http_setup_ssl: SSLCreateContext failed.");
- http->error = errno = ENOMEM;
- http->status = HTTP_STATUS_ERROR;
- _cupsSetHTTPError(HTTP_STATUS_ERROR);
- return (-1);
- }
+/*
+ * 'httpCredentialsString()' - Return a string representing the credentials.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
- error = SSLSetConnection(http->tls, http);
- DEBUG_printf(("4http_setup_ssl: SSLSetConnection, error=%d", (int)error));
+size_t /* O - Total size of credentials string */
+httpCredentialsString(
+ cups_array_t *credentials, /* I - Credentials */
+ char *buffer, /* I - Buffer or @code NULL@ */
+ size_t bufsize) /* I - Size of buffer */
+{
+ http_credential_t *first = (http_credential_t *)cupsArrayFirst(credentials);
+ /* First certificate */
+ PCCERT_CONTEXT cert; /* Certificate */
- if (!error)
- {
- error = SSLSetIOFuncs(http->tls, _httpReadCDSA, _httpWriteCDSA);
- DEBUG_printf(("4http_setup_ssl: SSLSetIOFuncs, error=%d", (int)error));
- }
- if (!error)
- {
- error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
- true);
- DEBUG_printf(("4http_setup_ssl: SSLSetSessionOption, error=%d",
- (int)error));
- }
+ DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
- if (!error)
+ if (!buffer)
+ return (0);
+
+ if (buffer && bufsize > 0)
+ *buffer = '\0';
+
+ cert = http_sspi_create_credential(first);
+
+ if (cert)
{
- if (cg->client_cert_cb)
+ char cert_name[256]; /* Common name */
+ SYSTEMTIME systime; /* System time */
+ struct tm tm; /* UNIX date/time */
+ time_t expiration; /* Expiration date of cert */
+ unsigned char md5_digest[16]; /* MD5 result */
+
+ FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime);
+
+ tm.tm_year = systime.wYear - 1900;
+ tm.tm_mon = systime.wMonth - 1;
+ tm.tm_mday = systime.wDay;
+ tm.tm_hour = systime.wHour;
+ tm.tm_min = systime.wMinute;
+ tm.tm_sec = systime.wSecond;
+
+ expiration = mktime(&tm);
+
+ if (CertNameToStrA(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
{
- error = SSLSetSessionOption(http->tls,
- kSSLSessionOptionBreakOnCertRequested, true);
- DEBUG_printf(("4http_setup_ssl: kSSLSessionOptionBreakOnCertRequested, "
- "error=%d", (int)error));
+ /*
+ * Extract common name at end...
+ */
+
+ char *ptr = strrchr(cert_name, ',');
+ if (ptr && ptr[1])
+ _cups_strcpy(cert_name, ptr + 2);
}
else
- {
- error = http_set_credentials(http);
- DEBUG_printf(("4http_setup_ssl: http_set_credentials, error=%d",
- (int)error));
- }
- }
+ strlcpy(cert_name, "unknown", sizeof(cert_name));
- /*
- * Let the server know which hostname/domain we are trying to connect to
- * in case it wants to serve up a certificate with a matching common name.
- */
+ cupsHashData("md5", first->data, first->datalen, md5_digest, sizeof(md5_digest));
- if (!error)
- {
- error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
+ snprintf(buffer, bufsize, "%s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", cert_name, httpGetDateString(expiration), md5_digest[0], md5_digest[1], md5_digest[2], md5_digest[3], md5_digest[4], md5_digest[5], md5_digest[6], md5_digest[7], md5_digest[8], md5_digest[9], md5_digest[10], md5_digest[11], md5_digest[12], md5_digest[13], md5_digest[14], md5_digest[15]);
- DEBUG_printf(("4http_setup_ssl: SSLSetPeerDomainName, error=%d",
- (int)error));
+ CertFreeCertificateContext(cert);
}
- if (!error)
- {
- int done = 0; /* Are we done yet? */
+ DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
- while (!error && !done)
- {
- error = SSLHandshake(http->tls);
+ return (strlen(buffer));
+}
- DEBUG_printf(("4http_setup_ssl: SSLHandshake returned %d.", (int)error));
- switch (error)
- {
- case noErr :
- done = 1;
- break;
-
- case errSSLWouldBlock :
- error = noErr; /* Force a retry */
- usleep(1000); /* in 1 millisecond */
- break;
-
- case errSSLServerAuthCompleted :
- error = 0;
- if (cg->server_cert_cb)
- {
- error = httpCopyCredentials(http, &credentials);
- if (!error)
- {
- error = (cg->server_cert_cb)(http, http->tls, credentials,
- cg->server_cert_data);
- httpFreeCredentials(credentials);
- }
-
- DEBUG_printf(("4http_setup_ssl: Server certificate callback "
- "returned %d.", (int)error));
- }
- break;
-
- case errSSLClientCertRequested :
- error = 0;
-
- if (cg->client_cert_cb)
- {
- names = NULL;
- if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
- dn_array)
- {
- if ((names = cupsArrayNew(NULL, NULL)) != NULL)
- {
- for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
- {
- data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
-
- if ((credential = malloc(sizeof(*credential))) != NULL)
- {
- credential->datalen = CFDataGetLength(data);
- if ((credential->data = malloc(credential->datalen)))
- {
- memcpy((void *)credential->data, CFDataGetBytePtr(data),
- credential->datalen);
- cupsArrayAdd(names, credential);
- }
- else
- free(credential);
- }
- }
- }
-
- CFRelease(dn_array);
- }
-
- if (!error)
- {
- error = (cg->client_cert_cb)(http, http->tls, names,
- cg->client_cert_data);
-
- DEBUG_printf(("4http_setup_ssl: Client certificate callback "
- "returned %d.", (int)error));
- }
-
- httpFreeCredentials(names);
- }
- break;
-
- case errSSLUnknownRootCert :
- message = _("Unable to establish a secure connection to host "
- "(untrusted certificate).");
- break;
-
- case errSSLNoRootCert :
- message = _("Unable to establish a secure connection to host "
- "(self-signed certificate).");
- break;
-
- case errSSLCertExpired :
- message = _("Unable to establish a secure connection to host "
- "(expired certificate).");
- break;
-
- case errSSLCertNotYetValid :
- message = _("Unable to establish a secure connection to host "
- "(certificate not yet valid).");
- break;
-
- case errSSLHostNameMismatch :
- message = _("Unable to establish a secure connection to host "
- "(host name mismatch).");
- break;
-
- case errSSLXCertChainInvalid :
- message = _("Unable to establish a secure connection to host "
- "(certificate chain invalid).");
- break;
-
- case errSSLConnectionRefused :
- message = _("Unable to establish a secure connection to host "
- "(peer dropped connection before responding).");
- break;
-
- default :
- break;
- }
- }
- }
+/*
+ * '_httpFreeCredentials()' - Free internal credentials.
+ */
- if (error)
- {
- http->error = error;
- http->status = HTTP_STATUS_ERROR;
- errno = ECONNREFUSED;
+void
+_httpFreeCredentials(
+ http_tls_credentials_t credentials) /* I - Internal credentials */
+{
+ if (!credentials)
+ return;
- CFRelease(http->tls);
- http->tls = NULL;
+ CertFreeCertificateContext(credentials);
+}
- /*
- * If an error string wasn't set by the callbacks use a generic one...
- */
- if (!message)
-#ifdef HAVE_CSSMERRORSTRING
- message = cssmErrorString(error);
-#else
- message = _("Unable to establish a secure connection to host.");
-#endif /* HAVE_CSSMERRORSTRING */
+/*
+ * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
+ *
+ * @since CUPS 2.0/OS 10.10@
+ */
+
+int /* O - 0 on success, -1 on error */
+httpLoadCredentials(
+ const char *path, /* I - Keychain path or @code NULL@ for default */
+ cups_array_t **credentials, /* IO - Credentials */
+ const char *common_name) /* I - Common name for credentials */
+{
+ HCERTSTORE store = NULL; /* Certificate store */
+ PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
+ DWORD dwSize = 0; /* 32 bit size */
+ PBYTE p = NULL; /* Temporary storage */
+ HCRYPTPROV hProv = (HCRYPTPROV)NULL;
+ /* Handle to a CSP */
+ CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
+#ifdef DEBUG
+ char error[1024]; /* Error message buffer */
+#endif /* DEBUG */
- _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
- return (-1);
- }
+ DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
-# elif defined(HAVE_SSPISSL)
- http->tls = _sspiAlloc();
+ (void)path;
- if (!http->tls)
+ if (credentials)
{
- _cupsSetHTTPError(HTTP_STATUS_ERROR);
+ *credentials = NULL;
+ }
+ else
+ {
+ DEBUG_puts("1httpLoadCredentials: NULL credentials pointer, returning -1.");
return (-1);
}
- http->tls->sock = http->fd;
- dwSize = sizeof(username) / sizeof(TCHAR);
- GetUserName(username, &dwSize);
- _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR),
- sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username);
-
- if (!_sspiGetCredentials(http->tls_credentials, L"ClientContainer",
- commonName, FALSE))
+ if (!common_name)
{
- _sspiFree(http->tls_credentials);
- http->tls_credentials = NULL;
+ DEBUG_puts("1httpLoadCredentials: Bad common name, returning -1.");
+ return (-1);
+ }
- http->error = EIO;
- http->status = HTTP_STATUS_ERROR;
+ if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
+ {
+ if (GetLastError() == NTE_EXISTS)
+ {
+ if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
+ {
+ DEBUG_printf(("1httpLoadCredentials: CryptAcquireContext failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+ goto cleanup;
+ }
+ }
+ }
- _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
- _("Unable to establish a secure connection to host."), 1);
+ store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
- return (-1);
+ if (!store)
+ {
+ DEBUG_printf(("1httpLoadCredentials: CertOpenSystemStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+ goto cleanup;
}
- _sspiSetAllowsAnyRoot(http->tls_credentials, TRUE);
- _sspiSetAllowsExpiredCerts(http->tls_credentials, TRUE);
+ dwSize = 0;
- if (!_sspiConnect(http->tls_credentials, hostname))
+ if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
{
- _sspiFree(http->tls_credentials);
- http->tls_credentials = NULL;
-
- http->error = EIO;
- http->status = HTTP_STATUS_ERROR;
+ DEBUG_printf(("1httpLoadCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+ goto cleanup;
+ }
- _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
- _("Unable to establish a secure connection to host."), 1);
+ p = (PBYTE)malloc(dwSize);
- return (-1);
+ if (!p)
+ {
+ DEBUG_printf(("1httpLoadCredentials: malloc failed for %d bytes.", dwSize));
+ goto cleanup;
}
-# endif /* HAVE_CDSASSL */
-
- return (0);
-}
+ if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
+ {
+ DEBUG_printf(("1httpLoadCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+ goto cleanup;
+ }
-/*
- * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection.
- */
+ sib.cbData = dwSize;
+ sib.pbData = p;
-static void
-http_shutdown_ssl(http_t *http) /* I - Connection to server */
-{
-# ifdef HAVE_LIBSSL
- SSL_CTX *context; /* Context for encryption */
+ storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &sib, NULL);
- context = SSL_get_SSL_CTX(http->tls);
+ if (!storedContext)
+ {
+ DEBUG_printf(("1httpLoadCredentials: Unable to find credentials for \"%s\".", common_name));
+ goto cleanup;
+ }
- SSL_shutdown(http->tls);
- SSL_CTX_free(context);
- SSL_free(http->tls);
+ *credentials = cupsArrayNew(NULL, NULL);
+ httpAddCredential(*credentials, storedContext->pbCertEncoded, storedContext->cbCertEncoded);
-# elif defined(HAVE_GNUTLS)
- gnutls_certificate_client_credentials *credentials;
- /* TLS credentials */
+cleanup:
- credentials = (gnutls_certificate_client_credentials *)(http->tls_credentials);
+ /*
+ * Cleanup
+ */
- gnutls_bye(http->tls, GNUTLS_SHUT_RDWR);
- gnutls_deinit(http->tls);
- gnutls_certificate_free_credentials(*credentials);
- free(credentials);
+ if (storedContext)
+ CertFreeCertificateContext(storedContext);
-# elif defined(HAVE_CDSASSL)
- while (SSLClose(http->tls) == errSSLWouldBlock)
- usleep(1000);
+ if (p)
+ free(p);
- CFRelease(http->tls);
+ if (store)
+ CertCloseStore(store, 0);
- if (http->tls_credentials)
- CFRelease(http->tls_credentials);
+ if (hProv)
+ CryptReleaseContext(hProv, 0);
-# elif defined(HAVE_SSPISSL)
- _sspiFree(http->tls_credentials);
-# endif /* HAVE_LIBSSL */
+ DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials ? 0 : -1));
- http->tls = NULL;
- http->tls_credentials = NULL;
+ return (*credentials ? 0 : -1);
}
-#endif /* HAVE_SSL */
-#ifdef HAVE_SSL
/*
- * 'http_write_ssl()' - Write to a SSL/TLS connection.
+ * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
+ *
+ * @since CUPS 2.0/OS 10.10@
*/
-static int /* O - Bytes written */
-http_write_ssl(http_t *http, /* I - Connection to server */
- const char *buf, /* I - Buffer holding data */
- int len) /* I - Length of buffer */
+int /* O - -1 on error, 0 on success */
+httpSaveCredentials(
+ const char *path, /* I - Keychain path or @code NULL@ for default */
+ cups_array_t *credentials, /* I - Credentials */
+ const char *common_name) /* I - Common name for credentials */
{
- ssize_t result; /* Return value */
-
+ HCERTSTORE store = NULL; /* Certificate store */
+ PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
+ PCCERT_CONTEXT createdContext = NULL; /* Context created by us */
+ DWORD dwSize = 0; /* 32 bit size */
+ PBYTE p = NULL; /* Temporary storage */
+ HCRYPTPROV hProv = (HCRYPTPROV)NULL;
+ /* Handle to a CSP */
+ CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */
+ int ret = -1; /* Return value */
+#ifdef DEBUG
+ char error[1024]; /* Error message buffer */
+#endif /* DEBUG */
- DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
-# if defined(HAVE_LIBSSL)
- result = SSL_write((SSL *)(http->tls), buf, len);
+ DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
-# elif defined(HAVE_GNUTLS)
- result = gnutls_record_send(http->tls, buf, len);
+ (void)path;
- if (result < 0 && !errno)
+ if (!common_name)
{
- /*
- * Convert GNU TLS error to errno value...
- */
+ DEBUG_puts("1httpSaveCredentials: Bad common name, returning -1.");
+ return (-1);
+ }
- switch (result)
- {
- case GNUTLS_E_INTERRUPTED :
- errno = EINTR;
- break;
-
- case GNUTLS_E_AGAIN :
- errno = EAGAIN;
- break;
-
- default :
- errno = EPIPE;
- break;
- }
-
- result = -1;
- }
-
-# elif defined(HAVE_CDSASSL)
- OSStatus error; /* Error info */
- size_t processed; /* Number of bytes processed */
-
-
- error = SSLWrite(http->tls, buf, len, &processed);
-
- switch (error)
- {
- case 0 :
- result = (int)processed;
- break;
-
- case errSSLWouldBlock :
- if (processed)
- result = (int)processed;
- else
- {
- result = -1;
- errno = EINTR;
- }
- break;
-
- case errSSLClosedGraceful :
- default :
- if (processed)
- result = (int)processed;
- else
- {
- result = -1;
- errno = EPIPE;
- }
- break;
- }
-# elif defined(HAVE_SSPISSL)
- return _sspiWrite((_sspi_struct_t *)http->tls, (void *)buf, len);
-# endif /* HAVE_LIBSSL */
-
- DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
-
- return ((int)result);
-}
-#endif /* HAVE_SSL */
-
-
-#ifdef HAVE_SSL
-/*
- * 'http_write_ssl()' - Write to a SSL/TLS connection.
- */
-
-static int /* O - Bytes written */
-http_write_ssl(http_t *http, /* I - Connection to server */
- const char *buf, /* I - Buffer holding data */
- int len) /* I - Length of buffer */
-{
- ssize_t result; /* Return value */
-
-
- DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
-
-# if defined(HAVE_LIBSSL)
- result = SSL_write((SSL *)(http->tls), buf, len);
-
-# elif defined(HAVE_GNUTLS)
- result = gnutls_record_send(http->tls, buf, len);
-
- if (result < 0 && !errno)
- {
- /*
- * Convert GNU TLS error to errno value...
- */
-
- switch (result)
- {
- case GNUTLS_E_INTERRUPTED :
- errno = EINTR;
- break;
-
- case GNUTLS_E_AGAIN :
- errno = EAGAIN;
- break;
-
- default :
- errno = EPIPE;
- break;
- }
-
- result = -1;
- }
-
-# elif defined(HAVE_CDSASSL)
- OSStatus error; /* Error info */
- size_t processed; /* Number of bytes processed */
-
-
- error = SSLWrite(http->tls, buf, len, &processed);
-
- switch (error)
+ createdContext = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
+ if (!createdContext)
{
- case 0 :
- result = (int)processed;
- break;
-
- case errSSLWouldBlock :
- if (processed)
- result = (int)processed;
- else
- {
- result = -1;
- errno = EINTR;
- }
- break;
-
- case errSSLClosedGraceful :
- default :
- if (processed)
- result = (int)processed;
- else
- {
- result = -1;
- errno = EPIPE;
- }
- break;
+ DEBUG_puts("1httpSaveCredentials: Bad credentials, returning -1.");
+ return (-1);
}
-# elif defined(HAVE_SSPISSL)
- return _sspiWrite((_sspi_struct_t *)http->tls, (void *)buf, len);
-# endif /* HAVE_LIBSSL */
-
- DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
-
- return ((int)result);
-}
-#endif /* HAVE_SSL */
-
-
-/*
- * 'sspi_alloc()' - Allocate SSPI ssl object
- */
-_sspi_struct_t* /* O - New SSPI/SSL object */
-_sspiAlloc(void)
-{
- _sspi_struct_t *conn = calloc(sizeof(_sspi_struct_t), 1);
-
- if (conn)
- conn->sock = INVALID_SOCKET;
-
- return (conn);
-}
-
-/*
- * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store
- * If one cannot be found, one is created.
- */
-BOOL /* O - 1 on success, 0 on failure */
-_sspiGetCredentials(_sspi_struct_t *conn,
- /* I - Client connection */
- const LPWSTR container,
- /* I - Cert container name */
- const TCHAR *cn, /* I - Common name of certificate */
- BOOL isServer)
- /* I - Is caller a server? */
-{
- HCERTSTORE store = NULL; /* Certificate store */
- PCCERT_CONTEXT storedContext = NULL;
- /* Context created from the store */
- PCCERT_CONTEXT createdContext = NULL;
- /* Context created by us */
- DWORD dwSize = 0; /* 32 bit size */
- PBYTE p = NULL; /* Temporary storage */
- HCRYPTPROV hProv = (HCRYPTPROV) NULL;
- /* Handle to a CSP */
- CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
- SCHANNEL_CRED SchannelCred; /* Schannel credential data */
- TimeStamp tsExpiry; /* Time stamp */
- SECURITY_STATUS Status; /* Status */
- HCRYPTKEY hKey = (HCRYPTKEY) NULL;
- /* Handle to crypto key */
- CRYPT_KEY_PROV_INFO kpi; /* Key container info */
- SYSTEMTIME et; /* System time */
- CERT_EXTENSIONS exts; /* Array of cert extensions */
- CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */
- BOOL ok = TRUE; /* Return value */
-
- if (!conn)
- return (FALSE);
- if (!cn)
- return (FALSE);
-
- if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W,
- PROV_RSA_FULL,
- CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
+ if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
{
if (GetLastError() == NTE_EXISTS)
{
- if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W,
- PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
+ if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
{
- DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n",
- GetLastError()));
- ok = FALSE;
+ DEBUG_printf(("1httpSaveCredentials: CryptAcquireContext failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
goto cleanup;
}
}
}
- store = CertOpenStore(CERT_STORE_PROV_SYSTEM,
- X509_ASN_ENCODING|PKCS_7_ASN_ENCODING,
- hProv,
- CERT_SYSTEM_STORE_LOCAL_MACHINE |
- CERT_STORE_NO_CRYPT_RELEASE_FLAG |
- CERT_STORE_OPEN_EXISTING_FLAG,
- L"MY");
+ store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
if (!store)
{
- DEBUG_printf(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n",
- GetLastError()));
- ok = FALSE;
+ DEBUG_printf(("1httpSaveCredentials: CertOpenSystemStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
goto cleanup;
}
dwSize = 0;
- if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR,
- NULL, NULL, &dwSize, NULL))
+ if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
{
- DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n",
- GetLastError()));
- ok = FALSE;
+ DEBUG_printf(("1httpSaveCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
goto cleanup;
}
- p = (PBYTE) malloc(dwSize);
+ p = (PBYTE)malloc(dwSize);
if (!p)
{
- DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize));
- ok = FALSE;
+ DEBUG_printf(("1httpSaveCredentials: malloc failed for %d bytes.", dwSize));
goto cleanup;
}
- if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR, NULL,
- p, &dwSize, NULL))
+ if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
{
- DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x",
- GetLastError()));
- ok = FALSE;
+ DEBUG_printf(("1httpSaveCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
goto cleanup;
}
- sib.cbData = dwSize;
- sib.pbData = p;
-
- storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING,
- 0, CERT_FIND_SUBJECT_NAME, &sib, NULL);
+ /*
+ * Add the created context to the named store, and associate it with the named
+ * container...
+ */
- if (!storedContext)
+ if (!CertAddCertificateContextToStore(store, createdContext, CERT_STORE_ADD_REPLACE_EXISTING, &storedContext))
{
- /*
- * If we couldn't find the context, then we'll
- * create a new one
- */
- if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey))
- {
- DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x",
- GetLastError()));
- ok = FALSE;
- goto cleanup;
- }
-
- ZeroMemory(&kpi, sizeof(kpi));
- kpi.pwszContainerName = (LPWSTR) container;
- kpi.pwszProvName = MS_DEF_PROV_W;
- kpi.dwProvType = PROV_RSA_FULL;
- kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
- kpi.dwKeySpec = AT_KEYEXCHANGE;
-
- GetSystemTime(&et);
- et.wYear += 10;
-
- ZeroMemory(&exts, sizeof(exts));
-
- createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL,
- &et, &exts);
-
- if (!createdContext)
- {
- DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x",
- GetLastError()));
- ok = FALSE;
- goto cleanup;
- }
-
- if (!CertAddCertificateContextToStore(store, createdContext,
- CERT_STORE_ADD_REPLACE_EXISTING,
- &storedContext))
- {
- DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x",
- GetLastError()));
- ok = FALSE;
- goto cleanup;
- }
-
- ZeroMemory(&ckp, sizeof(ckp));
- ckp.pwszContainerName = (LPWSTR) container;
- ckp.pwszProvName = MS_DEF_PROV_W;
- ckp.dwProvType = PROV_RSA_FULL;
- ckp.dwFlags = CRYPT_MACHINE_KEYSET;
- ckp.dwKeySpec = AT_KEYEXCHANGE;
-
- if (!CertSetCertificateContextProperty(storedContext,
- CERT_KEY_PROV_INFO_PROP_ID,
- 0, &ckp))
- {
- DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x",
- GetLastError()));
- ok = FALSE;
- goto cleanup;
- }
+ DEBUG_printf(("1httpSaveCredentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
+ goto cleanup;
}
- ZeroMemory(&SchannelCred, sizeof(SchannelCred));
-
- SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
- SchannelCred.cCreds = 1;
- SchannelCred.paCred = &storedContext;
-
- /*
- * SSPI doesn't seem to like it if grbitEnabledProtocols
- * is set for a client
- */
- if (isServer)
- SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
+ ZeroMemory(&ckp, sizeof(ckp));
+ ckp.pwszContainerName = L"RememberedContainer";
+ ckp.pwszProvName = MS_DEF_PROV_W;
+ ckp.dwProvType = PROV_RSA_FULL;
+ ckp.dwFlags = CRYPT_MACHINE_KEYSET;
+ ckp.dwKeySpec = AT_KEYEXCHANGE;
- /*
- * Create an SSPI credential.
- */
- Status = AcquireCredentialsHandle(NULL, UNISP_NAME,
- isServer ? SECPKG_CRED_INBOUND:SECPKG_CRED_OUTBOUND,
- NULL, &SchannelCred, NULL, NULL, &conn->creds,
- &tsExpiry);
- if (Status != SEC_E_OK)
+ if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp))
{
- DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status));
- ok = FALSE;
+ DEBUG_printf(("1httpSaveCredentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
goto cleanup;
}
+ ret = 0;
+
cleanup:
/*
* Cleanup
*/
- if (hKey)
- CryptDestroyKey(hKey);
if (createdContext)
CertFreeCertificateContext(createdContext);
if (hProv)
CryptReleaseContext(hProv, 0);
- return (ok);
+ DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret));
+ return (ret);
}
/*
- * '_sspiConnect()' - Make an SSL connection. This function
- * assumes a TCP/IP connection has already
- * been successfully made
+ * '_httpTLSInitialize()' - Initialize the TLS stack.
*/
-BOOL /* O - 1 on success, 0 on failure */
-_sspiConnect(_sspi_struct_t *conn, /* I - Client connection */
- const CHAR *hostname) /* I - Server hostname */
-{
- PCCERT_CONTEXT serverCert; /* Server certificate */
- DWORD dwSSPIFlags; /* SSL connection attributes we want */
- DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
- TimeStamp tsExpiry; /* Time stamp */
- SECURITY_STATUS scRet; /* Status */
- DWORD cbData; /* Data count */
- SecBufferDesc inBuffer; /* Array of SecBuffer structs */
- SecBuffer inBuffers[2]; /* Security package buffer */
- SecBufferDesc outBuffer; /* Array of SecBuffer structs */
- SecBuffer outBuffers[1]; /* Security package buffer */
- BOOL ok = TRUE; /* Return value */
-
- serverCert = NULL;
-
- dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
- ISC_REQ_REPLAY_DETECT |
- ISC_REQ_CONFIDENTIALITY |
- ISC_RET_EXTENDED_ERROR |
- ISC_REQ_ALLOCATE_MEMORY |
- ISC_REQ_STREAM;
+void
+_httpTLSInitialize(void)
+{
/*
- * Initiate a ClientHello message and generate a token.
+ * Nothing to do...
*/
- outBuffers[0].pvBuffer = NULL;
- outBuffers[0].BufferType = SECBUFFER_TOKEN;
- outBuffers[0].cbBuffer = 0;
+}
- outBuffer.cBuffers = 1;
- outBuffer.pBuffers = outBuffers;
- outBuffer.ulVersion = SECBUFFER_VERSION;
- scRet = InitializeSecurityContext(&conn->creds, NULL, TEXT(""), dwSSPIFlags,
- 0, SECURITY_NATIVE_DREP, NULL, 0, &conn->context,
- &outBuffer, &dwSSPIOutFlags, &tsExpiry);
+/*
+ * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
+ */
- if (scRet != SEC_I_CONTINUE_NEEDED)
- {
- DEBUG_printf(("_sspiConnect: InitializeSecurityContext(1) failed: %x", scRet));
- ok = FALSE;
- goto cleanup;
- }
+size_t /* O - Bytes available */
+_httpTLSPending(http_t *http) /* I - HTTP connection */
+{
+ if (http->tls)
+ return (http->tls->readBufferUsed);
+ else
+ return (0);
+}
+
+
+/*
+ * '_httpTLSRead()' - Read from a SSL/TLS connection.
+ */
+
+int /* O - Bytes read */
+_httpTLSRead(http_t *http, /* I - HTTP connection */
+ char *buf, /* I - Buffer to store data */
+ int len) /* I - Length of buffer */
+{
+ int i; /* Looping var */
+ _http_sspi_t *sspi = http->tls; /* SSPI data */
+ SecBufferDesc message; /* Array of SecBuffer struct */
+ SecBuffer buffers[4] = { 0 }; /* Security package buffer */
+ int num = 0; /* Return value */
+ PSecBuffer pDataBuffer; /* Data buffer */
+ PSecBuffer pExtraBuffer; /* Excess data buffer */
+ SECURITY_STATUS scRet; /* SSPI status */
+
+
+ DEBUG_printf(("4_httpTLSRead(http=%p, buf=%p, len=%d)", http, buf, len));
/*
- * Send response to server if there is one.
+ * If there are bytes that have already been decrypted and have not yet been
+ * read, return those...
*/
- if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
- {
- cbData = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
- if ((cbData == SOCKET_ERROR) || !cbData)
- {
- DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
- FreeContextBuffer(outBuffers[0].pvBuffer);
- DeleteSecurityContext(&conn->context);
- ok = FALSE;
- goto cleanup;
- }
+ if (sspi->readBufferUsed > 0)
+ {
+ int bytesToCopy = min(sspi->readBufferUsed, len);
+ /* Number of bytes to copy */
- DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData));
+ memcpy(buf, sspi->readBuffer, bytesToCopy);
+ sspi->readBufferUsed -= bytesToCopy;
- /*
- * Free output buffer.
- */
- FreeContextBuffer(outBuffers[0].pvBuffer);
- outBuffers[0].pvBuffer = NULL;
- }
+ if (sspi->readBufferUsed > 0)
+ memmove(sspi->readBuffer, sspi->readBuffer + bytesToCopy, sspi->readBufferUsed);
- dwSSPIFlags = ISC_REQ_MANUAL_CRED_VALIDATION |
- ISC_REQ_SEQUENCE_DETECT |
- ISC_REQ_REPLAY_DETECT |
- ISC_REQ_CONFIDENTIALITY |
- ISC_RET_EXTENDED_ERROR |
- ISC_REQ_ALLOCATE_MEMORY |
- ISC_REQ_STREAM;
+ DEBUG_printf(("5_httpTLSRead: Returning %d bytes previously decrypted.", bytesToCopy));
- conn->decryptBufferUsed = 0;
+ return (bytesToCopy);
+ }
/*
- * Loop until the handshake is finished or an error occurs.
+ * Initialize security buffer structs
*/
- scRet = SEC_I_CONTINUE_NEEDED;
- while(scRet == SEC_I_CONTINUE_NEEDED ||
- scRet == SEC_E_INCOMPLETE_MESSAGE ||
- scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+ message.ulVersion = SECBUFFER_VERSION;
+ message.cBuffers = 4;
+ message.pBuffers = buffers;
+
+ do
{
- if ((conn->decryptBufferUsed == 0) || (scRet == SEC_E_INCOMPLETE_MESSAGE))
+ /*
+ * If there is not enough space in the buffer, then increase its size...
+ */
+
+ if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
{
- if (conn->decryptBufferLength <= conn->decryptBufferUsed)
+ BYTE *temp; /* New buffer */
+
+ if (sspi->decryptBufferLength >= 262144)
{
- conn->decryptBufferLength += 4096;
- conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer, conn->decryptBufferLength);
+ WSASetLastError(E_OUTOFMEMORY);
+ DEBUG_puts("_httpTLSRead: Decryption buffer too large (>256k)");
+ return (-1);
+ }
- if (!conn->decryptBuffer)
- {
- DEBUG_printf(("_sspiConnect: unable to allocate %d byte decrypt buffer",
- conn->decryptBufferLength));
- SetLastError(E_OUTOFMEMORY);
- ok = FALSE;
- goto cleanup;
- }
+ if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
+ {
+ DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte decryption buffer.", sspi->decryptBufferLength + 4096));
+ WSASetLastError(E_OUTOFMEMORY);
+ return (-1);
}
- cbData = recv(conn->sock, conn->decryptBuffer + conn->decryptBufferUsed,
- (int) (conn->decryptBufferLength - conn->decryptBufferUsed), 0);
+ sspi->decryptBufferLength += 4096;
+ sspi->decryptBuffer = temp;
+
+ DEBUG_printf(("_httpTLSRead: Resized decryption buffer to %d bytes.", sspi->decryptBufferLength));
+ }
+
+ buffers[0].pvBuffer = sspi->decryptBuffer;
+ buffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed;
+ buffers[0].BufferType = SECBUFFER_DATA;
+ buffers[1].BufferType = SECBUFFER_EMPTY;
+ buffers[2].BufferType = SECBUFFER_EMPTY;
+ buffers[3].BufferType = SECBUFFER_EMPTY;
+
+ DEBUG_printf(("5_httpTLSRead: decryptBufferUsed=%d", sspi->decryptBufferUsed));
- if (cbData == SOCKET_ERROR)
+ scRet = DecryptMessage(&sspi->context, &message, 0, NULL);
+
+ if (scRet == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ num = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
+ if (num < 0)
{
- DEBUG_printf(("_sspiConnect: recv failed: %d", WSAGetLastError()));
- ok = FALSE;
- goto cleanup;
+ DEBUG_printf(("5_httpTLSRead: recv failed: %d", WSAGetLastError()));
+ return (-1);
}
- else if (cbData == 0)
+ else if (num == 0)
{
- DEBUG_printf(("_sspiConnect: server unexpectedly disconnected"));
- ok = FALSE;
- goto cleanup;
+ DEBUG_puts("5_httpTLSRead: Server disconnected.");
+ return (0);
}
- DEBUG_printf(("_sspiConnect: %d bytes of handshake data received",
- cbData));
+ DEBUG_printf(("5_httpTLSRead: Read %d bytes into decryption buffer.", num));
- conn->decryptBufferUsed += cbData;
+ sspi->decryptBufferUsed += num;
}
+ }
+ while (scRet == SEC_E_INCOMPLETE_MESSAGE);
- /*
- * Set up the input buffers. Buffer 0 is used to pass in data
- * received from the server. Schannel will consume some or all
- * of this. Leftover data (if any) will be placed in buffer 1 and
- * given a buffer type of SECBUFFER_EXTRA.
- */
- inBuffers[0].pvBuffer = conn->decryptBuffer;
- inBuffers[0].cbBuffer = (unsigned long) conn->decryptBufferUsed;
- inBuffers[0].BufferType = SECBUFFER_TOKEN;
+ if (scRet == SEC_I_CONTEXT_EXPIRED)
+ {
+ DEBUG_puts("5_httpTLSRead: Context expired.");
+ WSASetLastError(WSAECONNRESET);
+ return (-1);
+ }
+ else if (scRet != SEC_E_OK)
+ {
+ DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
+ WSASetLastError(WSASYSCALLFAILURE);
+ return (-1);
+ }
- inBuffers[1].pvBuffer = NULL;
- inBuffers[1].cbBuffer = 0;
- inBuffers[1].BufferType = SECBUFFER_EMPTY;
+ /*
+ * The decryption worked. Now, locate data buffer.
+ */
- inBuffer.cBuffers = 2;
- inBuffer.pBuffers = inBuffers;
- inBuffer.ulVersion = SECBUFFER_VERSION;
+ pDataBuffer = NULL;
+ pExtraBuffer = NULL;
- /*
- * Set up the output buffers. These are initialized to NULL
- * so as to make it less likely we'll attempt to free random
- * garbage later.
- */
- outBuffers[0].pvBuffer = NULL;
- outBuffers[0].BufferType= SECBUFFER_TOKEN;
- outBuffers[0].cbBuffer = 0;
+ for (i = 1; i < 4; i++)
+ {
+ if (buffers[i].BufferType == SECBUFFER_DATA)
+ pDataBuffer = &buffers[i];
+ else if (!pExtraBuffer && (buffers[i].BufferType == SECBUFFER_EXTRA))
+ pExtraBuffer = &buffers[i];
+ }
- outBuffer.cBuffers = 1;
- outBuffer.pBuffers = outBuffers;
- outBuffer.ulVersion = SECBUFFER_VERSION;
+ /*
+ * If a data buffer is found, then copy the decrypted bytes to the passed-in
+ * buffer...
+ */
- /*
- * Call InitializeSecurityContext.
- */
- scRet = InitializeSecurityContext(&conn->creds, &conn->context, NULL, dwSSPIFlags,
- 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL,
- &outBuffer, &dwSSPIOutFlags, &tsExpiry);
+ if (pDataBuffer)
+ {
+ int bytesToCopy = min((int)pDataBuffer->cbBuffer, len);
+ /* Number of bytes to copy into buf */
+ int bytesToSave = pDataBuffer->cbBuffer - bytesToCopy;
+ /* Number of bytes to save in our read buffer */
+
+ if (bytesToCopy)
+ memcpy(buf, pDataBuffer->pvBuffer, bytesToCopy);
/*
- * If InitializeSecurityContext was successful (or if the error was
- * one of the special extended ones), send the contends of the output
- * buffer to the server.
+ * If there are more decrypted bytes than can be copied to the passed in
+ * buffer, then save them...
*/
- if (scRet == SEC_E_OK ||
- scRet == SEC_I_CONTINUE_NEEDED ||
- FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
+
+ if (bytesToSave)
{
- if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
+ if ((sspi->readBufferLength - sspi->readBufferUsed) < bytesToSave)
{
- cbData = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
-
- if ((cbData == SOCKET_ERROR) || !cbData)
- {
- DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
- FreeContextBuffer(outBuffers[0].pvBuffer);
- DeleteSecurityContext(&conn->context);
- ok = FALSE;
- goto cleanup;
- }
+ BYTE *temp; /* New buffer pointer */
- DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData));
+ if ((temp = realloc(sspi->readBuffer, sspi->readBufferUsed + bytesToSave)) == NULL)
+ {
+ DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", sspi->readBufferUsed + bytesToSave));
+ WSASetLastError(E_OUTOFMEMORY);
+ return (-1);
+ }
- /*
- * Free output buffer.
- */
- FreeContextBuffer(outBuffers[0].pvBuffer);
- outBuffers[0].pvBuffer = NULL;
+ sspi->readBufferLength = sspi->readBufferUsed + bytesToSave;
+ sspi->readBuffer = temp;
}
- }
- /*
- * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
- * then we need to read more data from the server and try again.
- */
- if (scRet == SEC_E_INCOMPLETE_MESSAGE)
- continue;
+ memcpy(((BYTE *)sspi->readBuffer) + sspi->readBufferUsed, ((BYTE *)pDataBuffer->pvBuffer) + bytesToCopy, bytesToSave);
- /*
- * If InitializeSecurityContext returned SEC_E_OK, then the
- * handshake completed successfully.
- */
- if (scRet == SEC_E_OK)
+ sspi->readBufferUsed += bytesToSave;
+ }
+
+ num = bytesToCopy;
+ }
+ else
+ {
+ DEBUG_puts("_httpTLSRead: Unable to find data buffer.");
+ WSASetLastError(WSASYSCALLFAILURE);
+ return (-1);
+ }
+
+ /*
+ * If the decryption process left extra bytes, then save those back in
+ * decryptBuffer. They will be processed the next time through the loop.
+ */
+
+ if (pExtraBuffer)
+ {
+ memmove(sspi->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
+ sspi->decryptBufferUsed = pExtraBuffer->cbBuffer;
+ }
+ else
+ {
+ sspi->decryptBufferUsed = 0;
+ }
+
+ return (num);
+}
+
+
+/*
+ * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
+ */
+
+void
+_httpTLSSetOptions(int options, /* I - Options */
+ int min_version, /* I - Minimum TLS version */
+ int max_version) /* I - Maximum TLS version */
+{
+ if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
+ {
+ tls_options = options;
+ tls_min_version = min_version;
+ tls_max_version = max_version;
+ }
+}
+
+
+/*
+ * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
+ */
+
+int /* O - 0 on success, -1 on failure */
+_httpTLSStart(http_t *http) /* I - HTTP connection */
+{
+ char hostname[256], /* Hostname */
+ *hostptr; /* Pointer into hostname */
+
+
+ DEBUG_printf(("3_httpTLSStart(http=%p)", http));
+
+ if (tls_options < 0)
+ {
+ DEBUG_puts("4_httpTLSStart: Setting defaults.");
+ _cupsSetDefaults();
+ DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options));
+ }
+
+ if ((http->tls = http_sspi_alloc()) == NULL)
+ return (-1);
+
+ if (http->mode == _HTTP_MODE_CLIENT)
+ {
+ /*
+ * Client: determine hostname...
+ */
+
+ if (httpAddrLocalhost(http->hostaddr))
+ {
+ strlcpy(hostname, "localhost", sizeof(hostname));
+ }
+ else
+ {
+ /*
+ * Otherwise make sure the hostname we have does not end in a trailing dot.
+ */
+
+ strlcpy(hostname, http->hostname, sizeof(hostname));
+ if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
+ *hostptr == '.')
+ *hostptr = '\0';
+ }
+
+ return (http_sspi_client(http, hostname));
+ }
+ else
+ {
+ /*
+ * Server: determine hostname to use...
+ */
+
+ if (http->fields[HTTP_FIELD_HOST])
+ {
+ /*
+ * Use hostname for TLS upgrade...
+ */
+
+ strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
+ }
+ else
+ {
+ /*
+ * Resolve hostname from connection address...
+ */
+
+ http_addr_t addr; /* Connection address */
+ socklen_t addrlen; /* Length of address */
+
+ addrlen = sizeof(addr);
+ if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
+ {
+ DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
+ hostname[0] = '\0';
+ }
+ else if (httpAddrLocalhost(&addr))
+ hostname[0] = '\0';
+ else
+ {
+ httpAddrLookup(&addr, hostname, sizeof(hostname));
+ DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
+ }
+ }
+
+ return (http_sspi_server(http, hostname));
+ }
+}
+
+
+/*
+ * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
+ */
+
+void
+_httpTLSStop(http_t *http) /* I - HTTP connection */
+{
+ _http_sspi_t *sspi = http->tls; /* SSPI data */
+
+
+ if (sspi->contextInitialized && http->fd >= 0)
+ {
+ SecBufferDesc message; /* Array of SecBuffer struct */
+ SecBuffer buffers[1] = { 0 };
+ /* Security package buffer */
+ DWORD dwType; /* Type */
+ DWORD status; /* Status */
+
+ /*
+ * Notify schannel that we are about to close the connection.
+ */
+
+ dwType = SCHANNEL_SHUTDOWN;
+
+ buffers[0].pvBuffer = &dwType;
+ buffers[0].BufferType = SECBUFFER_TOKEN;
+ buffers[0].cbBuffer = sizeof(dwType);
+
+ message.cBuffers = 1;
+ message.pBuffers = buffers;
+ message.ulVersion = SECBUFFER_VERSION;
+
+ status = ApplyControlToken(&sspi->context, &message);
+
+ if (SUCCEEDED(status))
+ {
+ PBYTE pbMessage; /* Message buffer */
+ DWORD cbMessage; /* Message buffer count */
+ DWORD cbData; /* Data count */
+ DWORD dwSSPIFlags; /* SSL attributes we requested */
+ DWORD dwSSPIOutFlags; /* SSL attributes we received */
+ TimeStamp tsExpiry; /* Time stamp */
+
+ dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
+ ASC_REQ_REPLAY_DETECT |
+ ASC_REQ_CONFIDENTIALITY |
+ ASC_REQ_EXTENDED_ERROR |
+ ASC_REQ_ALLOCATE_MEMORY |
+ ASC_REQ_STREAM;
+
+ buffers[0].pvBuffer = NULL;
+ buffers[0].BufferType = SECBUFFER_TOKEN;
+ buffers[0].cbBuffer = 0;
+
+ message.cBuffers = 1;
+ message.pBuffers = buffers;
+ message.ulVersion = SECBUFFER_VERSION;
+
+ status = AcceptSecurityContext(&sspi->creds, &sspi->context, NULL,
+ dwSSPIFlags, SECURITY_NATIVE_DREP, NULL,
+ &message, &dwSSPIOutFlags, &tsExpiry);
+
+ if (SUCCEEDED(status))
+ {
+ pbMessage = buffers[0].pvBuffer;
+ cbMessage = buffers[0].cbBuffer;
+
+ /*
+ * Send the close notify message to the client.
+ */
+
+ if (pbMessage && cbMessage)
+ {
+ cbData = send(http->fd, pbMessage, cbMessage, 0);
+ if ((cbData == SOCKET_ERROR) || (cbData == 0))
+ {
+ status = WSAGetLastError();
+ DEBUG_printf(("_httpTLSStop: sending close notify failed: %d", status));
+ }
+ else
+ {
+ FreeContextBuffer(pbMessage);
+ }
+ }
+ }
+ else
+ {
+ DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), status)));
+ }
+ }
+ else
+ {
+ DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), status)));
+ }
+ }
+
+ http_sspi_free(sspi);
+
+ http->tls = NULL;
+}
+
+
+/*
+ * '_httpTLSWrite()' - Write to a SSL/TLS connection.
+ */
+
+int /* O - Bytes written */
+_httpTLSWrite(http_t *http, /* I - HTTP connection */
+ const char *buf, /* I - Buffer holding data */
+ int len) /* I - Length of buffer */
+{
+ _http_sspi_t *sspi = http->tls; /* SSPI data */
+ SecBufferDesc message; /* Array of SecBuffer struct */
+ SecBuffer buffers[4] = { 0 }; /* Security package buffer */
+ int bufferLen; /* Buffer length */
+ int bytesLeft; /* Bytes left to write */
+ const char *bufptr; /* Pointer into buffer */
+ int num = 0; /* Return value */
+
+
+ bufferLen = sspi->streamSizes.cbMaximumMessage + sspi->streamSizes.cbHeader + sspi->streamSizes.cbTrailer;
+
+ if (bufferLen > sspi->writeBufferLength)
+ {
+ BYTE *temp; /* New buffer pointer */
+
+ if ((temp = (BYTE *)realloc(sspi->writeBuffer, bufferLen)) == NULL)
+ {
+ DEBUG_printf(("_httpTLSWrite: Unable to allocate buffer of %d bytes.", bufferLen));
+ WSASetLastError(E_OUTOFMEMORY);
+ return (-1);
+ }
+
+ sspi->writeBuffer = temp;
+ sspi->writeBufferLength = bufferLen;
+ }
+
+ bytesLeft = len;
+ bufptr = buf;
+
+ while (bytesLeft)
+ {
+ int chunk = min((int)sspi->streamSizes.cbMaximumMessage, bytesLeft);
+ /* Size of data to write */
+ SECURITY_STATUS scRet; /* SSPI status */
+
+ /*
+ * Copy user data into the buffer, starting just past the header...
+ */
+
+ memcpy(sspi->writeBuffer + sspi->streamSizes.cbHeader, bufptr, chunk);
+
+ /*
+ * Setup the SSPI buffers
+ */
+
+ message.ulVersion = SECBUFFER_VERSION;
+ message.cBuffers = 4;
+ message.pBuffers = buffers;
+
+ buffers[0].pvBuffer = sspi->writeBuffer;
+ buffers[0].cbBuffer = sspi->streamSizes.cbHeader;
+ buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
+ buffers[1].pvBuffer = sspi->writeBuffer + sspi->streamSizes.cbHeader;
+ buffers[1].cbBuffer = (unsigned long) chunk;
+ buffers[1].BufferType = SECBUFFER_DATA;
+ buffers[2].pvBuffer = sspi->writeBuffer + sspi->streamSizes.cbHeader + chunk;
+ buffers[2].cbBuffer = sspi->streamSizes.cbTrailer;
+ buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ buffers[3].BufferType = SECBUFFER_EMPTY;
+
+ /*
+ * Encrypt the data
+ */
+
+ scRet = EncryptMessage(&sspi->context, 0, &message, 0);
+
+ if (FAILED(scRet))
+ {
+ DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
+ WSASetLastError(WSASYSCALLFAILURE);
+ return (-1);
+ }
+
+ /*
+ * Send the data. Remember the size of the total data to send is the size
+ * of the header, the size of the data the caller passed in and the size
+ * of the trailer...
+ */
+
+ num = send(http->fd, sspi->writeBuffer, buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer, 0);
+
+ if (num <= 0)
+ {
+ DEBUG_printf(("_httpTLSWrite: send failed: %ld", WSAGetLastError()));
+ return (num);
+ }
+
+ bytesLeft -= chunk;
+ bufptr += chunk;
+ }
+
+ return (len);
+}
+
+
+#if 0
+/*
+ * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+http_setup_ssl(http_t *http) /* I - Connection to server */
+{
+ char hostname[256], /* Hostname */
+ *hostptr; /* Pointer into hostname */
+
+ TCHAR username[256]; /* Username returned from GetUserName() */
+ TCHAR commonName[256];/* Common name for certificate */
+ DWORD dwSize; /* 32 bit size */
+
+
+ DEBUG_printf(("7http_setup_ssl(http=%p)", http));
+
+ /*
+ * Get the hostname to use for SSL...
+ */
+
+ if (httpAddrLocalhost(http->hostaddr))
+ {
+ strlcpy(hostname, "localhost", sizeof(hostname));
+ }
+ else
+ {
+ /*
+ * Otherwise make sure the hostname we have does not end in a trailing dot.
+ */
+
+ strlcpy(hostname, http->hostname, sizeof(hostname));
+ if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
+ *hostptr == '.')
+ *hostptr = '\0';
+ }
+
+ http->tls = http_sspi_alloc();
+
+ if (!http->tls)
+ {
+ _cupsSetHTTPError(HTTP_STATUS_ERROR);
+ return (-1);
+ }
+
+ dwSize = sizeof(username) / sizeof(TCHAR);
+ GetUserName(username, &dwSize);
+ _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR),
+ sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username);
+
+ if (!_sspiGetCredentials(http->tls, L"ClientContainer",
+ commonName, FALSE))
+ {
+ _sspiFree(http->tls);
+ http->tls = NULL;
+
+ http->error = EIO;
+ http->status = HTTP_STATUS_ERROR;
+
+ _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
+ _("Unable to establish a secure connection to host."), 1);
+
+ return (-1);
+ }
+
+ _sspiSetAllowsAnyRoot(http->tls, TRUE);
+ _sspiSetAllowsExpiredCerts(http->tls, TRUE);
+
+ if (!_sspiConnect(http->tls, hostname))
+ {
+ _sspiFree(http->tls);
+ http->tls = NULL;
+
+ http->error = EIO;
+ http->status = HTTP_STATUS_ERROR;
+
+ _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
+ _("Unable to establish a secure connection to host."), 1);
+
+ return (-1);
+ }
+
+ return (0);
+}
+#endif // 0
+
+
+/*
+ * 'http_sspi_alloc()' - Allocate SSPI object.
+ */
+
+static _http_sspi_t * /* O - New SSPI/SSL object */
+http_sspi_alloc(void)
+{
+ return ((_http_sspi_t *)calloc(sizeof(_http_sspi_t), 1));
+}
+
+
+/*
+ * 'http_sspi_client()' - Negotiate a TLS connection as a client.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+http_sspi_client(http_t *http, /* I - Client connection */
+ const char *hostname) /* I - Server hostname */
+{
+ _http_sspi_t *sspi = http->tls; /* SSPI data */
+ DWORD dwSize; /* Size for buffer */
+ DWORD dwSSPIFlags; /* SSL connection attributes we want */
+ DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
+ TimeStamp tsExpiry; /* Time stamp */
+ SECURITY_STATUS scRet; /* Status */
+ int cbData; /* Data count */
+ SecBufferDesc inBuffer; /* Array of SecBuffer structs */
+ SecBuffer inBuffers[2]; /* Security package buffer */
+ SecBufferDesc outBuffer; /* Array of SecBuffer structs */
+ SecBuffer outBuffers[1]; /* Security package buffer */
+ int ret = 0; /* Return value */
+ char username[1024], /* Current username */
+ common_name[1024]; /* CN=username */
+
+
+ DEBUG_printf(("4http_sspi_client(http=%p, hostname=\"%s\")", http, hostname));
+
+ dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ /*
+ * Lookup the client certificate...
+ */
+
+ dwSize = sizeof(username);
+ GetUserNameA(username, &dwSize);
+ snprintf(common_name, sizeof(common_name), "CN=%s", username);
+
+ if (!http_sspi_find_credentials(http, L"ClientContainer", common_name))
+ if (!http_sspi_make_credentials(http->tls, L"ClientContainer", common_name, _HTTP_MODE_CLIENT, 10))
+ {
+ DEBUG_puts("5http_sspi_client: Unable to get client credentials.");
+ return (-1);
+ }
+
+ /*
+ * Initiate a ClientHello message and generate a token.
+ */
+
+ outBuffers[0].pvBuffer = NULL;
+ outBuffers[0].BufferType = SECBUFFER_TOKEN;
+ outBuffers[0].cbBuffer = 0;
+
+ outBuffer.cBuffers = 1;
+ outBuffer.pBuffers = outBuffers;
+ outBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = InitializeSecurityContext(&sspi->creds, NULL, TEXT(""), dwSSPIFlags, 0, SECURITY_NATIVE_DREP, NULL, 0, &sspi->context, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
+
+ if (scRet != SEC_I_CONTINUE_NEEDED)
+ {
+ DEBUG_printf(("5http_sspi_client: InitializeSecurityContext(1) failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
+ return (-1);
+ }
+
+ /*
+ * Send response to server if there is one.
+ */
+
+ if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
+ {
+ if ((cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0)) <= 0)
+ {
+ DEBUG_printf(("5http_sspi_client: send failed: %d", WSAGetLastError()));
+ FreeContextBuffer(outBuffers[0].pvBuffer);
+ DeleteSecurityContext(&sspi->context);
+ return (-1);
+ }
+
+ DEBUG_printf(("5http_sspi_client: %d bytes of handshake data sent.", cbData));
+
+ FreeContextBuffer(outBuffers[0].pvBuffer);
+ outBuffers[0].pvBuffer = NULL;
+ }
+
+ dwSSPIFlags = ISC_REQ_MANUAL_CRED_VALIDATION |
+ ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ sspi->decryptBufferUsed = 0;
+
+ /*
+ * Loop until the handshake is finished or an error occurs.
+ */
+
+ scRet = SEC_I_CONTINUE_NEEDED;
+
+ while(scRet == SEC_I_CONTINUE_NEEDED ||
+ scRet == SEC_E_INCOMPLETE_MESSAGE ||
+ scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ if (sspi->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
+ {
+ BYTE *temp; /* New buffer */
+
+ if (sspi->decryptBufferLength >= 262144)
+ {
+ WSASetLastError(E_OUTOFMEMORY);
+ DEBUG_puts("5http_sspi_client: Decryption buffer too large (>256k)");
+ return (-1);
+ }
+
+ if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
+ {
+ DEBUG_printf(("5http_sspi_client: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
+ WSASetLastError(E_OUTOFMEMORY);
+ return (-1);
+ }
+
+ sspi->decryptBufferLength += 4096;
+ sspi->decryptBuffer = temp;
+ }
+
+ cbData = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
+
+ if (cbData < 0)
+ {
+ DEBUG_printf(("5http_sspi_client: recv failed: %d", WSAGetLastError()));
+ return (-1);
+ }
+ else if (cbData == 0)
+ {
+ DEBUG_printf(("5http_sspi_client: Server unexpectedly disconnected."));
+ return (-1);
+ }
+
+ DEBUG_printf(("5http_sspi_client: %d bytes of handshake data received", cbData));
+
+ sspi->decryptBufferUsed += cbData;
+ }
+
+ /*
+ * Set up the input buffers. Buffer 0 is used to pass in data received from
+ * the server. Schannel will consume some or all of this. Leftover data
+ * (if any) will be placed in buffer 1 and given a buffer type of
+ * SECBUFFER_EXTRA.
+ */
+
+ inBuffers[0].pvBuffer = sspi->decryptBuffer;
+ inBuffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed;
+ inBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+ inBuffers[1].pvBuffer = NULL;
+ inBuffers[1].cbBuffer = 0;
+ inBuffers[1].BufferType = SECBUFFER_EMPTY;
+
+ inBuffer.cBuffers = 2;
+ inBuffer.pBuffers = inBuffers;
+ inBuffer.ulVersion = SECBUFFER_VERSION;
+
+ /*
+ * Set up the output buffers. These are initialized to NULL so as to make it
+ * less likely we'll attempt to free random garbage later.
+ */
+
+ outBuffers[0].pvBuffer = NULL;
+ outBuffers[0].BufferType = SECBUFFER_TOKEN;
+ outBuffers[0].cbBuffer = 0;
+
+ outBuffer.cBuffers = 1;
+ outBuffer.pBuffers = outBuffers;
+ outBuffer.ulVersion = SECBUFFER_VERSION;
+
+ /*
+ * Call InitializeSecurityContext.
+ */
+
+ scRet = InitializeSecurityContext(&sspi->creds, &sspi->context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
+
+ /*
+ * If InitializeSecurityContext was successful (or if the error was one of
+ * the special extended ones), send the contents of the output buffer to the
+ * server.
+ */
+
+ if (scRet == SEC_E_OK ||
+ scRet == SEC_I_CONTINUE_NEEDED ||
+ FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
+ {
+ if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
+ {
+ cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
+
+ if (cbData <= 0)
+ {
+ DEBUG_printf(("5http_sspi_client: send failed: %d", WSAGetLastError()));
+ FreeContextBuffer(outBuffers[0].pvBuffer);
+ DeleteSecurityContext(&sspi->context);
+ return (-1);
+ }
+
+ DEBUG_printf(("5http_sspi_client: %d bytes of handshake data sent.", cbData));
+
+ /*
+ * Free output buffer.
+ */
+
+ FreeContextBuffer(outBuffers[0].pvBuffer);
+ outBuffers[0].pvBuffer = NULL;
+ }
+ }
+
+ /*
+ * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we
+ * need to read more data from the server and try again.
+ */
+
+ if (scRet == SEC_E_INCOMPLETE_MESSAGE)
+ continue;
+
+ /*
+ * If InitializeSecurityContext returned SEC_E_OK, then the handshake
+ * completed successfully.
+ */
+
+ if (scRet == SEC_E_OK)
{
/*
* If the "extra" buffer contains data, this is encrypted application
- * protocol layer stuff. It needs to be saved. The application layer
- * will later decrypt it with DecryptMessage.
+ * protocol layer stuff. It needs to be saved. The application layer will
+ * later decrypt it with DecryptMessage.
*/
- DEBUG_printf(("_sspiConnect: Handshake was successful"));
+
+ DEBUG_puts("5http_sspi_client: Handshake was successful.");
if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
{
- if (conn->decryptBufferLength < inBuffers[1].cbBuffer)
- {
- conn->decryptBuffer = realloc(conn->decryptBuffer, inBuffers[1].cbBuffer);
-
- if (!conn->decryptBuffer)
- {
- DEBUG_printf(("_sspiConnect: unable to allocate %d bytes for decrypt buffer",
- inBuffers[1].cbBuffer));
- SetLastError(E_OUTOFMEMORY);
- ok = FALSE;
- goto cleanup;
- }
- }
+ memmove(sspi->decryptBuffer, sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
- memmove(conn->decryptBuffer,
- conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer),
- inBuffers[1].cbBuffer);
+ sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
- conn->decryptBufferUsed = inBuffers[1].cbBuffer;
-
- DEBUG_printf(("_sspiConnect: %d bytes of app data was bundled with handshake data",
- conn->decryptBufferUsed));
+ DEBUG_printf(("5http_sspi_client: %d bytes of app data was bundled with handshake data", sspi->decryptBufferUsed));
}
else
- conn->decryptBufferUsed = 0;
+ sspi->decryptBufferUsed = 0;
/*
* Bail out to quit
*/
+
break;
}
/*
* Check for fatal error.
*/
+
if (FAILED(scRet))
{
- DEBUG_printf(("_sspiConnect: InitializeSecurityContext(2) failed: %x", scRet));
- ok = FALSE;
+ DEBUG_printf(("5http_sspi_client: InitializeSecurityContext(2) failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
+ ret = -1;
break;
}
* If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
* then the server just requested client authentication.
*/
+
if (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
{
/*
* Unimplemented
*/
- DEBUG_printf(("_sspiConnect: server requested client credentials"));
- ok = FALSE;
+
+ DEBUG_printf(("5http_sspi_client: server requested client credentials."));
+ ret = -1;
break;
}
/*
- * Copy any leftover data from the "extra" buffer, and go around
- * again.
+ * Copy any leftover data from the "extra" buffer, and go around again.
*/
+
if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
{
- memmove(conn->decryptBuffer,
- conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer),
- inBuffers[1].cbBuffer);
+ memmove(sspi->decryptBuffer, sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
- conn->decryptBufferUsed = inBuffers[1].cbBuffer;
+ sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
}
else
{
- conn->decryptBufferUsed = 0;
+ sspi->decryptBufferUsed = 0;
}
}
- if (ok)
+ if (!ret)
{
- conn->contextInitialized = TRUE;
-
/*
- * Get the server cert
+ * Success! Get the server cert
*/
- scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID*) &serverCert );
- if (scRet != SEC_E_OK)
- {
- DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet));
- ok = FALSE;
- goto cleanup;
- }
+ sspi->contextInitialized = TRUE;
- scRet = sspi_verify_certificate(serverCert, hostname, conn->certFlags);
+ scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID *)&(sspi->remoteCert));
if (scRet != SEC_E_OK)
{
- DEBUG_printf(("_sspiConnect: sspi_verify_certificate failed: %x", scRet));
- ok = FALSE;
- goto cleanup;
+ DEBUG_printf(("5http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
+ return (-1);
}
/*
* Find out how big the header/trailer will be:
*/
- scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes);
+
+ scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_STREAM_SIZES, &sspi->streamSizes);
if (scRet != SEC_E_OK)
{
- DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet));
- ok = FALSE;
+ DEBUG_printf(("5http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
+ ret = -1;
}
}
-cleanup:
+ return (ret);
+}
- if (serverCert)
- CertFreeCertificateContext(serverCert);
- return (ok);
+/*
+ * 'http_sspi_create_credential()' - Create an SSPI certificate context.
+ */
+
+static PCCERT_CONTEXT /* O - Certificate context */
+http_sspi_create_credential(
+ http_credential_t *cred) /* I - Credential */
+{
+ if (cred)
+ return (CertCreateCertificateContext(X509_ASN_ENCODING, cred->data, cred->datalen));
+ else
+ return (NULL);
}
/*
- * '_sspiAccept()' - Accept an SSL/TLS connection
+ * 'http_sspi_find_credentials()' - Retrieve a TLS certificate from the system store.
*/
-BOOL /* O - 1 on success, 0 on failure */
-_sspiAccept(_sspi_struct_t *conn) /* I - Client connection */
+
+static BOOL /* O - 1 on success, 0 on failure */
+http_sspi_find_credentials(
+ http_t *http, /* I - HTTP connection */
+ const LPWSTR container, /* I - Cert container name */
+ const char *common_name) /* I - Common name of certificate */
{
- DWORD dwSSPIFlags; /* SSL connection attributes we want */
- DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
- TimeStamp tsExpiry; /* Time stamp */
- SECURITY_STATUS scRet; /* SSPI Status */
- SecBufferDesc inBuffer; /* Array of SecBuffer structs */
- SecBuffer inBuffers[2]; /* Security package buffer */
- SecBufferDesc outBuffer; /* Array of SecBuffer structs */
- SecBuffer outBuffers[1]; /* Security package buffer */
- DWORD num = 0; /* 32 bit status value */
- BOOL fInitContext = TRUE;
- /* Has the context been init'd? */
- BOOL ok = TRUE; /* Return value */
-
- if (!conn)
- return (FALSE);
+ _http_sspi_t *sspi = http->tls; /* SSPI data */
+ HCERTSTORE store = NULL; /* Certificate store */
+ PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
+ DWORD dwSize = 0; /* 32 bit size */
+ PBYTE p = NULL; /* Temporary storage */
+ HCRYPTPROV hProv = (HCRYPTPROV)NULL;
+ /* Handle to a CSP */
+ CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
+ SCHANNEL_CRED SchannelCred; /* Schannel credential data */
+ TimeStamp tsExpiry; /* Time stamp */
+ SECURITY_STATUS Status; /* Status */
+ BOOL ok = TRUE; /* Return value */
+
+
+ if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
+ {
+ if (GetLastError() == NTE_EXISTS)
+ {
+ if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
+ {
+ DEBUG_printf(("5http_sspi_find_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+ ok = FALSE;
+ goto cleanup;
+ }
+ }
+ }
+
+ store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
+
+ if (!store)
+ {
+ DEBUG_printf(("5http_sspi_find_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ dwSize = 0;
+
+ if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
+ {
+ DEBUG_printf(("5http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ p = (PBYTE)malloc(dwSize);
+
+ if (!p)
+ {
+ DEBUG_printf(("5http_sspi_find_credentials: malloc failed for %d bytes.", dwSize));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
+ {
+ DEBUG_printf(("5http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ sib.cbData = dwSize;
+ sib.pbData = p;
+
+ storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &sib, NULL);
+
+ if (!storedContext)
+ {
+ DEBUG_printf(("5http_sspi_find_credentials: Unable to find credentials for \"%s\".", common_name));
+ ok = FALSE;
+ goto cleanup;
+ }
+
+ ZeroMemory(&SchannelCred, sizeof(SchannelCred));
+
+ SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
+ SchannelCred.cCreds = 1;
+ SchannelCred.paCred = &storedContext;
+
+ /*
+ * Set supported protocols (can also be overriden in the registry...)
+ */
+
+#ifdef SP_PROT_TLS1_2_SERVER
+ if (http->mode == _HTTP_MODE_SERVER)
+ {
+ if (tls_min_version > _HTTP_TLS_1_1)
+ SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER;
+ else if (tls_min_version > _HTTP_TLS_1_0)
+ SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER;
+ else if (tls_min_version == _HTTP_TLS_SSL3)
+ SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_0_SERVER | SP_PROT_SSL3_SERVER;
+ else
+ SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_0_SERVER;
+ }
+ else
+ {
+ if (tls_min_version > _HTTP_TLS_1_1)
+ SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
+ else if (tls_min_version > _HTTP_TLS_1_0)
+ SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT;
+ else if (tls_min_version == _HTTP_TLS_SSL3)
+ SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_0_CLIENT | SP_PROT_SSL3_CLIENT;
+ else
+ SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_0_CLIENT;
+ }
+
+#else
+ if (http->mode == _HTTP_MODE_SERVER)
+ {
+ if (tls_min_version == _HTTP_TLS_SSL3)
+ SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER | SP_PROT_SSL3_SERVER;
+ else
+ SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER;
+ }
+ else
+ {
+ if (tls_min_version == _HTTP_TLS_SSL3)
+ SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT | SP_PROT_SSL3_CLIENT;
+ else
+ SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;
+ }
+#endif /* SP_PROT_TLS1_2_SERVER */
+
+ /* TODO: Support _HTTP_TLS_ALLOW_RC4, _HTTP_TLS_ALLOW_DH, and _HTTP_TLS_DENY_CBC options; right now we'll rely on Windows registry to enable/disable RC4/DH/CBC... */
- dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
- ASC_REQ_REPLAY_DETECT |
- ASC_REQ_CONFIDENTIALITY |
- ASC_REQ_EXTENDED_ERROR |
- ASC_REQ_ALLOCATE_MEMORY |
- ASC_REQ_STREAM;
+ /*
+ * Create an SSPI credential.
+ */
+
+ Status = AcquireCredentialsHandle(NULL, UNISP_NAME, http->mode == _HTTP_MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &sspi->creds, &tsExpiry);
+ if (Status != SEC_E_OK)
+ {
+ DEBUG_printf(("5http_sspi_find_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), Status)));
+ ok = FALSE;
+ goto cleanup;
+ }
- conn->decryptBufferUsed = 0;
+cleanup:
/*
- * Set OutBuffer for AcceptSecurityContext call
+ * Cleanup
*/
- outBuffer.cBuffers = 1;
- outBuffer.pBuffers = outBuffers;
- outBuffer.ulVersion = SECBUFFER_VERSION;
- scRet = SEC_I_CONTINUE_NEEDED;
+ if (storedContext)
+ CertFreeCertificateContext(storedContext);
- while (scRet == SEC_I_CONTINUE_NEEDED ||
- scRet == SEC_E_INCOMPLETE_MESSAGE ||
- scRet == SEC_I_INCOMPLETE_CREDENTIALS)
- {
- if ((conn->decryptBufferUsed == 0) || (scRet == SEC_E_INCOMPLETE_MESSAGE))
- {
- if (conn->decryptBufferLength <= conn->decryptBufferUsed)
- {
- conn->decryptBufferLength += 4096;
- conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer,
- conn->decryptBufferLength);
+ if (p)
+ free(p);
- if (!conn->decryptBuffer)
- {
- DEBUG_printf(("_sspiAccept: unable to allocate %d byte decrypt buffer",
- conn->decryptBufferLength));
- ok = FALSE;
- goto cleanup;
- }
- }
+ if (store)
+ CertCloseStore(store, 0);
- for (;;)
- {
- num = recv(conn->sock,
- conn->decryptBuffer + conn->decryptBufferUsed,
- (int)(conn->decryptBufferLength - conn->decryptBufferUsed),
- 0);
+ if (hProv)
+ CryptReleaseContext(hProv, 0);
- if ((num == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK))
- Sleep(1);
- else
- break;
- }
+ return (ok);
+}
- if (num == SOCKET_ERROR)
- {
- DEBUG_printf(("_sspiAccept: recv failed: %d", WSAGetLastError()));
- ok = FALSE;
- goto cleanup;
- }
- else if (num == 0)
- {
- DEBUG_printf(("_sspiAccept: client disconnected"));
- ok = FALSE;
- goto cleanup;
- }
- DEBUG_printf(("_sspiAccept: received %d (handshake) bytes from client",
- num));
- conn->decryptBufferUsed += num;
- }
+/*
+ * 'http_sspi_free()' - Close a connection and free resources.
+ */
- /*
- * InBuffers[1] is for getting extra data that
- * SSPI/SCHANNEL doesn't proccess on this
- * run around the loop.
- */
- inBuffers[0].pvBuffer = conn->decryptBuffer;
- inBuffers[0].cbBuffer = (unsigned long) conn->decryptBufferUsed;
- inBuffers[0].BufferType = SECBUFFER_TOKEN;
+static void
+http_sspi_free(_http_sspi_t *sspi) /* I - SSPI data */
+{
+ if (!sspi)
+ return;
- inBuffers[1].pvBuffer = NULL;
- inBuffers[1].cbBuffer = 0;
- inBuffers[1].BufferType = SECBUFFER_EMPTY;
+ if (sspi->contextInitialized)
+ DeleteSecurityContext(&sspi->context);
- inBuffer.cBuffers = 2;
- inBuffer.pBuffers = inBuffers;
- inBuffer.ulVersion = SECBUFFER_VERSION;
+ if (sspi->decryptBuffer)
+ free(sspi->decryptBuffer);
- /*
- * Initialize these so if we fail, pvBuffer contains NULL,
- * so we don't try to free random garbage at the quit
- */
- outBuffers[0].pvBuffer = NULL;
- outBuffers[0].BufferType = SECBUFFER_TOKEN;
- outBuffers[0].cbBuffer = 0;
+ if (sspi->readBuffer)
+ free(sspi->readBuffer);
- scRet = AcceptSecurityContext(&conn->creds, (fInitContext?NULL:&conn->context),
- &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP,
- (fInitContext?&conn->context:NULL), &outBuffer,
- &dwSSPIOutFlags, &tsExpiry);
+ if (sspi->writeBuffer)
+ free(sspi->writeBuffer);
- fInitContext = FALSE;
+ if (sspi->localCert)
+ CertFreeCertificateContext(sspi->localCert);
- if (scRet == SEC_E_OK ||
- scRet == SEC_I_CONTINUE_NEEDED ||
- (FAILED(scRet) && ((dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) != 0)))
- {
- if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
- {
- /*
- * Send response to server if there is one
- */
- num = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
+ if (sspi->remoteCert)
+ CertFreeCertificateContext(sspi->remoteCert);
- if ((num == SOCKET_ERROR) || (num == 0))
- {
- DEBUG_printf(("_sspiAccept: handshake send failed: %d", WSAGetLastError()));
- ok = FALSE;
- goto cleanup;
- }
+ free(sspi);
+}
- DEBUG_printf(("_sspiAccept: send %d handshake bytes to client",
- outBuffers[0].cbBuffer));
- FreeContextBuffer(outBuffers[0].pvBuffer);
- outBuffers[0].pvBuffer = NULL;
- }
- }
+/*
+ * 'http_sspi_make_credentials()' - Create a TLS certificate in the system store.
+ */
- if (scRet == SEC_E_OK)
- {
- /*
- * If there's extra data then save it for
- * next time we go to decrypt
- */
- if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
- {
- memcpy(conn->decryptBuffer,
- (LPBYTE) (conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer)),
- inBuffers[1].cbBuffer);
- conn->decryptBufferUsed = inBuffers[1].cbBuffer;
- }
- else
- {
- conn->decryptBufferUsed = 0;
- }
+static BOOL /* O - 1 on success, 0 on failure */
+http_sspi_make_credentials(
+ _http_sspi_t *sspi, /* I - SSPI data */
+ const LPWSTR container, /* I - Cert container name */
+ const char *common_name, /* I - Common name of certificate */
+ _http_mode_t mode, /* I - Client or server? */
+ int years) /* I - Years until expiration */
+{
+ HCERTSTORE store = NULL; /* Certificate store */
+ PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
+ PCCERT_CONTEXT createdContext = NULL; /* Context created by us */
+ DWORD dwSize = 0; /* 32 bit size */
+ PBYTE p = NULL; /* Temporary storage */
+ HCRYPTPROV hProv = (HCRYPTPROV)NULL;
+ /* Handle to a CSP */
+ CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
+ SCHANNEL_CRED SchannelCred; /* Schannel credential data */
+ TimeStamp tsExpiry; /* Time stamp */
+ SECURITY_STATUS Status; /* Status */
+ HCRYPTKEY hKey = (HCRYPTKEY)NULL; /* Handle to crypto key */
+ CRYPT_KEY_PROV_INFO kpi; /* Key container info */
+ SYSTEMTIME et; /* System time */
+ CERT_EXTENSIONS exts; /* Array of cert extensions */
+ CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */
+ BOOL ok = TRUE; /* Return value */
- ok = TRUE;
- break;
- }
- else if (FAILED(scRet) && (scRet != SEC_E_INCOMPLETE_MESSAGE))
- {
- DEBUG_printf(("_sspiAccept: AcceptSecurityContext failed: %x", scRet));
- ok = FALSE;
- break;
- }
- if (scRet != SEC_E_INCOMPLETE_MESSAGE &&
- scRet != SEC_I_INCOMPLETE_CREDENTIALS)
+ DEBUG_printf(("4http_sspi_make_credentials(sspi=%p, container=%p, common_name=\"%s\", mode=%d, years=%d)", sspi, container, common_name, mode, years));
+
+ if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
+ {
+ if (GetLastError() == NTE_EXISTS)
{
- if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
- {
- memcpy(conn->decryptBuffer,
- (LPBYTE) (conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer)),
- inBuffers[1].cbBuffer);
- conn->decryptBufferUsed = inBuffers[1].cbBuffer;
- }
- else
+ if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
{
- conn->decryptBufferUsed = 0;
+ DEBUG_printf(("5http_sspi_make_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+ ok = FALSE;
+ goto cleanup;
}
}
}
- if (ok)
+ store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
+
+ if (!store)
{
- conn->contextInitialized = TRUE;
+ DEBUG_printf(("5http_sspi_make_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+ ok = FALSE;
+ goto cleanup;
+ }
- /*
- * Find out how big the header will be:
- */
- scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes);
+ dwSize = 0;
- if (scRet != SEC_E_OK)
- {
- DEBUG_printf(("_sspiAccept: QueryContextAttributes failed: %x", scRet));
- ok = FALSE;
- }
+ if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
+ {
+ DEBUG_printf(("5http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+ ok = FALSE;
+ goto cleanup;
}
-cleanup:
+ p = (PBYTE)malloc(dwSize);
- return (ok);
-}
+ if (!p)
+ {
+ DEBUG_printf(("5http_sspi_make_credentials: malloc failed for %d bytes", dwSize));
+ ok = FALSE;
+ goto cleanup;
+ }
+ if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
+ {
+ DEBUG_printf(("5http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+ ok = FALSE;
+ goto cleanup;
+ }
-/*
- * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
- */
-void
-_sspiSetAllowsAnyRoot(_sspi_struct_t *conn,
- /* I - Client connection */
- BOOL allow)
- /* I - Allow any root */
-{
- conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_UNKNOWN_CA :
- conn->certFlags & ~SECURITY_FLAG_IGNORE_UNKNOWN_CA;
-}
+ /*
+ * Create a private key and self-signed certificate...
+ */
+ if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey))
+ {
+ DEBUG_printf(("5http_sspi_make_credentials: CryptGenKey failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+ ok = FALSE;
+ goto cleanup;
+ }
-/*
- * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
- */
-void
-_sspiSetAllowsExpiredCerts(_sspi_struct_t *conn,
- /* I - Client connection */
- BOOL allow)
- /* I - Allow expired certs */
-{
- conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID :
- conn->certFlags & ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
-}
+ ZeroMemory(&kpi, sizeof(kpi));
+ kpi.pwszContainerName = (LPWSTR)container;
+ kpi.pwszProvName = MS_DEF_PROV_W;
+ kpi.dwProvType = PROV_RSA_FULL;
+ kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
+ kpi.dwKeySpec = AT_KEYEXCHANGE;
+ GetSystemTime(&et);
+ et.wYear += years;
+ if (et.wMonth == 2 && et.wDay == 29)
+ et.wDay = 28; /* Avoid Feb 29th due to leap years */
-/*
- * '_sspiWrite()' - Write a buffer to an ssl socket
- */
-int /* O - Bytes written or SOCKET_ERROR */
-_sspiWrite(_sspi_struct_t *conn, /* I - Client connection */
- void *buf, /* I - Buffer */
- size_t len) /* I - Buffer length */
-{
- SecBufferDesc message; /* Array of SecBuffer struct */
- SecBuffer buffers[4] = { 0 }; /* Security package buffer */
- BYTE *buffer = NULL; /* Scratch buffer */
- int bufferLen; /* Buffer length */
- size_t bytesLeft; /* Bytes left to write */
- int index = 0; /* Index into buffer */
- int num = 0; /* Return value */
+ ZeroMemory(&exts, sizeof(exts));
- if (!conn || !buf || !len)
+ createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL, &et, &exts);
+
+ if (!createdContext)
{
- WSASetLastError(WSAEINVAL);
- num = SOCKET_ERROR;
+ DEBUG_printf(("5http_sspi_make_credentials: CertCreateSelfSignCertificate failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+ ok = FALSE;
goto cleanup;
}
- bufferLen = conn->streamSizes.cbMaximumMessage +
- conn->streamSizes.cbHeader +
- conn->streamSizes.cbTrailer;
-
- buffer = (BYTE*) malloc(bufferLen);
+ /*
+ * Add the created context to the named store, and associate it with the named
+ * container...
+ */
- if (!buffer)
+ if (!CertAddCertificateContextToStore(store, createdContext, CERT_STORE_ADD_REPLACE_EXISTING, &storedContext))
{
- DEBUG_printf(("_sspiWrite: buffer alloc of %d bytes failed", bufferLen));
- WSASetLastError(E_OUTOFMEMORY);
- num = SOCKET_ERROR;
+ DEBUG_printf(("5http_sspi_make_credentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+ ok = FALSE;
goto cleanup;
}
- bytesLeft = len;
+ ZeroMemory(&ckp, sizeof(ckp));
+ ckp.pwszContainerName = (LPWSTR) container;
+ ckp.pwszProvName = MS_DEF_PROV_W;
+ ckp.dwProvType = PROV_RSA_FULL;
+ ckp.dwFlags = CRYPT_MACHINE_KEYSET;
+ ckp.dwKeySpec = AT_KEYEXCHANGE;
- while (bytesLeft)
+ if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp))
{
- size_t chunk = min(conn->streamSizes.cbMaximumMessage, /* Size of data to write */
- bytesLeft);
- SECURITY_STATUS scRet; /* SSPI status */
+ DEBUG_printf(("5http_sspi_make_credentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
+ ok = FALSE;
+ goto cleanup;
+ }
- /*
- * Copy user data into the buffer, starting
- * just past the header
- */
- memcpy(buffer + conn->streamSizes.cbHeader,
- ((BYTE*) buf) + index,
- chunk);
+ /*
+ * Get a handle to use the certificate...
+ */
- /*
- * Setup the SSPI buffers
- */
- message.ulVersion = SECBUFFER_VERSION;
- message.cBuffers = 4;
- message.pBuffers = buffers;
- buffers[0].pvBuffer = buffer;
- buffers[0].cbBuffer = conn->streamSizes.cbHeader;
- buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
- buffers[1].pvBuffer = buffer + conn->streamSizes.cbHeader;
- buffers[1].cbBuffer = (unsigned long) chunk;
- buffers[1].BufferType = SECBUFFER_DATA;
- buffers[2].pvBuffer = buffer + conn->streamSizes.cbHeader + chunk;
- buffers[2].cbBuffer = conn->streamSizes.cbTrailer;
- buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
- buffers[3].BufferType = SECBUFFER_EMPTY;
+ ZeroMemory(&SchannelCred, sizeof(SchannelCred));
- /*
- * Encrypt the data
- */
- scRet = EncryptMessage(&conn->context, 0, &message, 0);
+ SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
+ SchannelCred.cCreds = 1;
+ SchannelCred.paCred = &storedContext;
- if (FAILED(scRet))
- {
- DEBUG_printf(("_sspiWrite: EncryptMessage failed: %x", scRet));
- WSASetLastError(WSASYSCALLFAILURE);
- num = SOCKET_ERROR;
- goto cleanup;
- }
+ /*
+ * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
+ */
- /*
- * Send the data. Remember the size of
- * the total data to send is the size
- * of the header, the size of the data
- * the caller passed in and the size
- * of the trailer
- */
- num = send(conn->sock,
- buffer,
- buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer,
- 0);
+ if (mode == _HTTP_MODE_SERVER)
+ SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
- if ((num == SOCKET_ERROR) || (num == 0))
- {
- DEBUG_printf(("_sspiWrite: send failed: %ld", WSAGetLastError()));
- goto cleanup;
- }
+ /*
+ * Create an SSPI credential.
+ */
- bytesLeft -= (int) chunk;
- index += (int) chunk;
+ Status = AcquireCredentialsHandle(NULL, UNISP_NAME, mode == _HTTP_MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &sspi->creds, &tsExpiry);
+ if (Status != SEC_E_OK)
+ {
+ DEBUG_printf(("5http_sspi_make_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), Status)));
+ ok = FALSE;
+ goto cleanup;
}
- num = (int) len;
-
cleanup:
- if (buffer)
- free(buffer);
+ /*
+ * Cleanup
+ */
- return (num);
+ if (hKey)
+ CryptDestroyKey(hKey);
+
+ if (createdContext)
+ CertFreeCertificateContext(createdContext);
+
+ if (storedContext)
+ CertFreeCertificateContext(storedContext);
+
+ if (p)
+ free(p);
+
+ if (store)
+ CertCloseStore(store, 0);
+
+ if (hProv)
+ CryptReleaseContext(hProv, 0);
+
+ return (ok);
}
/*
- * '_sspiRead()' - Read a buffer from an ssl socket
+ * 'http_sspi_server()' - Negotiate a TLS connection as a server.
*/
-int /* O - Bytes read or SOCKET_ERROR */
-_sspiRead(_sspi_struct_t *conn, /* I - Client connection */
- void *buf, /* I - Buffer */
- size_t len) /* I - Buffer length */
+
+static int /* O - 0 on success, -1 on failure */
+http_sspi_server(http_t *http, /* I - HTTP connection */
+ const char *hostname) /* I - Hostname of server */
{
- SecBufferDesc message; /* Array of SecBuffer struct */
- SecBuffer buffers[4] = { 0 }; /* Security package buffer */
- int num = 0; /* Return value */
+ _http_sspi_t *sspi = http->tls; /* I - SSPI data */
+ char common_name[512]; /* Common name for cert */
+ DWORD dwSSPIFlags; /* SSL connection attributes we want */
+ DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
+ TimeStamp tsExpiry; /* Time stamp */
+ SECURITY_STATUS scRet; /* SSPI Status */
+ SecBufferDesc inBuffer; /* Array of SecBuffer structs */
+ SecBuffer inBuffers[2]; /* Security package buffer */
+ SecBufferDesc outBuffer; /* Array of SecBuffer structs */
+ SecBuffer outBuffers[1]; /* Security package buffer */
+ int num = 0; /* 32 bit status value */
+ BOOL fInitContext = TRUE; /* Has the context been init'd? */
+ int ret = 0; /* Return value */
+
+
+ DEBUG_printf(("4http_sspi_server(http=%p, hostname=\"%s\")", http, hostname));
- if (!conn)
- {
- WSASetLastError(WSAEINVAL);
- num = SOCKET_ERROR;
- goto cleanup;
- }
+ dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
+ ASC_REQ_REPLAY_DETECT |
+ ASC_REQ_CONFIDENTIALITY |
+ ASC_REQ_EXTENDED_ERROR |
+ ASC_REQ_ALLOCATE_MEMORY |
+ ASC_REQ_STREAM;
+
+ sspi->decryptBufferUsed = 0;
/*
- * If there are bytes that have already been
- * decrypted and have not yet been read, return
- * those
+ * Lookup the server certificate...
*/
- if (buf && (conn->readBufferUsed > 0))
- {
- int bytesToCopy = (int) min(conn->readBufferUsed, len); /* Amount of bytes to copy */
- /* from read buffer */
- memcpy(buf, conn->readBuffer, bytesToCopy);
- conn->readBufferUsed -= bytesToCopy;
+ snprintf(common_name, sizeof(common_name), "CN=%s", hostname);
- if (conn->readBufferUsed > 0)
- /*
- * If the caller didn't request all the bytes
- * we have in the buffer, then move the unread
- * bytes down
- */
- memmove(conn->readBuffer,
- conn->readBuffer + bytesToCopy,
- conn->readBufferUsed);
+ if (!http_sspi_find_credentials(http, L"ServerContainer", common_name))
+ if (!http_sspi_make_credentials(http->tls, L"ServerContainer", common_name, _HTTP_MODE_SERVER, 10))
+ {
+ DEBUG_puts("5http_sspi_server: Unable to get server credentials.");
+ return (-1);
+ }
- num = bytesToCopy;
- }
- else
- {
- PSecBuffer pDataBuffer; /* Data buffer */
- PSecBuffer pExtraBuffer; /* Excess data buffer */
- SECURITY_STATUS scRet; /* SSPI status */
- int i; /* Loop control variable */
+ /*
+ * Set OutBuffer for AcceptSecurityContext call
+ */
- /*
- * Initialize security buffer structs
- */
- message.ulVersion = SECBUFFER_VERSION;
- message.cBuffers = 4;
- message.pBuffers = buffers;
+ outBuffer.cBuffers = 1;
+ outBuffer.pBuffers = outBuffers;
+ outBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = SEC_I_CONTINUE_NEEDED;
- do
+ while (scRet == SEC_I_CONTINUE_NEEDED ||
+ scRet == SEC_E_INCOMPLETE_MESSAGE ||
+ scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ if (sspi->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
{
- /*
- * If there is not enough space in the
- * buffer, then increase it's size
- */
- if (conn->decryptBufferLength <= conn->decryptBufferUsed)
+ if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
{
- conn->decryptBufferLength += 4096;
- conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer,
- conn->decryptBufferLength);
+ BYTE *temp; /* New buffer */
- if (!conn->decryptBuffer)
- {
- DEBUG_printf(("_sspiRead: unable to allocate %d byte buffer",
- conn->decryptBufferLength));
- WSASetLastError(E_OUTOFMEMORY);
- num = SOCKET_ERROR;
- goto cleanup;
- }
- }
+ if (sspi->decryptBufferLength >= 262144)
+ {
+ WSASetLastError(E_OUTOFMEMORY);
+ DEBUG_puts("5http_sspi_server: Decryption buffer too large (>256k)");
+ return (-1);
+ }
- buffers[0].pvBuffer = conn->decryptBuffer;
- buffers[0].cbBuffer = (unsigned long) conn->decryptBufferUsed;
- buffers[0].BufferType = SECBUFFER_DATA;
- buffers[1].BufferType = SECBUFFER_EMPTY;
- buffers[2].BufferType = SECBUFFER_EMPTY;
- buffers[3].BufferType = SECBUFFER_EMPTY;
+ if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
+ {
+ DEBUG_printf(("5http_sspi_server: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
+ WSASetLastError(E_OUTOFMEMORY);
+ return (-1);
+ }
- scRet = DecryptMessage(&conn->context, &message, 0, NULL);
+ sspi->decryptBufferLength += 4096;
+ sspi->decryptBuffer = temp;
+ }
- if (scRet == SEC_E_INCOMPLETE_MESSAGE)
+ for (;;)
{
- if (buf)
- {
- num = recv(conn->sock,
- conn->decryptBuffer + conn->decryptBufferUsed,
- (int)(conn->decryptBufferLength - conn->decryptBufferUsed),
- 0);
- if (num == SOCKET_ERROR)
- {
- DEBUG_printf(("_sspiRead: recv failed: %d", WSAGetLastError()));
- goto cleanup;
- }
- else if (num == 0)
- {
- DEBUG_printf(("_sspiRead: server disconnected"));
- goto cleanup;
- }
+ num = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
- conn->decryptBufferUsed += num;
- }
+ if (num == -1 && WSAGetLastError() == WSAEWOULDBLOCK)
+ Sleep(1);
else
- {
- num = (int) conn->readBufferUsed;
- goto cleanup;
- }
+ break;
}
- }
- while (scRet == SEC_E_INCOMPLETE_MESSAGE);
-
- if (scRet == SEC_I_CONTEXT_EXPIRED)
- {
- DEBUG_printf(("_sspiRead: context expired"));
- WSASetLastError(WSAECONNRESET);
- num = SOCKET_ERROR;
- goto cleanup;
- }
- else if (scRet != SEC_E_OK)
- {
- DEBUG_printf(("_sspiRead: DecryptMessage failed: %lx", scRet));
- WSASetLastError(WSASYSCALLFAILURE);
- num = SOCKET_ERROR;
- goto cleanup;
- }
-
- /*
- * The decryption worked. Now, locate data buffer.
- */
- pDataBuffer = NULL;
- pExtraBuffer = NULL;
- for (i = 1; i < 4; i++)
- {
- if (buffers[i].BufferType == SECBUFFER_DATA)
- pDataBuffer = &buffers[i];
- else if (!pExtraBuffer && (buffers[i].BufferType == SECBUFFER_EXTRA))
- pExtraBuffer = &buffers[i];
- }
- /*
- * If a data buffer is found, then copy
- * the decrypted bytes to the passed-in
- * buffer
- */
- if (pDataBuffer)
- {
- int bytesToCopy = min(pDataBuffer->cbBuffer, (int) len);
- /* Number of bytes to copy into buf */
- int bytesToSave = pDataBuffer->cbBuffer - bytesToCopy;
- /* Number of bytes to save in our read buffer */
-
- if (bytesToCopy)
- memcpy(buf, pDataBuffer->pvBuffer, bytesToCopy);
-
- /*
- * If there are more decrypted bytes than can be
- * copied to the passed in buffer, then save them
- */
- if (bytesToSave)
+ if (num < 0)
{
- if ((int)(conn->readBufferLength - conn->readBufferUsed) < bytesToSave)
- {
- conn->readBufferLength = conn->readBufferUsed + bytesToSave;
- conn->readBuffer = realloc(conn->readBuffer,
- conn->readBufferLength);
-
- if (!conn->readBuffer)
- {
- DEBUG_printf(("_sspiRead: unable to allocate %d bytes", conn->readBufferLength));
- WSASetLastError(E_OUTOFMEMORY);
- num = SOCKET_ERROR;
- goto cleanup;
- }
- }
-
- memcpy(((BYTE*) conn->readBuffer) + conn->readBufferUsed,
- ((BYTE*) pDataBuffer->pvBuffer) + bytesToCopy,
- bytesToSave);
-
- conn->readBufferUsed += bytesToSave;
+ DEBUG_printf(("5http_sspi_server: recv failed: %d", WSAGetLastError()));
+ return (-1);
+ }
+ else if (num == 0)
+ {
+ DEBUG_puts("5http_sspi_server: client disconnected");
+ return (-1);
}
- num = (buf) ? bytesToCopy : (int) conn->readBufferUsed;
- }
- else
- {
- DEBUG_printf(("_sspiRead: unable to find data buffer"));
- WSASetLastError(WSASYSCALLFAILURE);
- num = SOCKET_ERROR;
- goto cleanup;
+ DEBUG_printf(("5http_sspi_server: received %d (handshake) bytes from client.", num));
+ sspi->decryptBufferUsed += num;
}
/*
- * If the decryption process left extra bytes,
- * then save those back in decryptBuffer. They will
- * be processed the next time through the loop.
+ * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process
+ * on this run around the loop.
*/
- if (pExtraBuffer)
- {
- memmove(conn->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
- conn->decryptBufferUsed = pExtraBuffer->cbBuffer;
- }
- else
- {
- conn->decryptBufferUsed = 0;
- }
- }
-
-cleanup:
-
- return (num);
-}
+ inBuffers[0].pvBuffer = sspi->decryptBuffer;
+ inBuffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed;
+ inBuffers[0].BufferType = SECBUFFER_TOKEN;
-/*
- * '_sspiPending()' - Returns the number of available bytes
- */
-int /* O - Number of available bytes */
-_sspiPending(_sspi_struct_t *conn) /* I - Client connection */
-{
- return (_sspiRead(conn, NULL, 0));
-}
-
+ inBuffers[1].pvBuffer = NULL;
+ inBuffers[1].cbBuffer = 0;
+ inBuffers[1].BufferType = SECBUFFER_EMPTY;
-/*
- * '_sspiFree()' - Close a connection and free resources
- */
-void
-_sspiFree(_sspi_struct_t *conn) /* I - Client connection */
-{
- if (!conn)
- return;
+ inBuffer.cBuffers = 2;
+ inBuffer.pBuffers = inBuffers;
+ inBuffer.ulVersion = SECBUFFER_VERSION;
- if (conn->contextInitialized)
- {
- SecBufferDesc message; /* Array of SecBuffer struct */
- SecBuffer buffers[1] = { 0 };
- /* Security package buffer */
- DWORD dwType; /* Type */
- DWORD status; /* Status */
+ /*
+ * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to
+ * free random garbage at the quit.
+ */
- /*
- * Notify schannel that we are about to close the connection.
- */
- dwType = SCHANNEL_SHUTDOWN;
+ outBuffers[0].pvBuffer = NULL;
+ outBuffers[0].BufferType = SECBUFFER_TOKEN;
+ outBuffers[0].cbBuffer = 0;
- buffers[0].pvBuffer = &dwType;
- buffers[0].BufferType = SECBUFFER_TOKEN;
- buffers[0].cbBuffer = sizeof(dwType);
+ scRet = AcceptSecurityContext(&sspi->creds, (fInitContext?NULL:&sspi->context), &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP, (fInitContext?&sspi->context:NULL), &outBuffer, &dwSSPIOutFlags, &tsExpiry);
- message.cBuffers = 1;
- message.pBuffers = buffers;
- message.ulVersion = SECBUFFER_VERSION;
+ fInitContext = FALSE;
- status = ApplyControlToken(&conn->context, &message);
+ if (scRet == SEC_E_OK ||
+ scRet == SEC_I_CONTINUE_NEEDED ||
+ (FAILED(scRet) && ((dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) != 0)))
+ {
+ if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
+ {
+ /*
+ * Send response to server if there is one.
+ */
- if (SUCCEEDED(status))
- {
- PBYTE pbMessage; /* Message buffer */
- DWORD cbMessage; /* Message buffer count */
- DWORD cbData; /* Data count */
- DWORD dwSSPIFlags; /* SSL attributes we requested */
- DWORD dwSSPIOutFlags; /* SSL attributes we received */
- TimeStamp tsExpiry; /* Time stamp */
+ num = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
- dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
- ASC_REQ_REPLAY_DETECT |
- ASC_REQ_CONFIDENTIALITY |
- ASC_REQ_EXTENDED_ERROR |
- ASC_REQ_ALLOCATE_MEMORY |
- ASC_REQ_STREAM;
+ if (num <= 0)
+ {
+ DEBUG_printf(("5http_sspi_server: handshake send failed: %d", WSAGetLastError()));
+ return (-1);
+ }
- buffers[0].pvBuffer = NULL;
- buffers[0].BufferType = SECBUFFER_TOKEN;
- buffers[0].cbBuffer = 0;
+ DEBUG_printf(("5http_sspi_server: sent %d handshake bytes to client.", outBuffers[0].cbBuffer));
- message.cBuffers = 1;
- message.pBuffers = buffers;
- message.ulVersion = SECBUFFER_VERSION;
+ FreeContextBuffer(outBuffers[0].pvBuffer);
+ outBuffers[0].pvBuffer = NULL;
+ }
+ }
- status = AcceptSecurityContext(&conn->creds, &conn->context, NULL,
- dwSSPIFlags, SECURITY_NATIVE_DREP, NULL,
- &message, &dwSSPIOutFlags, &tsExpiry);
+ if (scRet == SEC_E_OK)
+ {
+ /*
+ * If there's extra data then save it for next time we go to decrypt.
+ */
- if (SUCCEEDED(status))
+ if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
{
- pbMessage = buffers[0].pvBuffer;
- cbMessage = buffers[0].cbBuffer;
-
- /*
- * Send the close notify message to the client.
- */
- if (pbMessage && cbMessage)
- {
- cbData = send(conn->sock, pbMessage, cbMessage, 0);
- if ((cbData == SOCKET_ERROR) || (cbData == 0))
- {
- status = WSAGetLastError();
- DEBUG_printf(("_sspiFree: sending close notify failed: %d", status));
- }
- else
- {
- FreeContextBuffer(pbMessage);
- }
- }
+ memcpy(sspi->decryptBuffer, (LPBYTE)(sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
+ sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
}
else
{
- DEBUG_printf(("_sspiFree: AcceptSecurityContext failed: %x", status));
+ sspi->decryptBufferUsed = 0;
}
+ break;
}
- else
+ else if (FAILED(scRet) && scRet != SEC_E_INCOMPLETE_MESSAGE)
{
- DEBUG_printf(("_sspiFree: ApplyControlToken failed: %x", status));
+ DEBUG_printf(("5http_sspi_server: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
+ ret = -1;
+ break;
}
- DeleteSecurityContext(&conn->context);
- conn->contextInitialized = FALSE;
+ if (scRet != SEC_E_INCOMPLETE_MESSAGE &&
+ scRet != SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
+ {
+ memcpy(sspi->decryptBuffer, (LPBYTE)(sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
+ sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
+ }
+ else
+ {
+ sspi->decryptBufferUsed = 0;
+ }
+ }
}
- if (conn->decryptBuffer)
+ if (!ret)
{
- free(conn->decryptBuffer);
- conn->decryptBuffer = NULL;
- }
+ sspi->contextInitialized = TRUE;
- if (conn->readBuffer)
- {
- free(conn->readBuffer);
- conn->readBuffer = NULL;
+ /*
+ * Find out how big the header will be:
+ */
+
+ scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_STREAM_SIZES, &sspi->streamSizes);
+
+ if (scRet != SEC_E_OK)
+ {
+ DEBUG_printf(("5http_sspi_server: QueryContextAttributes failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
+ ret = -1;
+ }
}
- if (conn->sock != INVALID_SOCKET)
+ return (ret);
+}
+
+
+/*
+ * 'http_sspi_strerror()' - Return a string for the specified error code.
+ */
+
+static const char * /* O - String for error */
+http_sspi_strerror(char *buffer, /* I - Error message buffer */
+ size_t bufsize, /* I - Size of buffer */
+ DWORD code) /* I - Error code */
+{
+ if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buffer, bufsize, NULL))
{
- closesocket(conn->sock);
- conn->sock = INVALID_SOCKET;
+ /*
+ * Strip trailing CR + LF...
+ */
+
+ char *ptr; /* Pointer into error message */
+
+ for (ptr = buffer + strlen(buffer) - 1; ptr >= buffer; ptr --)
+ if (*ptr == '\n' || *ptr == '\r')
+ *ptr = '\0';
+ else
+ break;
}
+ else
+ snprintf(buffer, bufsize, "Unknown error %x", code);
- free(conn);
+ return (buffer);
}
/*
- * 'sspi_verify_certificate()' - Verify a server certificate
+ * 'http_sspi_verify()' - Verify a certificate.
*/
-static DWORD /* 0 - Error code (0 == No error) */
-sspi_verify_certificate(PCCERT_CONTEXT serverCert,
- /* I - Server certificate */
- const CHAR *serverName,
- /* I - Server name */
- DWORD dwCertFlags)
- /* I - Verification flags */
+
+static DWORD /* O - Error code (0 == No error) */
+http_sspi_verify(
+ PCCERT_CONTEXT cert, /* I - Server certificate */
+ const char *common_name, /* I - Common name */
+ DWORD dwCertFlags) /* I - Verification flags */
{
- HTTPSPolicyCallbackData httpsPolicy;
- /* HTTPS Policy Struct */
- CERT_CHAIN_POLICY_PARA policyPara;
- /* Cert chain policy parameters */
- CERT_CHAIN_POLICY_STATUS policyStatus;
- /* Cert chain policy status */
- CERT_CHAIN_PARA chainPara;
- /* Used for searching and matching criteria */
- PCCERT_CHAIN_CONTEXT chainContext = NULL;
+ HTTPSPolicyCallbackData httpsPolicy; /* HTTPS Policy Struct */
+ CERT_CHAIN_POLICY_PARA policyPara; /* Cert chain policy parameters */
+ CERT_CHAIN_POLICY_STATUS policyStatus;/* Cert chain policy status */
+ CERT_CHAIN_PARA chainPara; /* Used for searching and matching criteria */
+ PCCERT_CHAIN_CONTEXT chainContext = NULL;
/* Certificate chain */
- PWSTR serverNameUnicode = NULL;
- /* Unicode server name */
- LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
- szOID_SERVER_GATED_CRYPTO,
- szOID_SGC_NETSCAPE };
+ PWSTR commonNameUnicode = NULL;
+ /* Unicode common name */
+ LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE };
/* How are we using this certificate? */
- DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
+ DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
/* Number of ites in rgszUsages */
- DWORD count; /* 32 bit count variable */
- DWORD status; /* Return value */
+ DWORD count; /* 32 bit count variable */
+ DWORD status; /* Return value */
+#ifdef DEBUG
+ char error[1024]; /* Error message string */
+#endif /* DEBUG */
- if (!serverCert)
- {
- status = SEC_E_WRONG_PRINCIPAL;
- goto cleanup;
- }
+
+ if (!cert)
+ return (SEC_E_WRONG_PRINCIPAL);
/*
- * Convert server name to unicode.
+ * Convert common name to Unicode.
*/
- if (!serverName || (strlen(serverName) == 0))
- {
- status = SEC_E_WRONG_PRINCIPAL;
- goto cleanup;
- }
- count = MultiByteToWideChar(CP_ACP, 0, serverName, -1, NULL, 0);
- serverNameUnicode = LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR));
- if (!serverNameUnicode)
- {
- status = SEC_E_INSUFFICIENT_MEMORY;
- goto cleanup;
- }
- count = MultiByteToWideChar(CP_ACP, 0, serverName, -1, serverNameUnicode, count);
- if (count == 0)
+ if (!common_name || !*common_name)
+ return (SEC_E_WRONG_PRINCIPAL);
+
+ count = MultiByteToWideChar(CP_ACP, 0, common_name, -1, NULL, 0);
+ commonNameUnicode = LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR));
+ if (!commonNameUnicode)
+ return (SEC_E_INSUFFICIENT_MEMORY);
+
+ if (!MultiByteToWideChar(CP_ACP, 0, common_name, -1, commonNameUnicode, count))
{
- status = SEC_E_WRONG_PRINCIPAL;
- goto cleanup;
+ LocalFree(commonNameUnicode);
+ return (SEC_E_WRONG_PRINCIPAL);
}
/*
* Build certificate chain.
*/
+
ZeroMemory(&chainPara, sizeof(chainPara));
+
chainPara.cbSize = sizeof(chainPara);
chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
chainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;
chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
- if (!CertGetCertificateChain(NULL, serverCert, NULL, serverCert->hCertStore,
- &chainPara, 0, NULL, &chainContext))
+ if (!CertGetCertificateChain(NULL, cert, NULL, cert->hCertStore, &chainPara, 0, NULL, &chainContext))
{
status = GetLastError();
- DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status));
- goto cleanup;
+
+ DEBUG_printf(("CertGetCertificateChain returned: %s", http_sspi_strerror(error, sizeof(error), status)));
+
+ LocalFree(commonNameUnicode);
+ return (status);
}
/*
* Validate certificate chain.
*/
+
ZeroMemory(&httpsPolicy, sizeof(HTTPSPolicyCallbackData));
httpsPolicy.cbStruct = sizeof(HTTPSPolicyCallbackData);
httpsPolicy.dwAuthType = AUTHTYPE_SERVER;
httpsPolicy.fdwChecks = dwCertFlags;
- httpsPolicy.pwszServerName = serverNameUnicode;
+ httpsPolicy.pwszServerName = commonNameUnicode;
memset(&policyPara, 0, sizeof(policyPara));
policyPara.cbSize = sizeof(policyPara);
memset(&policyStatus, 0, sizeof(policyStatus));
policyStatus.cbSize = sizeof(policyStatus);
- if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext,
- &policyPara, &policyStatus))
+ if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext, &policyPara, &policyStatus))
{
status = GetLastError();
- DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status));
- goto cleanup;
- }
- if (policyStatus.dwError)
- {
- status = policyStatus.dwError;
- goto cleanup;
+ DEBUG_printf(("CertVerifyCertificateChainPolicy returned %s", http_sspi_strerror(error, sizeof(error), status)));
}
-
- status = SEC_E_OK;
-
-cleanup:
+ else if (policyStatus.dwError)
+ status = policyStatus.dwError;
+ else
+ status = SEC_E_OK;
if (chainContext)
CertFreeCertificateChain(chainContext);
- if (serverNameUnicode)
- LocalFree(serverNameUnicode);
+ if (commonNameUnicode)
+ LocalFree(commonNameUnicode);
return (status);
}
-
-
-/*
- * End of "$Id$".
- */