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_DATE_INVALID
42 # define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */
43 #endif /* !SECURITY_FLAG_IGNORE_CERT_DATE_INVALID */
49 static _http_sspi_t
*http_sspi_alloc(void);
50 static int http_sspi_client(http_t
*http
, const char *hostname
);
51 static PCCERT_CONTEXT
http_sspi_create_credential(http_credential_t
*cred
);
52 static BOOL
http_sspi_find_credentials(http_t
*http
, const LPWSTR containerName
, const char *common_name
);
53 static void http_sspi_free(_http_sspi_t
*sspi
);
54 static BOOL
http_sspi_make_credentials(_http_sspi_t
*sspi
, const LPWSTR containerName
, const char *common_name
, _http_mode_t mode
, int years
);
55 static int http_sspi_server(http_t
*http
, const char *hostname
);
56 static void http_sspi_set_allows_any_root(_http_sspi_t
*sspi
, BOOL allow
);
57 static void http_sspi_set_allows_expired_certs(_http_sspi_t
*sspi
, BOOL allow
);
58 static const char *http_sspi_strerror(_http_sspi_t
*sspi
, DWORD code
);
59 static DWORD
http_sspi_verify(PCCERT_CONTEXT cert
, const char *common_name
, DWORD dwCertFlags
);
63 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
68 int /* O - 1 on success, 0 on failure */
69 cupsMakeServerCredentials(
70 const char *path
, /* I - Keychain path or @code NULL@ for default */
71 const char *common_name
, /* I - Common name */
72 int num_alt_names
, /* I - Number of subject alternate names */
73 const char **alt_names
, /* I - Subject Alternate Names */
74 time_t expiration_date
) /* I - Expiration date */
76 _http_sspi_t
*sspi
; /* SSPI data */
77 int ret
; /* Return value */
80 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
));
86 sspi
= http_sspi_alloc();
87 ret
= http_sspi_make_credentials(sspi
, L
"ServerContainer", common_name
, _HTTP_MODE_SERVER
, (int)((expiration_date
- time(NULL
) + 86399) / 86400 / 365));
96 * 'cupsSetServerCredentials()' - Set the default server credentials.
98 * Note: The server credentials are used by all threads in the running process.
99 * This function is threadsafe.
104 int /* O - 1 on success, 0 on failure */
105 cupsSetServerCredentials(
106 const char *path
, /* I - Keychain path or @code NULL@ for default */
107 const char *common_name
, /* I - Default common name for server */
108 int auto_create
) /* I - 1 = automatically create self-signed certificates */
110 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path
, common_name
, auto_create
));
121 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
122 * an encrypted connection.
124 * @since CUPS 1.5/OS X 10.7@
127 int /* O - Status of call (0 = success) */
129 http_t
*http
, /* I - Connection to server */
130 cups_array_t
**credentials
) /* O - Array of credentials */
132 DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http
, credentials
));
144 * '_httpCreateCredentials()' - Create credentials in the internal format.
147 http_tls_credentials_t
/* O - Internal credentials */
148 _httpCreateCredentials(
149 cups_array_t
*credentials
) /* I - Array of credentials */
151 return (http_sspi_create_credential((http_credential_t
*)cupsArrayFirst(credentials
)));
156 * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
161 int /* O - 1 if valid, 0 otherwise */
162 httpCredentialsAreValidForName(
163 cups_array_t
*credentials
, /* I - Credentials */
164 const char *common_name
) /* I - Name to check */
166 int valid
= 1; /* Valid name? */
167 PCCERT_CONTEXT cert
= http_sspi_create_credential((http_credential_t
*)cupsArrayFirst(credentials
));
169 char cert_name
[1024]; /* Name from certificate */
174 if (!CertNameToStr(X509_ASN_ENCODING
, cert
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, cert_name
, sizeof(cert_name
)))
175 strlcpy(cert_name
, "unknown", sizeof(cert_name
));
177 CertFreeCertificateContext(cert
);
180 strlcpy(cert_name
, "unknown", sizeof(cert_name
));
183 * Compare the common names...
186 if (_cups_strcasecmp(common_name
, cert_name
))
189 * Not an exact match for the common name, check for wildcard certs...
192 const char *domain
= strchr(common_name
, '.');
193 /* Domain in common name */
195 if (strncmp(cert_name
, "*.", 2) || !domain
|| _cups_strcasecmp(domain
, cert_name
+ 1))
198 * Not a wildcard match.
201 /* TODO: Check subject alternate names */
211 * 'httpCredentialsGetTrust()' - Return the trust of credentials.
216 http_trust_t
/* O - Level of trust */
217 httpCredentialsGetTrust(
218 cups_array_t
*credentials
, /* I - Credentials */
219 const char *common_name
) /* I - Common name for trust lookup */
221 http_trust_t trust
= HTTP_TRUST_OK
; /* Level of trust */
222 PCCERT_CONTEXT cert
= NULL
; /* Certificate to validate */
223 DWORD certFlags
= 0; /* Cert verification flags */
224 _cups_globals_t
*cg
= _cupsGlobals(); /* Per-thread global data */
228 return (HTTP_TRUST_UNKNOWN
);
230 cert
= http_sspi_create_credential((http_credential_t
*)cupsArrayFirst(credentials
));
232 return (HTTP_TRUST_UNKNOWN
);
235 certFlags
|= SECURITY_FLAG_IGNORE_UNKNOWN_CA
;
237 if (cg
->expired_certs
)
238 certFlags
|= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
240 if (!cg
->validate_certs
)
241 certFlags
|= SECURITY_FLAG_IGNORE_CERT_CN_INVALID
;
243 if (http_sspi_verify(cert
, common_name
, certFlags
) != SEC_E_OK
)
244 trust
= HTTP_TRUST_INVALID
;
246 CertFreeCertificateContext(cert
);
253 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
258 time_t /* O - Expiration date of credentials */
259 httpCredentialsGetExpiration(
260 cups_array_t
*credentials
) /* I - Credentials */
262 time_t expiration_date
= 0; /* Expiration data of credentials */
263 PCCERT_CONTEXT cert
= http_sspi_create_credential((http_credential_t
*)cupsArrayFirst(credentials
));
268 SYSTEMTIME systime
; /* System time */
269 struct tm tm
; /* UNIX date/time */
271 FileTimeToSystemTime(cert
->pCertInfo
->NotAfter
, &systime
);
273 tm
.tm_year
= systime
.wYear
- 1900;
274 tm
.tm_mon
= systime
.wMonth
- 1;
275 tm
.tm_mday
= systime
.wDay
;
276 tm
.tm_hour
= systime
.wHour
;
277 tm
.tm_min
= systime
.wMinute
;
278 tm
.tm_sec
= systime
.wSecond
;
280 expiration_date
= mktime(&tm
);
282 CertFreeCertificateContext(cert
);
285 return (expiration_date
);
290 * 'httpCredentialsString()' - Return a string representing the credentials.
295 size_t /* O - Total size of credentials string */
296 httpCredentialsString(
297 cups_array_t
*credentials
, /* I - Credentials */
298 char *buffer
, /* I - Buffer or @code NULL@ */
299 size_t bufsize
) /* I - Size of buffer */
301 http_credential_t
*first
= (http_credential_t
*)cupsArrayFirst(credentials
);
302 /* First certificate */
303 PCCERT_CONTEXT cert
; /* Certificate */
306 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT
")", credentials
, buffer
, CUPS_LLCAST bufsize
));
311 if (buffer
&& bufsize
> 0)
314 cert
= http_sspi_create_credential(first
);
318 char cert_name
[256]; /* Common name */
319 SYSTEMTIME systime
; /* System time */
320 struct tm tm
; /* UNIX date/time */
321 time_t expiration
; /* Expiration date of cert */
322 _cups_md5_state_t md5_state
; /* MD5 state */
323 unsigned char md5_digest
[16]; /* MD5 result */
325 FileTimeToSystemTime(cert
->pCertInfo
->NotAfter
, &systime
);
327 tm
.tm_year
= systime
.wYear
- 1900;
328 tm
.tm_mon
= systime
.wMonth
- 1;
329 tm
.tm_mday
= systime
.wDay
;
330 tm
.tm_hour
= systime
.wHour
;
331 tm
.tm_min
= systime
.wMinute
;
332 tm
.tm_sec
= systime
.wSecond
;
334 expiration
= mktime(&tm
);
336 if (!CertNameToStr(X509_ASN_ENCODING
, cert
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, cert_name
, sizeof(cert_name
)))
337 strlcpy(cert_name
, "unknown", sizeof(cert_name
));
339 _cupsMD5Init(&md5_state
);
340 _cupsMD5Append(&md5_state
, first
->data
, (int)first
->datalen
);
341 _cupsMD5Finish(&md5_state
, md5_digest
);
343 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]);
345 CertFreeCertificateContext(cert
);
348 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer
));
350 return (strlen(buffer
));
355 * '_httpFreeCredentials()' - Free internal credentials.
359 _httpFreeCredentials(
360 http_tls_credentials_t credentials
) /* I - Internal credentials */
365 CertFreeCertificateContext(credentials
);
370 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
375 int /* O - 0 on success, -1 on error */
377 const char *path
, /* I - Keychain path or @code NULL@ for default */
378 cups_array_t
**credentials
, /* IO - Credentials */
379 const char *common_name
) /* I - Common name for credentials */
381 DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path
, credentials
, common_name
));
389 DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials
? 0 : -1));
391 return (*credentials
? 0 : -1);
396 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
401 int /* O - -1 on error, 0 on success */
403 const char *path
, /* I - Keychain path or @code NULL@ for default */
404 cups_array_t
*credentials
, /* I - Credentials */
405 const char *common_name
) /* I - Common name for credentials */
407 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path
, credentials
, common_name
));
413 DEBUG_printf(("1httpSaveCredentials: Returning %d.", -1));
419 * '_httpTLSInitialize()' - Initialize the TLS stack.
423 _httpTLSInitialize(void)
432 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
435 size_t /* O - Bytes available */
436 _httpTLSPending(http_t
*http
) /* I - HTTP connection */
439 return (http
->tls
->readBufferUsed
);
446 * '_httpTLSRead()' - Read from a SSL/TLS connection.
449 int /* O - Bytes read */
450 _httpTLSRead(http_t
*http
, /* I - HTTP connection */
451 char *buf
, /* I - Buffer to store data */
452 int len
) /* I - Length of buffer */
454 int i
; /* Looping var */
455 _http_sspi_t
*sspi
= http
->tls
; /* SSPI data */
456 SecBufferDesc message
; /* Array of SecBuffer struct */
457 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
458 int num
= 0; /* Return value */
459 PSecBuffer pDataBuffer
; /* Data buffer */
460 PSecBuffer pExtraBuffer
; /* Excess data buffer */
461 SECURITY_STATUS scRet
; /* SSPI status */
464 DEBUG_printf(("4_httpTLSRead(http=%p, buf=%p, len=%d)", http
, buf
, len
));
467 * If there are bytes that have already been decrypted and have not yet been
468 * read, return those...
471 if (sspi
->readBufferUsed
> 0)
473 int bytesToCopy
= min(sspi
->readBufferUsed
, len
);
474 /* Number of bytes to copy */
476 memcpy(buf
, sspi
->readBuffer
, bytesToCopy
);
477 sspi
->readBufferUsed
-= bytesToCopy
;
479 if (sspi
->readBufferUsed
> 0)
480 memmove(sspi
->readBuffer
, sspi
->readBuffer
+ bytesToCopy
, sspi
->readBufferUsed
);
482 DEBUG_printf(("5_httpTLSRead: Returning %d bytes previously decrypted.", bytesToCopy
));
484 return (bytesToCopy
);
488 * Initialize security buffer structs
491 message
.ulVersion
= SECBUFFER_VERSION
;
492 message
.cBuffers
= 4;
493 message
.pBuffers
= buffers
;
498 * If there is not enough space in the buffer, then increase its size...
501 if (sspi
->decryptBufferLength
<= sspi
->decryptBufferUsed
)
503 BYTE
*temp
; /* New buffer */
505 if (sspi
->decryptBufferLength
>= 262144)
507 WSASetLastError(E_OUTOFMEMORY
);
508 DEBUG_puts("_httpTLSRead: Decryption buffer too large (>256k)");
512 if ((temp
= realloc(sspi
->decryptBuffer
, sspi
->decryptBufferLength
+ 4096)) == NULL
)
514 DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte decryption buffer.", sspi
->decryptBufferLength
+ 4096));
515 WSASetLastError(E_OUTOFMEMORY
);
519 sspi
->decryptBufferLength
+= 4096;
520 sspi
->decryptBuffer
= temp
;
522 DEBUG_printf(("_httpTLSRead: Resized decryption buffer to %d bytes.", sspi
->decryptBufferLength
));
525 buffers
[0].pvBuffer
= sspi
->decryptBuffer
;
526 buffers
[0].cbBuffer
= (unsigned long)sspi
->decryptBufferUsed
;
527 buffers
[0].BufferType
= SECBUFFER_DATA
;
528 buffers
[1].BufferType
= SECBUFFER_EMPTY
;
529 buffers
[2].BufferType
= SECBUFFER_EMPTY
;
530 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
532 DEBUG_printf(("5_httpTLSRead: decryptBufferUsed=%d", sspi
->decryptBufferUsed
));
534 scRet
= DecryptMessage(&sspi
->context
, &message
, 0, NULL
);
536 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
538 num
= recv(http
->fd
, sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
, (int)(sspi
->decryptBufferLength
- sspi
->decryptBufferUsed
), 0);
541 DEBUG_printf(("5_httpTLSRead: recv failed: %d", WSAGetLastError()));
546 DEBUG_puts("5_httpTLSRead: Server disconnected.");
550 DEBUG_printf(("5_httpTLSRead: Read %d bytes into decryption buffer.", num
));
552 sspi
->decryptBufferUsed
+= num
;
555 while (scRet
== SEC_E_INCOMPLETE_MESSAGE
);
557 if (scRet
== SEC_I_CONTEXT_EXPIRED
)
559 DEBUG_puts("5_httpTLSRead: Context expired.");
560 WSASetLastError(WSAECONNRESET
);
563 else if (scRet
!= SEC_E_OK
)
565 DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %s", http_sspi_strerror(sspi
, scRet
)));
566 WSASetLastError(WSASYSCALLFAILURE
);
571 * The decryption worked. Now, locate data buffer.
577 for (i
= 1; i
< 4; i
++)
579 if (buffers
[i
].BufferType
== SECBUFFER_DATA
)
580 pDataBuffer
= &buffers
[i
];
581 else if (!pExtraBuffer
&& (buffers
[i
].BufferType
== SECBUFFER_EXTRA
))
582 pExtraBuffer
= &buffers
[i
];
586 * If a data buffer is found, then copy the decrypted bytes to the passed-in
592 int bytesToCopy
= min((int)pDataBuffer
->cbBuffer
, len
);
593 /* Number of bytes to copy into buf */
594 int bytesToSave
= pDataBuffer
->cbBuffer
- bytesToCopy
;
595 /* Number of bytes to save in our read buffer */
598 memcpy(buf
, pDataBuffer
->pvBuffer
, bytesToCopy
);
601 * If there are more decrypted bytes than can be copied to the passed in
602 * buffer, then save them...
607 if ((sspi
->readBufferLength
- sspi
->readBufferUsed
) < bytesToSave
)
609 BYTE
*temp
; /* New buffer pointer */
611 if ((temp
= realloc(sspi
->readBuffer
, sspi
->readBufferUsed
+ bytesToSave
)) == NULL
)
613 DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", sspi
->readBufferUsed
+ bytesToSave
));
614 WSASetLastError(E_OUTOFMEMORY
);
618 sspi
->readBufferLength
= sspi
->readBufferUsed
+ bytesToSave
;
619 sspi
->readBuffer
= temp
;
622 memcpy(((BYTE
*)sspi
->readBuffer
) + sspi
->readBufferUsed
, ((BYTE
*)pDataBuffer
->pvBuffer
) + bytesToCopy
, bytesToSave
);
624 sspi
->readBufferUsed
+= bytesToSave
;
631 DEBUG_puts("_httpTLSRead: Unable to find data buffer.");
632 WSASetLastError(WSASYSCALLFAILURE
);
637 * If the decryption process left extra bytes, then save those back in
638 * decryptBuffer. They will be processed the next time through the loop.
643 memmove(sspi
->decryptBuffer
, pExtraBuffer
->pvBuffer
, pExtraBuffer
->cbBuffer
);
644 sspi
->decryptBufferUsed
= pExtraBuffer
->cbBuffer
;
648 sspi
->decryptBufferUsed
= 0;
656 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
659 int /* O - 0 on success, -1 on failure */
660 _httpTLSStart(http_t
*http
) /* I - HTTP connection */
662 char hostname
[256], /* Hostname */
663 *hostptr
; /* Pointer into hostname */
666 DEBUG_printf(("7_httpTLSStart(http=%p)", http
));
668 if ((http
->tls
= http_sspi_alloc()) == NULL
)
671 if (http
->mode
== _HTTP_MODE_CLIENT
)
674 * Client: determine hostname...
677 if (httpAddrLocalhost(http
->hostaddr
))
679 strlcpy(hostname
, "localhost", sizeof(hostname
));
684 * Otherwise make sure the hostname we have does not end in a trailing dot.
687 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
688 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
693 return (http_sspi_client(http
, hostname
));
698 * Server: determine hostname to use...
701 if (http
->fields
[HTTP_FIELD_HOST
][0])
704 * Use hostname for TLS upgrade...
707 strlcpy(hostname
, http
->fields
[HTTP_FIELD_HOST
], sizeof(hostname
));
712 * Resolve hostname from connection address...
715 http_addr_t addr
; /* Connection address */
716 socklen_t addrlen
; /* Length of address */
718 addrlen
= sizeof(addr
);
719 if (getsockname(http
->fd
, (struct sockaddr
*)&addr
, &addrlen
))
721 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno
)));
724 else if (httpAddrLocalhost(&addr
))
728 httpAddrLookup(&addr
, hostname
, sizeof(hostname
));
729 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname
));
733 return (http_sspi_server(http
, hostname
));
739 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
743 _httpTLSStop(http_t
*http
) /* I - HTTP connection */
745 _http_sspi_t
*sspi
= http
->tls
; /* SSPI data */
748 if (sspi
->contextInitialized
&& http
->fd
>= 0)
750 SecBufferDesc message
; /* Array of SecBuffer struct */
751 SecBuffer buffers
[1] = { 0 };
752 /* Security package buffer */
753 DWORD dwType
; /* Type */
754 DWORD status
; /* Status */
757 * Notify schannel that we are about to close the connection.
760 dwType
= SCHANNEL_SHUTDOWN
;
762 buffers
[0].pvBuffer
= &dwType
;
763 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
764 buffers
[0].cbBuffer
= sizeof(dwType
);
766 message
.cBuffers
= 1;
767 message
.pBuffers
= buffers
;
768 message
.ulVersion
= SECBUFFER_VERSION
;
770 status
= ApplyControlToken(&sspi
->context
, &message
);
772 if (SUCCEEDED(status
))
774 PBYTE pbMessage
; /* Message buffer */
775 DWORD cbMessage
; /* Message buffer count */
776 DWORD cbData
; /* Data count */
777 DWORD dwSSPIFlags
; /* SSL attributes we requested */
778 DWORD dwSSPIOutFlags
; /* SSL attributes we received */
779 TimeStamp tsExpiry
; /* Time stamp */
781 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
782 ASC_REQ_REPLAY_DETECT
|
783 ASC_REQ_CONFIDENTIALITY
|
784 ASC_REQ_EXTENDED_ERROR
|
785 ASC_REQ_ALLOCATE_MEMORY
|
788 buffers
[0].pvBuffer
= NULL
;
789 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
790 buffers
[0].cbBuffer
= 0;
792 message
.cBuffers
= 1;
793 message
.pBuffers
= buffers
;
794 message
.ulVersion
= SECBUFFER_VERSION
;
796 status
= AcceptSecurityContext(&sspi
->creds
, &sspi
->context
, NULL
,
797 dwSSPIFlags
, SECURITY_NATIVE_DREP
, NULL
,
798 &message
, &dwSSPIOutFlags
, &tsExpiry
);
800 if (SUCCEEDED(status
))
802 pbMessage
= buffers
[0].pvBuffer
;
803 cbMessage
= buffers
[0].cbBuffer
;
806 * Send the close notify message to the client.
809 if (pbMessage
&& cbMessage
)
811 cbData
= send(http
->fd
, pbMessage
, cbMessage
, 0);
812 if ((cbData
== SOCKET_ERROR
) || (cbData
== 0))
814 status
= WSAGetLastError();
815 DEBUG_printf(("_httpTLSStop: sending close notify failed: %d", status
));
819 FreeContextBuffer(pbMessage
);
825 DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi
, status
)));
830 DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %s", http_sspi_strerror(sspi
, status
)));
834 http_sspi_free(sspi
);
841 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
844 int /* O - Bytes written */
845 _httpTLSWrite(http_t
*http
, /* I - HTTP connection */
846 const char *buf
, /* I - Buffer holding data */
847 int len
) /* I - Length of buffer */
849 _http_sspi_t
*sspi
= http
->tls
; /* SSPI data */
850 SecBufferDesc message
; /* Array of SecBuffer struct */
851 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
852 int bufferLen
; /* Buffer length */
853 int bytesLeft
; /* Bytes left to write */
854 const char *bufptr
; /* Pointer into buffer */
855 int num
= 0; /* Return value */
858 bufferLen
= sspi
->streamSizes
.cbMaximumMessage
+ sspi
->streamSizes
.cbHeader
+ sspi
->streamSizes
.cbTrailer
;
860 if (bufferLen
> sspi
->writeBufferLength
)
862 BYTE
*temp
; /* New buffer pointer */
864 if ((temp
= (BYTE
*)realloc(sspi
->writeBuffer
, bufferLen
)) == NULL
)
866 DEBUG_printf(("_httpTLSWrite: Unable to allocate buffer of %d bytes.", bufferLen
));
867 WSASetLastError(E_OUTOFMEMORY
);
871 sspi
->writeBuffer
= temp
;
872 sspi
->writeBufferLength
= bufferLen
;
880 int chunk
= min((int)sspi
->streamSizes
.cbMaximumMessage
, bytesLeft
);
881 /* Size of data to write */
882 SECURITY_STATUS scRet
; /* SSPI status */
885 * Copy user data into the buffer, starting just past the header...
888 memcpy(sspi
->writeBuffer
+ sspi
->streamSizes
.cbHeader
, bufptr
, chunk
);
891 * Setup the SSPI buffers
894 message
.ulVersion
= SECBUFFER_VERSION
;
895 message
.cBuffers
= 4;
896 message
.pBuffers
= buffers
;
898 buffers
[0].pvBuffer
= sspi
->writeBuffer
;
899 buffers
[0].cbBuffer
= sspi
->streamSizes
.cbHeader
;
900 buffers
[0].BufferType
= SECBUFFER_STREAM_HEADER
;
901 buffers
[1].pvBuffer
= sspi
->writeBuffer
+ sspi
->streamSizes
.cbHeader
;
902 buffers
[1].cbBuffer
= (unsigned long) chunk
;
903 buffers
[1].BufferType
= SECBUFFER_DATA
;
904 buffers
[2].pvBuffer
= sspi
->writeBuffer
+ sspi
->streamSizes
.cbHeader
+ chunk
;
905 buffers
[2].cbBuffer
= sspi
->streamSizes
.cbTrailer
;
906 buffers
[2].BufferType
= SECBUFFER_STREAM_TRAILER
;
907 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
913 scRet
= EncryptMessage(&sspi
->context
, 0, &message
, 0);
917 DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %s", http_sspi_strerror(sspi
, scRet
)));
918 WSASetLastError(WSASYSCALLFAILURE
);
923 * Send the data. Remember the size of the total data to send is the size
924 * of the header, the size of the data the caller passed in and the size
928 num
= send(http
->fd
, sspi
->writeBuffer
, buffers
[0].cbBuffer
+ buffers
[1].cbBuffer
+ buffers
[2].cbBuffer
, 0);
932 DEBUG_printf(("_httpTLSWrite: send failed: %ld", WSAGetLastError()));
946 * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
949 static int /* O - 0 on success, -1 on failure */
950 http_setup_ssl(http_t
*http
) /* I - Connection to server */
952 char hostname
[256], /* Hostname */
953 *hostptr
; /* Pointer into hostname */
955 TCHAR username
[256]; /* Username returned from GetUserName() */
956 TCHAR commonName
[256];/* Common name for certificate */
957 DWORD dwSize
; /* 32 bit size */
960 DEBUG_printf(("7http_setup_ssl(http=%p)", http
));
963 * Get the hostname to use for SSL...
966 if (httpAddrLocalhost(http
->hostaddr
))
968 strlcpy(hostname
, "localhost", sizeof(hostname
));
973 * Otherwise make sure the hostname we have does not end in a trailing dot.
976 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
977 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
982 http
->tls
= http_sspi_alloc();
986 _cupsSetHTTPError(HTTP_STATUS_ERROR
);
990 dwSize
= sizeof(username
) / sizeof(TCHAR
);
991 GetUserName(username
, &dwSize
);
992 _sntprintf_s(commonName
, sizeof(commonName
) / sizeof(TCHAR
),
993 sizeof(commonName
) / sizeof(TCHAR
), TEXT("CN=%s"), username
);
995 if (!_sspiGetCredentials(http
->tls
, L
"ClientContainer",
998 _sspiFree(http
->tls
);
1002 http
->status
= HTTP_STATUS_ERROR
;
1004 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
1005 _("Unable to establish a secure connection to host."), 1);
1010 _sspiSetAllowsAnyRoot(http
->tls
, TRUE
);
1011 _sspiSetAllowsExpiredCerts(http
->tls
, TRUE
);
1013 if (!_sspiConnect(http
->tls
, hostname
))
1015 _sspiFree(http
->tls
);
1019 http
->status
= HTTP_STATUS_ERROR
;
1021 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
1022 _("Unable to establish a secure connection to host."), 1);
1033 * 'http_sspi_alloc()' - Allocate SSPI object.
1036 static _http_sspi_t
* /* O - New SSPI/SSL object */
1037 http_sspi_alloc(void)
1039 return ((_http_sspi_t
*)calloc(sizeof(_http_sspi_t
), 1));
1044 * 'http_sspi_client()' - Negotiate a TLS connection as a client.
1047 static int /* O - 0 on success, -1 on failure */
1048 http_sspi_client(http_t
*http
, /* I - Client connection */
1049 const char *hostname
) /* I - Server hostname */
1051 _http_sspi_t
*sspi
= http
->tls
; /* SSPI data */
1052 DWORD dwSize
; /* Size for buffer */
1053 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1054 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1055 TimeStamp tsExpiry
; /* Time stamp */
1056 SECURITY_STATUS scRet
; /* Status */
1057 int cbData
; /* Data count */
1058 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1059 SecBuffer inBuffers
[2]; /* Security package buffer */
1060 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1061 SecBuffer outBuffers
[1]; /* Security package buffer */
1062 int ret
= 0; /* Return value */
1063 char username
[1024], /* Current username */
1064 common_name
[1024]; /* CN=username */
1067 DEBUG_printf(("http_sspi_client(http=%p, hostname=\"%s\")", http
, hostname
));
1069 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
|
1070 ISC_REQ_REPLAY_DETECT
|
1071 ISC_REQ_CONFIDENTIALITY
|
1072 ISC_RET_EXTENDED_ERROR
|
1073 ISC_REQ_ALLOCATE_MEMORY
|
1077 * Lookup the client certificate...
1080 dwSize
= sizeof(username
);
1081 GetUserName(username
, &dwSize
);
1082 snprintf(common_name
, sizeof(common_name
), "CN=%s", username
);
1084 if (!http_sspi_find_credentials(http
, L
"ClientContainer", common_name
))
1085 if (!http_sspi_make_credentials(http
->tls
, L
"ClientContainer", common_name
, _HTTP_MODE_CLIENT
, 10))
1087 DEBUG_puts("http_sspi_client: Unable to get client credentials.");
1092 * Initiate a ClientHello message and generate a token.
1095 outBuffers
[0].pvBuffer
= NULL
;
1096 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1097 outBuffers
[0].cbBuffer
= 0;
1099 outBuffer
.cBuffers
= 1;
1100 outBuffer
.pBuffers
= outBuffers
;
1101 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1103 scRet
= InitializeSecurityContext(&sspi
->creds
, NULL
, TEXT(""), dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, NULL
, 0, &sspi
->context
, &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1105 if (scRet
!= SEC_I_CONTINUE_NEEDED
)
1107 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(1) failed: %s", http_sspi_strerror(sspi
, scRet
)));
1112 * Send response to server if there is one.
1115 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1117 if ((cbData
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0)) <= 0)
1119 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1120 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1121 DeleteSecurityContext(&sspi
->context
);
1125 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData
));
1127 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1128 outBuffers
[0].pvBuffer
= NULL
;
1131 dwSSPIFlags
= ISC_REQ_MANUAL_CRED_VALIDATION
|
1132 ISC_REQ_SEQUENCE_DETECT
|
1133 ISC_REQ_REPLAY_DETECT
|
1134 ISC_REQ_CONFIDENTIALITY
|
1135 ISC_RET_EXTENDED_ERROR
|
1136 ISC_REQ_ALLOCATE_MEMORY
|
1139 sspi
->decryptBufferUsed
= 0;
1142 * Loop until the handshake is finished or an error occurs.
1145 scRet
= SEC_I_CONTINUE_NEEDED
;
1147 while(scRet
== SEC_I_CONTINUE_NEEDED
||
1148 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1149 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1151 if (sspi
->decryptBufferUsed
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1153 if (sspi
->decryptBufferLength
<= sspi
->decryptBufferUsed
)
1155 BYTE
*temp
; /* New buffer */
1157 if (sspi
->decryptBufferLength
>= 262144)
1159 WSASetLastError(E_OUTOFMEMORY
);
1160 DEBUG_puts("http_sspi_client: Decryption buffer too large (>256k)");
1164 if ((temp
= realloc(sspi
->decryptBuffer
, sspi
->decryptBufferLength
+ 4096)) == NULL
)
1166 DEBUG_printf(("http_sspi_client: Unable to allocate %d byte buffer.", sspi
->decryptBufferLength
+ 4096));
1167 WSASetLastError(E_OUTOFMEMORY
);
1171 sspi
->decryptBufferLength
+= 4096;
1172 sspi
->decryptBuffer
= temp
;
1175 cbData
= recv(http
->fd
, sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
, (int)(sspi
->decryptBufferLength
- sspi
->decryptBufferUsed
), 0);
1179 DEBUG_printf(("http_sspi_client: recv failed: %d", WSAGetLastError()));
1182 else if (cbData
== 0)
1184 DEBUG_printf(("http_sspi_client: Server unexpectedly disconnected."));
1188 DEBUG_printf(("http_sspi_client: %d bytes of handshake data received", cbData
));
1190 sspi
->decryptBufferUsed
+= cbData
;
1194 * Set up the input buffers. Buffer 0 is used to pass in data received from
1195 * the server. Schannel will consume some or all of this. Leftover data
1196 * (if any) will be placed in buffer 1 and given a buffer type of
1200 inBuffers
[0].pvBuffer
= sspi
->decryptBuffer
;
1201 inBuffers
[0].cbBuffer
= (unsigned long)sspi
->decryptBufferUsed
;
1202 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1204 inBuffers
[1].pvBuffer
= NULL
;
1205 inBuffers
[1].cbBuffer
= 0;
1206 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1208 inBuffer
.cBuffers
= 2;
1209 inBuffer
.pBuffers
= inBuffers
;
1210 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1213 * Set up the output buffers. These are initialized to NULL so as to make it
1214 * less likely we'll attempt to free random garbage later.
1217 outBuffers
[0].pvBuffer
= NULL
;
1218 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1219 outBuffers
[0].cbBuffer
= 0;
1221 outBuffer
.cBuffers
= 1;
1222 outBuffer
.pBuffers
= outBuffers
;
1223 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1226 * Call InitializeSecurityContext.
1229 scRet
= InitializeSecurityContext(&sspi
->creds
, &sspi
->context
, NULL
, dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, &inBuffer
, 0, NULL
, &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1232 * If InitializeSecurityContext was successful (or if the error was one of
1233 * the special extended ones), send the contents of the output buffer to the
1237 if (scRet
== SEC_E_OK
||
1238 scRet
== SEC_I_CONTINUE_NEEDED
||
1239 FAILED(scRet
) && (dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
))
1241 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1243 cbData
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1247 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1248 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1249 DeleteSecurityContext(&sspi
->context
);
1253 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData
));
1256 * Free output buffer.
1259 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1260 outBuffers
[0].pvBuffer
= NULL
;
1265 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we
1266 * need to read more data from the server and try again.
1269 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1273 * If InitializeSecurityContext returned SEC_E_OK, then the handshake
1274 * completed successfully.
1277 if (scRet
== SEC_E_OK
)
1280 * If the "extra" buffer contains data, this is encrypted application
1281 * protocol layer stuff. It needs to be saved. The application layer will
1282 * later decrypt it with DecryptMessage.
1285 DEBUG_puts("http_sspi_client: Handshake was successful.");
1287 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1289 memmove(sspi
->decryptBuffer
, sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
- inBuffers
[1].cbBuffer
, inBuffers
[1].cbBuffer
);
1291 sspi
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1293 DEBUG_printf(("http_sspi_client: %d bytes of app data was bundled with handshake data", sspi
->decryptBufferUsed
));
1296 sspi
->decryptBufferUsed
= 0;
1306 * Check for fatal error.
1311 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(2) failed: %s", http_sspi_strerror(sspi
, scRet
)));
1317 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1318 * then the server just requested client authentication.
1321 if (scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1327 DEBUG_printf(("http_sspi_client: server requested client credentials."));
1333 * Copy any leftover data from the "extra" buffer, and go around again.
1336 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1338 memmove(sspi
->decryptBuffer
, sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
- inBuffers
[1].cbBuffer
, inBuffers
[1].cbBuffer
);
1340 sspi
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1344 sspi
->decryptBufferUsed
= 0;
1351 * Success! Get the server cert
1354 sspi
->contextInitialized
= TRUE
;
1356 scRet
= QueryContextAttributes(&sspi
->context
, SECPKG_ATTR_REMOTE_CERT_CONTEXT
, (VOID
*)&(sspi
->remoteCert
));
1358 if (scRet
!= SEC_E_OK
)
1360 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %s", http_sspi_strerror(sspi
, scRet
)));
1365 * Find out how big the header/trailer will be:
1368 scRet
= QueryContextAttributes(&sspi
->context
, SECPKG_ATTR_STREAM_SIZES
, &sspi
->streamSizes
);
1370 if (scRet
!= SEC_E_OK
)
1372 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %s", http_sspi_strerror(sspi
, scRet
)));
1382 * 'http_sspi_create_credential()' - Create an SSPI certificate context.
1385 static PCCERT_CONTEXT
/* O - Certificate context */
1386 http_sspi_create_credential(
1387 http_credential_t
*cred
) /* I - Credential */
1390 return (CertCreateCertificateContext(X509_ASN_ENCODING
, cred
->data
, cred
->datalen
));
1397 * 'http_sspi_find_credentials()' - Retrieve a TLS certificate from the system store.
1400 static BOOL
/* O - 1 on success, 0 on failure */
1401 http_sspi_find_credentials(
1402 http_t
*http
, /* I - HTTP connection */
1403 const LPWSTR container
, /* I - Cert container name */
1404 const char *common_name
) /* I - Common name of certificate */
1406 _http_sspi_t
*sspi
= http
->tls
; /* SSPI data */
1407 HCERTSTORE store
= NULL
; /* Certificate store */
1408 PCCERT_CONTEXT storedContext
= NULL
; /* Context created from the store */
1409 DWORD dwSize
= 0; /* 32 bit size */
1410 PBYTE p
= NULL
; /* Temporary storage */
1411 HCRYPTPROV hProv
= (HCRYPTPROV
)NULL
;
1412 /* Handle to a CSP */
1413 CERT_NAME_BLOB sib
; /* Arbitrary array of bytes */
1414 SCHANNEL_CRED SchannelCred
; /* Schannel credential data */
1415 TimeStamp tsExpiry
; /* Time stamp */
1416 SECURITY_STATUS Status
; /* Status */
1417 BOOL ok
= TRUE
; /* Return value */
1420 if (!CryptAcquireContextW(&hProv
, (LPWSTR
)container
, MS_DEF_PROV_W
, PROV_RSA_FULL
, CRYPT_NEWKEYSET
| CRYPT_MACHINE_KEYSET
))
1422 if (GetLastError() == NTE_EXISTS
)
1424 if (!CryptAcquireContextW(&hProv
, (LPWSTR
)container
, MS_DEF_PROV_W
, PROV_RSA_FULL
, CRYPT_MACHINE_KEYSET
))
1426 DEBUG_printf(("http_sspi_find_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1433 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");
1437 DEBUG_printf(("http_sspi_find_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1444 if (!CertStrToName(X509_ASN_ENCODING
, common_name
, CERT_OID_NAME_STR
, NULL
, NULL
, &dwSize
, NULL
))
1446 DEBUG_printf(("http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1451 p
= (PBYTE
)malloc(dwSize
);
1455 DEBUG_printf(("http_sspi_find_credentials: malloc failed for %d bytes.", dwSize
));
1460 if (!CertStrToName(X509_ASN_ENCODING
, common_name
, CERT_OID_NAME_STR
, NULL
, p
, &dwSize
, NULL
))
1462 DEBUG_printf(("http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1467 sib
.cbData
= dwSize
;
1470 storedContext
= CertFindCertificateInStore(store
, X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
, 0, CERT_FIND_SUBJECT_NAME
, &sib
, NULL
);
1474 DEBUG_printf(("http_sspi_find_credentials: Unable to find credentials for \"%s\".", common_name
));
1479 ZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
1481 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
1482 SchannelCred
.cCreds
= 1;
1483 SchannelCred
.paCred
= &storedContext
;
1486 * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
1489 if (http
->mode
== _HTTP_MODE_SERVER
)
1490 SchannelCred
.grbitEnabledProtocols
= SP_PROT_SSL3TLS1
;
1493 * Create an SSPI credential.
1496 Status
= AcquireCredentialsHandle(NULL
, UNISP_NAME
, http
->mode
== _HTTP_MODE_SERVER
? SECPKG_CRED_INBOUND
: SECPKG_CRED_OUTBOUND
, NULL
, &SchannelCred
, NULL
, NULL
, &sspi
->creds
, &tsExpiry
);
1497 if (Status
!= SEC_E_OK
)
1499 DEBUG_printf(("http_sspi_find_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi
, Status
)));
1511 CertFreeCertificateContext(storedContext
);
1517 CertCloseStore(store
, 0);
1520 CryptReleaseContext(hProv
, 0);
1527 * 'http_sspi_free()' - Close a connection and free resources.
1531 http_sspi_free(_http_sspi_t
*sspi
) /* I - SSPI data */
1536 if (sspi
->contextInitialized
)
1537 DeleteSecurityContext(&sspi
->context
);
1539 if (sspi
->decryptBuffer
)
1540 free(sspi
->decryptBuffer
);
1542 if (sspi
->readBuffer
)
1543 free(sspi
->readBuffer
);
1545 if (sspi
->writeBuffer
)
1546 free(sspi
->writeBuffer
);
1548 if (sspi
->localCert
)
1549 CertFreeCertificateContext(sspi
->localCert
);
1551 if (sspi
->remoteCert
)
1552 CertFreeCertificateContext(sspi
->remoteCert
);
1559 * 'http_sspi_make_credentials()' - Create a TLS certificate in the system store.
1562 static BOOL
/* O - 1 on success, 0 on failure */
1563 http_sspi_make_credentials(
1564 _http_sspi_t
*sspi
, /* I - SSPI data */
1565 const LPWSTR container
, /* I - Cert container name */
1566 const char *common_name
, /* I - Common name of certificate */
1567 _http_mode_t mode
, /* I - Client or server? */
1568 int years
) /* I - Years until expiration */
1570 HCERTSTORE store
= NULL
; /* Certificate store */
1571 PCCERT_CONTEXT storedContext
= NULL
; /* Context created from the store */
1572 PCCERT_CONTEXT createdContext
= NULL
; /* Context created by us */
1573 DWORD dwSize
= 0; /* 32 bit size */
1574 PBYTE p
= NULL
; /* Temporary storage */
1575 HCRYPTPROV hProv
= (HCRYPTPROV
)NULL
;
1576 /* Handle to a CSP */
1577 CERT_NAME_BLOB sib
; /* Arbitrary array of bytes */
1578 SCHANNEL_CRED SchannelCred
; /* Schannel credential data */
1579 TimeStamp tsExpiry
; /* Time stamp */
1580 SECURITY_STATUS Status
; /* Status */
1581 HCRYPTKEY hKey
= (HCRYPTKEY
)NULL
; /* Handle to crypto key */
1582 CRYPT_KEY_PROV_INFO kpi
; /* Key container info */
1583 SYSTEMTIME et
; /* System time */
1584 CERT_EXTENSIONS exts
; /* Array of cert extensions */
1585 CRYPT_KEY_PROV_INFO ckp
; /* Handle to crypto key */
1586 BOOL ok
= TRUE
; /* Return value */
1589 DEBUG_printf(("4http_sspi_make_credentials(sspi=%p, container=%p, common_name=\"%s\", mode=%d, years=%d)", sspi
, container
, common_name
, mode
, years
));
1591 if (!CryptAcquireContextW(&hProv
, (LPWSTR
)container
, MS_DEF_PROV_W
, PROV_RSA_FULL
, CRYPT_NEWKEYSET
| CRYPT_MACHINE_KEYSET
))
1593 if (GetLastError() == NTE_EXISTS
)
1595 if (!CryptAcquireContextW(&hProv
, (LPWSTR
)container
, MS_DEF_PROV_W
, PROV_RSA_FULL
, CRYPT_MACHINE_KEYSET
))
1597 DEBUG_printf(("http_sspi_make_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1604 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");
1608 DEBUG_printf(("http_sspi_make_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1615 if (!CertStrToName(X509_ASN_ENCODING
, common_name
, CERT_OID_NAME_STR
, NULL
, NULL
, &dwSize
, NULL
))
1617 DEBUG_printf(("http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1622 p
= (PBYTE
)malloc(dwSize
);
1626 DEBUG_printf(("http_sspi_make_credentials: malloc failed for %d bytes", dwSize
));
1631 if (!CertStrToName(X509_ASN_ENCODING
, common_name
, CERT_OID_NAME_STR
, NULL
, p
, &dwSize
, NULL
))
1633 DEBUG_printf(("http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1639 * If we couldn't find the context, then we'll create a new one...
1642 if (!CryptGenKey(hProv
, AT_KEYEXCHANGE
, CRYPT_EXPORTABLE
, &hKey
))
1644 DEBUG_printf(("http_sspi_make_credentials: CryptGenKey failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1649 ZeroMemory(&kpi
, sizeof(kpi
));
1650 kpi
.pwszContainerName
= (LPWSTR
)container
;
1651 kpi
.pwszProvName
= MS_DEF_PROV_W
;
1652 kpi
.dwProvType
= PROV_RSA_FULL
;
1653 kpi
.dwFlags
= CERT_SET_KEY_CONTEXT_PROP_ID
;
1654 kpi
.dwKeySpec
= AT_KEYEXCHANGE
;
1659 ZeroMemory(&exts
, sizeof(exts
));
1661 createdContext
= CertCreateSelfSignCertificate(hProv
, &sib
, 0, &kpi
, NULL
, NULL
, &et
, &exts
);
1663 if (!createdContext
)
1665 DEBUG_printf(("http_sspi_make_credentials: CertCreateSelfSignCertificate failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1670 if (!CertAddCertificateContextToStore(store
, createdContext
, CERT_STORE_ADD_REPLACE_EXISTING
, &storedContext
))
1672 DEBUG_printf(("http_sspi_make_credentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1677 ZeroMemory(&ckp
, sizeof(ckp
));
1678 ckp
.pwszContainerName
= (LPWSTR
) container
;
1679 ckp
.pwszProvName
= MS_DEF_PROV_W
;
1680 ckp
.dwProvType
= PROV_RSA_FULL
;
1681 ckp
.dwFlags
= CRYPT_MACHINE_KEYSET
;
1682 ckp
.dwKeySpec
= AT_KEYEXCHANGE
;
1684 if (!CertSetCertificateContextProperty(storedContext
, CERT_KEY_PROV_INFO_PROP_ID
, 0, &ckp
))
1686 DEBUG_printf(("http_sspi_make_credentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(sspi
, GetLastError())));
1692 ZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
1694 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
1695 SchannelCred
.cCreds
= 1;
1696 SchannelCred
.paCred
= &storedContext
;
1699 * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
1702 if (mode
== _HTTP_MODE_SERVER
)
1703 SchannelCred
.grbitEnabledProtocols
= SP_PROT_SSL3TLS1
;
1706 * Create an SSPI credential.
1709 Status
= AcquireCredentialsHandle(NULL
, UNISP_NAME
, mode
== _HTTP_MODE_SERVER
? SECPKG_CRED_INBOUND
: SECPKG_CRED_OUTBOUND
, NULL
, &SchannelCred
, NULL
, NULL
, &sspi
->creds
, &tsExpiry
);
1710 if (Status
!= SEC_E_OK
)
1712 DEBUG_printf(("http_sspi_make_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi
, Status
)));
1724 CryptDestroyKey(hKey
);
1727 CertFreeCertificateContext(createdContext
);
1730 CertFreeCertificateContext(storedContext
);
1736 CertCloseStore(store
, 0);
1739 CryptReleaseContext(hProv
, 0);
1746 * 'http_sspi_server()' - Negotiate a TLS connection as a server.
1749 static int /* O - 0 on success, -1 on failure */
1750 http_sspi_server(http_t
*http
, /* I - HTTP connection */
1751 const char *hostname
) /* I - Hostname of server */
1753 _http_sspi_t
*sspi
= http
->tls
; /* I - SSPI data */
1754 char common_name
[512]; /* Common name for cert */
1755 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1756 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1757 TimeStamp tsExpiry
; /* Time stamp */
1758 SECURITY_STATUS scRet
; /* SSPI Status */
1759 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1760 SecBuffer inBuffers
[2]; /* Security package buffer */
1761 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1762 SecBuffer outBuffers
[1]; /* Security package buffer */
1763 int num
= 0; /* 32 bit status value */
1764 BOOL fInitContext
= TRUE
; /* Has the context been init'd? */
1765 int ret
= 0; /* Return value */
1768 DEBUG_printf(("http_sspi_server(http=%p, hostname=\"%s\")", http
, hostname
));
1770 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
1771 ASC_REQ_REPLAY_DETECT
|
1772 ASC_REQ_CONFIDENTIALITY
|
1773 ASC_REQ_EXTENDED_ERROR
|
1774 ASC_REQ_ALLOCATE_MEMORY
|
1777 sspi
->decryptBufferUsed
= 0;
1780 * Lookup the server certificate...
1783 snprintf(common_name
, sizeof(common_name
), "CN=%s", hostname
);
1785 if (!http_sspi_find_credentials(http
, L
"ServerContainer", common_name
))
1786 if (!http_sspi_make_credentials(http
->tls
, L
"ServerContainer", common_name
, _HTTP_MODE_SERVER
, 10))
1788 DEBUG_puts("http_sspi_server: Unable to get server credentials.");
1793 * Set OutBuffer for AcceptSecurityContext call
1796 outBuffer
.cBuffers
= 1;
1797 outBuffer
.pBuffers
= outBuffers
;
1798 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1800 scRet
= SEC_I_CONTINUE_NEEDED
;
1802 while (scRet
== SEC_I_CONTINUE_NEEDED
||
1803 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1804 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1806 if (sspi
->decryptBufferUsed
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1808 if (sspi
->decryptBufferLength
<= sspi
->decryptBufferUsed
)
1810 BYTE
*temp
; /* New buffer */
1812 if (sspi
->decryptBufferLength
>= 262144)
1814 WSASetLastError(E_OUTOFMEMORY
);
1815 DEBUG_puts("http_sspi_server: Decryption buffer too large (>256k)");
1819 if ((temp
= realloc(sspi
->decryptBuffer
, sspi
->decryptBufferLength
+ 4096)) == NULL
)
1821 DEBUG_printf(("http_sspi_server: Unable to allocate %d byte buffer.", sspi
->decryptBufferLength
+ 4096));
1822 WSASetLastError(E_OUTOFMEMORY
);
1826 sspi
->decryptBufferLength
+= 4096;
1827 sspi
->decryptBuffer
= temp
;
1832 num
= recv(http
->fd
, sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
, (int)(sspi
->decryptBufferLength
- sspi
->decryptBufferUsed
), 0);
1834 if (num
== -1 && WSAGetLastError() == WSAEWOULDBLOCK
)
1842 DEBUG_printf(("http_sspi_server: recv failed: %d", WSAGetLastError()));
1847 DEBUG_puts("http_sspi_server: client disconnected");
1851 DEBUG_printf(("http_sspi_server: received %d (handshake) bytes from client.", num
));
1852 sspi
->decryptBufferUsed
+= num
;
1856 * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process
1857 * on this run around the loop.
1860 inBuffers
[0].pvBuffer
= sspi
->decryptBuffer
;
1861 inBuffers
[0].cbBuffer
= (unsigned long)sspi
->decryptBufferUsed
;
1862 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1864 inBuffers
[1].pvBuffer
= NULL
;
1865 inBuffers
[1].cbBuffer
= 0;
1866 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1868 inBuffer
.cBuffers
= 2;
1869 inBuffer
.pBuffers
= inBuffers
;
1870 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1873 * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to
1874 * free random garbage at the quit.
1877 outBuffers
[0].pvBuffer
= NULL
;
1878 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1879 outBuffers
[0].cbBuffer
= 0;
1881 scRet
= AcceptSecurityContext(&sspi
->creds
, (fInitContext
?NULL
:&sspi
->context
), &inBuffer
, dwSSPIFlags
, SECURITY_NATIVE_DREP
, (fInitContext
?&sspi
->context
:NULL
), &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1883 fInitContext
= FALSE
;
1885 if (scRet
== SEC_E_OK
||
1886 scRet
== SEC_I_CONTINUE_NEEDED
||
1887 (FAILED(scRet
) && ((dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
) != 0)))
1889 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1892 * Send response to server if there is one.
1895 num
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1899 DEBUG_printf(("http_sspi_server: handshake send failed: %d", WSAGetLastError()));
1903 DEBUG_printf(("http_sspi_server: sent %d handshake bytes to client.", outBuffers
[0].cbBuffer
));
1905 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1906 outBuffers
[0].pvBuffer
= NULL
;
1910 if (scRet
== SEC_E_OK
)
1913 * If there's extra data then save it for next time we go to decrypt.
1916 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1918 memcpy(sspi
->decryptBuffer
, (LPBYTE
)(sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
- inBuffers
[1].cbBuffer
), inBuffers
[1].cbBuffer
);
1919 sspi
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1923 sspi
->decryptBufferUsed
= 0;
1927 else if (FAILED(scRet
) && scRet
!= SEC_E_INCOMPLETE_MESSAGE
)
1929 DEBUG_printf(("http_sspi_server: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi
, scRet
)));
1934 if (scRet
!= SEC_E_INCOMPLETE_MESSAGE
&&
1935 scRet
!= SEC_I_INCOMPLETE_CREDENTIALS
)
1937 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1939 memcpy(sspi
->decryptBuffer
, (LPBYTE
)(sspi
->decryptBuffer
+ sspi
->decryptBufferUsed
- inBuffers
[1].cbBuffer
), inBuffers
[1].cbBuffer
);
1940 sspi
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1944 sspi
->decryptBufferUsed
= 0;
1951 sspi
->contextInitialized
= TRUE
;
1954 * Find out how big the header will be:
1957 scRet
= QueryContextAttributes(&sspi
->context
, SECPKG_ATTR_STREAM_SIZES
, &sspi
->streamSizes
);
1959 if (scRet
!= SEC_E_OK
)
1961 DEBUG_printf(("http_sspi_server: QueryContextAttributes failed: %s", http_sspi_strerror(sspi
, scRet
)));
1971 * 'http_sspi_strerror()' - Return a string for the specified error code.
1974 static const char * /* O - String for error */
1975 http_sspi_strerror(_http_sspi_t
*sspi
, /* I - SSPI data */
1976 DWORD code
) /* I - Error code */
1978 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
, NULL
, code
, 0, sspi
->error
, sizeof(sspi
->error
), NULL
))
1981 * Strip trailing CR + LF...
1984 char *ptr
; /* Pointer into error message */
1986 for (ptr
= sspi
->error
+ strlen(sspi
->error
) - 1; ptr
>= sspi
->error
; ptr
--)
1987 if (*ptr
== '\n' || *ptr
== '\r')
1993 snprintf(sspi
->error
, sizeof(sspi
->error
), "Unknown error %x", code
);
1995 return (sspi
->error
);
2000 * 'http_sspi_verify()' - Verify a certificate.
2003 static DWORD
/* O - Error code (0 == No error) */
2005 PCCERT_CONTEXT cert
, /* I - Server certificate */
2006 const char *common_name
, /* I - Common name */
2007 DWORD dwCertFlags
) /* I - Verification flags */
2009 HTTPSPolicyCallbackData httpsPolicy
; /* HTTPS Policy Struct */
2010 CERT_CHAIN_POLICY_PARA policyPara
; /* Cert chain policy parameters */
2011 CERT_CHAIN_POLICY_STATUS policyStatus
;/* Cert chain policy status */
2012 CERT_CHAIN_PARA chainPara
; /* Used for searching and matching criteria */
2013 PCCERT_CHAIN_CONTEXT chainContext
= NULL
;
2014 /* Certificate chain */
2015 PWSTR commonNameUnicode
= NULL
;
2016 /* Unicode common name */
2017 LPSTR rgszUsages
[] = { szOID_PKIX_KP_SERVER_AUTH
,
2018 szOID_SERVER_GATED_CRYPTO
,
2019 szOID_SGC_NETSCAPE
};
2020 /* How are we using this certificate? */
2021 DWORD cUsages
= sizeof(rgszUsages
) / sizeof(LPSTR
);
2022 /* Number of ites in rgszUsages */
2023 DWORD count
; /* 32 bit count variable */
2024 DWORD status
; /* Return value */
2028 return (SEC_E_WRONG_PRINCIPAL
);
2031 * Convert common name to Unicode.
2034 if (!common_name
|| !*common_name
)
2035 return (SEC_E_WRONG_PRINCIPAL
);
2037 count
= MultiByteToWideChar(CP_ACP
, 0, common_name
, -1, NULL
, 0);
2038 commonNameUnicode
= LocalAlloc(LMEM_FIXED
, count
* sizeof(WCHAR
));
2039 if (!commonNameUnicode
)
2040 return (SEC_E_INSUFFICIENT_MEMORY
);
2042 if (!MultiByteToWideChar(CP_ACP
, 0, common_name
, -1, commonNameUnicode
, count
))
2044 LocalFree(commonNameUnicode
);
2045 return (SEC_E_WRONG_PRINCIPAL
);
2049 * Build certificate chain.
2052 ZeroMemory(&chainPara
, sizeof(chainPara
));
2054 chainPara
.cbSize
= sizeof(chainPara
);
2055 chainPara
.RequestedUsage
.dwType
= USAGE_MATCH_TYPE_OR
;
2056 chainPara
.RequestedUsage
.Usage
.cUsageIdentifier
= cUsages
;
2057 chainPara
.RequestedUsage
.Usage
.rgpszUsageIdentifier
= rgszUsages
;
2059 if (!CertGetCertificateChain(NULL
, cert
, NULL
, cert
->hCertStore
, &chainPara
, 0, NULL
, &chainContext
))
2061 status
= GetLastError();
2062 DEBUG_printf(("CertGetCertificateChain returned: %x", status
));
2064 LocalFree(commonNameUnicode
);
2069 * Validate certificate chain.
2072 ZeroMemory(&httpsPolicy
, sizeof(HTTPSPolicyCallbackData
));
2073 httpsPolicy
.cbStruct
= sizeof(HTTPSPolicyCallbackData
);
2074 httpsPolicy
.dwAuthType
= AUTHTYPE_SERVER
;
2075 httpsPolicy
.fdwChecks
= dwCertFlags
;
2076 httpsPolicy
.pwszServerName
= commonNameUnicode
;
2078 memset(&policyPara
, 0, sizeof(policyPara
));
2079 policyPara
.cbSize
= sizeof(policyPara
);
2080 policyPara
.pvExtraPolicyPara
= &httpsPolicy
;
2082 memset(&policyStatus
, 0, sizeof(policyStatus
));
2083 policyStatus
.cbSize
= sizeof(policyStatus
);
2085 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL
, chainContext
, &policyPara
, &policyStatus
))
2087 status
= GetLastError();
2088 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %x", status
));
2090 else if (policyStatus
.dwError
)
2091 status
= policyStatus
.dwError
;
2096 CertFreeCertificateChain(chainContext
);
2098 if (commonNameUnicode
)
2099 LocalFree(commonNameUnicode
);