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