]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/svn/auth-cmd.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / svn / auth-cmd.c
1 /*
2  * auth-cmd.c:  Subversion auth creds cache administration
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 /*** Includes. ***/
25
26 #include <apr_general.h>
27 #include <apr_getopt.h>
28 #include <apr_fnmatch.h>
29 #include <apr_tables.h>
30
31 #include "svn_private_config.h"
32
33 #include "svn_private_config.h"
34 #include "svn_pools.h"
35 #include "svn_error.h"
36 #include "svn_opt.h"
37 #include "svn_dirent_uri.h"
38 #include "svn_hash.h"
39 #include "svn_utf.h"
40 #include "svn_cmdline.h"
41 #include "svn_config.h"
42 #include "svn_auth.h"
43 #include "svn_sorts.h"
44 #include "svn_base64.h"
45 #include "svn_x509.h"
46 #include "svn_time.h"
47
48 #include "private/svn_cmdline_private.h"
49 #include "private/svn_token.h"
50 #include "private/svn_sorts_private.h"
51
52 #include "cl.h"
53
54 /* The separator between credentials . */
55 #define SEP_STRING \
56   "------------------------------------------------------------------------\n"
57
58 static svn_error_t *
59 show_cert_failures(const char *failure_string,
60                    apr_pool_t *scratch_pool)
61 {
62   unsigned int failures;
63
64   SVN_ERR(svn_cstring_atoui(&failures, failure_string));
65
66   if (0 == (failures & (SVN_AUTH_SSL_NOTYETVALID | SVN_AUTH_SSL_EXPIRED |
67                         SVN_AUTH_SSL_CNMISMATCH | SVN_AUTH_SSL_UNKNOWNCA |
68                         SVN_AUTH_SSL_OTHER)))
69     return SVN_NO_ERROR;
70
71   SVN_ERR(svn_cmdline_printf(
72             scratch_pool, _("Automatic certificate validity check failed "
73                             "because:\n")));
74
75   if (failures & SVN_AUTH_SSL_NOTYETVALID)
76     SVN_ERR(svn_cmdline_printf(
77               scratch_pool, _("  The certificate is not yet valid.\n")));
78
79   if (failures & SVN_AUTH_SSL_EXPIRED)
80     SVN_ERR(svn_cmdline_printf(
81               scratch_pool, _("  The certificate has expired.\n")));
82
83   if (failures & SVN_AUTH_SSL_CNMISMATCH)
84     SVN_ERR(svn_cmdline_printf(
85               scratch_pool, _("  The certificate's Common Name (hostname) "
86                               "does not match the remote hostname.\n")));
87
88   if (failures & SVN_AUTH_SSL_UNKNOWNCA)
89     SVN_ERR(svn_cmdline_printf(
90               scratch_pool, _("  The certificate issuer is unknown.\n")));
91
92   if (failures & SVN_AUTH_SSL_OTHER)
93     SVN_ERR(svn_cmdline_printf(
94               scratch_pool, _("  Unknown verification failure.\n")));
95
96   return SVN_NO_ERROR;
97 }
98
99
100 /* decodes from format we store certs in for auth creds and
101  * turns parsing errors into warnings if PRINT_WARNING is TRUE
102  * and ignores them otherwise. returns NULL if it couldn't
103  * parse a cert for any reason. */
104 static svn_x509_certinfo_t *
105 parse_certificate(const svn_string_t *ascii_cert,
106                   svn_boolean_t print_warning,
107                   apr_pool_t *result_pool,
108                   apr_pool_t *scratch_pool)
109 {
110   svn_x509_certinfo_t *certinfo;
111   const svn_string_t *der_cert;
112   svn_error_t *err;
113
114   /* Convert header-less PEM to DER by undoing base64 encoding. */
115   der_cert = svn_base64_decode_string(ascii_cert, scratch_pool);
116
117   err = svn_x509_parse_cert(&certinfo, der_cert->data, der_cert->len,
118                             result_pool, scratch_pool);
119   if (err)
120     {
121       /* Just display X.509 parsing errors as warnings and continue */
122       if (print_warning)
123         svn_handle_warning2(stderr, err, "svn: ");
124       svn_error_clear(err);
125       return NULL;
126     }
127
128   return certinfo;
129 }
130
131
132 struct walk_credentials_baton_t
133 {
134   int matches;
135   svn_boolean_t list;
136   svn_boolean_t delete;
137   svn_boolean_t show_passwords;
138   apr_array_header_t *patterns;
139 };
140
141 static svn_boolean_t
142 match_pattern(const char *pattern, const char *value,
143               svn_boolean_t caseblind, apr_pool_t *scratch_pool)
144 {
145   const char *p = apr_psprintf(scratch_pool, "*%s*", pattern);
146   int flags = (caseblind ? APR_FNM_CASE_BLIND : 0);
147
148   return (apr_fnmatch(p, value, flags) == APR_SUCCESS);
149 }
150
151 static svn_boolean_t
152 match_certificate(svn_x509_certinfo_t **certinfo,
153                   const char *pattern,
154                   const svn_string_t *ascii_cert,
155                   apr_pool_t *result_pool,
156                   apr_pool_t *scratch_pool)
157 {
158   const char *value;
159   const svn_checksum_t *checksum;
160   const apr_array_header_t *hostnames;
161   int i;
162
163   *certinfo = parse_certificate(ascii_cert, FALSE, result_pool, scratch_pool);
164   if (*certinfo == NULL)
165     return FALSE;
166
167   value = svn_x509_certinfo_get_subject(*certinfo, scratch_pool);
168   if (match_pattern(pattern, value, FALSE, scratch_pool))
169     return TRUE;
170
171   value = svn_x509_certinfo_get_issuer(*certinfo, scratch_pool);
172   if (match_pattern(pattern, value, FALSE, scratch_pool))
173     return TRUE;
174
175   checksum = svn_x509_certinfo_get_digest(*certinfo);
176   value = svn_checksum_to_cstring_display(checksum, scratch_pool);
177   if (match_pattern(pattern, value, TRUE, scratch_pool))
178     return TRUE;
179
180   hostnames = svn_x509_certinfo_get_hostnames(*certinfo);
181   if (hostnames)
182     {
183       for (i = 0; i < hostnames->nelts; i++)
184         {
185           const char *hostname = APR_ARRAY_IDX(hostnames, i, const char *);
186           if (match_pattern(pattern, hostname, TRUE, scratch_pool))
187             return TRUE;
188         }
189     }
190
191   return FALSE;
192 }
193
194
195 static svn_error_t *
196 match_credential(svn_boolean_t *match,
197                  svn_x509_certinfo_t **certinfo,
198                  const char *cred_kind,
199                  const char *realmstring,
200                  apr_array_header_t *patterns,
201                  apr_array_header_t *cred_items,
202                  apr_pool_t *result_pool,
203                  apr_pool_t *scratch_pool)
204 {
205   int i;
206   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
207
208   *match = FALSE;
209
210   for (i = 0; i < patterns->nelts; i++)
211     {
212       const char *pattern = APR_ARRAY_IDX(patterns, i, const char *);
213       int j;
214
215       *match = match_pattern(pattern, cred_kind, FALSE, iterpool);
216       if (!*match)
217         *match = match_pattern(pattern, realmstring, FALSE, iterpool);
218       if (!*match)
219         {
220           svn_pool_clear(iterpool);
221           for (j = 0; j < cred_items->nelts; j++)
222             {
223               svn_sort__item_t item;
224               const char *key;
225               svn_string_t *value;
226
227               item = APR_ARRAY_IDX(cred_items, j, svn_sort__item_t);
228               key = item.key;
229               value = item.value;
230               if (strcmp(key, SVN_CONFIG_AUTHN_PASSWORD_KEY) == 0 ||
231                   strcmp(key, SVN_CONFIG_AUTHN_PASSPHRASE_KEY) == 0)
232                 continue; /* don't match secrets */
233               else if (strcmp(key, SVN_CONFIG_AUTHN_ASCII_CERT_KEY) == 0)
234                 *match = match_certificate(certinfo, pattern, value,
235                                            result_pool, iterpool);
236               else
237                 *match = match_pattern(pattern, value->data, FALSE, iterpool);
238
239               if (*match)
240                 break;
241             }
242         }
243       if (!*match)
244         break;
245     }
246
247   return SVN_NO_ERROR;
248 }
249
250 static svn_error_t *
251 show_cert(svn_x509_certinfo_t *certinfo, const svn_string_t *pem_cert,
252           apr_pool_t *scratch_pool)
253 {
254   const apr_array_header_t *hostnames;
255
256   if (certinfo == NULL)
257     certinfo = parse_certificate(pem_cert, TRUE, scratch_pool, scratch_pool);
258   if (certinfo == NULL)
259     return SVN_NO_ERROR;
260
261   SVN_ERR(svn_cmdline_printf(scratch_pool, _("Subject: %s\n"),
262                              svn_x509_certinfo_get_subject(certinfo, scratch_pool)));
263   SVN_ERR(svn_cmdline_printf(scratch_pool, _("Valid from: %s\n"),
264                              svn_time_to_human_cstring(
265                                  svn_x509_certinfo_get_valid_from(certinfo),
266                                  scratch_pool)));
267   SVN_ERR(svn_cmdline_printf(scratch_pool, _("Valid until: %s\n"),
268                              svn_time_to_human_cstring(
269                                  svn_x509_certinfo_get_valid_to(certinfo),
270                                  scratch_pool)));
271   SVN_ERR(svn_cmdline_printf(scratch_pool, _("Issuer: %s\n"),
272                              svn_x509_certinfo_get_issuer(certinfo, scratch_pool)));
273   SVN_ERR(svn_cmdline_printf(scratch_pool, _("Fingerprint: %s\n"),
274                              svn_checksum_to_cstring_display(
275                                  svn_x509_certinfo_get_digest(certinfo),
276                                  scratch_pool)));
277
278   hostnames = svn_x509_certinfo_get_hostnames(certinfo);
279   if (hostnames && !apr_is_empty_array(hostnames))
280     {
281       int i;
282       svn_stringbuf_t *buf = svn_stringbuf_create_empty(scratch_pool);
283       for (i = 0; i < hostnames->nelts; ++i)
284         {
285           const char *hostname = APR_ARRAY_IDX(hostnames, i, const char*);
286           if (i > 0)
287             svn_stringbuf_appendbytes(buf, ", ", 2);
288           svn_stringbuf_appendbytes(buf, hostname, strlen(hostname));
289         }
290       SVN_ERR(svn_cmdline_printf(scratch_pool, _("Hostnames: %s\n"),
291                                  buf->data));
292     }
293
294   return SVN_NO_ERROR;
295 }
296
297 static svn_error_t *
298 list_credential(const char *cred_kind,
299                 const char *realmstring,
300                 apr_array_header_t *cred_items,
301                 svn_boolean_t show_passwords,
302                 svn_x509_certinfo_t *certinfo,
303                 apr_pool_t *scratch_pool)
304 {
305   int i;
306   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
307
308   SVN_ERR(svn_cmdline_printf(scratch_pool, SEP_STRING));
309   SVN_ERR(svn_cmdline_printf(scratch_pool,
310                              _("Credential kind: %s\n"), cred_kind));
311   SVN_ERR(svn_cmdline_printf(scratch_pool,
312                              _("Authentication realm: %s\n"), realmstring));
313
314   for (i = 0; i < cred_items->nelts; i++)
315     {
316       svn_sort__item_t item;
317       const char *key;
318       svn_string_t *value;
319
320       svn_pool_clear(iterpool);
321       item = APR_ARRAY_IDX(cred_items, i, svn_sort__item_t);
322       key = item.key;
323       value = item.value;
324       if (strcmp(value->data, realmstring) == 0)
325         continue; /* realm string was already shown above */
326       else if (strcmp(key, SVN_CONFIG_AUTHN_PASSWORD_KEY) == 0)
327         {
328           if (show_passwords)
329             SVN_ERR(svn_cmdline_printf(iterpool,
330                                        _("Password: %s\n"), value->data));
331           else
332             SVN_ERR(svn_cmdline_printf(iterpool, _("Password: [not shown]\n")));
333         }
334       else if (strcmp(key, SVN_CONFIG_AUTHN_PASSPHRASE_KEY) == 0)
335         {
336           if (show_passwords)
337             SVN_ERR(svn_cmdline_printf(iterpool,
338                                        _("Passphrase: %s\n"), value->data));
339           else
340             SVN_ERR(svn_cmdline_printf(iterpool,
341                                        _("Passphrase: [not shown]\n")));
342         }
343       else if (strcmp(key, SVN_CONFIG_AUTHN_PASSTYPE_KEY) == 0)
344         SVN_ERR(svn_cmdline_printf(iterpool, _("Password cache: %s\n"),
345                                    value->data));
346       else if (strcmp(key, SVN_CONFIG_AUTHN_USERNAME_KEY) == 0)
347         SVN_ERR(svn_cmdline_printf(iterpool, _("Username: %s\n"), value->data));
348       else if (strcmp(key, SVN_CONFIG_AUTHN_ASCII_CERT_KEY) == 0)
349        SVN_ERR(show_cert(certinfo, value, iterpool));
350       else if (strcmp(key, SVN_CONFIG_AUTHN_FAILURES_KEY) == 0)
351         SVN_ERR(show_cert_failures(value->data, iterpool));
352       else
353         SVN_ERR(svn_cmdline_printf(iterpool, "%s: %s\n", key, value->data));
354     }
355   svn_pool_destroy(iterpool);
356
357   SVN_ERR(svn_cmdline_printf(scratch_pool, "\n"));
358   return SVN_NO_ERROR;
359 }
360
361 /* This implements `svn_config_auth_walk_func_t` */
362 static svn_error_t *
363 walk_credentials(svn_boolean_t *delete_cred,
364                  void *baton,
365                  const char *cred_kind,
366                  const char *realmstring,
367                  apr_hash_t *cred_hash,
368                  apr_pool_t *scratch_pool)
369 {
370   struct walk_credentials_baton_t *b = baton;
371   apr_array_header_t *sorted_cred_items;
372   svn_x509_certinfo_t *certinfo = NULL;
373
374   *delete_cred = FALSE;
375
376   sorted_cred_items = svn_sort__hash(cred_hash,
377                                      svn_sort_compare_items_lexically,
378                                      scratch_pool);
379   if (b->patterns->nelts > 0)
380     {
381       svn_boolean_t match;
382
383       SVN_ERR(match_credential(&match, &certinfo, cred_kind, realmstring,
384                                b->patterns, sorted_cred_items,
385                                scratch_pool, scratch_pool));
386       if (!match)
387         return SVN_NO_ERROR;
388     }
389
390   b->matches++;
391
392   if (b->list)
393     SVN_ERR(list_credential(cred_kind, realmstring, sorted_cred_items,
394                             b->show_passwords, certinfo, scratch_pool));
395   if (b->delete)
396     {
397       *delete_cred = TRUE;
398       SVN_ERR(svn_cmdline_printf(scratch_pool,
399                                  _("Deleting %s credential for realm '%s'\n"),
400                                  cred_kind, realmstring));
401     }
402
403   return SVN_NO_ERROR;
404 }
405
406
407 /* This implements `svn_opt_subcommand_t'. */
408 svn_error_t *
409 svn_cl__auth(apr_getopt_t *os, void *baton, apr_pool_t *pool)
410 {
411   svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
412   const char *config_path;
413   struct walk_credentials_baton_t b;
414
415   b.matches = 0;
416   b.show_passwords = opt_state->show_passwords;
417   b.list = !opt_state->remove;
418   b.delete = opt_state->remove;
419   b.patterns = apr_array_make(pool, 1, sizeof(const char *));
420   for (; os->ind < os->argc; os->ind++)
421     {
422       /* The apr_getopt targets are still in native encoding. */
423       const char *raw_target = os->argv[os->ind];
424       const char *utf8_target;
425
426       SVN_ERR(svn_utf_cstring_to_utf8(&utf8_target,
427                                       raw_target, pool));
428       APR_ARRAY_PUSH(b.patterns, const char *) = utf8_target;
429     }
430
431   SVN_ERR(svn_config_get_user_config_path(&config_path,
432                                           opt_state->config_dir, NULL,
433                                           pool));
434
435   if (b.delete && b.patterns->nelts < 1)
436     return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
437
438   SVN_ERR(svn_config_walk_auth_data(config_path, walk_credentials, &b, pool));
439
440   if (b.list)
441     {
442       if (b.matches == 0)
443         {
444           if (b.patterns->nelts == 0)
445             SVN_ERR(svn_cmdline_printf(pool,
446                       _("Credentials cache in '%s' is empty\n"),
447                       svn_dirent_local_style(config_path, pool)));
448           else
449             return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, 0,
450                                      _("Credentials cache in '%s' contains "
451                                        "no matching credentials"),
452                                      svn_dirent_local_style(config_path, pool));
453         }
454       else
455         {
456           if (b.patterns->nelts == 0)
457             SVN_ERR(svn_cmdline_printf(pool,
458                       _("Credentials cache in '%s' contains %d credentials\n"),
459                       svn_dirent_local_style(config_path, pool), b.matches));
460           else
461             SVN_ERR(svn_cmdline_printf(pool,
462                       _("Credentials cache in '%s' contains %d matching "
463                         "credentials\n"),
464                       svn_dirent_local_style(config_path, pool), b.matches));
465         }
466
467     }
468
469   if (b.delete)
470     {
471       if (b.matches == 0)
472         return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, 0,
473                                  _("Credentials cache in '%s' contains "
474                                    "no matching credentials"),
475                                  svn_dirent_local_style(config_path, pool));
476       else
477         SVN_ERR(svn_cmdline_printf(pool, _("Deleted %d matching credentials "
478                                    "from '%s'\n"), b.matches,
479                                    svn_dirent_local_style(config_path, pool)));
480     }
481
482   return SVN_NO_ERROR;
483 }