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