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 void http_sspi_free(_http_sspi_t
*conn
);
52 static BOOL
http_sspi_get_credentials(_http_sspi_t
*conn
, const LPWSTR containerName
, const TCHAR
*commonName
, BOOL server
);
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 */
429 WSASetLastError(WSAEINVAL
);
434 * If there are bytes that have already been decrypted and have not yet been
435 * read, return those...
438 if (conn
->readBufferUsed
> 0)
440 int bytesToCopy
= min(conn
->readBufferUsed
, len
);
441 /* Number of bytes to copy */
443 memcpy(buf
, conn
->readBuffer
, bytesToCopy
);
444 conn
->readBufferUsed
-= bytesToCopy
;
446 if (conn
->readBufferUsed
> 0)
447 memmove(conn
->readBuffer
, conn
->readBuffer
+ bytesToCopy
, conn
->readBufferUsed
);
449 return (bytesToCopy
);
453 * Initialize security buffer structs
456 message
.ulVersion
= SECBUFFER_VERSION
;
457 message
.cBuffers
= 4;
458 message
.pBuffers
= buffers
;
463 * If there is not enough space in the buffer, then increase its size...
466 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
468 BYTE
*temp
; /* New buffer */
470 if (conn
->decryptBufferLength
>= 262144)
472 WSASetLastError(E_OUTOFMEMORY
);
473 DEBUG_puts("_httpTLSRead: Decryption buffer too large (>256k)");
477 if ((temp
= realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
+ 4096)) == NULL
)
479 DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte buffer.", conn
->decryptBufferLength
+ 4096));
480 WSASetLastError(E_OUTOFMEMORY
);
484 conn
->decryptBufferLength
+= 4096;
485 conn
->decryptBuffer
= temp
;
488 buffers
[0].pvBuffer
= conn
->decryptBuffer
;
489 buffers
[0].cbBuffer
= (unsigned long) conn
->decryptBufferUsed
;
490 buffers
[0].BufferType
= SECBUFFER_DATA
;
491 buffers
[1].BufferType
= SECBUFFER_EMPTY
;
492 buffers
[2].BufferType
= SECBUFFER_EMPTY
;
493 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
495 scRet
= DecryptMessage(&conn
->context
, &message
, 0, NULL
);
497 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
499 num
= recv(http
->fd
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
, (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
502 DEBUG_printf(("_httpTLSRead: recv failed: %d", WSAGetLastError()));
507 DEBUG_puts("_httpTLSRead: Server disconnected.");
511 conn
->decryptBufferUsed
+= num
;
514 while (scRet
== SEC_E_INCOMPLETE_MESSAGE
);
516 if (scRet
== SEC_I_CONTEXT_EXPIRED
)
518 DEBUG_puts("_httpTLSRead: Context expired.");
519 WSASetLastError(WSAECONNRESET
);
522 else if (scRet
!= SEC_E_OK
)
524 DEBUG_printf(("_httpTLSRead: DecryptMessage failed: %lx", scRet
));
525 WSASetLastError(WSASYSCALLFAILURE
);
530 * The decryption worked. Now, locate data buffer.
536 for (i
= 1; i
< 4; i
++)
538 if (buffers
[i
].BufferType
== SECBUFFER_DATA
)
539 pDataBuffer
= &buffers
[i
];
540 else if (!pExtraBuffer
&& (buffers
[i
].BufferType
== SECBUFFER_EXTRA
))
541 pExtraBuffer
= &buffers
[i
];
545 * If a data buffer is found, then copy the decrypted bytes to the passed-in
551 int bytesToCopy
= min(pDataBuffer
->cbBuffer
, len
);
552 /* Number of bytes to copy into buf */
553 int bytesToSave
= pDataBuffer
->cbBuffer
- bytesToCopy
;
554 /* Number of bytes to save in our read buffer */
557 memcpy(buf
, pDataBuffer
->pvBuffer
, bytesToCopy
);
560 * If there are more decrypted bytes than can be copied to the passed in
561 * buffer, then save them...
566 if ((conn
->readBufferLength
- conn
->readBufferUsed
) < bytesToSave
)
568 BYTE
*temp
; /* New buffer pointer */
570 if ((temp
= realloc(conn
->readBuffer
, conn
->readBufferUsed
+ bytesToSave
)) == NULL
)
572 DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", conn
->readBufferUsed
+ bytesToSave
));
573 WSASetLastError(E_OUTOFMEMORY
);
577 conn
->readBufferLength
= conn
->readBufferUsed
+ bytesToSave
;
578 conn
->readBuffer
= temp
;
581 memcpy(((BYTE
*)conn
->readBuffer
) + conn
->readBufferUsed
, ((BYTE
*)pDataBuffer
->pvBuffer
) + bytesToCopy
, bytesToSave
);
583 conn
->readBufferUsed
+= bytesToSave
;
586 return (bytesToCopy
);
590 DEBUG_puts("_httpTLSRead: Unable to find data buffer."));
591 WSASetLastError(WSASYSCALLFAILURE
);
596 * If the decryption process left extra bytes, then save those back in
597 * decryptBuffer. They will be processed the next time through the loop.
602 memmove(conn
->decryptBuffer
, pExtraBuffer
->pvBuffer
, pExtraBuffer
->cbBuffer
);
603 conn
->decryptBufferUsed
= pExtraBuffer
->cbBuffer
;
607 conn
->decryptBufferUsed
= 0;
615 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
618 int /* O - 0 on success, -1 on failure */
619 _httpTLSStart(http_t
*http
) /* I - HTTP connection */
621 char hostname
[256], /* Hostname */
622 *hostptr
; /* Pointer into hostname */
625 DEBUG_printf(("7_httpTLSStart(http=%p)", http
));
627 if ((http
->tls
= http_sspi_alloc()) == NULL
)
630 if (http
->mode
== _HTTP_MODE_CLIENT
)
633 * Client: determine hostname...
636 if (httpAddrLocalhost(http
->hostaddr
))
638 strlcpy(hostname
, "localhost", sizeof(hostname
));
643 * Otherwise make sure the hostname we have does not end in a trailing dot.
646 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
647 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
652 return (http_sspi_client(http
, hostname
));
657 * Server: determine hostname to use...
660 if (http
->fields
[HTTP_FIELD_HOST
][0])
663 * Use hostname for TLS upgrade...
666 strlcpy(hostname
, http
->fields
[HTTP_FIELD_HOST
], sizeof(hostname
));
671 * Resolve hostname from connection address...
674 http_addr_t addr
; /* Connection address */
675 socklen_t addrlen
; /* Length of address */
677 addrlen
= sizeof(addr
);
678 if (getsockname(http
->fd
, (struct sockaddr
*)&addr
, &addrlen
))
680 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno
)));
683 else if (httpAddrLocalhost(&addr
))
687 httpAddrLookup(&addr
, hostname
, sizeof(hostname
));
688 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname
));
692 return (http_sspi_server(http
, hostname
));
698 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
702 _httpTLSStop(http_t
*http
) /* I - HTTP connection */
704 _http_sspi_t
*conn
= http
->tls
; /* SSPI data */
707 if (conn
->contextInitialized
&& http
->fd
>= 0)
709 SecBufferDesc message
; /* Array of SecBuffer struct */
710 SecBuffer buffers
[1] = { 0 };
711 /* Security package buffer */
712 DWORD dwType
; /* Type */
713 DWORD status
; /* Status */
716 * Notify schannel that we are about to close the connection.
719 dwType
= SCHANNEL_SHUTDOWN
;
721 buffers
[0].pvBuffer
= &dwType
;
722 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
723 buffers
[0].cbBuffer
= sizeof(dwType
);
725 message
.cBuffers
= 1;
726 message
.pBuffers
= buffers
;
727 message
.ulVersion
= SECBUFFER_VERSION
;
729 status
= ApplyControlToken(&conn
->context
, &message
);
731 if (SUCCEEDED(status
))
733 PBYTE pbMessage
; /* Message buffer */
734 DWORD cbMessage
; /* Message buffer count */
735 DWORD cbData
; /* Data count */
736 DWORD dwSSPIFlags
; /* SSL attributes we requested */
737 DWORD dwSSPIOutFlags
; /* SSL attributes we received */
738 TimeStamp tsExpiry
; /* Time stamp */
740 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
741 ASC_REQ_REPLAY_DETECT
|
742 ASC_REQ_CONFIDENTIALITY
|
743 ASC_REQ_EXTENDED_ERROR
|
744 ASC_REQ_ALLOCATE_MEMORY
|
747 buffers
[0].pvBuffer
= NULL
;
748 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
749 buffers
[0].cbBuffer
= 0;
751 message
.cBuffers
= 1;
752 message
.pBuffers
= buffers
;
753 message
.ulVersion
= SECBUFFER_VERSION
;
755 status
= AcceptSecurityContext(&conn
->creds
, &conn
->context
, NULL
,
756 dwSSPIFlags
, SECURITY_NATIVE_DREP
, NULL
,
757 &message
, &dwSSPIOutFlags
, &tsExpiry
);
759 if (SUCCEEDED(status
))
761 pbMessage
= buffers
[0].pvBuffer
;
762 cbMessage
= buffers
[0].cbBuffer
;
765 * Send the close notify message to the client.
768 if (pbMessage
&& cbMessage
)
770 cbData
= send(http
->fd
, pbMessage
, cbMessage
, 0);
771 if ((cbData
== SOCKET_ERROR
) || (cbData
== 0))
773 status
= WSAGetLastError();
774 DEBUG_printf(("_httpTLSStop: sending close notify failed: %d", status
));
778 FreeContextBuffer(pbMessage
);
784 DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %x", status
));
789 DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %x", status
));
793 http_sspi_free(conn
);
800 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
803 int /* O - Bytes written */
804 _httpTLSWrite(http_t
*http
, /* I - HTTP connection */
805 const char *buf
, /* I - Buffer holding data */
806 int len
) /* I - Length of buffer */
808 _http_sspi_t
*conn
= http
->tls
; /* SSPI data */
809 SecBufferDesc message
; /* Array of SecBuffer struct */
810 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
811 int bufferLen
; /* Buffer length */
812 int bytesLeft
; /* Bytes left to write */
813 const char *bufptr
; /* Pointer into buffer */
814 int num
= 0; /* Return value */
817 bufferLen
= conn
->streamSizes
.cbMaximumMessage
+ conn
->streamSizes
.cbHeader
+ conn
->streamSizes
.cbTrailer
;
819 if (bufferLen
> conn
->writeBufferLen
)
821 BYTE
*temp
; /* New buffer pointer */
823 if ((temp
= (BYTE
*)realloc(conn
->writeBuffer
, bufferLen
)) == NULL
)
825 DEBUG_printf(("_httpTLSWrite: Unable to allocate buffer of %d bytes.", bufferLen
));
826 WSASetLastError(E_OUTOFMEMORY
);
830 conn
->writeBuffer
= temp
;
831 conn
->writeBufferLen
= bufferLen
;
839 int chunk
= min(conn
->streamSizes
.cbMaximumMessage
, bytesLeft
);
840 /* Size of data to write */
841 SECURITY_STATUS scRet
; /* SSPI status */
844 * Copy user data into the buffer, starting just past the header...
847 memcpy(conn
->writeBuffer
+ conn
->streamSizes
.cbHeader
, bufptr
, chunk
);
850 * Setup the SSPI buffers
853 message
.ulVersion
= SECBUFFER_VERSION
;
854 message
.cBuffers
= 4;
855 message
.pBuffers
= buffers
;
857 buffers
[0].pvBuffer
= conn
->writeBuffer
;
858 buffers
[0].cbBuffer
= conn
->streamSizes
.cbHeader
;
859 buffers
[0].BufferType
= SECBUFFER_STREAM_HEADER
;
860 buffers
[1].pvBuffer
= conn
->writeBuffer
+ conn
->streamSizes
.cbHeader
;
861 buffers
[1].cbBuffer
= (unsigned long) chunk
;
862 buffers
[1].BufferType
= SECBUFFER_DATA
;
863 buffers
[2].pvBuffer
= conn
->writeBuffer
+ conn
->streamSizes
.cbHeader
+ chunk
;
864 buffers
[2].cbBuffer
= conn
->streamSizes
.cbTrailer
;
865 buffers
[2].BufferType
= SECBUFFER_STREAM_TRAILER
;
866 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
872 scRet
= EncryptMessage(&conn
->context
, 0, &message
, 0);
876 DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %x", scRet
));
877 WSASetLastError(WSASYSCALLFAILURE
);
882 * Send the data. Remember the size of the total data to send is the size
883 * of the header, the size of the data the caller passed in and the size
887 num
= send(http
->fd
, conn
->writeBuffer
, buffers
[0].cbBuffer
+ buffers
[1].cbBuffer
+ buffers
[2].cbBuffer
, 0);
891 DEBUG_printf(("_httpTLSWrite: send failed: %ld", WSAGetLastError()));
905 * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
908 static int /* O - 0 on success, -1 on failure */
909 http_setup_ssl(http_t
*http
) /* I - Connection to server */
911 char hostname
[256], /* Hostname */
912 *hostptr
; /* Pointer into hostname */
914 TCHAR username
[256]; /* Username returned from GetUserName() */
915 TCHAR commonName
[256];/* Common name for certificate */
916 DWORD dwSize
; /* 32 bit size */
919 DEBUG_printf(("7http_setup_ssl(http=%p)", http
));
922 * Get the hostname to use for SSL...
925 if (httpAddrLocalhost(http
->hostaddr
))
927 strlcpy(hostname
, "localhost", sizeof(hostname
));
932 * Otherwise make sure the hostname we have does not end in a trailing dot.
935 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
936 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
941 http
->tls
= _sspiAlloc();
945 _cupsSetHTTPError(HTTP_STATUS_ERROR
);
949 http
->tls
->sock
= http
->fd
;
950 dwSize
= sizeof(username
) / sizeof(TCHAR
);
951 GetUserName(username
, &dwSize
);
952 _sntprintf_s(commonName
, sizeof(commonName
) / sizeof(TCHAR
),
953 sizeof(commonName
) / sizeof(TCHAR
), TEXT("CN=%s"), username
);
955 if (!_sspiGetCredentials(http
->tls
, L
"ClientContainer",
958 _sspiFree(http
->tls
);
962 http
->status
= HTTP_STATUS_ERROR
;
964 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
965 _("Unable to establish a secure connection to host."), 1);
970 _sspiSetAllowsAnyRoot(http
->tls
, TRUE
);
971 _sspiSetAllowsExpiredCerts(http
->tls
, TRUE
);
973 if (!_sspiConnect(http
->tls
, hostname
))
975 _sspiFree(http
->tls
);
979 http
->status
= HTTP_STATUS_ERROR
;
981 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
982 _("Unable to establish a secure connection to host."), 1);
992 * 'http_sspi_alloc()' - Allocate SSPI object.
995 static _http_sspi_t
* /* O - New SSPI/SSL object */
996 http_sspi_alloc(void)
998 _http_sspi_t
*conn
= calloc(sizeof(_http_sspi_t
), 1);
1005 * 'http_sspi_client()' - Negotiate a TLS connection as a client.
1008 static int /* O - 0 on success, -1 on failure */
1009 http_sspi_client(http_t
*http
, /* I - Client connection */
1010 const char *hostname
) /* I - Server hostname */
1012 _http_sspi_t
*conn
; /* SSPI data */
1013 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1014 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1015 TimeStamp tsExpiry
; /* Time stamp */
1016 SECURITY_STATUS scRet
; /* Status */
1017 int cbData
; /* Data count */
1018 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1019 SecBuffer inBuffers
[2]; /* Security package buffer */
1020 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1021 SecBuffer outBuffers
[1]; /* Security package buffer */
1022 int ret
= 0; /* Return value */
1025 DEBUG_printf(("http_sspi_client(http=%p, hostname=\"%s\")", http
, hostname
));
1028 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
|
1029 ISC_REQ_REPLAY_DETECT
|
1030 ISC_REQ_CONFIDENTIALITY
|
1031 ISC_RET_EXTENDED_ERROR
|
1032 ISC_REQ_ALLOCATE_MEMORY
|
1036 * Initiate a ClientHello message and generate a token.
1039 outBuffers
[0].pvBuffer
= NULL
;
1040 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1041 outBuffers
[0].cbBuffer
= 0;
1043 outBuffer
.cBuffers
= 1;
1044 outBuffer
.pBuffers
= outBuffers
;
1045 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1047 scRet
= InitializeSecurityContext(&conn
->creds
, NULL
, TEXT(""), dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, NULL
, 0, &conn
->context
, &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1049 if (scRet
!= SEC_I_CONTINUE_NEEDED
)
1051 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(1) failed: %x", scRet
));
1056 * Send response to server if there is one.
1059 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1061 if ((cbData
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0)) <= 0)
1063 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1064 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1065 DeleteSecurityContext(&conn
->context
);
1069 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData
));
1071 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1072 outBuffers
[0].pvBuffer
= NULL
;
1075 dwSSPIFlags
= ISC_REQ_MANUAL_CRED_VALIDATION
|
1076 ISC_REQ_SEQUENCE_DETECT
|
1077 ISC_REQ_REPLAY_DETECT
|
1078 ISC_REQ_CONFIDENTIALITY
|
1079 ISC_RET_EXTENDED_ERROR
|
1080 ISC_REQ_ALLOCATE_MEMORY
|
1083 conn
->decryptBufferUsed
= 0;
1086 * Loop until the handshake is finished or an error occurs.
1089 scRet
= SEC_I_CONTINUE_NEEDED
;
1091 while(scRet
== SEC_I_CONTINUE_NEEDED
||
1092 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1093 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1095 if (conn
->decryptBufferUsed
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1097 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1099 BYTE
*temp
; /* New buffer */
1101 if (conn
->decryptBufferLength
>= 262144)
1103 WSASetLastError(E_OUTOFMEMORY
);
1104 DEBUG_puts("http_sspi_client: Decryption buffer too large (>256k)");
1108 if ((temp
= realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
+ 4096)) == NULL
)
1110 DEBUG_printf(("http_sspi_client: Unable to allocate %d byte buffer.", conn
->decryptBufferLength
+ 4096));
1111 WSASetLastError(E_OUTOFMEMORY
);
1115 conn
->decryptBufferLength
+= 4096;
1116 conn
->decryptBuffer
= temp
;
1119 cbData
= recv(http
->fd
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
, (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
1123 DEBUG_printf(("http_sspi_client: recv failed: %d", WSAGetLastError()));
1126 else if (cbData
== 0)
1128 DEBUG_printf(("http_sspi_client: Server unexpectedly disconnected."));
1132 DEBUG_printf(("http_sspi_client: %d bytes of handshake data received", cbData
));
1134 conn
->decryptBufferUsed
+= cbData
;
1138 * Set up the input buffers. Buffer 0 is used to pass in data received from
1139 * the server. Schannel will consume some or all of this. Leftover data
1140 * (if any) will be placed in buffer 1 and given a buffer type of
1144 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
1145 inBuffers
[0].cbBuffer
= (unsigned long)conn
->decryptBufferUsed
;
1146 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1148 inBuffers
[1].pvBuffer
= NULL
;
1149 inBuffers
[1].cbBuffer
= 0;
1150 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1152 inBuffer
.cBuffers
= 2;
1153 inBuffer
.pBuffers
= inBuffers
;
1154 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1157 * Set up the output buffers. These are initialized to NULL so as to make it
1158 * less likely we'll attempt to free random garbage later.
1161 outBuffers
[0].pvBuffer
= NULL
;
1162 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1163 outBuffers
[0].cbBuffer
= 0;
1165 outBuffer
.cBuffers
= 1;
1166 outBuffer
.pBuffers
= outBuffers
;
1167 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1170 * Call InitializeSecurityContext.
1173 scRet
= InitializeSecurityContext(&conn
->creds
, &conn
->context
, NULL
, dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, &inBuffer
, 0, NULL
, &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1176 * If InitializeSecurityContext was successful (or if the error was one of
1177 * the special extended ones), send the contents of the output buffer to the
1181 if (scRet
== SEC_E_OK
||
1182 scRet
== SEC_I_CONTINUE_NEEDED
||
1183 FAILED(scRet
) && (dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
))
1185 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1187 cbData
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1191 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1192 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1193 DeleteSecurityContext(&conn
->context
);
1197 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData
));
1200 * Free output buffer.
1203 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1204 outBuffers
[0].pvBuffer
= NULL
;
1209 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we
1210 * need to read more data from the server and try again.
1213 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1217 * If InitializeSecurityContext returned SEC_E_OK, then the handshake
1218 * completed successfully.
1221 if (scRet
== SEC_E_OK
)
1224 * If the "extra" buffer contains data, this is encrypted application
1225 * protocol layer stuff. It needs to be saved. The application layer will
1226 * later decrypt it with DecryptMessage.
1229 DEBUG_puts("http_sspi_client: Handshake was successful.");
1231 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1233 memmove(conn
->decryptBuffer
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
, inBuffers
[1].cbBuffer
);
1235 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1237 DEBUG_printf(("http_sspi_client: %d bytes of app data was bundled with handshake data", conn
->decryptBufferUsed
));
1240 conn
->decryptBufferUsed
= 0;
1250 * Check for fatal error.
1255 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(2) failed: %x", scRet
));
1261 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1262 * then the server just requested client authentication.
1265 if (scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1271 DEBUG_printf(("http_sspi_client: server requested client credentials."));
1277 * Copy any leftover data from the "extra" buffer, and go around again.
1280 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1282 memmove(conn
->decryptBuffer
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
, inBuffers
[1].cbBuffer
);
1284 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1288 conn
->decryptBufferUsed
= 0;
1295 * Success! Get the server cert
1298 conn
->contextInitialized
= TRUE
;
1300 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_REMOTE_CERT_CONTEXT
, (VOID
*)&(conn
->remoteCert
));
1302 if (scRet
!= SEC_E_OK
)
1304 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet
));
1309 /* TODO: Move this out for opt-in server cert validation, like other platforms. */
1310 scRet
= http_sspi_verify(conn
->remoteCert
, hostname
, conn
->certFlags
);
1312 if (scRet
!= SEC_E_OK
)
1314 DEBUG_printf(("http_sspi_client: sspi_verify_certificate failed: %x", scRet
));
1321 * Find out how big the header/trailer will be:
1324 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
1326 if (scRet
!= SEC_E_OK
)
1328 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet
));
1338 * 'http_sspi_free()' - Close a connection and free resources.
1342 http_sspi_free(_http_sspi_t
*conn
) /* I - Client connection */
1347 if (conn
->contextInitialized
)
1348 DeleteSecurityContext(&conn
->context
);
1350 if (conn
->decryptBuffer
)
1351 free(conn
->decryptBuffer
);
1353 if (conn
->readBuffer
)
1354 free(conn
->readBuffer
);
1356 if (conn
->writeBuffer
)
1357 free(conn
->writeBuffer
);
1359 if (conn
->localCert
.pbCertEncoded
)
1360 CertFreeCertificateContext(conn
->localCert
);
1362 if (conn
->remoteCert
.pbCertEncoded
)
1363 CertFreeCertificateContext(conn
->remoteCert
);
1371 * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store
1372 * If one cannot be found, one is created.
1374 BOOL
/* O - 1 on success, 0 on failure */
1375 _sspiGetCredentials(_http_sspi_t
*conn
,
1376 /* I - Client connection */
1377 const LPWSTR container
,
1378 /* I - Cert container name */
1379 const TCHAR
*cn
, /* I - Common name of certificate */
1381 /* I - Is caller a server? */
1383 HCERTSTORE store
= NULL
; /* Certificate store */
1384 PCCERT_CONTEXT storedContext
= NULL
;
1385 /* Context created from the store */
1386 PCCERT_CONTEXT createdContext
= NULL
;
1387 /* Context created by us */
1388 DWORD dwSize
= 0; /* 32 bit size */
1389 PBYTE p
= NULL
; /* Temporary storage */
1390 HCRYPTPROV hProv
= (HCRYPTPROV
) NULL
;
1391 /* Handle to a CSP */
1392 CERT_NAME_BLOB sib
; /* Arbitrary array of bytes */
1393 SCHANNEL_CRED SchannelCred
; /* Schannel credential data */
1394 TimeStamp tsExpiry
; /* Time stamp */
1395 SECURITY_STATUS Status
; /* Status */
1396 HCRYPTKEY hKey
= (HCRYPTKEY
) NULL
;
1397 /* Handle to crypto key */
1398 CRYPT_KEY_PROV_INFO kpi
; /* Key container info */
1399 SYSTEMTIME et
; /* System time */
1400 CERT_EXTENSIONS exts
; /* Array of cert extensions */
1401 CRYPT_KEY_PROV_INFO ckp
; /* Handle to crypto key */
1402 BOOL ok
= TRUE
; /* Return value */
1409 if (!CryptAcquireContextW(&hProv
, (LPWSTR
) container
, MS_DEF_PROV_W
,
1411 CRYPT_NEWKEYSET
| CRYPT_MACHINE_KEYSET
))
1413 if (GetLastError() == NTE_EXISTS
)
1415 if (!CryptAcquireContextW(&hProv
, (LPWSTR
) container
, MS_DEF_PROV_W
,
1416 PROV_RSA_FULL
, CRYPT_MACHINE_KEYSET
))
1418 DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n",
1426 store
= CertOpenStore(CERT_STORE_PROV_SYSTEM
,
1427 X509_ASN_ENCODING
|PKCS_7_ASN_ENCODING
,
1429 CERT_SYSTEM_STORE_LOCAL_MACHINE
|
1430 CERT_STORE_NO_CRYPT_RELEASE_FLAG
|
1431 CERT_STORE_OPEN_EXISTING_FLAG
,
1436 DEBUG_printf(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n",
1444 if (!CertStrToName(X509_ASN_ENCODING
, cn
, CERT_OID_NAME_STR
,
1445 NULL
, NULL
, &dwSize
, NULL
))
1447 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n",
1453 p
= (PBYTE
) malloc(dwSize
);
1457 DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize
));
1462 if (!CertStrToName(X509_ASN_ENCODING
, cn
, CERT_OID_NAME_STR
, NULL
,
1465 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x",
1471 sib
.cbData
= dwSize
;
1474 storedContext
= CertFindCertificateInStore(store
, X509_ASN_ENCODING
|PKCS_7_ASN_ENCODING
,
1475 0, CERT_FIND_SUBJECT_NAME
, &sib
, NULL
);
1480 * If we couldn't find the context, then we'll
1483 if (!CryptGenKey(hProv
, AT_KEYEXCHANGE
, CRYPT_EXPORTABLE
, &hKey
))
1485 DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x",
1491 ZeroMemory(&kpi
, sizeof(kpi
));
1492 kpi
.pwszContainerName
= (LPWSTR
) container
;
1493 kpi
.pwszProvName
= MS_DEF_PROV_W
;
1494 kpi
.dwProvType
= PROV_RSA_FULL
;
1495 kpi
.dwFlags
= CERT_SET_KEY_CONTEXT_PROP_ID
;
1496 kpi
.dwKeySpec
= AT_KEYEXCHANGE
;
1501 ZeroMemory(&exts
, sizeof(exts
));
1503 createdContext
= CertCreateSelfSignCertificate(hProv
, &sib
, 0, &kpi
, NULL
, NULL
,
1506 if (!createdContext
)
1508 DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x",
1514 if (!CertAddCertificateContextToStore(store
, createdContext
,
1515 CERT_STORE_ADD_REPLACE_EXISTING
,
1518 DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x",
1524 ZeroMemory(&ckp
, sizeof(ckp
));
1525 ckp
.pwszContainerName
= (LPWSTR
) container
;
1526 ckp
.pwszProvName
= MS_DEF_PROV_W
;
1527 ckp
.dwProvType
= PROV_RSA_FULL
;
1528 ckp
.dwFlags
= CRYPT_MACHINE_KEYSET
;
1529 ckp
.dwKeySpec
= AT_KEYEXCHANGE
;
1531 if (!CertSetCertificateContextProperty(storedContext
,
1532 CERT_KEY_PROV_INFO_PROP_ID
,
1535 DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x",
1542 ZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
1544 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
1545 SchannelCred
.cCreds
= 1;
1546 SchannelCred
.paCred
= &storedContext
;
1549 * SSPI doesn't seem to like it if grbitEnabledProtocols
1550 * is set for a client
1553 SchannelCred
.grbitEnabledProtocols
= SP_PROT_SSL3TLS1
;
1556 * Create an SSPI credential.
1558 Status
= AcquireCredentialsHandle(NULL
, UNISP_NAME
,
1559 isServer
? SECPKG_CRED_INBOUND
:SECPKG_CRED_OUTBOUND
,
1560 NULL
, &SchannelCred
, NULL
, NULL
, &conn
->creds
,
1562 if (Status
!= SEC_E_OK
)
1564 DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status
));
1575 CryptDestroyKey(hKey
);
1578 CertFreeCertificateContext(createdContext
);
1581 CertFreeCertificateContext(storedContext
);
1587 CertCloseStore(store
, 0);
1590 CryptReleaseContext(hProv
, 0);
1598 * 'http_sspi_server()' - Negotiate a TLS connection as a server.
1601 static int /* O - 0 on success, -1 on failure */
1602 http_sspi_server(http_t
*http
, /* I - HTTP connection */
1603 const char *hostname
) /* I - Hostname of server */
1605 _http_sspi_t
*conn
= http
->tls
; /* I - SSPI data */
1606 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1607 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1608 TimeStamp tsExpiry
; /* Time stamp */
1609 SECURITY_STATUS scRet
; /* SSPI Status */
1610 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1611 SecBuffer inBuffers
[2]; /* Security package buffer */
1612 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1613 SecBuffer outBuffers
[1]; /* Security package buffer */
1614 int num
= 0; /* 32 bit status value */
1615 BOOL fInitContext
= TRUE
; /* Has the context been init'd? */
1616 int ret
= 0; /* Return value */
1619 DEBUG_printf(("http_sspi_server(http=%p, hostname=\"%s\")", http
, hostname
));
1621 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
1622 ASC_REQ_REPLAY_DETECT
|
1623 ASC_REQ_CONFIDENTIALITY
|
1624 ASC_REQ_EXTENDED_ERROR
|
1625 ASC_REQ_ALLOCATE_MEMORY
|
1628 conn
->decryptBufferUsed
= 0;
1631 * Set OutBuffer for AcceptSecurityContext call
1634 outBuffer
.cBuffers
= 1;
1635 outBuffer
.pBuffers
= outBuffers
;
1636 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1638 scRet
= SEC_I_CONTINUE_NEEDED
;
1640 while (scRet
== SEC_I_CONTINUE_NEEDED
||
1641 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1642 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1644 if (conn
->decryptBufferUsed
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1646 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1648 BYTE
*temp
; /* New buffer */
1650 if (conn
->decryptBufferLength
>= 262144)
1652 WSASetLastError(E_OUTOFMEMORY
);
1653 DEBUG_puts("http_sspi_server: Decryption buffer too large (>256k)");
1657 if ((temp
= realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
+ 4096)) == NULL
)
1659 DEBUG_printf(("http_sspi_server: Unable to allocate %d byte buffer.", conn
->decryptBufferLength
+ 4096));
1660 WSASetLastError(E_OUTOFMEMORY
);
1664 conn
->decryptBufferLength
+= 4096;
1665 conn
->decryptBuffer
= temp
;
1670 num
= recv(http
->fd
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
, (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
1672 if (num
== -1 WSAGetLastError() == WSAEWOULDBLOCK
)
1680 DEBUG_printf(("http_sspi_server: recv failed: %d", WSAGetLastError()));
1685 DEBUG_puts("http_sspi_server: client disconnected");
1689 DEBUG_printf(("http_sspi_server: received %d (handshake) bytes from client.", num
));
1690 conn
->decryptBufferUsed
+= num
;
1694 * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process
1695 * on this run around the loop.
1698 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
1699 inBuffers
[0].cbBuffer
= (unsigned long)conn
->decryptBufferUsed
;
1700 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1702 inBuffers
[1].pvBuffer
= NULL
;
1703 inBuffers
[1].cbBuffer
= 0;
1704 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1706 inBuffer
.cBuffers
= 2;
1707 inBuffer
.pBuffers
= inBuffers
;
1708 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1711 * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to
1712 * free random garbage at the quit.
1715 outBuffers
[0].pvBuffer
= NULL
;
1716 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1717 outBuffers
[0].cbBuffer
= 0;
1719 scRet
= AcceptSecurityContext(&conn
->creds
, (fInitContext
?NULL
:&conn
->context
), &inBuffer
, dwSSPIFlags
, SECURITY_NATIVE_DREP
, (fInitContext
?&conn
->context
:NULL
), &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1721 fInitContext
= FALSE
;
1723 if (scRet
== SEC_E_OK
||
1724 scRet
== SEC_I_CONTINUE_NEEDED
||
1725 (FAILED(scRet
) && ((dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
) != 0)))
1727 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1730 * Send response to server if there is one.
1733 num
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1737 DEBUG_printf(("http_sspi_server: handshake send failed: %d", WSAGetLastError()));
1741 DEBUG_printf(("http_sspi_server: sent %d handshake bytes to client.", outBuffers
[0].cbBuffer
));
1743 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1744 outBuffers
[0].pvBuffer
= NULL
;
1748 if (scRet
== SEC_E_OK
)
1751 * If there's extra data then save it for next time we go to decrypt.
1754 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1756 memcpy(conn
->decryptBuffer
, (LPBYTE
)(conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
), inBuffers
[1].cbBuffer
);
1757 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1761 conn
->decryptBufferUsed
= 0;
1765 else if (FAILED(scRet
) && scRet
!= SEC_E_INCOMPLETE_MESSAGE
)
1767 DEBUG_printf(("http_sspi_server: AcceptSecurityContext failed: %x", scRet
));
1772 if (scRet
!= SEC_E_INCOMPLETE_MESSAGE
&&
1773 scRet
!= SEC_I_INCOMPLETE_CREDENTIALS
)
1775 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1777 memcpy(conn
->decryptBuffer
, (LPBYTE
)(conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
), inBuffers
[1].cbBuffer
);
1778 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1782 conn
->decryptBufferUsed
= 0;
1789 conn
->contextInitialized
= TRUE
;
1792 * Find out how big the header will be:
1795 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
1797 if (scRet
!= SEC_E_OK
)
1799 DEBUG_printf(("http_sspi_server: QueryContextAttributes failed: %x", scRet
));
1810 * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
1813 _sspiSetAllowsAnyRoot(_http_sspi_t
*conn
,
1814 /* I - Client connection */
1816 /* I - Allow any root */
1818 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_UNKNOWN_CA
:
1819 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_UNKNOWN_CA
;
1824 * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
1827 _sspiSetAllowsExpiredCerts(_http_sspi_t
*conn
,
1828 /* I - Client connection */
1830 /* I - Allow expired certs */
1832 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
:
1833 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
1838 * 'http_sspi_verify()' - Verify a certificate.
1841 static DWORD
/* O - Error code (0 == No error) */
1843 PCCERT_CONTEXT cert
, /* I - Server certificate */
1844 const char *common_name
, /* I - Common name */
1845 DWORD dwCertFlags
) /* I - Verification flags */
1847 HTTPSPolicyCallbackData httpsPolicy
; /* HTTPS Policy Struct */
1848 CERT_CHAIN_POLICY_PARA policyPara
; /* Cert chain policy parameters */
1849 CERT_CHAIN_POLICY_STATUS policyStatus
;/* Cert chain policy status */
1850 CERT_CHAIN_PARA chainPara
; /* Used for searching and matching criteria */
1851 PCCERT_CHAIN_CONTEXT chainContext
= NULL
;
1852 /* Certificate chain */
1853 PWSTR commonNameUnicode
= NULL
;
1854 /* Unicode common name */
1855 LPSTR rgszUsages
[] = { szOID_PKIX_KP_SERVER_AUTH
,
1856 szOID_SERVER_GATED_CRYPTO
,
1857 szOID_SGC_NETSCAPE
};
1858 /* How are we using this certificate? */
1859 DWORD cUsages
= sizeof(rgszUsages
) / sizeof(LPSTR
);
1860 /* Number of ites in rgszUsages */
1861 DWORD count
; /* 32 bit count variable */
1862 DWORD status
; /* Return value */
1866 return (SEC_E_WRONG_PRINCIPAL
);
1869 * Convert common name to Unicode.
1872 if (!common_name
|| !*common_name
)
1873 return (SEC_E_WRONG_PRINCIPAL
);
1875 count
= MultiByteToWideChar(CP_ACP
, 0, common_name
, -1, NULL
, 0);
1876 commonNameUnicode
= LocalAlloc(LMEM_FIXED
, count
* sizeof(WCHAR
));
1877 if (!commonNameUnicode
)
1878 return (SEC_E_INSUFFICIENT_MEMORY
);
1880 if (!MultiByteToWideChar(CP_ACP
, 0, common_name
, -1, commonNameUnicode
, count
))
1882 LocalFree(commonNameUnicode
);
1883 return (SEC_E_WRONG_PRINCIPAL
);
1887 * Build certificate chain.
1890 ZeroMemory(&chainPara
, sizeof(chainPara
));
1892 chainPara
.cbSize
= sizeof(chainPara
);
1893 chainPara
.RequestedUsage
.dwType
= USAGE_MATCH_TYPE_OR
;
1894 chainPara
.RequestedUsage
.Usage
.cUsageIdentifier
= cUsages
;
1895 chainPara
.RequestedUsage
.Usage
.rgpszUsageIdentifier
= rgszUsages
;
1897 if (!CertGetCertificateChain(NULL
, cert
, NULL
, cert
->hCertStore
, &chainPara
, 0, NULL
, &chainContext
))
1899 status
= GetLastError();
1900 DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status
));
1902 LocalFree(commonNameUnicode
);
1907 * Validate certificate chain.
1910 ZeroMemory(&httpsPolicy
, sizeof(HTTPSPolicyCallbackData
));
1911 httpsPolicy
.cbStruct
= sizeof(HTTPSPolicyCallbackData
);
1912 httpsPolicy
.dwAuthType
= AUTHTYPE_SERVER
;
1913 httpsPolicy
.fdwChecks
= dwCertFlags
;
1914 httpsPolicy
.pwszServerName
= commonNameUnicode
;
1916 memset(&policyPara
, 0, sizeof(policyPara
));
1917 policyPara
.cbSize
= sizeof(policyPara
);
1918 policyPara
.pvExtraPolicyPara
= &httpsPolicy
;
1920 memset(&policyStatus
, 0, sizeof(policyStatus
));
1921 policyStatus
.cbSize
= sizeof(policyStatus
);
1923 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL
, chainContext
, &policyPara
, &policyStatus
))
1925 status
= GetLastError();
1926 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status
));
1928 else if (policyStatus
.dwError
)
1929 status
= policyStatus
.dwError
;
1934 CertFreeCertificateChain(chainContext
);
1936 if (commonNameUnicode
)
1937 LocalFree(commonNameUnicode
);