source: valtobtest/subversion-1.6.2/subversion/libsvn_subr/ssl_client_cert_pw_providers.c @ 3

Last change on this file since 3 was 3, checked in by valtob, 15 years ago

subversion source 1.6.2 as test

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