1/* 2 * ssl_client_cert_pw_providers.c: providers for 3 * SVN_AUTH_CRED_SSL_CLIENT_CERT_PW 4 * 5 * ==================================================================== 6 * Licensed to the Apache Software Foundation (ASF) under one 7 * or more contributor license agreements. See the NOTICE file 8 * distributed with this work for additional information 9 * regarding copyright ownership. The ASF licenses this file 10 * to you under the Apache License, Version 2.0 (the 11 * "License"); you may not use this file except in compliance 12 * with the License. You may obtain a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, 17 * software distributed under the License is distributed on an 18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 * KIND, either express or implied. See the License for the 20 * specific language governing permissions and limitations 21 * under the License. 22 * ==================================================================== 23 */ 24 25 26#include <apr_pools.h> 27 28#include "svn_hash.h" 29#include "svn_auth.h" 30#include "svn_error.h" 31#include "svn_config.h" 32#include "svn_string.h" 33 34#include "private/svn_auth_private.h" 35 36#include "svn_private_config.h" 37 38/*-----------------------------------------------------------------------*/ 39/* File provider */ 40/*-----------------------------------------------------------------------*/ 41 42/* The keys that will be stored on disk. These serve the same role as 43 * similar constants in other providers. 44 * 45 * AUTHN_PASSTYPE_KEY just records the passphrase type next to the 46 * passphrase, so that anyone who is manually editing their authn 47 * files can know which provider owns the password. 48 */ 49#define AUTHN_PASSPHRASE_KEY "passphrase" 50#define AUTHN_PASSTYPE_KEY "passtype" 51 52/* Baton type for the ssl client cert passphrase provider. */ 53typedef struct ssl_client_cert_pw_file_provider_baton_t 54{ 55 svn_auth_plaintext_passphrase_prompt_func_t plaintext_passphrase_prompt_func; 56 void *prompt_baton; 57 /* We cache the user's answer to the plaintext prompt, keyed 58 by realm, in case we'll be called multiple times for the 59 same realm. So: keys are 'const char *' realm strings, and 60 values are 'svn_boolean_t *'. */ 61 apr_hash_t *plaintext_answers; 62} ssl_client_cert_pw_file_provider_baton_t; 63 64/* This implements the svn_auth__password_get_t interface. 65 Set **PASSPHRASE to the plaintext passphrase retrieved from CREDS; 66 ignore other parameters. */ 67svn_error_t * 68svn_auth__ssl_client_cert_pw_get(svn_boolean_t *done, 69 const char **passphrase, 70 apr_hash_t *creds, 71 const char *realmstring, 72 const char *username, 73 apr_hash_t *parameters, 74 svn_boolean_t non_interactive, 75 apr_pool_t *pool) 76{ 77 svn_string_t *str; 78 str = svn_hash_gets(creds, AUTHN_PASSPHRASE_KEY); 79 if (str && str->data) 80 { 81 *passphrase = str->data; 82 *done = TRUE; 83 return SVN_NO_ERROR; 84 } 85 *done = FALSE; 86 return SVN_NO_ERROR; 87} 88 89/* This implements the svn_auth__password_set_t interface. 90 Store PASSPHRASE in CREDS; ignore other parameters. */ 91svn_error_t * 92svn_auth__ssl_client_cert_pw_set(svn_boolean_t *done, 93 apr_hash_t *creds, 94 const char *realmstring, 95 const char *username, 96 const char *passphrase, 97 apr_hash_t *parameters, 98 svn_boolean_t non_interactive, 99 apr_pool_t *pool) 100{ 101 svn_hash_sets(creds, AUTHN_PASSPHRASE_KEY, 102 svn_string_create(passphrase, pool)); 103 *done = TRUE; 104 return SVN_NO_ERROR; 105} 106 107svn_error_t * 108svn_auth__ssl_client_cert_pw_cache_get(void **credentials_p, 109 void **iter_baton, 110 void *provider_baton, 111 apr_hash_t *parameters, 112 const char *realmstring, 113 svn_auth__password_get_t passphrase_get, 114 const char *passtype, 115 apr_pool_t *pool) 116{ 117 svn_config_t *cfg = svn_hash_gets(parameters, 118 SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS); 119 const char *server_group = svn_hash_gets(parameters, 120 SVN_AUTH_PARAM_SERVER_GROUP); 121 svn_boolean_t non_interactive = svn_hash_gets(parameters, 122 SVN_AUTH_PARAM_NON_INTERACTIVE) 123 != NULL; 124 const char *password = 125 svn_config_get_server_setting(cfg, server_group, 126 SVN_CONFIG_OPTION_SSL_CLIENT_CERT_PASSWORD, 127 NULL); 128 if (! password) 129 { 130 svn_error_t *err; 131 apr_hash_t *creds_hash = NULL; 132 const char *config_dir = svn_hash_gets(parameters, 133 SVN_AUTH_PARAM_CONFIG_DIR); 134 135 /* Try to load passphrase from the auth/ cache. */ 136 err = svn_config_read_auth_data(&creds_hash, 137 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 138 realmstring, config_dir, pool); 139 svn_error_clear(err); 140 if (! err && creds_hash) 141 { 142 svn_boolean_t done; 143 144 SVN_ERR(passphrase_get(&done, &password, creds_hash, realmstring, 145 NULL, parameters, non_interactive, pool)); 146 if (!done) 147 password = NULL; 148 } 149 } 150 151 if (password) 152 { 153 svn_auth_cred_ssl_client_cert_pw_t *cred 154 = apr_palloc(pool, sizeof(*cred)); 155 cred->password = password; 156 cred->may_save = FALSE; 157 *credentials_p = cred; 158 } 159 else *credentials_p = NULL; 160 *iter_baton = NULL; 161 return SVN_NO_ERROR; 162} 163 164 165svn_error_t * 166svn_auth__ssl_client_cert_pw_cache_set(svn_boolean_t *saved, 167 void *credentials, 168 void *provider_baton, 169 apr_hash_t *parameters, 170 const char *realmstring, 171 svn_auth__password_set_t passphrase_set, 172 const char *passtype, 173 apr_pool_t *pool) 174{ 175 svn_auth_cred_ssl_client_cert_pw_t *creds = credentials; 176 apr_hash_t *creds_hash = NULL; 177 const char *config_dir; 178 svn_error_t *err; 179 svn_boolean_t dont_store_passphrase = 180 svn_hash_gets(parameters, SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP) 181 != NULL; 182 svn_boolean_t non_interactive = 183 svn_hash_gets(parameters, SVN_AUTH_PARAM_NON_INTERACTIVE) != NULL; 184 svn_boolean_t no_auth_cache = 185 (! creds->may_save) 186 || (svn_hash_gets(parameters, SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL); 187 188 *saved = FALSE; 189 190 if (no_auth_cache) 191 return SVN_NO_ERROR; 192 193 config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR); 194 creds_hash = apr_hash_make(pool); 195 196 /* Don't store passphrase in any form if the user has told 197 us not to do so. */ 198 if (! dont_store_passphrase) 199 { 200 svn_boolean_t may_save_passphrase = FALSE; 201 202 /* If the passphrase is going to be stored encrypted, go right 203 ahead and store it to disk. Else determine whether saving 204 in plaintext is OK. */ 205 if (strcmp(passtype, SVN_AUTH__WINCRYPT_PASSWORD_TYPE) == 0 206 || strcmp(passtype, SVN_AUTH__KWALLET_PASSWORD_TYPE) == 0 207 || strcmp(passtype, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE) == 0 208 || strcmp(passtype, SVN_AUTH__KEYCHAIN_PASSWORD_TYPE) == 0) 209 { 210 may_save_passphrase = TRUE; 211 } 212 else 213 { 214#ifdef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE 215 may_save_passphrase = FALSE; 216#else 217 const char *store_ssl_client_cert_pp_plaintext = 218 svn_hash_gets(parameters, 219 SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT); 220 ssl_client_cert_pw_file_provider_baton_t *b = 221 (ssl_client_cert_pw_file_provider_baton_t *)provider_baton; 222 223 if (svn_cstring_casecmp(store_ssl_client_cert_pp_plaintext, 224 SVN_CONFIG_ASK) == 0) 225 { 226 if (non_interactive) 227 { 228 /* In non-interactive mode, the default behaviour is 229 to not store the passphrase */ 230 may_save_passphrase = FALSE; 231 } 232 else if (b->plaintext_passphrase_prompt_func) 233 { 234 /* We're interactive, and the client provided a 235 prompt callback. So we can ask the user. 236 Check for a cached answer before prompting. 237 238 This is a pointer-to-boolean, rather than just a 239 boolean, because we must distinguish between 240 "cached answer is no" and "no answer has been 241 cached yet". */ 242 svn_boolean_t *cached_answer = 243 svn_hash_gets(b->plaintext_answers, realmstring); 244 245 if (cached_answer != NULL) 246 { 247 may_save_passphrase = *cached_answer; 248 } 249 else 250 { 251 apr_pool_t *cached_answer_pool; 252 253 /* Nothing cached for this realm, prompt the user. */ 254 SVN_ERR((*b->plaintext_passphrase_prompt_func)( 255 &may_save_passphrase, 256 realmstring, 257 b->prompt_baton, 258 pool)); 259 260 /* Cache the user's answer in case we're called again 261 * for the same realm. 262 * 263 * We allocate the answer cache in the hash table's pool 264 * to make sure that is has the same life time as the 265 * hash table itself. This means that the answer will 266 * survive across RA sessions -- which is important, 267 * because otherwise we'd prompt users once per RA session. 268 */ 269 cached_answer_pool = apr_hash_pool_get(b->plaintext_answers); 270 cached_answer = apr_palloc(cached_answer_pool, 271 sizeof(*cached_answer)); 272 *cached_answer = may_save_passphrase; 273 svn_hash_sets(b->plaintext_answers, realmstring, 274 cached_answer); 275 } 276 } 277 else 278 { 279 may_save_passphrase = FALSE; 280 } 281 } 282 else if (svn_cstring_casecmp(store_ssl_client_cert_pp_plaintext, 283 SVN_CONFIG_FALSE) == 0) 284 { 285 may_save_passphrase = FALSE; 286 } 287 else if (svn_cstring_casecmp(store_ssl_client_cert_pp_plaintext, 288 SVN_CONFIG_TRUE) == 0) 289 { 290 may_save_passphrase = TRUE; 291 } 292 else 293 { 294 return svn_error_createf 295 (SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE, NULL, 296 _("Config error: invalid value '%s' for option '%s'"), 297 store_ssl_client_cert_pp_plaintext, 298 SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT); 299 } 300#endif 301 } 302 303 if (may_save_passphrase) 304 { 305 SVN_ERR(passphrase_set(saved, creds_hash, realmstring, 306 NULL, creds->password, parameters, 307 non_interactive, pool)); 308 309 if (*saved && passtype) 310 { 311 svn_hash_sets(creds_hash, AUTHN_PASSTYPE_KEY, 312 svn_string_create(passtype, pool)); 313 } 314 315 /* Save credentials to disk. */ 316 err = svn_config_write_auth_data(creds_hash, 317 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 318 realmstring, config_dir, pool); 319 svn_error_clear(err); 320 *saved = ! err; 321 } 322 } 323 324 return SVN_NO_ERROR; 325} 326 327 328/* This implements the svn_auth_provider_t.first_credentials API. 329 It gets cached (unencrypted) credentials from the ssl client cert 330 password provider's cache. */ 331static svn_error_t * 332ssl_client_cert_pw_file_first_credentials(void **credentials_p, 333 void **iter_baton, 334 void *provider_baton, 335 apr_hash_t *parameters, 336 const char *realmstring, 337 apr_pool_t *pool) 338{ 339 return svn_auth__ssl_client_cert_pw_cache_get(credentials_p, iter_baton, 340 provider_baton, parameters, 341 realmstring, 342 svn_auth__ssl_client_cert_pw_get, 343 SVN_AUTH__SIMPLE_PASSWORD_TYPE, 344 pool); 345} 346 347 348/* This implements the svn_auth_provider_t.save_credentials API. 349 It saves the credentials unencrypted. */ 350static svn_error_t * 351ssl_client_cert_pw_file_save_credentials(svn_boolean_t *saved, 352 void *credentials, 353 void *provider_baton, 354 apr_hash_t *parameters, 355 const char *realmstring, 356 apr_pool_t *pool) 357{ 358 return svn_auth__ssl_client_cert_pw_cache_set(saved, credentials, 359 provider_baton, 360 parameters, 361 realmstring, 362 svn_auth__ssl_client_cert_pw_set, 363 SVN_AUTH__SIMPLE_PASSWORD_TYPE, 364 pool); 365} 366 367 368static const svn_auth_provider_t ssl_client_cert_pw_file_provider = { 369 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 370 ssl_client_cert_pw_file_first_credentials, 371 NULL, 372 ssl_client_cert_pw_file_save_credentials 373}; 374 375 376/*** Public API to SSL file providers. ***/ 377void 378svn_auth_get_ssl_client_cert_pw_file_provider2 379 (svn_auth_provider_object_t **provider, 380 svn_auth_plaintext_passphrase_prompt_func_t plaintext_passphrase_prompt_func, 381 void *prompt_baton, 382 apr_pool_t *pool) 383{ 384 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 385 ssl_client_cert_pw_file_provider_baton_t *pb = apr_pcalloc(pool, 386 sizeof(*pb)); 387 388 pb->plaintext_passphrase_prompt_func = plaintext_passphrase_prompt_func; 389 pb->prompt_baton = prompt_baton; 390 pb->plaintext_answers = apr_hash_make(pool); 391 392 po->vtable = &ssl_client_cert_pw_file_provider; 393 po->provider_baton = pb; 394 *provider = po; 395} 396 397 398/*-----------------------------------------------------------------------*/ 399/* Prompt provider */ 400/*-----------------------------------------------------------------------*/ 401 402/* Baton type for client passphrase prompting. 403 There is no iteration baton type. */ 404typedef struct ssl_client_cert_pw_prompt_provider_baton_t 405{ 406 svn_auth_ssl_client_cert_pw_prompt_func_t prompt_func; 407 void *prompt_baton; 408 409 /* how many times to re-prompt after the first one fails */ 410 int retry_limit; 411} ssl_client_cert_pw_prompt_provider_baton_t; 412 413/* Iteration baton. */ 414typedef struct ssl_client_cert_pw_prompt_iter_baton_t 415{ 416 /* The original provider baton */ 417 ssl_client_cert_pw_prompt_provider_baton_t *pb; 418 419 /* The original realmstring */ 420 const char *realmstring; 421 422 /* how many times we've reprompted */ 423 int retries; 424} ssl_client_cert_pw_prompt_iter_baton_t; 425 426 427static svn_error_t * 428ssl_client_cert_pw_prompt_first_cred(void **credentials_p, 429 void **iter_baton, 430 void *provider_baton, 431 apr_hash_t *parameters, 432 const char *realmstring, 433 apr_pool_t *pool) 434{ 435 ssl_client_cert_pw_prompt_provider_baton_t *pb = provider_baton; 436 ssl_client_cert_pw_prompt_iter_baton_t *ib = 437 apr_pcalloc(pool, sizeof(*ib)); 438 const char *no_auth_cache = svn_hash_gets(parameters, 439 SVN_AUTH_PARAM_NO_AUTH_CACHE); 440 441 SVN_ERR(pb->prompt_func((svn_auth_cred_ssl_client_cert_pw_t **) 442 credentials_p, pb->prompt_baton, realmstring, 443 ! no_auth_cache, pool)); 444 445 ib->pb = pb; 446 ib->realmstring = apr_pstrdup(pool, realmstring); 447 ib->retries = 0; 448 *iter_baton = ib; 449 450 return SVN_NO_ERROR; 451} 452 453 454static svn_error_t * 455ssl_client_cert_pw_prompt_next_cred(void **credentials_p, 456 void *iter_baton, 457 void *provider_baton, 458 apr_hash_t *parameters, 459 const char *realmstring, 460 apr_pool_t *pool) 461{ 462 ssl_client_cert_pw_prompt_iter_baton_t *ib = iter_baton; 463 const char *no_auth_cache = svn_hash_gets(parameters, 464 SVN_AUTH_PARAM_NO_AUTH_CACHE); 465 466 if ((ib->pb->retry_limit >= 0) && (ib->retries >= ib->pb->retry_limit)) 467 { 468 /* give up, go on to next provider. */ 469 *credentials_p = NULL; 470 return SVN_NO_ERROR; 471 } 472 ib->retries++; 473 474 return ib->pb->prompt_func((svn_auth_cred_ssl_client_cert_pw_t **) 475 credentials_p, ib->pb->prompt_baton, 476 ib->realmstring, ! no_auth_cache, pool); 477} 478 479 480static const svn_auth_provider_t client_cert_pw_prompt_provider = { 481 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 482 ssl_client_cert_pw_prompt_first_cred, 483 ssl_client_cert_pw_prompt_next_cred, 484 NULL 485}; 486 487 488void svn_auth_get_ssl_client_cert_pw_prompt_provider 489 (svn_auth_provider_object_t **provider, 490 svn_auth_ssl_client_cert_pw_prompt_func_t prompt_func, 491 void *prompt_baton, 492 int retry_limit, 493 apr_pool_t *pool) 494{ 495 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); 496 ssl_client_cert_pw_prompt_provider_baton_t *pb = 497 apr_palloc(pool, sizeof(*pb)); 498 499 pb->prompt_func = prompt_func; 500 pb->prompt_baton = prompt_baton; 501 pb->retry_limit = retry_limit; 502 503 po->vtable = &client_cert_pw_prompt_provider; 504 po->provider_baton = pb; 505 *provider = po; 506} 507