]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_subr/ssl_client_cert_pw_providers.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.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 /* Baton type for the ssl client cert passphrase provider. */
43 typedef struct ssl_client_cert_pw_file_provider_baton_t
44 {
45   svn_auth_plaintext_passphrase_prompt_func_t plaintext_passphrase_prompt_func;
46   void *prompt_baton;
47   /* We cache the user's answer to the plaintext prompt, keyed
48      by realm, in case we'll be called multiple times for the
49      same realm.  So: keys are 'const char *' realm strings, and
50      values are 'svn_boolean_t *'. */
51   apr_hash_t *plaintext_answers;
52 } ssl_client_cert_pw_file_provider_baton_t;
53
54 /* This implements the svn_auth__password_get_t interface.
55    Set **PASSPHRASE to the plaintext passphrase retrieved from CREDS;
56    ignore other parameters. */
57 svn_error_t *
58 svn_auth__ssl_client_cert_pw_get(svn_boolean_t *done,
59                                  const char **passphrase,
60                                  apr_hash_t *creds,
61                                  const char *realmstring,
62                                  const char *username,
63                                  apr_hash_t *parameters,
64                                  svn_boolean_t non_interactive,
65                                  apr_pool_t *pool)
66 {
67   svn_string_t *str;
68   str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_PASSPHRASE_KEY);
69   if (str && str->data)
70     {
71       *passphrase = str->data;
72       *done = TRUE;
73       return SVN_NO_ERROR;
74     }
75   *done = FALSE;
76   return SVN_NO_ERROR;
77 }
78
79 /* This implements the svn_auth__password_set_t interface.
80    Store PASSPHRASE in CREDS; ignore other parameters. */
81 svn_error_t *
82 svn_auth__ssl_client_cert_pw_set(svn_boolean_t *done,
83                                  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   svn_hash_sets(creds, SVN_CONFIG_AUTHN_PASSPHRASE_KEY,
92                 svn_string_create(passphrase, pool));
93   *done = TRUE;
94   return SVN_NO_ERROR;
95 }
96
97 svn_error_t *
98 svn_auth__ssl_client_cert_pw_cache_get(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 = svn_hash_gets(parameters,
108                                     SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS);
109   const char *server_group = svn_hash_gets(parameters,
110                                            SVN_AUTH_PARAM_SERVER_GROUP);
111   svn_boolean_t non_interactive = svn_hash_gets(parameters,
112                                                 SVN_AUTH_PARAM_NON_INTERACTIVE)
113       != NULL;
114   const char *password =
115     svn_config_get_server_setting(cfg, server_group,
116                                   SVN_CONFIG_OPTION_SSL_CLIENT_CERT_PASSWORD,
117                                   NULL);
118   if (! password)
119     {
120       svn_error_t *err;
121       apr_hash_t *creds_hash = NULL;
122       const char *config_dir = svn_hash_gets(parameters,
123                                              SVN_AUTH_PARAM_CONFIG_DIR);
124
125       /* Try to load passphrase from the auth/ cache. */
126       err = svn_config_read_auth_data(&creds_hash,
127                                       SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
128                                       realmstring, config_dir, pool);
129       svn_error_clear(err);
130       if (! err && creds_hash)
131         {
132           svn_boolean_t done;
133
134           SVN_ERR(passphrase_get(&done, &password, creds_hash, realmstring,
135                                  NULL, parameters, non_interactive, pool));
136           if (!done)
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
155 svn_error_t *
156 svn_auth__ssl_client_cert_pw_cache_set(svn_boolean_t *saved,
157                                        void *credentials,
158                                        void *provider_baton,
159                                        apr_hash_t *parameters,
160                                        const char *realmstring,
161                                        svn_auth__password_set_t passphrase_set,
162                                        const char *passtype,
163                                        apr_pool_t *pool)
164 {
165   svn_auth_cred_ssl_client_cert_pw_t *creds = credentials;
166   apr_hash_t *creds_hash = NULL;
167   const char *config_dir;
168   svn_error_t *err;
169   svn_boolean_t dont_store_passphrase =
170     svn_hash_gets(parameters, SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP)
171     != NULL;
172   svn_boolean_t non_interactive =
173       svn_hash_gets(parameters, SVN_AUTH_PARAM_NON_INTERACTIVE) != NULL;
174   svn_boolean_t no_auth_cache =
175     (! creds->may_save)
176     || (svn_hash_gets(parameters, SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL);
177
178   *saved = FALSE;
179
180   if (no_auth_cache)
181     return SVN_NO_ERROR;
182
183   config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR);
184   creds_hash = apr_hash_make(pool);
185
186   /* Don't store passphrase in any form if the user has told
187      us not to do so. */
188   if (! dont_store_passphrase)
189     {
190       svn_boolean_t may_save_passphrase = FALSE;
191
192       /* If the passphrase is going to be stored encrypted, go right
193          ahead and store it to disk. Else determine whether saving
194          in plaintext is OK. */
195       if (strcmp(passtype, SVN_AUTH__WINCRYPT_PASSWORD_TYPE) == 0
196           || strcmp(passtype, SVN_AUTH__KWALLET_PASSWORD_TYPE) == 0
197           || strcmp(passtype, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE) == 0
198           || strcmp(passtype, SVN_AUTH__KEYCHAIN_PASSWORD_TYPE) == 0)
199         {
200           may_save_passphrase = TRUE;
201         }
202       else
203         {
204 #ifdef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
205           may_save_passphrase = FALSE;
206 #else
207           const char *store_ssl_client_cert_pp_plaintext =
208             svn_hash_gets(parameters,
209                           SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT);
210           ssl_client_cert_pw_file_provider_baton_t *b =
211             (ssl_client_cert_pw_file_provider_baton_t *)provider_baton;
212
213           if (svn_cstring_casecmp(store_ssl_client_cert_pp_plaintext,
214                                   SVN_CONFIG_ASK) == 0)
215             {
216               if (non_interactive)
217                 {
218                   /* In non-interactive mode, the default behaviour is
219                      to not store the passphrase */
220                   may_save_passphrase = FALSE;
221                 }
222               else if (b->plaintext_passphrase_prompt_func)
223                 {
224                   /* We're interactive, and the client provided a
225                      prompt callback.  So we can ask the user.
226                      Check for a cached answer before prompting.
227
228                      This is a pointer-to-boolean, rather than just a
229                      boolean, because we must distinguish between
230                      "cached answer is no" and "no answer has been
231                      cached yet". */
232                   svn_boolean_t *cached_answer =
233                     svn_hash_gets(b->plaintext_answers, realmstring);
234
235                   if (cached_answer != NULL)
236                     {
237                       may_save_passphrase = *cached_answer;
238                     }
239                   else
240                     {
241                       apr_pool_t *cached_answer_pool;
242
243                       /* Nothing cached for this realm, prompt the user. */
244                       SVN_ERR((*b->plaintext_passphrase_prompt_func)(
245                                 &may_save_passphrase,
246                                 realmstring,
247                                 b->prompt_baton,
248                                 pool));
249
250                       /* Cache the user's answer in case we're called again
251                        * for the same realm.
252                        *
253                        * We allocate the answer cache in the hash table's pool
254                        * to make sure that is has the same life time as the
255                        * hash table itself. This means that the answer will
256                        * survive across RA sessions -- which is important,
257                        * because otherwise we'd prompt users once per RA session.
258                        */
259                       cached_answer_pool = apr_hash_pool_get(b->plaintext_answers);
260                       cached_answer = apr_palloc(cached_answer_pool,
261                                                  sizeof(*cached_answer));
262                       *cached_answer = may_save_passphrase;
263                       svn_hash_sets(b->plaintext_answers, realmstring,
264                                     cached_answer);
265                     }
266                 }
267               else
268                 {
269                   may_save_passphrase = FALSE;
270                 }
271             }
272           else if (svn_cstring_casecmp(store_ssl_client_cert_pp_plaintext,
273                                        SVN_CONFIG_FALSE) == 0)
274             {
275               may_save_passphrase = FALSE;
276             }
277           else if (svn_cstring_casecmp(store_ssl_client_cert_pp_plaintext,
278                                        SVN_CONFIG_TRUE) == 0)
279             {
280               may_save_passphrase = TRUE;
281             }
282           else
283             {
284               return svn_error_createf
285                 (SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE, NULL,
286                  _("Config error: invalid value '%s' for option '%s'"),
287                 store_ssl_client_cert_pp_plaintext,
288                 SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT);
289             }
290 #endif
291         }
292
293       if (may_save_passphrase)
294         {
295           SVN_ERR(passphrase_set(saved, creds_hash, realmstring,
296                                  NULL, creds->password, parameters,
297                                  non_interactive, pool));
298
299           if (*saved && passtype)
300             {
301               svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_PASSTYPE_KEY,
302                             svn_string_create(passtype, pool));
303             }
304
305           /* Save credentials to disk. */
306           err = svn_config_write_auth_data(creds_hash,
307                                            SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
308                                            realmstring, config_dir, pool);
309           svn_error_clear(err);
310           *saved = ! err;
311         }
312     }
313
314   return SVN_NO_ERROR;
315 }
316
317
318 /* This implements the svn_auth_provider_t.first_credentials API.
319    It gets cached (unencrypted) credentials from the ssl client cert
320    password provider's cache. */
321 static svn_error_t *
322 ssl_client_cert_pw_file_first_credentials(void **credentials_p,
323                                           void **iter_baton,
324                                           void *provider_baton,
325                                           apr_hash_t *parameters,
326                                           const char *realmstring,
327                                           apr_pool_t *pool)
328 {
329   return svn_auth__ssl_client_cert_pw_cache_get(credentials_p, iter_baton,
330                                                 provider_baton, parameters,
331                                                 realmstring,
332                                                 svn_auth__ssl_client_cert_pw_get,
333                                                 SVN_AUTH__SIMPLE_PASSWORD_TYPE,
334                                                 pool);
335 }
336
337
338 /* This implements the svn_auth_provider_t.save_credentials API.
339    It saves the credentials unencrypted. */
340 static svn_error_t *
341 ssl_client_cert_pw_file_save_credentials(svn_boolean_t *saved,
342                                          void *credentials,
343                                          void *provider_baton,
344                                          apr_hash_t *parameters,
345                                          const char *realmstring,
346                                          apr_pool_t *pool)
347 {
348   return svn_auth__ssl_client_cert_pw_cache_set(saved, credentials,
349                                                 provider_baton,
350                                                 parameters,
351                                                 realmstring,
352                                                 svn_auth__ssl_client_cert_pw_set,
353                                                 SVN_AUTH__SIMPLE_PASSWORD_TYPE,
354                                                 pool);
355 }
356
357
358 static const svn_auth_provider_t ssl_client_cert_pw_file_provider = {
359   SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
360   ssl_client_cert_pw_file_first_credentials,
361   NULL,
362   ssl_client_cert_pw_file_save_credentials
363 };
364
365
366 /*** Public API to SSL file providers. ***/
367 void
368 svn_auth_get_ssl_client_cert_pw_file_provider2
369   (svn_auth_provider_object_t **provider,
370    svn_auth_plaintext_passphrase_prompt_func_t plaintext_passphrase_prompt_func,
371    void *prompt_baton,
372    apr_pool_t *pool)
373 {
374   svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
375   ssl_client_cert_pw_file_provider_baton_t *pb = apr_pcalloc(pool,
376                                                              sizeof(*pb));
377
378   pb->plaintext_passphrase_prompt_func = plaintext_passphrase_prompt_func;
379   pb->prompt_baton = prompt_baton;
380   pb->plaintext_answers = apr_hash_make(pool);
381
382   po->vtable = &ssl_client_cert_pw_file_provider;
383   po->provider_baton = pb;
384   *provider = po;
385 }
386
387 \f
388 /*-----------------------------------------------------------------------*/
389 /* Prompt provider                                                       */
390 /*-----------------------------------------------------------------------*/
391
392 /* Baton type for client passphrase prompting.
393    There is no iteration baton type. */
394 typedef struct ssl_client_cert_pw_prompt_provider_baton_t
395 {
396   svn_auth_ssl_client_cert_pw_prompt_func_t prompt_func;
397   void *prompt_baton;
398
399   /* how many times to re-prompt after the first one fails */
400   int retry_limit;
401 } ssl_client_cert_pw_prompt_provider_baton_t;
402
403 /* Iteration baton. */
404 typedef struct ssl_client_cert_pw_prompt_iter_baton_t
405 {
406   /* The original provider baton */
407   ssl_client_cert_pw_prompt_provider_baton_t *pb;
408
409   /* The original realmstring */
410   const char *realmstring;
411
412   /* how many times we've reprompted */
413   int retries;
414 } ssl_client_cert_pw_prompt_iter_baton_t;
415
416
417 static svn_error_t *
418 ssl_client_cert_pw_prompt_first_cred(void **credentials_p,
419                                      void **iter_baton,
420                                      void *provider_baton,
421                                      apr_hash_t *parameters,
422                                      const char *realmstring,
423                                      apr_pool_t *pool)
424 {
425   ssl_client_cert_pw_prompt_provider_baton_t *pb = provider_baton;
426   ssl_client_cert_pw_prompt_iter_baton_t *ib =
427     apr_pcalloc(pool, sizeof(*ib));
428   const char *no_auth_cache = svn_hash_gets(parameters,
429                                             SVN_AUTH_PARAM_NO_AUTH_CACHE);
430
431   SVN_ERR(pb->prompt_func((svn_auth_cred_ssl_client_cert_pw_t **)
432                           credentials_p, pb->prompt_baton, realmstring,
433                           ! no_auth_cache, pool));
434
435   ib->pb = pb;
436   ib->realmstring = apr_pstrdup(pool, realmstring);
437   ib->retries = 0;
438   *iter_baton = ib;
439
440   return SVN_NO_ERROR;
441 }
442
443
444 static svn_error_t *
445 ssl_client_cert_pw_prompt_next_cred(void **credentials_p,
446                                     void *iter_baton,
447                                     void *provider_baton,
448                                     apr_hash_t *parameters,
449                                     const char *realmstring,
450                                     apr_pool_t *pool)
451 {
452   ssl_client_cert_pw_prompt_iter_baton_t *ib = iter_baton;
453   const char *no_auth_cache = svn_hash_gets(parameters,
454                                             SVN_AUTH_PARAM_NO_AUTH_CACHE);
455
456   if ((ib->pb->retry_limit >= 0) && (ib->retries >= ib->pb->retry_limit))
457     {
458       /* give up, go on to next provider. */
459       *credentials_p = NULL;
460       return SVN_NO_ERROR;
461     }
462   ib->retries++;
463
464   return ib->pb->prompt_func((svn_auth_cred_ssl_client_cert_pw_t **)
465                              credentials_p, ib->pb->prompt_baton,
466                              ib->realmstring, ! no_auth_cache, pool);
467 }
468
469
470 static const svn_auth_provider_t client_cert_pw_prompt_provider = {
471   SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
472   ssl_client_cert_pw_prompt_first_cred,
473   ssl_client_cert_pw_prompt_next_cred,
474   NULL
475 };
476
477
478 void svn_auth_get_ssl_client_cert_pw_prompt_provider
479   (svn_auth_provider_object_t **provider,
480    svn_auth_ssl_client_cert_pw_prompt_func_t prompt_func,
481    void *prompt_baton,
482    int retry_limit,
483    apr_pool_t *pool)
484 {
485   svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
486   ssl_client_cert_pw_prompt_provider_baton_t *pb =
487     apr_palloc(pool, sizeof(*pb));
488
489   pb->prompt_func = prompt_func;
490   pb->prompt_baton = prompt_baton;
491   pb->retry_limit = retry_limit;
492
493   po->vtable = &client_cert_pw_prompt_provider;
494   po->provider_baton = pb;
495   *provider = po;
496 }