]> git.ipfire.org Git - thirdparty/cups.git/blame_incremental - cups/tls-sspi.c
Save work - fix the cert helper functions, implement httpCopyCredentials.
[thirdparty/cups.git] / cups / tls-sspi.c
... / ...
CommitLineData
1/*
2 * "$Id$"
3 *
4 * TLS support for CUPS on Windows using SSPI.
5 *
6 * Copyright 2010-2014 by Apple Inc.
7 *
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/".
13 *
14 * This file is subject to the Apple OS-Developed Software exception.
15 */
16
17/*
18 * Include necessary headers...
19 */
20
21#include "debug-private.h"
22
23
24/*
25 * Include necessary libraries...
26 */
27
28#pragma comment(lib, "Crypt32.lib")
29#pragma comment(lib, "Secur32.lib")
30#pragma comment(lib, "Ws2_32.lib")
31
32
33/*
34 * Constants...
35 */
36
37#ifndef SECURITY_FLAG_IGNORE_UNKNOWN_CA
38# define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100 /* Untrusted root */
39#endif /* SECURITY_FLAG_IGNORE_UNKNOWN_CA */
40
41#ifndef SECURITY_FLAG_IGNORE_CERT_CN_INVALID
42# define SECURITY_FLAG_IGNORE_CERT_CN_INVALID 0x00001000 /* Common name does not match */
43#endif /* !SECURITY_FLAG_IGNORE_CERT_CN_INVALID */
44
45#ifndef SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
46# define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */
47#endif /* !SECURITY_FLAG_IGNORE_CERT_DATE_INVALID */
48
49/*
50 * Local functions...
51 */
52
53static _http_sspi_t *http_sspi_alloc(void);
54static int http_sspi_client(http_t *http, const char *hostname);
55static PCCERT_CONTEXT http_sspi_create_credential(http_credential_t *cred);
56static BOOL http_sspi_find_credentials(http_t *http, const LPWSTR containerName, const char *common_name);
57static void http_sspi_free(_http_sspi_t *sspi);
58static BOOL http_sspi_make_credentials(_http_sspi_t *sspi, const LPWSTR containerName, const char *common_name, _http_mode_t mode, int years);
59static int http_sspi_server(http_t *http, const char *hostname);
60static void http_sspi_set_allows_any_root(_http_sspi_t *sspi, BOOL allow);
61static void http_sspi_set_allows_expired_certs(_http_sspi_t *sspi, BOOL allow);
62static const char *http_sspi_strerror(_http_sspi_t *sspi, DWORD code);
63static DWORD http_sspi_verify(PCCERT_CONTEXT cert, const char *common_name, DWORD dwCertFlags);
64
65
66/*
67 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
68 *
69 * @since CUPS 2.0@
70 */
71
72int /* O - 1 on success, 0 on failure */
73cupsMakeServerCredentials(
74 const char *path, /* I - Keychain path or @code NULL@ for default */
75 const char *common_name, /* I - Common name */
76 int num_alt_names, /* I - Number of subject alternate names */
77 const char **alt_names, /* I - Subject Alternate Names */
78 time_t expiration_date) /* I - Expiration date */
79{
80 _http_sspi_t *sspi; /* SSPI data */
81 int ret; /* Return value */
82
83
84 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));
85
86 (void)path;
87 (void)num_alt_names;
88 (void)alt_names;
89
90 sspi = http_sspi_alloc();
91 ret = http_sspi_make_credentials(sspi, L"ServerContainer", common_name, _HTTP_MODE_SERVER, (int)((expiration_date - time(NULL) + 86399) / 86400 / 365));
92
93 http_sspi_free(sspi);
94
95 return (ret);
96}
97
98
99/*
100 * 'cupsSetServerCredentials()' - Set the default server credentials.
101 *
102 * Note: The server credentials are used by all threads in the running process.
103 * This function is threadsafe.
104 *
105 * @since CUPS 2.0@
106 */
107
108int /* O - 1 on success, 0 on failure */
109cupsSetServerCredentials(
110 const char *path, /* I - Keychain path or @code NULL@ for default */
111 const char *common_name, /* I - Default common name for server */
112 int auto_create) /* I - 1 = automatically create self-signed certificates */
113{
114 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
115
116 (void)path;
117 (void)common_name;
118 (void)auto_create;
119
120 return (0);
121}
122
123
124/*
125 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
126 * an encrypted connection.
127 *
128 * @since CUPS 1.5/OS X 10.7@
129 */
130
131int /* O - Status of call (0 = success) */
132httpCopyCredentials(
133 http_t *http, /* I - Connection to server */
134 cups_array_t **credentials) /* O - Array of credentials */
135{
136 DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials));
137
138 if (!http || !http->tls || !http->tls->remoteCert || !credentials)
139 {
140 if (credentials)
141 *credentials = NULL;
142
143 return (-1);
144 }
145
146 *credentials = cupsArrayNew(NULL, NULL);
147 httpAddCredential(*credentials, http->tls->remoteCert->pbCertEncoded, http->tls->remoteCert->cbCertEncoded);
148
149 return (0);
150}
151
152
153/*
154 * '_httpCreateCredentials()' - Create credentials in the internal format.
155 */
156
157http_tls_credentials_t /* O - Internal credentials */
158_httpCreateCredentials(
159 cups_array_t *credentials) /* I - Array of credentials */
160{
161 return (http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials)));
162}
163
164
165/*
166 * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
167 *
168 * @since CUPS 2.0@
169 */
170
171int /* O - 1 if valid, 0 otherwise */
172httpCredentialsAreValidForName(
173 cups_array_t *credentials, /* I - Credentials */
174 const char *common_name) /* I - Name to check */
175{
176 int valid = 1; /* Valid name? */
177 PCCERT_CONTEXT cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
178 /* Certificate */
179 char cert_name[1024]; /* Name from certificate */
180
181
182 if (cert)
183 {
184 if (CertNameToStr(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
185 {
186 /*
187 * Extract common name at end...
188 */
189
190 char *ptr = strrchr(cert_name, ',');
191 if (ptr && ptr[1])
192 _cups_strcpy(cert_name, ptr + 2);
193 }
194 else
195 strlcpy(cert_name, "unknown", sizeof(cert_name));
196
197 CertFreeCertificateContext(cert);
198 }
199 else
200 strlcpy(cert_name, "unknown", sizeof(cert_name));
201
202 /*
203 * Compare the common names...
204 */
205
206 if (_cups_strcasecmp(common_name, cert_name))
207 {
208 /*
209 * Not an exact match for the common name, check for wildcard certs...
210 */
211
212 const char *domain = strchr(common_name, '.');
213 /* Domain in common name */
214
215 if (strncmp(cert_name, "*.", 2) || !domain || _cups_strcasecmp(domain, cert_name + 1))
216 {
217 /*
218 * Not a wildcard match.
219 */
220
221 /* TODO: Check subject alternate names */
222 valid = 0;
223 }
224 }
225
226 return (valid);
227}
228
229
230/*
231 * 'httpCredentialsGetTrust()' - Return the trust of credentials.
232 *
233 * @since CUPS 2.0@
234 */
235
236http_trust_t /* O - Level of trust */
237httpCredentialsGetTrust(
238 cups_array_t *credentials, /* I - Credentials */
239 const char *common_name) /* I - Common name for trust lookup */
240{
241 http_trust_t trust = HTTP_TRUST_OK; /* Level of trust */
242 PCCERT_CONTEXT cert = NULL; /* Certificate to validate */
243 DWORD certFlags = 0; /* Cert verification flags */
244 _cups_globals_t *cg = _cupsGlobals(); /* Per-thread global data */
245
246
247 if (!common_name)
248 return (HTTP_TRUST_UNKNOWN);
249
250 cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
251 if (!cert)
252 return (HTTP_TRUST_UNKNOWN);
253
254 if (cg->any_root)
255 certFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
256
257 if (cg->expired_certs)
258 certFlags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
259
260 if (!cg->validate_certs)
261 certFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
262
263 if (http_sspi_verify(cert, common_name, certFlags) != SEC_E_OK)
264 trust = HTTP_TRUST_INVALID;
265
266 CertFreeCertificateContext(cert);
267
268 return (trust);
269}
270
271
272/*
273 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
274 *
275 * @since CUPS 2.0@
276 */
277
278time_t /* O - Expiration date of credentials */
279httpCredentialsGetExpiration(
280 cups_array_t *credentials) /* I - Credentials */
281{
282 time_t expiration_date = 0; /* Expiration data of credentials */
283 PCCERT_CONTEXT cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
284 /* Certificate */
285
286 if (cert)
287 {
288 SYSTEMTIME systime; /* System time */
289 struct tm tm; /* UNIX date/time */
290
291 FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime);
292
293 tm.tm_year = systime.wYear - 1900;
294 tm.tm_mon = systime.wMonth - 1;
295 tm.tm_mday = systime.wDay;
296 tm.tm_hour = systime.wHour;
297 tm.tm_min = systime.wMinute;
298 tm.tm_sec = systime.wSecond;
299
300 expiration_date = mktime(&tm);
301
302 CertFreeCertificateContext(cert);
303 }
304
305 return (expiration_date);
306}
307
308
309/*
310 * 'httpCredentialsString()' - Return a string representing the credentials.
311 *
312 * @since CUPS 2.0@
313 */
314
315size_t /* O - Total size of credentials string */
316httpCredentialsString(
317 cups_array_t *credentials, /* I - Credentials */
318 char *buffer, /* I - Buffer or @code NULL@ */
319 size_t bufsize) /* I - Size of buffer */
320{
321 http_credential_t *first = (http_credential_t *)cupsArrayFirst(credentials);
322 /* First certificate */
323 PCCERT_CONTEXT cert; /* Certificate */
324
325
326 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
327
328 if (!buffer)
329 return (0);
330
331 if (buffer && bufsize > 0)
332 *buffer = '\0';
333
334 cert = http_sspi_create_credential(first);
335
336 if (cert)
337 {
338 char cert_name[256]; /* Common name */
339 SYSTEMTIME systime; /* System time */
340 struct tm tm; /* UNIX date/time */
341 time_t expiration; /* Expiration date of cert */
342 _cups_md5_state_t md5_state; /* MD5 state */
343 unsigned char md5_digest[16]; /* MD5 result */
344
345 FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime);
346
347 tm.tm_year = systime.wYear - 1900;
348 tm.tm_mon = systime.wMonth - 1;
349 tm.tm_mday = systime.wDay;
350 tm.tm_hour = systime.wHour;
351 tm.tm_min = systime.wMinute;
352 tm.tm_sec = systime.wSecond;
353
354 expiration = mktime(&tm);
355
356 if (CertNameToStr(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
357 {
358 /*
359 * Extract common name at end...
360 */
361
362 char *ptr = strrchr(cert_name, ',');
363 if (ptr && ptr[1])
364 _cups_strcpy(cert_name, ptr + 2);
365 }
366 else
367 strlcpy(cert_name, "unknown", sizeof(cert_name));
368
369 _cupsMD5Init(&md5_state);
370 _cupsMD5Append(&md5_state, first->data, (int)first->datalen);
371 _cupsMD5Finish(&md5_state, md5_digest);
372
373 snprintf(buffer, bufsize, "%s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", cert_name, httpGetDateString(expiration), md5_digest[0], md5_digest[1], md5_digest[2], md5_digest[3], md5_digest[4], md5_digest[5], md5_digest[6], md5_digest[7], md5_digest[8], md5_digest[9], md5_digest[10], md5_digest[11], md5_digest[12], md5_digest[13], md5_digest[14], md5_digest[15]);
374
375 CertFreeCertificateContext(cert);
376 }
377
378 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
379
380 return (strlen(buffer));
381}
382
383
384/*
385 * '_httpFreeCredentials()' - Free internal credentials.
386 */
387
388void
389_httpFreeCredentials(
390 http_tls_credentials_t credentials) /* I - Internal credentials */
391{
392 if (!credentials)
393 return;
394
395 CertFreeCertificateContext(credentials);
396}
397
398
399/*
400 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
401 *
402 * @since CUPS 2.0@
403 */
404
405int /* O - 0 on success, -1 on error */
406httpLoadCredentials(
407 const char *path, /* I - Keychain path or @code NULL@ for default */
408 cups_array_t **credentials, /* IO - Credentials */
409 const char *common_name) /* I - Common name for credentials */
410{
411 DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
412
413 (void)path;
414 (void)common_name;
415
416 if (credentials)
417 *credentials = NULL;
418
419 DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials ? 0 : -1));
420
421 return (*credentials ? 0 : -1);
422}
423
424
425/*
426 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
427 *
428 * @since CUPS 2.0@
429 */
430
431int /* O - -1 on error, 0 on success */
432httpSaveCredentials(
433 const char *path, /* I - Keychain path or @code NULL@ for default */
434 cups_array_t *credentials, /* I - Credentials */
435 const char *common_name) /* I - Common name for credentials */
436{
437 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
438
439 (void)path;
440 (void)credentials;
441 (void)common_name;
442
443 DEBUG_printf(("1httpSaveCredentials: Returning %d.", -1));
444 return (-1);
445}
446
447
448/*
449 * '_httpTLSInitialize()' - Initialize the TLS stack.
450 */
451
452void
453_httpTLSInitialize(void)
454{
455 /*
456 * Nothing to do...
457 */
458}
459
460
461/*
462 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
463 */
464
465size_t /* O - Bytes available */
466_httpTLSPending(http_t *http) /* I - HTTP connection */
467{
468 if (http->tls)
469 return (http->tls->readBufferUsed);
470 else
471 return (0);
472}
473
474
475/*
476 * '_httpTLSRead()' - Read from a SSL/TLS connection.
477 */
478
479int /* O - Bytes read */
480_httpTLSRead(http_t *http, /* I - HTTP connection */
481 char *buf, /* I - Buffer to store data */
482 int len) /* I - Length of buffer */
483{
484 int i; /* Looping var */
485 _http_sspi_t *sspi = http->tls; /* SSPI data */
486 SecBufferDesc message; /* Array of SecBuffer struct */
487 SecBuffer buffers[4] = { 0 }; /* Security package buffer */
488 int num = 0; /* Return value */
489 PSecBuffer pDataBuffer; /* Data buffer */
490 PSecBuffer pExtraBuffer; /* Excess data buffer */
491 SECURITY_STATUS scRet; /* SSPI status */
492
493
494 DEBUG_printf(("4_httpTLSRead(http=%p, buf=%p, len=%d)", http, buf, len));
495
496 /*
497 * If there are bytes that have already been decrypted and have not yet been
498 * read, return those...
499 */
500
501 if (sspi->readBufferUsed > 0)
502 {
503 int bytesToCopy = min(sspi->readBufferUsed, len);
504 /* Number of bytes to copy */
505
506 memcpy(buf, sspi->readBuffer, bytesToCopy);
507 sspi->readBufferUsed -= bytesToCopy;
508
509 if (sspi->readBufferUsed > 0)
510 memmove(sspi->readBuffer, sspi->readBuffer + bytesToCopy, sspi->readBufferUsed);
511
512 DEBUG_printf(("5_httpTLSRead: Returning %d bytes previously decrypted.", bytesToCopy));
513
514 return (bytesToCopy);
515 }
516
517 /*
518 * Initialize security buffer structs
519 */
520
521 message.ulVersion = SECBUFFER_VERSION;
522 message.cBuffers = 4;
523 message.pBuffers = buffers;
524
525 do
526 {
527 /*
528 * If there is not enough space in the buffer, then increase its size...
529 */
530
531 if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
532 {
533 BYTE *temp; /* New buffer */
534
535 if (sspi->decryptBufferLength >= 262144)
536 {
537 WSASetLastError(E_OUTOFMEMORY);
538 DEBUG_puts("_httpTLSRead: Decryption buffer too large (>256k)");
539 return (-1);
540 }
541
542 if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
543 {
544 DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte decryption buffer.", sspi->decryptBufferLength + 4096));
545 WSASetLastError(E_OUTOFMEMORY);
546 return (-1);
547 }
548
549 sspi->decryptBufferLength += 4096;
550 sspi->decryptBuffer = temp;
551
552 DEBUG_printf(("_httpTLSRead: Resized decryption buffer to %d bytes.", sspi->decryptBufferLength));
553 }
554
555 buffers[0].pvBuffer = sspi->decryptBuffer;
556 buffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed;
557 buffers[0].BufferType = SECBUFFER_DATA;
558 buffers[1].BufferType = SECBUFFER_EMPTY;
559 buffers[2].BufferType = SECBUFFER_EMPTY;
560 buffers[3].BufferType = SECBUFFER_EMPTY;
561
562 DEBUG_printf(("5_httpTLSRead: decryptBufferUsed=%d", sspi->decryptBufferUsed));
563
564 scRet = DecryptMessage(&sspi->context, &message, 0, NULL);
565
566 if (scRet == SEC_E_INCOMPLETE_MESSAGE)
567 {
568 num = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
569 if (num < 0)
570 {
571 DEBUG_printf(("5_httpTLSRead: recv failed: %d", WSAGetLastError()));
572 return (-1);
573 }
574 else if (num == 0)
575 {
576 DEBUG_puts("5_httpTLSRead: Server disconnected.");
577 return (0);
578 }
579
580 DEBUG_printf(("5_httpTLSRead: Read %d bytes into decryption buffer.", num));
581
582 sspi->decryptBufferUsed += num;
583 }
584 }
585 while (scRet == SEC_E_INCOMPLETE_MESSAGE);
586
587 if (scRet == SEC_I_CONTEXT_EXPIRED)
588 {
589 DEBUG_puts("5_httpTLSRead: Context expired.");
590 WSASetLastError(WSAECONNRESET);
591 return (-1);
592 }
593 else if (scRet != SEC_E_OK)
594 {
595 DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %s", http_sspi_strerror(sspi, scRet)));
596 WSASetLastError(WSASYSCALLFAILURE);
597 return (-1);
598 }
599
600 /*
601 * The decryption worked. Now, locate data buffer.
602 */
603
604 pDataBuffer = NULL;
605 pExtraBuffer = NULL;
606
607 for (i = 1; i < 4; i++)
608 {
609 if (buffers[i].BufferType == SECBUFFER_DATA)
610 pDataBuffer = &buffers[i];
611 else if (!pExtraBuffer && (buffers[i].BufferType == SECBUFFER_EXTRA))
612 pExtraBuffer = &buffers[i];
613 }
614
615 /*
616 * If a data buffer is found, then copy the decrypted bytes to the passed-in
617 * buffer...
618 */
619
620 if (pDataBuffer)
621 {
622 int bytesToCopy = min((int)pDataBuffer->cbBuffer, len);
623 /* Number of bytes to copy into buf */
624 int bytesToSave = pDataBuffer->cbBuffer - bytesToCopy;
625 /* Number of bytes to save in our read buffer */
626
627 if (bytesToCopy)
628 memcpy(buf, pDataBuffer->pvBuffer, bytesToCopy);
629
630 /*
631 * If there are more decrypted bytes than can be copied to the passed in
632 * buffer, then save them...
633 */
634
635 if (bytesToSave)
636 {
637 if ((sspi->readBufferLength - sspi->readBufferUsed) < bytesToSave)
638 {
639 BYTE *temp; /* New buffer pointer */
640
641 if ((temp = realloc(sspi->readBuffer, sspi->readBufferUsed + bytesToSave)) == NULL)
642 {
643 DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", sspi->readBufferUsed + bytesToSave));
644 WSASetLastError(E_OUTOFMEMORY);
645 return (-1);
646 }
647
648 sspi->readBufferLength = sspi->readBufferUsed + bytesToSave;
649 sspi->readBuffer = temp;
650 }
651
652 memcpy(((BYTE *)sspi->readBuffer) + sspi->readBufferUsed, ((BYTE *)pDataBuffer->pvBuffer) + bytesToCopy, bytesToSave);
653
654 sspi->readBufferUsed += bytesToSave;
655 }
656
657 num = bytesToCopy;
658 }
659 else
660 {
661 DEBUG_puts("_httpTLSRead: Unable to find data buffer.");
662 WSASetLastError(WSASYSCALLFAILURE);
663 return (-1);
664 }
665
666 /*
667 * If the decryption process left extra bytes, then save those back in
668 * decryptBuffer. They will be processed the next time through the loop.
669 */
670
671 if (pExtraBuffer)
672 {
673 memmove(sspi->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
674 sspi->decryptBufferUsed = pExtraBuffer->cbBuffer;
675 }
676 else
677 {
678 sspi->decryptBufferUsed = 0;
679 }
680
681 return (num);
682}
683
684
685/*
686 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
687 */
688
689int /* O - 0 on success, -1 on failure */
690_httpTLSStart(http_t *http) /* I - HTTP connection */
691{
692 char hostname[256], /* Hostname */
693 *hostptr; /* Pointer into hostname */
694
695
696 DEBUG_printf(("7_httpTLSStart(http=%p)", http));
697
698 if ((http->tls = http_sspi_alloc()) == NULL)
699 return (-1);
700
701 if (http->mode == _HTTP_MODE_CLIENT)
702 {
703 /*
704 * Client: determine hostname...
705 */
706
707 if (httpAddrLocalhost(http->hostaddr))
708 {
709 strlcpy(hostname, "localhost", sizeof(hostname));
710 }
711 else
712 {
713 /*
714 * Otherwise make sure the hostname we have does not end in a trailing dot.
715 */
716
717 strlcpy(hostname, http->hostname, sizeof(hostname));
718 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
719 *hostptr == '.')
720 *hostptr = '\0';
721 }
722
723 return (http_sspi_client(http, hostname));
724 }
725 else
726 {
727 /*
728 * Server: determine hostname to use...
729 */
730
731 if (http->fields[HTTP_FIELD_HOST][0])
732 {
733 /*
734 * Use hostname for TLS upgrade...
735 */
736
737 strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
738 }
739 else
740 {
741 /*
742 * Resolve hostname from connection address...
743 */
744
745 http_addr_t addr; /* Connection address */
746 socklen_t addrlen; /* Length of address */
747
748 addrlen = sizeof(addr);
749 if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
750 {
751 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
752 hostname[0] = '\0';
753 }
754 else if (httpAddrLocalhost(&addr))
755 hostname[0] = '\0';
756 else
757 {
758 httpAddrLookup(&addr, hostname, sizeof(hostname));
759 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
760 }
761 }
762
763 return (http_sspi_server(http, hostname));
764 }
765}
766
767
768/*
769 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
770 */
771
772void
773_httpTLSStop(http_t *http) /* I - HTTP connection */
774{
775 _http_sspi_t *sspi = http->tls; /* SSPI data */
776
777
778 if (sspi->contextInitialized && http->fd >= 0)
779 {
780 SecBufferDesc message; /* Array of SecBuffer struct */
781 SecBuffer buffers[1] = { 0 };
782 /* Security package buffer */
783 DWORD dwType; /* Type */
784 DWORD status; /* Status */
785
786 /*
787 * Notify schannel that we are about to close the connection.
788 */
789
790 dwType = SCHANNEL_SHUTDOWN;
791
792 buffers[0].pvBuffer = &dwType;
793 buffers[0].BufferType = SECBUFFER_TOKEN;
794 buffers[0].cbBuffer = sizeof(dwType);
795
796 message.cBuffers = 1;
797 message.pBuffers = buffers;
798 message.ulVersion = SECBUFFER_VERSION;
799
800 status = ApplyControlToken(&sspi->context, &message);
801
802 if (SUCCEEDED(status))
803 {
804 PBYTE pbMessage; /* Message buffer */
805 DWORD cbMessage; /* Message buffer count */
806 DWORD cbData; /* Data count */
807 DWORD dwSSPIFlags; /* SSL attributes we requested */
808 DWORD dwSSPIOutFlags; /* SSL attributes we received */
809 TimeStamp tsExpiry; /* Time stamp */
810
811 dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
812 ASC_REQ_REPLAY_DETECT |
813 ASC_REQ_CONFIDENTIALITY |
814 ASC_REQ_EXTENDED_ERROR |
815 ASC_REQ_ALLOCATE_MEMORY |
816 ASC_REQ_STREAM;
817
818 buffers[0].pvBuffer = NULL;
819 buffers[0].BufferType = SECBUFFER_TOKEN;
820 buffers[0].cbBuffer = 0;
821
822 message.cBuffers = 1;
823 message.pBuffers = buffers;
824 message.ulVersion = SECBUFFER_VERSION;
825
826 status = AcceptSecurityContext(&sspi->creds, &sspi->context, NULL,
827 dwSSPIFlags, SECURITY_NATIVE_DREP, NULL,
828 &message, &dwSSPIOutFlags, &tsExpiry);
829
830 if (SUCCEEDED(status))
831 {
832 pbMessage = buffers[0].pvBuffer;
833 cbMessage = buffers[0].cbBuffer;
834
835 /*
836 * Send the close notify message to the client.
837 */
838
839 if (pbMessage && cbMessage)
840 {
841 cbData = send(http->fd, pbMessage, cbMessage, 0);
842 if ((cbData == SOCKET_ERROR) || (cbData == 0))
843 {
844 status = WSAGetLastError();
845 DEBUG_printf(("_httpTLSStop: sending close notify failed: %d", status));
846 }
847 else
848 {
849 FreeContextBuffer(pbMessage);
850 }
851 }
852 }
853 else
854 {
855 DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi, status)));
856 }
857 }
858 else
859 {
860 DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %s", http_sspi_strerror(sspi, status)));
861 }
862 }
863
864 http_sspi_free(sspi);
865
866 http->tls = NULL;
867}
868
869
870/*
871 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
872 */
873
874int /* O - Bytes written */
875_httpTLSWrite(http_t *http, /* I - HTTP connection */
876 const char *buf, /* I - Buffer holding data */
877 int len) /* I - Length of buffer */
878{
879 _http_sspi_t *sspi = http->tls; /* SSPI data */
880 SecBufferDesc message; /* Array of SecBuffer struct */
881 SecBuffer buffers[4] = { 0 }; /* Security package buffer */
882 int bufferLen; /* Buffer length */
883 int bytesLeft; /* Bytes left to write */
884 const char *bufptr; /* Pointer into buffer */
885 int num = 0; /* Return value */
886
887
888 bufferLen = sspi->streamSizes.cbMaximumMessage + sspi->streamSizes.cbHeader + sspi->streamSizes.cbTrailer;
889
890 if (bufferLen > sspi->writeBufferLength)
891 {
892 BYTE *temp; /* New buffer pointer */
893
894 if ((temp = (BYTE *)realloc(sspi->writeBuffer, bufferLen)) == NULL)
895 {
896 DEBUG_printf(("_httpTLSWrite: Unable to allocate buffer of %d bytes.", bufferLen));
897 WSASetLastError(E_OUTOFMEMORY);
898 return (-1);
899 }
900
901 sspi->writeBuffer = temp;
902 sspi->writeBufferLength = bufferLen;
903 }
904
905 bytesLeft = len;
906 bufptr = buf;
907
908 while (bytesLeft)
909 {
910 int chunk = min((int)sspi->streamSizes.cbMaximumMessage, bytesLeft);
911 /* Size of data to write */
912 SECURITY_STATUS scRet; /* SSPI status */
913
914 /*
915 * Copy user data into the buffer, starting just past the header...
916 */
917
918 memcpy(sspi->writeBuffer + sspi->streamSizes.cbHeader, bufptr, chunk);
919
920 /*
921 * Setup the SSPI buffers
922 */
923
924 message.ulVersion = SECBUFFER_VERSION;
925 message.cBuffers = 4;
926 message.pBuffers = buffers;
927
928 buffers[0].pvBuffer = sspi->writeBuffer;
929 buffers[0].cbBuffer = sspi->streamSizes.cbHeader;
930 buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
931 buffers[1].pvBuffer = sspi->writeBuffer + sspi->streamSizes.cbHeader;
932 buffers[1].cbBuffer = (unsigned long) chunk;
933 buffers[1].BufferType = SECBUFFER_DATA;
934 buffers[2].pvBuffer = sspi->writeBuffer + sspi->streamSizes.cbHeader + chunk;
935 buffers[2].cbBuffer = sspi->streamSizes.cbTrailer;
936 buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
937 buffers[3].BufferType = SECBUFFER_EMPTY;
938
939 /*
940 * Encrypt the data
941 */
942
943 scRet = EncryptMessage(&sspi->context, 0, &message, 0);
944
945 if (FAILED(scRet))
946 {
947 DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %s", http_sspi_strerror(sspi, scRet)));
948 WSASetLastError(WSASYSCALLFAILURE);
949 return (-1);
950 }
951
952 /*
953 * Send the data. Remember the size of the total data to send is the size
954 * of the header, the size of the data the caller passed in and the size
955 * of the trailer...
956 */
957
958 num = send(http->fd, sspi->writeBuffer, buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer, 0);
959
960 if (num <= 0)
961 {
962 DEBUG_printf(("_httpTLSWrite: send failed: %ld", WSAGetLastError()));
963 return (num);
964 }
965
966 bytesLeft -= chunk;
967 bufptr += chunk;
968 }
969
970 return (len);
971}
972
973
974#if 0
975/*
976 * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
977 */
978
979static int /* O - 0 on success, -1 on failure */
980http_setup_ssl(http_t *http) /* I - Connection to server */
981{
982 char hostname[256], /* Hostname */
983 *hostptr; /* Pointer into hostname */
984
985 TCHAR username[256]; /* Username returned from GetUserName() */
986 TCHAR commonName[256];/* Common name for certificate */
987 DWORD dwSize; /* 32 bit size */
988
989
990 DEBUG_printf(("7http_setup_ssl(http=%p)", http));
991
992 /*
993 * Get the hostname to use for SSL...
994 */
995
996 if (httpAddrLocalhost(http->hostaddr))
997 {
998 strlcpy(hostname, "localhost", sizeof(hostname));
999 }
1000 else
1001 {
1002 /*
1003 * Otherwise make sure the hostname we have does not end in a trailing dot.
1004 */
1005
1006 strlcpy(hostname, http->hostname, sizeof(hostname));
1007 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
1008 *hostptr == '.')
1009 *hostptr = '\0';
1010 }
1011
1012 http->tls = http_sspi_alloc();
1013
1014 if (!http->tls)
1015 {
1016 _cupsSetHTTPError(HTTP_STATUS_ERROR);
1017 return (-1);
1018 }
1019
1020 dwSize = sizeof(username) / sizeof(TCHAR);
1021 GetUserName(username, &dwSize);
1022 _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR),
1023 sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username);
1024
1025 if (!_sspiGetCredentials(http->tls, L"ClientContainer",
1026 commonName, FALSE))
1027 {
1028 _sspiFree(http->tls);
1029 http->tls = NULL;
1030
1031 http->error = EIO;
1032 http->status = HTTP_STATUS_ERROR;
1033
1034 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
1035 _("Unable to establish a secure connection to host."), 1);
1036
1037 return (-1);
1038 }
1039
1040 _sspiSetAllowsAnyRoot(http->tls, TRUE);
1041 _sspiSetAllowsExpiredCerts(http->tls, TRUE);
1042
1043 if (!_sspiConnect(http->tls, hostname))
1044 {
1045 _sspiFree(http->tls);
1046 http->tls = NULL;
1047
1048 http->error = EIO;
1049 http->status = HTTP_STATUS_ERROR;
1050
1051 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
1052 _("Unable to establish a secure connection to host."), 1);
1053
1054 return (-1);
1055 }
1056
1057 return (0);
1058}
1059#endif // 0
1060
1061
1062/*
1063 * 'http_sspi_alloc()' - Allocate SSPI object.
1064 */
1065
1066static _http_sspi_t * /* O - New SSPI/SSL object */
1067http_sspi_alloc(void)
1068{
1069 return ((_http_sspi_t *)calloc(sizeof(_http_sspi_t), 1));
1070}
1071
1072
1073/*
1074 * 'http_sspi_client()' - Negotiate a TLS connection as a client.
1075 */
1076
1077static int /* O - 0 on success, -1 on failure */
1078http_sspi_client(http_t *http, /* I - Client connection */
1079 const char *hostname) /* I - Server hostname */
1080{
1081 _http_sspi_t *sspi = http->tls; /* SSPI data */
1082 DWORD dwSize; /* Size for buffer */
1083 DWORD dwSSPIFlags; /* SSL connection attributes we want */
1084 DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
1085 TimeStamp tsExpiry; /* Time stamp */
1086 SECURITY_STATUS scRet; /* Status */
1087 int cbData; /* Data count */
1088 SecBufferDesc inBuffer; /* Array of SecBuffer structs */
1089 SecBuffer inBuffers[2]; /* Security package buffer */
1090 SecBufferDesc outBuffer; /* Array of SecBuffer structs */
1091 SecBuffer outBuffers[1]; /* Security package buffer */
1092 int ret = 0; /* Return value */
1093 char username[1024], /* Current username */
1094 common_name[1024]; /* CN=username */
1095
1096
1097 DEBUG_printf(("http_sspi_client(http=%p, hostname=\"%s\")", http, hostname));
1098
1099 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
1100 ISC_REQ_REPLAY_DETECT |
1101 ISC_REQ_CONFIDENTIALITY |
1102 ISC_RET_EXTENDED_ERROR |
1103 ISC_REQ_ALLOCATE_MEMORY |
1104 ISC_REQ_STREAM;
1105
1106 /*
1107 * Lookup the client certificate...
1108 */
1109
1110 dwSize = sizeof(username);
1111 GetUserName(username, &dwSize);
1112 snprintf(common_name, sizeof(common_name), "CN=%s", username);
1113
1114 if (!http_sspi_find_credentials(http, L"ClientContainer", common_name))
1115 if (!http_sspi_make_credentials(http->tls, L"ClientContainer", common_name, _HTTP_MODE_CLIENT, 10))
1116 {
1117 DEBUG_puts("http_sspi_client: Unable to get client credentials.");
1118 return (-1);
1119 }
1120
1121 /*
1122 * Initiate a ClientHello message and generate a token.
1123 */
1124
1125 outBuffers[0].pvBuffer = NULL;
1126 outBuffers[0].BufferType = SECBUFFER_TOKEN;
1127 outBuffers[0].cbBuffer = 0;
1128
1129 outBuffer.cBuffers = 1;
1130 outBuffer.pBuffers = outBuffers;
1131 outBuffer.ulVersion = SECBUFFER_VERSION;
1132
1133 scRet = InitializeSecurityContext(&sspi->creds, NULL, TEXT(""), dwSSPIFlags, 0, SECURITY_NATIVE_DREP, NULL, 0, &sspi->context, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
1134
1135 if (scRet != SEC_I_CONTINUE_NEEDED)
1136 {
1137 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(1) failed: %s", http_sspi_strerror(sspi, scRet)));
1138 return (-1);
1139 }
1140
1141 /*
1142 * Send response to server if there is one.
1143 */
1144
1145 if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
1146 {
1147 if ((cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0)) <= 0)
1148 {
1149 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1150 FreeContextBuffer(outBuffers[0].pvBuffer);
1151 DeleteSecurityContext(&sspi->context);
1152 return (-1);
1153 }
1154
1155 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData));
1156
1157 FreeContextBuffer(outBuffers[0].pvBuffer);
1158 outBuffers[0].pvBuffer = NULL;
1159 }
1160
1161 dwSSPIFlags = ISC_REQ_MANUAL_CRED_VALIDATION |
1162 ISC_REQ_SEQUENCE_DETECT |
1163 ISC_REQ_REPLAY_DETECT |
1164 ISC_REQ_CONFIDENTIALITY |
1165 ISC_RET_EXTENDED_ERROR |
1166 ISC_REQ_ALLOCATE_MEMORY |
1167 ISC_REQ_STREAM;
1168
1169 sspi->decryptBufferUsed = 0;
1170
1171 /*
1172 * Loop until the handshake is finished or an error occurs.
1173 */
1174
1175 scRet = SEC_I_CONTINUE_NEEDED;
1176
1177 while(scRet == SEC_I_CONTINUE_NEEDED ||
1178 scRet == SEC_E_INCOMPLETE_MESSAGE ||
1179 scRet == SEC_I_INCOMPLETE_CREDENTIALS)
1180 {
1181 if (sspi->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
1182 {
1183 if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
1184 {
1185 BYTE *temp; /* New buffer */
1186
1187 if (sspi->decryptBufferLength >= 262144)
1188 {
1189 WSASetLastError(E_OUTOFMEMORY);
1190 DEBUG_puts("http_sspi_client: Decryption buffer too large (>256k)");
1191 return (-1);
1192 }
1193
1194 if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
1195 {
1196 DEBUG_printf(("http_sspi_client: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
1197 WSASetLastError(E_OUTOFMEMORY);
1198 return (-1);
1199 }
1200
1201 sspi->decryptBufferLength += 4096;
1202 sspi->decryptBuffer = temp;
1203 }
1204
1205 cbData = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
1206
1207 if (cbData < 0)
1208 {
1209 DEBUG_printf(("http_sspi_client: recv failed: %d", WSAGetLastError()));
1210 return (-1);
1211 }
1212 else if (cbData == 0)
1213 {
1214 DEBUG_printf(("http_sspi_client: Server unexpectedly disconnected."));
1215 return (-1);
1216 }
1217
1218 DEBUG_printf(("http_sspi_client: %d bytes of handshake data received", cbData));
1219
1220 sspi->decryptBufferUsed += cbData;
1221 }
1222
1223 /*
1224 * Set up the input buffers. Buffer 0 is used to pass in data received from
1225 * the server. Schannel will consume some or all of this. Leftover data
1226 * (if any) will be placed in buffer 1 and given a buffer type of
1227 * SECBUFFER_EXTRA.
1228 */
1229
1230 inBuffers[0].pvBuffer = sspi->decryptBuffer;
1231 inBuffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed;
1232 inBuffers[0].BufferType = SECBUFFER_TOKEN;
1233
1234 inBuffers[1].pvBuffer = NULL;
1235 inBuffers[1].cbBuffer = 0;
1236 inBuffers[1].BufferType = SECBUFFER_EMPTY;
1237
1238 inBuffer.cBuffers = 2;
1239 inBuffer.pBuffers = inBuffers;
1240 inBuffer.ulVersion = SECBUFFER_VERSION;
1241
1242 /*
1243 * Set up the output buffers. These are initialized to NULL so as to make it
1244 * less likely we'll attempt to free random garbage later.
1245 */
1246
1247 outBuffers[0].pvBuffer = NULL;
1248 outBuffers[0].BufferType = SECBUFFER_TOKEN;
1249 outBuffers[0].cbBuffer = 0;
1250
1251 outBuffer.cBuffers = 1;
1252 outBuffer.pBuffers = outBuffers;
1253 outBuffer.ulVersion = SECBUFFER_VERSION;
1254
1255 /*
1256 * Call InitializeSecurityContext.
1257 */
1258
1259 scRet = InitializeSecurityContext(&sspi->creds, &sspi->context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
1260
1261 /*
1262 * If InitializeSecurityContext was successful (or if the error was one of
1263 * the special extended ones), send the contents of the output buffer to the
1264 * server.
1265 */
1266
1267 if (scRet == SEC_E_OK ||
1268 scRet == SEC_I_CONTINUE_NEEDED ||
1269 FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
1270 {
1271 if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
1272 {
1273 cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
1274
1275 if (cbData <= 0)
1276 {
1277 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1278 FreeContextBuffer(outBuffers[0].pvBuffer);
1279 DeleteSecurityContext(&sspi->context);
1280 return (-1);
1281 }
1282
1283 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData));
1284
1285 /*
1286 * Free output buffer.
1287 */
1288
1289 FreeContextBuffer(outBuffers[0].pvBuffer);
1290 outBuffers[0].pvBuffer = NULL;
1291 }
1292 }
1293
1294 /*
1295 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we
1296 * need to read more data from the server and try again.
1297 */
1298
1299 if (scRet == SEC_E_INCOMPLETE_MESSAGE)
1300 continue;
1301
1302 /*
1303 * If InitializeSecurityContext returned SEC_E_OK, then the handshake
1304 * completed successfully.
1305 */
1306
1307 if (scRet == SEC_E_OK)
1308 {
1309 /*
1310 * If the "extra" buffer contains data, this is encrypted application
1311 * protocol layer stuff. It needs to be saved. The application layer will
1312 * later decrypt it with DecryptMessage.
1313 */
1314
1315 DEBUG_puts("http_sspi_client: Handshake was successful.");
1316
1317 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
1318 {
1319 memmove(sspi->decryptBuffer, sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
1320
1321 sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
1322
1323 DEBUG_printf(("http_sspi_client: %d bytes of app data was bundled with handshake data", sspi->decryptBufferUsed));
1324 }
1325 else
1326 sspi->decryptBufferUsed = 0;
1327
1328 /*
1329 * Bail out to quit
1330 */
1331
1332 break;
1333 }
1334
1335 /*
1336 * Check for fatal error.
1337 */
1338
1339 if (FAILED(scRet))
1340 {
1341 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(2) failed: %s", http_sspi_strerror(sspi, scRet)));
1342 ret = -1;
1343 break;
1344 }
1345
1346 /*
1347 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1348 * then the server just requested client authentication.
1349 */
1350
1351 if (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
1352 {
1353 /*
1354 * Unimplemented
1355 */
1356
1357 DEBUG_printf(("http_sspi_client: server requested client credentials."));
1358 ret = -1;
1359 break;
1360 }
1361
1362 /*
1363 * Copy any leftover data from the "extra" buffer, and go around again.
1364 */
1365
1366 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
1367 {
1368 memmove(sspi->decryptBuffer, sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
1369
1370 sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
1371 }
1372 else
1373 {
1374 sspi->decryptBufferUsed = 0;
1375 }
1376 }
1377
1378 if (!ret)
1379 {
1380 /*
1381 * Success! Get the server cert
1382 */
1383
1384 sspi->contextInitialized = TRUE;
1385
1386 scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID *)&(sspi->remoteCert));
1387
1388 if (scRet != SEC_E_OK)
1389 {
1390 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %s", http_sspi_strerror(sspi, scRet)));
1391 return (-1);
1392 }
1393
1394 /*
1395 * Find out how big the header/trailer will be:
1396 */
1397
1398 scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_STREAM_SIZES, &sspi->streamSizes);
1399
1400 if (scRet != SEC_E_OK)
1401 {
1402 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %s", http_sspi_strerror(sspi, scRet)));
1403 ret = -1;
1404 }
1405 }
1406
1407 return (ret);
1408}
1409
1410
1411/*
1412 * 'http_sspi_create_credential()' - Create an SSPI certificate context.
1413 */
1414
1415static PCCERT_CONTEXT /* O - Certificate context */
1416http_sspi_create_credential(
1417 http_credential_t *cred) /* I - Credential */
1418{
1419 if (cred)
1420 return (CertCreateCertificateContext(X509_ASN_ENCODING, cred->data, cred->datalen));
1421 else
1422 return (NULL);
1423}
1424
1425
1426/*
1427 * 'http_sspi_find_credentials()' - Retrieve a TLS certificate from the system store.
1428 */
1429
1430static BOOL /* O - 1 on success, 0 on failure */
1431http_sspi_find_credentials(
1432 http_t *http, /* I - HTTP connection */
1433 const LPWSTR container, /* I - Cert container name */
1434 const char *common_name) /* I - Common name of certificate */
1435{
1436 _http_sspi_t *sspi = http->tls; /* SSPI data */
1437 HCERTSTORE store = NULL; /* Certificate store */
1438 PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
1439 DWORD dwSize = 0; /* 32 bit size */
1440 PBYTE p = NULL; /* Temporary storage */
1441 HCRYPTPROV hProv = (HCRYPTPROV)NULL;
1442 /* Handle to a CSP */
1443 CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
1444 SCHANNEL_CRED SchannelCred; /* Schannel credential data */
1445 TimeStamp tsExpiry; /* Time stamp */
1446 SECURITY_STATUS Status; /* Status */
1447 BOOL ok = TRUE; /* Return value */
1448
1449
1450 if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
1451 {
1452 if (GetLastError() == NTE_EXISTS)
1453 {
1454 if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
1455 {
1456 DEBUG_printf(("http_sspi_find_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi, GetLastError())));
1457 ok = FALSE;
1458 goto cleanup;
1459 }
1460 }
1461 }
1462
1463 store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
1464
1465 if (!store)
1466 {
1467 DEBUG_printf(("http_sspi_find_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi, GetLastError())));
1468 ok = FALSE;
1469 goto cleanup;
1470 }
1471
1472 dwSize = 0;
1473
1474 if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
1475 {
1476 DEBUG_printf(("http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi, GetLastError())));
1477 ok = FALSE;
1478 goto cleanup;
1479 }
1480
1481 p = (PBYTE)malloc(dwSize);
1482
1483 if (!p)
1484 {
1485 DEBUG_printf(("http_sspi_find_credentials: malloc failed for %d bytes.", dwSize));
1486 ok = FALSE;
1487 goto cleanup;
1488 }
1489
1490 if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
1491 {
1492 DEBUG_printf(("http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi, GetLastError())));
1493 ok = FALSE;
1494 goto cleanup;
1495 }
1496
1497 sib.cbData = dwSize;
1498 sib.pbData = p;
1499
1500 storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &sib, NULL);
1501
1502 if (!storedContext)
1503 {
1504 DEBUG_printf(("http_sspi_find_credentials: Unable to find credentials for \"%s\".", common_name));
1505 ok = FALSE;
1506 goto cleanup;
1507 }
1508
1509 ZeroMemory(&SchannelCred, sizeof(SchannelCred));
1510
1511 SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
1512 SchannelCred.cCreds = 1;
1513 SchannelCred.paCred = &storedContext;
1514
1515 /*
1516 * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
1517 */
1518
1519 if (http->mode == _HTTP_MODE_SERVER)
1520 SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
1521
1522 /*
1523 * Create an SSPI credential.
1524 */
1525
1526 Status = AcquireCredentialsHandle(NULL, UNISP_NAME, http->mode == _HTTP_MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &sspi->creds, &tsExpiry);
1527 if (Status != SEC_E_OK)
1528 {
1529 DEBUG_printf(("http_sspi_find_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi, Status)));
1530 ok = FALSE;
1531 goto cleanup;
1532 }
1533
1534cleanup:
1535
1536 /*
1537 * Cleanup
1538 */
1539
1540 if (storedContext)
1541 CertFreeCertificateContext(storedContext);
1542
1543 if (p)
1544 free(p);
1545
1546 if (store)
1547 CertCloseStore(store, 0);
1548
1549 if (hProv)
1550 CryptReleaseContext(hProv, 0);
1551
1552 return (ok);
1553}
1554
1555
1556/*
1557 * 'http_sspi_free()' - Close a connection and free resources.
1558 */
1559
1560static void
1561http_sspi_free(_http_sspi_t *sspi) /* I - SSPI data */
1562{
1563 if (!sspi)
1564 return;
1565
1566 if (sspi->contextInitialized)
1567 DeleteSecurityContext(&sspi->context);
1568
1569 if (sspi->decryptBuffer)
1570 free(sspi->decryptBuffer);
1571
1572 if (sspi->readBuffer)
1573 free(sspi->readBuffer);
1574
1575 if (sspi->writeBuffer)
1576 free(sspi->writeBuffer);
1577
1578 if (sspi->localCert)
1579 CertFreeCertificateContext(sspi->localCert);
1580
1581 if (sspi->remoteCert)
1582 CertFreeCertificateContext(sspi->remoteCert);
1583
1584 free(sspi);
1585}
1586
1587
1588/*
1589 * 'http_sspi_make_credentials()' - Create a TLS certificate in the system store.
1590 */
1591
1592static BOOL /* O - 1 on success, 0 on failure */
1593http_sspi_make_credentials(
1594 _http_sspi_t *sspi, /* I - SSPI data */
1595 const LPWSTR container, /* I - Cert container name */
1596 const char *common_name, /* I - Common name of certificate */
1597 _http_mode_t mode, /* I - Client or server? */
1598 int years) /* I - Years until expiration */
1599{
1600 HCERTSTORE store = NULL; /* Certificate store */
1601 PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
1602 PCCERT_CONTEXT createdContext = NULL; /* Context created by us */
1603 DWORD dwSize = 0; /* 32 bit size */
1604 PBYTE p = NULL; /* Temporary storage */
1605 HCRYPTPROV hProv = (HCRYPTPROV)NULL;
1606 /* Handle to a CSP */
1607 CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
1608 SCHANNEL_CRED SchannelCred; /* Schannel credential data */
1609 TimeStamp tsExpiry; /* Time stamp */
1610 SECURITY_STATUS Status; /* Status */
1611 HCRYPTKEY hKey = (HCRYPTKEY)NULL; /* Handle to crypto key */
1612 CRYPT_KEY_PROV_INFO kpi; /* Key container info */
1613 SYSTEMTIME et; /* System time */
1614 CERT_EXTENSIONS exts; /* Array of cert extensions */
1615 CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */
1616 BOOL ok = TRUE; /* Return value */
1617
1618
1619 DEBUG_printf(("4http_sspi_make_credentials(sspi=%p, container=%p, common_name=\"%s\", mode=%d, years=%d)", sspi, container, common_name, mode, years));
1620
1621 if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
1622 {
1623 if (GetLastError() == NTE_EXISTS)
1624 {
1625 if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
1626 {
1627 DEBUG_printf(("http_sspi_make_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi, GetLastError())));
1628 ok = FALSE;
1629 goto cleanup;
1630 }
1631 }
1632 }
1633
1634 store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
1635
1636 if (!store)
1637 {
1638 DEBUG_printf(("http_sspi_make_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi, GetLastError())));
1639 ok = FALSE;
1640 goto cleanup;
1641 }
1642
1643 dwSize = 0;
1644
1645 if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
1646 {
1647 DEBUG_printf(("http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi, GetLastError())));
1648 ok = FALSE;
1649 goto cleanup;
1650 }
1651
1652 p = (PBYTE)malloc(dwSize);
1653
1654 if (!p)
1655 {
1656 DEBUG_printf(("http_sspi_make_credentials: malloc failed for %d bytes", dwSize));
1657 ok = FALSE;
1658 goto cleanup;
1659 }
1660
1661 if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
1662 {
1663 DEBUG_printf(("http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi, GetLastError())));
1664 ok = FALSE;
1665 goto cleanup;
1666 }
1667
1668 /*
1669 * If we couldn't find the context, then we'll create a new one...
1670 */
1671
1672 if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey))
1673 {
1674 DEBUG_printf(("http_sspi_make_credentials: CryptGenKey failed: %s", http_sspi_strerror(sspi, GetLastError())));
1675 ok = FALSE;
1676 goto cleanup;
1677 }
1678
1679 ZeroMemory(&kpi, sizeof(kpi));
1680 kpi.pwszContainerName = (LPWSTR)container;
1681 kpi.pwszProvName = MS_DEF_PROV_W;
1682 kpi.dwProvType = PROV_RSA_FULL;
1683 kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
1684 kpi.dwKeySpec = AT_KEYEXCHANGE;
1685
1686 GetSystemTime(&et);
1687 et.wYear += years;
1688
1689 ZeroMemory(&exts, sizeof(exts));
1690
1691 createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL, &et, &exts);
1692
1693 if (!createdContext)
1694 {
1695 DEBUG_printf(("http_sspi_make_credentials: CertCreateSelfSignCertificate failed: %s", http_sspi_strerror(sspi, GetLastError())));
1696 ok = FALSE;
1697 goto cleanup;
1698 }
1699
1700 if (!CertAddCertificateContextToStore(store, createdContext, CERT_STORE_ADD_REPLACE_EXISTING, &storedContext))
1701 {
1702 DEBUG_printf(("http_sspi_make_credentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(sspi, GetLastError())));
1703 ok = FALSE;
1704 goto cleanup;
1705 }
1706
1707 ZeroMemory(&ckp, sizeof(ckp));
1708 ckp.pwszContainerName = (LPWSTR) container;
1709 ckp.pwszProvName = MS_DEF_PROV_W;
1710 ckp.dwProvType = PROV_RSA_FULL;
1711 ckp.dwFlags = CRYPT_MACHINE_KEYSET;
1712 ckp.dwKeySpec = AT_KEYEXCHANGE;
1713
1714 if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp))
1715 {
1716 DEBUG_printf(("http_sspi_make_credentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(sspi, GetLastError())));
1717 ok = FALSE;
1718 goto cleanup;
1719 }
1720
1721
1722 ZeroMemory(&SchannelCred, sizeof(SchannelCred));
1723
1724 SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
1725 SchannelCred.cCreds = 1;
1726 SchannelCred.paCred = &storedContext;
1727
1728 /*
1729 * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
1730 */
1731
1732 if (mode == _HTTP_MODE_SERVER)
1733 SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
1734
1735 /*
1736 * Create an SSPI credential.
1737 */
1738
1739 Status = AcquireCredentialsHandle(NULL, UNISP_NAME, mode == _HTTP_MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &sspi->creds, &tsExpiry);
1740 if (Status != SEC_E_OK)
1741 {
1742 DEBUG_printf(("http_sspi_make_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi, Status)));
1743 ok = FALSE;
1744 goto cleanup;
1745 }
1746
1747cleanup:
1748
1749 /*
1750 * Cleanup
1751 */
1752
1753 if (hKey)
1754 CryptDestroyKey(hKey);
1755
1756 if (createdContext)
1757 CertFreeCertificateContext(createdContext);
1758
1759 if (storedContext)
1760 CertFreeCertificateContext(storedContext);
1761
1762 if (p)
1763 free(p);
1764
1765 if (store)
1766 CertCloseStore(store, 0);
1767
1768 if (hProv)
1769 CryptReleaseContext(hProv, 0);
1770
1771 return (ok);
1772}
1773
1774
1775/*
1776 * 'http_sspi_server()' - Negotiate a TLS connection as a server.
1777 */
1778
1779static int /* O - 0 on success, -1 on failure */
1780http_sspi_server(http_t *http, /* I - HTTP connection */
1781 const char *hostname) /* I - Hostname of server */
1782{
1783 _http_sspi_t *sspi = http->tls; /* I - SSPI data */
1784 char common_name[512]; /* Common name for cert */
1785 DWORD dwSSPIFlags; /* SSL connection attributes we want */
1786 DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
1787 TimeStamp tsExpiry; /* Time stamp */
1788 SECURITY_STATUS scRet; /* SSPI Status */
1789 SecBufferDesc inBuffer; /* Array of SecBuffer structs */
1790 SecBuffer inBuffers[2]; /* Security package buffer */
1791 SecBufferDesc outBuffer; /* Array of SecBuffer structs */
1792 SecBuffer outBuffers[1]; /* Security package buffer */
1793 int num = 0; /* 32 bit status value */
1794 BOOL fInitContext = TRUE; /* Has the context been init'd? */
1795 int ret = 0; /* Return value */
1796
1797
1798 DEBUG_printf(("http_sspi_server(http=%p, hostname=\"%s\")", http, hostname));
1799
1800 dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
1801 ASC_REQ_REPLAY_DETECT |
1802 ASC_REQ_CONFIDENTIALITY |
1803 ASC_REQ_EXTENDED_ERROR |
1804 ASC_REQ_ALLOCATE_MEMORY |
1805 ASC_REQ_STREAM;
1806
1807 sspi->decryptBufferUsed = 0;
1808
1809 /*
1810 * Lookup the server certificate...
1811 */
1812
1813 snprintf(common_name, sizeof(common_name), "CN=%s", hostname);
1814
1815 if (!http_sspi_find_credentials(http, L"ServerContainer", common_name))
1816 if (!http_sspi_make_credentials(http->tls, L"ServerContainer", common_name, _HTTP_MODE_SERVER, 10))
1817 {
1818 DEBUG_puts("http_sspi_server: Unable to get server credentials.");
1819 return (-1);
1820 }
1821
1822 /*
1823 * Set OutBuffer for AcceptSecurityContext call
1824 */
1825
1826 outBuffer.cBuffers = 1;
1827 outBuffer.pBuffers = outBuffers;
1828 outBuffer.ulVersion = SECBUFFER_VERSION;
1829
1830 scRet = SEC_I_CONTINUE_NEEDED;
1831
1832 while (scRet == SEC_I_CONTINUE_NEEDED ||
1833 scRet == SEC_E_INCOMPLETE_MESSAGE ||
1834 scRet == SEC_I_INCOMPLETE_CREDENTIALS)
1835 {
1836 if (sspi->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
1837 {
1838 if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
1839 {
1840 BYTE *temp; /* New buffer */
1841
1842 if (sspi->decryptBufferLength >= 262144)
1843 {
1844 WSASetLastError(E_OUTOFMEMORY);
1845 DEBUG_puts("http_sspi_server: Decryption buffer too large (>256k)");
1846 return (-1);
1847 }
1848
1849 if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
1850 {
1851 DEBUG_printf(("http_sspi_server: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
1852 WSASetLastError(E_OUTOFMEMORY);
1853 return (-1);
1854 }
1855
1856 sspi->decryptBufferLength += 4096;
1857 sspi->decryptBuffer = temp;
1858 }
1859
1860 for (;;)
1861 {
1862 num = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
1863
1864 if (num == -1 && WSAGetLastError() == WSAEWOULDBLOCK)
1865 Sleep(1);
1866 else
1867 break;
1868 }
1869
1870 if (num < 0)
1871 {
1872 DEBUG_printf(("http_sspi_server: recv failed: %d", WSAGetLastError()));
1873 return (-1);
1874 }
1875 else if (num == 0)
1876 {
1877 DEBUG_puts("http_sspi_server: client disconnected");
1878 return (-1);
1879 }
1880
1881 DEBUG_printf(("http_sspi_server: received %d (handshake) bytes from client.", num));
1882 sspi->decryptBufferUsed += num;
1883 }
1884
1885 /*
1886 * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process
1887 * on this run around the loop.
1888 */
1889
1890 inBuffers[0].pvBuffer = sspi->decryptBuffer;
1891 inBuffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed;
1892 inBuffers[0].BufferType = SECBUFFER_TOKEN;
1893
1894 inBuffers[1].pvBuffer = NULL;
1895 inBuffers[1].cbBuffer = 0;
1896 inBuffers[1].BufferType = SECBUFFER_EMPTY;
1897
1898 inBuffer.cBuffers = 2;
1899 inBuffer.pBuffers = inBuffers;
1900 inBuffer.ulVersion = SECBUFFER_VERSION;
1901
1902 /*
1903 * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to
1904 * free random garbage at the quit.
1905 */
1906
1907 outBuffers[0].pvBuffer = NULL;
1908 outBuffers[0].BufferType = SECBUFFER_TOKEN;
1909 outBuffers[0].cbBuffer = 0;
1910
1911 scRet = AcceptSecurityContext(&sspi->creds, (fInitContext?NULL:&sspi->context), &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP, (fInitContext?&sspi->context:NULL), &outBuffer, &dwSSPIOutFlags, &tsExpiry);
1912
1913 fInitContext = FALSE;
1914
1915 if (scRet == SEC_E_OK ||
1916 scRet == SEC_I_CONTINUE_NEEDED ||
1917 (FAILED(scRet) && ((dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) != 0)))
1918 {
1919 if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
1920 {
1921 /*
1922 * Send response to server if there is one.
1923 */
1924
1925 num = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
1926
1927 if (num <= 0)
1928 {
1929 DEBUG_printf(("http_sspi_server: handshake send failed: %d", WSAGetLastError()));
1930 return (-1);
1931 }
1932
1933 DEBUG_printf(("http_sspi_server: sent %d handshake bytes to client.", outBuffers[0].cbBuffer));
1934
1935 FreeContextBuffer(outBuffers[0].pvBuffer);
1936 outBuffers[0].pvBuffer = NULL;
1937 }
1938 }
1939
1940 if (scRet == SEC_E_OK)
1941 {
1942 /*
1943 * If there's extra data then save it for next time we go to decrypt.
1944 */
1945
1946 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
1947 {
1948 memcpy(sspi->decryptBuffer, (LPBYTE)(sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
1949 sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
1950 }
1951 else
1952 {
1953 sspi->decryptBufferUsed = 0;
1954 }
1955 break;
1956 }
1957 else if (FAILED(scRet) && scRet != SEC_E_INCOMPLETE_MESSAGE)
1958 {
1959 DEBUG_printf(("http_sspi_server: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi, scRet)));
1960 ret = -1;
1961 break;
1962 }
1963
1964 if (scRet != SEC_E_INCOMPLETE_MESSAGE &&
1965 scRet != SEC_I_INCOMPLETE_CREDENTIALS)
1966 {
1967 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
1968 {
1969 memcpy(sspi->decryptBuffer, (LPBYTE)(sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
1970 sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
1971 }
1972 else
1973 {
1974 sspi->decryptBufferUsed = 0;
1975 }
1976 }
1977 }
1978
1979 if (!ret)
1980 {
1981 sspi->contextInitialized = TRUE;
1982
1983 /*
1984 * Find out how big the header will be:
1985 */
1986
1987 scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_STREAM_SIZES, &sspi->streamSizes);
1988
1989 if (scRet != SEC_E_OK)
1990 {
1991 DEBUG_printf(("http_sspi_server: QueryContextAttributes failed: %s", http_sspi_strerror(sspi, scRet)));
1992 ret = -1;
1993 }
1994 }
1995
1996 return (ret);
1997}
1998
1999
2000/*
2001 * 'http_sspi_strerror()' - Return a string for the specified error code.
2002 */
2003
2004static const char * /* O - String for error */
2005http_sspi_strerror(_http_sspi_t *sspi, /* I - SSPI data */
2006 DWORD code) /* I - Error code */
2007{
2008 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, sspi->error, sizeof(sspi->error), NULL))
2009 {
2010 /*
2011 * Strip trailing CR + LF...
2012 */
2013
2014 char *ptr; /* Pointer into error message */
2015
2016 for (ptr = sspi->error + strlen(sspi->error) - 1; ptr >= sspi->error; ptr --)
2017 if (*ptr == '\n' || *ptr == '\r')
2018 *ptr = '\0';
2019 else
2020 break;
2021 }
2022 else
2023 snprintf(sspi->error, sizeof(sspi->error), "Unknown error %x", code);
2024
2025 return (sspi->error);
2026}
2027
2028
2029/*
2030 * 'http_sspi_verify()' - Verify a certificate.
2031 */
2032
2033static DWORD /* O - Error code (0 == No error) */
2034http_sspi_verify(
2035 PCCERT_CONTEXT cert, /* I - Server certificate */
2036 const char *common_name, /* I - Common name */
2037 DWORD dwCertFlags) /* I - Verification flags */
2038{
2039 HTTPSPolicyCallbackData httpsPolicy; /* HTTPS Policy Struct */
2040 CERT_CHAIN_POLICY_PARA policyPara; /* Cert chain policy parameters */
2041 CERT_CHAIN_POLICY_STATUS policyStatus;/* Cert chain policy status */
2042 CERT_CHAIN_PARA chainPara; /* Used for searching and matching criteria */
2043 PCCERT_CHAIN_CONTEXT chainContext = NULL;
2044 /* Certificate chain */
2045 PWSTR commonNameUnicode = NULL;
2046 /* Unicode common name */
2047 LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
2048 szOID_SERVER_GATED_CRYPTO,
2049 szOID_SGC_NETSCAPE };
2050 /* How are we using this certificate? */
2051 DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
2052 /* Number of ites in rgszUsages */
2053 DWORD count; /* 32 bit count variable */
2054 DWORD status; /* Return value */
2055
2056
2057 if (!cert)
2058 return (SEC_E_WRONG_PRINCIPAL);
2059
2060 /*
2061 * Convert common name to Unicode.
2062 */
2063
2064 if (!common_name || !*common_name)
2065 return (SEC_E_WRONG_PRINCIPAL);
2066
2067 count = MultiByteToWideChar(CP_ACP, 0, common_name, -1, NULL, 0);
2068 commonNameUnicode = LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR));
2069 if (!commonNameUnicode)
2070 return (SEC_E_INSUFFICIENT_MEMORY);
2071
2072 if (!MultiByteToWideChar(CP_ACP, 0, common_name, -1, commonNameUnicode, count))
2073 {
2074 LocalFree(commonNameUnicode);
2075 return (SEC_E_WRONG_PRINCIPAL);
2076 }
2077
2078 /*
2079 * Build certificate chain.
2080 */
2081
2082 ZeroMemory(&chainPara, sizeof(chainPara));
2083
2084 chainPara.cbSize = sizeof(chainPara);
2085 chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
2086 chainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;
2087 chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
2088
2089 if (!CertGetCertificateChain(NULL, cert, NULL, cert->hCertStore, &chainPara, 0, NULL, &chainContext))
2090 {
2091 status = GetLastError();
2092 DEBUG_printf(("CertGetCertificateChain returned: %x", status));
2093
2094 LocalFree(commonNameUnicode);
2095 return (status);
2096 }
2097
2098 /*
2099 * Validate certificate chain.
2100 */
2101
2102 ZeroMemory(&httpsPolicy, sizeof(HTTPSPolicyCallbackData));
2103 httpsPolicy.cbStruct = sizeof(HTTPSPolicyCallbackData);
2104 httpsPolicy.dwAuthType = AUTHTYPE_SERVER;
2105 httpsPolicy.fdwChecks = dwCertFlags;
2106 httpsPolicy.pwszServerName = commonNameUnicode;
2107
2108 memset(&policyPara, 0, sizeof(policyPara));
2109 policyPara.cbSize = sizeof(policyPara);
2110 policyPara.pvExtraPolicyPara = &httpsPolicy;
2111
2112 memset(&policyStatus, 0, sizeof(policyStatus));
2113 policyStatus.cbSize = sizeof(policyStatus);
2114
2115 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext, &policyPara, &policyStatus))
2116 {
2117 status = GetLastError();
2118 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %x", status));
2119 }
2120 else if (policyStatus.dwError)
2121 status = policyStatus.dwError;
2122 else
2123 status = SEC_E_OK;
2124
2125 if (chainContext)
2126 CertFreeCertificateChain(chainContext);
2127
2128 if (commonNameUnicode)
2129 LocalFree(commonNameUnicode);
2130
2131 return (status);
2132}
2133
2134
2135/*
2136 * End of "$Id$".
2137 */