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