win32_crypto.c revision 299742
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 if (CertGetCertificateChain(NULL, cert_context, NULL, NULL, &chain_para, 399 CERT_CHAIN_CACHE_END_CERT | 400 CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, 401 NULL, &chain_context)) 402 { 403 CERT_CHAIN_POLICY_PARA policy_para; 404 CERT_CHAIN_POLICY_STATUS policy_status; 405 406 policy_para.cbSize = sizeof(policy_para); 407 policy_para.dwFlags = 0; 408 policy_para.pvExtraPolicyPara = NULL; 409 410 policy_status.cbSize = sizeof(policy_status); 411 412 if (CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, 413 chain_context, &policy_para, 414 &policy_status)) 415 { 416 if (policy_status.dwError == S_OK) 417 { 418 /* Windows thinks the certificate is valid. */ 419 *ok_p = TRUE; 420 } 421 } 422 423 CertFreeCertificateChain(chain_context); 424 } 425 CertFreeCertificateContext(cert_context); 426 } 427 428 return SVN_NO_ERROR; 429} 430 431/* Retrieve ssl server CA failure overrides (if any) from CryptoApi. */ 432static svn_error_t * 433windows_ssl_server_trust_first_credentials(void **credentials, 434 void **iter_baton, 435 void *provider_baton, 436 apr_hash_t *parameters, 437 const char *realmstring, 438 apr_pool_t *pool) 439{ 440 apr_uint32_t *failure_ptr = svn_hash_gets(parameters, 441 SVN_AUTH_PARAM_SSL_SERVER_FAILURES); 442 apr_uint32_t failures = *failure_ptr; 443 const svn_auth_ssl_server_cert_info_t *cert_info = 444 svn_hash_gets(parameters, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO); 445 446 *credentials = NULL; 447 *iter_baton = NULL; 448 449 /* We can accept only unknown certificate authority. */ 450 if (failures & SVN_AUTH_SSL_UNKNOWNCA) 451 { 452 svn_boolean_t ok; 453 454 SVN_ERR(windows_validate_certificate(&ok, cert_info->ascii_cert, pool)); 455 456 /* Windows thinks that certificate is ok. */ 457 if (ok) 458 { 459 /* Clear failure flag. */ 460 failures &= ~SVN_AUTH_SSL_UNKNOWNCA; 461 } 462 } 463 464 /* If all failures are cleared now, we return the creds */ 465 if (! failures) 466 { 467 svn_auth_cred_ssl_server_trust_t *creds = 468 apr_pcalloc(pool, sizeof(*creds)); 469 creds->accepted_failures = *failure_ptr & ~failures; 470 creds->may_save = FALSE; /* No need to save it. */ 471 *credentials = creds; 472 } 473 474 return SVN_NO_ERROR; 475} 476 477static const svn_auth_provider_t windows_server_trust_provider = { 478 SVN_AUTH_CRED_SSL_SERVER_TRUST, 479 windows_ssl_server_trust_first_credentials, 480 NULL, 481 NULL, 482}; 483 484/* Public API */ 485void 486svn_auth__get_windows_ssl_server_trust_provider 487 (svn_auth_provider_object_t **provider, apr_pool_t *pool) 488{ 489 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 490 491 po->vtable = &windows_server_trust_provider; 492 *provider = po; 493} 494 495static const svn_auth_provider_t windows_server_authority_provider = { 496 SVN_AUTH_CRED_SSL_SERVER_AUTHORITY, 497 windows_ssl_server_trust_first_credentials, 498 NULL, 499 NULL, 500}; 501 502/* Public API */ 503void 504svn_auth__get_windows_ssl_server_authority_provider( 505 svn_auth_provider_object_t **provider, 506 apr_pool_t *pool) 507{ 508 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 509 510 po->vtable = &windows_server_authority_provider; 511 *provider = po; 512} 513 514 515#endif /* WIN32 */ 516