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