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