diff options
author | peter <peter@FreeBSD.org> | 2013-06-18 02:07:41 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 2013-06-18 02:07:41 +0000 |
commit | d25dac7fcc6acc838b71bbda8916fd9665c709ab (patch) | |
tree | 135691142dc0e75a5e5d97b5074d03436435b8e0 /subversion/libsvn_subr/win32_crypto.c | |
download | FreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.zip FreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.tar.gz |
Import trimmed svn-1.8.0-rc3
Diffstat (limited to 'subversion/libsvn_subr/win32_crypto.c')
-rw-r--r-- | subversion/libsvn_subr/win32_crypto.c | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/subversion/libsvn_subr/win32_crypto.c b/subversion/libsvn_subr/win32_crypto.c new file mode 100644 index 0000000..a7e3828 --- /dev/null +++ b/subversion/libsvn_subr/win32_crypto.c @@ -0,0 +1,492 @@ +/* + * win32_crypto.c: win32 providers for SVN_AUTH_* + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +/* prevent "empty compilation unit" warning on e.g. UNIX */ +typedef int win32_crypto__dummy; + +/* ==================================================================== */ + +#if defined(WIN32) && !defined(__MINGW32__) + +/*** Includes. ***/ + +#include <apr_pools.h> +#include <apr_base64.h> +#include "svn_auth.h" +#include "svn_error.h" +#include "svn_hash.h" +#include "svn_utf.h" +#include "svn_config.h" +#include "svn_user.h" +#include "svn_base64.h" + +#include "private/svn_auth_private.h" + +#include "svn_private_config.h" + +#include <wincrypt.h> + + +/* The description string that's combined with unencrypted data by the + Windows CryptoAPI. Used during decryption to verify that the + encrypted data were valid. */ +static const WCHAR description[] = L"auth_svn.simple.wincrypt"; + + +/* Return a copy of ORIG, encrypted using the Windows CryptoAPI and + allocated from POOL. */ +const svn_string_t * +encrypt_data(const svn_string_t *orig, + apr_pool_t *pool) +{ + DATA_BLOB blobin; + DATA_BLOB blobout; + const svn_string_t *crypted = NULL; + + blobin.cbData = orig->len; + blobin.pbData = (BYTE *)orig->data; + if (CryptProtectData(&blobin, description, NULL, NULL, NULL, + CRYPTPROTECT_UI_FORBIDDEN, &blobout)) + { + crypted = svn_string_ncreate((const char *)blobout.pbData, + blobout.cbData, pool); + LocalFree(blobout.pbData); + } + return crypted; +} + +/* Return a copy of CRYPTED, decrypted using the Windows CryptoAPI and + allocated from POOL. */ +const svn_string_t * +decrypt_data(const svn_string_t *crypted, + apr_pool_t *pool) +{ + DATA_BLOB blobin; + DATA_BLOB blobout; + LPWSTR descr; + const svn_string_t *orig = NULL; + + blobin.cbData = crypted->len; + blobin.pbData = (BYTE *)crypted->data; + if (CryptUnprotectData(&blobin, &descr, NULL, NULL, NULL, + CRYPTPROTECT_UI_FORBIDDEN, &blobout)) + { + if (0 == lstrcmpW(descr, description)) + orig = svn_string_ncreate((const char *)blobout.pbData, + blobout.cbData, pool); + LocalFree(blobout.pbData); + LocalFree(descr); + } + return orig; +} + + +/*-----------------------------------------------------------------------*/ +/* Windows simple provider, encrypts the password on Win2k and later. */ +/*-----------------------------------------------------------------------*/ + +/* Implementation of svn_auth__password_set_t that encrypts + the incoming password using the Windows CryptoAPI. */ +static svn_error_t * +windows_password_encrypter(svn_boolean_t *done, + apr_hash_t *creds, + const char *realmstring, + const char *username, + const char *in, + apr_hash_t *parameters, + svn_boolean_t non_interactive, + apr_pool_t *pool) +{ + const svn_string_t *coded; + + coded = encrypt_data(svn_string_create(in, pool), pool); + if (coded) + { + coded = svn_base64_encode_string2(coded, FALSE, pool); + SVN_ERR(svn_auth__simple_password_set(done, creds, realmstring, username, + coded->data, parameters, + non_interactive, pool)); + } + + return SVN_NO_ERROR; +} + +/* Implementation of svn_auth__password_get_t that decrypts + the incoming password using the Windows CryptoAPI and verifies its + validity. */ +static svn_error_t * +windows_password_decrypter(svn_boolean_t *done, + const char **out, + apr_hash_t *creds, + const char *realmstring, + const char *username, + apr_hash_t *parameters, + svn_boolean_t non_interactive, + apr_pool_t *pool) +{ + const svn_string_t *orig; + const char *in; + + SVN_ERR(svn_auth__simple_password_get(done, &in, creds, realmstring, username, + parameters, non_interactive, pool)); + if (!*done) + return SVN_NO_ERROR; + + orig = svn_base64_decode_string(svn_string_create(in, pool), pool); + orig = decrypt_data(orig, pool); + if (orig) + { + *out = orig->data; + *done = TRUE; + } + else + { + *done = FALSE; + } + return SVN_NO_ERROR; +} + +/* Get cached encrypted credentials from the simple provider's cache. */ +static svn_error_t * +windows_simple_first_creds(void **credentials, + void **iter_baton, + void *provider_baton, + apr_hash_t *parameters, + const char *realmstring, + apr_pool_t *pool) +{ + return svn_auth__simple_creds_cache_get(credentials, + iter_baton, + provider_baton, + parameters, + realmstring, + windows_password_decrypter, + SVN_AUTH__WINCRYPT_PASSWORD_TYPE, + pool); +} + +/* Save encrypted credentials to the simple provider's cache. */ +static svn_error_t * +windows_simple_save_creds(svn_boolean_t *saved, + void *credentials, + void *provider_baton, + apr_hash_t *parameters, + const char *realmstring, + apr_pool_t *pool) +{ + return svn_auth__simple_creds_cache_set(saved, credentials, + provider_baton, + parameters, + realmstring, + windows_password_encrypter, + SVN_AUTH__WINCRYPT_PASSWORD_TYPE, + pool); +} + +static const svn_auth_provider_t windows_simple_provider = { + SVN_AUTH_CRED_SIMPLE, + windows_simple_first_creds, + NULL, + windows_simple_save_creds +}; + + +/* Public API */ +void +svn_auth_get_windows_simple_provider(svn_auth_provider_object_t **provider, + apr_pool_t *pool) +{ + svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); + + po->vtable = &windows_simple_provider; + *provider = po; +} + + +/*-----------------------------------------------------------------------*/ +/* Windows SSL server trust provider, validates ssl certificate using */ +/* CryptoApi. */ +/*-----------------------------------------------------------------------*/ + +/* Implementation of svn_auth__password_set_t that encrypts + the incoming password using the Windows CryptoAPI. */ +static svn_error_t * +windows_ssl_client_cert_pw_encrypter(svn_boolean_t *done, + apr_hash_t *creds, + const char *realmstring, + const char *username, + const char *in, + apr_hash_t *parameters, + svn_boolean_t non_interactive, + apr_pool_t *pool) +{ + const svn_string_t *coded; + + coded = encrypt_data(svn_string_create(in, pool), pool); + if (coded) + { + coded = svn_base64_encode_string2(coded, FALSE, pool); + SVN_ERR(svn_auth__ssl_client_cert_pw_set(done, creds, realmstring, + username, coded->data, + parameters, non_interactive, + pool)); + } + + return SVN_NO_ERROR; +} + +/* Implementation of svn_auth__password_get_t that decrypts + the incoming password using the Windows CryptoAPI and verifies its + validity. */ +static svn_error_t * +windows_ssl_client_cert_pw_decrypter(svn_boolean_t *done, + const char **out, + apr_hash_t *creds, + const char *realmstring, + const char *username, + apr_hash_t *parameters, + svn_boolean_t non_interactive, + apr_pool_t *pool) +{ + const svn_string_t *orig; + const char *in; + + SVN_ERR(svn_auth__ssl_client_cert_pw_get(done, &in, creds, realmstring, + username, parameters, + non_interactive, pool)); + if (!*done) + return SVN_NO_ERROR; + + orig = svn_base64_decode_string(svn_string_create(in, pool), pool); + orig = decrypt_data(orig, pool); + if (orig) + { + *out = orig->data; + *done = TRUE; + } + else + { + *done = FALSE; + } + return SVN_NO_ERROR; +} + +/* Get cached encrypted credentials from the simple provider's cache. */ +static svn_error_t * +windows_ssl_client_cert_pw_first_creds(void **credentials, + void **iter_baton, + void *provider_baton, + apr_hash_t *parameters, + const char *realmstring, + apr_pool_t *pool) +{ + return svn_auth__ssl_client_cert_pw_cache_get( + credentials, iter_baton, provider_baton, parameters, realmstring, + windows_ssl_client_cert_pw_decrypter, + SVN_AUTH__WINCRYPT_PASSWORD_TYPE, pool); +} + +/* Save encrypted credentials to the simple provider's cache. */ +static svn_error_t * +windows_ssl_client_cert_pw_save_creds(svn_boolean_t *saved, + void *credentials, + void *provider_baton, + apr_hash_t *parameters, + const char *realmstring, + apr_pool_t *pool) +{ + return svn_auth__ssl_client_cert_pw_cache_set( + saved, credentials, provider_baton, parameters, realmstring, + windows_ssl_client_cert_pw_encrypter, + SVN_AUTH__WINCRYPT_PASSWORD_TYPE, pool); +} + +static const svn_auth_provider_t windows_ssl_client_cert_pw_provider = { + SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, + windows_ssl_client_cert_pw_first_creds, + NULL, + windows_ssl_client_cert_pw_save_creds +}; + + +/* Public API */ +void +svn_auth_get_windows_ssl_client_cert_pw_provider + (svn_auth_provider_object_t **provider, + apr_pool_t *pool) +{ + svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); + + po->vtable = &windows_ssl_client_cert_pw_provider; + *provider = po; +} + + +/*-----------------------------------------------------------------------*/ +/* Windows SSL server trust provider, validates ssl certificate using */ +/* CryptoApi. */ +/*-----------------------------------------------------------------------*/ + +/* Helper to create CryptoAPI CERT_CONTEXT from base64 encoded BASE64_CERT. + * Returns NULL on error. + */ +static PCCERT_CONTEXT +certcontext_from_base64(const char *base64_cert, apr_pool_t *pool) +{ + PCCERT_CONTEXT cert_context = NULL; + int cert_len; + BYTE *binary_cert; + + /* Use apr-util as CryptStringToBinaryA is available only on XP+. */ + binary_cert = apr_palloc(pool, + apr_base64_decode_len(base64_cert)); + cert_len = apr_base64_decode((char*)binary_cert, base64_cert); + + /* Parse the certificate into a context. */ + cert_context = CertCreateCertificateContext + (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, binary_cert, cert_len); + + return cert_context; +} + +/* Helper for windows_ssl_server_trust_first_credentials for validating + * certificate using CryptoApi. Sets *OK_P to TRUE if base64 encoded ASCII_CERT + * certificate considered as valid. + */ +static svn_error_t * +windows_validate_certificate(svn_boolean_t *ok_p, + const char *ascii_cert, + apr_pool_t *pool) +{ + PCCERT_CONTEXT cert_context = NULL; + CERT_CHAIN_PARA chain_para; + PCCERT_CHAIN_CONTEXT chain_context = NULL; + + *ok_p = FALSE; + + /* Parse the certificate into a context. */ + cert_context = certcontext_from_base64(ascii_cert, pool); + + if (cert_context) + { + /* Retrieve the certificate chain of the certificate + (a certificate without a valid root does not have a chain). */ + memset(&chain_para, 0, sizeof(chain_para)); + chain_para.cbSize = sizeof(chain_para); + + if (CertGetCertificateChain(NULL, cert_context, NULL, NULL, &chain_para, + CERT_CHAIN_CACHE_END_CERT | + CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, + NULL, &chain_context)) + { + CERT_CHAIN_POLICY_PARA policy_para; + CERT_CHAIN_POLICY_STATUS policy_status; + + policy_para.cbSize = sizeof(policy_para); + policy_para.dwFlags = 0; + policy_para.pvExtraPolicyPara = NULL; + + policy_status.cbSize = sizeof(policy_status); + + if (CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, + chain_context, &policy_para, + &policy_status)) + { + if (policy_status.dwError == S_OK) + { + /* Windows thinks the certificate is valid. */ + *ok_p = TRUE; + } + } + + CertFreeCertificateChain(chain_context); + } + CertFreeCertificateContext(cert_context); + } + + return SVN_NO_ERROR; +} + +/* Retrieve ssl server CA failure overrides (if any) from CryptoApi. */ +static svn_error_t * +windows_ssl_server_trust_first_credentials(void **credentials, + void **iter_baton, + void *provider_baton, + apr_hash_t *parameters, + const char *realmstring, + apr_pool_t *pool) +{ + apr_uint32_t *failures = svn_hash_gets(parameters, + SVN_AUTH_PARAM_SSL_SERVER_FAILURES); + const svn_auth_ssl_server_cert_info_t *cert_info = + svn_hash_gets(parameters, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO); + + *credentials = NULL; + *iter_baton = NULL; + + /* We can accept only unknown certificate authority. */ + if (*failures & SVN_AUTH_SSL_UNKNOWNCA) + { + svn_boolean_t ok; + + SVN_ERR(windows_validate_certificate(&ok, cert_info->ascii_cert, pool)); + + /* Windows thinks that certificate is ok. */ + if (ok) + { + /* Clear failure flag. */ + *failures &= ~SVN_AUTH_SSL_UNKNOWNCA; + } + } + + /* If all failures are cleared now, we return the creds */ + if (! *failures) + { + svn_auth_cred_ssl_server_trust_t *creds = + apr_pcalloc(pool, sizeof(*creds)); + creds->may_save = FALSE; /* No need to save it. */ + *credentials = creds; + } + + return SVN_NO_ERROR; +} + +static const svn_auth_provider_t windows_server_trust_provider = { + SVN_AUTH_CRED_SSL_SERVER_TRUST, + windows_ssl_server_trust_first_credentials, + NULL, + NULL, +}; + +/* Public API */ +void +svn_auth_get_windows_ssl_server_trust_provider + (svn_auth_provider_object_t **provider, apr_pool_t *pool) +{ + svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); + + po->vtable = &windows_server_trust_provider; + *provider = po; +} + +#endif /* WIN32 */ |