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 DWORD
http_sspi_verify(PCCERT_CONTEXT cert
, const char *common_name
, DWORD dwCertFlags
);
60 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
65 int /* O - 1 on success, 0 on failure */
66 cupsMakeServerCredentials(
67 const char *path
, /* I - Keychain path or @code NULL@ for default */
68 const char *common_name
, /* I - Common name */
69 int num_alt_names
, /* I - Number of subject alternate names */
70 const char **alt_names
, /* I - Subject Alternate Names */
71 time_t expiration_date
) /* I - Expiration date */
73 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
));
79 (void)expiration_date
;
86 * 'cupsSetServerCredentials()' - Set the default server credentials.
88 * Note: The server credentials are used by all threads in the running process.
89 * This function is threadsafe.
94 int /* O - 1 on success, 0 on failure */
95 cupsSetServerCredentials(
96 const char *path
, /* I - Keychain path or @code NULL@ for default */
97 const char *common_name
, /* I - Default common name for server */
98 int auto_create
) /* I - 1 = automatically create self-signed certificates */
100 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path
, common_name
, auto_create
));
111 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
112 * an encrypted connection.
114 * @since CUPS 1.5/OS X 10.7@
117 int /* O - Status of call (0 = success) */
119 http_t
*http
, /* I - Connection to server */
120 cups_array_t
**credentials
) /* O - Array of credentials */
122 DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http
, credentials
));
134 * '_httpCreateCredentials()' - Create credentials in the internal format.
137 http_tls_credentials_t
/* O - Internal credentials */
138 _httpCreateCredentials(
139 cups_array_t
*credentials
) /* I - Array of credentials */
148 * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
153 int /* O - 1 if valid, 0 otherwise */
154 httpCredentialsAreValidForName(
155 cups_array_t
*credentials
, /* I - Credentials */
156 const char *common_name
) /* I - Name to check */
166 * 'httpCredentialsGetTrust()' - Return the trust of credentials.
171 http_trust_t
/* O - Level of trust */
172 httpCredentialsGetTrust(
173 cups_array_t
*credentials
, /* I - Credentials */
174 const char *common_name
) /* I - Common name for trust lookup */
176 http_trust_t trust
= HTTP_TRUST_OK
;
178 cups_array_t
*tcreds
= NULL
; /* Trusted credentials */
179 _cups_globals_t
*cg
= _cupsGlobals();
180 /* Per-thread globals */
184 return (HTTP_TRUST_UNKNOWN
);
187 * Look this common name up in the default keychains...
190 httpLoadCredentials(NULL
, &tcreds
, common_name
);
194 char credentials_str
[1024], /* String for incoming credentials */
195 tcreds_str
[1024]; /* String for saved credentials */
197 httpCredentialsString(credentials
, credentials_str
, sizeof(credentials_str
));
198 httpCredentialsString(tcreds
, tcreds_str
, sizeof(tcreds_str
));
200 if (strcmp(credentials_str
, tcreds_str
))
203 * Credentials don't match, let's look at the expiration date of the new
204 * credentials and allow if the new ones have a later expiration...
207 if (httpCredentialsGetExpiration(credentials
) <= httpCredentialsGetExpiration(tcreds
) ||
208 !httpCredentialsAreValidForName(credentials
, common_name
))
211 * Either the new credentials are not newly issued, or the common name
212 * does not match the issued certificate...
215 trust
= HTTP_TRUST_INVALID
;
217 else if (httpCredentialsGetExpiration(tcreds
) < time(NULL
))
220 * Save the renewed credentials...
223 trust
= HTTP_TRUST_RENEWED
;
225 httpSaveCredentials(NULL
, credentials
, common_name
);
229 httpFreeCredentials(tcreds
);
231 else if (cg
->validate_certs
&& !httpCredentialsAreValidForName(credentials
, common_name
))
232 trust
= HTTP_TRUST_INVALID
;
234 if (!cg
->expired_certs
&& time(NULL
) > httpCredentialsGetExpiration(credentials
))
235 trust
= HTTP_TRUST_EXPIRED
;
236 else if (!cg
->any_root
&& cupsArrayCount(credentials
) == 1)
237 trust
= HTTP_TRUST_INVALID
;
244 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
249 time_t /* O - Expiration date of credentials */
250 httpCredentialsGetExpiration(
251 cups_array_t
*credentials
) /* I - Credentials */
260 * 'httpCredentialsString()' - Return a string representing the credentials.
265 size_t /* O - Total size of credentials string */
266 httpCredentialsString(
267 cups_array_t
*credentials
, /* I - Credentials */
268 char *buffer
, /* I - Buffer or @code NULL@ */
269 size_t bufsize
) /* I - Size of buffer */
271 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT
")", credentials
, buffer
, CUPS_LLCAST bufsize
));
276 if (buffer
&& bufsize
> 0)
280 http_credential_t
*first
; /* First certificate */
281 SecCertificateRef secCert
; /* Certificate reference */
284 if ((first
= (http_credential_t
*)cupsArrayFirst(credentials
)) != NULL
&&
285 (secCert
= http_cdsa_create_credential(first
)) != NULL
)
287 CFStringRef cf_name
; /* CF common name string */
288 char name
[256]; /* Common name associated with cert */
289 time_t expiration
; /* Expiration date of cert */
290 _cups_md5_state_t md5_state
; /* MD5 state */
291 unsigned char md5_digest
[16]; /* MD5 result */
293 if ((cf_name
= SecCertificateCopySubjectSummary(secCert
)) != NULL
)
295 CFStringGetCString(cf_name
, name
, (CFIndex
)sizeof(name
), kCFStringEncodingUTF8
);
299 strlcpy(name
, "unknown", sizeof(name
));
301 expiration
= (time_t)(SecCertificateNotValidAfter(secCert
) + kCFAbsoluteTimeIntervalSince1970
);
303 _cupsMD5Init(&md5_state
);
304 _cupsMD5Append(&md5_state
, first
->data
, (int)first
->datalen
);
305 _cupsMD5Finish(&md5_state
, md5_digest
);
307 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]);
313 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer
));
315 return (strlen(buffer
));
320 * '_httpFreeCredentials()' - Free internal credentials.
324 _httpFreeCredentials(
325 http_tls_credentials_t credentials
) /* I - Internal credentials */
333 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
338 int /* O - 0 on success, -1 on error */
340 const char *path
, /* I - Keychain path or @code NULL@ for default */
341 cups_array_t
**credentials
, /* IO - Credentials */
342 const char *common_name
) /* I - Common name for credentials */
344 DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path
, credentials
, common_name
));
352 DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials
? 0 : -1));
354 return (*credentials
? 0 : -1);
359 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
364 int /* O - -1 on error, 0 on success */
366 const char *path
, /* I - Keychain path or @code NULL@ for default */
367 cups_array_t
*credentials
, /* I - Credentials */
368 const char *common_name
) /* I - Common name for credentials */
370 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path
, credentials
, common_name
));
376 DEBUG_printf(("1httpSaveCredentials: Returning %d.", -1));
382 * '_httpTLSInitialize()' - Initialize the TLS stack.
386 _httpTLSInitialize(void)
395 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
398 size_t /* O - Bytes available */
399 _httpTLSPending(http_t
*http
) /* I - HTTP connection */
402 return (http
->tls
->readBufferUsed
);
409 * '_httpTLSRead()' - Read from a SSL/TLS connection.
412 int /* O - Bytes read */
413 _httpTLSRead(http_t
*http
, /* I - HTTP connection */
414 char *buf
, /* I - Buffer to store data */
415 int len
) /* I - Length of buffer */
417 int i
; /* Looping var */
418 _http_sspi_t
*conn
= http
->tls
; /* SSPI data */
419 SecBufferDesc message
; /* Array of SecBuffer struct */
420 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
421 int num
= 0; /* Return value */
422 PSecBuffer pDataBuffer
; /* Data buffer */
423 PSecBuffer pExtraBuffer
; /* Excess data buffer */
424 SECURITY_STATUS scRet
; /* SSPI status */
427 DEBUG_printf(("4_httpTLSRead(http=%p, buf=%p, len=%d)", http
, buf
, len
));
431 DEBUG_puts("5_httpTLSRead: No TLS context, returning -1.");
432 WSASetLastError(WSAEINVAL
);
437 * If there are bytes that have already been decrypted and have not yet been
438 * read, return those...
441 if (conn
->readBufferUsed
> 0)
443 int bytesToCopy
= min(conn
->readBufferUsed
, len
);
444 /* Number of bytes to copy */
446 memcpy(buf
, conn
->readBuffer
, bytesToCopy
);
447 conn
->readBufferUsed
-= bytesToCopy
;
449 if (conn
->readBufferUsed
> 0)
450 memmove(conn
->readBuffer
, conn
->readBuffer
+ bytesToCopy
, conn
->readBufferUsed
);
452 DEBUG_printf(("5_httpTLSRead: Returning %d bytes previously decrypted.", bytesToCopy
));
454 return (bytesToCopy
);
458 * Initialize security buffer structs
461 message
.ulVersion
= SECBUFFER_VERSION
;
462 message
.cBuffers
= 4;
463 message
.pBuffers
= buffers
;
468 * If there is not enough space in the buffer, then increase its size...
471 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
473 BYTE
*temp
; /* New buffer */
475 if (conn
->decryptBufferLength
>= 262144)
477 WSASetLastError(E_OUTOFMEMORY
);
478 DEBUG_puts("_httpTLSRead: Decryption buffer too large (>256k)");
482 if ((temp
= realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
+ 4096)) == NULL
)
484 DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte decryption buffer.", conn
->decryptBufferLength
+ 4096));
485 WSASetLastError(E_OUTOFMEMORY
);
489 conn
->decryptBufferLength
+= 4096;
490 conn
->decryptBuffer
= temp
;
492 DEBUG_printf(("_httpTLSRead: Resized decryption buffer to %d bytes.", conn
->decryptBufferLength
));
495 buffers
[0].pvBuffer
= conn
->decryptBuffer
;
496 buffers
[0].cbBuffer
= (unsigned long)conn
->decryptBufferUsed
;
497 buffers
[0].BufferType
= SECBUFFER_DATA
;
498 buffers
[1].BufferType
= SECBUFFER_EMPTY
;
499 buffers
[2].BufferType
= SECBUFFER_EMPTY
;
500 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
502 DEBUG_printf(("5_httpTLSRead: decryptBufferUsed=%d", conn
->decryptBufferUsed
));
504 scRet
= DecryptMessage(&conn
->context
, &message
, 0, NULL
);
506 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
508 num
= recv(http
->fd
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
, (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
511 DEBUG_printf(("5_httpTLSRead: recv failed: %d", WSAGetLastError()));
516 DEBUG_puts("5_httpTLSRead: Server disconnected.");
520 DEBUG_printf(("5_httpTLSRead: Read %d bytes into decryption buffer.", num
));
522 conn
->decryptBufferUsed
+= num
;
525 while (scRet
== SEC_E_INCOMPLETE_MESSAGE
);
527 if (scRet
== SEC_I_CONTEXT_EXPIRED
)
529 DEBUG_puts("5_httpTLSRead: Context expired.");
530 WSASetLastError(WSAECONNRESET
);
533 else if (scRet
!= SEC_E_OK
)
535 char error
[1024] = "";
536 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
, NULL
, scRet
, 0, error
, sizeof(error
), NULL
);
537 DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %lx (%s)", scRet
, error
));
538 WSASetLastError(WSASYSCALLFAILURE
);
543 * The decryption worked. Now, locate data buffer.
549 for (i
= 1; i
< 4; i
++)
551 if (buffers
[i
].BufferType
== SECBUFFER_DATA
)
552 pDataBuffer
= &buffers
[i
];
553 else if (!pExtraBuffer
&& (buffers
[i
].BufferType
== SECBUFFER_EXTRA
))
554 pExtraBuffer
= &buffers
[i
];
558 * If a data buffer is found, then copy the decrypted bytes to the passed-in
564 int bytesToCopy
= min((int)pDataBuffer
->cbBuffer
, len
);
565 /* Number of bytes to copy into buf */
566 int bytesToSave
= pDataBuffer
->cbBuffer
- bytesToCopy
;
567 /* Number of bytes to save in our read buffer */
570 memcpy(buf
, pDataBuffer
->pvBuffer
, bytesToCopy
);
573 * If there are more decrypted bytes than can be copied to the passed in
574 * buffer, then save them...
579 if ((conn
->readBufferLength
- conn
->readBufferUsed
) < bytesToSave
)
581 BYTE
*temp
; /* New buffer pointer */
583 if ((temp
= realloc(conn
->readBuffer
, conn
->readBufferUsed
+ bytesToSave
)) == NULL
)
585 DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", conn
->readBufferUsed
+ bytesToSave
));
586 WSASetLastError(E_OUTOFMEMORY
);
590 conn
->readBufferLength
= conn
->readBufferUsed
+ bytesToSave
;
591 conn
->readBuffer
= temp
;
594 memcpy(((BYTE
*)conn
->readBuffer
) + conn
->readBufferUsed
, ((BYTE
*)pDataBuffer
->pvBuffer
) + bytesToCopy
, bytesToSave
);
596 conn
->readBufferUsed
+= bytesToSave
;
599 return (bytesToCopy
);
603 DEBUG_puts("_httpTLSRead: Unable to find data buffer.");
604 WSASetLastError(WSASYSCALLFAILURE
);
609 * If the decryption process left extra bytes, then save those back in
610 * decryptBuffer. They will be processed the next time through the loop.
615 memmove(conn
->decryptBuffer
, pExtraBuffer
->pvBuffer
, pExtraBuffer
->cbBuffer
);
616 conn
->decryptBufferUsed
= pExtraBuffer
->cbBuffer
;
620 conn
->decryptBufferUsed
= 0;
628 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
631 int /* O - 0 on success, -1 on failure */
632 _httpTLSStart(http_t
*http
) /* I - HTTP connection */
634 char hostname
[256], /* Hostname */
635 *hostptr
; /* Pointer into hostname */
638 DEBUG_printf(("7_httpTLSStart(http=%p)", http
));
640 if ((http
->tls
= http_sspi_alloc()) == NULL
)
643 if (http
->mode
== _HTTP_MODE_CLIENT
)
646 * Client: determine hostname...
649 if (httpAddrLocalhost(http
->hostaddr
))
651 strlcpy(hostname
, "localhost", sizeof(hostname
));
656 * Otherwise make sure the hostname we have does not end in a trailing dot.
659 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
660 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
665 return (http_sspi_client(http
, hostname
));
670 * Server: determine hostname to use...
673 if (http
->fields
[HTTP_FIELD_HOST
][0])
676 * Use hostname for TLS upgrade...
679 strlcpy(hostname
, http
->fields
[HTTP_FIELD_HOST
], sizeof(hostname
));
684 * Resolve hostname from connection address...
687 http_addr_t addr
; /* Connection address */
688 socklen_t addrlen
; /* Length of address */
690 addrlen
= sizeof(addr
);
691 if (getsockname(http
->fd
, (struct sockaddr
*)&addr
, &addrlen
))
693 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno
)));
696 else if (httpAddrLocalhost(&addr
))
700 httpAddrLookup(&addr
, hostname
, sizeof(hostname
));
701 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname
));
705 return (http_sspi_server(http
, hostname
));
711 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
715 _httpTLSStop(http_t
*http
) /* I - HTTP connection */
717 _http_sspi_t
*conn
= http
->tls
; /* SSPI data */
720 if (conn
->contextInitialized
&& http
->fd
>= 0)
722 SecBufferDesc message
; /* Array of SecBuffer struct */
723 SecBuffer buffers
[1] = { 0 };
724 /* Security package buffer */
725 DWORD dwType
; /* Type */
726 DWORD status
; /* Status */
729 * Notify schannel that we are about to close the connection.
732 dwType
= SCHANNEL_SHUTDOWN
;
734 buffers
[0].pvBuffer
= &dwType
;
735 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
736 buffers
[0].cbBuffer
= sizeof(dwType
);
738 message
.cBuffers
= 1;
739 message
.pBuffers
= buffers
;
740 message
.ulVersion
= SECBUFFER_VERSION
;
742 status
= ApplyControlToken(&conn
->context
, &message
);
744 if (SUCCEEDED(status
))
746 PBYTE pbMessage
; /* Message buffer */
747 DWORD cbMessage
; /* Message buffer count */
748 DWORD cbData
; /* Data count */
749 DWORD dwSSPIFlags
; /* SSL attributes we requested */
750 DWORD dwSSPIOutFlags
; /* SSL attributes we received */
751 TimeStamp tsExpiry
; /* Time stamp */
753 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
754 ASC_REQ_REPLAY_DETECT
|
755 ASC_REQ_CONFIDENTIALITY
|
756 ASC_REQ_EXTENDED_ERROR
|
757 ASC_REQ_ALLOCATE_MEMORY
|
760 buffers
[0].pvBuffer
= NULL
;
761 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
762 buffers
[0].cbBuffer
= 0;
764 message
.cBuffers
= 1;
765 message
.pBuffers
= buffers
;
766 message
.ulVersion
= SECBUFFER_VERSION
;
768 status
= AcceptSecurityContext(&conn
->creds
, &conn
->context
, NULL
,
769 dwSSPIFlags
, SECURITY_NATIVE_DREP
, NULL
,
770 &message
, &dwSSPIOutFlags
, &tsExpiry
);
772 if (SUCCEEDED(status
))
774 pbMessage
= buffers
[0].pvBuffer
;
775 cbMessage
= buffers
[0].cbBuffer
;
778 * Send the close notify message to the client.
781 if (pbMessage
&& cbMessage
)
783 cbData
= send(http
->fd
, pbMessage
, cbMessage
, 0);
784 if ((cbData
== SOCKET_ERROR
) || (cbData
== 0))
786 status
= WSAGetLastError();
787 DEBUG_printf(("_httpTLSStop: sending close notify failed: %d", status
));
791 FreeContextBuffer(pbMessage
);
797 DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %x", status
));
802 DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %x", status
));
806 http_sspi_free(conn
);
813 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
816 int /* O - Bytes written */
817 _httpTLSWrite(http_t
*http
, /* I - HTTP connection */
818 const char *buf
, /* I - Buffer holding data */
819 int len
) /* I - Length of buffer */
821 _http_sspi_t
*conn
= http
->tls
; /* SSPI data */
822 SecBufferDesc message
; /* Array of SecBuffer struct */
823 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
824 int bufferLen
; /* Buffer length */
825 int bytesLeft
; /* Bytes left to write */
826 const char *bufptr
; /* Pointer into buffer */
827 int num
= 0; /* Return value */
830 bufferLen
= conn
->streamSizes
.cbMaximumMessage
+ conn
->streamSizes
.cbHeader
+ conn
->streamSizes
.cbTrailer
;
832 if (bufferLen
> conn
->writeBufferLength
)
834 BYTE
*temp
; /* New buffer pointer */
836 if ((temp
= (BYTE
*)realloc(conn
->writeBuffer
, bufferLen
)) == NULL
)
838 DEBUG_printf(("_httpTLSWrite: Unable to allocate buffer of %d bytes.", bufferLen
));
839 WSASetLastError(E_OUTOFMEMORY
);
843 conn
->writeBuffer
= temp
;
844 conn
->writeBufferLength
= bufferLen
;
852 int chunk
= min((int)conn
->streamSizes
.cbMaximumMessage
, bytesLeft
);
853 /* Size of data to write */
854 SECURITY_STATUS scRet
; /* SSPI status */
857 * Copy user data into the buffer, starting just past the header...
860 memcpy(conn
->writeBuffer
+ conn
->streamSizes
.cbHeader
, bufptr
, chunk
);
863 * Setup the SSPI buffers
866 message
.ulVersion
= SECBUFFER_VERSION
;
867 message
.cBuffers
= 4;
868 message
.pBuffers
= buffers
;
870 buffers
[0].pvBuffer
= conn
->writeBuffer
;
871 buffers
[0].cbBuffer
= conn
->streamSizes
.cbHeader
;
872 buffers
[0].BufferType
= SECBUFFER_STREAM_HEADER
;
873 buffers
[1].pvBuffer
= conn
->writeBuffer
+ conn
->streamSizes
.cbHeader
;
874 buffers
[1].cbBuffer
= (unsigned long) chunk
;
875 buffers
[1].BufferType
= SECBUFFER_DATA
;
876 buffers
[2].pvBuffer
= conn
->writeBuffer
+ conn
->streamSizes
.cbHeader
+ chunk
;
877 buffers
[2].cbBuffer
= conn
->streamSizes
.cbTrailer
;
878 buffers
[2].BufferType
= SECBUFFER_STREAM_TRAILER
;
879 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
885 scRet
= EncryptMessage(&conn
->context
, 0, &message
, 0);
889 DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %x", scRet
));
890 WSASetLastError(WSASYSCALLFAILURE
);
895 * Send the data. Remember the size of the total data to send is the size
896 * of the header, the size of the data the caller passed in and the size
900 num
= send(http
->fd
, conn
->writeBuffer
, buffers
[0].cbBuffer
+ buffers
[1].cbBuffer
+ buffers
[2].cbBuffer
, 0);
904 DEBUG_printf(("_httpTLSWrite: send failed: %ld", WSAGetLastError()));
918 * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
921 static int /* O - 0 on success, -1 on failure */
922 http_setup_ssl(http_t
*http
) /* I - Connection to server */
924 char hostname
[256], /* Hostname */
925 *hostptr
; /* Pointer into hostname */
927 TCHAR username
[256]; /* Username returned from GetUserName() */
928 TCHAR commonName
[256];/* Common name for certificate */
929 DWORD dwSize
; /* 32 bit size */
932 DEBUG_printf(("7http_setup_ssl(http=%p)", http
));
935 * Get the hostname to use for SSL...
938 if (httpAddrLocalhost(http
->hostaddr
))
940 strlcpy(hostname
, "localhost", sizeof(hostname
));
945 * Otherwise make sure the hostname we have does not end in a trailing dot.
948 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
949 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
954 http
->tls
= http_sspi_alloc();
958 _cupsSetHTTPError(HTTP_STATUS_ERROR
);
962 dwSize
= sizeof(username
) / sizeof(TCHAR
);
963 GetUserName(username
, &dwSize
);
964 _sntprintf_s(commonName
, sizeof(commonName
) / sizeof(TCHAR
),
965 sizeof(commonName
) / sizeof(TCHAR
), TEXT("CN=%s"), username
);
967 if (!_sspiGetCredentials(http
->tls
, L
"ClientContainer",
970 _sspiFree(http
->tls
);
974 http
->status
= HTTP_STATUS_ERROR
;
976 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
977 _("Unable to establish a secure connection to host."), 1);
982 _sspiSetAllowsAnyRoot(http
->tls
, TRUE
);
983 _sspiSetAllowsExpiredCerts(http
->tls
, TRUE
);
985 if (!_sspiConnect(http
->tls
, hostname
))
987 _sspiFree(http
->tls
);
991 http
->status
= HTTP_STATUS_ERROR
;
993 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
994 _("Unable to establish a secure connection to host."), 1);
1005 * 'http_sspi_alloc()' - Allocate SSPI object.
1008 static _http_sspi_t
* /* O - New SSPI/SSL object */
1009 http_sspi_alloc(void)
1011 _http_sspi_t
*conn
= calloc(sizeof(_http_sspi_t
), 1);
1018 * 'http_sspi_client()' - Negotiate a TLS connection as a client.
1021 static int /* O - 0 on success, -1 on failure */
1022 http_sspi_client(http_t
*http
, /* I - Client connection */
1023 const char *hostname
) /* I - Server hostname */
1025 _http_sspi_t
*conn
= http
->tls
; /* SSPI data */
1026 DWORD dwSize
; /* Size for buffer */
1027 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1028 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1029 TimeStamp tsExpiry
; /* Time stamp */
1030 SECURITY_STATUS scRet
; /* Status */
1031 int cbData
; /* Data count */
1032 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1033 SecBuffer inBuffers
[2]; /* Security package buffer */
1034 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1035 SecBuffer outBuffers
[1]; /* Security package buffer */
1036 int ret
= 0; /* Return value */
1037 char username
[1024], /* Current username */
1038 common_name
[1024]; /* CN=username */
1041 DEBUG_printf(("http_sspi_client(http=%p, hostname=\"%s\")", http
, hostname
));
1043 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
|
1044 ISC_REQ_REPLAY_DETECT
|
1045 ISC_REQ_CONFIDENTIALITY
|
1046 ISC_RET_EXTENDED_ERROR
|
1047 ISC_REQ_ALLOCATE_MEMORY
|
1051 * Lookup the client certificate...
1054 dwSize
= sizeof(username
);
1055 GetUserName(username
, &dwSize
);
1056 snprintf(common_name
, sizeof(common_name
), "CN=%s", username
);
1058 if (!http_sspi_credentials(http
, L
"ClientContainer", common_name
, FALSE
))
1060 DEBUG_puts("http_sspi_client: Unable to get client credentials.");
1065 * Initiate a ClientHello message and generate a token.
1068 outBuffers
[0].pvBuffer
= NULL
;
1069 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1070 outBuffers
[0].cbBuffer
= 0;
1072 outBuffer
.cBuffers
= 1;
1073 outBuffer
.pBuffers
= outBuffers
;
1074 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1076 scRet
= InitializeSecurityContext(&conn
->creds
, NULL
, TEXT(""), dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, NULL
, 0, &conn
->context
, &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1078 if (scRet
!= SEC_I_CONTINUE_NEEDED
)
1080 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(1) failed: %x", scRet
));
1085 * Send response to server if there is one.
1088 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1090 if ((cbData
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0)) <= 0)
1092 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1093 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1094 DeleteSecurityContext(&conn
->context
);
1098 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData
));
1100 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1101 outBuffers
[0].pvBuffer
= NULL
;
1104 dwSSPIFlags
= ISC_REQ_MANUAL_CRED_VALIDATION
|
1105 ISC_REQ_SEQUENCE_DETECT
|
1106 ISC_REQ_REPLAY_DETECT
|
1107 ISC_REQ_CONFIDENTIALITY
|
1108 ISC_RET_EXTENDED_ERROR
|
1109 ISC_REQ_ALLOCATE_MEMORY
|
1112 conn
->decryptBufferUsed
= 0;
1115 * Loop until the handshake is finished or an error occurs.
1118 scRet
= SEC_I_CONTINUE_NEEDED
;
1120 while(scRet
== SEC_I_CONTINUE_NEEDED
||
1121 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1122 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1124 if (conn
->decryptBufferUsed
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1126 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1128 BYTE
*temp
; /* New buffer */
1130 if (conn
->decryptBufferLength
>= 262144)
1132 WSASetLastError(E_OUTOFMEMORY
);
1133 DEBUG_puts("http_sspi_client: Decryption buffer too large (>256k)");
1137 if ((temp
= realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
+ 4096)) == NULL
)
1139 DEBUG_printf(("http_sspi_client: Unable to allocate %d byte buffer.", conn
->decryptBufferLength
+ 4096));
1140 WSASetLastError(E_OUTOFMEMORY
);
1144 conn
->decryptBufferLength
+= 4096;
1145 conn
->decryptBuffer
= temp
;
1148 cbData
= recv(http
->fd
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
, (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
1152 DEBUG_printf(("http_sspi_client: recv failed: %d", WSAGetLastError()));
1155 else if (cbData
== 0)
1157 DEBUG_printf(("http_sspi_client: Server unexpectedly disconnected."));
1161 DEBUG_printf(("http_sspi_client: %d bytes of handshake data received", cbData
));
1163 conn
->decryptBufferUsed
+= cbData
;
1167 * Set up the input buffers. Buffer 0 is used to pass in data received from
1168 * the server. Schannel will consume some or all of this. Leftover data
1169 * (if any) will be placed in buffer 1 and given a buffer type of
1173 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
1174 inBuffers
[0].cbBuffer
= (unsigned long)conn
->decryptBufferUsed
;
1175 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1177 inBuffers
[1].pvBuffer
= NULL
;
1178 inBuffers
[1].cbBuffer
= 0;
1179 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1181 inBuffer
.cBuffers
= 2;
1182 inBuffer
.pBuffers
= inBuffers
;
1183 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1186 * Set up the output buffers. These are initialized to NULL so as to make it
1187 * less likely we'll attempt to free random garbage later.
1190 outBuffers
[0].pvBuffer
= NULL
;
1191 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1192 outBuffers
[0].cbBuffer
= 0;
1194 outBuffer
.cBuffers
= 1;
1195 outBuffer
.pBuffers
= outBuffers
;
1196 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1199 * Call InitializeSecurityContext.
1202 scRet
= InitializeSecurityContext(&conn
->creds
, &conn
->context
, NULL
, dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, &inBuffer
, 0, NULL
, &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1205 * If InitializeSecurityContext was successful (or if the error was one of
1206 * the special extended ones), send the contents of the output buffer to the
1210 if (scRet
== SEC_E_OK
||
1211 scRet
== SEC_I_CONTINUE_NEEDED
||
1212 FAILED(scRet
) && (dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
))
1214 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1216 cbData
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1220 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1221 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1222 DeleteSecurityContext(&conn
->context
);
1226 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData
));
1229 * Free output buffer.
1232 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1233 outBuffers
[0].pvBuffer
= NULL
;
1238 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we
1239 * need to read more data from the server and try again.
1242 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1246 * If InitializeSecurityContext returned SEC_E_OK, then the handshake
1247 * completed successfully.
1250 if (scRet
== SEC_E_OK
)
1253 * If the "extra" buffer contains data, this is encrypted application
1254 * protocol layer stuff. It needs to be saved. The application layer will
1255 * later decrypt it with DecryptMessage.
1258 DEBUG_puts("http_sspi_client: Handshake was successful.");
1260 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1262 memmove(conn
->decryptBuffer
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
, inBuffers
[1].cbBuffer
);
1264 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1266 DEBUG_printf(("http_sspi_client: %d bytes of app data was bundled with handshake data", conn
->decryptBufferUsed
));
1269 conn
->decryptBufferUsed
= 0;
1279 * Check for fatal error.
1284 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(2) failed: %x", scRet
));
1290 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1291 * then the server just requested client authentication.
1294 if (scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1300 DEBUG_printf(("http_sspi_client: server requested client credentials."));
1306 * Copy any leftover data from the "extra" buffer, and go around again.
1309 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1311 memmove(conn
->decryptBuffer
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
, inBuffers
[1].cbBuffer
);
1313 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1317 conn
->decryptBufferUsed
= 0;
1324 * Success! Get the server cert
1327 conn
->contextInitialized
= TRUE
;
1329 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_REMOTE_CERT_CONTEXT
, (VOID
*)&(conn
->remoteCert
));
1331 if (scRet
!= SEC_E_OK
)
1333 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet
));
1338 /* TODO: Move this out for opt-in server cert validation, like other platforms. */
1339 scRet
= http_sspi_verify(conn
->remoteCert
, hostname
, conn
->certFlags
);
1341 if (scRet
!= SEC_E_OK
)
1343 DEBUG_printf(("http_sspi_client: sspi_verify_certificate failed: %x", scRet
));
1350 * Find out how big the header/trailer will be:
1353 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
1355 if (scRet
!= SEC_E_OK
)
1357 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet
));
1367 * 'http_sspi_credentials()' - Retrieve a TLS certificate from the system store.
1370 static BOOL
/* O - 1 on success, 0 on failure */
1371 http_sspi_credentials(
1372 http_t
*http
, /* I - HTTP connection */
1373 const LPWSTR container
, /* I - Cert container name */
1374 const char *common_name
, /* I - Common name of certificate */
1375 BOOL isServer
) /* I - Is caller a server? */
1377 _http_sspi_t
*conn
= http
->tls
; /* SSPI data */
1378 HCERTSTORE store
= NULL
; /* Certificate store */
1379 PCCERT_CONTEXT storedContext
= NULL
; /* Context created from the store */
1380 PCCERT_CONTEXT createdContext
= NULL
; /* Context created by us */
1381 DWORD dwSize
= 0; /* 32 bit size */
1382 PBYTE p
= NULL
; /* Temporary storage */
1383 HCRYPTPROV hProv
= (HCRYPTPROV
)NULL
;
1384 /* Handle to a CSP */
1385 CERT_NAME_BLOB sib
; /* Arbitrary array of bytes */
1386 SCHANNEL_CRED SchannelCred
; /* Schannel credential data */
1387 TimeStamp tsExpiry
; /* Time stamp */
1388 SECURITY_STATUS Status
; /* Status */
1389 HCRYPTKEY hKey
= (HCRYPTKEY
)NULL
; /* Handle to crypto key */
1390 CRYPT_KEY_PROV_INFO kpi
; /* Key container info */
1391 SYSTEMTIME et
; /* System time */
1392 CERT_EXTENSIONS exts
; /* Array of cert extensions */
1393 CRYPT_KEY_PROV_INFO ckp
; /* Handle to crypto key */
1394 BOOL ok
= TRUE
; /* Return value */
1397 if (!CryptAcquireContextW(&hProv
, (LPWSTR
)container
, MS_DEF_PROV_W
, PROV_RSA_FULL
, CRYPT_NEWKEYSET
| CRYPT_MACHINE_KEYSET
))
1399 if (GetLastError() == NTE_EXISTS
)
1401 if (!CryptAcquireContextW(&hProv
, (LPWSTR
)container
, MS_DEF_PROV_W
, PROV_RSA_FULL
, CRYPT_MACHINE_KEYSET
))
1403 DEBUG_printf(("http_sspi_credentials: CryptAcquireContext failed: %x", GetLastError()));
1410 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");
1414 DEBUG_printf(("http_sspi_credentials: CertOpenSystemStore failed: %x", GetLastError()));
1421 if (!CertStrToName(X509_ASN_ENCODING
, common_name
, CERT_OID_NAME_STR
, NULL
, NULL
, &dwSize
, NULL
))
1423 DEBUG_printf(("http_sspi_credentials: CertStrToName failed: %x", GetLastError()));
1428 p
= (PBYTE
)malloc(dwSize
);
1432 DEBUG_printf(("http_sspi_credentials: malloc failed for %d bytes", dwSize
));
1437 if (!CertStrToName(X509_ASN_ENCODING
, common_name
, CERT_OID_NAME_STR
, NULL
, p
, &dwSize
, NULL
))
1439 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x", GetLastError()));
1444 sib
.cbData
= dwSize
;
1447 storedContext
= CertFindCertificateInStore(store
, X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
, 0, CERT_FIND_SUBJECT_NAME
, &sib
, NULL
);
1452 * If we couldn't find the context, then we'll create a new one...
1455 if (!CryptGenKey(hProv
, AT_KEYEXCHANGE
, CRYPT_EXPORTABLE
, &hKey
))
1457 DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x", GetLastError()));
1462 ZeroMemory(&kpi
, sizeof(kpi
));
1463 kpi
.pwszContainerName
= (LPWSTR
)container
;
1464 kpi
.pwszProvName
= MS_DEF_PROV_W
;
1465 kpi
.dwProvType
= PROV_RSA_FULL
;
1466 kpi
.dwFlags
= CERT_SET_KEY_CONTEXT_PROP_ID
;
1467 kpi
.dwKeySpec
= AT_KEYEXCHANGE
;
1472 ZeroMemory(&exts
, sizeof(exts
));
1474 createdContext
= CertCreateSelfSignCertificate(hProv
, &sib
, 0, &kpi
, NULL
, NULL
, &et
, &exts
);
1476 if (!createdContext
)
1478 DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x", GetLastError()));
1483 if (!CertAddCertificateContextToStore(store
, createdContext
, CERT_STORE_ADD_REPLACE_EXISTING
, &storedContext
))
1485 DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x", GetLastError()));
1490 ZeroMemory(&ckp
, sizeof(ckp
));
1491 ckp
.pwszContainerName
= (LPWSTR
) container
;
1492 ckp
.pwszProvName
= MS_DEF_PROV_W
;
1493 ckp
.dwProvType
= PROV_RSA_FULL
;
1494 ckp
.dwFlags
= CRYPT_MACHINE_KEYSET
;
1495 ckp
.dwKeySpec
= AT_KEYEXCHANGE
;
1497 if (!CertSetCertificateContextProperty(storedContext
, CERT_KEY_PROV_INFO_PROP_ID
, 0, &ckp
))
1499 DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x", GetLastError()));
1505 ZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
1507 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
1508 SchannelCred
.cCreds
= 1;
1509 SchannelCred
.paCred
= &storedContext
;
1512 * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
1516 SchannelCred
.grbitEnabledProtocols
= SP_PROT_SSL3TLS1
;
1519 * Create an SSPI credential.
1522 Status
= AcquireCredentialsHandle(NULL
, UNISP_NAME
, isServer
? SECPKG_CRED_INBOUND
: SECPKG_CRED_OUTBOUND
, NULL
, &SchannelCred
, NULL
, NULL
, &conn
->creds
, &tsExpiry
);
1523 if (Status
!= SEC_E_OK
)
1525 DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status
));
1537 CryptDestroyKey(hKey
);
1540 CertFreeCertificateContext(createdContext
);
1543 CertFreeCertificateContext(storedContext
);
1549 CertCloseStore(store
, 0);
1552 CryptReleaseContext(hProv
, 0);
1559 * 'http_sspi_free()' - Close a connection and free resources.
1563 http_sspi_free(_http_sspi_t
*conn
) /* I - Client connection */
1568 if (conn
->contextInitialized
)
1569 DeleteSecurityContext(&conn
->context
);
1571 if (conn
->decryptBuffer
)
1572 free(conn
->decryptBuffer
);
1574 if (conn
->readBuffer
)
1575 free(conn
->readBuffer
);
1577 if (conn
->writeBuffer
)
1578 free(conn
->writeBuffer
);
1580 if (conn
->localCert
)
1581 CertFreeCertificateContext(conn
->localCert
);
1583 if (conn
->remoteCert
)
1584 CertFreeCertificateContext(conn
->remoteCert
);
1591 * 'http_sspi_server()' - Negotiate a TLS connection as a server.
1594 static int /* O - 0 on success, -1 on failure */
1595 http_sspi_server(http_t
*http
, /* I - HTTP connection */
1596 const char *hostname
) /* I - Hostname of server */
1598 _http_sspi_t
*conn
= http
->tls
; /* I - SSPI data */
1599 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1600 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1601 TimeStamp tsExpiry
; /* Time stamp */
1602 SECURITY_STATUS scRet
; /* SSPI Status */
1603 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1604 SecBuffer inBuffers
[2]; /* Security package buffer */
1605 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1606 SecBuffer outBuffers
[1]; /* Security package buffer */
1607 int num
= 0; /* 32 bit status value */
1608 BOOL fInitContext
= TRUE
; /* Has the context been init'd? */
1609 int ret
= 0; /* Return value */
1612 DEBUG_printf(("http_sspi_server(http=%p, hostname=\"%s\")", http
, hostname
));
1614 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
1615 ASC_REQ_REPLAY_DETECT
|
1616 ASC_REQ_CONFIDENTIALITY
|
1617 ASC_REQ_EXTENDED_ERROR
|
1618 ASC_REQ_ALLOCATE_MEMORY
|
1621 conn
->decryptBufferUsed
= 0;
1624 * Set OutBuffer for AcceptSecurityContext call
1627 outBuffer
.cBuffers
= 1;
1628 outBuffer
.pBuffers
= outBuffers
;
1629 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1631 scRet
= SEC_I_CONTINUE_NEEDED
;
1633 while (scRet
== SEC_I_CONTINUE_NEEDED
||
1634 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1635 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1637 if (conn
->decryptBufferUsed
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1639 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1641 BYTE
*temp
; /* New buffer */
1643 if (conn
->decryptBufferLength
>= 262144)
1645 WSASetLastError(E_OUTOFMEMORY
);
1646 DEBUG_puts("http_sspi_server: Decryption buffer too large (>256k)");
1650 if ((temp
= realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
+ 4096)) == NULL
)
1652 DEBUG_printf(("http_sspi_server: Unable to allocate %d byte buffer.", conn
->decryptBufferLength
+ 4096));
1653 WSASetLastError(E_OUTOFMEMORY
);
1657 conn
->decryptBufferLength
+= 4096;
1658 conn
->decryptBuffer
= temp
;
1663 num
= recv(http
->fd
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
, (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
1665 if (num
== -1 && WSAGetLastError() == WSAEWOULDBLOCK
)
1673 DEBUG_printf(("http_sspi_server: recv failed: %d", WSAGetLastError()));
1678 DEBUG_puts("http_sspi_server: client disconnected");
1682 DEBUG_printf(("http_sspi_server: received %d (handshake) bytes from client.", num
));
1683 conn
->decryptBufferUsed
+= num
;
1687 * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process
1688 * on this run around the loop.
1691 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
1692 inBuffers
[0].cbBuffer
= (unsigned long)conn
->decryptBufferUsed
;
1693 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1695 inBuffers
[1].pvBuffer
= NULL
;
1696 inBuffers
[1].cbBuffer
= 0;
1697 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1699 inBuffer
.cBuffers
= 2;
1700 inBuffer
.pBuffers
= inBuffers
;
1701 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1704 * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to
1705 * free random garbage at the quit.
1708 outBuffers
[0].pvBuffer
= NULL
;
1709 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1710 outBuffers
[0].cbBuffer
= 0;
1712 scRet
= AcceptSecurityContext(&conn
->creds
, (fInitContext
?NULL
:&conn
->context
), &inBuffer
, dwSSPIFlags
, SECURITY_NATIVE_DREP
, (fInitContext
?&conn
->context
:NULL
), &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1714 fInitContext
= FALSE
;
1716 if (scRet
== SEC_E_OK
||
1717 scRet
== SEC_I_CONTINUE_NEEDED
||
1718 (FAILED(scRet
) && ((dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
) != 0)))
1720 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1723 * Send response to server if there is one.
1726 num
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1730 DEBUG_printf(("http_sspi_server: handshake send failed: %d", WSAGetLastError()));
1734 DEBUG_printf(("http_sspi_server: sent %d handshake bytes to client.", outBuffers
[0].cbBuffer
));
1736 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1737 outBuffers
[0].pvBuffer
= NULL
;
1741 if (scRet
== SEC_E_OK
)
1744 * If there's extra data then save it for next time we go to decrypt.
1747 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1749 memcpy(conn
->decryptBuffer
, (LPBYTE
)(conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
), inBuffers
[1].cbBuffer
);
1750 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1754 conn
->decryptBufferUsed
= 0;
1758 else if (FAILED(scRet
) && scRet
!= SEC_E_INCOMPLETE_MESSAGE
)
1760 DEBUG_printf(("http_sspi_server: AcceptSecurityContext failed: %x", scRet
));
1765 if (scRet
!= SEC_E_INCOMPLETE_MESSAGE
&&
1766 scRet
!= SEC_I_INCOMPLETE_CREDENTIALS
)
1768 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1770 memcpy(conn
->decryptBuffer
, (LPBYTE
)(conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
), inBuffers
[1].cbBuffer
);
1771 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1775 conn
->decryptBufferUsed
= 0;
1782 conn
->contextInitialized
= TRUE
;
1785 * Find out how big the header will be:
1788 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
1790 if (scRet
!= SEC_E_OK
)
1792 DEBUG_printf(("http_sspi_server: QueryContextAttributes failed: %x", scRet
));
1803 * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
1806 _sspiSetAllowsAnyRoot(_http_sspi_t
*conn
,
1807 /* I - Client connection */
1809 /* I - Allow any root */
1811 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_UNKNOWN_CA
:
1812 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_UNKNOWN_CA
;
1817 * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
1820 _sspiSetAllowsExpiredCerts(_http_sspi_t
*conn
,
1821 /* I - Client connection */
1823 /* I - Allow expired certs */
1825 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
:
1826 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
1831 * 'http_sspi_verify()' - Verify a certificate.
1834 static DWORD
/* O - Error code (0 == No error) */
1836 PCCERT_CONTEXT cert
, /* I - Server certificate */
1837 const char *common_name
, /* I - Common name */
1838 DWORD dwCertFlags
) /* I - Verification flags */
1840 HTTPSPolicyCallbackData httpsPolicy
; /* HTTPS Policy Struct */
1841 CERT_CHAIN_POLICY_PARA policyPara
; /* Cert chain policy parameters */
1842 CERT_CHAIN_POLICY_STATUS policyStatus
;/* Cert chain policy status */
1843 CERT_CHAIN_PARA chainPara
; /* Used for searching and matching criteria */
1844 PCCERT_CHAIN_CONTEXT chainContext
= NULL
;
1845 /* Certificate chain */
1846 PWSTR commonNameUnicode
= NULL
;
1847 /* Unicode common name */
1848 LPSTR rgszUsages
[] = { szOID_PKIX_KP_SERVER_AUTH
,
1849 szOID_SERVER_GATED_CRYPTO
,
1850 szOID_SGC_NETSCAPE
};
1851 /* How are we using this certificate? */
1852 DWORD cUsages
= sizeof(rgszUsages
) / sizeof(LPSTR
);
1853 /* Number of ites in rgszUsages */
1854 DWORD count
; /* 32 bit count variable */
1855 DWORD status
; /* Return value */
1859 return (SEC_E_WRONG_PRINCIPAL
);
1862 * Convert common name to Unicode.
1865 if (!common_name
|| !*common_name
)
1866 return (SEC_E_WRONG_PRINCIPAL
);
1868 count
= MultiByteToWideChar(CP_ACP
, 0, common_name
, -1, NULL
, 0);
1869 commonNameUnicode
= LocalAlloc(LMEM_FIXED
, count
* sizeof(WCHAR
));
1870 if (!commonNameUnicode
)
1871 return (SEC_E_INSUFFICIENT_MEMORY
);
1873 if (!MultiByteToWideChar(CP_ACP
, 0, common_name
, -1, commonNameUnicode
, count
))
1875 LocalFree(commonNameUnicode
);
1876 return (SEC_E_WRONG_PRINCIPAL
);
1880 * Build certificate chain.
1883 ZeroMemory(&chainPara
, sizeof(chainPara
));
1885 chainPara
.cbSize
= sizeof(chainPara
);
1886 chainPara
.RequestedUsage
.dwType
= USAGE_MATCH_TYPE_OR
;
1887 chainPara
.RequestedUsage
.Usage
.cUsageIdentifier
= cUsages
;
1888 chainPara
.RequestedUsage
.Usage
.rgpszUsageIdentifier
= rgszUsages
;
1890 if (!CertGetCertificateChain(NULL
, cert
, NULL
, cert
->hCertStore
, &chainPara
, 0, NULL
, &chainContext
))
1892 status
= GetLastError();
1893 DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status
));
1895 LocalFree(commonNameUnicode
);
1900 * Validate certificate chain.
1903 ZeroMemory(&httpsPolicy
, sizeof(HTTPSPolicyCallbackData
));
1904 httpsPolicy
.cbStruct
= sizeof(HTTPSPolicyCallbackData
);
1905 httpsPolicy
.dwAuthType
= AUTHTYPE_SERVER
;
1906 httpsPolicy
.fdwChecks
= dwCertFlags
;
1907 httpsPolicy
.pwszServerName
= commonNameUnicode
;
1909 memset(&policyPara
, 0, sizeof(policyPara
));
1910 policyPara
.cbSize
= sizeof(policyPara
);
1911 policyPara
.pvExtraPolicyPara
= &httpsPolicy
;
1913 memset(&policyStatus
, 0, sizeof(policyStatus
));
1914 policyStatus
.cbSize
= sizeof(policyStatus
);
1916 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL
, chainContext
, &policyPara
, &policyStatus
))
1918 status
= GetLastError();
1919 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status
));
1921 else if (policyStatus
.dwError
)
1922 status
= policyStatus
.dwError
;
1927 CertFreeCertificateChain(chainContext
);
1929 if (commonNameUnicode
)
1930 LocalFree(commonNameUnicode
);