* subversion/libsvn_subr/validate.c
[svn.git] / subversion / libsvn_subr / ssl_server_trust_providers.c
blobe31b40ffe1d19cb2f6a644a0f20fab905c29a826
1 /*
2 * ssl_server_trust_providers.c: providers for
3 * SVN_AUTH_CRED_SSL_SERVER_TRUST
5 * ====================================================================
6 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
8 * This software is licensed as described in the file COPYING, which
9 * you should have received as part of this distribution. The terms
10 * are also available at http://subversion.tigris.org/license-1.html.
11 * If newer versions of this license are posted there, you may use a
12 * newer version instead, at your option.
14 * This software consists of voluntary contributions made by many
15 * individuals. For exact contribution history, see the revision
16 * history and logs, available at http://subversion.tigris.org/.
17 * ====================================================================
20 /* ==================================================================== */
24 /*** Includes. ***/
26 #include <apr_pools.h>
27 #include "svn_auth.h"
28 #include "svn_error.h"
29 #include "svn_config.h"
32 /*-----------------------------------------------------------------------*/
33 /* File provider */
34 /*-----------------------------------------------------------------------*/
36 /* The keys that will be stored on disk */
37 #define SVN_AUTH__AUTHFILE_ASCII_CERT_KEY "ascii_cert"
38 #define SVN_AUTH__AUTHFILE_FAILURES_KEY "failures"
41 /* retieve ssl server CA failure overrides (if any) from servers
42 config */
43 static svn_error_t *
44 ssl_server_trust_file_first_credentials(void **credentials,
45 void **iter_baton,
46 void *provider_baton,
47 apr_hash_t *parameters,
48 const char *realmstring,
49 apr_pool_t *pool)
51 apr_uint32_t *failures = apr_hash_get(parameters,
52 SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
53 APR_HASH_KEY_STRING);
54 const svn_auth_ssl_server_cert_info_t *cert_info =
55 apr_hash_get(parameters,
56 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,
57 APR_HASH_KEY_STRING);
58 apr_hash_t *creds_hash = NULL;
59 const char *config_dir;
60 svn_error_t *error = SVN_NO_ERROR;
62 *credentials = NULL;
63 *iter_baton = NULL;
65 /* Check if this is a permanently accepted certificate */
66 config_dir = apr_hash_get(parameters,
67 SVN_AUTH_PARAM_CONFIG_DIR,
68 APR_HASH_KEY_STRING);
69 error =
70 svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_SSL_SERVER_TRUST,
71 realmstring, config_dir, pool);
72 svn_error_clear(error);
73 if (! error && creds_hash)
75 svn_string_t *trusted_cert, *this_cert, *failstr;
76 apr_uint32_t last_failures = 0;
78 trusted_cert = apr_hash_get(creds_hash,
79 SVN_AUTH__AUTHFILE_ASCII_CERT_KEY,
80 APR_HASH_KEY_STRING);
81 this_cert = svn_string_create(cert_info->ascii_cert, pool);
82 failstr = apr_hash_get(creds_hash,
83 SVN_AUTH__AUTHFILE_FAILURES_KEY,
84 APR_HASH_KEY_STRING);
86 if (failstr)
88 char *endptr;
89 unsigned long tmp_ulong = strtoul(failstr->data, &endptr, 10);
91 if (*endptr == '\0')
92 last_failures = (apr_uint32_t) tmp_ulong;
95 /* If the cert is trusted and there are no new failures, we
96 * accept it by clearing all failures. */
97 if (trusted_cert &&
98 svn_string_compare(this_cert, trusted_cert) &&
99 (*failures & ~last_failures) == 0)
101 *failures = 0;
105 /* If all failures are cleared now, we return the creds */
106 if (! *failures)
108 svn_auth_cred_ssl_server_trust_t *creds =
109 apr_pcalloc(pool, sizeof(*creds));
110 creds->may_save = FALSE; /* No need to save it again... */
111 *credentials = creds;
114 return SVN_NO_ERROR;
118 static svn_error_t *
119 ssl_server_trust_file_save_credentials(svn_boolean_t *saved,
120 void *credentials,
121 void *provider_baton,
122 apr_hash_t *parameters,
123 const char *realmstring,
124 apr_pool_t *pool)
126 svn_auth_cred_ssl_server_trust_t *creds = credentials;
127 const svn_auth_ssl_server_cert_info_t *cert_info;
128 apr_hash_t *creds_hash = NULL;
129 const char *config_dir;
131 if (! creds->may_save)
132 return SVN_NO_ERROR;
134 config_dir = apr_hash_get(parameters,
135 SVN_AUTH_PARAM_CONFIG_DIR,
136 APR_HASH_KEY_STRING);
138 cert_info = apr_hash_get(parameters,
139 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,
140 APR_HASH_KEY_STRING);
142 creds_hash = apr_hash_make(pool);
143 apr_hash_set(creds_hash,
144 SVN_AUTH__AUTHFILE_ASCII_CERT_KEY,
145 APR_HASH_KEY_STRING,
146 svn_string_create(cert_info->ascii_cert, pool));
147 apr_hash_set(creds_hash,
148 SVN_AUTH__AUTHFILE_FAILURES_KEY,
149 APR_HASH_KEY_STRING,
150 svn_string_createf(pool, "%lu", (unsigned long)
151 creds->accepted_failures));
153 SVN_ERR(svn_config_write_auth_data(creds_hash,
154 SVN_AUTH_CRED_SSL_SERVER_TRUST,
155 realmstring,
156 config_dir,
157 pool));
158 *saved = TRUE;
159 return SVN_NO_ERROR;
163 static const svn_auth_provider_t ssl_server_trust_file_provider = {
164 SVN_AUTH_CRED_SSL_SERVER_TRUST,
165 &ssl_server_trust_file_first_credentials,
166 NULL,
167 &ssl_server_trust_file_save_credentials,
171 /*** Public API to SSL file providers. ***/
172 void
173 svn_auth_get_ssl_server_trust_file_provider
174 (svn_auth_provider_object_t **provider, apr_pool_t *pool)
176 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
178 po->vtable = &ssl_server_trust_file_provider;
179 *provider = po;
183 /*-----------------------------------------------------------------------*/
184 /* Prompt provider */
185 /*-----------------------------------------------------------------------*/
187 /* Baton type for prompting to verify server ssl creds.
188 There is no iteration baton type. */
189 typedef struct
191 svn_auth_ssl_server_trust_prompt_func_t prompt_func;
192 void *prompt_baton;
193 } ssl_server_trust_prompt_provider_baton_t;
196 static svn_error_t *
197 ssl_server_trust_prompt_first_cred(void **credentials_p,
198 void **iter_baton,
199 void *provider_baton,
200 apr_hash_t *parameters,
201 const char *realmstring,
202 apr_pool_t *pool)
204 ssl_server_trust_prompt_provider_baton_t *pb = provider_baton;
205 apr_uint32_t *failures = apr_hash_get(parameters,
206 SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
207 APR_HASH_KEY_STRING);
208 const char *no_auth_cache = apr_hash_get(parameters,
209 SVN_AUTH_PARAM_NO_AUTH_CACHE,
210 APR_HASH_KEY_STRING);
211 const svn_auth_ssl_server_cert_info_t *cert_info =
212 apr_hash_get(parameters,
213 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,
214 APR_HASH_KEY_STRING);
216 SVN_ERR(pb->prompt_func((svn_auth_cred_ssl_server_trust_t **)
217 credentials_p, pb->prompt_baton, realmstring,
218 *failures, cert_info, ! no_auth_cache &&
219 ! (*failures & SVN_AUTH_SSL_OTHER), pool));
221 *iter_baton = NULL;
222 return SVN_NO_ERROR;
226 static const svn_auth_provider_t ssl_server_trust_prompt_provider = {
227 SVN_AUTH_CRED_SSL_SERVER_TRUST,
228 ssl_server_trust_prompt_first_cred,
229 NULL,
230 NULL
234 /*** Public API to SSL prompting providers. ***/
235 void
236 svn_auth_get_ssl_server_trust_prompt_provider
237 (svn_auth_provider_object_t **provider,
238 svn_auth_ssl_server_trust_prompt_func_t prompt_func,
239 void *prompt_baton,
240 apr_pool_t *pool)
242 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
243 ssl_server_trust_prompt_provider_baton_t *pb =
244 apr_palloc(pool, sizeof(*pb));
245 pb->prompt_func = prompt_func;
246 pb->prompt_baton = prompt_baton;
247 po->vtable = &ssl_server_trust_prompt_provider;
248 po->provider_baton = pb;
249 *provider = po;
253 /*-----------------------------------------------------------------------*/
254 /* Windows SSL server trust provider, validates ssl certificate using */
255 /* CryptoApi. */
256 /*-----------------------------------------------------------------------*/
258 #if defined(WIN32) && !defined(__MINGW32__)
259 #include <wincrypt.h>
260 #include <apr_base64.h>
262 /* Retrieve ssl server CA failure overrides (if any) from CryptoApi. */
263 static svn_error_t *
264 windows_ssl_server_trust_first_credentials(void **credentials,
265 void **iter_baton,
266 void *provider_baton,
267 apr_hash_t *parameters,
268 const char *realmstring,
269 apr_pool_t *pool)
271 typedef PCCERT_CONTEXT (WINAPI *createcertcontext_fn_t)(
272 DWORD dwCertEncodingType,
273 const BYTE *pbCertEncoded,
274 DWORD cbCertEncoded);
276 typedef BOOL (WINAPI *getcertchain_fn_t)(
277 HCERTCHAINENGINE hChainEngine,
278 PCCERT_CONTEXT pCertContext,
279 LPFILETIME pTime,
280 HCERTSTORE hAdditionalStore,
281 PCERT_CHAIN_PARA pChainPara,
282 DWORD dwFlags,
283 LPVOID pvReserved,
284 PCCERT_CHAIN_CONTEXT* ppChainContext);
286 typedef VOID (WINAPI *freecertchain_fn_t)(
287 PCCERT_CHAIN_CONTEXT pChainContext);
289 typedef BOOL (WINAPI *freecertcontext_fn_t)(
290 PCCERT_CONTEXT pCertContext);
292 HINSTANCE cryptodll = NULL;
293 createcertcontext_fn_t createcertcontext;
294 getcertchain_fn_t getcertchain;
295 freecertchain_fn_t freecertchain;
296 freecertcontext_fn_t freecertcontext;
297 PCCERT_CONTEXT cert_context = NULL;
298 CERT_CHAIN_PARA chain_para;
299 PCCERT_CHAIN_CONTEXT chain_context = NULL;
300 svn_boolean_t ok = TRUE;
302 apr_uint32_t *failures = apr_hash_get(parameters,
303 SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
304 APR_HASH_KEY_STRING);
305 const svn_auth_ssl_server_cert_info_t *cert_info =
306 apr_hash_get(parameters,
307 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,
308 APR_HASH_KEY_STRING);
310 if (*failures & ~SVN_AUTH_SSL_UNKNOWNCA)
312 /* give up, go on to next provider; the only thing we can accept
313 is an unknown certificate authority. */
315 *credentials = NULL;
316 return SVN_NO_ERROR;
319 /* In case anyone wonders why we use LoadLibraryA here: This will
320 always work on Win9x/Me, whilst LoadLibraryW may not. */
321 cryptodll = LoadLibraryA("Crypt32.dll");
323 if (!cryptodll)
325 /* give up, go on to next provider. */
326 *credentials = NULL;
327 return SVN_NO_ERROR;
330 createcertcontext =
331 (createcertcontext_fn_t)GetProcAddress(cryptodll,
332 "CertCreateCertificateContext");
333 getcertchain =
334 (getcertchain_fn_t)GetProcAddress(cryptodll, "CertGetCertificateChain");
335 freecertchain =
336 (freecertchain_fn_t)GetProcAddress(cryptodll, "CertFreeCertificateChain");
337 freecertcontext =
338 (freecertcontext_fn_t)GetProcAddress(cryptodll,
339 "CertFreeCertificateContext");
341 if (!createcertcontext || !getcertchain || !freecertchain
342 || !freecertcontext)
343 ok = FALSE;
345 if (ok)
347 int cert_len;
348 char *binary_cert;
350 /* Use apr-util as CryptStringToBinaryA is available only on XP+. */
351 binary_cert = apr_palloc(pool,
352 apr_base64_decode_len(cert_info->ascii_cert));
353 cert_len = apr_base64_decode(binary_cert, cert_info->ascii_cert);
355 /* Parse the certificate into a context. */
356 cert_context = createcertcontext
357 (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, binary_cert, cert_len);
359 if (!cert_context)
360 ok = FALSE; /* Windows does not think the certificate is valid. */
363 if (ok)
365 /* Retrieve the certificate chain of the certificate
366 (a certificate without a valid root does not have a chain). */
367 memset(&chain_para, 0, sizeof(chain_para));
368 chain_para.cbSize = sizeof(chain_para);
370 if (getcertchain(NULL, cert_context, NULL, NULL, &chain_para,
371 CERT_CHAIN_CACHE_END_CERT,
372 NULL, &chain_context))
374 if (chain_context->rgpChain[0]->TrustStatus.dwErrorStatus
375 != CERT_TRUST_NO_ERROR)
377 /* The certificate is not 100% valid, just fall back to the
378 Subversion certificate handling. */
380 ok = FALSE;
383 else
384 ok = FALSE;
387 if (chain_context)
388 freecertchain(chain_context);
389 if (cert_context)
390 freecertcontext(cert_context);
391 if (cryptodll)
392 FreeLibrary(cryptodll);
394 if (!ok)
396 /* go on to next provider. */
397 *credentials = NULL;
398 return SVN_NO_ERROR;
400 else
402 svn_auth_cred_ssl_server_trust_t *creds =
403 apr_pcalloc(pool, sizeof(*creds));
404 creds->may_save = FALSE; /* No need to save it. */
405 *credentials = creds;
408 return SVN_NO_ERROR;
412 static const svn_auth_provider_t windows_server_trust_provider = {
413 SVN_AUTH_CRED_SSL_SERVER_TRUST,
414 windows_ssl_server_trust_first_credentials,
415 NULL,
416 NULL,
419 /* Public API */
420 void
421 svn_auth_get_windows_ssl_server_trust_provider
422 (svn_auth_provider_object_t **provider, apr_pool_t *pool)
424 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
426 po->vtable = &windows_server_trust_provider;
427 *provider = po;
431 #endif /* WIN32 */