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