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 BOOL
http_sspi_credentials(http_t
*http
, const LPWSTR containerName
, const char *common_name
, BOOL server
);
52 static void http_sspi_free(_http_sspi_t
*conn
);
53 static int http_sspi_server(http_t
*http
, const char *hostname
);
54 static void http_sspi_set_allows_any_root(_http_sspi_t
*conn
, BOOL allow
);
55 static void http_sspi_set_allows_expired_certs(_http_sspi_t
*conn
, BOOL allow
);
56 static const char *http_sspi_strerror(_http_sspi_t
*conn
, DWORD code
);
57 static DWORD
http_sspi_verify(PCCERT_CONTEXT cert
, const char *common_name
, DWORD dwCertFlags
);
61 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
66 int /* O - 1 on success, 0 on failure */
67 cupsMakeServerCredentials(
68 const char *path
, /* I - Keychain path or @code NULL@ for default */
69 const char *common_name
, /* I - Common name */
70 int num_alt_names
, /* I - Number of subject alternate names */
71 const char **alt_names
, /* I - Subject Alternate Names */
72 time_t expiration_date
) /* I - Expiration date */
74 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
));
80 (void)expiration_date
;
87 * 'cupsSetServerCredentials()' - Set the default server credentials.
89 * Note: The server credentials are used by all threads in the running process.
90 * This function is threadsafe.
95 int /* O - 1 on success, 0 on failure */
96 cupsSetServerCredentials(
97 const char *path
, /* I - Keychain path or @code NULL@ for default */
98 const char *common_name
, /* I - Default common name for server */
99 int auto_create
) /* I - 1 = automatically create self-signed certificates */
101 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path
, common_name
, auto_create
));
112 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
113 * an encrypted connection.
115 * @since CUPS 1.5/OS X 10.7@
118 int /* O - Status of call (0 = success) */
120 http_t
*http
, /* I - Connection to server */
121 cups_array_t
**credentials
) /* O - Array of credentials */
123 DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http
, credentials
));
135 * '_httpCreateCredentials()' - Create credentials in the internal format.
138 http_tls_credentials_t
/* O - Internal credentials */
139 _httpCreateCredentials(
140 cups_array_t
*credentials
) /* I - Array of credentials */
149 * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
154 int /* O - 1 if valid, 0 otherwise */
155 httpCredentialsAreValidForName(
156 cups_array_t
*credentials
, /* I - Credentials */
157 const char *common_name
) /* I - Name to check */
167 * 'httpCredentialsGetTrust()' - Return the trust of credentials.
172 http_trust_t
/* O - Level of trust */
173 httpCredentialsGetTrust(
174 cups_array_t
*credentials
, /* I - Credentials */
175 const char *common_name
) /* I - Common name for trust lookup */
177 http_trust_t trust
= HTTP_TRUST_OK
;
179 cups_array_t
*tcreds
= NULL
; /* Trusted credentials */
180 _cups_globals_t
*cg
= _cupsGlobals();
181 /* Per-thread globals */
185 return (HTTP_TRUST_UNKNOWN
);
188 * Look this common name up in the default keychains...
191 httpLoadCredentials(NULL
, &tcreds
, common_name
);
195 char credentials_str
[1024], /* String for incoming credentials */
196 tcreds_str
[1024]; /* String for saved credentials */
198 httpCredentialsString(credentials
, credentials_str
, sizeof(credentials_str
));
199 httpCredentialsString(tcreds
, tcreds_str
, sizeof(tcreds_str
));
201 if (strcmp(credentials_str
, tcreds_str
))
204 * Credentials don't match, let's look at the expiration date of the new
205 * credentials and allow if the new ones have a later expiration...
208 if (httpCredentialsGetExpiration(credentials
) <= httpCredentialsGetExpiration(tcreds
) ||
209 !httpCredentialsAreValidForName(credentials
, common_name
))
212 * Either the new credentials are not newly issued, or the common name
213 * does not match the issued certificate...
216 trust
= HTTP_TRUST_INVALID
;
218 else if (httpCredentialsGetExpiration(tcreds
) < time(NULL
))
221 * Save the renewed credentials...
224 trust
= HTTP_TRUST_RENEWED
;
226 httpSaveCredentials(NULL
, credentials
, common_name
);
230 httpFreeCredentials(tcreds
);
232 else if (cg
->validate_certs
&& !httpCredentialsAreValidForName(credentials
, common_name
))
233 trust
= HTTP_TRUST_INVALID
;
235 if (!cg
->expired_certs
&& time(NULL
) > httpCredentialsGetExpiration(credentials
))
236 trust
= HTTP_TRUST_EXPIRED
;
237 else if (!cg
->any_root
&& cupsArrayCount(credentials
) == 1)
238 trust
= HTTP_TRUST_INVALID
;
245 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
250 time_t /* O - Expiration date of credentials */
251 httpCredentialsGetExpiration(
252 cups_array_t
*credentials
) /* I - Credentials */
261 * 'httpCredentialsString()' - Return a string representing the credentials.
266 size_t /* O - Total size of credentials string */
267 httpCredentialsString(
268 cups_array_t
*credentials
, /* I - Credentials */
269 char *buffer
, /* I - Buffer or @code NULL@ */
270 size_t bufsize
) /* I - Size of buffer */
272 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT
")", credentials
, buffer
, CUPS_LLCAST bufsize
));
277 if (buffer
&& bufsize
> 0)
281 http_credential_t
*first
; /* First certificate */
282 SecCertificateRef secCert
; /* Certificate reference */
285 if ((first
= (http_credential_t
*)cupsArrayFirst(credentials
)) != NULL
&&
286 (secCert
= http_cdsa_create_credential(first
)) != NULL
)
288 CFStringRef cf_name
; /* CF common name string */
289 char name
[256]; /* Common name associated with cert */
290 time_t expiration
; /* Expiration date of cert */
291 _cups_md5_state_t md5_state
; /* MD5 state */
292 unsigned char md5_digest
[16]; /* MD5 result */
294 if ((cf_name
= SecCertificateCopySubjectSummary(secCert
)) != NULL
)
296 CFStringGetCString(cf_name
, name
, (CFIndex
)sizeof(name
), kCFStringEncodingUTF8
);
300 strlcpy(name
, "unknown", sizeof(name
));
302 expiration
= (time_t)(SecCertificateNotValidAfter(secCert
) + kCFAbsoluteTimeIntervalSince1970
);
304 _cupsMD5Init(&md5_state
);
305 _cupsMD5Append(&md5_state
, first
->data
, (int)first
->datalen
);
306 _cupsMD5Finish(&md5_state
, md5_digest
);
308 snprintf(buffer
, bufsize
, "%s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", 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]);
314 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer
));
316 return (strlen(buffer
));
321 * '_httpFreeCredentials()' - Free internal credentials.
325 _httpFreeCredentials(
326 http_tls_credentials_t credentials
) /* I - Internal credentials */
334 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
339 int /* O - 0 on success, -1 on error */
341 const char *path
, /* I - Keychain path or @code NULL@ for default */
342 cups_array_t
**credentials
, /* IO - Credentials */
343 const char *common_name
) /* I - Common name for credentials */
345 DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path
, credentials
, common_name
));
353 DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials
? 0 : -1));
355 return (*credentials
? 0 : -1);
360 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
365 int /* O - -1 on error, 0 on success */
367 const char *path
, /* I - Keychain path or @code NULL@ for default */
368 cups_array_t
*credentials
, /* I - Credentials */
369 const char *common_name
) /* I - Common name for credentials */
371 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path
, credentials
, common_name
));
377 DEBUG_printf(("1httpSaveCredentials: Returning %d.", -1));
383 * '_httpTLSInitialize()' - Initialize the TLS stack.
387 _httpTLSInitialize(void)
396 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
399 size_t /* O - Bytes available */
400 _httpTLSPending(http_t
*http
) /* I - HTTP connection */
403 return (http
->tls
->readBufferUsed
);
410 * '_httpTLSRead()' - Read from a SSL/TLS connection.
413 int /* O - Bytes read */
414 _httpTLSRead(http_t
*http
, /* I - HTTP connection */
415 char *buf
, /* I - Buffer to store data */
416 int len
) /* I - Length of buffer */
418 int i
; /* Looping var */
419 _http_sspi_t
*conn
= http
->tls
; /* SSPI data */
420 SecBufferDesc message
; /* Array of SecBuffer struct */
421 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
422 int num
= 0; /* Return value */
423 PSecBuffer pDataBuffer
; /* Data buffer */
424 PSecBuffer pExtraBuffer
; /* Excess data buffer */
425 SECURITY_STATUS scRet
; /* SSPI status */
428 DEBUG_printf(("4_httpTLSRead(http=%p, buf=%p, len=%d)", http
, buf
, len
));
432 DEBUG_puts("5_httpTLSRead: No TLS context, returning -1.");
433 WSASetLastError(WSAEINVAL
);
438 * If there are bytes that have already been decrypted and have not yet been
439 * read, return those...
442 if (conn
->readBufferUsed
> 0)
444 int bytesToCopy
= min(conn
->readBufferUsed
, len
);
445 /* Number of bytes to copy */
447 memcpy(buf
, conn
->readBuffer
, bytesToCopy
);
448 conn
->readBufferUsed
-= bytesToCopy
;
450 if (conn
->readBufferUsed
> 0)
451 memmove(conn
->readBuffer
, conn
->readBuffer
+ bytesToCopy
, conn
->readBufferUsed
);
453 DEBUG_printf(("5_httpTLSRead: Returning %d bytes previously decrypted.", bytesToCopy
));
455 return (bytesToCopy
);
459 * Initialize security buffer structs
462 message
.ulVersion
= SECBUFFER_VERSION
;
463 message
.cBuffers
= 4;
464 message
.pBuffers
= buffers
;
469 * If there is not enough space in the buffer, then increase its size...
472 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
474 BYTE
*temp
; /* New buffer */
476 if (conn
->decryptBufferLength
>= 262144)
478 WSASetLastError(E_OUTOFMEMORY
);
479 DEBUG_puts("_httpTLSRead: Decryption buffer too large (>256k)");
483 if ((temp
= realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
+ 4096)) == NULL
)
485 DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte decryption buffer.", conn
->decryptBufferLength
+ 4096));
486 WSASetLastError(E_OUTOFMEMORY
);
490 conn
->decryptBufferLength
+= 4096;
491 conn
->decryptBuffer
= temp
;
493 DEBUG_printf(("_httpTLSRead: Resized decryption buffer to %d bytes.", conn
->decryptBufferLength
));
496 buffers
[0].pvBuffer
= conn
->decryptBuffer
;
497 buffers
[0].cbBuffer
= (unsigned long)conn
->decryptBufferUsed
;
498 buffers
[0].BufferType
= SECBUFFER_DATA
;
499 buffers
[1].BufferType
= SECBUFFER_EMPTY
;
500 buffers
[2].BufferType
= SECBUFFER_EMPTY
;
501 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
503 DEBUG_printf(("5_httpTLSRead: decryptBufferUsed=%d", conn
->decryptBufferUsed
));
505 scRet
= DecryptMessage(&conn
->context
, &message
, 0, NULL
);
507 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
509 num
= recv(http
->fd
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
, (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
512 DEBUG_printf(("5_httpTLSRead: recv failed: %d", WSAGetLastError()));
517 DEBUG_puts("5_httpTLSRead: Server disconnected.");
521 DEBUG_printf(("5_httpTLSRead: Read %d bytes into decryption buffer.", num
));
523 conn
->decryptBufferUsed
+= num
;
526 while (scRet
== SEC_E_INCOMPLETE_MESSAGE
);
528 if (scRet
== SEC_I_CONTEXT_EXPIRED
)
530 DEBUG_puts("5_httpTLSRead: Context expired.");
531 WSASetLastError(WSAECONNRESET
);
534 else if (scRet
!= SEC_E_OK
)
536 DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %s", http_sspi_strerror(conn
, scRet
)));
537 WSASetLastError(WSASYSCALLFAILURE
);
542 * The decryption worked. Now, locate data buffer.
548 for (i
= 1; i
< 4; i
++)
550 if (buffers
[i
].BufferType
== SECBUFFER_DATA
)
551 pDataBuffer
= &buffers
[i
];
552 else if (!pExtraBuffer
&& (buffers
[i
].BufferType
== SECBUFFER_EXTRA
))
553 pExtraBuffer
= &buffers
[i
];
557 * If a data buffer is found, then copy the decrypted bytes to the passed-in
563 int bytesToCopy
= min((int)pDataBuffer
->cbBuffer
, len
);
564 /* Number of bytes to copy into buf */
565 int bytesToSave
= pDataBuffer
->cbBuffer
- bytesToCopy
;
566 /* Number of bytes to save in our read buffer */
569 memcpy(buf
, pDataBuffer
->pvBuffer
, bytesToCopy
);
572 * If there are more decrypted bytes than can be copied to the passed in
573 * buffer, then save them...
578 if ((conn
->readBufferLength
- conn
->readBufferUsed
) < bytesToSave
)
580 BYTE
*temp
; /* New buffer pointer */
582 if ((temp
= realloc(conn
->readBuffer
, conn
->readBufferUsed
+ bytesToSave
)) == NULL
)
584 DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", conn
->readBufferUsed
+ bytesToSave
));
585 WSASetLastError(E_OUTOFMEMORY
);
589 conn
->readBufferLength
= conn
->readBufferUsed
+ bytesToSave
;
590 conn
->readBuffer
= temp
;
593 memcpy(((BYTE
*)conn
->readBuffer
) + conn
->readBufferUsed
, ((BYTE
*)pDataBuffer
->pvBuffer
) + bytesToCopy
, bytesToSave
);
595 conn
->readBufferUsed
+= bytesToSave
;
602 DEBUG_puts("_httpTLSRead: Unable to find data buffer.");
603 WSASetLastError(WSASYSCALLFAILURE
);
608 * If the decryption process left extra bytes, then save those back in
609 * decryptBuffer. They will be processed the next time through the loop.
614 memmove(conn
->decryptBuffer
, pExtraBuffer
->pvBuffer
, pExtraBuffer
->cbBuffer
);
615 conn
->decryptBufferUsed
= pExtraBuffer
->cbBuffer
;
619 conn
->decryptBufferUsed
= 0;
627 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
630 int /* O - 0 on success, -1 on failure */
631 _httpTLSStart(http_t
*http
) /* I - HTTP connection */
633 char hostname
[256], /* Hostname */
634 *hostptr
; /* Pointer into hostname */
637 DEBUG_printf(("7_httpTLSStart(http=%p)", http
));
639 if ((http
->tls
= http_sspi_alloc()) == NULL
)
642 if (http
->mode
== _HTTP_MODE_CLIENT
)
645 * Client: determine hostname...
648 if (httpAddrLocalhost(http
->hostaddr
))
650 strlcpy(hostname
, "localhost", sizeof(hostname
));
655 * Otherwise make sure the hostname we have does not end in a trailing dot.
658 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
659 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
664 return (http_sspi_client(http
, hostname
));
669 * Server: determine hostname to use...
672 if (http
->fields
[HTTP_FIELD_HOST
][0])
675 * Use hostname for TLS upgrade...
678 strlcpy(hostname
, http
->fields
[HTTP_FIELD_HOST
], sizeof(hostname
));
683 * Resolve hostname from connection address...
686 http_addr_t addr
; /* Connection address */
687 socklen_t addrlen
; /* Length of address */
689 addrlen
= sizeof(addr
);
690 if (getsockname(http
->fd
, (struct sockaddr
*)&addr
, &addrlen
))
692 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno
)));
695 else if (httpAddrLocalhost(&addr
))
699 httpAddrLookup(&addr
, hostname
, sizeof(hostname
));
700 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname
));
704 return (http_sspi_server(http
, hostname
));
710 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
714 _httpTLSStop(http_t
*http
) /* I - HTTP connection */
716 _http_sspi_t
*conn
= http
->tls
; /* SSPI data */
719 if (conn
->contextInitialized
&& http
->fd
>= 0)
721 SecBufferDesc message
; /* Array of SecBuffer struct */
722 SecBuffer buffers
[1] = { 0 };
723 /* Security package buffer */
724 DWORD dwType
; /* Type */
725 DWORD status
; /* Status */
728 * Notify schannel that we are about to close the connection.
731 dwType
= SCHANNEL_SHUTDOWN
;
733 buffers
[0].pvBuffer
= &dwType
;
734 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
735 buffers
[0].cbBuffer
= sizeof(dwType
);
737 message
.cBuffers
= 1;
738 message
.pBuffers
= buffers
;
739 message
.ulVersion
= SECBUFFER_VERSION
;
741 status
= ApplyControlToken(&conn
->context
, &message
);
743 if (SUCCEEDED(status
))
745 PBYTE pbMessage
; /* Message buffer */
746 DWORD cbMessage
; /* Message buffer count */
747 DWORD cbData
; /* Data count */
748 DWORD dwSSPIFlags
; /* SSL attributes we requested */
749 DWORD dwSSPIOutFlags
; /* SSL attributes we received */
750 TimeStamp tsExpiry
; /* Time stamp */
752 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
753 ASC_REQ_REPLAY_DETECT
|
754 ASC_REQ_CONFIDENTIALITY
|
755 ASC_REQ_EXTENDED_ERROR
|
756 ASC_REQ_ALLOCATE_MEMORY
|
759 buffers
[0].pvBuffer
= NULL
;
760 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
761 buffers
[0].cbBuffer
= 0;
763 message
.cBuffers
= 1;
764 message
.pBuffers
= buffers
;
765 message
.ulVersion
= SECBUFFER_VERSION
;
767 status
= AcceptSecurityContext(&conn
->creds
, &conn
->context
, NULL
,
768 dwSSPIFlags
, SECURITY_NATIVE_DREP
, NULL
,
769 &message
, &dwSSPIOutFlags
, &tsExpiry
);
771 if (SUCCEEDED(status
))
773 pbMessage
= buffers
[0].pvBuffer
;
774 cbMessage
= buffers
[0].cbBuffer
;
777 * Send the close notify message to the client.
780 if (pbMessage
&& cbMessage
)
782 cbData
= send(http
->fd
, pbMessage
, cbMessage
, 0);
783 if ((cbData
== SOCKET_ERROR
) || (cbData
== 0))
785 status
= WSAGetLastError();
786 DEBUG_printf(("_httpTLSStop: sending close notify failed: %d", status
));
790 FreeContextBuffer(pbMessage
);
796 DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %s", http_sspi_strerror(conn
, status
)));
801 DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %s", http_sspi_strerror(conn
, status
)));
805 http_sspi_free(conn
);
812 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
815 int /* O - Bytes written */
816 _httpTLSWrite(http_t
*http
, /* I - HTTP connection */
817 const char *buf
, /* I - Buffer holding data */
818 int len
) /* I - Length of buffer */
820 _http_sspi_t
*conn
= http
->tls
; /* SSPI data */
821 SecBufferDesc message
; /* Array of SecBuffer struct */
822 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
823 int bufferLen
; /* Buffer length */
824 int bytesLeft
; /* Bytes left to write */
825 const char *bufptr
; /* Pointer into buffer */
826 int num
= 0; /* Return value */
829 bufferLen
= conn
->streamSizes
.cbMaximumMessage
+ conn
->streamSizes
.cbHeader
+ conn
->streamSizes
.cbTrailer
;
831 if (bufferLen
> conn
->writeBufferLength
)
833 BYTE
*temp
; /* New buffer pointer */
835 if ((temp
= (BYTE
*)realloc(conn
->writeBuffer
, bufferLen
)) == NULL
)
837 DEBUG_printf(("_httpTLSWrite: Unable to allocate buffer of %d bytes.", bufferLen
));
838 WSASetLastError(E_OUTOFMEMORY
);
842 conn
->writeBuffer
= temp
;
843 conn
->writeBufferLength
= bufferLen
;
851 int chunk
= min((int)conn
->streamSizes
.cbMaximumMessage
, bytesLeft
);
852 /* Size of data to write */
853 SECURITY_STATUS scRet
; /* SSPI status */
856 * Copy user data into the buffer, starting just past the header...
859 memcpy(conn
->writeBuffer
+ conn
->streamSizes
.cbHeader
, bufptr
, chunk
);
862 * Setup the SSPI buffers
865 message
.ulVersion
= SECBUFFER_VERSION
;
866 message
.cBuffers
= 4;
867 message
.pBuffers
= buffers
;
869 buffers
[0].pvBuffer
= conn
->writeBuffer
;
870 buffers
[0].cbBuffer
= conn
->streamSizes
.cbHeader
;
871 buffers
[0].BufferType
= SECBUFFER_STREAM_HEADER
;
872 buffers
[1].pvBuffer
= conn
->writeBuffer
+ conn
->streamSizes
.cbHeader
;
873 buffers
[1].cbBuffer
= (unsigned long) chunk
;
874 buffers
[1].BufferType
= SECBUFFER_DATA
;
875 buffers
[2].pvBuffer
= conn
->writeBuffer
+ conn
->streamSizes
.cbHeader
+ chunk
;
876 buffers
[2].cbBuffer
= conn
->streamSizes
.cbTrailer
;
877 buffers
[2].BufferType
= SECBUFFER_STREAM_TRAILER
;
878 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
884 scRet
= EncryptMessage(&conn
->context
, 0, &message
, 0);
888 DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %s", http_sspi_strerror(conn
, scRet
)));
889 WSASetLastError(WSASYSCALLFAILURE
);
894 * Send the data. Remember the size of the total data to send is the size
895 * of the header, the size of the data the caller passed in and the size
899 num
= send(http
->fd
, conn
->writeBuffer
, buffers
[0].cbBuffer
+ buffers
[1].cbBuffer
+ buffers
[2].cbBuffer
, 0);
903 DEBUG_printf(("_httpTLSWrite: send failed: %ld", WSAGetLastError()));
917 * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
920 static int /* O - 0 on success, -1 on failure */
921 http_setup_ssl(http_t
*http
) /* I - Connection to server */
923 char hostname
[256], /* Hostname */
924 *hostptr
; /* Pointer into hostname */
926 TCHAR username
[256]; /* Username returned from GetUserName() */
927 TCHAR commonName
[256];/* Common name for certificate */
928 DWORD dwSize
; /* 32 bit size */
931 DEBUG_printf(("7http_setup_ssl(http=%p)", http
));
934 * Get the hostname to use for SSL...
937 if (httpAddrLocalhost(http
->hostaddr
))
939 strlcpy(hostname
, "localhost", sizeof(hostname
));
944 * Otherwise make sure the hostname we have does not end in a trailing dot.
947 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
948 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
953 http
->tls
= http_sspi_alloc();
957 _cupsSetHTTPError(HTTP_STATUS_ERROR
);
961 dwSize
= sizeof(username
) / sizeof(TCHAR
);
962 GetUserName(username
, &dwSize
);
963 _sntprintf_s(commonName
, sizeof(commonName
) / sizeof(TCHAR
),
964 sizeof(commonName
) / sizeof(TCHAR
), TEXT("CN=%s"), username
);
966 if (!_sspiGetCredentials(http
->tls
, L
"ClientContainer",
969 _sspiFree(http
->tls
);
973 http
->status
= HTTP_STATUS_ERROR
;
975 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
976 _("Unable to establish a secure connection to host."), 1);
981 _sspiSetAllowsAnyRoot(http
->tls
, TRUE
);
982 _sspiSetAllowsExpiredCerts(http
->tls
, TRUE
);
984 if (!_sspiConnect(http
->tls
, hostname
))
986 _sspiFree(http
->tls
);
990 http
->status
= HTTP_STATUS_ERROR
;
992 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
993 _("Unable to establish a secure connection to host."), 1);
1004 * 'http_sspi_alloc()' - Allocate SSPI object.
1007 static _http_sspi_t
* /* O - New SSPI/SSL object */
1008 http_sspi_alloc(void)
1010 _http_sspi_t
*conn
= calloc(sizeof(_http_sspi_t
), 1);
1017 * 'http_sspi_client()' - Negotiate a TLS connection as a client.
1020 static int /* O - 0 on success, -1 on failure */
1021 http_sspi_client(http_t
*http
, /* I - Client connection */
1022 const char *hostname
) /* I - Server hostname */
1024 _http_sspi_t
*conn
= http
->tls
; /* SSPI data */
1025 DWORD dwSize
; /* Size for buffer */
1026 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1027 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1028 TimeStamp tsExpiry
; /* Time stamp */
1029 SECURITY_STATUS scRet
; /* Status */
1030 int cbData
; /* Data count */
1031 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1032 SecBuffer inBuffers
[2]; /* Security package buffer */
1033 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1034 SecBuffer outBuffers
[1]; /* Security package buffer */
1035 int ret
= 0; /* Return value */
1036 char username
[1024], /* Current username */
1037 common_name
[1024]; /* CN=username */
1040 DEBUG_printf(("http_sspi_client(http=%p, hostname=\"%s\")", http
, hostname
));
1042 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
|
1043 ISC_REQ_REPLAY_DETECT
|
1044 ISC_REQ_CONFIDENTIALITY
|
1045 ISC_RET_EXTENDED_ERROR
|
1046 ISC_REQ_ALLOCATE_MEMORY
|
1050 * Lookup the client certificate...
1053 dwSize
= sizeof(username
);
1054 GetUserName(username
, &dwSize
);
1055 snprintf(common_name
, sizeof(common_name
), "CN=%s", username
);
1057 if (!http_sspi_credentials(http
, L
"ClientContainer", common_name
, FALSE
))
1059 DEBUG_puts("http_sspi_client: Unable to get client credentials.");
1064 * Initiate a ClientHello message and generate a token.
1067 outBuffers
[0].pvBuffer
= NULL
;
1068 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1069 outBuffers
[0].cbBuffer
= 0;
1071 outBuffer
.cBuffers
= 1;
1072 outBuffer
.pBuffers
= outBuffers
;
1073 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1075 scRet
= InitializeSecurityContext(&conn
->creds
, NULL
, TEXT(""), dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, NULL
, 0, &conn
->context
, &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1077 if (scRet
!= SEC_I_CONTINUE_NEEDED
)
1079 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(1) failed: %s", http_sspi_strerror(conn
, scRet
)));
1084 * Send response to server if there is one.
1087 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1089 if ((cbData
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0)) <= 0)
1091 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1092 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1093 DeleteSecurityContext(&conn
->context
);
1097 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData
));
1099 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1100 outBuffers
[0].pvBuffer
= NULL
;
1103 dwSSPIFlags
= ISC_REQ_MANUAL_CRED_VALIDATION
|
1104 ISC_REQ_SEQUENCE_DETECT
|
1105 ISC_REQ_REPLAY_DETECT
|
1106 ISC_REQ_CONFIDENTIALITY
|
1107 ISC_RET_EXTENDED_ERROR
|
1108 ISC_REQ_ALLOCATE_MEMORY
|
1111 conn
->decryptBufferUsed
= 0;
1114 * Loop until the handshake is finished or an error occurs.
1117 scRet
= SEC_I_CONTINUE_NEEDED
;
1119 while(scRet
== SEC_I_CONTINUE_NEEDED
||
1120 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1121 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1123 if (conn
->decryptBufferUsed
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1125 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1127 BYTE
*temp
; /* New buffer */
1129 if (conn
->decryptBufferLength
>= 262144)
1131 WSASetLastError(E_OUTOFMEMORY
);
1132 DEBUG_puts("http_sspi_client: Decryption buffer too large (>256k)");
1136 if ((temp
= realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
+ 4096)) == NULL
)
1138 DEBUG_printf(("http_sspi_client: Unable to allocate %d byte buffer.", conn
->decryptBufferLength
+ 4096));
1139 WSASetLastError(E_OUTOFMEMORY
);
1143 conn
->decryptBufferLength
+= 4096;
1144 conn
->decryptBuffer
= temp
;
1147 cbData
= recv(http
->fd
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
, (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
1151 DEBUG_printf(("http_sspi_client: recv failed: %d", WSAGetLastError()));
1154 else if (cbData
== 0)
1156 DEBUG_printf(("http_sspi_client: Server unexpectedly disconnected."));
1160 DEBUG_printf(("http_sspi_client: %d bytes of handshake data received", cbData
));
1162 conn
->decryptBufferUsed
+= cbData
;
1166 * Set up the input buffers. Buffer 0 is used to pass in data received from
1167 * the server. Schannel will consume some or all of this. Leftover data
1168 * (if any) will be placed in buffer 1 and given a buffer type of
1172 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
1173 inBuffers
[0].cbBuffer
= (unsigned long)conn
->decryptBufferUsed
;
1174 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1176 inBuffers
[1].pvBuffer
= NULL
;
1177 inBuffers
[1].cbBuffer
= 0;
1178 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1180 inBuffer
.cBuffers
= 2;
1181 inBuffer
.pBuffers
= inBuffers
;
1182 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1185 * Set up the output buffers. These are initialized to NULL so as to make it
1186 * less likely we'll attempt to free random garbage later.
1189 outBuffers
[0].pvBuffer
= NULL
;
1190 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1191 outBuffers
[0].cbBuffer
= 0;
1193 outBuffer
.cBuffers
= 1;
1194 outBuffer
.pBuffers
= outBuffers
;
1195 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1198 * Call InitializeSecurityContext.
1201 scRet
= InitializeSecurityContext(&conn
->creds
, &conn
->context
, NULL
, dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, &inBuffer
, 0, NULL
, &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1204 * If InitializeSecurityContext was successful (or if the error was one of
1205 * the special extended ones), send the contents of the output buffer to the
1209 if (scRet
== SEC_E_OK
||
1210 scRet
== SEC_I_CONTINUE_NEEDED
||
1211 FAILED(scRet
) && (dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
))
1213 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1215 cbData
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1219 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1220 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1221 DeleteSecurityContext(&conn
->context
);
1225 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData
));
1228 * Free output buffer.
1231 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1232 outBuffers
[0].pvBuffer
= NULL
;
1237 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we
1238 * need to read more data from the server and try again.
1241 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1245 * If InitializeSecurityContext returned SEC_E_OK, then the handshake
1246 * completed successfully.
1249 if (scRet
== SEC_E_OK
)
1252 * If the "extra" buffer contains data, this is encrypted application
1253 * protocol layer stuff. It needs to be saved. The application layer will
1254 * later decrypt it with DecryptMessage.
1257 DEBUG_puts("http_sspi_client: Handshake was successful.");
1259 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1261 memmove(conn
->decryptBuffer
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
, inBuffers
[1].cbBuffer
);
1263 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1265 DEBUG_printf(("http_sspi_client: %d bytes of app data was bundled with handshake data", conn
->decryptBufferUsed
));
1268 conn
->decryptBufferUsed
= 0;
1278 * Check for fatal error.
1283 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(2) failed: %s", http_sspi_strerror(conn
, scRet
)));
1289 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1290 * then the server just requested client authentication.
1293 if (scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1299 DEBUG_printf(("http_sspi_client: server requested client credentials."));
1305 * Copy any leftover data from the "extra" buffer, and go around again.
1308 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1310 memmove(conn
->decryptBuffer
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
, inBuffers
[1].cbBuffer
);
1312 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1316 conn
->decryptBufferUsed
= 0;
1323 * Success! Get the server cert
1326 conn
->contextInitialized
= TRUE
;
1328 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_REMOTE_CERT_CONTEXT
, (VOID
*)&(conn
->remoteCert
));
1330 if (scRet
!= SEC_E_OK
)
1332 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %s", http_sspi_strerror(conn
, scRet
)));
1337 /* TODO: Move this out for opt-in server cert validation, like other platforms. */
1338 scRet
= http_sspi_verify(conn
->remoteCert
, hostname
, conn
->certFlags
);
1340 if (scRet
!= SEC_E_OK
)
1342 DEBUG_printf(("http_sspi_client: sspi_verify_certificate failed: %s", http_sspi_strerror(conn
, scRet
)));
1349 * Find out how big the header/trailer will be:
1352 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
1354 if (scRet
!= SEC_E_OK
)
1356 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %s", http_sspi_strerror(conn
, scRet
)));
1366 * 'http_sspi_credentials()' - Retrieve a TLS certificate from the system store.
1369 static BOOL
/* O - 1 on success, 0 on failure */
1370 http_sspi_credentials(
1371 http_t
*http
, /* I - HTTP connection */
1372 const LPWSTR container
, /* I - Cert container name */
1373 const char *common_name
, /* I - Common name of certificate */
1374 BOOL isServer
) /* I - Is caller a server? */
1376 _http_sspi_t
*conn
= http
->tls
; /* SSPI data */
1377 HCERTSTORE store
= NULL
; /* Certificate store */
1378 PCCERT_CONTEXT storedContext
= NULL
; /* Context created from the store */
1379 PCCERT_CONTEXT createdContext
= NULL
; /* Context created by us */
1380 DWORD dwSize
= 0; /* 32 bit size */
1381 PBYTE p
= NULL
; /* Temporary storage */
1382 HCRYPTPROV hProv
= (HCRYPTPROV
)NULL
;
1383 /* Handle to a CSP */
1384 CERT_NAME_BLOB sib
; /* Arbitrary array of bytes */
1385 SCHANNEL_CRED SchannelCred
; /* Schannel credential data */
1386 TimeStamp tsExpiry
; /* Time stamp */
1387 SECURITY_STATUS Status
; /* Status */
1388 HCRYPTKEY hKey
= (HCRYPTKEY
)NULL
; /* Handle to crypto key */
1389 CRYPT_KEY_PROV_INFO kpi
; /* Key container info */
1390 SYSTEMTIME et
; /* System time */
1391 CERT_EXTENSIONS exts
; /* Array of cert extensions */
1392 CRYPT_KEY_PROV_INFO ckp
; /* Handle to crypto key */
1393 BOOL ok
= TRUE
; /* Return value */
1396 if (!CryptAcquireContextW(&hProv
, (LPWSTR
)container
, MS_DEF_PROV_W
, PROV_RSA_FULL
, CRYPT_NEWKEYSET
| CRYPT_MACHINE_KEYSET
))
1398 if (GetLastError() == NTE_EXISTS
)
1400 if (!CryptAcquireContextW(&hProv
, (LPWSTR
)container
, MS_DEF_PROV_W
, PROV_RSA_FULL
, CRYPT_MACHINE_KEYSET
))
1402 DEBUG_printf(("http_sspi_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(conn
, GetLastError())));
1409 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");
1413 DEBUG_printf(("http_sspi_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(conn
, GetLastError())));
1420 if (!CertStrToName(X509_ASN_ENCODING
, common_name
, CERT_OID_NAME_STR
, NULL
, NULL
, &dwSize
, NULL
))
1422 DEBUG_printf(("http_sspi_credentials: CertStrToName failed: %s", http_sspi_strerror(conn
, GetLastError())));
1427 p
= (PBYTE
)malloc(dwSize
);
1431 DEBUG_printf(("http_sspi_credentials: malloc failed for %d bytes", dwSize
));
1436 if (!CertStrToName(X509_ASN_ENCODING
, common_name
, CERT_OID_NAME_STR
, NULL
, p
, &dwSize
, NULL
))
1438 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %s", http_sspi_strerror(conn
, GetLastError())));
1443 sib
.cbData
= dwSize
;
1446 storedContext
= CertFindCertificateInStore(store
, X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
, 0, CERT_FIND_SUBJECT_NAME
, &sib
, NULL
);
1451 * If we couldn't find the context, then we'll create a new one...
1454 if (!CryptGenKey(hProv
, AT_KEYEXCHANGE
, CRYPT_EXPORTABLE
, &hKey
))
1456 DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %s", http_sspi_strerror(conn
, GetLastError())));
1461 ZeroMemory(&kpi
, sizeof(kpi
));
1462 kpi
.pwszContainerName
= (LPWSTR
)container
;
1463 kpi
.pwszProvName
= MS_DEF_PROV_W
;
1464 kpi
.dwProvType
= PROV_RSA_FULL
;
1465 kpi
.dwFlags
= CERT_SET_KEY_CONTEXT_PROP_ID
;
1466 kpi
.dwKeySpec
= AT_KEYEXCHANGE
;
1471 ZeroMemory(&exts
, sizeof(exts
));
1473 createdContext
= CertCreateSelfSignCertificate(hProv
, &sib
, 0, &kpi
, NULL
, NULL
, &et
, &exts
);
1475 if (!createdContext
)
1477 DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %s", http_sspi_strerror(conn
, GetLastError())));
1482 if (!CertAddCertificateContextToStore(store
, createdContext
, CERT_STORE_ADD_REPLACE_EXISTING
, &storedContext
))
1484 DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(conn
, GetLastError())));
1489 ZeroMemory(&ckp
, sizeof(ckp
));
1490 ckp
.pwszContainerName
= (LPWSTR
) container
;
1491 ckp
.pwszProvName
= MS_DEF_PROV_W
;
1492 ckp
.dwProvType
= PROV_RSA_FULL
;
1493 ckp
.dwFlags
= CRYPT_MACHINE_KEYSET
;
1494 ckp
.dwKeySpec
= AT_KEYEXCHANGE
;
1496 if (!CertSetCertificateContextProperty(storedContext
, CERT_KEY_PROV_INFO_PROP_ID
, 0, &ckp
))
1498 DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(conn
, GetLastError())));
1504 ZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
1506 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
1507 SchannelCred
.cCreds
= 1;
1508 SchannelCred
.paCred
= &storedContext
;
1511 * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
1515 SchannelCred
.grbitEnabledProtocols
= SP_PROT_SSL3TLS1
;
1518 * Create an SSPI credential.
1521 Status
= AcquireCredentialsHandle(NULL
, UNISP_NAME
, isServer
? SECPKG_CRED_INBOUND
: SECPKG_CRED_OUTBOUND
, NULL
, &SchannelCred
, NULL
, NULL
, &conn
->creds
, &tsExpiry
);
1522 if (Status
!= SEC_E_OK
)
1524 DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(conn
, Status
)));
1536 CryptDestroyKey(hKey
);
1539 CertFreeCertificateContext(createdContext
);
1542 CertFreeCertificateContext(storedContext
);
1548 CertCloseStore(store
, 0);
1551 CryptReleaseContext(hProv
, 0);
1558 * 'http_sspi_free()' - Close a connection and free resources.
1562 http_sspi_free(_http_sspi_t
*conn
) /* I - SSPI data */
1567 if (conn
->contextInitialized
)
1568 DeleteSecurityContext(&conn
->context
);
1570 if (conn
->decryptBuffer
)
1571 free(conn
->decryptBuffer
);
1573 if (conn
->readBuffer
)
1574 free(conn
->readBuffer
);
1576 if (conn
->writeBuffer
)
1577 free(conn
->writeBuffer
);
1579 if (conn
->localCert
)
1580 CertFreeCertificateContext(conn
->localCert
);
1582 if (conn
->remoteCert
)
1583 CertFreeCertificateContext(conn
->remoteCert
);
1590 * 'http_sspi_server()' - Negotiate a TLS connection as a server.
1593 static int /* O - 0 on success, -1 on failure */
1594 http_sspi_server(http_t
*http
, /* I - HTTP connection */
1595 const char *hostname
) /* I - Hostname of server */
1597 _http_sspi_t
*conn
= http
->tls
; /* I - SSPI data */
1598 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1599 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1600 TimeStamp tsExpiry
; /* Time stamp */
1601 SECURITY_STATUS scRet
; /* SSPI Status */
1602 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1603 SecBuffer inBuffers
[2]; /* Security package buffer */
1604 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1605 SecBuffer outBuffers
[1]; /* Security package buffer */
1606 int num
= 0; /* 32 bit status value */
1607 BOOL fInitContext
= TRUE
; /* Has the context been init'd? */
1608 int ret
= 0; /* Return value */
1611 DEBUG_printf(("http_sspi_server(http=%p, hostname=\"%s\")", http
, hostname
));
1613 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
1614 ASC_REQ_REPLAY_DETECT
|
1615 ASC_REQ_CONFIDENTIALITY
|
1616 ASC_REQ_EXTENDED_ERROR
|
1617 ASC_REQ_ALLOCATE_MEMORY
|
1620 conn
->decryptBufferUsed
= 0;
1623 * Set OutBuffer for AcceptSecurityContext call
1626 outBuffer
.cBuffers
= 1;
1627 outBuffer
.pBuffers
= outBuffers
;
1628 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1630 scRet
= SEC_I_CONTINUE_NEEDED
;
1632 while (scRet
== SEC_I_CONTINUE_NEEDED
||
1633 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1634 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1636 if (conn
->decryptBufferUsed
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1638 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1640 BYTE
*temp
; /* New buffer */
1642 if (conn
->decryptBufferLength
>= 262144)
1644 WSASetLastError(E_OUTOFMEMORY
);
1645 DEBUG_puts("http_sspi_server: Decryption buffer too large (>256k)");
1649 if ((temp
= realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
+ 4096)) == NULL
)
1651 DEBUG_printf(("http_sspi_server: Unable to allocate %d byte buffer.", conn
->decryptBufferLength
+ 4096));
1652 WSASetLastError(E_OUTOFMEMORY
);
1656 conn
->decryptBufferLength
+= 4096;
1657 conn
->decryptBuffer
= temp
;
1662 num
= recv(http
->fd
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
, (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
1664 if (num
== -1 && WSAGetLastError() == WSAEWOULDBLOCK
)
1672 DEBUG_printf(("http_sspi_server: recv failed: %d", WSAGetLastError()));
1677 DEBUG_puts("http_sspi_server: client disconnected");
1681 DEBUG_printf(("http_sspi_server: received %d (handshake) bytes from client.", num
));
1682 conn
->decryptBufferUsed
+= num
;
1686 * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process
1687 * on this run around the loop.
1690 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
1691 inBuffers
[0].cbBuffer
= (unsigned long)conn
->decryptBufferUsed
;
1692 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1694 inBuffers
[1].pvBuffer
= NULL
;
1695 inBuffers
[1].cbBuffer
= 0;
1696 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1698 inBuffer
.cBuffers
= 2;
1699 inBuffer
.pBuffers
= inBuffers
;
1700 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1703 * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to
1704 * free random garbage at the quit.
1707 outBuffers
[0].pvBuffer
= NULL
;
1708 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1709 outBuffers
[0].cbBuffer
= 0;
1711 scRet
= AcceptSecurityContext(&conn
->creds
, (fInitContext
?NULL
:&conn
->context
), &inBuffer
, dwSSPIFlags
, SECURITY_NATIVE_DREP
, (fInitContext
?&conn
->context
:NULL
), &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1713 fInitContext
= FALSE
;
1715 if (scRet
== SEC_E_OK
||
1716 scRet
== SEC_I_CONTINUE_NEEDED
||
1717 (FAILED(scRet
) && ((dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
) != 0)))
1719 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1722 * Send response to server if there is one.
1725 num
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1729 DEBUG_printf(("http_sspi_server: handshake send failed: %d", WSAGetLastError()));
1733 DEBUG_printf(("http_sspi_server: sent %d handshake bytes to client.", outBuffers
[0].cbBuffer
));
1735 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1736 outBuffers
[0].pvBuffer
= NULL
;
1740 if (scRet
== SEC_E_OK
)
1743 * If there's extra data then save it for next time we go to decrypt.
1746 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1748 memcpy(conn
->decryptBuffer
, (LPBYTE
)(conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
), inBuffers
[1].cbBuffer
);
1749 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1753 conn
->decryptBufferUsed
= 0;
1757 else if (FAILED(scRet
) && scRet
!= SEC_E_INCOMPLETE_MESSAGE
)
1759 DEBUG_printf(("http_sspi_server: AcceptSecurityContext failed: %s", http_sspi_strerror(conn
, scRet
)));
1764 if (scRet
!= SEC_E_INCOMPLETE_MESSAGE
&&
1765 scRet
!= SEC_I_INCOMPLETE_CREDENTIALS
)
1767 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1769 memcpy(conn
->decryptBuffer
, (LPBYTE
)(conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
), inBuffers
[1].cbBuffer
);
1770 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1774 conn
->decryptBufferUsed
= 0;
1781 conn
->contextInitialized
= TRUE
;
1784 * Find out how big the header will be:
1787 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
1789 if (scRet
!= SEC_E_OK
)
1791 DEBUG_printf(("http_sspi_server: QueryContextAttributes failed: %s", http_sspi_strerror(conn
, scRet
)));
1801 * 'http_sspi_strerror()' - Return a string for the specified error code.
1804 static const char * /* O - String for error */
1805 http_sspi_strerror(_http_sspi_t
*conn
, /* I - SSPI data */
1806 DWORD code
) /* I - Error code */
1808 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
, NULL
, code
, 0, conn
->error
, sizeof(conn
->error
), NULL
))
1811 * Strip trailing CR + LF...
1814 char *ptr
; /* Pointer into error message */
1816 for (ptr
= conn
->error
+ strlen(conn
->error
) - 1; ptr
>= conn
->error
; ptr
--)
1817 if (*ptr
== '\n' || *ptr
== '\r')
1823 snprintf(conn
->error
, sizeof(conn
->error
), "Unknown error %x", code
);
1825 return (conn
->error
);
1831 * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
1834 _sspiSetAllowsAnyRoot(_http_sspi_t
*conn
,
1837 /* I - Allow any root */
1839 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_UNKNOWN_CA
:
1840 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_UNKNOWN_CA
;
1845 * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
1848 _sspiSetAllowsExpiredCerts(_http_sspi_t
*conn
,
1851 /* I - Allow expired certs */
1853 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
:
1854 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
1859 * 'http_sspi_verify()' - Verify a certificate.
1862 static DWORD
/* O - Error code (0 == No error) */
1864 PCCERT_CONTEXT cert
, /* I - Server certificate */
1865 const char *common_name
, /* I - Common name */
1866 DWORD dwCertFlags
) /* I - Verification flags */
1868 HTTPSPolicyCallbackData httpsPolicy
; /* HTTPS Policy Struct */
1869 CERT_CHAIN_POLICY_PARA policyPara
; /* Cert chain policy parameters */
1870 CERT_CHAIN_POLICY_STATUS policyStatus
;/* Cert chain policy status */
1871 CERT_CHAIN_PARA chainPara
; /* Used for searching and matching criteria */
1872 PCCERT_CHAIN_CONTEXT chainContext
= NULL
;
1873 /* Certificate chain */
1874 PWSTR commonNameUnicode
= NULL
;
1875 /* Unicode common name */
1876 LPSTR rgszUsages
[] = { szOID_PKIX_KP_SERVER_AUTH
,
1877 szOID_SERVER_GATED_CRYPTO
,
1878 szOID_SGC_NETSCAPE
};
1879 /* How are we using this certificate? */
1880 DWORD cUsages
= sizeof(rgszUsages
) / sizeof(LPSTR
);
1881 /* Number of ites in rgszUsages */
1882 DWORD count
; /* 32 bit count variable */
1883 DWORD status
; /* Return value */
1887 return (SEC_E_WRONG_PRINCIPAL
);
1890 * Convert common name to Unicode.
1893 if (!common_name
|| !*common_name
)
1894 return (SEC_E_WRONG_PRINCIPAL
);
1896 count
= MultiByteToWideChar(CP_ACP
, 0, common_name
, -1, NULL
, 0);
1897 commonNameUnicode
= LocalAlloc(LMEM_FIXED
, count
* sizeof(WCHAR
));
1898 if (!commonNameUnicode
)
1899 return (SEC_E_INSUFFICIENT_MEMORY
);
1901 if (!MultiByteToWideChar(CP_ACP
, 0, common_name
, -1, commonNameUnicode
, count
))
1903 LocalFree(commonNameUnicode
);
1904 return (SEC_E_WRONG_PRINCIPAL
);
1908 * Build certificate chain.
1911 ZeroMemory(&chainPara
, sizeof(chainPara
));
1913 chainPara
.cbSize
= sizeof(chainPara
);
1914 chainPara
.RequestedUsage
.dwType
= USAGE_MATCH_TYPE_OR
;
1915 chainPara
.RequestedUsage
.Usage
.cUsageIdentifier
= cUsages
;
1916 chainPara
.RequestedUsage
.Usage
.rgpszUsageIdentifier
= rgszUsages
;
1918 if (!CertGetCertificateChain(NULL
, cert
, NULL
, cert
->hCertStore
, &chainPara
, 0, NULL
, &chainContext
))
1920 status
= GetLastError();
1921 DEBUG_printf(("CertGetCertificateChain returned: %s", http_sspi_strerror(conn
, status
)));
1923 LocalFree(commonNameUnicode
);
1928 * Validate certificate chain.
1931 ZeroMemory(&httpsPolicy
, sizeof(HTTPSPolicyCallbackData
));
1932 httpsPolicy
.cbStruct
= sizeof(HTTPSPolicyCallbackData
);
1933 httpsPolicy
.dwAuthType
= AUTHTYPE_SERVER
;
1934 httpsPolicy
.fdwChecks
= dwCertFlags
;
1935 httpsPolicy
.pwszServerName
= commonNameUnicode
;
1937 memset(&policyPara
, 0, sizeof(policyPara
));
1938 policyPara
.cbSize
= sizeof(policyPara
);
1939 policyPara
.pvExtraPolicyPara
= &httpsPolicy
;
1941 memset(&policyStatus
, 0, sizeof(policyStatus
));
1942 policyStatus
.cbSize
= sizeof(policyStatus
);
1944 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL
, chainContext
, &policyPara
, &policyStatus
))
1946 status
= GetLastError();
1947 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status
));
1949 else if (policyStatus
.dwError
)
1950 status
= policyStatus
.dwError
;
1955 CertFreeCertificateChain(chainContext
);
1957 if (commonNameUnicode
)
1958 LocalFree(commonNameUnicode
);