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