1251881Speter/*
2251881Speter * simple_providers.c: providers for SVN_AUTH_CRED_SIMPLE
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter/* ==================================================================== */
25251881Speter
26251881Speter
27251881Speter
28251881Speter/*** Includes. ***/
29251881Speter
30251881Speter#include <apr_pools.h>
31251881Speter#include "svn_auth.h"
32251881Speter#include "svn_dirent_uri.h"
33251881Speter#include "svn_hash.h"
34251881Speter#include "svn_pools.h"
35251881Speter#include "svn_error.h"
36251881Speter#include "svn_utf.h"
37251881Speter#include "svn_config.h"
38251881Speter#include "svn_user.h"
39251881Speter
40251881Speter#include "private/svn_auth_private.h"
41251881Speter
42251881Speter#include "svn_private_config.h"
43251881Speter
44251881Speter#include "auth.h"
45251881Speter
46251881Speter/*-----------------------------------------------------------------------*/
47251881Speter/* File provider                                                         */
48251881Speter/*-----------------------------------------------------------------------*/
49251881Speter
50251881Speter/* The keys that will be stored on disk.  These serve the same role as
51251881Speter   similar constants in other providers. */
52251881Speter#define AUTHN_USERNAME_KEY            "username"
53251881Speter#define AUTHN_PASSWORD_KEY            "password"
54251881Speter#define AUTHN_PASSTYPE_KEY            "passtype"
55251881Speter
56251881Speter/* Baton type for the simple provider. */
57251881Spetertypedef struct simple_provider_baton_t
58251881Speter{
59251881Speter  svn_auth_plaintext_prompt_func_t plaintext_prompt_func;
60251881Speter  void *prompt_baton;
61251881Speter  /* We cache the user's answer to the plaintext prompt, keyed
62251881Speter   * by realm, in case we'll be called multiple times for the
63251881Speter   * same realm. */
64251881Speter  apr_hash_t *plaintext_answers;
65251881Speter} simple_provider_baton_t;
66251881Speter
67251881Speter
68251881Speter/* Implementation of svn_auth__password_get_t that retrieves
69251881Speter   the plaintext password from CREDS. */
70251881Spetersvn_error_t *
71251881Spetersvn_auth__simple_password_get(svn_boolean_t *done,
72251881Speter                              const char **password,
73251881Speter                              apr_hash_t *creds,
74251881Speter                              const char *realmstring,
75251881Speter                              const char *username,
76251881Speter                              apr_hash_t *parameters,
77251881Speter                              svn_boolean_t non_interactive,
78251881Speter                              apr_pool_t *pool)
79251881Speter{
80251881Speter  svn_string_t *str;
81251881Speter
82251881Speter  *done = FALSE;
83251881Speter
84251881Speter  str = svn_hash_gets(creds, AUTHN_USERNAME_KEY);
85251881Speter  if (str && username && strcmp(str->data, username) == 0)
86251881Speter    {
87251881Speter      str = svn_hash_gets(creds, AUTHN_PASSWORD_KEY);
88251881Speter      if (str && str->data)
89251881Speter        {
90251881Speter          *password = str->data;
91251881Speter          *done = TRUE;
92251881Speter        }
93251881Speter    }
94251881Speter
95251881Speter  return SVN_NO_ERROR;
96251881Speter}
97251881Speter
98251881Speter/* Implementation of svn_auth__password_set_t that stores
99251881Speter   the plaintext password in CREDS. */
100251881Spetersvn_error_t *
101251881Spetersvn_auth__simple_password_set(svn_boolean_t *done,
102251881Speter                              apr_hash_t *creds,
103251881Speter                              const char *realmstring,
104251881Speter                              const char *username,
105251881Speter                              const char *password,
106251881Speter                              apr_hash_t *parameters,
107251881Speter                              svn_boolean_t non_interactive,
108251881Speter                              apr_pool_t *pool)
109251881Speter{
110251881Speter  svn_hash_sets(creds, AUTHN_PASSWORD_KEY, svn_string_create(password, pool));
111251881Speter  *done = TRUE;
112251881Speter
113251881Speter  return SVN_NO_ERROR;
114251881Speter}
115251881Speter
116251881Speter/* Set **USERNAME to the username retrieved from CREDS; ignore
117251881Speter   other parameters. *USERNAME will have the same lifetime as CREDS. */
118251881Speterstatic svn_boolean_t
119251881Spetersimple_username_get(const char **username,
120251881Speter                    apr_hash_t *creds,
121251881Speter                    const char *realmstring,
122251881Speter                    svn_boolean_t non_interactive)
123251881Speter{
124251881Speter  svn_string_t *str;
125251881Speter  str = svn_hash_gets(creds, AUTHN_USERNAME_KEY);
126251881Speter  if (str && str->data)
127251881Speter    {
128251881Speter      *username = str->data;
129251881Speter      return TRUE;
130251881Speter    }
131251881Speter  return FALSE;
132251881Speter}
133251881Speter
134251881Speter
135251881Spetersvn_error_t *
136251881Spetersvn_auth__simple_creds_cache_get(void **credentials,
137251881Speter                                 void **iter_baton,
138251881Speter                                 void *provider_baton,
139251881Speter                                 apr_hash_t *parameters,
140251881Speter                                 const char *realmstring,
141251881Speter                                 svn_auth__password_get_t password_get,
142251881Speter                                 const char *passtype,
143251881Speter                                 apr_pool_t *pool)
144251881Speter{
145251881Speter  const char *config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR);
146251881Speter  svn_config_t *cfg = svn_hash_gets(parameters,
147251881Speter                                    SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS);
148251881Speter  const char *server_group = svn_hash_gets(parameters,
149251881Speter                                           SVN_AUTH_PARAM_SERVER_GROUP);
150251881Speter  const char *username = svn_hash_gets(parameters,
151251881Speter                                       SVN_AUTH_PARAM_DEFAULT_USERNAME);
152251881Speter  const char *password = svn_hash_gets(parameters,
153251881Speter                                       SVN_AUTH_PARAM_DEFAULT_PASSWORD);
154251881Speter  svn_boolean_t non_interactive = svn_hash_gets(parameters,
155251881Speter                                                SVN_AUTH_PARAM_NON_INTERACTIVE)
156251881Speter      != NULL;
157251881Speter  const char *default_username = NULL; /* Default username from cache. */
158251881Speter  const char *default_password = NULL; /* Default password from cache. */
159251881Speter
160251881Speter  /* This checks if we should save the CREDS, iff saving the credentials is
161251881Speter     allowed by the run-time configuration. */
162251881Speter  svn_boolean_t need_to_save = FALSE;
163251881Speter  apr_hash_t *creds_hash = NULL;
164251881Speter  svn_error_t *err;
165251881Speter  svn_string_t *str;
166251881Speter
167251881Speter  /* Try to load credentials from a file on disk, based on the
168251881Speter     realmstring.  Don't throw an error, though: if something went
169251881Speter     wrong reading the file, no big deal.  What really matters is that
170251881Speter     we failed to get the creds, so allow the auth system to try the
171251881Speter     next provider. */
172251881Speter  err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_SIMPLE,
173251881Speter                                  realmstring, config_dir, pool);
174251881Speter  if (err)
175251881Speter    {
176251881Speter      svn_error_clear(err);
177251881Speter      err = NULL;
178251881Speter    }
179251881Speter  else if (creds_hash)
180251881Speter    {
181251881Speter      /* We have something in the auth cache for this realm. */
182251881Speter      svn_boolean_t have_passtype = FALSE;
183251881Speter
184251881Speter      /* The password type in the auth data must match the
185251881Speter         mangler's type, otherwise the password must be
186251881Speter         interpreted by another provider. */
187251881Speter      str = svn_hash_gets(creds_hash, AUTHN_PASSTYPE_KEY);
188251881Speter      if (str && str->data)
189251881Speter        if (passtype && (0 == strcmp(str->data, passtype)))
190251881Speter          have_passtype = TRUE;
191251881Speter
192251881Speter      /* See if we need to save this username if it is not present in
193251881Speter         auth cache. */
194251881Speter      if (username)
195251881Speter        {
196251881Speter          if (!simple_username_get(&default_username, creds_hash, realmstring,
197251881Speter                                   non_interactive))
198251881Speter            {
199251881Speter              need_to_save = TRUE;
200251881Speter            }
201251881Speter          else
202251881Speter            {
203251881Speter              if (strcmp(default_username, username) != 0)
204251881Speter                need_to_save = TRUE;
205251881Speter            }
206251881Speter        }
207251881Speter
208251881Speter      /* See if we need to save this password if it is not present in
209251881Speter         auth cache. */
210251881Speter      if (password)
211251881Speter        {
212251881Speter          if (have_passtype)
213251881Speter            {
214251881Speter              svn_boolean_t done;
215251881Speter
216251881Speter              SVN_ERR(password_get(&done, &default_password, creds_hash,
217251881Speter                                   realmstring, username, parameters,
218251881Speter                                   non_interactive, pool));
219251881Speter              if (!done)
220251881Speter                {
221251881Speter                  need_to_save = TRUE;
222251881Speter                }
223251881Speter              else
224251881Speter                {
225251881Speter                  if (strcmp(default_password, password) != 0)
226251881Speter                    need_to_save = TRUE;
227251881Speter                }
228251881Speter            }
229251881Speter        }
230251881Speter
231251881Speter      /* If we don't have a username and a password yet, we try the
232251881Speter         auth cache */
233251881Speter      if (! (username && password))
234251881Speter        {
235251881Speter          if (! username)
236251881Speter            if (!simple_username_get(&username, creds_hash, realmstring,
237251881Speter                                     non_interactive))
238251881Speter              username = NULL;
239251881Speter
240251881Speter          if (username && ! password)
241251881Speter            {
242251881Speter              if (! have_passtype)
243251881Speter                password = NULL;
244251881Speter              else
245251881Speter                {
246251881Speter                  svn_boolean_t done;
247251881Speter
248251881Speter                  SVN_ERR(password_get(&done, &password, creds_hash,
249251881Speter                                       realmstring, username, parameters,
250251881Speter                                       non_interactive, pool));
251251881Speter                  if (!done)
252251881Speter                    password = NULL;
253251881Speter
254251881Speter                  /* If the auth data didn't contain a password type,
255251881Speter                     force a write to upgrade the format of the auth
256251881Speter                     data file. */
257251881Speter                  if (password && ! have_passtype)
258251881Speter                    need_to_save = TRUE;
259251881Speter                }
260251881Speter            }
261251881Speter        }
262251881Speter    }
263251881Speter  else
264251881Speter    {
265251881Speter      /* Nothing was present in the auth cache, so indicate that these
266251881Speter         credentials should be saved. */
267251881Speter      need_to_save = TRUE;
268251881Speter    }
269251881Speter
270251881Speter  /* If we don't have a username yet, check the 'servers' file */
271251881Speter  if (! username)
272251881Speter    {
273251881Speter      username = svn_config_get_server_setting(cfg, server_group,
274251881Speter                                               SVN_CONFIG_OPTION_USERNAME,
275251881Speter                                               NULL);
276251881Speter    }
277251881Speter
278251881Speter  /* Ask the OS for the username if we have a password but no
279251881Speter     username. */
280251881Speter  if (password && ! username)
281251881Speter    username = svn_user_get_name(pool);
282251881Speter
283251881Speter  if (username && password)
284251881Speter    {
285251881Speter      svn_auth_cred_simple_t *creds = apr_pcalloc(pool, sizeof(*creds));
286251881Speter      creds->username = username;
287251881Speter      creds->password = password;
288251881Speter      creds->may_save = need_to_save;
289251881Speter      *credentials = creds;
290251881Speter    }
291251881Speter  else
292251881Speter    *credentials = NULL;
293251881Speter
294251881Speter  *iter_baton = NULL;
295251881Speter
296251881Speter  return SVN_NO_ERROR;
297251881Speter}
298251881Speter
299251881Speter
300251881Spetersvn_error_t *
301251881Spetersvn_auth__simple_creds_cache_set(svn_boolean_t *saved,
302251881Speter                                 void *credentials,
303251881Speter                                 void *provider_baton,
304251881Speter                                 apr_hash_t *parameters,
305251881Speter                                 const char *realmstring,
306251881Speter                                 svn_auth__password_set_t password_set,
307251881Speter                                 const char *passtype,
308251881Speter                                 apr_pool_t *pool)
309251881Speter{
310251881Speter  svn_auth_cred_simple_t *creds = credentials;
311251881Speter  apr_hash_t *creds_hash = NULL;
312251881Speter  const char *config_dir;
313251881Speter  svn_error_t *err;
314251881Speter  svn_boolean_t dont_store_passwords =
315251881Speter    svn_hash_gets(parameters, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL;
316251881Speter  svn_boolean_t non_interactive = svn_hash_gets(parameters,
317251881Speter                                                SVN_AUTH_PARAM_NON_INTERACTIVE)
318251881Speter      != NULL;
319251881Speter  svn_boolean_t no_auth_cache =
320251881Speter    (! creds->may_save) || (svn_hash_gets(parameters,
321251881Speter                                          SVN_AUTH_PARAM_NO_AUTH_CACHE)
322251881Speter                            != NULL);
323251881Speter
324251881Speter  /* Make sure we've been passed a passtype. */
325251881Speter  SVN_ERR_ASSERT(passtype != NULL);
326251881Speter
327251881Speter  *saved = FALSE;
328251881Speter
329251881Speter  if (no_auth_cache)
330251881Speter    return SVN_NO_ERROR;
331251881Speter
332251881Speter  config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR);
333251881Speter
334251881Speter  /* Put the username into the credentials hash. */
335251881Speter  creds_hash = apr_hash_make(pool);
336251881Speter  svn_hash_sets(creds_hash, AUTHN_USERNAME_KEY,
337251881Speter                svn_string_create(creds->username, pool));
338251881Speter
339251881Speter  /* Don't store passwords in any form if the user has told
340251881Speter   * us not to do so. */
341251881Speter  if (! dont_store_passwords)
342251881Speter    {
343251881Speter      svn_boolean_t may_save_password = FALSE;
344251881Speter
345251881Speter      /* If the password is going to be stored encrypted, go right
346251881Speter       * ahead and store it to disk. Else determine whether saving
347251881Speter       * in plaintext is OK. */
348251881Speter      if (passtype &&
349251881Speter           (strcmp(passtype, SVN_AUTH__WINCRYPT_PASSWORD_TYPE) == 0
350251881Speter            || strcmp(passtype, SVN_AUTH__KEYCHAIN_PASSWORD_TYPE) == 0
351251881Speter            || strcmp(passtype, SVN_AUTH__KWALLET_PASSWORD_TYPE) == 0
352251881Speter            || strcmp(passtype, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE) == 0
353251881Speter            || strcmp(passtype, SVN_AUTH__GPG_AGENT_PASSWORD_TYPE) == 0))
354251881Speter        {
355251881Speter          may_save_password = TRUE;
356251881Speter        }
357251881Speter      else
358251881Speter        {
359251881Speter#ifdef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
360251881Speter          may_save_password = FALSE;
361251881Speter#else
362251881Speter          const char *store_plaintext_passwords =
363251881Speter            svn_hash_gets(parameters, SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS);
364251881Speter          simple_provider_baton_t *b =
365251881Speter            (simple_provider_baton_t *)provider_baton;
366251881Speter
367251881Speter          if (store_plaintext_passwords
368251881Speter              && svn_cstring_casecmp(store_plaintext_passwords,
369251881Speter                                     SVN_CONFIG_ASK) == 0)
370251881Speter            {
371251881Speter              if (non_interactive)
372251881Speter                /* In non-interactive mode, the default behaviour is
373251881Speter                 * to not store the password, because it is usually
374251881Speter                 * passed on the command line. */
375251881Speter                may_save_password = FALSE;
376251881Speter              else if (b->plaintext_prompt_func)
377251881Speter                {
378251881Speter                  /* We're interactive, and the client provided a
379251881Speter                   * prompt callback. So we can ask the user.
380251881Speter                   *
381251881Speter                   * Check for a cached answer before prompting. */
382251881Speter                  svn_boolean_t *cached_answer;
383251881Speter                  cached_answer = svn_hash_gets(b->plaintext_answers,
384251881Speter                                                realmstring);
385251881Speter                  if (cached_answer != NULL)
386251881Speter                    may_save_password = *cached_answer;
387251881Speter                  else
388251881Speter                    {
389251881Speter                      apr_pool_t *cached_answer_pool;
390251881Speter
391251881Speter                      /* Nothing cached for this realm, prompt the user. */
392251881Speter                      SVN_ERR((*b->plaintext_prompt_func)(&may_save_password,
393251881Speter                                                          realmstring,
394251881Speter                                                          b->prompt_baton,
395251881Speter                                                          pool));
396251881Speter
397251881Speter                      /* Cache the user's answer in case we're called again
398251881Speter                       * for the same realm.
399251881Speter                       *
400251881Speter                       * We allocate the answer cache in the hash table's pool
401251881Speter                       * to make sure that is has the same life time as the
402251881Speter                       * hash table itself. This means that the answer will
403251881Speter                       * survive across RA sessions -- which is important,
404251881Speter                       * because otherwise we'd prompt users once per RA session.
405251881Speter                       */
406251881Speter                      cached_answer_pool = apr_hash_pool_get(b->plaintext_answers);
407251881Speter                      cached_answer = apr_palloc(cached_answer_pool,
408251881Speter                                                 sizeof(svn_boolean_t));
409251881Speter                      *cached_answer = may_save_password;
410251881Speter                      svn_hash_sets(b->plaintext_answers, realmstring,
411251881Speter                                    cached_answer);
412251881Speter                    }
413251881Speter                }
414251881Speter              else
415251881Speter                {
416251881Speter                  /* TODO: We might want to default to not storing if the
417251881Speter                   * prompt callback is NULL, i.e. have may_save_password
418251881Speter                   * default to FALSE here, in order to force clients to
419251881Speter                   * implement the callback.
420251881Speter                   *
421251881Speter                   * This would change the semantics of old API though.
422251881Speter                   *
423251881Speter                   * So for now, clients that don't implement the callback
424251881Speter                   * and provide no explicit value for
425251881Speter                   * SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS
426251881Speter                   * cause unencrypted passwords to be stored by default.
427251881Speter                   * Needless to say, our own client is sane, but who knows
428251881Speter                   * what other clients are doing.
429251881Speter                   */
430251881Speter                  may_save_password = TRUE;
431251881Speter                }
432251881Speter            }
433251881Speter          else if (store_plaintext_passwords
434251881Speter                   && svn_cstring_casecmp(store_plaintext_passwords,
435251881Speter                                          SVN_CONFIG_FALSE) == 0)
436251881Speter            {
437251881Speter              may_save_password = FALSE;
438251881Speter            }
439251881Speter          else if (!store_plaintext_passwords
440251881Speter                   || svn_cstring_casecmp(store_plaintext_passwords,
441251881Speter                                          SVN_CONFIG_TRUE) == 0)
442251881Speter            {
443251881Speter              may_save_password = TRUE;
444251881Speter            }
445251881Speter          else
446251881Speter            {
447251881Speter              return svn_error_createf
448251881Speter                (SVN_ERR_BAD_CONFIG_VALUE, NULL,
449251881Speter                 _("Config error: invalid value '%s' for option '%s'"),
450251881Speter                store_plaintext_passwords,
451251881Speter                SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS);
452251881Speter            }
453251881Speter#endif
454251881Speter        }
455251881Speter
456251881Speter      if (may_save_password)
457251881Speter        {
458251881Speter          SVN_ERR(password_set(saved, creds_hash, realmstring,
459251881Speter                               creds->username, creds->password,
460251881Speter                               parameters, non_interactive, pool));
461251881Speter          if (*saved && passtype)
462251881Speter            /* Store the password type with the auth data, so that we
463251881Speter               know which provider owns the password. */
464251881Speter            svn_hash_sets(creds_hash, AUTHN_PASSTYPE_KEY,
465251881Speter                          svn_string_create(passtype, pool));
466251881Speter        }
467251881Speter    }
468251881Speter
469251881Speter  /* Save credentials to disk. */
470251881Speter  err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_SIMPLE,
471251881Speter                                   realmstring, config_dir, pool);
472251881Speter  if (err)
473251881Speter    *saved = FALSE;
474251881Speter
475251881Speter  /* ### return error? */
476251881Speter  svn_error_clear(err);
477251881Speter
478251881Speter  return SVN_NO_ERROR;
479251881Speter}
480251881Speter
481251881Speter/* Get cached (unencrypted) credentials from the simple provider's cache. */
482251881Speterstatic svn_error_t *
483251881Spetersimple_first_creds(void **credentials,
484251881Speter                   void **iter_baton,
485251881Speter                   void *provider_baton,
486251881Speter                   apr_hash_t *parameters,
487251881Speter                   const char *realmstring,
488251881Speter                   apr_pool_t *pool)
489251881Speter{
490251881Speter  return svn_auth__simple_creds_cache_get(credentials, iter_baton,
491251881Speter                                          provider_baton, parameters,
492251881Speter                                          realmstring,
493251881Speter                                          svn_auth__simple_password_get,
494251881Speter                                          SVN_AUTH__SIMPLE_PASSWORD_TYPE,
495251881Speter                                          pool);
496251881Speter}
497251881Speter
498251881Speter/* Save (unencrypted) credentials to the simple provider's cache. */
499251881Speterstatic svn_error_t *
500251881Spetersimple_save_creds(svn_boolean_t *saved,
501251881Speter                  void *credentials,
502251881Speter                  void *provider_baton,
503251881Speter                  apr_hash_t *parameters,
504251881Speter                  const char *realmstring,
505251881Speter                  apr_pool_t *pool)
506251881Speter{
507251881Speter  return svn_auth__simple_creds_cache_set(saved, credentials, provider_baton,
508251881Speter                                          parameters, realmstring,
509251881Speter                                          svn_auth__simple_password_set,
510251881Speter                                          SVN_AUTH__SIMPLE_PASSWORD_TYPE,
511251881Speter                                          pool);
512251881Speter}
513251881Speter
514251881Speterstatic const svn_auth_provider_t simple_provider = {
515251881Speter  SVN_AUTH_CRED_SIMPLE,
516251881Speter  simple_first_creds,
517251881Speter  NULL,
518251881Speter  simple_save_creds
519251881Speter};
520251881Speter
521251881Speter
522251881Speter/* Public API */
523251881Spetervoid
524251881Spetersvn_auth_get_simple_provider2
525251881Speter  (svn_auth_provider_object_t **provider,
526251881Speter   svn_auth_plaintext_prompt_func_t plaintext_prompt_func,
527251881Speter   void* prompt_baton,
528251881Speter   apr_pool_t *pool)
529251881Speter{
530251881Speter  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
531251881Speter  simple_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
532251881Speter
533251881Speter  pb->plaintext_prompt_func = plaintext_prompt_func;
534251881Speter  pb->prompt_baton = prompt_baton;
535251881Speter  pb->plaintext_answers = apr_hash_make(pool);
536251881Speter
537251881Speter  po->vtable = &simple_provider;
538251881Speter  po->provider_baton = pb;
539251881Speter  *provider = po;
540251881Speter}
541251881Speter
542251881Speter
543251881Speter/*-----------------------------------------------------------------------*/
544251881Speter/* Prompt provider                                                       */
545251881Speter/*-----------------------------------------------------------------------*/
546251881Speter
547251881Speter/* Baton type for username/password prompting. */
548251881Spetertypedef struct simple_prompt_provider_baton_t
549251881Speter{
550251881Speter  svn_auth_simple_prompt_func_t prompt_func;
551251881Speter  void *prompt_baton;
552251881Speter
553251881Speter  /* how many times to re-prompt after the first one fails */
554251881Speter  int retry_limit;
555251881Speter} simple_prompt_provider_baton_t;
556251881Speter
557251881Speter
558251881Speter/* Iteration baton type for username/password prompting. */
559251881Spetertypedef struct simple_prompt_iter_baton_t
560251881Speter{
561251881Speter  /* how many times we've reprompted */
562251881Speter  int retries;
563251881Speter} simple_prompt_iter_baton_t;
564251881Speter
565251881Speter
566251881Speter
567251881Speter/*** Helper Functions ***/
568251881Speterstatic svn_error_t *
569251881Speterprompt_for_simple_creds(svn_auth_cred_simple_t **cred_p,
570251881Speter                        simple_prompt_provider_baton_t *pb,
571251881Speter                        apr_hash_t *parameters,
572251881Speter                        const char *realmstring,
573251881Speter                        svn_boolean_t first_time,
574251881Speter                        svn_boolean_t may_save,
575251881Speter                        apr_pool_t *pool)
576251881Speter{
577251881Speter  const char *default_username = NULL;
578251881Speter  const char *default_password = NULL;
579251881Speter
580251881Speter  *cred_p = NULL;
581251881Speter
582251881Speter  /* If we're allowed to check for default usernames and passwords, do
583251881Speter     so. */
584251881Speter  if (first_time)
585251881Speter    {
586251881Speter      default_username = svn_hash_gets(parameters,
587251881Speter                                       SVN_AUTH_PARAM_DEFAULT_USERNAME);
588251881Speter
589251881Speter      /* No default username?  Try the auth cache. */
590251881Speter      if (! default_username)
591251881Speter        {
592251881Speter          const char *config_dir = svn_hash_gets(parameters,
593251881Speter                                                 SVN_AUTH_PARAM_CONFIG_DIR);
594251881Speter          apr_hash_t *creds_hash = NULL;
595251881Speter          svn_string_t *str;
596251881Speter          svn_error_t *err;
597251881Speter
598251881Speter          err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_SIMPLE,
599251881Speter                                          realmstring, config_dir, pool);
600251881Speter          svn_error_clear(err);
601251881Speter          if (! err && creds_hash)
602251881Speter            {
603251881Speter              str = svn_hash_gets(creds_hash, AUTHN_USERNAME_KEY);
604251881Speter              if (str && str->data)
605251881Speter                default_username = str->data;
606251881Speter            }
607251881Speter        }
608251881Speter
609251881Speter      /* Still no default username?  Try the 'servers' file. */
610251881Speter      if (! default_username)
611251881Speter        {
612251881Speter          svn_config_t *cfg = svn_hash_gets(parameters,
613251881Speter                                            SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS);
614251881Speter          const char *server_group = svn_hash_gets(parameters,
615251881Speter                                                   SVN_AUTH_PARAM_SERVER_GROUP);
616251881Speter          default_username =
617251881Speter            svn_config_get_server_setting(cfg, server_group,
618251881Speter                                          SVN_CONFIG_OPTION_USERNAME,
619251881Speter                                          NULL);
620251881Speter        }
621251881Speter
622251881Speter      /* Still no default username?  Try the UID. */
623251881Speter      if (! default_username)
624251881Speter        default_username = svn_user_get_name(pool);
625251881Speter
626251881Speter      default_password = svn_hash_gets(parameters,
627251881Speter                                       SVN_AUTH_PARAM_DEFAULT_PASSWORD);
628251881Speter    }
629251881Speter
630251881Speter  /* If we have defaults, just build the cred here and return it.
631251881Speter   *
632251881Speter   * ### I do wonder why this is here instead of in a separate
633251881Speter   * ### 'defaults' provider that would run before the prompt
634251881Speter   * ### provider... Hmmm.
635251881Speter   */
636251881Speter  if (default_username && default_password)
637251881Speter    {
638251881Speter      *cred_p = apr_palloc(pool, sizeof(**cred_p));
639251881Speter      (*cred_p)->username = apr_pstrdup(pool, default_username);
640251881Speter      (*cred_p)->password = apr_pstrdup(pool, default_password);
641251881Speter      (*cred_p)->may_save = TRUE;
642251881Speter    }
643251881Speter  else
644251881Speter    {
645251881Speter      SVN_ERR(pb->prompt_func(cred_p, pb->prompt_baton, realmstring,
646251881Speter                              default_username, may_save, pool));
647251881Speter    }
648251881Speter
649251881Speter  return SVN_NO_ERROR;
650251881Speter}
651251881Speter
652251881Speter
653251881Speter/* Our first attempt will use any default username/password passed
654251881Speter   in, and prompt for the remaining stuff. */
655251881Speterstatic svn_error_t *
656251881Spetersimple_prompt_first_creds(void **credentials_p,
657251881Speter                          void **iter_baton,
658251881Speter                          void *provider_baton,
659251881Speter                          apr_hash_t *parameters,
660251881Speter                          const char *realmstring,
661251881Speter                          apr_pool_t *pool)
662251881Speter{
663251881Speter  simple_prompt_provider_baton_t *pb = provider_baton;
664251881Speter  simple_prompt_iter_baton_t *ibaton = apr_pcalloc(pool, sizeof(*ibaton));
665251881Speter  const char *no_auth_cache = svn_hash_gets(parameters,
666251881Speter                                            SVN_AUTH_PARAM_NO_AUTH_CACHE);
667251881Speter
668251881Speter  SVN_ERR(prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,
669251881Speter                                  pb, parameters, realmstring, TRUE,
670251881Speter                                  ! no_auth_cache, pool));
671251881Speter
672251881Speter  ibaton->retries = 0;
673251881Speter  *iter_baton = ibaton;
674251881Speter
675251881Speter  return SVN_NO_ERROR;
676251881Speter}
677251881Speter
678251881Speter
679251881Speter/* Subsequent attempts to fetch will ignore the default values, and
680251881Speter   simply re-prompt for both, up to a maximum of ib->pb->retry_limit. */
681251881Speterstatic svn_error_t *
682251881Spetersimple_prompt_next_creds(void **credentials_p,
683251881Speter                         void *iter_baton,
684251881Speter                         void *provider_baton,
685251881Speter                         apr_hash_t *parameters,
686251881Speter                         const char *realmstring,
687251881Speter                         apr_pool_t *pool)
688251881Speter{
689251881Speter  simple_prompt_iter_baton_t *ib = iter_baton;
690251881Speter  simple_prompt_provider_baton_t *pb = provider_baton;
691251881Speter  const char *no_auth_cache = svn_hash_gets(parameters,
692251881Speter                                            SVN_AUTH_PARAM_NO_AUTH_CACHE);
693251881Speter
694251881Speter  if ((pb->retry_limit >= 0) && (ib->retries >= pb->retry_limit))
695251881Speter    {
696251881Speter      /* give up, go on to next provider. */
697251881Speter      *credentials_p = NULL;
698251881Speter      return SVN_NO_ERROR;
699251881Speter    }
700251881Speter  ib->retries++;
701251881Speter
702251881Speter  return prompt_for_simple_creds((svn_auth_cred_simple_t **) credentials_p,
703251881Speter                                 pb, parameters, realmstring, FALSE,
704251881Speter                                 ! no_auth_cache, pool);
705251881Speter}
706251881Speter
707251881Speterstatic const svn_auth_provider_t simple_prompt_provider = {
708251881Speter  SVN_AUTH_CRED_SIMPLE,
709251881Speter  simple_prompt_first_creds,
710251881Speter  simple_prompt_next_creds,
711251881Speter  NULL,
712251881Speter};
713251881Speter
714251881Speter
715251881Speter/* Public API */
716251881Spetervoid
717251881Spetersvn_auth_get_simple_prompt_provider
718251881Speter  (svn_auth_provider_object_t **provider,
719251881Speter   svn_auth_simple_prompt_func_t prompt_func,
720251881Speter   void *prompt_baton,
721251881Speter   int retry_limit,
722251881Speter   apr_pool_t *pool)
723251881Speter{
724251881Speter  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
725251881Speter  simple_prompt_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
726251881Speter
727251881Speter  pb->prompt_func = prompt_func;
728251881Speter  pb->prompt_baton = prompt_baton;
729251881Speter  pb->retry_limit = retry_limit;
730251881Speter
731251881Speter  po->vtable = &simple_prompt_provider;
732251881Speter  po->provider_baton = pb;
733251881Speter  *provider = po;
734251881Speter}
735