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