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