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