2 * auth.c: authentication support functions for Subversion
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
20 #include <apr_pools.h>
21 #include <apr_tables.h>
22 #include <apr_strings.h>
24 #include "svn_types.h"
25 #include "svn_error.h"
28 /* The good way to think of this machinery is as a set of tables.
30 - Each type of credentials selects a single table.
32 - In a given table, each row is a 'provider' capable of returning
33 the same type of credentials. Each column represents a
34 provider's repeated attempts to provide credentials.
36 When the caller asks for a particular type of credentials, the
37 machinery in this file walks over the appropriate table. It starts
38 with the first provider (first row), and calls first_credentials()
39 to get the first set of credentials (first column). If the caller
40 is unhappy with the credentials, then each subsequent call to
41 next_credentials() traverses the row from left to right. If the
42 provider returns error at any point, then we go to the next provider
43 (row). We continue this way until every provider fails, or
44 until the client is happy with the returned credentials.
46 Note that the caller cannot see the table traversal, and thus has
47 no idea when we switch providers.
52 /* This effectively defines a single table. Every provider in this
53 array returns the same kind of credentials. */
56 /* ordered array of svn_auth_provider_object_t */
57 apr_array_header_t
*providers
;
62 /* The main auth baton. */
63 struct svn_auth_baton_t
65 /* a collection of tables. maps cred_kind -> provider_set */
68 /* the pool I'm allocated in. */
71 /* run-time parameters needed by providers. */
72 apr_hash_t
*parameters
;
74 /* run-time credentials cache. */
75 apr_hash_t
*creds_cache
;
79 /* Abstracted iteration baton */
80 struct svn_auth_iterstate_t
82 provider_set_t
*table
; /* the table being searched */
83 int provider_idx
; /* the current provider (row) */
84 svn_boolean_t got_first
; /* did we get the provider's first creds? */
85 void *provider_iter_baton
; /* the provider's own iteration context */
86 const char *realmstring
; /* The original realmstring passed in */
87 const char *cache_key
; /* key to use in auth_baton's creds_cache */
88 svn_auth_baton_t
*auth_baton
; /* the original auth_baton. */
94 svn_auth_open(svn_auth_baton_t
**auth_baton
,
95 apr_array_header_t
*providers
,
99 svn_auth_provider_object_t
*provider
;
102 /* Build the auth_baton. */
103 ab
= apr_pcalloc(pool
, sizeof(*ab
));
104 ab
->tables
= apr_hash_make(pool
);
105 ab
->parameters
= apr_hash_make(pool
);
106 ab
->creds_cache
= apr_hash_make(pool
);
109 /* Register each provider in order. Providers of different
110 credentials will be automatically sorted into different tables by
111 register_provider(). */
112 for (i
= 0; i
< providers
->nelts
; i
++)
114 provider_set_t
*table
;
115 provider
= APR_ARRAY_IDX(providers
, i
, svn_auth_provider_object_t
*);
117 /* Add it to the appropriate table in the auth_baton */
118 table
= apr_hash_get(ab
->tables
,
119 provider
->vtable
->cred_kind
, APR_HASH_KEY_STRING
);
122 table
= apr_pcalloc(pool
, sizeof(*table
));
124 = apr_array_make(pool
, 1, sizeof(svn_auth_provider_object_t
*));
126 apr_hash_set(ab
->tables
,
127 provider
->vtable
->cred_kind
, APR_HASH_KEY_STRING
,
130 APR_ARRAY_PUSH(table
->providers
, svn_auth_provider_object_t
*)
140 svn_auth_set_parameter(svn_auth_baton_t
*auth_baton
,
144 apr_hash_set(auth_baton
->parameters
, name
, APR_HASH_KEY_STRING
, value
);
148 svn_auth_get_parameter(svn_auth_baton_t
*auth_baton
,
151 return apr_hash_get(auth_baton
->parameters
, name
, APR_HASH_KEY_STRING
);
157 svn_auth_first_credentials(void **credentials
,
158 svn_auth_iterstate_t
**state
,
159 const char *cred_kind
,
160 const char *realmstring
,
161 svn_auth_baton_t
*auth_baton
,
165 provider_set_t
*table
;
166 svn_auth_provider_object_t
*provider
= NULL
;
168 void *iter_baton
= NULL
;
169 svn_boolean_t got_first
= FALSE
;
170 svn_auth_iterstate_t
*iterstate
;
171 const char *cache_key
;
173 /* Get the appropriate table of providers for CRED_KIND. */
174 table
= apr_hash_get(auth_baton
->tables
, cred_kind
, APR_HASH_KEY_STRING
);
176 return svn_error_createf(SVN_ERR_AUTHN_NO_PROVIDER
, NULL
,
177 "No provider registered for '%s' credentials",
180 /* First, see if we have cached creds in the auth_baton. */
181 cache_key
= apr_pstrcat(pool
, cred_kind
, ":", realmstring
, NULL
);
182 creds
= apr_hash_get(auth_baton
->creds_cache
,
183 cache_key
, APR_HASH_KEY_STRING
);
189 /* If not, find a provider that can give "first" credentials. */
191 /* Find a provider that can give "first" credentials. */
192 for (i
= 0; i
< table
->providers
->nelts
; i
++)
194 provider
= APR_ARRAY_IDX(table
->providers
, i
,
195 svn_auth_provider_object_t
*);
196 SVN_ERR(provider
->vtable
->first_credentials
197 (&creds
, &iter_baton
, provider
->provider_baton
,
198 auth_baton
->parameters
, realmstring
, auth_baton
->pool
));
212 /* Build an abstract iteration state. */
213 iterstate
= apr_pcalloc(pool
, sizeof(*iterstate
));
214 iterstate
->table
= table
;
215 iterstate
->provider_idx
= i
;
216 iterstate
->got_first
= got_first
;
217 iterstate
->provider_iter_baton
= iter_baton
;
218 iterstate
->realmstring
= apr_pstrdup(pool
, realmstring
);
219 iterstate
->cache_key
= cache_key
;
220 iterstate
->auth_baton
= auth_baton
;
223 /* Put the creds in the cache */
224 apr_hash_set(auth_baton
->creds_cache
,
225 apr_pstrdup(auth_baton
->pool
, cache_key
),
230 *credentials
= creds
;
237 svn_auth_next_credentials(void **credentials
,
238 svn_auth_iterstate_t
*state
,
241 svn_auth_baton_t
*auth_baton
= state
->auth_baton
;
242 svn_auth_provider_object_t
*provider
;
243 provider_set_t
*table
= state
->table
;
246 /* Continue traversing the table from where we left off. */
248 state
->provider_idx
< table
->providers
->nelts
;
249 state
->provider_idx
++)
251 provider
= APR_ARRAY_IDX(table
->providers
,
253 svn_auth_provider_object_t
*);
254 if (! state
->got_first
)
256 SVN_ERR(provider
->vtable
->first_credentials
257 (&creds
, &(state
->provider_iter_baton
),
258 provider
->provider_baton
, auth_baton
->parameters
,
259 state
->realmstring
, auth_baton
->pool
));
260 state
->got_first
= TRUE
;
264 if (provider
->vtable
->next_credentials
)
265 SVN_ERR(provider
->vtable
->next_credentials
266 (&creds
, state
->provider_iter_baton
,
267 provider
->provider_baton
, auth_baton
->parameters
,
268 state
->realmstring
, auth_baton
->pool
));
273 /* Put the creds in the cache */
274 apr_hash_set(auth_baton
->creds_cache
,
275 state
->cache_key
, APR_HASH_KEY_STRING
,
280 state
->got_first
= FALSE
;
283 *credentials
= creds
;
290 svn_auth_save_credentials(svn_auth_iterstate_t
*state
,
294 svn_auth_provider_object_t
*provider
;
295 svn_boolean_t save_succeeded
= FALSE
;
296 const char *no_auth_cache
;
297 svn_auth_baton_t
*auth_baton
;
300 if (! state
|| state
->table
->providers
->nelts
<= state
->provider_idx
)
303 auth_baton
= state
->auth_baton
;
304 creds
= apr_hash_get(state
->auth_baton
->creds_cache
,
305 state
->cache_key
, APR_HASH_KEY_STRING
);
309 /* Do not save the creds if SVN_AUTH_PARAM_NO_AUTH_CACHE is set */
310 no_auth_cache
= apr_hash_get(auth_baton
->parameters
,
311 SVN_AUTH_PARAM_NO_AUTH_CACHE
,
312 APR_HASH_KEY_STRING
);
316 /* First, try to save the creds using the provider that produced them. */
317 provider
= APR_ARRAY_IDX(state
->table
->providers
,
319 svn_auth_provider_object_t
*);
320 if (provider
->vtable
->save_credentials
)
321 SVN_ERR(provider
->vtable
->save_credentials(&save_succeeded
,
323 provider
->provider_baton
,
324 auth_baton
->parameters
,
330 /* Otherwise, loop from the top of the list, asking every provider
331 to attempt a save. ### todo: someday optimize so we don't
332 necessarily start from the top of the list. */
333 for (i
= 0; i
< state
->table
->providers
->nelts
; i
++)
335 provider
= APR_ARRAY_IDX(state
->table
->providers
, i
,
336 svn_auth_provider_object_t
*);
337 if (provider
->vtable
->save_credentials
)
338 SVN_ERR(provider
->vtable
->save_credentials
339 (&save_succeeded
, creds
,
340 provider
->provider_baton
, auth_baton
->parameters
,
341 state
->realmstring
, pool
));
347 /* ### notice that at the moment, if no provider can save, there's
348 no way the caller will know. */
353 svn_auth_ssl_server_cert_info_t
*
354 svn_auth_ssl_server_cert_info_dup
355 (const svn_auth_ssl_server_cert_info_t
*info
, apr_pool_t
*pool
)
357 svn_auth_ssl_server_cert_info_t
*new_info
358 = apr_palloc(pool
, sizeof(*new_info
));
362 new_info
->hostname
= apr_pstrdup(pool
, new_info
->hostname
);
363 new_info
->fingerprint
= apr_pstrdup(pool
, new_info
->fingerprint
);
364 new_info
->valid_from
= apr_pstrdup(pool
, new_info
->valid_from
);
365 new_info
->valid_until
= apr_pstrdup(pool
, new_info
->valid_until
);
366 new_info
->issuer_dname
= apr_pstrdup(pool
, new_info
->issuer_dname
);
367 new_info
->ascii_cert
= apr_pstrdup(pool
, new_info
->ascii_cert
);