1251881Speter/* 2251881Speter * win32_crypto.c: win32 providers for SVN_AUTH_* 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* prevent "empty compilation unit" warning on e.g. UNIX */ 25251881Spetertypedef int win32_crypto__dummy; 26251881Speter 27251881Speter/* ==================================================================== */ 28251881Speter 29251881Speter#if defined(WIN32) && !defined(__MINGW32__) 30251881Speter 31251881Speter/*** Includes. ***/ 32251881Speter 33251881Speter#include <apr_pools.h> 34251881Speter#include <apr_base64.h> 35251881Speter#include "svn_auth.h" 36251881Speter#include "svn_error.h" 37251881Speter#include "svn_hash.h" 38251881Speter#include "svn_utf.h" 39251881Speter#include "svn_config.h" 40251881Speter#include "svn_user.h" 41251881Speter#include "svn_base64.h" 42251881Speter 43299742Sdim#include "auth.h" 44251881Speter#include "private/svn_auth_private.h" 45251881Speter 46251881Speter#include "svn_private_config.h" 47251881Speter 48251881Speter#include <wincrypt.h> 49251881Speter 50251881Speter 51251881Speter/* The description string that's combined with unencrypted data by the 52251881Speter Windows CryptoAPI. Used during decryption to verify that the 53251881Speter encrypted data were valid. */ 54251881Speterstatic const WCHAR description[] = L"auth_svn.simple.wincrypt"; 55251881Speter 56251881Speter 57251881Speter/* Return a copy of ORIG, encrypted using the Windows CryptoAPI and 58251881Speter allocated from POOL. */ 59251881Speterconst svn_string_t * 60251881Speterencrypt_data(const svn_string_t *orig, 61251881Speter apr_pool_t *pool) 62251881Speter{ 63251881Speter DATA_BLOB blobin; 64251881Speter DATA_BLOB blobout; 65251881Speter const svn_string_t *crypted = NULL; 66251881Speter 67251881Speter blobin.cbData = orig->len; 68251881Speter blobin.pbData = (BYTE *)orig->data; 69251881Speter if (CryptProtectData(&blobin, description, NULL, NULL, NULL, 70251881Speter CRYPTPROTECT_UI_FORBIDDEN, &blobout)) 71251881Speter { 72251881Speter crypted = svn_string_ncreate((const char *)blobout.pbData, 73251881Speter blobout.cbData, pool); 74251881Speter LocalFree(blobout.pbData); 75251881Speter } 76251881Speter return crypted; 77251881Speter} 78251881Speter 79251881Speter/* Return a copy of CRYPTED, decrypted using the Windows CryptoAPI and 80251881Speter allocated from POOL. */ 81251881Speterconst svn_string_t * 82251881Speterdecrypt_data(const svn_string_t *crypted, 83251881Speter apr_pool_t *pool) 84251881Speter{ 85251881Speter DATA_BLOB blobin; 86251881Speter DATA_BLOB blobout; 87251881Speter LPWSTR descr; 88251881Speter const svn_string_t *orig = NULL; 89251881Speter 90251881Speter blobin.cbData = crypted->len; 91251881Speter blobin.pbData = (BYTE *)crypted->data; 92251881Speter if (CryptUnprotectData(&blobin, &descr, NULL, NULL, NULL, 93251881Speter CRYPTPROTECT_UI_FORBIDDEN, &blobout)) 94251881Speter { 95251881Speter if (0 == lstrcmpW(descr, description)) 96251881Speter orig = svn_string_ncreate((const char *)blobout.pbData, 97251881Speter blobout.cbData, pool); 98251881Speter LocalFree(blobout.pbData); 99251881Speter LocalFree(descr); 100251881Speter } 101251881Speter return orig; 102251881Speter} 103251881Speter 104251881Speter 105251881Speter/*-----------------------------------------------------------------------*/ 106251881Speter/* Windows simple provider, encrypts the password on Win2k and later. */ 107251881Speter/*-----------------------------------------------------------------------*/ 108251881Speter 109251881Speter/* Implementation of svn_auth__password_set_t that encrypts 110251881Speter the incoming password using the Windows CryptoAPI. */ 111251881Speterstatic svn_error_t * 112251881Speterwindows_password_encrypter(svn_boolean_t *done, 113251881Speter apr_hash_t *creds, 114251881Speter const char *realmstring, 115251881Speter const char *username, 116251881Speter const char *in, 117251881Speter apr_hash_t *parameters, 118251881Speter svn_boolean_t non_interactive, 119251881Speter apr_pool_t *pool) 120251881Speter{ 121251881Speter const svn_string_t *coded; 122251881Speter 123251881Speter coded = encrypt_data(svn_string_create(in, pool), pool); 124251881Speter if (coded) 125251881Speter { 126251881Speter coded = svn_base64_encode_string2(coded, FALSE, pool); 127251881Speter SVN_ERR(svn_auth__simple_password_set(done, creds, realmstring, username, 128251881Speter coded->data, parameters, 129251881Speter non_interactive, pool)); 130251881Speter } 131251881Speter 132251881Speter return SVN_NO_ERROR; 133251881Speter} 134251881Speter 135251881Speter/* Implementation of svn_auth__password_get_t that decrypts 136251881Speter the incoming password using the Windows CryptoAPI and verifies its 137251881Speter validity. */ 138251881Speterstatic svn_error_t * 139251881Speterwindows_password_decrypter(svn_boolean_t *done, 140251881Speter const char **out, 141251881Speter apr_hash_t *creds, 142251881Speter const char *realmstring, 143251881Speter const char *username, 144251881Speter apr_hash_t *parameters, 145251881Speter svn_boolean_t non_interactive, 146251881Speter apr_pool_t *pool) 147251881Speter{ 148251881Speter const svn_string_t *orig; 149251881Speter const char *in; 150251881Speter 151251881Speter SVN_ERR(svn_auth__simple_password_get(done, &in, creds, realmstring, username, 152251881Speter parameters, non_interactive, pool)); 153251881Speter if (!*done) 154251881Speter return SVN_NO_ERROR; 155251881Speter 156251881Speter orig = svn_base64_decode_string(svn_string_create(in, pool), pool); 157251881Speter orig = decrypt_data(orig, pool); 158251881Speter if (orig) 159251881Speter { 160251881Speter *out = orig->data; 161251881Speter *done = TRUE; 162251881Speter } 163251881Speter else 164251881Speter { 165251881Speter *done = FALSE; 166251881Speter } 167251881Speter return SVN_NO_ERROR; 168251881Speter} 169251881Speter 170251881Speter/* Get cached encrypted credentials from the simple provider's cache. */ 171251881Speterstatic svn_error_t * 172251881Speterwindows_simple_first_creds(void **credentials, 173251881Speter void **iter_baton, 174251881Speter void *provider_baton, 175251881Speter apr_hash_t *parameters, 176251881Speter const char *realmstring, 177251881Speter apr_pool_t *pool) 178251881Speter{ 179251881Speter return svn_auth__simple_creds_cache_get(credentials, 180251881Speter iter_baton, 181251881Speter provider_baton, 182251881Speter parameters, 183251881Speter realmstring, 184251881Speter windows_password_decrypter, 185251881Speter SVN_AUTH__WINCRYPT_PASSWORD_TYPE, 186251881Speter pool); 187251881Speter} 188251881Speter 189251881Speter/* Save encrypted credentials to the simple provider's cache. */ 190251881Speterstatic svn_error_t * 191251881Speterwindows_simple_save_creds(svn_boolean_t *saved, 192251881Speter void *credentials, 193251881Speter void *provider_baton, 194251881Speter apr_hash_t *parameters, 195251881Speter const char *realmstring, 196251881Speter apr_pool_t *pool) 197251881Speter{ 198251881Speter return svn_auth__simple_creds_cache_set(saved, credentials, 199251881Speter provider_baton, 200251881Speter parameters, 201251881Speter realmstring, 202251881Speter windows_password_encrypter, 203251881Speter SVN_AUTH__WINCRYPT_PASSWORD_TYPE, 204251881Speter pool); 205251881Speter} 206251881Speter 207251881Speterstatic const svn_auth_provider_t windows_simple_provider = { 208251881Speter SVN_AUTH_CRED_SIMPLE, 209251881Speter windows_simple_first_creds, 210251881Speter NULL, 211251881Speter windows_simple_save_creds 212251881Speter}; 213251881Speter 214251881Speter 215251881Speter/* Public API */ 216251881Spetervoid 217299742Sdimsvn_auth__get_windows_simple_provider(svn_auth_provider_object_t **provider, 218251881Speter apr_pool_t *pool) 219251881Speter{ 220251881Speter svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 221251881Speter 222251881Speter po->vtable = &windows_simple_provider; 223251881Speter *provider = po; 224251881Speter} 225251881Speter 226251881Speter 227251881Speter/*-----------------------------------------------------------------------*/ 228251881Speter/* Windows SSL server trust provider, validates ssl certificate using */ 229251881Speter/* CryptoApi. */ 230251881Speter/*-----------------------------------------------------------------------*/ 231251881Speter 232251881Speter/* Implementation of svn_auth__password_set_t that encrypts 233251881Speter the incoming password using the Windows CryptoAPI. */ 234251881Speterstatic svn_error_t * 235251881Speterwindows_ssl_client_cert_pw_encrypter(svn_boolean_t *done, 236251881Speter apr_hash_t *creds, 237251881Speter const char *realmstring, 238251881Speter const char *username, 239251881Speter const char *in, 240251881Speter apr_hash_t *parameters, 241251881Speter svn_boolean_t non_interactive, 242251881Speter apr_pool_t *pool) 243251881Speter{ 244251881Speter const svn_string_t *coded; 245251881Speter 246251881Speter coded = encrypt_data(svn_string_create(in, pool), pool); 247251881Speter if (coded) 248251881Speter { 249251881Speter coded = svn_base64_encode_string2(coded, FALSE, pool); 250251881Speter SVN_ERR(svn_auth__ssl_client_cert_pw_set(done, creds, realmstring, 251251881Speter username, coded->data, 252251881Speter parameters, non_interactive, 253251881Speter pool)); 254251881Speter } 255251881Speter 256251881Speter return SVN_NO_ERROR; 257251881Speter} 258251881Speter 259251881Speter/* Implementation of svn_auth__password_get_t that decrypts 260251881Speter the incoming password using the Windows CryptoAPI and verifies its 261251881Speter validity. */ 262251881Speterstatic svn_error_t * 263251881Speterwindows_ssl_client_cert_pw_decrypter(svn_boolean_t *done, 264251881Speter const char **out, 265251881Speter apr_hash_t *creds, 266251881Speter const char *realmstring, 267251881Speter const char *username, 268251881Speter apr_hash_t *parameters, 269251881Speter svn_boolean_t non_interactive, 270251881Speter apr_pool_t *pool) 271251881Speter{ 272251881Speter const svn_string_t *orig; 273251881Speter const char *in; 274251881Speter 275251881Speter SVN_ERR(svn_auth__ssl_client_cert_pw_get(done, &in, creds, realmstring, 276251881Speter username, parameters, 277251881Speter non_interactive, pool)); 278251881Speter if (!*done) 279251881Speter return SVN_NO_ERROR; 280251881Speter 281251881Speter orig = svn_base64_decode_string(svn_string_create(in, pool), pool); 282251881Speter orig = decrypt_data(orig, pool); 283251881Speter if (orig) 284251881Speter { 285251881Speter *out = orig->data; 286251881Speter *done = TRUE; 287251881Speter } 288251881Speter else 289251881Speter { 290251881Speter *done = FALSE; 291251881Speter } 292251881Speter return SVN_NO_ERROR; 293251881Speter} 294251881Speter 295251881Speter/* Get cached encrypted credentials from the simple provider's cache. */ 296251881Speterstatic svn_error_t * 297251881Speterwindows_ssl_client_cert_pw_first_creds(void **credentials, 298251881Speter void **iter_baton, 299251881Speter void *provider_baton, 300251881Speter apr_hash_t *parameters, 301251881Speter const char *realmstring, 302251881Speter apr_pool_t *pool) 303251881Speter{ 304251881Speter return svn_auth__ssl_client_cert_pw_cache_get( 305251881Speter credentials, iter_baton, provider_baton, parameters, realmstring, 306251881Speter windows_ssl_client_cert_pw_decrypter, 307251881Speter SVN_AUTH__WINCRYPT_PASSWORD_TYPE, pool); 308251881Speter} 309251881Speter 310251881Speter/* Save encrypted credentials to the simple provider's cache. */ 311251881Speterstatic svn_error_t * 312251881Speterwindows_ssl_client_cert_pw_save_creds(svn_boolean_t *saved, 313251881Speter void *credentials, 314251881Speter void *provider_baton, 315251881Speter apr_hash_t *parameters, 316251881Speter const char *realmstring, 317251881Speter apr_pool_t *pool) 318251881Speter{ 319251881Speter return svn_auth__ssl_client_cert_pw_cache_set( 320251881Speter saved, credentials, provider_baton, parameters, realmstring, 321251881Speter windows_ssl_client_cert_pw_encrypter, 322251881Speter SVN_AUTH__WINCRYPT_PASSWORD_TYPE, pool); 323251881Speter} 324251881Speter 325251881Speterstatic const svn_auth_provider_t windows_ssl_client_cert_pw_provider = { 326251881Speter SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 327251881Speter windows_ssl_client_cert_pw_first_creds, 328251881Speter NULL, 329251881Speter windows_ssl_client_cert_pw_save_creds 330251881Speter}; 331251881Speter 332251881Speter 333251881Speter/* Public API */ 334251881Spetervoid 335299742Sdimsvn_auth__get_windows_ssl_client_cert_pw_provider 336251881Speter (svn_auth_provider_object_t **provider, 337251881Speter apr_pool_t *pool) 338251881Speter{ 339251881Speter svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 340251881Speter 341251881Speter po->vtable = &windows_ssl_client_cert_pw_provider; 342251881Speter *provider = po; 343251881Speter} 344251881Speter 345251881Speter 346251881Speter/*-----------------------------------------------------------------------*/ 347251881Speter/* Windows SSL server trust provider, validates ssl certificate using */ 348251881Speter/* CryptoApi. */ 349251881Speter/*-----------------------------------------------------------------------*/ 350251881Speter 351251881Speter/* Helper to create CryptoAPI CERT_CONTEXT from base64 encoded BASE64_CERT. 352251881Speter * Returns NULL on error. 353251881Speter */ 354251881Speterstatic PCCERT_CONTEXT 355251881Spetercertcontext_from_base64(const char *base64_cert, apr_pool_t *pool) 356251881Speter{ 357251881Speter PCCERT_CONTEXT cert_context = NULL; 358251881Speter int cert_len; 359251881Speter BYTE *binary_cert; 360251881Speter 361251881Speter /* Use apr-util as CryptStringToBinaryA is available only on XP+. */ 362251881Speter binary_cert = apr_palloc(pool, 363251881Speter apr_base64_decode_len(base64_cert)); 364251881Speter cert_len = apr_base64_decode((char*)binary_cert, base64_cert); 365251881Speter 366251881Speter /* Parse the certificate into a context. */ 367251881Speter cert_context = CertCreateCertificateContext 368251881Speter (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, binary_cert, cert_len); 369251881Speter 370251881Speter return cert_context; 371251881Speter} 372251881Speter 373251881Speter/* Helper for windows_ssl_server_trust_first_credentials for validating 374251881Speter * certificate using CryptoApi. Sets *OK_P to TRUE if base64 encoded ASCII_CERT 375251881Speter * certificate considered as valid. 376251881Speter */ 377251881Speterstatic svn_error_t * 378251881Speterwindows_validate_certificate(svn_boolean_t *ok_p, 379251881Speter const char *ascii_cert, 380251881Speter apr_pool_t *pool) 381251881Speter{ 382251881Speter PCCERT_CONTEXT cert_context = NULL; 383251881Speter CERT_CHAIN_PARA chain_para; 384251881Speter PCCERT_CHAIN_CONTEXT chain_context = NULL; 385251881Speter 386251881Speter *ok_p = FALSE; 387251881Speter 388251881Speter /* Parse the certificate into a context. */ 389251881Speter cert_context = certcontext_from_base64(ascii_cert, pool); 390251881Speter 391251881Speter if (cert_context) 392251881Speter { 393251881Speter /* Retrieve the certificate chain of the certificate 394251881Speter (a certificate without a valid root does not have a chain). */ 395251881Speter memset(&chain_para, 0, sizeof(chain_para)); 396251881Speter chain_para.cbSize = sizeof(chain_para); 397251881Speter 398251881Speter if (CertGetCertificateChain(NULL, cert_context, NULL, NULL, &chain_para, 399251881Speter CERT_CHAIN_CACHE_END_CERT | 400251881Speter CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, 401251881Speter NULL, &chain_context)) 402251881Speter { 403251881Speter CERT_CHAIN_POLICY_PARA policy_para; 404251881Speter CERT_CHAIN_POLICY_STATUS policy_status; 405251881Speter 406251881Speter policy_para.cbSize = sizeof(policy_para); 407251881Speter policy_para.dwFlags = 0; 408251881Speter policy_para.pvExtraPolicyPara = NULL; 409251881Speter 410251881Speter policy_status.cbSize = sizeof(policy_status); 411251881Speter 412251881Speter if (CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, 413251881Speter chain_context, &policy_para, 414251881Speter &policy_status)) 415251881Speter { 416251881Speter if (policy_status.dwError == S_OK) 417251881Speter { 418251881Speter /* Windows thinks the certificate is valid. */ 419251881Speter *ok_p = TRUE; 420251881Speter } 421251881Speter } 422251881Speter 423251881Speter CertFreeCertificateChain(chain_context); 424251881Speter } 425251881Speter CertFreeCertificateContext(cert_context); 426251881Speter } 427251881Speter 428251881Speter return SVN_NO_ERROR; 429251881Speter} 430251881Speter 431251881Speter/* Retrieve ssl server CA failure overrides (if any) from CryptoApi. */ 432251881Speterstatic svn_error_t * 433251881Speterwindows_ssl_server_trust_first_credentials(void **credentials, 434251881Speter void **iter_baton, 435251881Speter void *provider_baton, 436251881Speter apr_hash_t *parameters, 437251881Speter const char *realmstring, 438251881Speter apr_pool_t *pool) 439251881Speter{ 440262253Speter apr_uint32_t *failure_ptr = svn_hash_gets(parameters, 441262253Speter SVN_AUTH_PARAM_SSL_SERVER_FAILURES); 442262253Speter apr_uint32_t failures = *failure_ptr; 443251881Speter const svn_auth_ssl_server_cert_info_t *cert_info = 444251881Speter svn_hash_gets(parameters, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO); 445251881Speter 446251881Speter *credentials = NULL; 447251881Speter *iter_baton = NULL; 448251881Speter 449251881Speter /* We can accept only unknown certificate authority. */ 450262253Speter if (failures & SVN_AUTH_SSL_UNKNOWNCA) 451251881Speter { 452251881Speter svn_boolean_t ok; 453251881Speter 454251881Speter SVN_ERR(windows_validate_certificate(&ok, cert_info->ascii_cert, pool)); 455251881Speter 456251881Speter /* Windows thinks that certificate is ok. */ 457251881Speter if (ok) 458251881Speter { 459251881Speter /* Clear failure flag. */ 460262253Speter failures &= ~SVN_AUTH_SSL_UNKNOWNCA; 461251881Speter } 462251881Speter } 463251881Speter 464251881Speter /* If all failures are cleared now, we return the creds */ 465262253Speter if (! failures) 466251881Speter { 467251881Speter svn_auth_cred_ssl_server_trust_t *creds = 468251881Speter apr_pcalloc(pool, sizeof(*creds)); 469262253Speter creds->accepted_failures = *failure_ptr & ~failures; 470251881Speter creds->may_save = FALSE; /* No need to save it. */ 471251881Speter *credentials = creds; 472251881Speter } 473251881Speter 474251881Speter return SVN_NO_ERROR; 475251881Speter} 476251881Speter 477251881Speterstatic const svn_auth_provider_t windows_server_trust_provider = { 478251881Speter SVN_AUTH_CRED_SSL_SERVER_TRUST, 479251881Speter windows_ssl_server_trust_first_credentials, 480251881Speter NULL, 481251881Speter NULL, 482251881Speter}; 483251881Speter 484251881Speter/* Public API */ 485251881Spetervoid 486299742Sdimsvn_auth__get_windows_ssl_server_trust_provider 487251881Speter (svn_auth_provider_object_t **provider, apr_pool_t *pool) 488251881Speter{ 489251881Speter svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 490251881Speter 491251881Speter po->vtable = &windows_server_trust_provider; 492251881Speter *provider = po; 493251881Speter} 494251881Speter 495262253Speterstatic const svn_auth_provider_t windows_server_authority_provider = { 496262253Speter SVN_AUTH_CRED_SSL_SERVER_AUTHORITY, 497262253Speter windows_ssl_server_trust_first_credentials, 498262253Speter NULL, 499262253Speter NULL, 500262253Speter}; 501262253Speter 502262253Speter/* Public API */ 503262253Spetervoid 504262253Spetersvn_auth__get_windows_ssl_server_authority_provider( 505262253Speter svn_auth_provider_object_t **provider, 506262253Speter apr_pool_t *pool) 507262253Speter{ 508262253Speter svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 509262253Speter 510262253Speter po->vtable = &windows_server_authority_provider; 511262253Speter *provider = po; 512262253Speter} 513262253Speter 514262253Speter 515251881Speter#endif /* WIN32 */ 516