2
* auth-cmd.c: Subversion auth creds cache administration
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
13
* http://www.apache.org/licenses/LICENSE-2.0
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
21
* ====================================================================
26
#include <apr_general.h>
27
#include <apr_getopt.h>
28
#include <apr_fnmatch.h>
29
#include <apr_tables.h>
31
#include "svn_private_config.h"
33
#include "svn_private_config.h"
34
#include "svn_pools.h"
35
#include "svn_error.h"
37
#include "svn_dirent_uri.h"
40
#include "svn_cmdline.h"
41
#include "svn_config.h"
43
#include "svn_sorts.h"
44
#include "svn_base64.h"
48
#include "private/svn_cmdline_private.h"
49
#include "private/svn_token.h"
50
#include "private/svn_sorts_private.h"
54
/* The separator between credentials . */
56
"------------------------------------------------------------------------\n"
59
show_cert_failures(const char *failure_string,
60
apr_pool_t *scratch_pool)
62
unsigned int failures;
64
SVN_ERR(svn_cstring_atoui(&failures, failure_string));
66
if (0 == (failures & (SVN_AUTH_SSL_NOTYETVALID | SVN_AUTH_SSL_EXPIRED |
67
SVN_AUTH_SSL_CNMISMATCH | SVN_AUTH_SSL_UNKNOWNCA |
71
SVN_ERR(svn_cmdline_printf(
72
scratch_pool, _("Automatic certificate validity check failed "
75
if (failures & SVN_AUTH_SSL_NOTYETVALID)
76
SVN_ERR(svn_cmdline_printf(
77
scratch_pool, _(" The certificate is not yet valid.\n")));
79
if (failures & SVN_AUTH_SSL_EXPIRED)
80
SVN_ERR(svn_cmdline_printf(
81
scratch_pool, _(" The certificate has expired.\n")));
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")));
88
if (failures & SVN_AUTH_SSL_UNKNOWNCA)
89
SVN_ERR(svn_cmdline_printf(
90
scratch_pool, _(" The certificate issuer is unknown.\n")));
92
if (failures & SVN_AUTH_SSL_OTHER)
93
SVN_ERR(svn_cmdline_printf(
94
scratch_pool, _(" Unknown verification failure.\n")));
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)
110
svn_x509_certinfo_t *certinfo;
111
const svn_string_t *der_cert;
114
/* Convert header-less PEM to DER by undoing base64 encoding. */
115
der_cert = svn_base64_decode_string(ascii_cert, scratch_pool);
117
err = svn_x509_parse_cert(&certinfo, der_cert->data, der_cert->len,
118
result_pool, scratch_pool);
121
/* Just display X.509 parsing errors as warnings and continue */
123
svn_handle_warning2(stderr, err, "svn: ");
124
svn_error_clear(err);
132
struct walk_credentials_baton_t
136
svn_boolean_t delete;
137
svn_boolean_t show_passwords;
138
apr_array_header_t *patterns;
142
match_pattern(const char *pattern, const char *value,
143
svn_boolean_t caseblind, apr_pool_t *scratch_pool)
145
const char *p = apr_psprintf(scratch_pool, "*%s*", pattern);
146
int flags = (caseblind ? APR_FNM_CASE_BLIND : 0);
148
return (apr_fnmatch(p, value, flags) == APR_SUCCESS);
152
match_certificate(svn_x509_certinfo_t **certinfo,
154
const svn_string_t *ascii_cert,
155
apr_pool_t *result_pool,
156
apr_pool_t *scratch_pool)
159
const svn_checksum_t *checksum;
160
const apr_array_header_t *hostnames;
163
*certinfo = parse_certificate(ascii_cert, FALSE, result_pool, scratch_pool);
164
if (*certinfo == NULL)
167
value = svn_x509_certinfo_get_subject(*certinfo, scratch_pool);
168
if (match_pattern(pattern, value, FALSE, scratch_pool))
171
value = svn_x509_certinfo_get_issuer(*certinfo, scratch_pool);
172
if (match_pattern(pattern, value, FALSE, scratch_pool))
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))
180
hostnames = svn_x509_certinfo_get_hostnames(*certinfo);
183
for (i = 0; i < hostnames->nelts; i++)
185
const char *hostname = APR_ARRAY_IDX(hostnames, i, const char *);
186
if (match_pattern(pattern, hostname, TRUE, scratch_pool))
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)
206
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
210
for (i = 0; i < patterns->nelts; i++)
212
const char *pattern = APR_ARRAY_IDX(patterns, i, const char *);
215
*match = match_pattern(pattern, cred_kind, FALSE, iterpool);
217
*match = match_pattern(pattern, realmstring, FALSE, iterpool);
220
svn_pool_clear(iterpool);
221
for (j = 0; j < cred_items->nelts; j++)
223
svn_sort__item_t item;
227
item = APR_ARRAY_IDX(cred_items, j, svn_sort__item_t);
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);
237
*match = match_pattern(pattern, value->data, FALSE, iterpool);
251
show_cert(svn_x509_certinfo_t *certinfo, const svn_string_t *pem_cert,
252
apr_pool_t *scratch_pool)
254
const apr_array_header_t *hostnames;
256
if (certinfo == NULL)
257
certinfo = parse_certificate(pem_cert, TRUE, scratch_pool, scratch_pool);
258
if (certinfo == NULL)
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),
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),
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),
278
hostnames = svn_x509_certinfo_get_hostnames(certinfo);
279
if (hostnames && !apr_is_empty_array(hostnames))
282
svn_stringbuf_t *buf = svn_stringbuf_create_empty(scratch_pool);
283
for (i = 0; i < hostnames->nelts; ++i)
285
const char *hostname = APR_ARRAY_IDX(hostnames, i, const char*);
287
svn_stringbuf_appendbytes(buf, ", ", 2);
288
svn_stringbuf_appendbytes(buf, hostname, strlen(hostname));
290
SVN_ERR(svn_cmdline_printf(scratch_pool, _("Hostnames: %s\n"),
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)
306
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
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));
314
for (i = 0; i < cred_items->nelts; i++)
316
svn_sort__item_t item;
320
svn_pool_clear(iterpool);
321
item = APR_ARRAY_IDX(cred_items, i, svn_sort__item_t);
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)
329
SVN_ERR(svn_cmdline_printf(iterpool,
330
_("Password: %s\n"), value->data));
332
SVN_ERR(svn_cmdline_printf(iterpool, _("Password: [not shown]\n")));
334
else if (strcmp(key, SVN_CONFIG_AUTHN_PASSPHRASE_KEY) == 0)
337
SVN_ERR(svn_cmdline_printf(iterpool,
338
_("Passphrase: %s\n"), value->data));
340
SVN_ERR(svn_cmdline_printf(iterpool,
341
_("Passphrase: [not shown]\n")));
343
else if (strcmp(key, SVN_CONFIG_AUTHN_PASSTYPE_KEY) == 0)
344
SVN_ERR(svn_cmdline_printf(iterpool, _("Password cache: %s\n"),
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));
353
SVN_ERR(svn_cmdline_printf(iterpool, "%s: %s\n", key, value->data));
355
svn_pool_destroy(iterpool);
357
SVN_ERR(svn_cmdline_printf(scratch_pool, "\n"));
361
/* This implements `svn_config_auth_walk_func_t` */
363
walk_credentials(svn_boolean_t *delete_cred,
365
const char *cred_kind,
366
const char *realmstring,
367
apr_hash_t *cred_hash,
368
apr_pool_t *scratch_pool)
370
struct walk_credentials_baton_t *b = baton;
371
apr_array_header_t *sorted_cred_items;
372
svn_x509_certinfo_t *certinfo = NULL;
374
*delete_cred = FALSE;
376
sorted_cred_items = svn_sort__hash(cred_hash,
377
svn_sort_compare_items_lexically,
379
if (b->patterns->nelts > 0)
383
SVN_ERR(match_credential(&match, &certinfo, cred_kind, realmstring,
384
b->patterns, sorted_cred_items,
385
scratch_pool, scratch_pool));
393
SVN_ERR(list_credential(cred_kind, realmstring, sorted_cred_items,
394
b->show_passwords, certinfo, scratch_pool));
398
SVN_ERR(svn_cmdline_printf(scratch_pool,
399
_("Deleting %s credential for realm '%s'\n"),
400
cred_kind, realmstring));
407
/* This implements `svn_opt_subcommand_t'. */
409
svn_cl__auth(apr_getopt_t *os, void *baton, apr_pool_t *pool)
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;
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++)
422
/* The apr_getopt targets are still in native encoding. */
423
const char *raw_target = os->argv[os->ind];
424
const char *utf8_target;
426
SVN_ERR(svn_utf_cstring_to_utf8(&utf8_target,
428
APR_ARRAY_PUSH(b.patterns, const char *) = utf8_target;
431
SVN_ERR(svn_config_get_user_config_path(&config_path,
432
opt_state->config_dir, NULL,
435
if (b.delete && b.patterns->nelts < 1)
436
return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
438
SVN_ERR(svn_config_walk_auth_data(config_path, walk_credentials, &b, pool));
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)));
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));
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));
461
SVN_ERR(svn_cmdline_printf(pool,
462
_("Credentials cache in '%s' contains %d matching "
464
svn_dirent_local_style(config_path, pool), b.matches));
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));
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)));