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