]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_subr/win32_crypto.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_subr / win32_crypto.c
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 */
25 typedef 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 \f
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. */
54 static 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. */
59 const svn_string_t *
60 encrypt_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. */
81 const svn_string_t *
82 decrypt_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 \f
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. */
111 static svn_error_t *
112 windows_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. */
138 static svn_error_t *
139 windows_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. */
171 static svn_error_t *
172 windows_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. */
190 static svn_error_t *
191 windows_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
207 static 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 */
216 void
217 svn_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 \f
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. */
234 static svn_error_t *
235 windows_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. */
262 static svn_error_t *
263 windows_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. */
296 static svn_error_t *
297 windows_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. */
311 static svn_error_t *
312 windows_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
325 static 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 */
334 void
335 svn_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 \f
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  */
354 static PCCERT_CONTEXT
355 certcontext_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  */
377 static svn_error_t *
378 windows_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. */
432 static svn_error_t *
433 windows_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
477 static 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 */
485 void
486 svn_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
495 static 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 */
503 void
504 svn_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 */