]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_subr/win32_crypto.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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 "private/svn_auth_private.h"
44
45 #include "svn_private_config.h"
46
47 #include <wincrypt.h>
48
49 \f
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. */
53 static 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. */
58 const svn_string_t *
59 encrypt_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. */
80 const svn_string_t *
81 decrypt_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 \f
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. */
110 static svn_error_t *
111 windows_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. */
137 static svn_error_t *
138 windows_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. */
170 static svn_error_t *
171 windows_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. */
189 static svn_error_t *
190 windows_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
206 static 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 */
215 void
216 svn_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 \f
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. */
233 static svn_error_t *
234 windows_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. */
261 static svn_error_t *
262 windows_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. */
295 static svn_error_t *
296 windows_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. */
310 static svn_error_t *
311 windows_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
324 static 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 */
333 void
334 svn_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 \f
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  */
353 static PCCERT_CONTEXT
354 certcontext_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  */
376 static svn_error_t *
377 windows_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. */
431 static svn_error_t *
432 windows_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
476 static 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 */
484 void
485 svn_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
494 static 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 */
502 void
503 svn_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 */