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((int)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
->writeBufferLength
)
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
->writeBufferLength
= bufferLen
;
839 int chunk
= min((int)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
= http_sspi_alloc();
945 _cupsSetHTTPError(HTTP_STATUS_ERROR
);
949 dwSize
= sizeof(username
) / sizeof(TCHAR
);
950 GetUserName(username
, &dwSize
);
951 _sntprintf_s(commonName
, sizeof(commonName
) / sizeof(TCHAR
),
952 sizeof(commonName
) / sizeof(TCHAR
), TEXT("CN=%s"), username
);
954 if (!_sspiGetCredentials(http
->tls
, L
"ClientContainer",
957 _sspiFree(http
->tls
);
961 http
->status
= HTTP_STATUS_ERROR
;
963 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
964 _("Unable to establish a secure connection to host."), 1);
969 _sspiSetAllowsAnyRoot(http
->tls
, TRUE
);
970 _sspiSetAllowsExpiredCerts(http
->tls
, TRUE
);
972 if (!_sspiConnect(http
->tls
, hostname
))
974 _sspiFree(http
->tls
);
978 http
->status
= HTTP_STATUS_ERROR
;
980 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
981 _("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
= http
->tls
; /* 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
));
1027 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
|
1028 ISC_REQ_REPLAY_DETECT
|
1029 ISC_REQ_CONFIDENTIALITY
|
1030 ISC_RET_EXTENDED_ERROR
|
1031 ISC_REQ_ALLOCATE_MEMORY
|
1035 * Initiate a ClientHello message and generate a token.
1038 outBuffers
[0].pvBuffer
= NULL
;
1039 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1040 outBuffers
[0].cbBuffer
= 0;
1042 outBuffer
.cBuffers
= 1;
1043 outBuffer
.pBuffers
= outBuffers
;
1044 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1046 scRet
= InitializeSecurityContext(&conn
->creds
, NULL
, TEXT(""), dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, NULL
, 0, &conn
->context
, &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1048 if (scRet
!= SEC_I_CONTINUE_NEEDED
)
1050 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(1) failed: %x", scRet
));
1055 * Send response to server if there is one.
1058 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1060 if ((cbData
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0)) <= 0)
1062 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1063 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1064 DeleteSecurityContext(&conn
->context
);
1068 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData
));
1070 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1071 outBuffers
[0].pvBuffer
= NULL
;
1074 dwSSPIFlags
= ISC_REQ_MANUAL_CRED_VALIDATION
|
1075 ISC_REQ_SEQUENCE_DETECT
|
1076 ISC_REQ_REPLAY_DETECT
|
1077 ISC_REQ_CONFIDENTIALITY
|
1078 ISC_RET_EXTENDED_ERROR
|
1079 ISC_REQ_ALLOCATE_MEMORY
|
1082 conn
->decryptBufferUsed
= 0;
1085 * Loop until the handshake is finished or an error occurs.
1088 scRet
= SEC_I_CONTINUE_NEEDED
;
1090 while(scRet
== SEC_I_CONTINUE_NEEDED
||
1091 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1092 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1094 if (conn
->decryptBufferUsed
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1096 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1098 BYTE
*temp
; /* New buffer */
1100 if (conn
->decryptBufferLength
>= 262144)
1102 WSASetLastError(E_OUTOFMEMORY
);
1103 DEBUG_puts("http_sspi_client: Decryption buffer too large (>256k)");
1107 if ((temp
= realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
+ 4096)) == NULL
)
1109 DEBUG_printf(("http_sspi_client: Unable to allocate %d byte buffer.", conn
->decryptBufferLength
+ 4096));
1110 WSASetLastError(E_OUTOFMEMORY
);
1114 conn
->decryptBufferLength
+= 4096;
1115 conn
->decryptBuffer
= temp
;
1118 cbData
= recv(http
->fd
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
, (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
1122 DEBUG_printf(("http_sspi_client: recv failed: %d", WSAGetLastError()));
1125 else if (cbData
== 0)
1127 DEBUG_printf(("http_sspi_client: Server unexpectedly disconnected."));
1131 DEBUG_printf(("http_sspi_client: %d bytes of handshake data received", cbData
));
1133 conn
->decryptBufferUsed
+= cbData
;
1137 * Set up the input buffers. Buffer 0 is used to pass in data received from
1138 * the server. Schannel will consume some or all of this. Leftover data
1139 * (if any) will be placed in buffer 1 and given a buffer type of
1143 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
1144 inBuffers
[0].cbBuffer
= (unsigned long)conn
->decryptBufferUsed
;
1145 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1147 inBuffers
[1].pvBuffer
= NULL
;
1148 inBuffers
[1].cbBuffer
= 0;
1149 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1151 inBuffer
.cBuffers
= 2;
1152 inBuffer
.pBuffers
= inBuffers
;
1153 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1156 * Set up the output buffers. These are initialized to NULL so as to make it
1157 * less likely we'll attempt to free random garbage later.
1160 outBuffers
[0].pvBuffer
= NULL
;
1161 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1162 outBuffers
[0].cbBuffer
= 0;
1164 outBuffer
.cBuffers
= 1;
1165 outBuffer
.pBuffers
= outBuffers
;
1166 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1169 * Call InitializeSecurityContext.
1172 scRet
= InitializeSecurityContext(&conn
->creds
, &conn
->context
, NULL
, dwSSPIFlags
, 0, SECURITY_NATIVE_DREP
, &inBuffer
, 0, NULL
, &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1175 * If InitializeSecurityContext was successful (or if the error was one of
1176 * the special extended ones), send the contents of the output buffer to the
1180 if (scRet
== SEC_E_OK
||
1181 scRet
== SEC_I_CONTINUE_NEEDED
||
1182 FAILED(scRet
) && (dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
))
1184 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1186 cbData
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1190 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1191 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1192 DeleteSecurityContext(&conn
->context
);
1196 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData
));
1199 * Free output buffer.
1202 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1203 outBuffers
[0].pvBuffer
= NULL
;
1208 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we
1209 * need to read more data from the server and try again.
1212 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1216 * If InitializeSecurityContext returned SEC_E_OK, then the handshake
1217 * completed successfully.
1220 if (scRet
== SEC_E_OK
)
1223 * If the "extra" buffer contains data, this is encrypted application
1224 * protocol layer stuff. It needs to be saved. The application layer will
1225 * later decrypt it with DecryptMessage.
1228 DEBUG_puts("http_sspi_client: Handshake was successful.");
1230 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1232 memmove(conn
->decryptBuffer
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
, inBuffers
[1].cbBuffer
);
1234 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1236 DEBUG_printf(("http_sspi_client: %d bytes of app data was bundled with handshake data", conn
->decryptBufferUsed
));
1239 conn
->decryptBufferUsed
= 0;
1249 * Check for fatal error.
1254 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(2) failed: %x", scRet
));
1260 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1261 * then the server just requested client authentication.
1264 if (scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1270 DEBUG_printf(("http_sspi_client: server requested client credentials."));
1276 * Copy any leftover data from the "extra" buffer, and go around again.
1279 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1281 memmove(conn
->decryptBuffer
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
, inBuffers
[1].cbBuffer
);
1283 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1287 conn
->decryptBufferUsed
= 0;
1294 * Success! Get the server cert
1297 conn
->contextInitialized
= TRUE
;
1299 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_REMOTE_CERT_CONTEXT
, (VOID
*)&(conn
->remoteCert
));
1301 if (scRet
!= SEC_E_OK
)
1303 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet
));
1308 /* TODO: Move this out for opt-in server cert validation, like other platforms. */
1309 scRet
= http_sspi_verify(conn
->remoteCert
, hostname
, conn
->certFlags
);
1311 if (scRet
!= SEC_E_OK
)
1313 DEBUG_printf(("http_sspi_client: sspi_verify_certificate failed: %x", scRet
));
1320 * Find out how big the header/trailer will be:
1323 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
1325 if (scRet
!= SEC_E_OK
)
1327 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet
));
1337 * 'http_sspi_free()' - Close a connection and free resources.
1341 http_sspi_free(_http_sspi_t
*conn
) /* I - Client connection */
1346 if (conn
->contextInitialized
)
1347 DeleteSecurityContext(&conn
->context
);
1349 if (conn
->decryptBuffer
)
1350 free(conn
->decryptBuffer
);
1352 if (conn
->readBuffer
)
1353 free(conn
->readBuffer
);
1355 if (conn
->writeBuffer
)
1356 free(conn
->writeBuffer
);
1358 if (conn
->localCert
)
1359 CertFreeCertificateContext(conn
->localCert
);
1361 if (conn
->remoteCert
)
1362 CertFreeCertificateContext(conn
->remoteCert
);
1370 * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store
1371 * If one cannot be found, one is created.
1373 BOOL
/* O - 1 on success, 0 on failure */
1374 _sspiGetCredentials(_http_sspi_t
*conn
,
1375 /* I - Client connection */
1376 const LPWSTR container
,
1377 /* I - Cert container name */
1378 const TCHAR
*cn
, /* I - Common name of certificate */
1380 /* I - Is caller a server? */
1382 HCERTSTORE store
= NULL
; /* Certificate store */
1383 PCCERT_CONTEXT storedContext
= NULL
;
1384 /* Context created from the store */
1385 PCCERT_CONTEXT createdContext
= NULL
;
1386 /* Context created by us */
1387 DWORD dwSize
= 0; /* 32 bit size */
1388 PBYTE p
= NULL
; /* Temporary storage */
1389 HCRYPTPROV hProv
= (HCRYPTPROV
) NULL
;
1390 /* Handle to a CSP */
1391 CERT_NAME_BLOB sib
; /* Arbitrary array of bytes */
1392 SCHANNEL_CRED SchannelCred
; /* Schannel credential data */
1393 TimeStamp tsExpiry
; /* Time stamp */
1394 SECURITY_STATUS Status
; /* Status */
1395 HCRYPTKEY hKey
= (HCRYPTKEY
) NULL
;
1396 /* Handle to crypto key */
1397 CRYPT_KEY_PROV_INFO kpi
; /* Key container info */
1398 SYSTEMTIME et
; /* System time */
1399 CERT_EXTENSIONS exts
; /* Array of cert extensions */
1400 CRYPT_KEY_PROV_INFO ckp
; /* Handle to crypto key */
1401 BOOL ok
= TRUE
; /* Return value */
1408 if (!CryptAcquireContextW(&hProv
, (LPWSTR
) container
, MS_DEF_PROV_W
,
1410 CRYPT_NEWKEYSET
| CRYPT_MACHINE_KEYSET
))
1412 if (GetLastError() == NTE_EXISTS
)
1414 if (!CryptAcquireContextW(&hProv
, (LPWSTR
) container
, MS_DEF_PROV_W
,
1415 PROV_RSA_FULL
, CRYPT_MACHINE_KEYSET
))
1417 DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n",
1425 store
= CertOpenStore(CERT_STORE_PROV_SYSTEM
,
1426 X509_ASN_ENCODING
|PKCS_7_ASN_ENCODING
,
1428 CERT_SYSTEM_STORE_LOCAL_MACHINE
|
1429 CERT_STORE_NO_CRYPT_RELEASE_FLAG
|
1430 CERT_STORE_OPEN_EXISTING_FLAG
,
1435 DEBUG_printf(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n",
1443 if (!CertStrToName(X509_ASN_ENCODING
, cn
, CERT_OID_NAME_STR
,
1444 NULL
, NULL
, &dwSize
, NULL
))
1446 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n",
1452 p
= (PBYTE
) malloc(dwSize
);
1456 DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize
));
1461 if (!CertStrToName(X509_ASN_ENCODING
, cn
, CERT_OID_NAME_STR
, NULL
,
1464 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x",
1470 sib
.cbData
= dwSize
;
1473 storedContext
= CertFindCertificateInStore(store
, X509_ASN_ENCODING
|PKCS_7_ASN_ENCODING
,
1474 0, CERT_FIND_SUBJECT_NAME
, &sib
, NULL
);
1479 * If we couldn't find the context, then we'll
1482 if (!CryptGenKey(hProv
, AT_KEYEXCHANGE
, CRYPT_EXPORTABLE
, &hKey
))
1484 DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x",
1490 ZeroMemory(&kpi
, sizeof(kpi
));
1491 kpi
.pwszContainerName
= (LPWSTR
) container
;
1492 kpi
.pwszProvName
= MS_DEF_PROV_W
;
1493 kpi
.dwProvType
= PROV_RSA_FULL
;
1494 kpi
.dwFlags
= CERT_SET_KEY_CONTEXT_PROP_ID
;
1495 kpi
.dwKeySpec
= AT_KEYEXCHANGE
;
1500 ZeroMemory(&exts
, sizeof(exts
));
1502 createdContext
= CertCreateSelfSignCertificate(hProv
, &sib
, 0, &kpi
, NULL
, NULL
,
1505 if (!createdContext
)
1507 DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x",
1513 if (!CertAddCertificateContextToStore(store
, createdContext
,
1514 CERT_STORE_ADD_REPLACE_EXISTING
,
1517 DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x",
1523 ZeroMemory(&ckp
, sizeof(ckp
));
1524 ckp
.pwszContainerName
= (LPWSTR
) container
;
1525 ckp
.pwszProvName
= MS_DEF_PROV_W
;
1526 ckp
.dwProvType
= PROV_RSA_FULL
;
1527 ckp
.dwFlags
= CRYPT_MACHINE_KEYSET
;
1528 ckp
.dwKeySpec
= AT_KEYEXCHANGE
;
1530 if (!CertSetCertificateContextProperty(storedContext
,
1531 CERT_KEY_PROV_INFO_PROP_ID
,
1534 DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x",
1541 ZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
1543 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
1544 SchannelCred
.cCreds
= 1;
1545 SchannelCred
.paCred
= &storedContext
;
1548 * SSPI doesn't seem to like it if grbitEnabledProtocols
1549 * is set for a client
1552 SchannelCred
.grbitEnabledProtocols
= SP_PROT_SSL3TLS1
;
1555 * Create an SSPI credential.
1557 Status
= AcquireCredentialsHandle(NULL
, UNISP_NAME
,
1558 isServer
? SECPKG_CRED_INBOUND
:SECPKG_CRED_OUTBOUND
,
1559 NULL
, &SchannelCred
, NULL
, NULL
, &conn
->creds
,
1561 if (Status
!= SEC_E_OK
)
1563 DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status
));
1574 CryptDestroyKey(hKey
);
1577 CertFreeCertificateContext(createdContext
);
1580 CertFreeCertificateContext(storedContext
);
1586 CertCloseStore(store
, 0);
1589 CryptReleaseContext(hProv
, 0);
1597 * 'http_sspi_server()' - Negotiate a TLS connection as a server.
1600 static int /* O - 0 on success, -1 on failure */
1601 http_sspi_server(http_t
*http
, /* I - HTTP connection */
1602 const char *hostname
) /* I - Hostname of server */
1604 _http_sspi_t
*conn
= http
->tls
; /* I - SSPI data */
1605 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1606 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1607 TimeStamp tsExpiry
; /* Time stamp */
1608 SECURITY_STATUS scRet
; /* SSPI Status */
1609 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1610 SecBuffer inBuffers
[2]; /* Security package buffer */
1611 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1612 SecBuffer outBuffers
[1]; /* Security package buffer */
1613 int num
= 0; /* 32 bit status value */
1614 BOOL fInitContext
= TRUE
; /* Has the context been init'd? */
1615 int ret
= 0; /* Return value */
1618 DEBUG_printf(("http_sspi_server(http=%p, hostname=\"%s\")", http
, hostname
));
1620 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
1621 ASC_REQ_REPLAY_DETECT
|
1622 ASC_REQ_CONFIDENTIALITY
|
1623 ASC_REQ_EXTENDED_ERROR
|
1624 ASC_REQ_ALLOCATE_MEMORY
|
1627 conn
->decryptBufferUsed
= 0;
1630 * Set OutBuffer for AcceptSecurityContext call
1633 outBuffer
.cBuffers
= 1;
1634 outBuffer
.pBuffers
= outBuffers
;
1635 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1637 scRet
= SEC_I_CONTINUE_NEEDED
;
1639 while (scRet
== SEC_I_CONTINUE_NEEDED
||
1640 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1641 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1643 if (conn
->decryptBufferUsed
== 0 || scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1645 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1647 BYTE
*temp
; /* New buffer */
1649 if (conn
->decryptBufferLength
>= 262144)
1651 WSASetLastError(E_OUTOFMEMORY
);
1652 DEBUG_puts("http_sspi_server: Decryption buffer too large (>256k)");
1656 if ((temp
= realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
+ 4096)) == NULL
)
1658 DEBUG_printf(("http_sspi_server: Unable to allocate %d byte buffer.", conn
->decryptBufferLength
+ 4096));
1659 WSASetLastError(E_OUTOFMEMORY
);
1663 conn
->decryptBufferLength
+= 4096;
1664 conn
->decryptBuffer
= temp
;
1669 num
= recv(http
->fd
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
, (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
1671 if (num
== -1 && WSAGetLastError() == WSAEWOULDBLOCK
)
1679 DEBUG_printf(("http_sspi_server: recv failed: %d", WSAGetLastError()));
1684 DEBUG_puts("http_sspi_server: client disconnected");
1688 DEBUG_printf(("http_sspi_server: received %d (handshake) bytes from client.", num
));
1689 conn
->decryptBufferUsed
+= num
;
1693 * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process
1694 * on this run around the loop.
1697 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
1698 inBuffers
[0].cbBuffer
= (unsigned long)conn
->decryptBufferUsed
;
1699 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1701 inBuffers
[1].pvBuffer
= NULL
;
1702 inBuffers
[1].cbBuffer
= 0;
1703 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1705 inBuffer
.cBuffers
= 2;
1706 inBuffer
.pBuffers
= inBuffers
;
1707 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1710 * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to
1711 * free random garbage at the quit.
1714 outBuffers
[0].pvBuffer
= NULL
;
1715 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1716 outBuffers
[0].cbBuffer
= 0;
1718 scRet
= AcceptSecurityContext(&conn
->creds
, (fInitContext
?NULL
:&conn
->context
), &inBuffer
, dwSSPIFlags
, SECURITY_NATIVE_DREP
, (fInitContext
?&conn
->context
:NULL
), &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1720 fInitContext
= FALSE
;
1722 if (scRet
== SEC_E_OK
||
1723 scRet
== SEC_I_CONTINUE_NEEDED
||
1724 (FAILED(scRet
) && ((dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
) != 0)))
1726 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1729 * Send response to server if there is one.
1732 num
= send(http
->fd
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1736 DEBUG_printf(("http_sspi_server: handshake send failed: %d", WSAGetLastError()));
1740 DEBUG_printf(("http_sspi_server: sent %d handshake bytes to client.", outBuffers
[0].cbBuffer
));
1742 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1743 outBuffers
[0].pvBuffer
= NULL
;
1747 if (scRet
== SEC_E_OK
)
1750 * If there's extra data then save it for next time we go to decrypt.
1753 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1755 memcpy(conn
->decryptBuffer
, (LPBYTE
)(conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
), inBuffers
[1].cbBuffer
);
1756 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1760 conn
->decryptBufferUsed
= 0;
1764 else if (FAILED(scRet
) && scRet
!= SEC_E_INCOMPLETE_MESSAGE
)
1766 DEBUG_printf(("http_sspi_server: AcceptSecurityContext failed: %x", scRet
));
1771 if (scRet
!= SEC_E_INCOMPLETE_MESSAGE
&&
1772 scRet
!= SEC_I_INCOMPLETE_CREDENTIALS
)
1774 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1776 memcpy(conn
->decryptBuffer
, (LPBYTE
)(conn
->decryptBuffer
+ conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
), inBuffers
[1].cbBuffer
);
1777 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1781 conn
->decryptBufferUsed
= 0;
1788 conn
->contextInitialized
= TRUE
;
1791 * Find out how big the header will be:
1794 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
1796 if (scRet
!= SEC_E_OK
)
1798 DEBUG_printf(("http_sspi_server: QueryContextAttributes failed: %x", scRet
));
1809 * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
1812 _sspiSetAllowsAnyRoot(_http_sspi_t
*conn
,
1813 /* I - Client connection */
1815 /* I - Allow any root */
1817 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_UNKNOWN_CA
:
1818 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_UNKNOWN_CA
;
1823 * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
1826 _sspiSetAllowsExpiredCerts(_http_sspi_t
*conn
,
1827 /* I - Client connection */
1829 /* I - Allow expired certs */
1831 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
:
1832 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
1837 * 'http_sspi_verify()' - Verify a certificate.
1840 static DWORD
/* O - Error code (0 == No error) */
1842 PCCERT_CONTEXT cert
, /* I - Server certificate */
1843 const char *common_name
, /* I - Common name */
1844 DWORD dwCertFlags
) /* I - Verification flags */
1846 HTTPSPolicyCallbackData httpsPolicy
; /* HTTPS Policy Struct */
1847 CERT_CHAIN_POLICY_PARA policyPara
; /* Cert chain policy parameters */
1848 CERT_CHAIN_POLICY_STATUS policyStatus
;/* Cert chain policy status */
1849 CERT_CHAIN_PARA chainPara
; /* Used for searching and matching criteria */
1850 PCCERT_CHAIN_CONTEXT chainContext
= NULL
;
1851 /* Certificate chain */
1852 PWSTR commonNameUnicode
= NULL
;
1853 /* Unicode common name */
1854 LPSTR rgszUsages
[] = { szOID_PKIX_KP_SERVER_AUTH
,
1855 szOID_SERVER_GATED_CRYPTO
,
1856 szOID_SGC_NETSCAPE
};
1857 /* How are we using this certificate? */
1858 DWORD cUsages
= sizeof(rgszUsages
) / sizeof(LPSTR
);
1859 /* Number of ites in rgszUsages */
1860 DWORD count
; /* 32 bit count variable */
1861 DWORD status
; /* Return value */
1865 return (SEC_E_WRONG_PRINCIPAL
);
1868 * Convert common name to Unicode.
1871 if (!common_name
|| !*common_name
)
1872 return (SEC_E_WRONG_PRINCIPAL
);
1874 count
= MultiByteToWideChar(CP_ACP
, 0, common_name
, -1, NULL
, 0);
1875 commonNameUnicode
= LocalAlloc(LMEM_FIXED
, count
* sizeof(WCHAR
));
1876 if (!commonNameUnicode
)
1877 return (SEC_E_INSUFFICIENT_MEMORY
);
1879 if (!MultiByteToWideChar(CP_ACP
, 0, common_name
, -1, commonNameUnicode
, count
))
1881 LocalFree(commonNameUnicode
);
1882 return (SEC_E_WRONG_PRINCIPAL
);
1886 * Build certificate chain.
1889 ZeroMemory(&chainPara
, sizeof(chainPara
));
1891 chainPara
.cbSize
= sizeof(chainPara
);
1892 chainPara
.RequestedUsage
.dwType
= USAGE_MATCH_TYPE_OR
;
1893 chainPara
.RequestedUsage
.Usage
.cUsageIdentifier
= cUsages
;
1894 chainPara
.RequestedUsage
.Usage
.rgpszUsageIdentifier
= rgszUsages
;
1896 if (!CertGetCertificateChain(NULL
, cert
, NULL
, cert
->hCertStore
, &chainPara
, 0, NULL
, &chainContext
))
1898 status
= GetLastError();
1899 DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status
));
1901 LocalFree(commonNameUnicode
);
1906 * Validate certificate chain.
1909 ZeroMemory(&httpsPolicy
, sizeof(HTTPSPolicyCallbackData
));
1910 httpsPolicy
.cbStruct
= sizeof(HTTPSPolicyCallbackData
);
1911 httpsPolicy
.dwAuthType
= AUTHTYPE_SERVER
;
1912 httpsPolicy
.fdwChecks
= dwCertFlags
;
1913 httpsPolicy
.pwszServerName
= commonNameUnicode
;
1915 memset(&policyPara
, 0, sizeof(policyPara
));
1916 policyPara
.cbSize
= sizeof(policyPara
);
1917 policyPara
.pvExtraPolicyPara
= &httpsPolicy
;
1919 memset(&policyStatus
, 0, sizeof(policyStatus
));
1920 policyStatus
.cbSize
= sizeof(policyStatus
);
1922 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL
, chainContext
, &policyPara
, &policyStatus
))
1924 status
= GetLastError();
1925 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status
));
1927 else if (policyStatus
.dwError
)
1928 status
= policyStatus
.dwError
;
1933 CertFreeCertificateChain(chainContext
);
1935 if (commonNameUnicode
)
1936 LocalFree(commonNameUnicode
);