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