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