simple_providers.c revision 299742
1/*
2 * simple_providers.c: providers for SVN_AUTH_CRED_SIMPLE
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24/* ==================================================================== */
25
26
27
28/*** Includes. ***/
29
30#include <apr_pools.h>
31#include "svn_auth.h"
32#include "svn_dirent_uri.h"
33#include "svn_hash.h"
34#include "svn_pools.h"
35#include "svn_error.h"
36#include "svn_utf.h"
37#include "svn_config.h"
38#include "svn_user.h"
39
40#include "private/svn_auth_private.h"
41
42#include "svn_private_config.h"
43
44#include "auth.h"
45
46/*-----------------------------------------------------------------------*/
47/* File provider                                                         */
48/*-----------------------------------------------------------------------*/
49
50/* Baton type for the simple provider. */
51typedef struct simple_provider_baton_t
52{
53  svn_auth_plaintext_prompt_func_t plaintext_prompt_func;
54  void *prompt_baton;
55  /* We cache the user's answer to the plaintext prompt, keyed
56   * by realm, in case we'll be called multiple times for the
57   * same realm. */
58  apr_hash_t *plaintext_answers;
59} simple_provider_baton_t;
60
61
62/* Implementation of svn_auth__password_get_t that retrieves
63   the plaintext password from CREDS. */
64svn_error_t *
65svn_auth__simple_password_get(svn_boolean_t *done,
66                              const char **password,
67                              apr_hash_t *creds,
68                              const char *realmstring,
69                              const char *username,
70                              apr_hash_t *parameters,
71                              svn_boolean_t non_interactive,
72                              apr_pool_t *pool)
73{
74  svn_string_t *str;
75
76  *done = FALSE;
77
78  str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_USERNAME_KEY);
79  if (str && username && strcmp(str->data, username) == 0)
80    {
81      str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_PASSWORD_KEY);
82      if (str && str->data)
83        {
84          *password = str->data;
85          *done = TRUE;
86        }
87    }
88
89  return SVN_NO_ERROR;
90}
91
92/* Implementation of svn_auth__password_set_t that stores
93   the plaintext password in CREDS. */
94svn_error_t *
95svn_auth__simple_password_set(svn_boolean_t *done,
96                              apr_hash_t *creds,
97                              const char *realmstring,
98                              const char *username,
99                              const char *password,
100                              apr_hash_t *parameters,
101                              svn_boolean_t non_interactive,
102                              apr_pool_t *pool)
103{
104  svn_hash_sets(creds, SVN_CONFIG_AUTHN_PASSWORD_KEY,
105                svn_string_create(password, pool));
106  *done = TRUE;
107
108  return SVN_NO_ERROR;
109}
110
111/* Set **USERNAME to the username retrieved from CREDS; ignore
112   other parameters. *USERNAME will have the same lifetime as CREDS. */
113static svn_boolean_t
114simple_username_get(const char **username,
115                    apr_hash_t *creds,
116                    const char *realmstring,
117                    svn_boolean_t non_interactive)
118{
119  svn_string_t *str;
120  str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_USERNAME_KEY);
121  if (str && str->data)
122    {
123      *username = str->data;
124      return TRUE;
125    }
126  return FALSE;
127}
128
129
130svn_error_t *
131svn_auth__simple_creds_cache_get(void **credentials,
132                                 void **iter_baton,
133                                 void *provider_baton,
134                                 apr_hash_t *parameters,
135                                 const char *realmstring,
136                                 svn_auth__password_get_t password_get,
137                                 const char *passtype,
138                                 apr_pool_t *pool)
139{
140  const char *config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR);
141  svn_config_t *cfg = svn_hash_gets(parameters,
142                                    SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS);
143  const char *server_group = svn_hash_gets(parameters,
144                                           SVN_AUTH_PARAM_SERVER_GROUP);
145  const char *username = svn_hash_gets(parameters,
146                                       SVN_AUTH_PARAM_DEFAULT_USERNAME);
147  const char *password = svn_hash_gets(parameters,
148                                       SVN_AUTH_PARAM_DEFAULT_PASSWORD);
149  svn_boolean_t non_interactive = svn_hash_gets(parameters,
150                                                SVN_AUTH_PARAM_NON_INTERACTIVE)
151      != NULL;
152  const char *default_username = NULL; /* Default username from cache. */
153  const char *default_password = NULL; /* Default password from cache. */
154
155  /* This checks if we should save the CREDS, iff saving the credentials is
156     allowed by the run-time configuration. */
157  svn_boolean_t need_to_save = FALSE;
158  apr_hash_t *creds_hash = NULL;
159  svn_error_t *err;
160  svn_string_t *str;
161
162  /* Try to load credentials from a file on disk, based on the
163     realmstring.  Don't throw an error, though: if something went
164     wrong reading the file, no big deal.  What really matters is that
165     we failed to get the creds, so allow the auth system to try the
166     next provider. */
167  err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_SIMPLE,
168                                  realmstring, config_dir, pool);
169  if (err)
170    {
171      svn_error_clear(err);
172      err = NULL;
173    }
174  else if (creds_hash)
175    {
176      /* We have something in the auth cache for this realm. */
177      svn_boolean_t have_passtype = FALSE;
178
179      /* The password type in the auth data must match the
180         mangler's type, otherwise the password must be
181         interpreted by another provider. */
182      str = svn_hash_gets(creds_hash, SVN_CONFIG_AUTHN_PASSTYPE_KEY);
183      if (str && str->data)
184        if (passtype && (0 == strcmp(str->data, passtype)))
185          have_passtype = TRUE;
186
187      /* See if we need to save this username if it is not present in
188         auth cache. */
189      if (username)
190        {
191          if (!simple_username_get(&default_username, creds_hash, realmstring,
192                                   non_interactive))
193            {
194              need_to_save = TRUE;
195            }
196          else
197            {
198              if (strcmp(default_username, username) != 0)
199                need_to_save = TRUE;
200            }
201        }
202
203      /* See if we need to save this password if it is not present in
204         auth cache. */
205      if (password)
206        {
207          if (have_passtype)
208            {
209              svn_boolean_t done;
210
211              SVN_ERR(password_get(&done, &default_password, creds_hash,
212                                   realmstring, username, parameters,
213                                   non_interactive, pool));
214              if (!done)
215                {
216                  need_to_save = TRUE;
217                }
218              else
219                {
220                  if (strcmp(default_password, password) != 0)
221                    need_to_save = TRUE;
222                }
223            }
224        }
225
226      /* If we don't have a username and a password yet, we try the
227         auth cache */
228      if (! (username && password))
229        {
230          if (! username)
231            if (!simple_username_get(&username, creds_hash, realmstring,
232                                     non_interactive))
233              username = NULL;
234
235          if (username && ! password)
236            {
237              if (! have_passtype)
238                password = NULL;
239              else
240                {
241                  svn_boolean_t done;
242
243                  SVN_ERR(password_get(&done, &password, creds_hash,
244                                       realmstring, username, parameters,
245                                       non_interactive, pool));
246                  if (!done)
247                    password = NULL;
248
249                  /* If the auth data didn't contain a password type,
250                     force a write to upgrade the format of the auth
251                     data file. */
252                  if (password && ! have_passtype)
253                    need_to_save = TRUE;
254                }
255            }
256        }
257    }
258  else
259    {
260      /* Nothing was present in the auth cache, so indicate that these
261         credentials should be saved. */
262      need_to_save = TRUE;
263    }
264
265  /* If we don't have a username yet, check the 'servers' file */
266  if (! username)
267    {
268      username = svn_config_get_server_setting(cfg, server_group,
269                                               SVN_CONFIG_OPTION_USERNAME,
270                                               NULL);
271    }
272
273  /* Ask the OS for the username if we have a password but no
274     username. */
275  if (password && ! username)
276    username = svn_user_get_name(pool);
277
278  if (username && password)
279    {
280      svn_auth_cred_simple_t *creds = apr_pcalloc(pool, sizeof(*creds));
281      creds->username = username;
282      creds->password = password;
283      creds->may_save = need_to_save;
284      *credentials = creds;
285    }
286  else
287    *credentials = NULL;
288
289  *iter_baton = NULL;
290
291  return SVN_NO_ERROR;
292}
293
294
295svn_error_t *
296svn_auth__simple_creds_cache_set(svn_boolean_t *saved,
297                                 void *credentials,
298                                 void *provider_baton,
299                                 apr_hash_t *parameters,
300                                 const char *realmstring,
301                                 svn_auth__password_set_t password_set,
302                                 const char *passtype,
303                                 apr_pool_t *pool)
304{
305  svn_auth_cred_simple_t *creds = credentials;
306  apr_hash_t *creds_hash = NULL;
307  const char *config_dir;
308  svn_error_t *err;
309  svn_boolean_t dont_store_passwords =
310    svn_hash_gets(parameters, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL;
311  svn_boolean_t non_interactive = svn_hash_gets(parameters,
312                                                SVN_AUTH_PARAM_NON_INTERACTIVE)
313      != NULL;
314  svn_boolean_t no_auth_cache =
315    (! creds->may_save) || (svn_hash_gets(parameters,
316                                          SVN_AUTH_PARAM_NO_AUTH_CACHE)
317                            != NULL);
318
319  /* Make sure we've been passed a passtype. */
320  SVN_ERR_ASSERT(passtype != NULL);
321
322  *saved = FALSE;
323
324  if (no_auth_cache)
325    return SVN_NO_ERROR;
326
327  config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR);
328
329  /* Put the username into the credentials hash. */
330  creds_hash = apr_hash_make(pool);
331  svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_USERNAME_KEY,
332                svn_string_create(creds->username, pool));
333
334  /* Don't store passwords in any form if the user has told
335   * us not to do so. */
336  if (! dont_store_passwords)
337    {
338      svn_boolean_t may_save_password = FALSE;
339
340      /* If the password is going to be stored encrypted, go right
341       * ahead and store it to disk. Else determine whether saving
342       * in plaintext is OK. */
343      if (passtype &&
344           (strcmp(passtype, SVN_AUTH__WINCRYPT_PASSWORD_TYPE) == 0
345            || strcmp(passtype, SVN_AUTH__KEYCHAIN_PASSWORD_TYPE) == 0
346            || strcmp(passtype, SVN_AUTH__KWALLET_PASSWORD_TYPE) == 0
347            || strcmp(passtype, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE) == 0
348            || strcmp(passtype, SVN_AUTH__GPG_AGENT_PASSWORD_TYPE) == 0))
349        {
350          may_save_password = TRUE;
351        }
352      else
353        {
354#ifdef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
355          may_save_password = FALSE;
356#else
357          const char *store_plaintext_passwords =
358            svn_hash_gets(parameters, SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS);
359          simple_provider_baton_t *b =
360            (simple_provider_baton_t *)provider_baton;
361
362          if (store_plaintext_passwords
363              && svn_cstring_casecmp(store_plaintext_passwords,
364                                     SVN_CONFIG_ASK) == 0)
365            {
366              if (non_interactive)
367                /* In non-interactive mode, the default behaviour is
368                 * to not store the password, because it is usually
369                 * passed on the command line. */
370                may_save_password = FALSE;
371              else if (b->plaintext_prompt_func)
372                {
373                  /* We're interactive, and the client provided a
374                   * prompt callback. So we can ask the user.
375                   *
376                   * Check for a cached answer before prompting. */
377                  svn_boolean_t *cached_answer;
378                  cached_answer = svn_hash_gets(b->plaintext_answers,
379                                                realmstring);
380                  if (cached_answer != NULL)
381                    may_save_password = *cached_answer;
382                  else
383                    {
384                      apr_pool_t *cached_answer_pool;
385
386                      /* Nothing cached for this realm, prompt the user. */
387                      SVN_ERR((*b->plaintext_prompt_func)(&may_save_password,
388                                                          realmstring,
389                                                          b->prompt_baton,
390                                                          pool));
391
392                      /* Cache the user's answer in case we're called again
393                       * for the same realm.
394                       *
395                       * We allocate the answer cache in the hash table's pool
396                       * to make sure that is has the same life time as the
397                       * hash table itself. This means that the answer will
398                       * survive across RA sessions -- which is important,
399                       * because otherwise we'd prompt users once per RA session.
400                       */
401                      cached_answer_pool = apr_hash_pool_get(b->plaintext_answers);
402                      cached_answer = apr_palloc(cached_answer_pool,
403                                                 sizeof(svn_boolean_t));
404                      *cached_answer = may_save_password;
405                      svn_hash_sets(b->plaintext_answers, realmstring,
406                                    cached_answer);
407                    }
408                }
409              else
410                {
411                  /* TODO: We might want to default to not storing if the
412                   * prompt callback is NULL, i.e. have may_save_password
413                   * default to FALSE here, in order to force clients to
414                   * implement the callback.
415                   *
416                   * This would change the semantics of old API though.
417                   *
418                   * So for now, clients that don't implement the callback
419                   * and provide no explicit value for
420                   * SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS
421                   * cause unencrypted passwords to be stored by default.
422                   * Needless to say, our own client is sane, but who knows
423                   * what other clients are doing.
424                   */
425                  may_save_password = TRUE;
426                }
427            }
428          else if (store_plaintext_passwords
429                   && svn_cstring_casecmp(store_plaintext_passwords,
430                                          SVN_CONFIG_FALSE) == 0)
431            {
432              may_save_password = FALSE;
433            }
434          else if (!store_plaintext_passwords
435                   || svn_cstring_casecmp(store_plaintext_passwords,
436                                          SVN_CONFIG_TRUE) == 0)
437            {
438              may_save_password = TRUE;
439            }
440          else
441            {
442              return svn_error_createf
443                (SVN_ERR_BAD_CONFIG_VALUE, NULL,
444                 _("Config error: invalid value '%s' for option '%s'"),
445                store_plaintext_passwords,
446                SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS);
447            }
448#endif
449        }
450
451      if (may_save_password)
452        {
453          SVN_ERR(password_set(saved, creds_hash, realmstring,
454                               creds->username, creds->password,
455                               parameters, non_interactive, pool));
456          if (*saved && passtype)
457            /* Store the password type with the auth data, so that we
458               know which provider owns the password. */
459            svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_PASSTYPE_KEY,
460                          svn_string_create(passtype, pool));
461        }
462    }
463
464  /* Save credentials to disk. */
465  err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_SIMPLE,
466                                   realmstring, config_dir, pool);
467  if (err)
468    *saved = FALSE;
469
470  /* ### return error? */
471  svn_error_clear(err);
472
473  return SVN_NO_ERROR;
474}
475
476/* Get cached (unencrypted) credentials from the simple provider's cache. */
477static svn_error_t *
478simple_first_creds(void **credentials,
479                   void **iter_baton,
480                   void *provider_baton,
481                   apr_hash_t *parameters,
482                   const char *realmstring,
483                   apr_pool_t *pool)
484{
485  return svn_auth__simple_creds_cache_get(credentials, iter_baton,
486                                          provider_baton, parameters,
487                                          realmstring,
488                                          svn_auth__simple_password_get,
489                                          SVN_AUTH__SIMPLE_PASSWORD_TYPE,
490                                          pool);
491}
492
493/* Save (unencrypted) credentials to the simple provider's cache. */
494static svn_error_t *
495simple_save_creds(svn_boolean_t *saved,
496                  void *credentials,
497                  void *provider_baton,
498                  apr_hash_t *parameters,
499                  const char *realmstring,
500                  apr_pool_t *pool)
501{
502  return svn_auth__simple_creds_cache_set(saved, credentials, provider_baton,
503                                          parameters, realmstring,
504                                          svn_auth__simple_password_set,
505                                          SVN_AUTH__SIMPLE_PASSWORD_TYPE,
506                                          pool);
507}
508
509static const svn_auth_provider_t simple_provider = {
510  SVN_AUTH_CRED_SIMPLE,
511  simple_first_creds,
512  NULL,
513  simple_save_creds
514};
515
516
517/* Public API */
518void
519svn_auth_get_simple_provider2
520  (svn_auth_provider_object_t **provider,
521   svn_auth_plaintext_prompt_func_t plaintext_prompt_func,
522   void* prompt_baton,
523   apr_pool_t *pool)
524{
525  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
526  simple_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
527
528  pb->plaintext_prompt_func = plaintext_prompt_func;
529  pb->prompt_baton = prompt_baton;
530  pb->plaintext_answers = apr_hash_make(pool);
531
532  po->vtable = &simple_provider;
533  po->provider_baton = pb;
534  *provider = po;
535}
536
537
538/*-----------------------------------------------------------------------*/
539/* Prompt provider                                                       */
540/*-----------------------------------------------------------------------*/
541
542/* Baton type for username/password prompting. */
543typedef struct simple_prompt_provider_baton_t
544{
545  svn_auth_simple_prompt_func_t prompt_func;
546  void *prompt_baton;
547
548  /* how many times to re-prompt after the first one fails */
549  int retry_limit;
550} simple_prompt_provider_baton_t;
551
552
553/* Iteration baton type for username/password prompting. */
554typedef struct simple_prompt_iter_baton_t
555{
556  /* how many times we've reprompted */
557  int retries;
558} simple_prompt_iter_baton_t;
559
560
561
562/*** Helper Functions ***/
563static svn_error_t *
564prompt_for_simple_creds(svn_auth_cred_simple_t **cred_p,
565                        simple_prompt_provider_baton_t *pb,
566                        apr_hash_t *parameters,
567                        const char *realmstring,
568                        svn_boolean_t first_time,
569                        svn_boolean_t may_save,
570                        apr_pool_t *pool)
571{
572  const char *default_username = NULL;
573  const char *default_password = NULL;
574
575  *cred_p = NULL;
576
577  /* If we're allowed to check for default usernames and passwords, do
578     so. */
579  if (first_time)
580    {
581      default_username = svn_hash_gets(parameters,
582                                       SVN_AUTH_PARAM_DEFAULT_USERNAME);
583
584      /* No default username?  Try the auth cache. */
585      if (! default_username)
586        {
587          const char *config_dir = svn_hash_gets(parameters,
588                                                 SVN_AUTH_PARAM_CONFIG_DIR);
589          apr_hash_t *creds_hash = NULL;
590          svn_string_t *str;
591          svn_error_t *err;
592
593          err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_SIMPLE,
594                                          realmstring, config_dir, pool);
595          svn_error_clear(err);
596          if (! err && creds_hash)
597            {
598              str = svn_hash_gets(creds_hash, SVN_CONFIG_AUTHN_USERNAME_KEY);
599              if (str && str->data)
600                default_username = str->data;
601            }
602        }
603
604      /* Still no default username?  Try the 'servers' file. */
605      if (! default_username)
606        {
607          svn_config_t *cfg = svn_hash_gets(parameters,
608                                            SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS);
609          const char *server_group = svn_hash_gets(parameters,
610                                                   SVN_AUTH_PARAM_SERVER_GROUP);
611          default_username =
612            svn_config_get_server_setting(cfg, server_group,
613                                          SVN_CONFIG_OPTION_USERNAME,
614                                          NULL);
615        }
616
617      /* Still no default username?  Try the UID. */
618      if (! default_username)
619        default_username = svn_user_get_name(pool);
620
621      default_password = svn_hash_gets(parameters,
622                                       SVN_AUTH_PARAM_DEFAULT_PASSWORD);
623    }
624
625  /* If we have defaults, just build the cred here and return it.
626   *
627   * ### I do wonder why this is here instead of in a separate
628   * ### 'defaults' provider that would run before the prompt
629   * ### provider... Hmmm.
630   */
631  if (default_username && default_password)
632    {
633      *cred_p = apr_palloc(pool, sizeof(**cred_p));
634      (*cred_p)->username = apr_pstrdup(pool, default_username);
635      (*cred_p)->password = apr_pstrdup(pool, default_password);
636      (*cred_p)->may_save = TRUE;
637    }
638  else
639    {
640      SVN_ERR(pb->prompt_func(cred_p, pb->prompt_baton, realmstring,
641                              default_username, may_save, pool));
642    }
643
644  return SVN_NO_ERROR;
645}
646
647
648/* Our first attempt will use any default username/password passed
649   in, and prompt for the remaining stuff. */
650static svn_error_t *
651simple_prompt_first_creds(void **credentials_p,
652                          void **iter_baton,
653                          void *provider_baton,
654                          apr_hash_t *parameters,
655                          const char *realmstring,
656                          apr_pool_t *pool)
657{
658  simple_prompt_provider_baton_t *pb = provider_baton;
659  simple_prompt_iter_baton_t *ibaton = apr_pcalloc(pool, sizeof(*ibaton));
660  const char *no_auth_cache = svn_hash_gets(parameters,
661                                            SVN_AUTH_PARAM_NO_AUTH_CACHE);
662
663  SVN_ERR(prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,
664                                  pb, parameters, realmstring, TRUE,
665                                  ! no_auth_cache, pool));
666
667  ibaton->retries = 0;
668  *iter_baton = ibaton;
669
670  return SVN_NO_ERROR;
671}
672
673
674/* Subsequent attempts to fetch will ignore the default values, and
675   simply re-prompt for both, up to a maximum of ib->pb->retry_limit. */
676static svn_error_t *
677simple_prompt_next_creds(void **credentials_p,
678                         void *iter_baton,
679                         void *provider_baton,
680                         apr_hash_t *parameters,
681                         const char *realmstring,
682                         apr_pool_t *pool)
683{
684  simple_prompt_iter_baton_t *ib = iter_baton;
685  simple_prompt_provider_baton_t *pb = provider_baton;
686  const char *no_auth_cache = svn_hash_gets(parameters,
687                                            SVN_AUTH_PARAM_NO_AUTH_CACHE);
688
689  if ((pb->retry_limit >= 0) && (ib->retries >= pb->retry_limit))
690    {
691      /* give up, go on to next provider. */
692      *credentials_p = NULL;
693      return SVN_NO_ERROR;
694    }
695  ib->retries++;
696
697  return prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,
698                                 pb, parameters, realmstring, FALSE,
699                                 ! no_auth_cache, pool);
700}
701
702static const svn_auth_provider_t simple_prompt_provider = {
703  SVN_AUTH_CRED_SIMPLE,
704  simple_prompt_first_creds,
705  simple_prompt_next_creds,
706  NULL,
707};
708
709
710/* Public API */
711void
712svn_auth_get_simple_prompt_provider
713  (svn_auth_provider_object_t **provider,
714   svn_auth_simple_prompt_func_t prompt_func,
715   void *prompt_baton,
716   int retry_limit,
717   apr_pool_t *pool)
718{
719  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
720  simple_prompt_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
721
722  pb->prompt_func = prompt_func;
723  pb->prompt_baton = prompt_baton;
724  pb->retry_limit = retry_limit;
725
726  po->vtable = &simple_prompt_provider;
727  po->provider_baton = pb;
728  *provider = po;
729}
730