win32_crypto.c revision 262253
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 "private/svn_auth_private.h" 44 45#include "svn_private_config.h" 46 47#include <wincrypt.h> 48 49 50/* The description string that's combined with unencrypted data by the 51 Windows CryptoAPI. Used during decryption to verify that the 52 encrypted data were valid. */ 53static const WCHAR description[] = L"auth_svn.simple.wincrypt"; 54 55 56/* Return a copy of ORIG, encrypted using the Windows CryptoAPI and 57 allocated from POOL. */ 58const svn_string_t * 59encrypt_data(const svn_string_t *orig, 60 apr_pool_t *pool) 61{ 62 DATA_BLOB blobin; 63 DATA_BLOB blobout; 64 const svn_string_t *crypted = NULL; 65 66 blobin.cbData = orig->len; 67 blobin.pbData = (BYTE *)orig->data; 68 if (CryptProtectData(&blobin, description, NULL, NULL, NULL, 69 CRYPTPROTECT_UI_FORBIDDEN, &blobout)) 70 { 71 crypted = svn_string_ncreate((const char *)blobout.pbData, 72 blobout.cbData, pool); 73 LocalFree(blobout.pbData); 74 } 75 return crypted; 76} 77 78/* Return a copy of CRYPTED, decrypted using the Windows CryptoAPI and 79 allocated from POOL. */ 80const svn_string_t * 81decrypt_data(const svn_string_t *crypted, 82 apr_pool_t *pool) 83{ 84 DATA_BLOB blobin; 85 DATA_BLOB blobout; 86 LPWSTR descr; 87 const svn_string_t *orig = NULL; 88 89 blobin.cbData = crypted->len; 90 blobin.pbData = (BYTE *)crypted->data; 91 if (CryptUnprotectData(&blobin, &descr, NULL, NULL, NULL, 92 CRYPTPROTECT_UI_FORBIDDEN, &blobout)) 93 { 94 if (0 == lstrcmpW(descr, description)) 95 orig = svn_string_ncreate((const char *)blobout.pbData, 96 blobout.cbData, pool); 97 LocalFree(blobout.pbData); 98 LocalFree(descr); 99 } 100 return orig; 101} 102 103 104/*-----------------------------------------------------------------------*/ 105/* Windows simple provider, encrypts the password on Win2k and later. */ 106/*-----------------------------------------------------------------------*/ 107 108/* Implementation of svn_auth__password_set_t that encrypts 109 the incoming password using the Windows CryptoAPI. */ 110static svn_error_t * 111windows_password_encrypter(svn_boolean_t *done, 112 apr_hash_t *creds, 113 const char *realmstring, 114 const char *username, 115 const char *in, 116 apr_hash_t *parameters, 117 svn_boolean_t non_interactive, 118 apr_pool_t *pool) 119{ 120 const svn_string_t *coded; 121 122 coded = encrypt_data(svn_string_create(in, pool), pool); 123 if (coded) 124 { 125 coded = svn_base64_encode_string2(coded, FALSE, pool); 126 SVN_ERR(svn_auth__simple_password_set(done, creds, realmstring, username, 127 coded->data, parameters, 128 non_interactive, pool)); 129 } 130 131 return SVN_NO_ERROR; 132} 133 134/* Implementation of svn_auth__password_get_t that decrypts 135 the incoming password using the Windows CryptoAPI and verifies its 136 validity. */ 137static svn_error_t * 138windows_password_decrypter(svn_boolean_t *done, 139 const char **out, 140 apr_hash_t *creds, 141 const char *realmstring, 142 const char *username, 143 apr_hash_t *parameters, 144 svn_boolean_t non_interactive, 145 apr_pool_t *pool) 146{ 147 const svn_string_t *orig; 148 const char *in; 149 150 SVN_ERR(svn_auth__simple_password_get(done, &in, creds, realmstring, username, 151 parameters, non_interactive, pool)); 152 if (!*done) 153 return SVN_NO_ERROR; 154 155 orig = svn_base64_decode_string(svn_string_create(in, pool), pool); 156 orig = decrypt_data(orig, pool); 157 if (orig) 158 { 159 *out = orig->data; 160 *done = TRUE; 161 } 162 else 163 { 164 *done = FALSE; 165 } 166 return SVN_NO_ERROR; 167} 168 169/* Get cached encrypted credentials from the simple provider's cache. */ 170static svn_error_t * 171windows_simple_first_creds(void **credentials, 172 void **iter_baton, 173 void *provider_baton, 174 apr_hash_t *parameters, 175 const char *realmstring, 176 apr_pool_t *pool) 177{ 178 return svn_auth__simple_creds_cache_get(credentials, 179 iter_baton, 180 provider_baton, 181 parameters, 182 realmstring, 183 windows_password_decrypter, 184 SVN_AUTH__WINCRYPT_PASSWORD_TYPE, 185 pool); 186} 187 188/* Save encrypted credentials to the simple provider's cache. */ 189static svn_error_t * 190windows_simple_save_creds(svn_boolean_t *saved, 191 void *credentials, 192 void *provider_baton, 193 apr_hash_t *parameters, 194 const char *realmstring, 195 apr_pool_t *pool) 196{ 197 return svn_auth__simple_creds_cache_set(saved, credentials, 198 provider_baton, 199 parameters, 200 realmstring, 201 windows_password_encrypter, 202 SVN_AUTH__WINCRYPT_PASSWORD_TYPE, 203 pool); 204} 205 206static const svn_auth_provider_t windows_simple_provider = { 207 SVN_AUTH_CRED_SIMPLE, 208 windows_simple_first_creds, 209 NULL, 210 windows_simple_save_creds 211}; 212 213 214/* Public API */ 215void 216svn_auth_get_windows_simple_provider(svn_auth_provider_object_t **provider, 217 apr_pool_t *pool) 218{ 219 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 220 221 po->vtable = &windows_simple_provider; 222 *provider = po; 223} 224 225 226/*-----------------------------------------------------------------------*/ 227/* Windows SSL server trust provider, validates ssl certificate using */ 228/* CryptoApi. */ 229/*-----------------------------------------------------------------------*/ 230 231/* Implementation of svn_auth__password_set_t that encrypts 232 the incoming password using the Windows CryptoAPI. */ 233static svn_error_t * 234windows_ssl_client_cert_pw_encrypter(svn_boolean_t *done, 235 apr_hash_t *creds, 236 const char *realmstring, 237 const char *username, 238 const char *in, 239 apr_hash_t *parameters, 240 svn_boolean_t non_interactive, 241 apr_pool_t *pool) 242{ 243 const svn_string_t *coded; 244 245 coded = encrypt_data(svn_string_create(in, pool), pool); 246 if (coded) 247 { 248 coded = svn_base64_encode_string2(coded, FALSE, pool); 249 SVN_ERR(svn_auth__ssl_client_cert_pw_set(done, creds, realmstring, 250 username, coded->data, 251 parameters, non_interactive, 252 pool)); 253 } 254 255 return SVN_NO_ERROR; 256} 257 258/* Implementation of svn_auth__password_get_t that decrypts 259 the incoming password using the Windows CryptoAPI and verifies its 260 validity. */ 261static svn_error_t * 262windows_ssl_client_cert_pw_decrypter(svn_boolean_t *done, 263 const char **out, 264 apr_hash_t *creds, 265 const char *realmstring, 266 const char *username, 267 apr_hash_t *parameters, 268 svn_boolean_t non_interactive, 269 apr_pool_t *pool) 270{ 271 const svn_string_t *orig; 272 const char *in; 273 274 SVN_ERR(svn_auth__ssl_client_cert_pw_get(done, &in, creds, realmstring, 275 username, parameters, 276 non_interactive, pool)); 277 if (!*done) 278 return SVN_NO_ERROR; 279 280 orig = svn_base64_decode_string(svn_string_create(in, pool), pool); 281 orig = decrypt_data(orig, pool); 282 if (orig) 283 { 284 *out = orig->data; 285 *done = TRUE; 286 } 287 else 288 { 289 *done = FALSE; 290 } 291 return SVN_NO_ERROR; 292} 293 294/* Get cached encrypted credentials from the simple provider's cache. */ 295static svn_error_t * 296windows_ssl_client_cert_pw_first_creds(void **credentials, 297 void **iter_baton, 298 void *provider_baton, 299 apr_hash_t *parameters, 300 const char *realmstring, 301 apr_pool_t *pool) 302{ 303 return svn_auth__ssl_client_cert_pw_cache_get( 304 credentials, iter_baton, provider_baton, parameters, realmstring, 305 windows_ssl_client_cert_pw_decrypter, 306 SVN_AUTH__WINCRYPT_PASSWORD_TYPE, pool); 307} 308 309/* Save encrypted credentials to the simple provider's cache. */ 310static svn_error_t * 311windows_ssl_client_cert_pw_save_creds(svn_boolean_t *saved, 312 void *credentials, 313 void *provider_baton, 314 apr_hash_t *parameters, 315 const char *realmstring, 316 apr_pool_t *pool) 317{ 318 return svn_auth__ssl_client_cert_pw_cache_set( 319 saved, credentials, provider_baton, parameters, realmstring, 320 windows_ssl_client_cert_pw_encrypter, 321 SVN_AUTH__WINCRYPT_PASSWORD_TYPE, pool); 322} 323 324static const svn_auth_provider_t windows_ssl_client_cert_pw_provider = { 325 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 326 windows_ssl_client_cert_pw_first_creds, 327 NULL, 328 windows_ssl_client_cert_pw_save_creds 329}; 330 331 332/* Public API */ 333void 334svn_auth_get_windows_ssl_client_cert_pw_provider 335 (svn_auth_provider_object_t **provider, 336 apr_pool_t *pool) 337{ 338 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 339 340 po->vtable = &windows_ssl_client_cert_pw_provider; 341 *provider = po; 342} 343 344 345/*-----------------------------------------------------------------------*/ 346/* Windows SSL server trust provider, validates ssl certificate using */ 347/* CryptoApi. */ 348/*-----------------------------------------------------------------------*/ 349 350/* Helper to create CryptoAPI CERT_CONTEXT from base64 encoded BASE64_CERT. 351 * Returns NULL on error. 352 */ 353static PCCERT_CONTEXT 354certcontext_from_base64(const char *base64_cert, apr_pool_t *pool) 355{ 356 PCCERT_CONTEXT cert_context = NULL; 357 int cert_len; 358 BYTE *binary_cert; 359 360 /* Use apr-util as CryptStringToBinaryA is available only on XP+. */ 361 binary_cert = apr_palloc(pool, 362 apr_base64_decode_len(base64_cert)); 363 cert_len = apr_base64_decode((char*)binary_cert, base64_cert); 364 365 /* Parse the certificate into a context. */ 366 cert_context = CertCreateCertificateContext 367 (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, binary_cert, cert_len); 368 369 return cert_context; 370} 371 372/* Helper for windows_ssl_server_trust_first_credentials for validating 373 * certificate using CryptoApi. Sets *OK_P to TRUE if base64 encoded ASCII_CERT 374 * certificate considered as valid. 375 */ 376static svn_error_t * 377windows_validate_certificate(svn_boolean_t *ok_p, 378 const char *ascii_cert, 379 apr_pool_t *pool) 380{ 381 PCCERT_CONTEXT cert_context = NULL; 382 CERT_CHAIN_PARA chain_para; 383 PCCERT_CHAIN_CONTEXT chain_context = NULL; 384 385 *ok_p = FALSE; 386 387 /* Parse the certificate into a context. */ 388 cert_context = certcontext_from_base64(ascii_cert, pool); 389 390 if (cert_context) 391 { 392 /* Retrieve the certificate chain of the certificate 393 (a certificate without a valid root does not have a chain). */ 394 memset(&chain_para, 0, sizeof(chain_para)); 395 chain_para.cbSize = sizeof(chain_para); 396 397 if (CertGetCertificateChain(NULL, cert_context, NULL, NULL, &chain_para, 398 CERT_CHAIN_CACHE_END_CERT | 399 CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, 400 NULL, &chain_context)) 401 { 402 CERT_CHAIN_POLICY_PARA policy_para; 403 CERT_CHAIN_POLICY_STATUS policy_status; 404 405 policy_para.cbSize = sizeof(policy_para); 406 policy_para.dwFlags = 0; 407 policy_para.pvExtraPolicyPara = NULL; 408 409 policy_status.cbSize = sizeof(policy_status); 410 411 if (CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, 412 chain_context, &policy_para, 413 &policy_status)) 414 { 415 if (policy_status.dwError == S_OK) 416 { 417 /* Windows thinks the certificate is valid. */ 418 *ok_p = TRUE; 419 } 420 } 421 422 CertFreeCertificateChain(chain_context); 423 } 424 CertFreeCertificateContext(cert_context); 425 } 426 427 return SVN_NO_ERROR; 428} 429 430/* Retrieve ssl server CA failure overrides (if any) from CryptoApi. */ 431static svn_error_t * 432windows_ssl_server_trust_first_credentials(void **credentials, 433 void **iter_baton, 434 void *provider_baton, 435 apr_hash_t *parameters, 436 const char *realmstring, 437 apr_pool_t *pool) 438{ 439 apr_uint32_t *failure_ptr = svn_hash_gets(parameters, 440 SVN_AUTH_PARAM_SSL_SERVER_FAILURES); 441 apr_uint32_t failures = *failure_ptr; 442 const svn_auth_ssl_server_cert_info_t *cert_info = 443 svn_hash_gets(parameters, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO); 444 445 *credentials = NULL; 446 *iter_baton = NULL; 447 448 /* We can accept only unknown certificate authority. */ 449 if (failures & SVN_AUTH_SSL_UNKNOWNCA) 450 { 451 svn_boolean_t ok; 452 453 SVN_ERR(windows_validate_certificate(&ok, cert_info->ascii_cert, pool)); 454 455 /* Windows thinks that certificate is ok. */ 456 if (ok) 457 { 458 /* Clear failure flag. */ 459 failures &= ~SVN_AUTH_SSL_UNKNOWNCA; 460 } 461 } 462 463 /* If all failures are cleared now, we return the creds */ 464 if (! failures) 465 { 466 svn_auth_cred_ssl_server_trust_t *creds = 467 apr_pcalloc(pool, sizeof(*creds)); 468 creds->accepted_failures = *failure_ptr & ~failures; 469 creds->may_save = FALSE; /* No need to save it. */ 470 *credentials = creds; 471 } 472 473 return SVN_NO_ERROR; 474} 475 476static const svn_auth_provider_t windows_server_trust_provider = { 477 SVN_AUTH_CRED_SSL_SERVER_TRUST, 478 windows_ssl_server_trust_first_credentials, 479 NULL, 480 NULL, 481}; 482 483/* Public API */ 484void 485svn_auth_get_windows_ssl_server_trust_provider 486 (svn_auth_provider_object_t **provider, apr_pool_t *pool) 487{ 488 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 489 490 po->vtable = &windows_server_trust_provider; 491 *provider = po; 492} 493 494static const svn_auth_provider_t windows_server_authority_provider = { 495 SVN_AUTH_CRED_SSL_SERVER_AUTHORITY, 496 windows_ssl_server_trust_first_credentials, 497 NULL, 498 NULL, 499}; 500 501/* Public API */ 502void 503svn_auth__get_windows_ssl_server_authority_provider( 504 svn_auth_provider_object_t **provider, 505 apr_pool_t *pool) 506{ 507 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 508 509 po->vtable = &windows_server_authority_provider; 510 *provider = po; 511} 512 513 514#endif /* WIN32 */ 515