1 /*
2 * username_providers.c: providers for SVN_AUTH_CRED_USERNAME
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 /* ==================================================================== */
25
26
27
28 /*** Includes. ***/
29
30 #include <apr_pools.h>
31 #include "svn_hash.h"
32 #include "svn_auth.h"
33 #include "svn_error.h"
34 #include "svn_utf.h"
35 #include "svn_config.h"
36 #include "svn_user.h"
37
38
39 /*-----------------------------------------------------------------------*/
40 /* File provider */
41 /*-----------------------------------------------------------------------*/
42
43 /*** Username-only Provider ***/
44 static svn_error_t *
username_first_creds(void ** credentials,void ** iter_baton,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)45 username_first_creds(void **credentials,
46 void **iter_baton,
47 void *provider_baton,
48 apr_hash_t *parameters,
49 const char *realmstring,
50 apr_pool_t *pool)
51 {
52 const char *config_dir = svn_hash_gets(parameters,
53 SVN_AUTH_PARAM_CONFIG_DIR);
54 const char *username = svn_hash_gets(parameters,
55 SVN_AUTH_PARAM_DEFAULT_USERNAME);
56 svn_boolean_t may_save = !! username;
57 svn_error_t *err;
58
59 /* If we don't have a usename yet, try the auth cache */
60 if (! username)
61 {
62 apr_hash_t *creds_hash = NULL;
63
64 /* Try to load credentials from a file on disk, based on the
65 realmstring. Don't throw an error, though: if something went
66 wrong reading the file, no big deal. What really matters is that
67 we failed to get the creds, so allow the auth system to try the
68 next provider. */
69 err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_USERNAME,
70 realmstring, config_dir, pool);
71 svn_error_clear(err);
72 if (! err && creds_hash)
73 {
74 svn_string_t *str = svn_hash_gets(creds_hash,
75 SVN_CONFIG_AUTHN_USERNAME_KEY);
76 if (str && str->data)
77 username = str->data;
78 }
79 }
80
81 /* If that failed, ask the OS for the username */
82 if (! username)
83 username = svn_user_get_name(pool);
84
85 if (username)
86 {
87 svn_auth_cred_simple_t *creds = apr_pcalloc(pool, sizeof(*creds));
88 creds->username = username;
89 creds->may_save = may_save;
90 *credentials = creds;
91 }
92 else
93 *credentials = NULL;
94
95 *iter_baton = NULL;
96
97 return SVN_NO_ERROR;
98 }
99
100
101 static svn_error_t *
username_save_creds(svn_boolean_t * saved,void * credentials,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)102 username_save_creds(svn_boolean_t *saved,
103 void *credentials,
104 void *provider_baton,
105 apr_hash_t *parameters,
106 const char *realmstring,
107 apr_pool_t *pool)
108 {
109 svn_auth_cred_simple_t *creds = credentials;
110 apr_hash_t *creds_hash = NULL;
111 const char *config_dir;
112 svn_error_t *err;
113
114 *saved = FALSE;
115
116 if (! creds->may_save)
117 return SVN_NO_ERROR;
118
119 config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR);
120
121 /* Put the credentials in a hash and save it to disk */
122 creds_hash = apr_hash_make(pool);
123 svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_USERNAME_KEY,
124 svn_string_create(creds->username, pool));
125 err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_USERNAME,
126 realmstring, config_dir, pool);
127 svn_error_clear(err);
128 *saved = ! err;
129
130 return SVN_NO_ERROR;
131 }
132
133
134 static const svn_auth_provider_t username_provider = {
135 SVN_AUTH_CRED_USERNAME,
136 username_first_creds,
137 NULL,
138 username_save_creds
139 };
140
141
142 /* Public API */
143 void
svn_auth_get_username_provider(svn_auth_provider_object_t ** provider,apr_pool_t * pool)144 svn_auth_get_username_provider(svn_auth_provider_object_t **provider,
145 apr_pool_t *pool)
146 {
147 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
148
149 po->vtable = &username_provider;
150 *provider = po;
151 }
152
153
154 /*-----------------------------------------------------------------------*/
155 /* Prompt provider */
156 /*-----------------------------------------------------------------------*/
157
158 /* Baton type for username-only prompting. */
159 typedef struct username_prompt_provider_baton_t
160 {
161 svn_auth_username_prompt_func_t prompt_func;
162 void *prompt_baton;
163
164 /* how many times to re-prompt after the first one fails */
165 int retry_limit;
166 } username_prompt_provider_baton_t;
167
168
169 /* Iteration baton type for username-only prompting. */
170 typedef struct username_prompt_iter_baton_t
171 {
172 /* how many times we've reprompted */
173 int retries;
174
175 } username_prompt_iter_baton_t;
176
177
178 /*** Helper Functions ***/
179 static svn_error_t *
prompt_for_username_creds(svn_auth_cred_username_t ** cred_p,username_prompt_provider_baton_t * pb,apr_hash_t * parameters,const char * realmstring,svn_boolean_t first_time,svn_boolean_t may_save,apr_pool_t * pool)180 prompt_for_username_creds(svn_auth_cred_username_t **cred_p,
181 username_prompt_provider_baton_t *pb,
182 apr_hash_t *parameters,
183 const char *realmstring,
184 svn_boolean_t first_time,
185 svn_boolean_t may_save,
186 apr_pool_t *pool)
187 {
188 const char *def_username = NULL;
189
190 *cred_p = NULL;
191
192 /* If we're allowed to check for default usernames, do so. */
193 if (first_time)
194 def_username = svn_hash_gets(parameters, SVN_AUTH_PARAM_DEFAULT_USERNAME);
195
196 /* If we have defaults, just build the cred here and return it.
197 *
198 * ### I do wonder why this is here instead of in a separate
199 * ### 'defaults' provider that would run before the prompt
200 * ### provider... Hmmm.
201 */
202 if (def_username)
203 {
204 *cred_p = apr_palloc(pool, sizeof(**cred_p));
205 (*cred_p)->username = apr_pstrdup(pool, def_username);
206 (*cred_p)->may_save = TRUE;
207 }
208 else
209 {
210 SVN_ERR(pb->prompt_func(cred_p, pb->prompt_baton, realmstring,
211 may_save, pool));
212 }
213
214 return SVN_NO_ERROR;
215 }
216
217
218 /* Our first attempt will use any default username passed
219 in, and prompt for the remaining stuff. */
220 static svn_error_t *
username_prompt_first_creds(void ** credentials_p,void ** iter_baton,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)221 username_prompt_first_creds(void **credentials_p,
222 void **iter_baton,
223 void *provider_baton,
224 apr_hash_t *parameters,
225 const char *realmstring,
226 apr_pool_t *pool)
227 {
228 username_prompt_provider_baton_t *pb = provider_baton;
229 username_prompt_iter_baton_t *ibaton = apr_pcalloc(pool, sizeof(*ibaton));
230 const char *no_auth_cache = svn_hash_gets(parameters,
231 SVN_AUTH_PARAM_NO_AUTH_CACHE);
232
233 SVN_ERR(prompt_for_username_creds
234 ((svn_auth_cred_username_t **) credentials_p, pb,
235 parameters, realmstring, TRUE, ! no_auth_cache, pool));
236
237 ibaton->retries = 0;
238 *iter_baton = ibaton;
239
240 return SVN_NO_ERROR;
241 }
242
243
244 /* Subsequent attempts to fetch will ignore the default username
245 value, and simply re-prompt for the username, up to a maximum of
246 ib->pb->retry_limit. */
247 static svn_error_t *
username_prompt_next_creds(void ** credentials_p,void * iter_baton,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)248 username_prompt_next_creds(void **credentials_p,
249 void *iter_baton,
250 void *provider_baton,
251 apr_hash_t *parameters,
252 const char *realmstring,
253 apr_pool_t *pool)
254 {
255 username_prompt_iter_baton_t *ib = iter_baton;
256 username_prompt_provider_baton_t *pb = provider_baton;
257 const char *no_auth_cache = svn_hash_gets(parameters,
258 SVN_AUTH_PARAM_NO_AUTH_CACHE);
259
260 if ((pb->retry_limit >= 0) && (ib->retries >= pb->retry_limit))
261 {
262 /* give up, go on to next provider. */
263 *credentials_p = NULL;
264 return SVN_NO_ERROR;
265 }
266 ib->retries++;
267
268 return prompt_for_username_creds
269 ((svn_auth_cred_username_t **) credentials_p, pb,
270 parameters, realmstring, FALSE, ! no_auth_cache, pool);
271 }
272
273
274 static const svn_auth_provider_t username_prompt_provider = {
275 SVN_AUTH_CRED_USERNAME,
276 username_prompt_first_creds,
277 username_prompt_next_creds,
278 NULL,
279 };
280
281
282 /* Public API */
283 void
svn_auth_get_username_prompt_provider(svn_auth_provider_object_t ** provider,svn_auth_username_prompt_func_t prompt_func,void * prompt_baton,int retry_limit,apr_pool_t * pool)284 svn_auth_get_username_prompt_provider
285 (svn_auth_provider_object_t **provider,
286 svn_auth_username_prompt_func_t prompt_func,
287 void *prompt_baton,
288 int retry_limit,
289 apr_pool_t *pool)
290 {
291 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
292 username_prompt_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
293
294 pb->prompt_func = prompt_func;
295 pb->prompt_baton = prompt_baton;
296 pb->retry_limit = retry_limit;
297
298 po->vtable = &username_prompt_provider;
299 po->provider_baton = pb;
300 *provider = po;
301 }
302