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