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