]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_subr/ssl_client_cert_pw_providers.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_subr / ssl_client_cert_pw_providers.c
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 \f
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. */
53 typedef 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. */
67 svn_error_t *
68 svn_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. */
91 svn_error_t *
92 svn_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
107 svn_error_t *
108 svn_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
165 svn_error_t *
166 svn_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. */
331 static svn_error_t *
332 ssl_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. */
350 static svn_error_t *
351 ssl_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
368 static 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. ***/
377 void
378 svn_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 \f
398 /*-----------------------------------------------------------------------*/
399 /* Prompt provider                                                       */
400 /*-----------------------------------------------------------------------*/
401
402 /* Baton type for client passphrase prompting.
403    There is no iteration baton type. */
404 typedef 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. */
414 typedef 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
427 static svn_error_t *
428 ssl_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
454 static svn_error_t *
455 ssl_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
480 static 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
488 void 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 }