win32_crypto.c revision 362181
1/* 2 * win32_crypto.c: win32 providers for SVN_AUTH_* 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24/* prevent "empty compilation unit" warning on e.g. UNIX */ 25typedef int win32_crypto__dummy; 26 27/* ==================================================================== */ 28 29#if defined(WIN32) && !defined(__MINGW32__) 30 31/*** Includes. ***/ 32 33#include <apr_pools.h> 34#include <apr_base64.h> 35#include "svn_auth.h" 36#include "svn_error.h" 37#include "svn_hash.h" 38#include "svn_utf.h" 39#include "svn_config.h" 40#include "svn_user.h" 41#include "svn_base64.h" 42 43#include "auth.h" 44#include "private/svn_auth_private.h" 45 46#include "svn_private_config.h" 47 48#include <wincrypt.h> 49 50 51/* The description string that's combined with unencrypted data by the 52 Windows CryptoAPI. Used during decryption to verify that the 53 encrypted data were valid. */ 54static const WCHAR description[] = L"auth_svn.simple.wincrypt"; 55 56 57/* Return a copy of ORIG, encrypted using the Windows CryptoAPI and 58 allocated from POOL. */ 59const svn_string_t * 60encrypt_data(const svn_string_t *orig, 61 apr_pool_t *pool) 62{ 63 DATA_BLOB blobin; 64 DATA_BLOB blobout; 65 const svn_string_t *crypted = NULL; 66 67 blobin.cbData = orig->len; 68 blobin.pbData = (BYTE *)orig->data; 69 if (CryptProtectData(&blobin, description, NULL, NULL, NULL, 70 CRYPTPROTECT_UI_FORBIDDEN, &blobout)) 71 { 72 crypted = svn_string_ncreate((const char *)blobout.pbData, 73 blobout.cbData, pool); 74 LocalFree(blobout.pbData); 75 } 76 return crypted; 77} 78 79/* Return a copy of CRYPTED, decrypted using the Windows CryptoAPI and 80 allocated from POOL. */ 81const svn_string_t * 82decrypt_data(const svn_string_t *crypted, 83 apr_pool_t *pool) 84{ 85 DATA_BLOB blobin; 86 DATA_BLOB blobout; 87 LPWSTR descr; 88 const svn_string_t *orig = NULL; 89 90 blobin.cbData = crypted->len; 91 blobin.pbData = (BYTE *)crypted->data; 92 if (CryptUnprotectData(&blobin, &descr, NULL, NULL, NULL, 93 CRYPTPROTECT_UI_FORBIDDEN, &blobout)) 94 { 95 if (0 == lstrcmpW(descr, description)) 96 orig = svn_string_ncreate((const char *)blobout.pbData, 97 blobout.cbData, pool); 98 LocalFree(blobout.pbData); 99 LocalFree(descr); 100 } 101 return orig; 102} 103 104 105/*-----------------------------------------------------------------------*/ 106/* Windows simple provider, encrypts the password on Win2k and later. */ 107/*-----------------------------------------------------------------------*/ 108 109/* Implementation of svn_auth__password_set_t that encrypts 110 the incoming password using the Windows CryptoAPI. */ 111static svn_error_t * 112windows_password_encrypter(svn_boolean_t *done, 113 apr_hash_t *creds, 114 const char *realmstring, 115 const char *username, 116 const char *in, 117 apr_hash_t *parameters, 118 svn_boolean_t non_interactive, 119 apr_pool_t *pool) 120{ 121 const svn_string_t *coded; 122 123 coded = encrypt_data(svn_string_create(in, pool), pool); 124 if (coded) 125 { 126 coded = svn_base64_encode_string2(coded, FALSE, pool); 127 SVN_ERR(svn_auth__simple_password_set(done, creds, realmstring, username, 128 coded->data, parameters, 129 non_interactive, pool)); 130 } 131 132 return SVN_NO_ERROR; 133} 134 135/* Implementation of svn_auth__password_get_t that decrypts 136 the incoming password using the Windows CryptoAPI and verifies its 137 validity. */ 138static svn_error_t * 139windows_password_decrypter(svn_boolean_t *done, 140 const char **out, 141 apr_hash_t *creds, 142 const char *realmstring, 143 const char *username, 144 apr_hash_t *parameters, 145 svn_boolean_t non_interactive, 146 apr_pool_t *pool) 147{ 148 const svn_string_t *orig; 149 const char *in; 150 151 SVN_ERR(svn_auth__simple_password_get(done, &in, creds, realmstring, username, 152 parameters, non_interactive, pool)); 153 if (!*done) 154 return SVN_NO_ERROR; 155 156 orig = svn_base64_decode_string(svn_string_create(in, pool), pool); 157 orig = decrypt_data(orig, pool); 158 if (orig) 159 { 160 *out = orig->data; 161 *done = TRUE; 162 } 163 else 164 { 165 *done = FALSE; 166 } 167 return SVN_NO_ERROR; 168} 169 170/* Get cached encrypted credentials from the simple provider's cache. */ 171static svn_error_t * 172windows_simple_first_creds(void **credentials, 173 void **iter_baton, 174 void *provider_baton, 175 apr_hash_t *parameters, 176 const char *realmstring, 177 apr_pool_t *pool) 178{ 179 return svn_auth__simple_creds_cache_get(credentials, 180 iter_baton, 181 provider_baton, 182 parameters, 183 realmstring, 184 windows_password_decrypter, 185 SVN_AUTH__WINCRYPT_PASSWORD_TYPE, 186 pool); 187} 188 189/* Save encrypted credentials to the simple provider's cache. */ 190static svn_error_t * 191windows_simple_save_creds(svn_boolean_t *saved, 192 void *credentials, 193 void *provider_baton, 194 apr_hash_t *parameters, 195 const char *realmstring, 196 apr_pool_t *pool) 197{ 198 return svn_auth__simple_creds_cache_set(saved, credentials, 199 provider_baton, 200 parameters, 201 realmstring, 202 windows_password_encrypter, 203 SVN_AUTH__WINCRYPT_PASSWORD_TYPE, 204 pool); 205} 206 207static const svn_auth_provider_t windows_simple_provider = { 208 SVN_AUTH_CRED_SIMPLE, 209 windows_simple_first_creds, 210 NULL, 211 windows_simple_save_creds 212}; 213 214 215/* Public API */ 216void 217svn_auth__get_windows_simple_provider(svn_auth_provider_object_t **provider, 218 apr_pool_t *pool) 219{ 220 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 221 222 po->vtable = &windows_simple_provider; 223 *provider = po; 224} 225 226 227/*-----------------------------------------------------------------------*/ 228/* Windows SSL server trust provider, validates ssl certificate using */ 229/* CryptoApi. */ 230/*-----------------------------------------------------------------------*/ 231 232/* Implementation of svn_auth__password_set_t that encrypts 233 the incoming password using the Windows CryptoAPI. */ 234static svn_error_t * 235windows_ssl_client_cert_pw_encrypter(svn_boolean_t *done, 236 apr_hash_t *creds, 237 const char *realmstring, 238 const char *username, 239 const char *in, 240 apr_hash_t *parameters, 241 svn_boolean_t non_interactive, 242 apr_pool_t *pool) 243{ 244 const svn_string_t *coded; 245 246 coded = encrypt_data(svn_string_create(in, pool), pool); 247 if (coded) 248 { 249 coded = svn_base64_encode_string2(coded, FALSE, pool); 250 SVN_ERR(svn_auth__ssl_client_cert_pw_set(done, creds, realmstring, 251 username, coded->data, 252 parameters, non_interactive, 253 pool)); 254 } 255 256 return SVN_NO_ERROR; 257} 258 259/* Implementation of svn_auth__password_get_t that decrypts 260 the incoming password using the Windows CryptoAPI and verifies its 261 validity. */ 262static svn_error_t * 263windows_ssl_client_cert_pw_decrypter(svn_boolean_t *done, 264 const char **out, 265 apr_hash_t *creds, 266 const char *realmstring, 267 const char *username, 268 apr_hash_t *parameters, 269 svn_boolean_t non_interactive, 270 apr_pool_t *pool) 271{ 272 const svn_string_t *orig; 273 const char *in; 274 275 SVN_ERR(svn_auth__ssl_client_cert_pw_get(done, &in, creds, realmstring, 276 username, parameters, 277 non_interactive, pool)); 278 if (!*done) 279 return SVN_NO_ERROR; 280 281 orig = svn_base64_decode_string(svn_string_create(in, pool), pool); 282 orig = decrypt_data(orig, pool); 283 if (orig) 284 { 285 *out = orig->data; 286 *done = TRUE; 287 } 288 else 289 { 290 *done = FALSE; 291 } 292 return SVN_NO_ERROR; 293} 294 295/* Get cached encrypted credentials from the simple provider's cache. */ 296static svn_error_t * 297windows_ssl_client_cert_pw_first_creds(void **credentials, 298 void **iter_baton, 299 void *provider_baton, 300 apr_hash_t *parameters, 301 const char *realmstring, 302 apr_pool_t *pool) 303{ 304 return svn_auth__ssl_client_cert_pw_cache_get( 305 credentials, iter_baton, provider_baton, parameters, realmstring, 306 windows_ssl_client_cert_pw_decrypter, 307 SVN_AUTH__WINCRYPT_PASSWORD_TYPE, pool); 308} 309 310/* Save encrypted credentials to the simple provider's cache. */ 311static svn_error_t * 312windows_ssl_client_cert_pw_save_creds(svn_boolean_t *saved, 313 void *credentials, 314 void *provider_baton, 315 apr_hash_t *parameters, 316 const char *realmstring, 317 apr_pool_t *pool) 318{ 319 return svn_auth__ssl_client_cert_pw_cache_set( 320 saved, credentials, provider_baton, parameters, realmstring, 321 windows_ssl_client_cert_pw_encrypter, 322 SVN_AUTH__WINCRYPT_PASSWORD_TYPE, pool); 323} 324 325static const svn_auth_provider_t windows_ssl_client_cert_pw_provider = { 326 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 327 windows_ssl_client_cert_pw_first_creds, 328 NULL, 329 windows_ssl_client_cert_pw_save_creds 330}; 331 332 333/* Public API */ 334void 335svn_auth__get_windows_ssl_client_cert_pw_provider 336 (svn_auth_provider_object_t **provider, 337 apr_pool_t *pool) 338{ 339 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 340 341 po->vtable = &windows_ssl_client_cert_pw_provider; 342 *provider = po; 343} 344 345 346/*-----------------------------------------------------------------------*/ 347/* Windows SSL server trust provider, validates ssl certificate using */ 348/* CryptoApi. */ 349/*-----------------------------------------------------------------------*/ 350 351/* Helper to create CryptoAPI CERT_CONTEXT from base64 encoded BASE64_CERT. 352 * Returns NULL on error. 353 */ 354static PCCERT_CONTEXT 355certcontext_from_base64(const char *base64_cert, apr_pool_t *pool) 356{ 357 PCCERT_CONTEXT cert_context = NULL; 358 int cert_len; 359 BYTE *binary_cert; 360 361 /* Use apr-util as CryptStringToBinaryA is available only on XP+. */ 362 binary_cert = apr_palloc(pool, 363 apr_base64_decode_len(base64_cert)); 364 cert_len = apr_base64_decode((char*)binary_cert, base64_cert); 365 366 /* Parse the certificate into a context. */ 367 cert_context = CertCreateCertificateContext 368 (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, binary_cert, cert_len); 369 370 return cert_context; 371} 372 373/* Helper for windows_ssl_server_trust_first_credentials for validating 374 * certificate using CryptoApi. Sets *OK_P to TRUE if base64 encoded ASCII_CERT 375 * certificate considered as valid. 376 */ 377static svn_error_t * 378windows_validate_certificate(svn_boolean_t *ok_p, 379 const char *ascii_cert, 380 apr_pool_t *pool) 381{ 382 PCCERT_CONTEXT cert_context = NULL; 383 CERT_CHAIN_PARA chain_para; 384 PCCERT_CHAIN_CONTEXT chain_context = NULL; 385 386 *ok_p = FALSE; 387 388 /* Parse the certificate into a context. */ 389 cert_context = certcontext_from_base64(ascii_cert, pool); 390 391 if (cert_context) 392 { 393 /* Retrieve the certificate chain of the certificate 394 (a certificate without a valid root does not have a chain). */ 395 memset(&chain_para, 0, sizeof(chain_para)); 396 chain_para.cbSize = sizeof(chain_para); 397 398 /* Don't hit the wire for URL based objects and revocation checks, as 399 that may cause stalls, network timeouts or spurious errors in cases 400 such as with the remote OCSP and CRL endpoints being inaccessible or 401 unreliable. 402 403 For this particular case of the SVN_AUTH_SSL_UNKNOWNCA cert failure 404 override we should be okay with just the data that we have immediately 405 available on the local machine. 406 */ 407 if (CertGetCertificateChain(NULL, cert_context, NULL, NULL, &chain_para, 408 CERT_CHAIN_CACHE_END_CERT | 409 CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL | 410 CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | 411 CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY, 412 NULL, &chain_context)) 413 { 414 CERT_CHAIN_POLICY_PARA policy_para; 415 CERT_CHAIN_POLICY_STATUS policy_status; 416 417 policy_para.cbSize = sizeof(policy_para); 418 /* We only use the local data for revocation checks, so they may 419 fail with errors like CRYPT_E_REVOCATION_OFFLINE; ignore those. */ 420 policy_para.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS; 421 policy_para.pvExtraPolicyPara = NULL; 422 423 policy_status.cbSize = sizeof(policy_status); 424 425 if (CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, 426 chain_context, &policy_para, 427 &policy_status)) 428 { 429 if (policy_status.dwError == S_OK) 430 { 431 /* Windows thinks the certificate is valid. */ 432 *ok_p = TRUE; 433 } 434 } 435 436 CertFreeCertificateChain(chain_context); 437 } 438 CertFreeCertificateContext(cert_context); 439 } 440 441 return SVN_NO_ERROR; 442} 443 444/* Retrieve ssl server CA failure overrides (if any) from CryptoApi. */ 445static svn_error_t * 446windows_ssl_server_trust_first_credentials(void **credentials, 447 void **iter_baton, 448 void *provider_baton, 449 apr_hash_t *parameters, 450 const char *realmstring, 451 apr_pool_t *pool) 452{ 453 apr_uint32_t *failure_ptr = svn_hash_gets(parameters, 454 SVN_AUTH_PARAM_SSL_SERVER_FAILURES); 455 apr_uint32_t failures = *failure_ptr; 456 const svn_auth_ssl_server_cert_info_t *cert_info = 457 svn_hash_gets(parameters, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO); 458 459 *credentials = NULL; 460 *iter_baton = NULL; 461 462 /* We can accept only unknown certificate authority. */ 463 if (failures & SVN_AUTH_SSL_UNKNOWNCA) 464 { 465 svn_boolean_t ok; 466 467 SVN_ERR(windows_validate_certificate(&ok, cert_info->ascii_cert, pool)); 468 469 /* Windows thinks that certificate is ok. */ 470 if (ok) 471 { 472 /* Clear failure flag. */ 473 failures &= ~SVN_AUTH_SSL_UNKNOWNCA; 474 } 475 } 476 477 /* If all failures are cleared now, we return the creds */ 478 if (! failures) 479 { 480 svn_auth_cred_ssl_server_trust_t *creds = 481 apr_pcalloc(pool, sizeof(*creds)); 482 creds->accepted_failures = *failure_ptr & ~failures; 483 creds->may_save = FALSE; /* No need to save it. */ 484 *credentials = creds; 485 } 486 487 return SVN_NO_ERROR; 488} 489 490static const svn_auth_provider_t windows_server_trust_provider = { 491 SVN_AUTH_CRED_SSL_SERVER_TRUST, 492 windows_ssl_server_trust_first_credentials, 493 NULL, 494 NULL, 495}; 496 497/* Public API */ 498void 499svn_auth__get_windows_ssl_server_trust_provider 500 (svn_auth_provider_object_t **provider, apr_pool_t *pool) 501{ 502 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 503 504 po->vtable = &windows_server_trust_provider; 505 *provider = po; 506} 507 508static const svn_auth_provider_t windows_server_authority_provider = { 509 SVN_AUTH_CRED_SSL_SERVER_AUTHORITY, 510 windows_ssl_server_trust_first_credentials, 511 NULL, 512 NULL, 513}; 514 515/* Public API */ 516void 517svn_auth__get_windows_ssl_server_authority_provider( 518 svn_auth_provider_object_t **provider, 519 apr_pool_t *pool) 520{ 521 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 522 523 po->vtable = &windows_server_authority_provider; 524 *provider = po; 525} 526 527 528#else /* !WIN32 */ 529 530/* Silence OSX ranlib warnings about object files with no symbols. */ 531#include <apr.h> 532extern const apr_uint32_t svn__fake__win32_crypto; 533const apr_uint32_t svn__fake__win32_crypto = 0xdeadbeef; 534 535#endif /* WIN32 */ 536