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
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 *
encrypt_data(const svn_string_t * orig,apr_pool_t * pool)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 *
decrypt_data(const svn_string_t * crypted,apr_pool_t * pool)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
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 *
windows_password_encrypter(svn_boolean_t * done,apr_hash_t * creds,const char * realmstring,const char * username,const char * in,apr_hash_t * parameters,svn_boolean_t non_interactive,apr_pool_t * pool)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 *
windows_password_decrypter(svn_boolean_t * done,const char ** out,apr_hash_t * creds,const char * realmstring,const char * username,apr_hash_t * parameters,svn_boolean_t non_interactive,apr_pool_t * pool)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 *
windows_simple_first_creds(void ** credentials,void ** iter_baton,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)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 *
windows_simple_save_creds(svn_boolean_t * saved,void * credentials,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)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
svn_auth__get_windows_simple_provider(svn_auth_provider_object_t ** provider,apr_pool_t * pool)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
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 *
windows_ssl_client_cert_pw_encrypter(svn_boolean_t * done,apr_hash_t * creds,const char * realmstring,const char * username,const char * in,apr_hash_t * parameters,svn_boolean_t non_interactive,apr_pool_t * pool)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 *
windows_ssl_client_cert_pw_decrypter(svn_boolean_t * done,const char ** out,apr_hash_t * creds,const char * realmstring,const char * username,apr_hash_t * parameters,svn_boolean_t non_interactive,apr_pool_t * pool)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 *
windows_ssl_client_cert_pw_first_creds(void ** credentials,void ** iter_baton,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)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 *
windows_ssl_client_cert_pw_save_creds(svn_boolean_t * saved,void * credentials,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)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
svn_auth__get_windows_ssl_client_cert_pw_provider(svn_auth_provider_object_t ** provider,apr_pool_t * pool)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
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
certcontext_from_base64(const char * base64_cert,apr_pool_t * pool)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 *
windows_validate_certificate(svn_boolean_t * ok_p,const char * ascii_cert,apr_pool_t * pool)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 /* Don't hit the wire for URL based objects and revocation checks, as
399 that may cause stalls, network timeouts or spurious errors in cases
400 such as with the remote OCSP and CRL endpoints being inaccessible or
401 unreliable.
402
403 For this particular case of the SVN_AUTH_SSL_UNKNOWNCA cert failure
404 override we should be okay with just the data that we have immediately
405 available on the local machine.
406 */
407 if (CertGetCertificateChain(NULL, cert_context, NULL, NULL, &chain_para,
408 CERT_CHAIN_CACHE_END_CERT |
409 CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL |
410 CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT |
411 CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY,
412 NULL, &chain_context))
413 {
414 CERT_CHAIN_POLICY_PARA policy_para;
415 CERT_CHAIN_POLICY_STATUS policy_status;
416
417 policy_para.cbSize = sizeof(policy_para);
418 /* We only use the local data for revocation checks, so they may
419 fail with errors like CRYPT_E_REVOCATION_OFFLINE; ignore those. */
420 policy_para.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
421 policy_para.pvExtraPolicyPara = NULL;
422
423 policy_status.cbSize = sizeof(policy_status);
424
425 if (CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL,
426 chain_context, &policy_para,
427 &policy_status))
428 {
429 if (policy_status.dwError == S_OK)
430 {
431 /* Windows thinks the certificate is valid. */
432 *ok_p = TRUE;
433 }
434 }
435
436 CertFreeCertificateChain(chain_context);
437 }
438 CertFreeCertificateContext(cert_context);
439 }
440
441 return SVN_NO_ERROR;
442 }
443
444 /* Retrieve ssl server CA failure overrides (if any) from CryptoApi. */
445 static svn_error_t *
windows_ssl_server_trust_first_credentials(void ** credentials,void ** iter_baton,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)446 windows_ssl_server_trust_first_credentials(void **credentials,
447 void **iter_baton,
448 void *provider_baton,
449 apr_hash_t *parameters,
450 const char *realmstring,
451 apr_pool_t *pool)
452 {
453 apr_uint32_t *failure_ptr = svn_hash_gets(parameters,
454 SVN_AUTH_PARAM_SSL_SERVER_FAILURES);
455 apr_uint32_t failures = *failure_ptr;
456 const svn_auth_ssl_server_cert_info_t *cert_info =
457 svn_hash_gets(parameters, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO);
458
459 *credentials = NULL;
460 *iter_baton = NULL;
461
462 /* We can accept only unknown certificate authority. */
463 if (failures & SVN_AUTH_SSL_UNKNOWNCA)
464 {
465 svn_boolean_t ok;
466
467 SVN_ERR(windows_validate_certificate(&ok, cert_info->ascii_cert, pool));
468
469 /* Windows thinks that certificate is ok. */
470 if (ok)
471 {
472 /* Clear failure flag. */
473 failures &= ~SVN_AUTH_SSL_UNKNOWNCA;
474 }
475 }
476
477 /* If all failures are cleared now, we return the creds */
478 if (! failures)
479 {
480 svn_auth_cred_ssl_server_trust_t *creds =
481 apr_pcalloc(pool, sizeof(*creds));
482 creds->accepted_failures = *failure_ptr & ~failures;
483 creds->may_save = FALSE; /* No need to save it. */
484 *credentials = creds;
485 }
486
487 return SVN_NO_ERROR;
488 }
489
490 static const svn_auth_provider_t windows_server_trust_provider = {
491 SVN_AUTH_CRED_SSL_SERVER_TRUST,
492 windows_ssl_server_trust_first_credentials,
493 NULL,
494 NULL,
495 };
496
497 /* Public API */
498 void
svn_auth__get_windows_ssl_server_trust_provider(svn_auth_provider_object_t ** provider,apr_pool_t * pool)499 svn_auth__get_windows_ssl_server_trust_provider
500 (svn_auth_provider_object_t **provider, apr_pool_t *pool)
501 {
502 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
503
504 po->vtable = &windows_server_trust_provider;
505 *provider = po;
506 }
507
508 static const svn_auth_provider_t windows_server_authority_provider = {
509 SVN_AUTH_CRED_SSL_SERVER_AUTHORITY,
510 windows_ssl_server_trust_first_credentials,
511 NULL,
512 NULL,
513 };
514
515 /* Public API */
516 void
svn_auth__get_windows_ssl_server_authority_provider(svn_auth_provider_object_t ** provider,apr_pool_t * pool)517 svn_auth__get_windows_ssl_server_authority_provider(
518 svn_auth_provider_object_t **provider,
519 apr_pool_t *pool)
520 {
521 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
522
523 po->vtable = &windows_server_authority_provider;
524 *provider = po;
525 }
526
527
528 #else /* !WIN32 */
529
530 /* Silence OSX ranlib warnings about object files with no symbols. */
531 #include <apr.h>
532 extern const apr_uint32_t svn__fake__win32_crypto;
533 const apr_uint32_t svn__fake__win32_crypto = 0xdeadbeef;
534
535 #endif /* WIN32 */
536