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