]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/tls-sspi.c
8ffbf43d3217c171155e0b1f308767c8cb6be14b
[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 HCERTSTORE store = NULL; /* Certificate store */
536 PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
537 PCCERT_CONTEXT createdContext = NULL; /* Context created by us */
538 DWORD dwSize = 0; /* 32 bit size */
539 PBYTE p = NULL; /* Temporary storage */
540 HCRYPTPROV hProv = (HCRYPTPROV)NULL;
541 /* Handle to a CSP */
542 CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */
543 int ret = -1; /* Return value */
544 #ifdef DEBUG
545 char error[1024]; /* Error message buffer */
546 #endif /* DEBUG */
547
548
549 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
550
551 (void)path;
552
553 if (!common_name)
554 {
555 DEBUG_puts("1httpSaveCredentials: Bad common name, returning -1.");
556 return (-1);
557 }
558
559 createdContext = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
560 if (!createdContext)
561 {
562 DEBUG_puts("1httpSaveCredentials: Bad credentials, returning -1.");
563 return (-1);
564 }
565
566 if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
567 {
568 if (GetLastError() == NTE_EXISTS)
569 {
570 if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
571 {
572 DEBUG_printf(("1httpSaveCredentials: CryptAcquireContext failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
573 goto cleanup;
574 }
575 }
576 }
577
578 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");
579
580 if (!store)
581 {
582 DEBUG_printf(("1httpSaveCredentials: CertOpenSystemStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
583 goto cleanup;
584 }
585
586 dwSize = 0;
587
588 if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
589 {
590 DEBUG_printf(("1httpSaveCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
591 goto cleanup;
592 }
593
594 p = (PBYTE)malloc(dwSize);
595
596 if (!p)
597 {
598 DEBUG_printf(("1httpSaveCredentials: malloc failed for %d bytes.", dwSize));
599 goto cleanup;
600 }
601
602 if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
603 {
604 DEBUG_printf(("1httpSaveCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
605 goto cleanup;
606 }
607
608 /*
609 * Add the created context to the named store, and associate it with the named
610 * container...
611 */
612
613 if (!CertAddCertificateContextToStore(store, createdContext, CERT_STORE_ADD_REPLACE_EXISTING, &storedContext))
614 {
615 DEBUG_printf(("1httpSaveCredentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
616 goto cleanup;
617 }
618
619 ZeroMemory(&ckp, sizeof(ckp));
620 ckp.pwszContainerName = L"RememberedContainer";
621 ckp.pwszProvName = MS_DEF_PROV_W;
622 ckp.dwProvType = PROV_RSA_FULL;
623 ckp.dwFlags = CRYPT_MACHINE_KEYSET;
624 ckp.dwKeySpec = AT_KEYEXCHANGE;
625
626 if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp))
627 {
628 DEBUG_printf(("1httpSaveCredentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
629 goto cleanup;
630 }
631
632 ret = 0;
633
634 cleanup:
635
636 /*
637 * Cleanup
638 */
639
640 if (createdContext)
641 CertFreeCertificateContext(createdContext);
642
643 if (storedContext)
644 CertFreeCertificateContext(storedContext);
645
646 if (p)
647 free(p);
648
649 if (store)
650 CertCloseStore(store, 0);
651
652 if (hProv)
653 CryptReleaseContext(hProv, 0);
654
655 DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret));
656 return (ret);
657 }
658
659
660 /*
661 * '_httpTLSInitialize()' - Initialize the TLS stack.
662 */
663
664 void
665 _httpTLSInitialize(void)
666 {
667 /*
668 * Nothing to do...
669 */
670 }
671
672
673 /*
674 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
675 */
676
677 size_t /* O - Bytes available */
678 _httpTLSPending(http_t *http) /* I - HTTP connection */
679 {
680 if (http->tls)
681 return (http->tls->readBufferUsed);
682 else
683 return (0);
684 }
685
686
687 /*
688 * '_httpTLSRead()' - Read from a SSL/TLS connection.
689 */
690
691 int /* O - Bytes read */
692 _httpTLSRead(http_t *http, /* I - HTTP connection */
693 char *buf, /* I - Buffer to store data */
694 int len) /* I - Length of buffer */
695 {
696 int i; /* Looping var */
697 _http_sspi_t *sspi = http->tls; /* SSPI data */
698 SecBufferDesc message; /* Array of SecBuffer struct */
699 SecBuffer buffers[4] = { 0 }; /* Security package buffer */
700 int num = 0; /* Return value */
701 PSecBuffer pDataBuffer; /* Data buffer */
702 PSecBuffer pExtraBuffer; /* Excess data buffer */
703 SECURITY_STATUS scRet; /* SSPI status */
704
705
706 DEBUG_printf(("4_httpTLSRead(http=%p, buf=%p, len=%d)", http, buf, len));
707
708 /*
709 * If there are bytes that have already been decrypted and have not yet been
710 * read, return those...
711 */
712
713 if (sspi->readBufferUsed > 0)
714 {
715 int bytesToCopy = min(sspi->readBufferUsed, len);
716 /* Number of bytes to copy */
717
718 memcpy(buf, sspi->readBuffer, bytesToCopy);
719 sspi->readBufferUsed -= bytesToCopy;
720
721 if (sspi->readBufferUsed > 0)
722 memmove(sspi->readBuffer, sspi->readBuffer + bytesToCopy, sspi->readBufferUsed);
723
724 DEBUG_printf(("5_httpTLSRead: Returning %d bytes previously decrypted.", bytesToCopy));
725
726 return (bytesToCopy);
727 }
728
729 /*
730 * Initialize security buffer structs
731 */
732
733 message.ulVersion = SECBUFFER_VERSION;
734 message.cBuffers = 4;
735 message.pBuffers = buffers;
736
737 do
738 {
739 /*
740 * If there is not enough space in the buffer, then increase its size...
741 */
742
743 if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
744 {
745 BYTE *temp; /* New buffer */
746
747 if (sspi->decryptBufferLength >= 262144)
748 {
749 WSASetLastError(E_OUTOFMEMORY);
750 DEBUG_puts("_httpTLSRead: Decryption buffer too large (>256k)");
751 return (-1);
752 }
753
754 if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
755 {
756 DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte decryption buffer.", sspi->decryptBufferLength + 4096));
757 WSASetLastError(E_OUTOFMEMORY);
758 return (-1);
759 }
760
761 sspi->decryptBufferLength += 4096;
762 sspi->decryptBuffer = temp;
763
764 DEBUG_printf(("_httpTLSRead: Resized decryption buffer to %d bytes.", sspi->decryptBufferLength));
765 }
766
767 buffers[0].pvBuffer = sspi->decryptBuffer;
768 buffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed;
769 buffers[0].BufferType = SECBUFFER_DATA;
770 buffers[1].BufferType = SECBUFFER_EMPTY;
771 buffers[2].BufferType = SECBUFFER_EMPTY;
772 buffers[3].BufferType = SECBUFFER_EMPTY;
773
774 DEBUG_printf(("5_httpTLSRead: decryptBufferUsed=%d", sspi->decryptBufferUsed));
775
776 scRet = DecryptMessage(&sspi->context, &message, 0, NULL);
777
778 if (scRet == SEC_E_INCOMPLETE_MESSAGE)
779 {
780 num = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
781 if (num < 0)
782 {
783 DEBUG_printf(("5_httpTLSRead: recv failed: %d", WSAGetLastError()));
784 return (-1);
785 }
786 else if (num == 0)
787 {
788 DEBUG_puts("5_httpTLSRead: Server disconnected.");
789 return (0);
790 }
791
792 DEBUG_printf(("5_httpTLSRead: Read %d bytes into decryption buffer.", num));
793
794 sspi->decryptBufferUsed += num;
795 }
796 }
797 while (scRet == SEC_E_INCOMPLETE_MESSAGE);
798
799 if (scRet == SEC_I_CONTEXT_EXPIRED)
800 {
801 DEBUG_puts("5_httpTLSRead: Context expired.");
802 WSASetLastError(WSAECONNRESET);
803 return (-1);
804 }
805 else if (scRet != SEC_E_OK)
806 {
807 DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
808 WSASetLastError(WSASYSCALLFAILURE);
809 return (-1);
810 }
811
812 /*
813 * The decryption worked. Now, locate data buffer.
814 */
815
816 pDataBuffer = NULL;
817 pExtraBuffer = NULL;
818
819 for (i = 1; i < 4; i++)
820 {
821 if (buffers[i].BufferType == SECBUFFER_DATA)
822 pDataBuffer = &buffers[i];
823 else if (!pExtraBuffer && (buffers[i].BufferType == SECBUFFER_EXTRA))
824 pExtraBuffer = &buffers[i];
825 }
826
827 /*
828 * If a data buffer is found, then copy the decrypted bytes to the passed-in
829 * buffer...
830 */
831
832 if (pDataBuffer)
833 {
834 int bytesToCopy = min((int)pDataBuffer->cbBuffer, len);
835 /* Number of bytes to copy into buf */
836 int bytesToSave = pDataBuffer->cbBuffer - bytesToCopy;
837 /* Number of bytes to save in our read buffer */
838
839 if (bytesToCopy)
840 memcpy(buf, pDataBuffer->pvBuffer, bytesToCopy);
841
842 /*
843 * If there are more decrypted bytes than can be copied to the passed in
844 * buffer, then save them...
845 */
846
847 if (bytesToSave)
848 {
849 if ((sspi->readBufferLength - sspi->readBufferUsed) < bytesToSave)
850 {
851 BYTE *temp; /* New buffer pointer */
852
853 if ((temp = realloc(sspi->readBuffer, sspi->readBufferUsed + bytesToSave)) == NULL)
854 {
855 DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", sspi->readBufferUsed + bytesToSave));
856 WSASetLastError(E_OUTOFMEMORY);
857 return (-1);
858 }
859
860 sspi->readBufferLength = sspi->readBufferUsed + bytesToSave;
861 sspi->readBuffer = temp;
862 }
863
864 memcpy(((BYTE *)sspi->readBuffer) + sspi->readBufferUsed, ((BYTE *)pDataBuffer->pvBuffer) + bytesToCopy, bytesToSave);
865
866 sspi->readBufferUsed += bytesToSave;
867 }
868
869 num = bytesToCopy;
870 }
871 else
872 {
873 DEBUG_puts("_httpTLSRead: Unable to find data buffer.");
874 WSASetLastError(WSASYSCALLFAILURE);
875 return (-1);
876 }
877
878 /*
879 * If the decryption process left extra bytes, then save those back in
880 * decryptBuffer. They will be processed the next time through the loop.
881 */
882
883 if (pExtraBuffer)
884 {
885 memmove(sspi->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
886 sspi->decryptBufferUsed = pExtraBuffer->cbBuffer;
887 }
888 else
889 {
890 sspi->decryptBufferUsed = 0;
891 }
892
893 return (num);
894 }
895
896
897 /*
898 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
899 */
900
901 int /* O - 0 on success, -1 on failure */
902 _httpTLSStart(http_t *http) /* I - HTTP connection */
903 {
904 char hostname[256], /* Hostname */
905 *hostptr; /* Pointer into hostname */
906
907
908 DEBUG_printf(("7_httpTLSStart(http=%p)", http));
909
910 if ((http->tls = http_sspi_alloc()) == NULL)
911 return (-1);
912
913 if (http->mode == _HTTP_MODE_CLIENT)
914 {
915 /*
916 * Client: determine hostname...
917 */
918
919 if (httpAddrLocalhost(http->hostaddr))
920 {
921 strlcpy(hostname, "localhost", sizeof(hostname));
922 }
923 else
924 {
925 /*
926 * Otherwise make sure the hostname we have does not end in a trailing dot.
927 */
928
929 strlcpy(hostname, http->hostname, sizeof(hostname));
930 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
931 *hostptr == '.')
932 *hostptr = '\0';
933 }
934
935 return (http_sspi_client(http, hostname));
936 }
937 else
938 {
939 /*
940 * Server: determine hostname to use...
941 */
942
943 if (http->fields[HTTP_FIELD_HOST][0])
944 {
945 /*
946 * Use hostname for TLS upgrade...
947 */
948
949 strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
950 }
951 else
952 {
953 /*
954 * Resolve hostname from connection address...
955 */
956
957 http_addr_t addr; /* Connection address */
958 socklen_t addrlen; /* Length of address */
959
960 addrlen = sizeof(addr);
961 if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
962 {
963 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
964 hostname[0] = '\0';
965 }
966 else if (httpAddrLocalhost(&addr))
967 hostname[0] = '\0';
968 else
969 {
970 httpAddrLookup(&addr, hostname, sizeof(hostname));
971 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
972 }
973 }
974
975 return (http_sspi_server(http, hostname));
976 }
977 }
978
979
980 /*
981 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
982 */
983
984 void
985 _httpTLSStop(http_t *http) /* I - HTTP connection */
986 {
987 _http_sspi_t *sspi = http->tls; /* SSPI data */
988
989
990 if (sspi->contextInitialized && http->fd >= 0)
991 {
992 SecBufferDesc message; /* Array of SecBuffer struct */
993 SecBuffer buffers[1] = { 0 };
994 /* Security package buffer */
995 DWORD dwType; /* Type */
996 DWORD status; /* Status */
997
998 /*
999 * Notify schannel that we are about to close the connection.
1000 */
1001
1002 dwType = SCHANNEL_SHUTDOWN;
1003
1004 buffers[0].pvBuffer = &dwType;
1005 buffers[0].BufferType = SECBUFFER_TOKEN;
1006 buffers[0].cbBuffer = sizeof(dwType);
1007
1008 message.cBuffers = 1;
1009 message.pBuffers = buffers;
1010 message.ulVersion = SECBUFFER_VERSION;
1011
1012 status = ApplyControlToken(&sspi->context, &message);
1013
1014 if (SUCCEEDED(status))
1015 {
1016 PBYTE pbMessage; /* Message buffer */
1017 DWORD cbMessage; /* Message buffer count */
1018 DWORD cbData; /* Data count */
1019 DWORD dwSSPIFlags; /* SSL attributes we requested */
1020 DWORD dwSSPIOutFlags; /* SSL attributes we received */
1021 TimeStamp tsExpiry; /* Time stamp */
1022
1023 dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
1024 ASC_REQ_REPLAY_DETECT |
1025 ASC_REQ_CONFIDENTIALITY |
1026 ASC_REQ_EXTENDED_ERROR |
1027 ASC_REQ_ALLOCATE_MEMORY |
1028 ASC_REQ_STREAM;
1029
1030 buffers[0].pvBuffer = NULL;
1031 buffers[0].BufferType = SECBUFFER_TOKEN;
1032 buffers[0].cbBuffer = 0;
1033
1034 message.cBuffers = 1;
1035 message.pBuffers = buffers;
1036 message.ulVersion = SECBUFFER_VERSION;
1037
1038 status = AcceptSecurityContext(&sspi->creds, &sspi->context, NULL,
1039 dwSSPIFlags, SECURITY_NATIVE_DREP, NULL,
1040 &message, &dwSSPIOutFlags, &tsExpiry);
1041
1042 if (SUCCEEDED(status))
1043 {
1044 pbMessage = buffers[0].pvBuffer;
1045 cbMessage = buffers[0].cbBuffer;
1046
1047 /*
1048 * Send the close notify message to the client.
1049 */
1050
1051 if (pbMessage && cbMessage)
1052 {
1053 cbData = send(http->fd, pbMessage, cbMessage, 0);
1054 if ((cbData == SOCKET_ERROR) || (cbData == 0))
1055 {
1056 status = WSAGetLastError();
1057 DEBUG_printf(("_httpTLSStop: sending close notify failed: %d", status));
1058 }
1059 else
1060 {
1061 FreeContextBuffer(pbMessage);
1062 }
1063 }
1064 }
1065 else
1066 {
1067 DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), status)));
1068 }
1069 }
1070 else
1071 {
1072 DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), status)));
1073 }
1074 }
1075
1076 http_sspi_free(sspi);
1077
1078 http->tls = NULL;
1079 }
1080
1081
1082 /*
1083 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
1084 */
1085
1086 int /* O - Bytes written */
1087 _httpTLSWrite(http_t *http, /* I - HTTP connection */
1088 const char *buf, /* I - Buffer holding data */
1089 int len) /* I - Length of buffer */
1090 {
1091 _http_sspi_t *sspi = http->tls; /* SSPI data */
1092 SecBufferDesc message; /* Array of SecBuffer struct */
1093 SecBuffer buffers[4] = { 0 }; /* Security package buffer */
1094 int bufferLen; /* Buffer length */
1095 int bytesLeft; /* Bytes left to write */
1096 const char *bufptr; /* Pointer into buffer */
1097 int num = 0; /* Return value */
1098
1099
1100 bufferLen = sspi->streamSizes.cbMaximumMessage + sspi->streamSizes.cbHeader + sspi->streamSizes.cbTrailer;
1101
1102 if (bufferLen > sspi->writeBufferLength)
1103 {
1104 BYTE *temp; /* New buffer pointer */
1105
1106 if ((temp = (BYTE *)realloc(sspi->writeBuffer, bufferLen)) == NULL)
1107 {
1108 DEBUG_printf(("_httpTLSWrite: Unable to allocate buffer of %d bytes.", bufferLen));
1109 WSASetLastError(E_OUTOFMEMORY);
1110 return (-1);
1111 }
1112
1113 sspi->writeBuffer = temp;
1114 sspi->writeBufferLength = bufferLen;
1115 }
1116
1117 bytesLeft = len;
1118 bufptr = buf;
1119
1120 while (bytesLeft)
1121 {
1122 int chunk = min((int)sspi->streamSizes.cbMaximumMessage, bytesLeft);
1123 /* Size of data to write */
1124 SECURITY_STATUS scRet; /* SSPI status */
1125
1126 /*
1127 * Copy user data into the buffer, starting just past the header...
1128 */
1129
1130 memcpy(sspi->writeBuffer + sspi->streamSizes.cbHeader, bufptr, chunk);
1131
1132 /*
1133 * Setup the SSPI buffers
1134 */
1135
1136 message.ulVersion = SECBUFFER_VERSION;
1137 message.cBuffers = 4;
1138 message.pBuffers = buffers;
1139
1140 buffers[0].pvBuffer = sspi->writeBuffer;
1141 buffers[0].cbBuffer = sspi->streamSizes.cbHeader;
1142 buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
1143 buffers[1].pvBuffer = sspi->writeBuffer + sspi->streamSizes.cbHeader;
1144 buffers[1].cbBuffer = (unsigned long) chunk;
1145 buffers[1].BufferType = SECBUFFER_DATA;
1146 buffers[2].pvBuffer = sspi->writeBuffer + sspi->streamSizes.cbHeader + chunk;
1147 buffers[2].cbBuffer = sspi->streamSizes.cbTrailer;
1148 buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
1149 buffers[3].BufferType = SECBUFFER_EMPTY;
1150
1151 /*
1152 * Encrypt the data
1153 */
1154
1155 scRet = EncryptMessage(&sspi->context, 0, &message, 0);
1156
1157 if (FAILED(scRet))
1158 {
1159 DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
1160 WSASetLastError(WSASYSCALLFAILURE);
1161 return (-1);
1162 }
1163
1164 /*
1165 * Send the data. Remember the size of the total data to send is the size
1166 * of the header, the size of the data the caller passed in and the size
1167 * of the trailer...
1168 */
1169
1170 num = send(http->fd, sspi->writeBuffer, buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer, 0);
1171
1172 if (num <= 0)
1173 {
1174 DEBUG_printf(("_httpTLSWrite: send failed: %ld", WSAGetLastError()));
1175 return (num);
1176 }
1177
1178 bytesLeft -= chunk;
1179 bufptr += chunk;
1180 }
1181
1182 return (len);
1183 }
1184
1185
1186 #if 0
1187 /*
1188 * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
1189 */
1190
1191 static int /* O - 0 on success, -1 on failure */
1192 http_setup_ssl(http_t *http) /* I - Connection to server */
1193 {
1194 char hostname[256], /* Hostname */
1195 *hostptr; /* Pointer into hostname */
1196
1197 TCHAR username[256]; /* Username returned from GetUserName() */
1198 TCHAR commonName[256];/* Common name for certificate */
1199 DWORD dwSize; /* 32 bit size */
1200
1201
1202 DEBUG_printf(("7http_setup_ssl(http=%p)", http));
1203
1204 /*
1205 * Get the hostname to use for SSL...
1206 */
1207
1208 if (httpAddrLocalhost(http->hostaddr))
1209 {
1210 strlcpy(hostname, "localhost", sizeof(hostname));
1211 }
1212 else
1213 {
1214 /*
1215 * Otherwise make sure the hostname we have does not end in a trailing dot.
1216 */
1217
1218 strlcpy(hostname, http->hostname, sizeof(hostname));
1219 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
1220 *hostptr == '.')
1221 *hostptr = '\0';
1222 }
1223
1224 http->tls = http_sspi_alloc();
1225
1226 if (!http->tls)
1227 {
1228 _cupsSetHTTPError(HTTP_STATUS_ERROR);
1229 return (-1);
1230 }
1231
1232 dwSize = sizeof(username) / sizeof(TCHAR);
1233 GetUserName(username, &dwSize);
1234 _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR),
1235 sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username);
1236
1237 if (!_sspiGetCredentials(http->tls, L"ClientContainer",
1238 commonName, FALSE))
1239 {
1240 _sspiFree(http->tls);
1241 http->tls = NULL;
1242
1243 http->error = EIO;
1244 http->status = HTTP_STATUS_ERROR;
1245
1246 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
1247 _("Unable to establish a secure connection to host."), 1);
1248
1249 return (-1);
1250 }
1251
1252 _sspiSetAllowsAnyRoot(http->tls, TRUE);
1253 _sspiSetAllowsExpiredCerts(http->tls, TRUE);
1254
1255 if (!_sspiConnect(http->tls, hostname))
1256 {
1257 _sspiFree(http->tls);
1258 http->tls = NULL;
1259
1260 http->error = EIO;
1261 http->status = HTTP_STATUS_ERROR;
1262
1263 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
1264 _("Unable to establish a secure connection to host."), 1);
1265
1266 return (-1);
1267 }
1268
1269 return (0);
1270 }
1271 #endif // 0
1272
1273
1274 /*
1275 * 'http_sspi_alloc()' - Allocate SSPI object.
1276 */
1277
1278 static _http_sspi_t * /* O - New SSPI/SSL object */
1279 http_sspi_alloc(void)
1280 {
1281 return ((_http_sspi_t *)calloc(sizeof(_http_sspi_t), 1));
1282 }
1283
1284
1285 /*
1286 * 'http_sspi_client()' - Negotiate a TLS connection as a client.
1287 */
1288
1289 static int /* O - 0 on success, -1 on failure */
1290 http_sspi_client(http_t *http, /* I - Client connection */
1291 const char *hostname) /* I - Server hostname */
1292 {
1293 _http_sspi_t *sspi = http->tls; /* SSPI data */
1294 DWORD dwSize; /* Size for buffer */
1295 DWORD dwSSPIFlags; /* SSL connection attributes we want */
1296 DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
1297 TimeStamp tsExpiry; /* Time stamp */
1298 SECURITY_STATUS scRet; /* Status */
1299 int cbData; /* Data count */
1300 SecBufferDesc inBuffer; /* Array of SecBuffer structs */
1301 SecBuffer inBuffers[2]; /* Security package buffer */
1302 SecBufferDesc outBuffer; /* Array of SecBuffer structs */
1303 SecBuffer outBuffers[1]; /* Security package buffer */
1304 int ret = 0; /* Return value */
1305 char username[1024], /* Current username */
1306 common_name[1024]; /* CN=username */
1307
1308
1309 DEBUG_printf(("4http_sspi_client(http=%p, hostname=\"%s\")", http, hostname));
1310
1311 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
1312 ISC_REQ_REPLAY_DETECT |
1313 ISC_REQ_CONFIDENTIALITY |
1314 ISC_RET_EXTENDED_ERROR |
1315 ISC_REQ_ALLOCATE_MEMORY |
1316 ISC_REQ_STREAM;
1317
1318 /*
1319 * Lookup the client certificate...
1320 */
1321
1322 dwSize = sizeof(username);
1323 GetUserName(username, &dwSize);
1324 snprintf(common_name, sizeof(common_name), "CN=%s", username);
1325
1326 if (!http_sspi_find_credentials(http, L"ClientContainer", common_name))
1327 if (!http_sspi_make_credentials(http->tls, L"ClientContainer", common_name, _HTTP_MODE_CLIENT, 10))
1328 {
1329 DEBUG_puts("5http_sspi_client: Unable to get client credentials.");
1330 return (-1);
1331 }
1332
1333 /*
1334 * Initiate a ClientHello message and generate a token.
1335 */
1336
1337 outBuffers[0].pvBuffer = NULL;
1338 outBuffers[0].BufferType = SECBUFFER_TOKEN;
1339 outBuffers[0].cbBuffer = 0;
1340
1341 outBuffer.cBuffers = 1;
1342 outBuffer.pBuffers = outBuffers;
1343 outBuffer.ulVersion = SECBUFFER_VERSION;
1344
1345 scRet = InitializeSecurityContext(&sspi->creds, NULL, TEXT(""), dwSSPIFlags, 0, SECURITY_NATIVE_DREP, NULL, 0, &sspi->context, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
1346
1347 if (scRet != SEC_I_CONTINUE_NEEDED)
1348 {
1349 DEBUG_printf(("5http_sspi_client: InitializeSecurityContext(1) failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
1350 return (-1);
1351 }
1352
1353 /*
1354 * Send response to server if there is one.
1355 */
1356
1357 if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
1358 {
1359 if ((cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0)) <= 0)
1360 {
1361 DEBUG_printf(("5http_sspi_client: send failed: %d", WSAGetLastError()));
1362 FreeContextBuffer(outBuffers[0].pvBuffer);
1363 DeleteSecurityContext(&sspi->context);
1364 return (-1);
1365 }
1366
1367 DEBUG_printf(("5http_sspi_client: %d bytes of handshake data sent.", cbData));
1368
1369 FreeContextBuffer(outBuffers[0].pvBuffer);
1370 outBuffers[0].pvBuffer = NULL;
1371 }
1372
1373 dwSSPIFlags = ISC_REQ_MANUAL_CRED_VALIDATION |
1374 ISC_REQ_SEQUENCE_DETECT |
1375 ISC_REQ_REPLAY_DETECT |
1376 ISC_REQ_CONFIDENTIALITY |
1377 ISC_RET_EXTENDED_ERROR |
1378 ISC_REQ_ALLOCATE_MEMORY |
1379 ISC_REQ_STREAM;
1380
1381 sspi->decryptBufferUsed = 0;
1382
1383 /*
1384 * Loop until the handshake is finished or an error occurs.
1385 */
1386
1387 scRet = SEC_I_CONTINUE_NEEDED;
1388
1389 while(scRet == SEC_I_CONTINUE_NEEDED ||
1390 scRet == SEC_E_INCOMPLETE_MESSAGE ||
1391 scRet == SEC_I_INCOMPLETE_CREDENTIALS)
1392 {
1393 if (sspi->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
1394 {
1395 if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
1396 {
1397 BYTE *temp; /* New buffer */
1398
1399 if (sspi->decryptBufferLength >= 262144)
1400 {
1401 WSASetLastError(E_OUTOFMEMORY);
1402 DEBUG_puts("5http_sspi_client: Decryption buffer too large (>256k)");
1403 return (-1);
1404 }
1405
1406 if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
1407 {
1408 DEBUG_printf(("5http_sspi_client: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
1409 WSASetLastError(E_OUTOFMEMORY);
1410 return (-1);
1411 }
1412
1413 sspi->decryptBufferLength += 4096;
1414 sspi->decryptBuffer = temp;
1415 }
1416
1417 cbData = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
1418
1419 if (cbData < 0)
1420 {
1421 DEBUG_printf(("5http_sspi_client: recv failed: %d", WSAGetLastError()));
1422 return (-1);
1423 }
1424 else if (cbData == 0)
1425 {
1426 DEBUG_printf(("5http_sspi_client: Server unexpectedly disconnected."));
1427 return (-1);
1428 }
1429
1430 DEBUG_printf(("5http_sspi_client: %d bytes of handshake data received", cbData));
1431
1432 sspi->decryptBufferUsed += cbData;
1433 }
1434
1435 /*
1436 * Set up the input buffers. Buffer 0 is used to pass in data received from
1437 * the server. Schannel will consume some or all of this. Leftover data
1438 * (if any) will be placed in buffer 1 and given a buffer type of
1439 * SECBUFFER_EXTRA.
1440 */
1441
1442 inBuffers[0].pvBuffer = sspi->decryptBuffer;
1443 inBuffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed;
1444 inBuffers[0].BufferType = SECBUFFER_TOKEN;
1445
1446 inBuffers[1].pvBuffer = NULL;
1447 inBuffers[1].cbBuffer = 0;
1448 inBuffers[1].BufferType = SECBUFFER_EMPTY;
1449
1450 inBuffer.cBuffers = 2;
1451 inBuffer.pBuffers = inBuffers;
1452 inBuffer.ulVersion = SECBUFFER_VERSION;
1453
1454 /*
1455 * Set up the output buffers. These are initialized to NULL so as to make it
1456 * less likely we'll attempt to free random garbage later.
1457 */
1458
1459 outBuffers[0].pvBuffer = NULL;
1460 outBuffers[0].BufferType = SECBUFFER_TOKEN;
1461 outBuffers[0].cbBuffer = 0;
1462
1463 outBuffer.cBuffers = 1;
1464 outBuffer.pBuffers = outBuffers;
1465 outBuffer.ulVersion = SECBUFFER_VERSION;
1466
1467 /*
1468 * Call InitializeSecurityContext.
1469 */
1470
1471 scRet = InitializeSecurityContext(&sspi->creds, &sspi->context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
1472
1473 /*
1474 * If InitializeSecurityContext was successful (or if the error was one of
1475 * the special extended ones), send the contents of the output buffer to the
1476 * server.
1477 */
1478
1479 if (scRet == SEC_E_OK ||
1480 scRet == SEC_I_CONTINUE_NEEDED ||
1481 FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
1482 {
1483 if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
1484 {
1485 cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
1486
1487 if (cbData <= 0)
1488 {
1489 DEBUG_printf(("5http_sspi_client: send failed: %d", WSAGetLastError()));
1490 FreeContextBuffer(outBuffers[0].pvBuffer);
1491 DeleteSecurityContext(&sspi->context);
1492 return (-1);
1493 }
1494
1495 DEBUG_printf(("5http_sspi_client: %d bytes of handshake data sent.", cbData));
1496
1497 /*
1498 * Free output buffer.
1499 */
1500
1501 FreeContextBuffer(outBuffers[0].pvBuffer);
1502 outBuffers[0].pvBuffer = NULL;
1503 }
1504 }
1505
1506 /*
1507 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we
1508 * need to read more data from the server and try again.
1509 */
1510
1511 if (scRet == SEC_E_INCOMPLETE_MESSAGE)
1512 continue;
1513
1514 /*
1515 * If InitializeSecurityContext returned SEC_E_OK, then the handshake
1516 * completed successfully.
1517 */
1518
1519 if (scRet == SEC_E_OK)
1520 {
1521 /*
1522 * If the "extra" buffer contains data, this is encrypted application
1523 * protocol layer stuff. It needs to be saved. The application layer will
1524 * later decrypt it with DecryptMessage.
1525 */
1526
1527 DEBUG_puts("5http_sspi_client: Handshake was successful.");
1528
1529 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
1530 {
1531 memmove(sspi->decryptBuffer, sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
1532
1533 sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
1534
1535 DEBUG_printf(("5http_sspi_client: %d bytes of app data was bundled with handshake data", sspi->decryptBufferUsed));
1536 }
1537 else
1538 sspi->decryptBufferUsed = 0;
1539
1540 /*
1541 * Bail out to quit
1542 */
1543
1544 break;
1545 }
1546
1547 /*
1548 * Check for fatal error.
1549 */
1550
1551 if (FAILED(scRet))
1552 {
1553 DEBUG_printf(("5http_sspi_client: InitializeSecurityContext(2) failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
1554 ret = -1;
1555 break;
1556 }
1557
1558 /*
1559 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1560 * then the server just requested client authentication.
1561 */
1562
1563 if (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
1564 {
1565 /*
1566 * Unimplemented
1567 */
1568
1569 DEBUG_printf(("5http_sspi_client: server requested client credentials."));
1570 ret = -1;
1571 break;
1572 }
1573
1574 /*
1575 * Copy any leftover data from the "extra" buffer, and go around again.
1576 */
1577
1578 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
1579 {
1580 memmove(sspi->decryptBuffer, sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
1581
1582 sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
1583 }
1584 else
1585 {
1586 sspi->decryptBufferUsed = 0;
1587 }
1588 }
1589
1590 if (!ret)
1591 {
1592 /*
1593 * Success! Get the server cert
1594 */
1595
1596 sspi->contextInitialized = TRUE;
1597
1598 scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID *)&(sspi->remoteCert));
1599
1600 if (scRet != SEC_E_OK)
1601 {
1602 DEBUG_printf(("5http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
1603 return (-1);
1604 }
1605
1606 /*
1607 * Find out how big the header/trailer will be:
1608 */
1609
1610 scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_STREAM_SIZES, &sspi->streamSizes);
1611
1612 if (scRet != SEC_E_OK)
1613 {
1614 DEBUG_printf(("5http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
1615 ret = -1;
1616 }
1617 }
1618
1619 return (ret);
1620 }
1621
1622
1623 /*
1624 * 'http_sspi_create_credential()' - Create an SSPI certificate context.
1625 */
1626
1627 static PCCERT_CONTEXT /* O - Certificate context */
1628 http_sspi_create_credential(
1629 http_credential_t *cred) /* I - Credential */
1630 {
1631 if (cred)
1632 return (CertCreateCertificateContext(X509_ASN_ENCODING, cred->data, cred->datalen));
1633 else
1634 return (NULL);
1635 }
1636
1637
1638 /*
1639 * 'http_sspi_find_credentials()' - Retrieve a TLS certificate from the system store.
1640 */
1641
1642 static BOOL /* O - 1 on success, 0 on failure */
1643 http_sspi_find_credentials(
1644 http_t *http, /* I - HTTP connection */
1645 const LPWSTR container, /* I - Cert container name */
1646 const char *common_name) /* I - Common name of certificate */
1647 {
1648 _http_sspi_t *sspi = http->tls; /* SSPI data */
1649 HCERTSTORE store = NULL; /* Certificate store */
1650 PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
1651 DWORD dwSize = 0; /* 32 bit size */
1652 PBYTE p = NULL; /* Temporary storage */
1653 HCRYPTPROV hProv = (HCRYPTPROV)NULL;
1654 /* Handle to a CSP */
1655 CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
1656 SCHANNEL_CRED SchannelCred; /* Schannel credential data */
1657 TimeStamp tsExpiry; /* Time stamp */
1658 SECURITY_STATUS Status; /* Status */
1659 BOOL ok = TRUE; /* Return value */
1660
1661
1662 if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
1663 {
1664 if (GetLastError() == NTE_EXISTS)
1665 {
1666 if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
1667 {
1668 DEBUG_printf(("5http_sspi_find_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1669 ok = FALSE;
1670 goto cleanup;
1671 }
1672 }
1673 }
1674
1675 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");
1676
1677 if (!store)
1678 {
1679 DEBUG_printf(("5http_sspi_find_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1680 ok = FALSE;
1681 goto cleanup;
1682 }
1683
1684 dwSize = 0;
1685
1686 if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
1687 {
1688 DEBUG_printf(("5http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1689 ok = FALSE;
1690 goto cleanup;
1691 }
1692
1693 p = (PBYTE)malloc(dwSize);
1694
1695 if (!p)
1696 {
1697 DEBUG_printf(("5http_sspi_find_credentials: malloc failed for %d bytes.", dwSize));
1698 ok = FALSE;
1699 goto cleanup;
1700 }
1701
1702 if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
1703 {
1704 DEBUG_printf(("5http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1705 ok = FALSE;
1706 goto cleanup;
1707 }
1708
1709 sib.cbData = dwSize;
1710 sib.pbData = p;
1711
1712 storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &sib, NULL);
1713
1714 if (!storedContext)
1715 {
1716 DEBUG_printf(("5http_sspi_find_credentials: Unable to find credentials for \"%s\".", common_name));
1717 ok = FALSE;
1718 goto cleanup;
1719 }
1720
1721 ZeroMemory(&SchannelCred, sizeof(SchannelCred));
1722
1723 SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
1724 SchannelCred.cCreds = 1;
1725 SchannelCred.paCred = &storedContext;
1726
1727 /*
1728 * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
1729 */
1730
1731 if (http->mode == _HTTP_MODE_SERVER)
1732 SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
1733
1734 /*
1735 * Create an SSPI credential.
1736 */
1737
1738 Status = AcquireCredentialsHandle(NULL, UNISP_NAME, http->mode == _HTTP_MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &sspi->creds, &tsExpiry);
1739 if (Status != SEC_E_OK)
1740 {
1741 DEBUG_printf(("5http_sspi_find_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), Status)));
1742 ok = FALSE;
1743 goto cleanup;
1744 }
1745
1746 cleanup:
1747
1748 /*
1749 * Cleanup
1750 */
1751
1752 if (storedContext)
1753 CertFreeCertificateContext(storedContext);
1754
1755 if (p)
1756 free(p);
1757
1758 if (store)
1759 CertCloseStore(store, 0);
1760
1761 if (hProv)
1762 CryptReleaseContext(hProv, 0);
1763
1764 return (ok);
1765 }
1766
1767
1768 /*
1769 * 'http_sspi_free()' - Close a connection and free resources.
1770 */
1771
1772 static void
1773 http_sspi_free(_http_sspi_t *sspi) /* I - SSPI data */
1774 {
1775 if (!sspi)
1776 return;
1777
1778 if (sspi->contextInitialized)
1779 DeleteSecurityContext(&sspi->context);
1780
1781 if (sspi->decryptBuffer)
1782 free(sspi->decryptBuffer);
1783
1784 if (sspi->readBuffer)
1785 free(sspi->readBuffer);
1786
1787 if (sspi->writeBuffer)
1788 free(sspi->writeBuffer);
1789
1790 if (sspi->localCert)
1791 CertFreeCertificateContext(sspi->localCert);
1792
1793 if (sspi->remoteCert)
1794 CertFreeCertificateContext(sspi->remoteCert);
1795
1796 free(sspi);
1797 }
1798
1799
1800 /*
1801 * 'http_sspi_make_credentials()' - Create a TLS certificate in the system store.
1802 */
1803
1804 static BOOL /* O - 1 on success, 0 on failure */
1805 http_sspi_make_credentials(
1806 _http_sspi_t *sspi, /* I - SSPI data */
1807 const LPWSTR container, /* I - Cert container name */
1808 const char *common_name, /* I - Common name of certificate */
1809 _http_mode_t mode, /* I - Client or server? */
1810 int years) /* I - Years until expiration */
1811 {
1812 HCERTSTORE store = NULL; /* Certificate store */
1813 PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
1814 PCCERT_CONTEXT createdContext = NULL; /* Context created by us */
1815 DWORD dwSize = 0; /* 32 bit size */
1816 PBYTE p = NULL; /* Temporary storage */
1817 HCRYPTPROV hProv = (HCRYPTPROV)NULL;
1818 /* Handle to a CSP */
1819 CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
1820 SCHANNEL_CRED SchannelCred; /* Schannel credential data */
1821 TimeStamp tsExpiry; /* Time stamp */
1822 SECURITY_STATUS Status; /* Status */
1823 HCRYPTKEY hKey = (HCRYPTKEY)NULL; /* Handle to crypto key */
1824 CRYPT_KEY_PROV_INFO kpi; /* Key container info */
1825 SYSTEMTIME et; /* System time */
1826 CERT_EXTENSIONS exts; /* Array of cert extensions */
1827 CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */
1828 BOOL ok = TRUE; /* Return value */
1829
1830
1831 DEBUG_printf(("4http_sspi_make_credentials(sspi=%p, container=%p, common_name=\"%s\", mode=%d, years=%d)", sspi, container, common_name, mode, years));
1832
1833 if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
1834 {
1835 if (GetLastError() == NTE_EXISTS)
1836 {
1837 if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
1838 {
1839 DEBUG_printf(("5http_sspi_make_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1840 ok = FALSE;
1841 goto cleanup;
1842 }
1843 }
1844 }
1845
1846 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");
1847
1848 if (!store)
1849 {
1850 DEBUG_printf(("5http_sspi_make_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1851 ok = FALSE;
1852 goto cleanup;
1853 }
1854
1855 dwSize = 0;
1856
1857 if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
1858 {
1859 DEBUG_printf(("5http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1860 ok = FALSE;
1861 goto cleanup;
1862 }
1863
1864 p = (PBYTE)malloc(dwSize);
1865
1866 if (!p)
1867 {
1868 DEBUG_printf(("5http_sspi_make_credentials: malloc failed for %d bytes", dwSize));
1869 ok = FALSE;
1870 goto cleanup;
1871 }
1872
1873 if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
1874 {
1875 DEBUG_printf(("5http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1876 ok = FALSE;
1877 goto cleanup;
1878 }
1879
1880 /*
1881 * Create a private key and self-signed certificate...
1882 */
1883
1884 if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey))
1885 {
1886 DEBUG_printf(("5http_sspi_make_credentials: CryptGenKey failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1887 ok = FALSE;
1888 goto cleanup;
1889 }
1890
1891 ZeroMemory(&kpi, sizeof(kpi));
1892 kpi.pwszContainerName = (LPWSTR)container;
1893 kpi.pwszProvName = MS_DEF_PROV_W;
1894 kpi.dwProvType = PROV_RSA_FULL;
1895 kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
1896 kpi.dwKeySpec = AT_KEYEXCHANGE;
1897
1898 GetSystemTime(&et);
1899 et.wYear += years;
1900
1901 ZeroMemory(&exts, sizeof(exts));
1902
1903 createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL, &et, &exts);
1904
1905 if (!createdContext)
1906 {
1907 DEBUG_printf(("5http_sspi_make_credentials: CertCreateSelfSignCertificate failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1908 ok = FALSE;
1909 goto cleanup;
1910 }
1911
1912 /*
1913 * Add the created context to the named store, and associate it with the named
1914 * container...
1915 */
1916
1917 if (!CertAddCertificateContextToStore(store, createdContext, CERT_STORE_ADD_REPLACE_EXISTING, &storedContext))
1918 {
1919 DEBUG_printf(("5http_sspi_make_credentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1920 ok = FALSE;
1921 goto cleanup;
1922 }
1923
1924 ZeroMemory(&ckp, sizeof(ckp));
1925 ckp.pwszContainerName = (LPWSTR) container;
1926 ckp.pwszProvName = MS_DEF_PROV_W;
1927 ckp.dwProvType = PROV_RSA_FULL;
1928 ckp.dwFlags = CRYPT_MACHINE_KEYSET;
1929 ckp.dwKeySpec = AT_KEYEXCHANGE;
1930
1931 if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp))
1932 {
1933 DEBUG_printf(("5http_sspi_make_credentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1934 ok = FALSE;
1935 goto cleanup;
1936 }
1937
1938 /*
1939 * Get a handle to use the certificate...
1940 */
1941
1942 ZeroMemory(&SchannelCred, sizeof(SchannelCred));
1943
1944 SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
1945 SchannelCred.cCreds = 1;
1946 SchannelCred.paCred = &storedContext;
1947
1948 /*
1949 * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
1950 */
1951
1952 if (mode == _HTTP_MODE_SERVER)
1953 SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
1954
1955 /*
1956 * Create an SSPI credential.
1957 */
1958
1959 Status = AcquireCredentialsHandle(NULL, UNISP_NAME, mode == _HTTP_MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &sspi->creds, &tsExpiry);
1960 if (Status != SEC_E_OK)
1961 {
1962 DEBUG_printf(("5http_sspi_make_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), Status)));
1963 ok = FALSE;
1964 goto cleanup;
1965 }
1966
1967 cleanup:
1968
1969 /*
1970 * Cleanup
1971 */
1972
1973 if (hKey)
1974 CryptDestroyKey(hKey);
1975
1976 if (createdContext)
1977 CertFreeCertificateContext(createdContext);
1978
1979 if (storedContext)
1980 CertFreeCertificateContext(storedContext);
1981
1982 if (p)
1983 free(p);
1984
1985 if (store)
1986 CertCloseStore(store, 0);
1987
1988 if (hProv)
1989 CryptReleaseContext(hProv, 0);
1990
1991 return (ok);
1992 }
1993
1994
1995 /*
1996 * 'http_sspi_server()' - Negotiate a TLS connection as a server.
1997 */
1998
1999 static int /* O - 0 on success, -1 on failure */
2000 http_sspi_server(http_t *http, /* I - HTTP connection */
2001 const char *hostname) /* I - Hostname of server */
2002 {
2003 _http_sspi_t *sspi = http->tls; /* I - SSPI data */
2004 char common_name[512]; /* Common name for cert */
2005 DWORD dwSSPIFlags; /* SSL connection attributes we want */
2006 DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
2007 TimeStamp tsExpiry; /* Time stamp */
2008 SECURITY_STATUS scRet; /* SSPI Status */
2009 SecBufferDesc inBuffer; /* Array of SecBuffer structs */
2010 SecBuffer inBuffers[2]; /* Security package buffer */
2011 SecBufferDesc outBuffer; /* Array of SecBuffer structs */
2012 SecBuffer outBuffers[1]; /* Security package buffer */
2013 int num = 0; /* 32 bit status value */
2014 BOOL fInitContext = TRUE; /* Has the context been init'd? */
2015 int ret = 0; /* Return value */
2016
2017
2018 DEBUG_printf(("4http_sspi_server(http=%p, hostname=\"%s\")", http, hostname));
2019
2020 dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
2021 ASC_REQ_REPLAY_DETECT |
2022 ASC_REQ_CONFIDENTIALITY |
2023 ASC_REQ_EXTENDED_ERROR |
2024 ASC_REQ_ALLOCATE_MEMORY |
2025 ASC_REQ_STREAM;
2026
2027 sspi->decryptBufferUsed = 0;
2028
2029 /*
2030 * Lookup the server certificate...
2031 */
2032
2033 snprintf(common_name, sizeof(common_name), "CN=%s", hostname);
2034
2035 if (!http_sspi_find_credentials(http, L"ServerContainer", common_name))
2036 if (!http_sspi_make_credentials(http->tls, L"ServerContainer", common_name, _HTTP_MODE_SERVER, 10))
2037 {
2038 DEBUG_puts("5http_sspi_server: Unable to get server credentials.");
2039 return (-1);
2040 }
2041
2042 /*
2043 * Set OutBuffer for AcceptSecurityContext call
2044 */
2045
2046 outBuffer.cBuffers = 1;
2047 outBuffer.pBuffers = outBuffers;
2048 outBuffer.ulVersion = SECBUFFER_VERSION;
2049
2050 scRet = SEC_I_CONTINUE_NEEDED;
2051
2052 while (scRet == SEC_I_CONTINUE_NEEDED ||
2053 scRet == SEC_E_INCOMPLETE_MESSAGE ||
2054 scRet == SEC_I_INCOMPLETE_CREDENTIALS)
2055 {
2056 if (sspi->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
2057 {
2058 if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
2059 {
2060 BYTE *temp; /* New buffer */
2061
2062 if (sspi->decryptBufferLength >= 262144)
2063 {
2064 WSASetLastError(E_OUTOFMEMORY);
2065 DEBUG_puts("5http_sspi_server: Decryption buffer too large (>256k)");
2066 return (-1);
2067 }
2068
2069 if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
2070 {
2071 DEBUG_printf(("5http_sspi_server: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
2072 WSASetLastError(E_OUTOFMEMORY);
2073 return (-1);
2074 }
2075
2076 sspi->decryptBufferLength += 4096;
2077 sspi->decryptBuffer = temp;
2078 }
2079
2080 for (;;)
2081 {
2082 num = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
2083
2084 if (num == -1 && WSAGetLastError() == WSAEWOULDBLOCK)
2085 Sleep(1);
2086 else
2087 break;
2088 }
2089
2090 if (num < 0)
2091 {
2092 DEBUG_printf(("5http_sspi_server: recv failed: %d", WSAGetLastError()));
2093 return (-1);
2094 }
2095 else if (num == 0)
2096 {
2097 DEBUG_puts("5http_sspi_server: client disconnected");
2098 return (-1);
2099 }
2100
2101 DEBUG_printf(("5http_sspi_server: received %d (handshake) bytes from client.", num));
2102 sspi->decryptBufferUsed += num;
2103 }
2104
2105 /*
2106 * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process
2107 * on this run around the loop.
2108 */
2109
2110 inBuffers[0].pvBuffer = sspi->decryptBuffer;
2111 inBuffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed;
2112 inBuffers[0].BufferType = SECBUFFER_TOKEN;
2113
2114 inBuffers[1].pvBuffer = NULL;
2115 inBuffers[1].cbBuffer = 0;
2116 inBuffers[1].BufferType = SECBUFFER_EMPTY;
2117
2118 inBuffer.cBuffers = 2;
2119 inBuffer.pBuffers = inBuffers;
2120 inBuffer.ulVersion = SECBUFFER_VERSION;
2121
2122 /*
2123 * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to
2124 * free random garbage at the quit.
2125 */
2126
2127 outBuffers[0].pvBuffer = NULL;
2128 outBuffers[0].BufferType = SECBUFFER_TOKEN;
2129 outBuffers[0].cbBuffer = 0;
2130
2131 scRet = AcceptSecurityContext(&sspi->creds, (fInitContext?NULL:&sspi->context), &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP, (fInitContext?&sspi->context:NULL), &outBuffer, &dwSSPIOutFlags, &tsExpiry);
2132
2133 fInitContext = FALSE;
2134
2135 if (scRet == SEC_E_OK ||
2136 scRet == SEC_I_CONTINUE_NEEDED ||
2137 (FAILED(scRet) && ((dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) != 0)))
2138 {
2139 if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
2140 {
2141 /*
2142 * Send response to server if there is one.
2143 */
2144
2145 num = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
2146
2147 if (num <= 0)
2148 {
2149 DEBUG_printf(("5http_sspi_server: handshake send failed: %d", WSAGetLastError()));
2150 return (-1);
2151 }
2152
2153 DEBUG_printf(("5http_sspi_server: sent %d handshake bytes to client.", outBuffers[0].cbBuffer));
2154
2155 FreeContextBuffer(outBuffers[0].pvBuffer);
2156 outBuffers[0].pvBuffer = NULL;
2157 }
2158 }
2159
2160 if (scRet == SEC_E_OK)
2161 {
2162 /*
2163 * If there's extra data then save it for next time we go to decrypt.
2164 */
2165
2166 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
2167 {
2168 memcpy(sspi->decryptBuffer, (LPBYTE)(sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
2169 sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
2170 }
2171 else
2172 {
2173 sspi->decryptBufferUsed = 0;
2174 }
2175 break;
2176 }
2177 else if (FAILED(scRet) && scRet != SEC_E_INCOMPLETE_MESSAGE)
2178 {
2179 DEBUG_printf(("5http_sspi_server: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
2180 ret = -1;
2181 break;
2182 }
2183
2184 if (scRet != SEC_E_INCOMPLETE_MESSAGE &&
2185 scRet != SEC_I_INCOMPLETE_CREDENTIALS)
2186 {
2187 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
2188 {
2189 memcpy(sspi->decryptBuffer, (LPBYTE)(sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
2190 sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
2191 }
2192 else
2193 {
2194 sspi->decryptBufferUsed = 0;
2195 }
2196 }
2197 }
2198
2199 if (!ret)
2200 {
2201 sspi->contextInitialized = TRUE;
2202
2203 /*
2204 * Find out how big the header will be:
2205 */
2206
2207 scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_STREAM_SIZES, &sspi->streamSizes);
2208
2209 if (scRet != SEC_E_OK)
2210 {
2211 DEBUG_printf(("5http_sspi_server: QueryContextAttributes failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
2212 ret = -1;
2213 }
2214 }
2215
2216 return (ret);
2217 }
2218
2219
2220 /*
2221 * 'http_sspi_strerror()' - Return a string for the specified error code.
2222 */
2223
2224 static const char * /* O - String for error */
2225 http_sspi_strerror(char *buffer, /* I - Error message buffer */
2226 size_t bufsize, /* I - Size of buffer */
2227 DWORD code) /* I - Error code */
2228 {
2229 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buffer, bufsize, NULL))
2230 {
2231 /*
2232 * Strip trailing CR + LF...
2233 */
2234
2235 char *ptr; /* Pointer into error message */
2236
2237 for (ptr = buffer + strlen(buffer) - 1; ptr >= buffer; ptr --)
2238 if (*ptr == '\n' || *ptr == '\r')
2239 *ptr = '\0';
2240 else
2241 break;
2242 }
2243 else
2244 snprintf(buffer, bufsize, "Unknown error %x", code);
2245
2246 return (buffer);
2247 }
2248
2249
2250 /*
2251 * 'http_sspi_verify()' - Verify a certificate.
2252 */
2253
2254 static DWORD /* O - Error code (0 == No error) */
2255 http_sspi_verify(
2256 PCCERT_CONTEXT cert, /* I - Server certificate */
2257 const char *common_name, /* I - Common name */
2258 DWORD dwCertFlags) /* I - Verification flags */
2259 {
2260 HTTPSPolicyCallbackData httpsPolicy; /* HTTPS Policy Struct */
2261 CERT_CHAIN_POLICY_PARA policyPara; /* Cert chain policy parameters */
2262 CERT_CHAIN_POLICY_STATUS policyStatus;/* Cert chain policy status */
2263 CERT_CHAIN_PARA chainPara; /* Used for searching and matching criteria */
2264 PCCERT_CHAIN_CONTEXT chainContext = NULL;
2265 /* Certificate chain */
2266 PWSTR commonNameUnicode = NULL;
2267 /* Unicode common name */
2268 LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
2269 szOID_SERVER_GATED_CRYPTO,
2270 szOID_SGC_NETSCAPE };
2271 /* How are we using this certificate? */
2272 DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
2273 /* Number of ites in rgszUsages */
2274 DWORD count; /* 32 bit count variable */
2275 DWORD status; /* Return value */
2276 #ifdef DEBUG
2277 char error[1024]; /* Error message string */
2278 #endif /* DEBUG */
2279
2280
2281 if (!cert)
2282 return (SEC_E_WRONG_PRINCIPAL);
2283
2284 /*
2285 * Convert common name to Unicode.
2286 */
2287
2288 if (!common_name || !*common_name)
2289 return (SEC_E_WRONG_PRINCIPAL);
2290
2291 count = MultiByteToWideChar(CP_ACP, 0, common_name, -1, NULL, 0);
2292 commonNameUnicode = LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR));
2293 if (!commonNameUnicode)
2294 return (SEC_E_INSUFFICIENT_MEMORY);
2295
2296 if (!MultiByteToWideChar(CP_ACP, 0, common_name, -1, commonNameUnicode, count))
2297 {
2298 LocalFree(commonNameUnicode);
2299 return (SEC_E_WRONG_PRINCIPAL);
2300 }
2301
2302 /*
2303 * Build certificate chain.
2304 */
2305
2306 ZeroMemory(&chainPara, sizeof(chainPara));
2307
2308 chainPara.cbSize = sizeof(chainPara);
2309 chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
2310 chainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;
2311 chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
2312
2313 if (!CertGetCertificateChain(NULL, cert, NULL, cert->hCertStore, &chainPara, 0, NULL, &chainContext))
2314 {
2315 status = GetLastError();
2316
2317 DEBUG_printf(("CertGetCertificateChain returned: %s", http_sspi_strerror(error, sizeof(error), status)));
2318
2319 LocalFree(commonNameUnicode);
2320 return (status);
2321 }
2322
2323 /*
2324 * Validate certificate chain.
2325 */
2326
2327 ZeroMemory(&httpsPolicy, sizeof(HTTPSPolicyCallbackData));
2328 httpsPolicy.cbStruct = sizeof(HTTPSPolicyCallbackData);
2329 httpsPolicy.dwAuthType = AUTHTYPE_SERVER;
2330 httpsPolicy.fdwChecks = dwCertFlags;
2331 httpsPolicy.pwszServerName = commonNameUnicode;
2332
2333 memset(&policyPara, 0, sizeof(policyPara));
2334 policyPara.cbSize = sizeof(policyPara);
2335 policyPara.pvExtraPolicyPara = &httpsPolicy;
2336
2337 memset(&policyStatus, 0, sizeof(policyStatus));
2338 policyStatus.cbSize = sizeof(policyStatus);
2339
2340 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext, &policyPara, &policyStatus))
2341 {
2342 status = GetLastError();
2343
2344 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %s", http_sspi_strerror(error, sizeof(error), status)));
2345 }
2346 else if (policyStatus.dwError)
2347 status = policyStatus.dwError;
2348 else
2349 status = SEC_E_OK;
2350
2351 if (chainContext)
2352 CertFreeCertificateChain(chainContext);
2353
2354 if (commonNameUnicode)
2355 LocalFree(commonNameUnicode);
2356
2357 return (status);
2358 }
2359
2360
2361 /*
2362 * End of "$Id$".
2363 */