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 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 *
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)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
svn_auth__get_windows_ssl_server_trust_provider(svn_auth_provider_object_t ** provider,apr_pool_t * pool)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
svn_auth__get_windows_ssl_server_authority_provider(svn_auth_provider_object_t ** provider,apr_pool_t * pool)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 */
516