4 * TLS support for CUPS on Windows using SSPI.
6 * Copyright 2010-2014 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * Include necessary headers...
21 #include "debug-private.h"
25 * Include necessary libraries...
28 #pragma comment(lib, "Crypt32.lib")
29 #pragma comment(lib, "Secur32.lib")
30 #pragma comment(lib, "Ws2_32.lib")
37 #ifndef SECURITY_FLAG_IGNORE_UNKNOWN_CA
38 # define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100 /* Untrusted root */
39 #endif /* SECURITY_FLAG_IGNORE_UNKNOWN_CA */
41 #ifndef SECURITY_FLAG_IGNORE_CERT_CN_INVALID
42 # define SECURITY_FLAG_IGNORE_CERT_CN_INVALID 0x00001000 /* Common name does not match */
43 #endif /* !SECURITY_FLAG_IGNORE_CERT_CN_INVALID */
45 #ifndef SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
46 # define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */
47 #endif /* !SECURITY_FLAG_IGNORE_CERT_DATE_INVALID */
53 static _http_sspi_t
*http_sspi_alloc(void);
54 static int http_sspi_client(http_t
*http
, const char *hostname
);
55 static PCCERT_CONTEXT
http_sspi_create_credential(http_credential_t
*cred
);
56 static BOOL
http_sspi_find_credentials(http_t
*http
, const LPWSTR containerName
, const char *common_name
);
57 static void http_sspi_free(_http_sspi_t
*sspi
);
58 static BOOL
http_sspi_make_credentials(_http_sspi_t
*sspi
, const LPWSTR containerName
, const char *common_name
, _http_mode_t mode
, int years
);
59 static int http_sspi_server(http_t
*http
, const char *hostname
);
60 static void http_sspi_set_allows_any_root(_http_sspi_t
*sspi
, BOOL allow
);
61 static void http_sspi_set_allows_expired_certs(_http_sspi_t
*sspi
, BOOL allow
);
62 static const char *http_sspi_strerror(_http_sspi_t
*sspi
, DWORD code
);
63 static DWORD
http_sspi_verify(PCCERT_CONTEXT cert
, const char *common_name
, DWORD dwCertFlags
);
67 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
72 int /* O - 1 on success, 0 on failure */
73 cupsMakeServerCredentials(
74 const char *path
, /* I - Keychain path or @code NULL@ for default */
75 const char *common_name
, /* I - Common name */
76 int num_alt_names
, /* I - Number of subject alternate names */
77 const char **alt_names
, /* I - Subject Alternate Names */
78 time_t expiration_date
) /* I - Expiration date */
80 _http_sspi_t
*sspi
; /* SSPI data */
81 int ret
; /* Return value */
84 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
));
90 sspi
= http_sspi_alloc();
91 ret
= http_sspi_make_credentials(sspi
, L
"ServerContainer", common_name
, _HTTP_MODE_SERVER
, (int)((expiration_date
- time(NULL
) + 86399) / 86400 / 365));
100 * 'cupsSetServerCredentials()' - Set the default server credentials.
102 * Note: The server credentials are used by all threads in the running process.
103 * This function is threadsafe.
108 int /* O - 1 on success, 0 on failure */
109 cupsSetServerCredentials(
110 const char *path
, /* I - Keychain path or @code NULL@ for default */
111 const char *common_name
, /* I - Default common name for server */
112 int auto_create
) /* I - 1 = automatically create self-signed certificates */
114 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path
, common_name
, auto_create
));
125 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
126 * an encrypted connection.
128 * @since CUPS 1.5/OS X 10.7@
131 int /* O - Status of call (0 = success) */
133 http_t
*http
, /* I - Connection to server */
134 cups_array_t
**credentials
) /* O - Array of credentials */
136 DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http
, credentials
));
138 if (!http
|| !http
->tls
|| !http
->tls
->remoteCert
|| !credentials
)
146 *credentials
= cupsArrayNew(NULL
, NULL
);
147 httpAddCredential(*credentials
, http
->tls
->remoteCert
->pbCertEncoded
, http
->tls
->remoteCert
->cbCertEncoded
);
154 * '_httpCreateCredentials()' - Create credentials in the internal format.
157 http_tls_credentials_t
/* O - Internal credentials */
158 _httpCreateCredentials(
159 cups_array_t
*credentials
) /* I - Array of credentials */
161 return (http_sspi_create_credential((http_credential_t
*)cupsArrayFirst(credentials
)));
166 * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
171 int /* O - 1 if valid, 0 otherwise */
172 httpCredentialsAreValidForName(
173 cups_array_t
*credentials
, /* I - Credentials */
174 const char *common_name
) /* I - Name to check */
176 int valid
= 1; /* Valid name? */
177 PCCERT_CONTEXT cert
= http_sspi_create_credential((http_credential_t
*)cupsArrayFirst(credentials
));
179 char cert_name
[1024]; /* Name from certificate */
184 if (CertNameToStr(X509_ASN_ENCODING
, &(cert
->pCertInfo
->Subject
), CERT_SIMPLE_NAME_STR
, cert_name
, sizeof(cert_name
)))
187 * Extract common name at end...
190 char *ptr
= strrchr(cert_name
, ',');
192 _cups_strcpy(cert_name
, ptr
+ 2);
195 strlcpy(cert_name
, "unknown", sizeof(cert_name
));
197 CertFreeCertificateContext(cert
);
200 strlcpy(cert_name
, "unknown", sizeof(cert_name
));
203 * Compare the common names...
206 if (_cups_strcasecmp(common_name
, cert_name
))
209 * Not an exact match for the common name, check for wildcard certs...
212 const char *domain
= strchr(common_name
, '.');
213 /* Domain in common name */
215 if (strncmp(cert_name
, "*.", 2) || !domain
|| _cups_strcasecmp(domain
, cert_name
+ 1))
218 * Not a wildcard match.
221 /* TODO: Check subject alternate names */
231 * 'httpCredentialsGetTrust()' - Return the trust of credentials.
236 http_trust_t
/* O - Level of trust */
237 httpCredentialsGetTrust(
238 cups_array_t
*credentials
, /* I - Credentials */
239 const char *common_name
) /* I - Common name for trust lookup */
241 http_trust_t trust
= HTTP_TRUST_OK
; /* Level of trust */
242 PCCERT_CONTEXT cert
= NULL
; /* Certificate to validate */
243 DWORD certFlags
= 0; /* Cert verification flags */
244 _cups_globals_t
*cg
= _cupsGlobals(); /* Per-thread global data */
248 return (HTTP_TRUST_UNKNOWN
);
250 cert
= http_sspi_create_credential((http_credential_t
*)cupsArrayFirst(credentials
));
252 return (HTTP_TRUST_UNKNOWN
);
255 certFlags
|= SECURITY_FLAG_IGNORE_UNKNOWN_CA
;
257 if (cg
->expired_certs
)
258 certFlags
|= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
260 if (!cg
->validate_certs
)
261 certFlags
|= SECURITY_FLAG_IGNORE_CERT_CN_INVALID
;
263 if (http_sspi_verify(cert
, common_name
, certFlags
) != SEC_E_OK
)
264 trust
= HTTP_TRUST_INVALID
;
266 CertFreeCertificateContext(cert
);
273 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
278 time_t /* O - Expiration date of credentials */
279 httpCredentialsGetExpiration(
280 cups_array_t
*credentials
) /* I - Credentials */
282 time_t expiration_date
= 0; /* Expiration data of credentials */
283 PCCERT_CONTEXT cert
= http_sspi_create_credential((http_credential_t
*)cupsArrayFirst(credentials
));
288 SYSTEMTIME systime
; /* System time */
289 struct tm tm
; /* UNIX date/time */
291 FileTimeToSystemTime(&(cert
->pCertInfo
->NotAfter
), &systime
);
293 tm
.tm_year
= systime
.wYear
- 1900;
294 tm
.tm_mon
= systime
.wMonth
- 1;
295 tm
.tm_mday
= systime
.wDay
;
296 tm
.tm_hour
= systime
.wHour
;
297 tm
.tm_min
= systime
.wMinute
;
298 tm
.tm_sec
= systime
.wSecond
;
300 expiration_date
= mktime(&tm
);
302 CertFreeCertificateContext(cert
);
305 return (expiration_date
);
310 * 'httpCredentialsString()' - Return a string representing the credentials.
315 size_t /* O - Total size of credentials string */
316 httpCredentialsString(
317 cups_array_t
*credentials
, /* I - Credentials */
318 char *buffer
, /* I - Buffer or @code NULL@ */
319 size_t bufsize
) /* I - Size of buffer */
321 http_credential_t
*first
= (http_credential_t
*)cupsArrayFirst(credentials
);
322 /* First certificate */
323 PCCERT_CONTEXT cert
; /* Certificate */
326 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT
")", credentials
, buffer
, CUPS_LLCAST bufsize
));
331 if (buffer
&& bufsize
> 0)
334 cert
= http_sspi_create_credential(first
);
338 char cert_name
[256]; /* Common name */
339 SYSTEMTIME systime
; /* System time */
340 struct tm tm
; /* UNIX date/time */
341 time_t expiration
; /* Expiration date of cert */
342 _cups_md5_state_t md5_state
; /* MD5 state */
343 unsigned char md5_digest
[16]; /* MD5 result */
345 FileTimeToSystemTime(&(cert
->pCertInfo
->NotAfter
), &systime
);
347 tm
.tm_year
= systime
.wYear
- 1900;
348 tm
.tm_mon
= systime
.wMonth
- 1;
349 tm
.tm_mday
= systime
.wDay
;
350 tm
.tm_hour
= systime
.wHour
;
351 tm
.tm_min
= systime
.wMinute
;
352 tm
.tm_sec
= systime
.wSecond
;
354 expiration
= mktime(&tm
);
356 if (CertNameToStr(X509_ASN_ENCODING
, &(cert
->pCertInfo
->Subject
), CERT_SIMPLE_NAME_STR
, cert_name
, sizeof(cert_name
)))
359 * Extract common name at end...
362 char *ptr
= strrchr(cert_name
, ',');
364 _cups_strcpy(cert_name
, ptr
+ 2);
367 strlcpy(cert_name
, "unknown", sizeof(cert_name
));
369 _cupsMD5Init(&md5_state
);
370 _cupsMD5Append(&md5_state
, first
->data
, (int)first
->datalen
);
371 _cupsMD5Finish(&md5_state
, md5_digest
);
373 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]);
375 CertFreeCertificateContext(cert
);
378 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer
));
380 return (strlen(buffer
));
385 * '_httpFreeCredentials()' - Free internal credentials.
389 _httpFreeCredentials(
390 http_tls_credentials_t credentials
) /* I - Internal credentials */
395 CertFreeCertificateContext(credentials
);
400 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
405 int /* O - 0 on success, -1 on error */
407 const char *path
, /* I - Keychain path or @code NULL@ for default */
408 cups_array_t
**credentials
, /* IO - Credentials */
409 const char *common_name
) /* I - Common name for credentials */
411 DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path
, credentials
, common_name
));
419 DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials
? 0 : -1));
421 return (*credentials
? 0 : -1);
426 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
431 int /* O - -1 on error, 0 on success */
433 const char *path
, /* I - Keychain path or @code NULL@ for default */
434 cups_array_t
*credentials
, /* I - Credentials */
435 const char *common_name
) /* I - Common name for credentials */
437 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path
, credentials
, common_name
));
443 DEBUG_printf(("1httpSaveCredentials: Returning %d.", -1));
449 * '_httpTLSInitialize()' - Initialize the TLS stack.
453 _httpTLSInitialize(void)
462 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
465 size_t /* O - Bytes available */
466 _httpTLSPending(http_t
*http
) /* I - HTTP connection */
469 return (http
->tls
->readBufferUsed
);
476 * '_httpTLSRead()' - Read from a SSL/TLS connection.
479 int /* O - Bytes read */
480 _httpTLSRead(http_t
*http
, /* I - HTTP connection */
481 char *buf
, /* I - Buffer to store data */
482 int len
) /* I - Length of buffer */
484 int i
; /* Looping var */
485 _http_sspi_t
*sspi
= http
->tls
; /* SSPI data */
486 SecBufferDesc message
; /* Array of SecBuffer struct */
487 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
488 int num
= 0; /* Return value */
489 PSecBuffer pDataBuffer
; /* Data buffer */
490 PSecBuffer pExtraBuffer
; /* Excess data buffer */
491 SECURITY_STATUS scRet
; /* SSPI status */
494 DEBUG_printf(("4_httpTLSRead(http=%p, buf=%p, len=%d)", http
, buf
, len
));
497 * If there are bytes that have already been decrypted and have not yet been
498 * read, return those...
501 if (sspi
->readBufferUsed
> 0)
503 int bytesToCopy
= min(sspi
->readBufferUsed
, len
);
504 /* Number of bytes to copy */
506 memcpy(buf
, sspi
->readBuffer
, bytesToCopy
);
507 sspi
->readBufferUsed
-= bytesToCopy
;
509 if (sspi
->readBufferUsed
> 0)
510 memmove(sspi
->readBuffer
, sspi
->readBuffer
+ bytesToCopy
, sspi
->readBufferUsed
);
512 DEBUG_printf(("5_httpTLSRead: Returning %d bytes previously decrypted.", bytesToCopy
));
514 return (bytesToCopy
);
518 * Initialize security buffer structs
521 message
.ulVersion
= SECBUFFER_VERSION
;
522 message
.cBuffers
= 4;
523 message
.pBuffers
= buffers
;
528 * If there is not enough space in the buffer, then increase its size...
531 if (sspi
->decryptBufferLength
<= sspi
->decryptBufferUsed
)
533 BYTE
*temp
; /* New buffer */
535 if (sspi
->decryptBufferLength
>= 262144)
537 WSASetLastError(E_OUTOFMEMORY
);
538 DEBUG_puts("_httpTLSRead: Decryption buffer too large (>256k)");
542 if ((temp
= realloc(sspi
->decryptBuffer
, sspi
->decryptBufferLength
+ 4096)) == NULL
)
544 DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte decryption buffer.", sspi
->decryptBufferLength
+ 4096));
545 WSASetLastError(E_OUTOFMEMORY
);
549 sspi
->decryptBufferLength
+= 4096;
550 sspi
->decryptBuffer
= temp
;
552 DEBUG_printf(("_httpTLSRead: Resized decryption buffer to %d bytes.", sspi
->decryptBufferLength
));
555 buffers
[0].pvBuffer
= sspi
->decryptBuffer
;
556 buffers
[0].cbBuffer
= (unsigned long)sspi
->decryptBufferUsed
;
557 buffers
[0].BufferType
= SECBUFFER_DATA
;
558 buffers
[1].BufferType
= SECBUFFER_EMPTY
;
559 buffers
[2].BufferType
= SECBUFFER_EMPTY
;
560 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
562 DEBUG_printf(("5_httpTLSRead: decryptBufferUsed=%d", sspi
->decryptBufferUsed
));
564 scRet
= DecryptMessage(&sspi
->context
, &message
, 0, NULL
);
566 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
568 num
= recv(http
->fd
, sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
, (int)(sspi
->decryptBufferLength
- sspi
->decryptBufferUsed
), 0);
571 DEBUG_printf(("5_httpTLSRead: recv failed: %d", WSAGetLastError()));
576 DEBUG_puts("5_httpTLSRead: Server disconnected.");
580 DEBUG_printf(("5_httpTLSRead: Read %d bytes into decryption buffer.", num
));
582 sspi
->decryptBufferUsed
+= num
;
585 while (scRet
== SEC_E_INCOMPLETE_MESSAGE
);
587 if (scRet
== SEC_I_CONTEXT_EXPIRED
)
589 DEBUG_puts("5_httpTLSRead: Context expired.");
590 WSASetLastError(WSAECONNRESET
);
593 else if (scRet
!= SEC_E_OK
)
595 DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %s", http_sspi_strerror(sspi
, scRet
)));
596 WSASetLastError(WSASYSCALLFAILURE
);
601 * The decryption worked. Now, locate data buffer.
607 for (i
= 1; i
< 4; i
++)
609 if (buffers
[i
].BufferType
== SECBUFFER_DATA
)
610 pDataBuffer
= &buffers
[i
];
611 else if (!pExtraBuffer
&& (buffers
[i
].BufferType
== SECBUFFER_EXTRA
))
612 pExtraBuffer
= &buffers
[i
];
616 * If a data buffer is found, then copy the decrypted bytes to the passed-in
622 int bytesToCopy
= min((int)pDataBuffer
->cbBuffer
, len
);
623 /* Number of bytes to copy into buf */
624 int bytesToSave
= pDataBuffer
->cbBuffer
- bytesToCopy
;
625 /* Number of bytes to save in our read buffer */
628 memcpy(buf
, pDataBuffer
->pvBuffer
, bytesToCopy
);
631 * If there are more decrypted bytes than can be copied to the passed in
632 * buffer, then save them...
637 if ((sspi
->readBufferLength
- sspi
->readBufferUsed
) < bytesToSave
)
639 BYTE
*temp
; /* New buffer pointer */
641 if ((temp
= realloc(sspi
->readBuffer
, sspi
->readBufferUsed
+ bytesToSave
)) == NULL
)
643 DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", sspi
->readBufferUsed
+ bytesToSave
));
644 WSASetLastError(E_OUTOFMEMORY
);
648 sspi
->readBufferLength
= sspi
->readBufferUsed
+ bytesToSave
;
649 sspi
->readBuffer
= temp
;
652 memcpy(((BYTE
*)sspi
->readBuffer
) + sspi
->readBufferUsed
, ((BYTE
*)pDataBuffer
->pvBuffer
) + bytesToCopy
, bytesToSave
);
654 sspi
->readBufferUsed
+= bytesToSave
;
661 DEBUG_puts("_httpTLSRead: Unable to find data buffer.");
662 WSASetLastError(WSASYSCALLFAILURE
);
667 * If the decryption process left extra bytes, then save those back in
668 * decryptBuffer. They will be processed the next time through the loop.
673 memmove(sspi
->decryptBuffer
, pExtraBuffer
->pvBuffer
, pExtraBuffer
->cbBuffer
);
674 sspi
->decryptBufferUsed
= pExtraBuffer
->cbBuffer
;
678 sspi
->decryptBufferUsed
= 0;
686 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
689 int /* O - 0 on success, -1 on failure */
690 _httpTLSStart(http_t
*http
) /* I - HTTP connection */
692 char hostname
[256], /* Hostname */
693 *hostptr
; /* Pointer into hostname */
696 DEBUG_printf(("7_httpTLSStart(http=%p)", http
));
698 if ((http
->tls
= http_sspi_alloc()) == NULL
)
701 if (http
->mode
== _HTTP_MODE_CLIENT
)
704 * Client: determine hostname...
707 if (httpAddrLocalhost(http
->hostaddr
))
709 strlcpy(hostname
, "localhost", sizeof(hostname
));
714 * Otherwise make sure the hostname we have does not end in a trailing dot.
717 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
718 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
723 return (http_sspi_client(http
, hostname
));
728 * Server: determine hostname to use...
731 if (http
->fields
[HTTP_FIELD_HOST
][0])
734 * Use hostname for TLS upgrade...
737 strlcpy(hostname
, http
->fields
[HTTP_FIELD_HOST
], sizeof(hostname
));
742 * Resolve hostname from connection address...
745 http_addr_t addr
; /* Connection address */
746 socklen_t addrlen
; /* Length of address */
748 addrlen
= sizeof(addr
);
749 if (getsockname(http
->fd
, (struct sockaddr
*)&addr
, &addrlen
))
751 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno
)));
754 else if (httpAddrLocalhost(&addr
))
758 httpAddrLookup(&addr
, hostname
, sizeof(hostname
));
759 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname
));
763 return (http_sspi_server(http
, hostname
));
769 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
773 _httpTLSStop(http_t
*http
) /* I - HTTP connection */
775 _http_sspi_t
*sspi
= http
->tls
; /* SSPI data */
778 if (sspi
->contextInitialized
&& http
->fd
>= 0)
780 SecBufferDesc message
; /* Array of SecBuffer struct */
781 SecBuffer buffers
[1] = { 0 };
782 /* Security package buffer */
783 DWORD dwType
; /* Type */
784 DWORD status
; /* Status */
787 * Notify schannel that we are about to close the connection.
790 dwType
= SCHANNEL_SHUTDOWN
;
792 buffers
[0].pvBuffer
= &dwType
;
793 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
794 buffers
[0].cbBuffer
= sizeof(dwType
);
796 message
.cBuffers
= 1;
797 message
.pBuffers
= buffers
;
798 message
.ulVersion
= SECBUFFER_VERSION
;
800 status
= ApplyControlToken(&sspi
->context
, &message
);
802 if (SUCCEEDED(status
))
804 PBYTE pbMessage
; /* Message buffer */
805 DWORD cbMessage
; /* Message buffer count */
806 DWORD cbData
; /* Data count */
807 DWORD dwSSPIFlags
; /* SSL attributes we requested */
808 DWORD dwSSPIOutFlags
; /* SSL attributes we received */
809 TimeStamp tsExpiry
; /* Time stamp */
811 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
812 ASC_REQ_REPLAY_DETECT
|
813 ASC_REQ_CONFIDENTIALITY
|
814 ASC_REQ_EXTENDED_ERROR
|
815 ASC_REQ_ALLOCATE_MEMORY
|
818 buffers
[0].pvBuffer
= NULL
;
819 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
820 buffers
[0].cbBuffer
= 0;
822 message
.cBuffers
= 1;
823 message
.pBuffers
= buffers
;
824 message
.ulVersion
= SECBUFFER_VERSION
;
826 status
= AcceptSecurityContext(&sspi
->creds
, &sspi
->context
, NULL
,
827 dwSSPIFlags
, SECURITY_NATIVE_DREP
, NULL
,
828 &message
, &dwSSPIOutFlags
, &tsExpiry
);
830 if (SUCCEEDED(status
))
832 pbMessage
= buffers
[0].pvBuffer
;
833 cbMessage
= buffers
[0].cbBuffer
;
836 * Send the close notify message to the client.
839 if (pbMessage
&& cbMessage
)
841 cbData
= send(http
->fd
, pbMessage
, cbMessage
, 0);
842 if ((cbData
== SOCKET_ERROR
) || (cbData
== 0))
844 status
= WSAGetLastError();
845 DEBUG_printf(("_httpTLSStop: sending close notify failed: %d", status
));
849 FreeContextBuffer(pbMessage
);
855 DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi
, status
)));
860 DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %s", http_sspi_strerror(sspi
, status
)));
864 http_sspi_free(sspi
);
871 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
874 int /* O - Bytes written */
875 _httpTLSWrite(http_t
*http
, /* I - HTTP connection */
876 const char *buf
, /* I - Buffer holding data */
877 int len
) /* I - Length of buffer */
879 _http_sspi_t
*sspi
= http
->tls
; /* SSPI data */
880 SecBufferDesc message
; /* Array of SecBuffer struct */
881 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
882 int bufferLen
; /* Buffer length */
883 int bytesLeft
; /* Bytes left to write */
884 const char *bufptr
; /* Pointer into buffer */
885 int num
= 0; /* Return value */
888 bufferLen
= sspi
->streamSizes
.cbMaximumMessage
+ sspi
->streamSizes
.cbHeader
+ sspi
->streamSizes
.cbTrailer
;
890 if (bufferLen
> sspi
->writeBufferLength
)
892 BYTE
*temp
; /* New buffer pointer */
894 if ((temp
= (BYTE
*)realloc(sspi
->writeBuffer
, bufferLen
)) == NULL
)
896 DEBUG_printf(("_httpTLSWrite: Unable to allocate buffer of %d bytes.", bufferLen
));
897 WSASetLastError(E_OUTOFMEMORY
);
901 sspi
->writeBuffer
= temp
;
902 sspi
->writeBufferLength
= bufferLen
;
910 int chunk
= min((int)sspi
->streamSizes
.cbMaximumMessage
, bytesLeft
);
911 /* Size of data to write */
912 SECURITY_STATUS scRet
; /* SSPI status */
915 * Copy user data into the buffer, starting just past the header...
918 memcpy(sspi
->writeBuffer
+ sspi
->streamSizes
.cbHeader
, bufptr
, chunk
);
921 * Setup the SSPI buffers
924 message
.ulVersion
= SECBUFFER_VERSION
;
925 message
.cBuffers
= 4;
926 message
.pBuffers
= buffers
;
928 buffers
[0].pvBuffer
= sspi
->writeBuffer
;
929 buffers
[0].cbBuffer
= sspi
->streamSizes
.cbHeader
;
930 buffers
[0].BufferType
= SECBUFFER_STREAM_HEADER
;
931 buffers
[1].pvBuffer
= sspi
->writeBuffer
+ sspi
->streamSizes
.cbHeader
;
932 buffers
[1].cbBuffer
= (unsigned long) chunk
;
933 buffers
[1].BufferType
= SECBUFFER_DATA
;
934 buffers
[2].pvBuffer
= sspi
->writeBuffer
+ sspi
->streamSizes
.cbHeader
+ chunk
;
935 buffers
[2].cbBuffer
= sspi
->streamSizes
.cbTrailer
;
936 buffers
[2].BufferType
= SECBUFFER_STREAM_TRAILER
;
937 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
943 scRet
= EncryptMessage(&sspi
->context
, 0, &message
, 0);
947 DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %s", http_sspi_strerror(sspi
, scRet
)));
948 WSASetLastError(WSASYSCALLFAILURE
);
953 * Send the data. Remember the size of the total data to send is the size
954 * of the header, the size of the data the caller passed in and the size
958 num
= send(http
->fd
, sspi
->writeBuffer
, buffers
[0].cbBuffer
+ buffers
[1].cbBuffer
+ buffers
[2].cbBuffer
, 0);
962 DEBUG_printf(("_httpTLSWrite: send failed: %ld", WSAGetLastError()));
976 * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
979 static int /* O - 0 on success, -1 on failure */
980 http_setup_ssl(http_t
*http
) /* I - Connection to server */
982 char hostname
[256], /* Hostname */
983 *hostptr
; /* Pointer into hostname */
985 TCHAR username
[256]; /* Username returned from GetUserName() */
986 TCHAR commonName
[256];/* Common name for certificate */
987 DWORD dwSize
; /* 32 bit size */
990 DEBUG_printf(("7http_setup_ssl(http=%p)", http
));
993 * Get the hostname to use for SSL...
996 if (httpAddrLocalhost(http
->hostaddr
))
998 strlcpy(hostname
, "localhost", sizeof(hostname
));
1003 * Otherwise make sure the hostname we have does not end in a trailing dot.
1006 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
1007 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
1012 http
->tls
= http_sspi_alloc();
1016 _cupsSetHTTPError(HTTP_STATUS_ERROR
);
1020 dwSize
= sizeof(username
) / sizeof(TCHAR
);
1021 GetUserName(username
, &dwSize
);
1022 _sntprintf_s(commonName
, sizeof(commonName
) / sizeof(TCHAR
),
1023 sizeof(commonName
) / sizeof(TCHAR
), TEXT("CN=%s"), username
);
1025 if (!_sspiGetCredentials(http
->tls
, L
"ClientContainer",
1028 _sspiFree(http
->tls
);
1032 http
->status
= HTTP_STATUS_ERROR
;
1034 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
1035 _("Unable to establish a secure connection to host."), 1);
1040 _sspiSetAllowsAnyRoot(http
->tls
, TRUE
);
1041 _sspiSetAllowsExpiredCerts(http
->tls
, TRUE
);
1043 if (!_sspiConnect(http
->tls
, hostname
))
1045 _sspiFree(http
->tls
);
1049 http
->status
= HTTP_STATUS_ERROR
;
1051 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
1052 _("Unable to establish a secure connection to host."), 1);
1063 * 'http_sspi_alloc()' - Allocate SSPI object.
1066 static _http_sspi_t
* /* O - New SSPI/SSL object */
1067 http_sspi_alloc(void)
1069 return ((_http_sspi_t
*)calloc(sizeof(_http_sspi_t
), 1));
1074 * 'http_sspi_client()' - Negotiate a TLS connection as a client.
1077 static int /* O - 0 on success, -1 on failure */
1078 http_sspi_client(http_t
*http
, /* I - Client connection */
1079 const char *hostname
) /* I - Server hostname */
1081 _http_sspi_t
*sspi
= http
->tls
; /* SSPI data */
1082 DWORD dwSize
; /* Size for buffer */
1083 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1084 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1085 TimeStamp tsExpiry
; /* Time stamp */
1086 SECURITY_STATUS scRet
; /* Status */
1087 int cbData
; /* Data count */
1088 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1089 SecBuffer inBuffers
[2]; /* Security package buffer */
1090 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1091 SecBuffer outBuffers
[1]; /* Security package buffer */
1092 int ret
= 0; /* Return value */
1093 char username
[1024], /* Current username */
1094 common_name
[1024]; /* CN=username */
1097 DEBUG_printf(("http_sspi_client(http=%p, hostname=\"%s\")", http
, hostname
));
1099 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
|
1100 ISC_REQ_REPLAY_DETECT
|
1101 ISC_REQ_CONFIDENTIALITY
|
1102 ISC_RET_EXTENDED_ERROR
|
1103 ISC_REQ_ALLOCATE_MEMORY
|
1107 * Lookup the client certificate...
1110 dwSize
= sizeof(username
);
1111 GetUserName(username
, &dwSize
);
1112 snprintf(common_name
, sizeof(common_name
), "CN=%s", username
);
1114 if (!http_sspi_find_credentials(http
, L
"ClientContainer", common_name
))
1115 if (!http_sspi_make_credentials(http
->tls
, L
"ClientContainer", common_name
, _HTTP_MODE_CLIENT
, 10))
1117 DEBUG_puts("http_sspi_client: Unable to get client credentials.");
1122 * Initiate a ClientHello message and generate a token.
1125 outBuffers
[0].pvBuffer
= NULL
;
1126 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1127 outBuffers
[0].cbBuffer
= 0;
1129 outBuffer
.cBuffers
= 1;
1130 outBuffer
.pBuffers
= outBuffers
;
1131 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1133 scRet
= InitializeSecurityContext(&sspi
->creds
, NULL
, TEXT(""), dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, NULL
, 0, &sspi
->context
, &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1135 if (scRet
!= SEC_I_CONTINUE_NEEDED
)
1137 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(1) failed: %s", http_sspi_strerror(sspi
, scRet
)));
1142 * Send response to server if there is one.
1145 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1147 if ((cbData
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0)) <= 0)
1149 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1150 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1151 DeleteSecurityContext(&sspi
->context
);
1155 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData
));
1157 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1158 outBuffers
[0].pvBuffer
= NULL
;
1161 dwSSPIFlags
= ISC_REQ_MANUAL_CRED_VALIDATION
|
1162 ISC_REQ_SEQUENCE_DETECT
|
1163 ISC_REQ_REPLAY_DETECT
|
1164 ISC_REQ_CONFIDENTIALITY
|
1165 ISC_RET_EXTENDED_ERROR
|
1166 ISC_REQ_ALLOCATE_MEMORY
|
1169 sspi
->decryptBufferUsed
= 0;
1172 * Loop until the handshake is finished or an error occurs.
1175 scRet
= SEC_I_CONTINUE_NEEDED
;
1177 while(scRet
== SEC_I_CONTINUE_NEEDED
||
1178 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1179 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1181 if (sspi
->decryptBufferUsed
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1183 if (sspi
->decryptBufferLength
<= sspi
->decryptBufferUsed
)
1185 BYTE
*temp
; /* New buffer */
1187 if (sspi
->decryptBufferLength
>= 262144)
1189 WSASetLastError(E_OUTOFMEMORY
);
1190 DEBUG_puts("http_sspi_client: Decryption buffer too large (>256k)");
1194 if ((temp
= realloc(sspi
->decryptBuffer
, sspi
->decryptBufferLength
+ 4096)) == NULL
)
1196 DEBUG_printf(("http_sspi_client: Unable to allocate %d byte buffer.", sspi
->decryptBufferLength
+ 4096));
1197 WSASetLastError(E_OUTOFMEMORY
);
1201 sspi
->decryptBufferLength
+= 4096;
1202 sspi
->decryptBuffer
= temp
;
1205 cbData
= recv(http
->fd
, sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
, (int)(sspi
->decryptBufferLength
- sspi
->decryptBufferUsed
), 0);
1209 DEBUG_printf(("http_sspi_client: recv failed: %d", WSAGetLastError()));
1212 else if (cbData
== 0)
1214 DEBUG_printf(("http_sspi_client: Server unexpectedly disconnected."));
1218 DEBUG_printf(("http_sspi_client: %d bytes of handshake data received", cbData
));
1220 sspi
->decryptBufferUsed
+= cbData
;
1224 * Set up the input buffers. Buffer 0 is used to pass in data received from
1225 * the server. Schannel will consume some or all of this. Leftover data
1226 * (if any) will be placed in buffer 1 and given a buffer type of
1230 inBuffers
[0].pvBuffer
= sspi
->decryptBuffer
;
1231 inBuffers
[0].cbBuffer
= (unsigned long)sspi
->decryptBufferUsed
;
1232 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1234 inBuffers
[1].pvBuffer
= NULL
;
1235 inBuffers
[1].cbBuffer
= 0;
1236 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1238 inBuffer
.cBuffers
= 2;
1239 inBuffer
.pBuffers
= inBuffers
;
1240 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1243 * Set up the output buffers. These are initialized to NULL so as to make it
1244 * less likely we'll attempt to free random garbage later.
1247 outBuffers
[0].pvBuffer
= NULL
;
1248 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1249 outBuffers
[0].cbBuffer
= 0;
1251 outBuffer
.cBuffers
= 1;
1252 outBuffer
.pBuffers
= outBuffers
;
1253 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1256 * Call InitializeSecurityContext.
1259 scRet
= InitializeSecurityContext(&sspi
->creds
, &sspi
->context
, NULL
, dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, &inBuffer
, 0, NULL
, &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1262 * If InitializeSecurityContext was successful (or if the error was one of
1263 * the special extended ones), send the contents of the output buffer to the
1267 if (scRet
== SEC_E_OK
||
1268 scRet
== SEC_I_CONTINUE_NEEDED
||
1269 FAILED(scRet
) && (dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
))
1271 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1273 cbData
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1277 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1278 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1279 DeleteSecurityContext(&sspi
->context
);
1283 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData
));
1286 * Free output buffer.
1289 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1290 outBuffers
[0].pvBuffer
= NULL
;
1295 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we
1296 * need to read more data from the server and try again.
1299 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1303 * If InitializeSecurityContext returned SEC_E_OK, then the handshake
1304 * completed successfully.
1307 if (scRet
== SEC_E_OK
)
1310 * If the "extra" buffer contains data, this is encrypted application
1311 * protocol layer stuff. It needs to be saved. The application layer will
1312 * later decrypt it with DecryptMessage.
1315 DEBUG_puts("http_sspi_client: Handshake was successful.");
1317 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1319 memmove(sspi
->decryptBuffer
, sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
- inBuffers
[1].cbBuffer
, inBuffers
[1].cbBuffer
);
1321 sspi
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1323 DEBUG_printf(("http_sspi_client: %d bytes of app data was bundled with handshake data", sspi
->decryptBufferUsed
));
1326 sspi
->decryptBufferUsed
= 0;
1336 * Check for fatal error.
1341 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(2) failed: %s", http_sspi_strerror(sspi
, scRet
)));
1347 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1348 * then the server just requested client authentication.
1351 if (scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1357 DEBUG_printf(("http_sspi_client: server requested client credentials."));
1363 * Copy any leftover data from the "extra" buffer, and go around again.
1366 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1368 memmove(sspi
->decryptBuffer
, sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
- inBuffers
[1].cbBuffer
, inBuffers
[1].cbBuffer
);
1370 sspi
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1374 sspi
->decryptBufferUsed
= 0;
1381 * Success! Get the server cert
1384 sspi
->contextInitialized
= TRUE
;
1386 scRet
= QueryContextAttributes(&sspi
->context
, SECPKG_ATTR_REMOTE_CERT_CONTEXT
, (VOID
*)&(sspi
->remoteCert
));
1388 if (scRet
!= SEC_E_OK
)
1390 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %s", http_sspi_strerror(sspi
, scRet
)));
1395 * Find out how big the header/trailer will be:
1398 scRet
= QueryContextAttributes(&sspi
->context
, SECPKG_ATTR_STREAM_SIZES
, &sspi
->streamSizes
);
1400 if (scRet
!= SEC_E_OK
)
1402 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %s", http_sspi_strerror(sspi
, scRet
)));
1412 * 'http_sspi_create_credential()' - Create an SSPI certificate context.
1415 static PCCERT_CONTEXT
/* O - Certificate context */
1416 http_sspi_create_credential(
1417 http_credential_t
*cred
) /* I - Credential */
1420 return (CertCreateCertificateContext(X509_ASN_ENCODING
, cred
->data
, cred
->datalen
));
1427 * 'http_sspi_find_credentials()' - Retrieve a TLS certificate from the system store.
1430 static BOOL
/* O - 1 on success, 0 on failure */
1431 http_sspi_find_credentials(
1432 http_t
*http
, /* I - HTTP connection */
1433 const LPWSTR container
, /* I - Cert container name */
1434 const char *common_name
) /* I - Common name of certificate */
1436 _http_sspi_t
*sspi
= http
->tls
; /* SSPI data */
1437 HCERTSTORE store
= NULL
; /* Certificate store */
1438 PCCERT_CONTEXT storedContext
= NULL
; /* Context created from the store */
1439 DWORD dwSize
= 0; /* 32 bit size */
1440 PBYTE p
= NULL
; /* Temporary storage */
1441 HCRYPTPROV hProv
= (HCRYPTPROV
)NULL
;
1442 /* Handle to a CSP */
1443 CERT_NAME_BLOB sib
; /* Arbitrary array of bytes */
1444 SCHANNEL_CRED SchannelCred
; /* Schannel credential data */
1445 TimeStamp tsExpiry
; /* Time stamp */
1446 SECURITY_STATUS Status
; /* Status */
1447 BOOL ok
= TRUE
; /* Return value */
1450 if (!CryptAcquireContextW(&hProv
, (LPWSTR
)container
, MS_DEF_PROV_W
, PROV_RSA_FULL
, CRYPT_NEWKEYSET
| CRYPT_MACHINE_KEYSET
))
1452 if (GetLastError() == NTE_EXISTS
)
1454 if (!CryptAcquireContextW(&hProv
, (LPWSTR
)container
, MS_DEF_PROV_W
, PROV_RSA_FULL
, CRYPT_MACHINE_KEYSET
))
1456 DEBUG_printf(("http_sspi_find_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1463 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");
1467 DEBUG_printf(("http_sspi_find_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1474 if (!CertStrToName(X509_ASN_ENCODING
, common_name
, CERT_OID_NAME_STR
, NULL
, NULL
, &dwSize
, NULL
))
1476 DEBUG_printf(("http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1481 p
= (PBYTE
)malloc(dwSize
);
1485 DEBUG_printf(("http_sspi_find_credentials: malloc failed for %d bytes.", dwSize
));
1490 if (!CertStrToName(X509_ASN_ENCODING
, common_name
, CERT_OID_NAME_STR
, NULL
, p
, &dwSize
, NULL
))
1492 DEBUG_printf(("http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1497 sib
.cbData
= dwSize
;
1500 storedContext
= CertFindCertificateInStore(store
, X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
, 0, CERT_FIND_SUBJECT_NAME
, &sib
, NULL
);
1504 DEBUG_printf(("http_sspi_find_credentials: Unable to find credentials for \"%s\".", common_name
));
1509 ZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
1511 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
1512 SchannelCred
.cCreds
= 1;
1513 SchannelCred
.paCred
= &storedContext
;
1516 * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
1519 if (http
->mode
== _HTTP_MODE_SERVER
)
1520 SchannelCred
.grbitEnabledProtocols
= SP_PROT_SSL3TLS1
;
1523 * Create an SSPI credential.
1526 Status
= AcquireCredentialsHandle(NULL
, UNISP_NAME
, http
->mode
== _HTTP_MODE_SERVER
? SECPKG_CRED_INBOUND
: SECPKG_CRED_OUTBOUND
, NULL
, &SchannelCred
, NULL
, NULL
, &sspi
->creds
, &tsExpiry
);
1527 if (Status
!= SEC_E_OK
)
1529 DEBUG_printf(("http_sspi_find_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi
, Status
)));
1541 CertFreeCertificateContext(storedContext
);
1547 CertCloseStore(store
, 0);
1550 CryptReleaseContext(hProv
, 0);
1557 * 'http_sspi_free()' - Close a connection and free resources.
1561 http_sspi_free(_http_sspi_t
*sspi
) /* I - SSPI data */
1566 if (sspi
->contextInitialized
)
1567 DeleteSecurityContext(&sspi
->context
);
1569 if (sspi
->decryptBuffer
)
1570 free(sspi
->decryptBuffer
);
1572 if (sspi
->readBuffer
)
1573 free(sspi
->readBuffer
);
1575 if (sspi
->writeBuffer
)
1576 free(sspi
->writeBuffer
);
1578 if (sspi
->localCert
)
1579 CertFreeCertificateContext(sspi
->localCert
);
1581 if (sspi
->remoteCert
)
1582 CertFreeCertificateContext(sspi
->remoteCert
);
1589 * 'http_sspi_make_credentials()' - Create a TLS certificate in the system store.
1592 static BOOL
/* O - 1 on success, 0 on failure */
1593 http_sspi_make_credentials(
1594 _http_sspi_t
*sspi
, /* I - SSPI data */
1595 const LPWSTR container
, /* I - Cert container name */
1596 const char *common_name
, /* I - Common name of certificate */
1597 _http_mode_t mode
, /* I - Client or server? */
1598 int years
) /* I - Years until expiration */
1600 HCERTSTORE store
= NULL
; /* Certificate store */
1601 PCCERT_CONTEXT storedContext
= NULL
; /* Context created from the store */
1602 PCCERT_CONTEXT createdContext
= NULL
; /* Context created by us */
1603 DWORD dwSize
= 0; /* 32 bit size */
1604 PBYTE p
= NULL
; /* Temporary storage */
1605 HCRYPTPROV hProv
= (HCRYPTPROV
)NULL
;
1606 /* Handle to a CSP */
1607 CERT_NAME_BLOB sib
; /* Arbitrary array of bytes */
1608 SCHANNEL_CRED SchannelCred
; /* Schannel credential data */
1609 TimeStamp tsExpiry
; /* Time stamp */
1610 SECURITY_STATUS Status
; /* Status */
1611 HCRYPTKEY hKey
= (HCRYPTKEY
)NULL
; /* Handle to crypto key */
1612 CRYPT_KEY_PROV_INFO kpi
; /* Key container info */
1613 SYSTEMTIME et
; /* System time */
1614 CERT_EXTENSIONS exts
; /* Array of cert extensions */
1615 CRYPT_KEY_PROV_INFO ckp
; /* Handle to crypto key */
1616 BOOL ok
= TRUE
; /* Return value */
1619 DEBUG_printf(("4http_sspi_make_credentials(sspi=%p, container=%p, common_name=\"%s\", mode=%d, years=%d)", sspi
, container
, common_name
, mode
, years
));
1621 if (!CryptAcquireContextW(&hProv
, (LPWSTR
)container
, MS_DEF_PROV_W
, PROV_RSA_FULL
, CRYPT_NEWKEYSET
| CRYPT_MACHINE_KEYSET
))
1623 if (GetLastError() == NTE_EXISTS
)
1625 if (!CryptAcquireContextW(&hProv
, (LPWSTR
)container
, MS_DEF_PROV_W
, PROV_RSA_FULL
, CRYPT_MACHINE_KEYSET
))
1627 DEBUG_printf(("http_sspi_make_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1634 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");
1638 DEBUG_printf(("http_sspi_make_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1645 if (!CertStrToName(X509_ASN_ENCODING
, common_name
, CERT_OID_NAME_STR
, NULL
, NULL
, &dwSize
, NULL
))
1647 DEBUG_printf(("http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1652 p
= (PBYTE
)malloc(dwSize
);
1656 DEBUG_printf(("http_sspi_make_credentials: malloc failed for %d bytes", dwSize
));
1661 if (!CertStrToName(X509_ASN_ENCODING
, common_name
, CERT_OID_NAME_STR
, NULL
, p
, &dwSize
, NULL
))
1663 DEBUG_printf(("http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1669 * If we couldn't find the context, then we'll create a new one...
1672 if (!CryptGenKey(hProv
, AT_KEYEXCHANGE
, CRYPT_EXPORTABLE
, &hKey
))
1674 DEBUG_printf(("http_sspi_make_credentials: CryptGenKey failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1679 ZeroMemory(&kpi
, sizeof(kpi
));
1680 kpi
.pwszContainerName
= (LPWSTR
)container
;
1681 kpi
.pwszProvName
= MS_DEF_PROV_W
;
1682 kpi
.dwProvType
= PROV_RSA_FULL
;
1683 kpi
.dwFlags
= CERT_SET_KEY_CONTEXT_PROP_ID
;
1684 kpi
.dwKeySpec
= AT_KEYEXCHANGE
;
1689 ZeroMemory(&exts
, sizeof(exts
));
1691 createdContext
= CertCreateSelfSignCertificate(hProv
, &sib
, 0, &kpi
, NULL
, NULL
, &et
, &exts
);
1693 if (!createdContext
)
1695 DEBUG_printf(("http_sspi_make_credentials: CertCreateSelfSignCertificate failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1700 if (!CertAddCertificateContextToStore(store
, createdContext
, CERT_STORE_ADD_REPLACE_EXISTING
, &storedContext
))
1702 DEBUG_printf(("http_sspi_make_credentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1707 ZeroMemory(&ckp
, sizeof(ckp
));
1708 ckp
.pwszContainerName
= (LPWSTR
) container
;
1709 ckp
.pwszProvName
= MS_DEF_PROV_W
;
1710 ckp
.dwProvType
= PROV_RSA_FULL
;
1711 ckp
.dwFlags
= CRYPT_MACHINE_KEYSET
;
1712 ckp
.dwKeySpec
= AT_KEYEXCHANGE
;
1714 if (!CertSetCertificateContextProperty(storedContext
, CERT_KEY_PROV_INFO_PROP_ID
, 0, &ckp
))
1716 DEBUG_printf(("http_sspi_make_credentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1722 ZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
1724 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
1725 SchannelCred
.cCreds
= 1;
1726 SchannelCred
.paCred
= &storedContext
;
1729 * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
1732 if (mode
== _HTTP_MODE_SERVER
)
1733 SchannelCred
.grbitEnabledProtocols
= SP_PROT_SSL3TLS1
;
1736 * Create an SSPI credential.
1739 Status
= AcquireCredentialsHandle(NULL
, UNISP_NAME
, mode
== _HTTP_MODE_SERVER
? SECPKG_CRED_INBOUND
: SECPKG_CRED_OUTBOUND
, NULL
, &SchannelCred
, NULL
, NULL
, &sspi
->creds
, &tsExpiry
);
1740 if (Status
!= SEC_E_OK
)
1742 DEBUG_printf(("http_sspi_make_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi
, Status
)));
1754 CryptDestroyKey(hKey
);
1757 CertFreeCertificateContext(createdContext
);
1760 CertFreeCertificateContext(storedContext
);
1766 CertCloseStore(store
, 0);
1769 CryptReleaseContext(hProv
, 0);
1776 * 'http_sspi_server()' - Negotiate a TLS connection as a server.
1779 static int /* O - 0 on success, -1 on failure */
1780 http_sspi_server(http_t
*http
, /* I - HTTP connection */
1781 const char *hostname
) /* I - Hostname of server */
1783 _http_sspi_t
*sspi
= http
->tls
; /* I - SSPI data */
1784 char common_name
[512]; /* Common name for cert */
1785 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1786 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1787 TimeStamp tsExpiry
; /* Time stamp */
1788 SECURITY_STATUS scRet
; /* SSPI Status */
1789 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1790 SecBuffer inBuffers
[2]; /* Security package buffer */
1791 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1792 SecBuffer outBuffers
[1]; /* Security package buffer */
1793 int num
= 0; /* 32 bit status value */
1794 BOOL fInitContext
= TRUE
; /* Has the context been init'd? */
1795 int ret
= 0; /* Return value */
1798 DEBUG_printf(("http_sspi_server(http=%p, hostname=\"%s\")", http
, hostname
));
1800 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
1801 ASC_REQ_REPLAY_DETECT
|
1802 ASC_REQ_CONFIDENTIALITY
|
1803 ASC_REQ_EXTENDED_ERROR
|
1804 ASC_REQ_ALLOCATE_MEMORY
|
1807 sspi
->decryptBufferUsed
= 0;
1810 * Lookup the server certificate...
1813 snprintf(common_name
, sizeof(common_name
), "CN=%s", hostname
);
1815 if (!http_sspi_find_credentials(http
, L
"ServerContainer", common_name
))
1816 if (!http_sspi_make_credentials(http
->tls
, L
"ServerContainer", common_name
, _HTTP_MODE_SERVER
, 10))
1818 DEBUG_puts("http_sspi_server: Unable to get server credentials.");
1823 * Set OutBuffer for AcceptSecurityContext call
1826 outBuffer
.cBuffers
= 1;
1827 outBuffer
.pBuffers
= outBuffers
;
1828 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1830 scRet
= SEC_I_CONTINUE_NEEDED
;
1832 while (scRet
== SEC_I_CONTINUE_NEEDED
||
1833 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1834 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1836 if (sspi
->decryptBufferUsed
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1838 if (sspi
->decryptBufferLength
<= sspi
->decryptBufferUsed
)
1840 BYTE
*temp
; /* New buffer */
1842 if (sspi
->decryptBufferLength
>= 262144)
1844 WSASetLastError(E_OUTOFMEMORY
);
1845 DEBUG_puts("http_sspi_server: Decryption buffer too large (>256k)");
1849 if ((temp
= realloc(sspi
->decryptBuffer
, sspi
->decryptBufferLength
+ 4096)) == NULL
)
1851 DEBUG_printf(("http_sspi_server: Unable to allocate %d byte buffer.", sspi
->decryptBufferLength
+ 4096));
1852 WSASetLastError(E_OUTOFMEMORY
);
1856 sspi
->decryptBufferLength
+= 4096;
1857 sspi
->decryptBuffer
= temp
;
1862 num
= recv(http
->fd
, sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
, (int)(sspi
->decryptBufferLength
- sspi
->decryptBufferUsed
), 0);
1864 if (num
== -1 && WSAGetLastError() == WSAEWOULDBLOCK
)
1872 DEBUG_printf(("http_sspi_server: recv failed: %d", WSAGetLastError()));
1877 DEBUG_puts("http_sspi_server: client disconnected");
1881 DEBUG_printf(("http_sspi_server: received %d (handshake) bytes from client.", num
));
1882 sspi
->decryptBufferUsed
+= num
;
1886 * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process
1887 * on this run around the loop.
1890 inBuffers
[0].pvBuffer
= sspi
->decryptBuffer
;
1891 inBuffers
[0].cbBuffer
= (unsigned long)sspi
->decryptBufferUsed
;
1892 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1894 inBuffers
[1].pvBuffer
= NULL
;
1895 inBuffers
[1].cbBuffer
= 0;
1896 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1898 inBuffer
.cBuffers
= 2;
1899 inBuffer
.pBuffers
= inBuffers
;
1900 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1903 * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to
1904 * free random garbage at the quit.
1907 outBuffers
[0].pvBuffer
= NULL
;
1908 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1909 outBuffers
[0].cbBuffer
= 0;
1911 scRet
= AcceptSecurityContext(&sspi
->creds
, (fInitContext
?NULL
:&sspi
->context
), &inBuffer
, dwSSPIFlags
, SECURITY_NATIVE_DREP
, (fInitContext
?&sspi
->context
:NULL
), &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1913 fInitContext
= FALSE
;
1915 if (scRet
== SEC_E_OK
||
1916 scRet
== SEC_I_CONTINUE_NEEDED
||
1917 (FAILED(scRet
) && ((dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
) != 0)))
1919 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1922 * Send response to server if there is one.
1925 num
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1929 DEBUG_printf(("http_sspi_server: handshake send failed: %d", WSAGetLastError()));
1933 DEBUG_printf(("http_sspi_server: sent %d handshake bytes to client.", outBuffers
[0].cbBuffer
));
1935 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1936 outBuffers
[0].pvBuffer
= NULL
;
1940 if (scRet
== SEC_E_OK
)
1943 * If there's extra data then save it for next time we go to decrypt.
1946 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1948 memcpy(sspi
->decryptBuffer
, (LPBYTE
)(sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
- inBuffers
[1].cbBuffer
), inBuffers
[1].cbBuffer
);
1949 sspi
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1953 sspi
->decryptBufferUsed
= 0;
1957 else if (FAILED(scRet
) && scRet
!= SEC_E_INCOMPLETE_MESSAGE
)
1959 DEBUG_printf(("http_sspi_server: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi
, scRet
)));
1964 if (scRet
!= SEC_E_INCOMPLETE_MESSAGE
&&
1965 scRet
!= SEC_I_INCOMPLETE_CREDENTIALS
)
1967 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1969 memcpy(sspi
->decryptBuffer
, (LPBYTE
)(sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
- inBuffers
[1].cbBuffer
), inBuffers
[1].cbBuffer
);
1970 sspi
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1974 sspi
->decryptBufferUsed
= 0;
1981 sspi
->contextInitialized
= TRUE
;
1984 * Find out how big the header will be:
1987 scRet
= QueryContextAttributes(&sspi
->context
, SECPKG_ATTR_STREAM_SIZES
, &sspi
->streamSizes
);
1989 if (scRet
!= SEC_E_OK
)
1991 DEBUG_printf(("http_sspi_server: QueryContextAttributes failed: %s", http_sspi_strerror(sspi
, scRet
)));
2001 * 'http_sspi_strerror()' - Return a string for the specified error code.
2004 static const char * /* O - String for error */
2005 http_sspi_strerror(_http_sspi_t
*sspi
, /* I - SSPI data */
2006 DWORD code
) /* I - Error code */
2008 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
, NULL
, code
, 0, sspi
->error
, sizeof(sspi
->error
), NULL
))
2011 * Strip trailing CR + LF...
2014 char *ptr
; /* Pointer into error message */
2016 for (ptr
= sspi
->error
+ strlen(sspi
->error
) - 1; ptr
>= sspi
->error
; ptr
--)
2017 if (*ptr
== '\n' || *ptr
== '\r')
2023 snprintf(sspi
->error
, sizeof(sspi
->error
), "Unknown error %x", code
);
2025 return (sspi
->error
);
2030 * 'http_sspi_verify()' - Verify a certificate.
2033 static DWORD
/* O - Error code (0 == No error) */
2035 PCCERT_CONTEXT cert
, /* I - Server certificate */
2036 const char *common_name
, /* I - Common name */
2037 DWORD dwCertFlags
) /* I - Verification flags */
2039 HTTPSPolicyCallbackData httpsPolicy
; /* HTTPS Policy Struct */
2040 CERT_CHAIN_POLICY_PARA policyPara
; /* Cert chain policy parameters */
2041 CERT_CHAIN_POLICY_STATUS policyStatus
;/* Cert chain policy status */
2042 CERT_CHAIN_PARA chainPara
; /* Used for searching and matching criteria */
2043 PCCERT_CHAIN_CONTEXT chainContext
= NULL
;
2044 /* Certificate chain */
2045 PWSTR commonNameUnicode
= NULL
;
2046 /* Unicode common name */
2047 LPSTR rgszUsages
[] = { szOID_PKIX_KP_SERVER_AUTH
,
2048 szOID_SERVER_GATED_CRYPTO
,
2049 szOID_SGC_NETSCAPE
};
2050 /* How are we using this certificate? */
2051 DWORD cUsages
= sizeof(rgszUsages
) / sizeof(LPSTR
);
2052 /* Number of ites in rgszUsages */
2053 DWORD count
; /* 32 bit count variable */
2054 DWORD status
; /* Return value */
2058 return (SEC_E_WRONG_PRINCIPAL
);
2061 * Convert common name to Unicode.
2064 if (!common_name
|| !*common_name
)
2065 return (SEC_E_WRONG_PRINCIPAL
);
2067 count
= MultiByteToWideChar(CP_ACP
, 0, common_name
, -1, NULL
, 0);
2068 commonNameUnicode
= LocalAlloc(LMEM_FIXED
, count
* sizeof(WCHAR
));
2069 if (!commonNameUnicode
)
2070 return (SEC_E_INSUFFICIENT_MEMORY
);
2072 if (!MultiByteToWideChar(CP_ACP
, 0, common_name
, -1, commonNameUnicode
, count
))
2074 LocalFree(commonNameUnicode
);
2075 return (SEC_E_WRONG_PRINCIPAL
);
2079 * Build certificate chain.
2082 ZeroMemory(&chainPara
, sizeof(chainPara
));
2084 chainPara
.cbSize
= sizeof(chainPara
);
2085 chainPara
.RequestedUsage
.dwType
= USAGE_MATCH_TYPE_OR
;
2086 chainPara
.RequestedUsage
.Usage
.cUsageIdentifier
= cUsages
;
2087 chainPara
.RequestedUsage
.Usage
.rgpszUsageIdentifier
= rgszUsages
;
2089 if (!CertGetCertificateChain(NULL
, cert
, NULL
, cert
->hCertStore
, &chainPara
, 0, NULL
, &chainContext
))
2091 status
= GetLastError();
2092 DEBUG_printf(("CertGetCertificateChain returned: %x", status
));
2094 LocalFree(commonNameUnicode
);
2099 * Validate certificate chain.
2102 ZeroMemory(&httpsPolicy
, sizeof(HTTPSPolicyCallbackData
));
2103 httpsPolicy
.cbStruct
= sizeof(HTTPSPolicyCallbackData
);
2104 httpsPolicy
.dwAuthType
= AUTHTYPE_SERVER
;
2105 httpsPolicy
.fdwChecks
= dwCertFlags
;
2106 httpsPolicy
.pwszServerName
= commonNameUnicode
;
2108 memset(&policyPara
, 0, sizeof(policyPara
));
2109 policyPara
.cbSize
= sizeof(policyPara
);
2110 policyPara
.pvExtraPolicyPara
= &httpsPolicy
;
2112 memset(&policyStatus
, 0, sizeof(policyStatus
));
2113 policyStatus
.cbSize
= sizeof(policyStatus
);
2115 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL
, chainContext
, &policyPara
, &policyStatus
))
2117 status
= GetLastError();
2118 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %x", status
));
2120 else if (policyStatus
.dwError
)
2121 status
= policyStatus
.dwError
;
2126 CertFreeCertificateChain(chainContext
);
2128 if (commonNameUnicode
)
2129 LocalFree(commonNameUnicode
);