]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/tls-sspi.c
Get builds working on Windows again.
[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 void http_sspi_free(_http_sspi_t *conn);
52 static BOOL http_sspi_get_credentials(_http_sspi_t *conn, const LPWSTR containerName, const TCHAR *commonName, BOOL server);
53 static int http_sspi_server(http_t *http, const char *hostname);
54 static void http_sspi_set_allows_any_root(_http_sspi_t *conn, BOOL allow);
55 static void http_sspi_set_allows_expired_certs(_http_sspi_t *conn, BOOL allow);
56 static DWORD http_sspi_verify(PCCERT_CONTEXT cert, const char *common_name, DWORD dwCertFlags);
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 dwSSPIFlags; /* SSL connection attributes we want */
1014 DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
1015 TimeStamp tsExpiry; /* Time stamp */
1016 SECURITY_STATUS scRet; /* Status */
1017 int cbData; /* Data count */
1018 SecBufferDesc inBuffer; /* Array of SecBuffer structs */
1019 SecBuffer inBuffers[2]; /* Security package buffer */
1020 SecBufferDesc outBuffer; /* Array of SecBuffer structs */
1021 SecBuffer outBuffers[1]; /* Security package buffer */
1022 int ret = 0; /* Return value */
1023
1024
1025 DEBUG_printf(("http_sspi_client(http=%p, hostname=\"%s\")", http, hostname));
1026
1027 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
1028 ISC_REQ_REPLAY_DETECT |
1029 ISC_REQ_CONFIDENTIALITY |
1030 ISC_RET_EXTENDED_ERROR |
1031 ISC_REQ_ALLOCATE_MEMORY |
1032 ISC_REQ_STREAM;
1033
1034 /*
1035 * Initiate a ClientHello message and generate a token.
1036 */
1037
1038 outBuffers[0].pvBuffer = NULL;
1039 outBuffers[0].BufferType = SECBUFFER_TOKEN;
1040 outBuffers[0].cbBuffer = 0;
1041
1042 outBuffer.cBuffers = 1;
1043 outBuffer.pBuffers = outBuffers;
1044 outBuffer.ulVersion = SECBUFFER_VERSION;
1045
1046 scRet = InitializeSecurityContext(&conn->creds, NULL, TEXT(""), dwSSPIFlags, 0, SECURITY_NATIVE_DREP, NULL, 0, &conn->context, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
1047
1048 if (scRet != SEC_I_CONTINUE_NEEDED)
1049 {
1050 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(1) failed: %x", scRet));
1051 return (-1);
1052 }
1053
1054 /*
1055 * Send response to server if there is one.
1056 */
1057
1058 if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
1059 {
1060 if ((cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0)) <= 0)
1061 {
1062 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1063 FreeContextBuffer(outBuffers[0].pvBuffer);
1064 DeleteSecurityContext(&conn->context);
1065 return (-1);
1066 }
1067
1068 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData));
1069
1070 FreeContextBuffer(outBuffers[0].pvBuffer);
1071 outBuffers[0].pvBuffer = NULL;
1072 }
1073
1074 dwSSPIFlags = ISC_REQ_MANUAL_CRED_VALIDATION |
1075 ISC_REQ_SEQUENCE_DETECT |
1076 ISC_REQ_REPLAY_DETECT |
1077 ISC_REQ_CONFIDENTIALITY |
1078 ISC_RET_EXTENDED_ERROR |
1079 ISC_REQ_ALLOCATE_MEMORY |
1080 ISC_REQ_STREAM;
1081
1082 conn->decryptBufferUsed = 0;
1083
1084 /*
1085 * Loop until the handshake is finished or an error occurs.
1086 */
1087
1088 scRet = SEC_I_CONTINUE_NEEDED;
1089
1090 while(scRet == SEC_I_CONTINUE_NEEDED ||
1091 scRet == SEC_E_INCOMPLETE_MESSAGE ||
1092 scRet == SEC_I_INCOMPLETE_CREDENTIALS)
1093 {
1094 if (conn->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
1095 {
1096 if (conn->decryptBufferLength <= conn->decryptBufferUsed)
1097 {
1098 BYTE *temp; /* New buffer */
1099
1100 if (conn->decryptBufferLength >= 262144)
1101 {
1102 WSASetLastError(E_OUTOFMEMORY);
1103 DEBUG_puts("http_sspi_client: Decryption buffer too large (>256k)");
1104 return (-1);
1105 }
1106
1107 if ((temp = realloc(conn->decryptBuffer, conn->decryptBufferLength + 4096)) == NULL)
1108 {
1109 DEBUG_printf(("http_sspi_client: Unable to allocate %d byte buffer.", conn->decryptBufferLength + 4096));
1110 WSASetLastError(E_OUTOFMEMORY);
1111 return (-1);
1112 }
1113
1114 conn->decryptBufferLength += 4096;
1115 conn->decryptBuffer = temp;
1116 }
1117
1118 cbData = recv(http->fd, conn->decryptBuffer + conn->decryptBufferUsed, (int)(conn->decryptBufferLength - conn->decryptBufferUsed), 0);
1119
1120 if (cbData < 0)
1121 {
1122 DEBUG_printf(("http_sspi_client: recv failed: %d", WSAGetLastError()));
1123 return (-1);
1124 }
1125 else if (cbData == 0)
1126 {
1127 DEBUG_printf(("http_sspi_client: Server unexpectedly disconnected."));
1128 return (-1);
1129 }
1130
1131 DEBUG_printf(("http_sspi_client: %d bytes of handshake data received", cbData));
1132
1133 conn->decryptBufferUsed += cbData;
1134 }
1135
1136 /*
1137 * Set up the input buffers. Buffer 0 is used to pass in data received from
1138 * the server. Schannel will consume some or all of this. Leftover data
1139 * (if any) will be placed in buffer 1 and given a buffer type of
1140 * SECBUFFER_EXTRA.
1141 */
1142
1143 inBuffers[0].pvBuffer = conn->decryptBuffer;
1144 inBuffers[0].cbBuffer = (unsigned long)conn->decryptBufferUsed;
1145 inBuffers[0].BufferType = SECBUFFER_TOKEN;
1146
1147 inBuffers[1].pvBuffer = NULL;
1148 inBuffers[1].cbBuffer = 0;
1149 inBuffers[1].BufferType = SECBUFFER_EMPTY;
1150
1151 inBuffer.cBuffers = 2;
1152 inBuffer.pBuffers = inBuffers;
1153 inBuffer.ulVersion = SECBUFFER_VERSION;
1154
1155 /*
1156 * Set up the output buffers. These are initialized to NULL so as to make it
1157 * less likely we'll attempt to free random garbage later.
1158 */
1159
1160 outBuffers[0].pvBuffer = NULL;
1161 outBuffers[0].BufferType = SECBUFFER_TOKEN;
1162 outBuffers[0].cbBuffer = 0;
1163
1164 outBuffer.cBuffers = 1;
1165 outBuffer.pBuffers = outBuffers;
1166 outBuffer.ulVersion = SECBUFFER_VERSION;
1167
1168 /*
1169 * Call InitializeSecurityContext.
1170 */
1171
1172 scRet = InitializeSecurityContext(&conn->creds, &conn->context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
1173
1174 /*
1175 * If InitializeSecurityContext was successful (or if the error was one of
1176 * the special extended ones), send the contents of the output buffer to the
1177 * server.
1178 */
1179
1180 if (scRet == SEC_E_OK ||
1181 scRet == SEC_I_CONTINUE_NEEDED ||
1182 FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
1183 {
1184 if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
1185 {
1186 cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
1187
1188 if (cbData <= 0)
1189 {
1190 DEBUG_printf(("http_sspi_client: send failed: %d", WSAGetLastError()));
1191 FreeContextBuffer(outBuffers[0].pvBuffer);
1192 DeleteSecurityContext(&conn->context);
1193 return (-1);
1194 }
1195
1196 DEBUG_printf(("http_sspi_client: %d bytes of handshake data sent.", cbData));
1197
1198 /*
1199 * Free output buffer.
1200 */
1201
1202 FreeContextBuffer(outBuffers[0].pvBuffer);
1203 outBuffers[0].pvBuffer = NULL;
1204 }
1205 }
1206
1207 /*
1208 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we
1209 * need to read more data from the server and try again.
1210 */
1211
1212 if (scRet == SEC_E_INCOMPLETE_MESSAGE)
1213 continue;
1214
1215 /*
1216 * If InitializeSecurityContext returned SEC_E_OK, then the handshake
1217 * completed successfully.
1218 */
1219
1220 if (scRet == SEC_E_OK)
1221 {
1222 /*
1223 * If the "extra" buffer contains data, this is encrypted application
1224 * protocol layer stuff. It needs to be saved. The application layer will
1225 * later decrypt it with DecryptMessage.
1226 */
1227
1228 DEBUG_puts("http_sspi_client: Handshake was successful.");
1229
1230 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
1231 {
1232 memmove(conn->decryptBuffer, conn->decryptBuffer + conn->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
1233
1234 conn->decryptBufferUsed = inBuffers[1].cbBuffer;
1235
1236 DEBUG_printf(("http_sspi_client: %d bytes of app data was bundled with handshake data", conn->decryptBufferUsed));
1237 }
1238 else
1239 conn->decryptBufferUsed = 0;
1240
1241 /*
1242 * Bail out to quit
1243 */
1244
1245 break;
1246 }
1247
1248 /*
1249 * Check for fatal error.
1250 */
1251
1252 if (FAILED(scRet))
1253 {
1254 DEBUG_printf(("http_sspi_client: InitializeSecurityContext(2) failed: %x", scRet));
1255 ret = -1;
1256 break;
1257 }
1258
1259 /*
1260 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1261 * then the server just requested client authentication.
1262 */
1263
1264 if (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
1265 {
1266 /*
1267 * Unimplemented
1268 */
1269
1270 DEBUG_printf(("http_sspi_client: server requested client credentials."));
1271 ret = -1;
1272 break;
1273 }
1274
1275 /*
1276 * Copy any leftover data from the "extra" buffer, and go around again.
1277 */
1278
1279 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
1280 {
1281 memmove(conn->decryptBuffer, conn->decryptBuffer + conn->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
1282
1283 conn->decryptBufferUsed = inBuffers[1].cbBuffer;
1284 }
1285 else
1286 {
1287 conn->decryptBufferUsed = 0;
1288 }
1289 }
1290
1291 if (!ret)
1292 {
1293 /*
1294 * Success! Get the server cert
1295 */
1296
1297 conn->contextInitialized = TRUE;
1298
1299 scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID *)&(conn->remoteCert));
1300
1301 if (scRet != SEC_E_OK)
1302 {
1303 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet));
1304 return (-1);
1305 }
1306
1307 #if 0
1308 /* TODO: Move this out for opt-in server cert validation, like other platforms. */
1309 scRet = http_sspi_verify(conn->remoteCert, hostname, conn->certFlags);
1310
1311 if (scRet != SEC_E_OK)
1312 {
1313 DEBUG_printf(("http_sspi_client: sspi_verify_certificate failed: %x", scRet));
1314 ret = -1;
1315 goto cleanup;
1316 }
1317 #endif // 0
1318
1319 /*
1320 * Find out how big the header/trailer will be:
1321 */
1322
1323 scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes);
1324
1325 if (scRet != SEC_E_OK)
1326 {
1327 DEBUG_printf(("http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet));
1328 ret = -1;
1329 }
1330 }
1331
1332 return (ret);
1333 }
1334
1335
1336 /*
1337 * 'http_sspi_free()' - Close a connection and free resources.
1338 */
1339
1340 static void
1341 http_sspi_free(_http_sspi_t *conn) /* I - Client connection */
1342 {
1343 if (!conn)
1344 return;
1345
1346 if (conn->contextInitialized)
1347 DeleteSecurityContext(&conn->context);
1348
1349 if (conn->decryptBuffer)
1350 free(conn->decryptBuffer);
1351
1352 if (conn->readBuffer)
1353 free(conn->readBuffer);
1354
1355 if (conn->writeBuffer)
1356 free(conn->writeBuffer);
1357
1358 if (conn->localCert)
1359 CertFreeCertificateContext(conn->localCert);
1360
1361 if (conn->remoteCert)
1362 CertFreeCertificateContext(conn->remoteCert);
1363
1364 free(conn);
1365 }
1366
1367
1368 #if 0
1369 /*
1370 * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store
1371 * If one cannot be found, one is created.
1372 */
1373 BOOL /* O - 1 on success, 0 on failure */
1374 _sspiGetCredentials(_http_sspi_t *conn,
1375 /* I - Client connection */
1376 const LPWSTR container,
1377 /* I - Cert container name */
1378 const TCHAR *cn, /* I - Common name of certificate */
1379 BOOL isServer)
1380 /* I - Is caller a server? */
1381 {
1382 HCERTSTORE store = NULL; /* Certificate store */
1383 PCCERT_CONTEXT storedContext = NULL;
1384 /* Context created from the store */
1385 PCCERT_CONTEXT createdContext = NULL;
1386 /* Context created by us */
1387 DWORD dwSize = 0; /* 32 bit size */
1388 PBYTE p = NULL; /* Temporary storage */
1389 HCRYPTPROV hProv = (HCRYPTPROV) NULL;
1390 /* Handle to a CSP */
1391 CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
1392 SCHANNEL_CRED SchannelCred; /* Schannel credential data */
1393 TimeStamp tsExpiry; /* Time stamp */
1394 SECURITY_STATUS Status; /* Status */
1395 HCRYPTKEY hKey = (HCRYPTKEY) NULL;
1396 /* Handle to crypto key */
1397 CRYPT_KEY_PROV_INFO kpi; /* Key container info */
1398 SYSTEMTIME et; /* System time */
1399 CERT_EXTENSIONS exts; /* Array of cert extensions */
1400 CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */
1401 BOOL ok = TRUE; /* Return value */
1402
1403 if (!conn)
1404 return (FALSE);
1405 if (!cn)
1406 return (FALSE);
1407
1408 if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W,
1409 PROV_RSA_FULL,
1410 CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
1411 {
1412 if (GetLastError() == NTE_EXISTS)
1413 {
1414 if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W,
1415 PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
1416 {
1417 DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n",
1418 GetLastError()));
1419 ok = FALSE;
1420 goto cleanup;
1421 }
1422 }
1423 }
1424
1425 store = CertOpenStore(CERT_STORE_PROV_SYSTEM,
1426 X509_ASN_ENCODING|PKCS_7_ASN_ENCODING,
1427 hProv,
1428 CERT_SYSTEM_STORE_LOCAL_MACHINE |
1429 CERT_STORE_NO_CRYPT_RELEASE_FLAG |
1430 CERT_STORE_OPEN_EXISTING_FLAG,
1431 L"MY");
1432
1433 if (!store)
1434 {
1435 DEBUG_printf(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n",
1436 GetLastError()));
1437 ok = FALSE;
1438 goto cleanup;
1439 }
1440
1441 dwSize = 0;
1442
1443 if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR,
1444 NULL, NULL, &dwSize, NULL))
1445 {
1446 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n",
1447 GetLastError()));
1448 ok = FALSE;
1449 goto cleanup;
1450 }
1451
1452 p = (PBYTE) malloc(dwSize);
1453
1454 if (!p)
1455 {
1456 DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize));
1457 ok = FALSE;
1458 goto cleanup;
1459 }
1460
1461 if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR, NULL,
1462 p, &dwSize, NULL))
1463 {
1464 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x",
1465 GetLastError()));
1466 ok = FALSE;
1467 goto cleanup;
1468 }
1469
1470 sib.cbData = dwSize;
1471 sib.pbData = p;
1472
1473 storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING,
1474 0, CERT_FIND_SUBJECT_NAME, &sib, NULL);
1475
1476 if (!storedContext)
1477 {
1478 /*
1479 * If we couldn't find the context, then we'll
1480 * create a new one
1481 */
1482 if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey))
1483 {
1484 DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x",
1485 GetLastError()));
1486 ok = FALSE;
1487 goto cleanup;
1488 }
1489
1490 ZeroMemory(&kpi, sizeof(kpi));
1491 kpi.pwszContainerName = (LPWSTR) container;
1492 kpi.pwszProvName = MS_DEF_PROV_W;
1493 kpi.dwProvType = PROV_RSA_FULL;
1494 kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
1495 kpi.dwKeySpec = AT_KEYEXCHANGE;
1496
1497 GetSystemTime(&et);
1498 et.wYear += 10;
1499
1500 ZeroMemory(&exts, sizeof(exts));
1501
1502 createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL,
1503 &et, &exts);
1504
1505 if (!createdContext)
1506 {
1507 DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x",
1508 GetLastError()));
1509 ok = FALSE;
1510 goto cleanup;
1511 }
1512
1513 if (!CertAddCertificateContextToStore(store, createdContext,
1514 CERT_STORE_ADD_REPLACE_EXISTING,
1515 &storedContext))
1516 {
1517 DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x",
1518 GetLastError()));
1519 ok = FALSE;
1520 goto cleanup;
1521 }
1522
1523 ZeroMemory(&ckp, sizeof(ckp));
1524 ckp.pwszContainerName = (LPWSTR) container;
1525 ckp.pwszProvName = MS_DEF_PROV_W;
1526 ckp.dwProvType = PROV_RSA_FULL;
1527 ckp.dwFlags = CRYPT_MACHINE_KEYSET;
1528 ckp.dwKeySpec = AT_KEYEXCHANGE;
1529
1530 if (!CertSetCertificateContextProperty(storedContext,
1531 CERT_KEY_PROV_INFO_PROP_ID,
1532 0, &ckp))
1533 {
1534 DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x",
1535 GetLastError()));
1536 ok = FALSE;
1537 goto cleanup;
1538 }
1539 }
1540
1541 ZeroMemory(&SchannelCred, sizeof(SchannelCred));
1542
1543 SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
1544 SchannelCred.cCreds = 1;
1545 SchannelCred.paCred = &storedContext;
1546
1547 /*
1548 * SSPI doesn't seem to like it if grbitEnabledProtocols
1549 * is set for a client
1550 */
1551 if (isServer)
1552 SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
1553
1554 /*
1555 * Create an SSPI credential.
1556 */
1557 Status = AcquireCredentialsHandle(NULL, UNISP_NAME,
1558 isServer ? SECPKG_CRED_INBOUND:SECPKG_CRED_OUTBOUND,
1559 NULL, &SchannelCred, NULL, NULL, &conn->creds,
1560 &tsExpiry);
1561 if (Status != SEC_E_OK)
1562 {
1563 DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status));
1564 ok = FALSE;
1565 goto cleanup;
1566 }
1567
1568 cleanup:
1569
1570 /*
1571 * Cleanup
1572 */
1573 if (hKey)
1574 CryptDestroyKey(hKey);
1575
1576 if (createdContext)
1577 CertFreeCertificateContext(createdContext);
1578
1579 if (storedContext)
1580 CertFreeCertificateContext(storedContext);
1581
1582 if (p)
1583 free(p);
1584
1585 if (store)
1586 CertCloseStore(store, 0);
1587
1588 if (hProv)
1589 CryptReleaseContext(hProv, 0);
1590
1591 return (ok);
1592 }
1593 #endif // 0
1594
1595
1596 /*
1597 * 'http_sspi_server()' - Negotiate a TLS connection as a server.
1598 */
1599
1600 static int /* O - 0 on success, -1 on failure */
1601 http_sspi_server(http_t *http, /* I - HTTP connection */
1602 const char *hostname) /* I - Hostname of server */
1603 {
1604 _http_sspi_t *conn = http->tls; /* I - SSPI data */
1605 DWORD dwSSPIFlags; /* SSL connection attributes we want */
1606 DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
1607 TimeStamp tsExpiry; /* Time stamp */
1608 SECURITY_STATUS scRet; /* SSPI Status */
1609 SecBufferDesc inBuffer; /* Array of SecBuffer structs */
1610 SecBuffer inBuffers[2]; /* Security package buffer */
1611 SecBufferDesc outBuffer; /* Array of SecBuffer structs */
1612 SecBuffer outBuffers[1]; /* Security package buffer */
1613 int num = 0; /* 32 bit status value */
1614 BOOL fInitContext = TRUE; /* Has the context been init'd? */
1615 int ret = 0; /* Return value */
1616
1617
1618 DEBUG_printf(("http_sspi_server(http=%p, hostname=\"%s\")", http, hostname));
1619
1620 dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
1621 ASC_REQ_REPLAY_DETECT |
1622 ASC_REQ_CONFIDENTIALITY |
1623 ASC_REQ_EXTENDED_ERROR |
1624 ASC_REQ_ALLOCATE_MEMORY |
1625 ASC_REQ_STREAM;
1626
1627 conn->decryptBufferUsed = 0;
1628
1629 /*
1630 * Set OutBuffer for AcceptSecurityContext call
1631 */
1632
1633 outBuffer.cBuffers = 1;
1634 outBuffer.pBuffers = outBuffers;
1635 outBuffer.ulVersion = SECBUFFER_VERSION;
1636
1637 scRet = SEC_I_CONTINUE_NEEDED;
1638
1639 while (scRet == SEC_I_CONTINUE_NEEDED ||
1640 scRet == SEC_E_INCOMPLETE_MESSAGE ||
1641 scRet == SEC_I_INCOMPLETE_CREDENTIALS)
1642 {
1643 if (conn->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
1644 {
1645 if (conn->decryptBufferLength <= conn->decryptBufferUsed)
1646 {
1647 BYTE *temp; /* New buffer */
1648
1649 if (conn->decryptBufferLength >= 262144)
1650 {
1651 WSASetLastError(E_OUTOFMEMORY);
1652 DEBUG_puts("http_sspi_server: Decryption buffer too large (>256k)");
1653 return (-1);
1654 }
1655
1656 if ((temp = realloc(conn->decryptBuffer, conn->decryptBufferLength + 4096)) == NULL)
1657 {
1658 DEBUG_printf(("http_sspi_server: Unable to allocate %d byte buffer.", conn->decryptBufferLength + 4096));
1659 WSASetLastError(E_OUTOFMEMORY);
1660 return (-1);
1661 }
1662
1663 conn->decryptBufferLength += 4096;
1664 conn->decryptBuffer = temp;
1665 }
1666
1667 for (;;)
1668 {
1669 num = recv(http->fd, conn->decryptBuffer + conn->decryptBufferUsed, (int)(conn->decryptBufferLength - conn->decryptBufferUsed), 0);
1670
1671 if (num == -1 && WSAGetLastError() == WSAEWOULDBLOCK)
1672 Sleep(1);
1673 else
1674 break;
1675 }
1676
1677 if (num < 0)
1678 {
1679 DEBUG_printf(("http_sspi_server: recv failed: %d", WSAGetLastError()));
1680 return (-1);
1681 }
1682 else if (num == 0)
1683 {
1684 DEBUG_puts("http_sspi_server: client disconnected");
1685 return (-1);
1686 }
1687
1688 DEBUG_printf(("http_sspi_server: received %d (handshake) bytes from client.", num));
1689 conn->decryptBufferUsed += num;
1690 }
1691
1692 /*
1693 * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process
1694 * on this run around the loop.
1695 */
1696
1697 inBuffers[0].pvBuffer = conn->decryptBuffer;
1698 inBuffers[0].cbBuffer = (unsigned long)conn->decryptBufferUsed;
1699 inBuffers[0].BufferType = SECBUFFER_TOKEN;
1700
1701 inBuffers[1].pvBuffer = NULL;
1702 inBuffers[1].cbBuffer = 0;
1703 inBuffers[1].BufferType = SECBUFFER_EMPTY;
1704
1705 inBuffer.cBuffers = 2;
1706 inBuffer.pBuffers = inBuffers;
1707 inBuffer.ulVersion = SECBUFFER_VERSION;
1708
1709 /*
1710 * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to
1711 * free random garbage at the quit.
1712 */
1713
1714 outBuffers[0].pvBuffer = NULL;
1715 outBuffers[0].BufferType = SECBUFFER_TOKEN;
1716 outBuffers[0].cbBuffer = 0;
1717
1718 scRet = AcceptSecurityContext(&conn->creds, (fInitContext?NULL:&conn->context), &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP, (fInitContext?&conn->context:NULL), &outBuffer, &dwSSPIOutFlags, &tsExpiry);
1719
1720 fInitContext = FALSE;
1721
1722 if (scRet == SEC_E_OK ||
1723 scRet == SEC_I_CONTINUE_NEEDED ||
1724 (FAILED(scRet) && ((dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) != 0)))
1725 {
1726 if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
1727 {
1728 /*
1729 * Send response to server if there is one.
1730 */
1731
1732 num = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
1733
1734 if (num <= 0)
1735 {
1736 DEBUG_printf(("http_sspi_server: handshake send failed: %d", WSAGetLastError()));
1737 return (-1);
1738 }
1739
1740 DEBUG_printf(("http_sspi_server: sent %d handshake bytes to client.", outBuffers[0].cbBuffer));
1741
1742 FreeContextBuffer(outBuffers[0].pvBuffer);
1743 outBuffers[0].pvBuffer = NULL;
1744 }
1745 }
1746
1747 if (scRet == SEC_E_OK)
1748 {
1749 /*
1750 * If there's extra data then save it for next time we go to decrypt.
1751 */
1752
1753 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
1754 {
1755 memcpy(conn->decryptBuffer, (LPBYTE)(conn->decryptBuffer + conn->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
1756 conn->decryptBufferUsed = inBuffers[1].cbBuffer;
1757 }
1758 else
1759 {
1760 conn->decryptBufferUsed = 0;
1761 }
1762 break;
1763 }
1764 else if (FAILED(scRet) && scRet != SEC_E_INCOMPLETE_MESSAGE)
1765 {
1766 DEBUG_printf(("http_sspi_server: AcceptSecurityContext failed: %x", scRet));
1767 ret = -1;
1768 break;
1769 }
1770
1771 if (scRet != SEC_E_INCOMPLETE_MESSAGE &&
1772 scRet != SEC_I_INCOMPLETE_CREDENTIALS)
1773 {
1774 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
1775 {
1776 memcpy(conn->decryptBuffer, (LPBYTE)(conn->decryptBuffer + conn->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
1777 conn->decryptBufferUsed = inBuffers[1].cbBuffer;
1778 }
1779 else
1780 {
1781 conn->decryptBufferUsed = 0;
1782 }
1783 }
1784 }
1785
1786 if (!ret)
1787 {
1788 conn->contextInitialized = TRUE;
1789
1790 /*
1791 * Find out how big the header will be:
1792 */
1793
1794 scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes);
1795
1796 if (scRet != SEC_E_OK)
1797 {
1798 DEBUG_printf(("http_sspi_server: QueryContextAttributes failed: %x", scRet));
1799 ret = -1;
1800 }
1801 }
1802
1803 return (ret);
1804 }
1805
1806
1807 #if 0
1808 /*
1809 * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
1810 */
1811 void
1812 _sspiSetAllowsAnyRoot(_http_sspi_t *conn,
1813 /* I - Client connection */
1814 BOOL allow)
1815 /* I - Allow any root */
1816 {
1817 conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_UNKNOWN_CA :
1818 conn->certFlags & ~SECURITY_FLAG_IGNORE_UNKNOWN_CA;
1819 }
1820
1821
1822 /*
1823 * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
1824 */
1825 void
1826 _sspiSetAllowsExpiredCerts(_http_sspi_t *conn,
1827 /* I - Client connection */
1828 BOOL allow)
1829 /* I - Allow expired certs */
1830 {
1831 conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID :
1832 conn->certFlags & ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
1833 }
1834
1835
1836 /*
1837 * 'http_sspi_verify()' - Verify a certificate.
1838 */
1839
1840 static DWORD /* O - Error code (0 == No error) */
1841 http_sspi_verify(
1842 PCCERT_CONTEXT cert, /* I - Server certificate */
1843 const char *common_name, /* I - Common name */
1844 DWORD dwCertFlags) /* I - Verification flags */
1845 {
1846 HTTPSPolicyCallbackData httpsPolicy; /* HTTPS Policy Struct */
1847 CERT_CHAIN_POLICY_PARA policyPara; /* Cert chain policy parameters */
1848 CERT_CHAIN_POLICY_STATUS policyStatus;/* Cert chain policy status */
1849 CERT_CHAIN_PARA chainPara; /* Used for searching and matching criteria */
1850 PCCERT_CHAIN_CONTEXT chainContext = NULL;
1851 /* Certificate chain */
1852 PWSTR commonNameUnicode = NULL;
1853 /* Unicode common name */
1854 LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
1855 szOID_SERVER_GATED_CRYPTO,
1856 szOID_SGC_NETSCAPE };
1857 /* How are we using this certificate? */
1858 DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
1859 /* Number of ites in rgszUsages */
1860 DWORD count; /* 32 bit count variable */
1861 DWORD status; /* Return value */
1862
1863
1864 if (!cert)
1865 return (SEC_E_WRONG_PRINCIPAL);
1866
1867 /*
1868 * Convert common name to Unicode.
1869 */
1870
1871 if (!common_name || !*common_name)
1872 return (SEC_E_WRONG_PRINCIPAL);
1873
1874 count = MultiByteToWideChar(CP_ACP, 0, common_name, -1, NULL, 0);
1875 commonNameUnicode = LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR));
1876 if (!commonNameUnicode)
1877 return (SEC_E_INSUFFICIENT_MEMORY);
1878
1879 if (!MultiByteToWideChar(CP_ACP, 0, common_name, -1, commonNameUnicode, count))
1880 {
1881 LocalFree(commonNameUnicode);
1882 return (SEC_E_WRONG_PRINCIPAL);
1883 }
1884
1885 /*
1886 * Build certificate chain.
1887 */
1888
1889 ZeroMemory(&chainPara, sizeof(chainPara));
1890
1891 chainPara.cbSize = sizeof(chainPara);
1892 chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
1893 chainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;
1894 chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
1895
1896 if (!CertGetCertificateChain(NULL, cert, NULL, cert->hCertStore, &chainPara, 0, NULL, &chainContext))
1897 {
1898 status = GetLastError();
1899 DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status));
1900
1901 LocalFree(commonNameUnicode);
1902 return (status);
1903 }
1904
1905 /*
1906 * Validate certificate chain.
1907 */
1908
1909 ZeroMemory(&httpsPolicy, sizeof(HTTPSPolicyCallbackData));
1910 httpsPolicy.cbStruct = sizeof(HTTPSPolicyCallbackData);
1911 httpsPolicy.dwAuthType = AUTHTYPE_SERVER;
1912 httpsPolicy.fdwChecks = dwCertFlags;
1913 httpsPolicy.pwszServerName = commonNameUnicode;
1914
1915 memset(&policyPara, 0, sizeof(policyPara));
1916 policyPara.cbSize = sizeof(policyPara);
1917 policyPara.pvExtraPolicyPara = &httpsPolicy;
1918
1919 memset(&policyStatus, 0, sizeof(policyStatus));
1920 policyStatus.cbSize = sizeof(policyStatus);
1921
1922 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext, &policyPara, &policyStatus))
1923 {
1924 status = GetLastError();
1925 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status));
1926 }
1927 else if (policyStatus.dwError)
1928 status = policyStatus.dwError;
1929 else
1930 status = SEC_E_OK;
1931
1932 if (chainContext)
1933 CertFreeCertificateChain(chainContext);
1934
1935 if (commonNameUnicode)
1936 LocalFree(commonNameUnicode);
1937
1938 return (status);
1939 }
1940 #endif // 0
1941
1942
1943 /*
1944 * End of "$Id$".
1945 */