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