]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_subr/auth.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 / auth.c
1 /*
2  * auth.c: authentication support functions for Subversion
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 #include <apr_pools.h>
26 #include <apr_tables.h>
27 #include <apr_strings.h>
28
29 #include "svn_hash.h"
30 #include "svn_types.h"
31 #include "svn_string.h"
32 #include "svn_error.h"
33 #include "svn_auth.h"
34 #include "svn_config.h"
35 #include "svn_private_config.h"
36 #include "svn_dso.h"
37 #include "svn_version.h"
38 #include "private/svn_auth_private.h"
39 #include "private/svn_dep_compat.h"
40 #include "private/svn_subr_private.h"
41
42 #include "auth.h"
43
44 /* AN OVERVIEW
45    ===========
46
47    A good way to think of this machinery is as a set of tables.
48
49      - Each type of credentials selects a single table.
50
51      - In a given table, each row is a 'provider' capable of returning
52        the same type of credentials.  Each column represents a
53        provider's repeated attempts to provide credentials.
54
55
56    Fetching Credentials from Providers
57    -----------------------------------
58
59    When the caller asks for a particular type of credentials, the
60    machinery in this file walks over the appropriate table.  It starts
61    with the first provider (first row), and calls first_credentials()
62    to get the first set of credentials (first column).  If the caller
63    is unhappy with the credentials, then each subsequent call to
64    next_credentials() traverses the row from left to right.  If the
65    provider returns error at any point, then we go to the next provider
66    (row).  We continue this way until every provider fails, or
67    until the client is happy with the returned credentials.
68
69    Note that the caller cannot see the table traversal, and thus has
70    no idea when we switch providers.
71
72
73    Storing Credentials with Providers
74    ----------------------------------
75
76    When the server has validated a set of credentials, and when
77    credential caching is enabled, we have the chance to store those
78    credentials for later use.  The provider which provided the working
79    credentials is the first one given the opportunity to (re)cache
80    those credentials.  Its save_credentials() function is invoked with
81    the working credentials.  If that provider reports that it
82    successfully stored the credentials, we're done.  Otherwise, we
83    walk the providers (rows) for that type of credentials in order
84    from the top of the table, allowing each in turn the opportunity to
85    store the credentials.  When one reports that it has done so
86    successfully -- or when we run out of providers (rows) to try --
87    the table walk ends.
88 */
89
90
91
92 /* This effectively defines a single table.  Every provider in this
93    array returns the same kind of credentials. */
94 typedef struct provider_set_t
95 {
96   /* ordered array of svn_auth_provider_object_t */
97   apr_array_header_t *providers;
98
99 } provider_set_t;
100
101
102 /* The main auth baton. */
103 struct svn_auth_baton_t
104 {
105   /* a collection of tables.  maps cred_kind -> provider_set */
106   apr_hash_t *tables;
107
108   /* the pool I'm allocated in. */
109   apr_pool_t *pool;
110
111   /* run-time parameters needed by providers. */
112   apr_hash_t *parameters;
113
114   /* run-time credentials cache. */
115   apr_hash_t *creds_cache;
116
117 };
118
119 /* Abstracted iteration baton */
120 struct svn_auth_iterstate_t
121 {
122   provider_set_t *table;        /* the table being searched */
123   int provider_idx;             /* the current provider (row) */
124   svn_boolean_t got_first;      /* did we get the provider's first creds? */
125   void *provider_iter_baton;    /* the provider's own iteration context */
126   const char *realmstring;      /* The original realmstring passed in */
127   const char *cache_key;        /* key to use in auth_baton's creds_cache */
128   svn_auth_baton_t *auth_baton; /* the original auth_baton. */
129 };
130
131
132
133 void
134 svn_auth_open(svn_auth_baton_t **auth_baton,
135               const apr_array_header_t *providers,
136               apr_pool_t *pool)
137 {
138   svn_auth_baton_t *ab;
139   svn_auth_provider_object_t *provider;
140   int i;
141
142   /* Build the auth_baton. */
143   ab = apr_pcalloc(pool, sizeof(*ab));
144   ab->tables = apr_hash_make(pool);
145   ab->parameters = apr_hash_make(pool);
146   ab->creds_cache = apr_hash_make(pool);
147   ab->pool = pool;
148
149   /* Register each provider in order.  Providers of different
150      credentials will be automatically sorted into different tables by
151      register_provider(). */
152   for (i = 0; i < providers->nelts; i++)
153     {
154       provider_set_t *table;
155       provider = APR_ARRAY_IDX(providers, i, svn_auth_provider_object_t *);
156
157       /* Add it to the appropriate table in the auth_baton */
158       table = svn_hash_gets(ab->tables, provider->vtable->cred_kind);
159       if (! table)
160         {
161           table = apr_pcalloc(pool, sizeof(*table));
162           table->providers
163             = apr_array_make(pool, 1, sizeof(svn_auth_provider_object_t *));
164
165           svn_hash_sets(ab->tables, provider->vtable->cred_kind, table);
166         }
167       APR_ARRAY_PUSH(table->providers, svn_auth_provider_object_t *)
168         = provider;
169     }
170
171   *auth_baton = ab;
172 }
173
174
175
176 void
177 svn_auth_set_parameter(svn_auth_baton_t *auth_baton,
178                        const char *name,
179                        const void *value)
180 {
181   svn_hash_sets(auth_baton->parameters, name, value);
182 }
183
184 const void *
185 svn_auth_get_parameter(svn_auth_baton_t *auth_baton,
186                        const char *name)
187 {
188   return svn_hash_gets(auth_baton->parameters, name);
189 }
190
191
192 /* Return the key used to address the in-memory cache of auth
193    credentials of type CRED_KIND and associated with REALMSTRING. */
194 static const char *
195 make_cache_key(const char *cred_kind,
196                const char *realmstring,
197                apr_pool_t *pool)
198 {
199   return apr_pstrcat(pool, cred_kind, ":", realmstring, (char *)NULL);
200 }
201
202 svn_error_t *
203 svn_auth_first_credentials(void **credentials,
204                            svn_auth_iterstate_t **state,
205                            const char *cred_kind,
206                            const char *realmstring,
207                            svn_auth_baton_t *auth_baton,
208                            apr_pool_t *pool)
209 {
210   int i = 0;
211   provider_set_t *table;
212   svn_auth_provider_object_t *provider = NULL;
213   void *creds = NULL;
214   void *iter_baton = NULL;
215   svn_boolean_t got_first = FALSE;
216   svn_auth_iterstate_t *iterstate;
217   const char *cache_key;
218
219   /* Get the appropriate table of providers for CRED_KIND. */
220   table = svn_hash_gets(auth_baton->tables, cred_kind);
221   if (! table)
222     return svn_error_createf(SVN_ERR_AUTHN_NO_PROVIDER, NULL,
223                              _("No provider registered for '%s' credentials"),
224                              cred_kind);
225
226   /* First, see if we have cached creds in the auth_baton. */
227   cache_key = make_cache_key(cred_kind, realmstring, pool);
228   creds = svn_hash_gets(auth_baton->creds_cache, cache_key);
229   if (creds)
230     {
231        got_first = FALSE;
232     }
233   else
234     /* If not, find a provider that can give "first" credentials. */
235     {
236       /* Find a provider that can give "first" credentials. */
237       for (i = 0; i < table->providers->nelts; i++)
238         {
239           provider = APR_ARRAY_IDX(table->providers, i,
240                                    svn_auth_provider_object_t *);
241           SVN_ERR(provider->vtable->first_credentials(&creds, &iter_baton,
242                                                       provider->provider_baton,
243                                                       auth_baton->parameters,
244                                                       realmstring,
245                                                       auth_baton->pool));
246
247           if (creds != NULL)
248             {
249               got_first = TRUE;
250               break;
251             }
252         }
253     }
254
255   if (! creds)
256     *state = NULL;
257   else
258     {
259       /* Build an abstract iteration state. */
260       iterstate = apr_pcalloc(pool, sizeof(*iterstate));
261       iterstate->table = table;
262       iterstate->provider_idx = i;
263       iterstate->got_first = got_first;
264       iterstate->provider_iter_baton = iter_baton;
265       iterstate->realmstring = apr_pstrdup(pool, realmstring);
266       iterstate->cache_key = cache_key;
267       iterstate->auth_baton = auth_baton;
268       *state = iterstate;
269
270       /* Put the creds in the cache */
271       svn_hash_sets(auth_baton->creds_cache,
272                     apr_pstrdup(auth_baton->pool, cache_key),
273                     creds);
274     }
275
276   *credentials = creds;
277
278   return SVN_NO_ERROR;
279 }
280
281
282 svn_error_t *
283 svn_auth_next_credentials(void **credentials,
284                           svn_auth_iterstate_t *state,
285                           apr_pool_t *pool)
286 {
287   svn_auth_baton_t *auth_baton = state->auth_baton;
288   svn_auth_provider_object_t *provider;
289   provider_set_t *table = state->table;
290   void *creds = NULL;
291
292   /* Continue traversing the table from where we left off. */
293   for (/* no init */;
294        state->provider_idx < table->providers->nelts;
295        state->provider_idx++)
296     {
297       provider = APR_ARRAY_IDX(table->providers,
298                                state->provider_idx,
299                                svn_auth_provider_object_t *);
300       if (! state->got_first)
301         {
302           SVN_ERR(provider->vtable->first_credentials(
303                       &creds, &(state->provider_iter_baton),
304                       provider->provider_baton, auth_baton->parameters,
305                       state->realmstring, auth_baton->pool));
306           state->got_first = TRUE;
307         }
308       else if (provider->vtable->next_credentials)
309         {
310           SVN_ERR(provider->vtable->next_credentials(
311                       &creds, state->provider_iter_baton,
312                       provider->provider_baton, auth_baton->parameters,
313                       state->realmstring, auth_baton->pool));
314         }
315
316       if (creds != NULL)
317         {
318           /* Put the creds in the cache */
319           svn_hash_sets(auth_baton->creds_cache, state->cache_key, creds);
320           break;
321         }
322
323       state->got_first = FALSE;
324     }
325
326   *credentials = creds;
327
328   return SVN_NO_ERROR;
329 }
330
331
332 svn_error_t *
333 svn_auth_save_credentials(svn_auth_iterstate_t *state,
334                           apr_pool_t *pool)
335 {
336   int i;
337   svn_auth_provider_object_t *provider;
338   svn_boolean_t save_succeeded = FALSE;
339   const char *no_auth_cache;
340   svn_auth_baton_t *auth_baton;
341   void *creds;
342
343   if (! state || state->table->providers->nelts <= state->provider_idx)
344     return SVN_NO_ERROR;
345
346   auth_baton = state->auth_baton;
347   creds = svn_hash_gets(state->auth_baton->creds_cache, state->cache_key);
348   if (! creds)
349     return SVN_NO_ERROR;
350
351   /* Do not save the creds if SVN_AUTH_PARAM_NO_AUTH_CACHE is set */
352   no_auth_cache = svn_hash_gets(auth_baton->parameters,
353                                 SVN_AUTH_PARAM_NO_AUTH_CACHE);
354   if (no_auth_cache)
355     return SVN_NO_ERROR;
356
357   /* First, try to save the creds using the provider that produced them. */
358   provider = APR_ARRAY_IDX(state->table->providers,
359                            state->provider_idx,
360                            svn_auth_provider_object_t *);
361   if (provider->vtable->save_credentials)
362     SVN_ERR(provider->vtable->save_credentials(&save_succeeded,
363                                                creds,
364                                                provider->provider_baton,
365                                                auth_baton->parameters,
366                                                state->realmstring,
367                                                pool));
368   if (save_succeeded)
369     return SVN_NO_ERROR;
370
371   /* Otherwise, loop from the top of the list, asking every provider
372      to attempt a save.  ### todo: someday optimize so we don't
373      necessarily start from the top of the list. */
374   for (i = 0; i < state->table->providers->nelts; i++)
375     {
376       provider = APR_ARRAY_IDX(state->table->providers, i,
377                                svn_auth_provider_object_t *);
378       if (provider->vtable->save_credentials)
379         SVN_ERR(provider->vtable->save_credentials
380                 (&save_succeeded, creds,
381                  provider->provider_baton,
382                  auth_baton->parameters,
383                  state->realmstring,
384                  pool));
385
386       if (save_succeeded)
387         break;
388     }
389
390   /* ### notice that at the moment, if no provider can save, there's
391      no way the caller will know. */
392
393   return SVN_NO_ERROR;
394 }
395
396
397 svn_error_t *
398 svn_auth_forget_credentials(svn_auth_baton_t *auth_baton,
399                             const char *cred_kind,
400                             const char *realmstring,
401                             apr_pool_t *scratch_pool)
402 {
403   SVN_ERR_ASSERT((cred_kind && realmstring) || (!cred_kind && !realmstring));
404
405   /* If we have a CRED_KIND and REALMSTRING, we clear out just the
406      cached item (if any).  Otherwise, empty the whole hash. */
407   if (cred_kind)
408     {
409       svn_hash_sets(auth_baton->creds_cache,
410                     make_cache_key(cred_kind, realmstring, scratch_pool),
411                     NULL);
412     }
413   else
414     {
415       apr_hash_clear(auth_baton->creds_cache);
416     }
417
418   return SVN_NO_ERROR;
419 }
420
421
422 svn_auth_ssl_server_cert_info_t *
423 svn_auth_ssl_server_cert_info_dup
424   (const svn_auth_ssl_server_cert_info_t *info, apr_pool_t *pool)
425 {
426   svn_auth_ssl_server_cert_info_t *new_info
427     = apr_palloc(pool, sizeof(*new_info));
428
429   *new_info = *info;
430
431   new_info->hostname = apr_pstrdup(pool, new_info->hostname);
432   new_info->fingerprint = apr_pstrdup(pool, new_info->fingerprint);
433   new_info->valid_from = apr_pstrdup(pool, new_info->valid_from);
434   new_info->valid_until = apr_pstrdup(pool, new_info->valid_until);
435   new_info->issuer_dname = apr_pstrdup(pool, new_info->issuer_dname);
436   new_info->ascii_cert = apr_pstrdup(pool, new_info->ascii_cert);
437
438   return new_info;
439 }
440
441 svn_error_t *
442 svn_auth_get_platform_specific_provider(svn_auth_provider_object_t **provider,
443                                         const char *provider_name,
444                                         const char *provider_type,
445                                         apr_pool_t *pool)
446 {
447   *provider = NULL;
448
449   if (apr_strnatcmp(provider_name, "gnome_keyring") == 0 ||
450       apr_strnatcmp(provider_name, "kwallet") == 0)
451     {
452 #if defined(SVN_HAVE_GNOME_KEYRING) || defined(SVN_HAVE_KWALLET)
453       apr_dso_handle_t *dso;
454       apr_dso_handle_sym_t provider_function_symbol, version_function_symbol;
455       const char *library_label, *library_name;
456       const char *provider_function_name, *version_function_name;
457       library_name = apr_psprintf(pool,
458                                   "libsvn_auth_%s-%d.so.%d",
459                                   provider_name,
460                                   SVN_VER_MAJOR, SVN_SOVERSION);
461       library_label = apr_psprintf(pool, "svn_%s", provider_name);
462       provider_function_name = apr_psprintf(pool,
463                                             "svn_auth_get_%s_%s_provider",
464                                             provider_name, provider_type);
465       version_function_name = apr_psprintf(pool,
466                                            "svn_auth_%s_version",
467                                            provider_name);
468       SVN_ERR(svn_dso_load(&dso, library_name));
469       if (dso)
470         {
471           if (apr_dso_sym(&version_function_symbol,
472                           dso,
473                           version_function_name) == 0)
474             {
475               svn_version_func_t version_function
476                 = version_function_symbol;
477               svn_version_checklist_t check_list[2];
478
479               check_list[0].label = library_label;
480               check_list[0].version_query = version_function;
481               check_list[1].label = NULL;
482               check_list[1].version_query = NULL;
483               SVN_ERR(svn_ver_check_list2(svn_subr_version(), check_list,
484                                           svn_ver_equal));
485             }
486           if (apr_dso_sym(&provider_function_symbol,
487                           dso,
488                           provider_function_name) == 0)
489             {
490               if (strcmp(provider_type, "simple") == 0)
491                 {
492                   svn_auth_simple_provider_func_t provider_function
493                     = provider_function_symbol;
494                   provider_function(provider, pool);
495                 }
496               else if (strcmp(provider_type, "ssl_client_cert_pw") == 0)
497                 {
498                   svn_auth_ssl_client_cert_pw_provider_func_t provider_function
499                     = provider_function_symbol;
500                   provider_function(provider, pool);
501                 }
502             }
503         }
504 #endif
505     }
506   else
507     {
508 #if defined(SVN_HAVE_GPG_AGENT)
509       if (strcmp(provider_name, "gpg_agent") == 0 &&
510           strcmp(provider_type, "simple") == 0)
511         {
512           svn_auth_get_gpg_agent_simple_provider(provider, pool);
513         }
514 #endif
515 #ifdef SVN_HAVE_KEYCHAIN_SERVICES
516       if (strcmp(provider_name, "keychain") == 0 &&
517           strcmp(provider_type, "simple") == 0)
518         {
519           svn_auth_get_keychain_simple_provider(provider, pool);
520         }
521       else if (strcmp(provider_name, "keychain") == 0 &&
522                strcmp(provider_type, "ssl_client_cert_pw") == 0)
523         {
524           svn_auth_get_keychain_ssl_client_cert_pw_provider(provider, pool);
525         }
526 #endif
527
528 #if defined(WIN32) && !defined(__MINGW32__)
529       if (strcmp(provider_name, "windows") == 0 &&
530           strcmp(provider_type, "simple") == 0)
531         {
532           svn_auth_get_windows_simple_provider(provider, pool);
533         }
534       else if (strcmp(provider_name, "windows") == 0 &&
535                strcmp(provider_type, "ssl_client_cert_pw") == 0)
536         {
537           svn_auth_get_windows_ssl_client_cert_pw_provider(provider, pool);
538         }
539       else if (strcmp(provider_name, "windows") == 0 &&
540                strcmp(provider_type, "ssl_server_trust") == 0)
541         {
542           svn_auth_get_windows_ssl_server_trust_provider(provider, pool);
543         }
544       else if (strcmp(provider_name, "windows") == 0 &&
545           strcmp(provider_type, "ssl_server_authority") == 0)
546         {
547           svn_auth__get_windows_ssl_server_authority_provider(provider, pool);
548         }
549 #endif
550     }
551
552   return SVN_NO_ERROR;
553 }
554
555 svn_error_t *
556 svn_auth_get_platform_specific_client_providers(apr_array_header_t **providers,
557                                                 svn_config_t *config,
558                                                 apr_pool_t *pool)
559 {
560   svn_auth_provider_object_t *provider;
561   const char *password_stores_config_option;
562   apr_array_header_t *password_stores;
563   int i;
564
565 #define SVN__MAYBE_ADD_PROVIDER(list, p) \
566   { if (p) APR_ARRAY_PUSH(list, svn_auth_provider_object_t *) = p; }
567
568 #define SVN__DEFAULT_AUTH_PROVIDER_LIST \
569          "gnome-keyring,kwallet,keychain,gpg-agent,windows-cryptoapi"
570
571   *providers = apr_array_make(pool, 12, sizeof(svn_auth_provider_object_t *));
572
573   /* Fetch the configured list of password stores, and split them into
574      an array. */
575   svn_config_get(config,
576                  &password_stores_config_option,
577                  SVN_CONFIG_SECTION_AUTH,
578                  SVN_CONFIG_OPTION_PASSWORD_STORES,
579                  SVN__DEFAULT_AUTH_PROVIDER_LIST);
580   password_stores = svn_cstring_split(password_stores_config_option,
581                                       " ,", TRUE, pool);
582
583   for (i = 0; i < password_stores->nelts; i++)
584     {
585       const char *password_store = APR_ARRAY_IDX(password_stores, i,
586                                                  const char *);
587
588       /* GNOME Keyring */
589       if (apr_strnatcmp(password_store, "gnome-keyring") == 0)
590         {
591           SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
592                                                           "gnome_keyring",
593                                                           "simple",
594                                                           pool));
595           SVN__MAYBE_ADD_PROVIDER(*providers, provider);
596
597           SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
598                                                           "gnome_keyring",
599                                                           "ssl_client_cert_pw",
600                                                           pool));
601           SVN__MAYBE_ADD_PROVIDER(*providers, provider);
602         }
603       /* GPG-AGENT */
604       else if (apr_strnatcmp(password_store, "gpg-agent") == 0)
605         {
606           SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
607                                                           "gpg_agent",
608                                                           "simple",
609                                                           pool));
610           SVN__MAYBE_ADD_PROVIDER(*providers, provider);
611         }
612       /* KWallet */
613       else if (apr_strnatcmp(password_store, "kwallet") == 0)
614         {
615           SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
616                                                           "kwallet",
617                                                           "simple",
618                                                           pool));
619           SVN__MAYBE_ADD_PROVIDER(*providers, provider);
620
621           SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
622                                                           "kwallet",
623                                                           "ssl_client_cert_pw",
624                                                           pool));
625           SVN__MAYBE_ADD_PROVIDER(*providers, provider);
626         }
627       /* Keychain */
628       else if (apr_strnatcmp(password_store, "keychain") == 0)
629         {
630           SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
631                                                           "keychain",
632                                                           "simple",
633                                                           pool));
634           SVN__MAYBE_ADD_PROVIDER(*providers, provider);
635
636           SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
637                                                           "keychain",
638                                                           "ssl_client_cert_pw",
639                                                           pool));
640           SVN__MAYBE_ADD_PROVIDER(*providers, provider);
641         }
642       /* Windows */
643       else if (apr_strnatcmp(password_store, "windows-cryptoapi") == 0)
644         {
645           SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
646                                                           "windows",
647                                                           "simple",
648                                                           pool));
649           SVN__MAYBE_ADD_PROVIDER(*providers, provider);
650
651           SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
652                                                           "windows",
653                                                           "ssl_client_cert_pw",
654                                                           pool));
655           SVN__MAYBE_ADD_PROVIDER(*providers, provider);
656         }
657     }
658
659   return SVN_NO_ERROR;
660 }