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