summaryrefslogtreecommitdiff
path: root/subversion/svn/auth-cmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/svn/auth-cmd.c')
-rw-r--r--subversion/svn/auth-cmd.c483
1 files changed, 483 insertions, 0 deletions
diff --git a/subversion/svn/auth-cmd.c b/subversion/svn/auth-cmd.c
new file mode 100644
index 0000000..68ca067
--- /dev/null
+++ b/subversion/svn/auth-cmd.c
@@ -0,0 +1,483 @@
+/*
+ * auth-cmd.c: Subversion auth creds cache administration
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+/*** Includes. ***/
+
+#include <apr_general.h>
+#include <apr_getopt.h>
+#include <apr_fnmatch.h>
+#include <apr_tables.h>
+
+#include "svn_private_config.h"
+
+#include "svn_private_config.h"
+#include "svn_pools.h"
+#include "svn_error.h"
+#include "svn_opt.h"
+#include "svn_dirent_uri.h"
+#include "svn_hash.h"
+#include "svn_utf.h"
+#include "svn_cmdline.h"
+#include "svn_config.h"
+#include "svn_auth.h"
+#include "svn_sorts.h"
+#include "svn_base64.h"
+#include "svn_x509.h"
+#include "svn_time.h"
+
+#include "private/svn_cmdline_private.h"
+#include "private/svn_token.h"
+#include "private/svn_sorts_private.h"
+
+#include "cl.h"
+
+/* The separator between credentials . */
+#define SEP_STRING \
+ "------------------------------------------------------------------------\n"
+
+static svn_error_t *
+show_cert_failures(const char *failure_string,
+ apr_pool_t *scratch_pool)
+{
+ unsigned int failures;
+
+ SVN_ERR(svn_cstring_atoui(&failures, failure_string));
+
+ if (0 == (failures & (SVN_AUTH_SSL_NOTYETVALID | SVN_AUTH_SSL_EXPIRED |
+ SVN_AUTH_SSL_CNMISMATCH | SVN_AUTH_SSL_UNKNOWNCA |
+ SVN_AUTH_SSL_OTHER)))
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_cmdline_printf(
+ scratch_pool, _("Automatic certificate validity check failed "
+ "because:\n")));
+
+ if (failures & SVN_AUTH_SSL_NOTYETVALID)
+ SVN_ERR(svn_cmdline_printf(
+ scratch_pool, _(" The certificate is not yet valid.\n")));
+
+ if (failures & SVN_AUTH_SSL_EXPIRED)
+ SVN_ERR(svn_cmdline_printf(
+ scratch_pool, _(" The certificate has expired.\n")));
+
+ if (failures & SVN_AUTH_SSL_CNMISMATCH)
+ SVN_ERR(svn_cmdline_printf(
+ scratch_pool, _(" The certificate's Common Name (hostname) "
+ "does not match the remote hostname.\n")));
+
+ if (failures & SVN_AUTH_SSL_UNKNOWNCA)
+ SVN_ERR(svn_cmdline_printf(
+ scratch_pool, _(" The certificate issuer is unknown.\n")));
+
+ if (failures & SVN_AUTH_SSL_OTHER)
+ SVN_ERR(svn_cmdline_printf(
+ scratch_pool, _(" Unknown verification failure.\n")));
+
+ return SVN_NO_ERROR;
+}
+
+
+/* decodes from format we store certs in for auth creds and
+ * turns parsing errors into warnings if PRINT_WARNING is TRUE
+ * and ignores them otherwise. returns NULL if it couldn't
+ * parse a cert for any reason. */
+static svn_x509_certinfo_t *
+parse_certificate(const svn_string_t *ascii_cert,
+ svn_boolean_t print_warning,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_x509_certinfo_t *certinfo;
+ const svn_string_t *der_cert;
+ svn_error_t *err;
+
+ /* Convert header-less PEM to DER by undoing base64 encoding. */
+ der_cert = svn_base64_decode_string(ascii_cert, scratch_pool);
+
+ err = svn_x509_parse_cert(&certinfo, der_cert->data, der_cert->len,
+ result_pool, scratch_pool);
+ if (err)
+ {
+ /* Just display X.509 parsing errors as warnings and continue */
+ if (print_warning)
+ svn_handle_warning2(stderr, err, "svn: ");
+ svn_error_clear(err);
+ return NULL;
+ }
+
+ return certinfo;
+}
+
+
+struct walk_credentials_baton_t
+{
+ int matches;
+ svn_boolean_t list;
+ svn_boolean_t delete;
+ svn_boolean_t show_passwords;
+ apr_array_header_t *patterns;
+};
+
+static svn_boolean_t
+match_pattern(const char *pattern, const char *value,
+ svn_boolean_t caseblind, apr_pool_t *scratch_pool)
+{
+ const char *p = apr_psprintf(scratch_pool, "*%s*", pattern);
+ int flags = (caseblind ? APR_FNM_CASE_BLIND : 0);
+
+ return (apr_fnmatch(p, value, flags) == APR_SUCCESS);
+}
+
+static svn_boolean_t
+match_certificate(svn_x509_certinfo_t **certinfo,
+ const char *pattern,
+ const svn_string_t *ascii_cert,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *value;
+ const svn_checksum_t *checksum;
+ const apr_array_header_t *hostnames;
+ int i;
+
+ *certinfo = parse_certificate(ascii_cert, FALSE, result_pool, scratch_pool);
+ if (*certinfo == NULL)
+ return FALSE;
+
+ value = svn_x509_certinfo_get_subject(*certinfo, scratch_pool);
+ if (match_pattern(pattern, value, FALSE, scratch_pool))
+ return TRUE;
+
+ value = svn_x509_certinfo_get_issuer(*certinfo, scratch_pool);
+ if (match_pattern(pattern, value, FALSE, scratch_pool))
+ return TRUE;
+
+ checksum = svn_x509_certinfo_get_digest(*certinfo);
+ value = svn_checksum_to_cstring_display(checksum, scratch_pool);
+ if (match_pattern(pattern, value, TRUE, scratch_pool))
+ return TRUE;
+
+ hostnames = svn_x509_certinfo_get_hostnames(*certinfo);
+ if (hostnames)
+ {
+ for (i = 0; i < hostnames->nelts; i++)
+ {
+ const char *hostname = APR_ARRAY_IDX(hostnames, i, const char *);
+ if (match_pattern(pattern, hostname, TRUE, scratch_pool))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static svn_error_t *
+match_credential(svn_boolean_t *match,
+ svn_x509_certinfo_t **certinfo,
+ const char *cred_kind,
+ const char *realmstring,
+ apr_array_header_t *patterns,
+ apr_array_header_t *cred_items,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+ *match = FALSE;
+
+ for (i = 0; i < patterns->nelts; i++)
+ {
+ const char *pattern = APR_ARRAY_IDX(patterns, i, const char *);
+ int j;
+
+ *match = match_pattern(pattern, cred_kind, FALSE, iterpool);
+ if (!*match)
+ *match = match_pattern(pattern, realmstring, FALSE, iterpool);
+ if (!*match)
+ {
+ svn_pool_clear(iterpool);
+ for (j = 0; j < cred_items->nelts; j++)
+ {
+ svn_sort__item_t item;
+ const char *key;
+ svn_string_t *value;
+
+ item = APR_ARRAY_IDX(cred_items, j, svn_sort__item_t);
+ key = item.key;
+ value = item.value;
+ if (strcmp(key, SVN_CONFIG_AUTHN_PASSWORD_KEY) == 0 ||
+ strcmp(key, SVN_CONFIG_AUTHN_PASSPHRASE_KEY) == 0)
+ continue; /* don't match secrets */
+ else if (strcmp(key, SVN_CONFIG_AUTHN_ASCII_CERT_KEY) == 0)
+ *match = match_certificate(certinfo, pattern, value,
+ result_pool, iterpool);
+ else
+ *match = match_pattern(pattern, value->data, FALSE, iterpool);
+
+ if (*match)
+ break;
+ }
+ }
+ if (!*match)
+ break;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+show_cert(svn_x509_certinfo_t *certinfo, const svn_string_t *pem_cert,
+ apr_pool_t *scratch_pool)
+{
+ const apr_array_header_t *hostnames;
+
+ if (certinfo == NULL)
+ certinfo = parse_certificate(pem_cert, TRUE, scratch_pool, scratch_pool);
+ if (certinfo == NULL)
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_cmdline_printf(scratch_pool, _("Subject: %s\n"),
+ svn_x509_certinfo_get_subject(certinfo, scratch_pool)));
+ SVN_ERR(svn_cmdline_printf(scratch_pool, _("Valid from: %s\n"),
+ svn_time_to_human_cstring(
+ svn_x509_certinfo_get_valid_from(certinfo),
+ scratch_pool)));
+ SVN_ERR(svn_cmdline_printf(scratch_pool, _("Valid until: %s\n"),
+ svn_time_to_human_cstring(
+ svn_x509_certinfo_get_valid_to(certinfo),
+ scratch_pool)));
+ SVN_ERR(svn_cmdline_printf(scratch_pool, _("Issuer: %s\n"),
+ svn_x509_certinfo_get_issuer(certinfo, scratch_pool)));
+ SVN_ERR(svn_cmdline_printf(scratch_pool, _("Fingerprint: %s\n"),
+ svn_checksum_to_cstring_display(
+ svn_x509_certinfo_get_digest(certinfo),
+ scratch_pool)));
+
+ hostnames = svn_x509_certinfo_get_hostnames(certinfo);
+ if (hostnames && !apr_is_empty_array(hostnames))
+ {
+ int i;
+ svn_stringbuf_t *buf = svn_stringbuf_create_empty(scratch_pool);
+ for (i = 0; i < hostnames->nelts; ++i)
+ {
+ const char *hostname = APR_ARRAY_IDX(hostnames, i, const char*);
+ if (i > 0)
+ svn_stringbuf_appendbytes(buf, ", ", 2);
+ svn_stringbuf_appendbytes(buf, hostname, strlen(hostname));
+ }
+ SVN_ERR(svn_cmdline_printf(scratch_pool, _("Hostnames: %s\n"),
+ buf->data));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+list_credential(const char *cred_kind,
+ const char *realmstring,
+ apr_array_header_t *cred_items,
+ svn_boolean_t show_passwords,
+ svn_x509_certinfo_t *certinfo,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+ SVN_ERR(svn_cmdline_printf(scratch_pool, SEP_STRING));
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("Credential kind: %s\n"), cred_kind));
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("Authentication realm: %s\n"), realmstring));
+
+ for (i = 0; i < cred_items->nelts; i++)
+ {
+ svn_sort__item_t item;
+ const char *key;
+ svn_string_t *value;
+
+ svn_pool_clear(iterpool);
+ item = APR_ARRAY_IDX(cred_items, i, svn_sort__item_t);
+ key = item.key;
+ value = item.value;
+ if (strcmp(value->data, realmstring) == 0)
+ continue; /* realm string was already shown above */
+ else if (strcmp(key, SVN_CONFIG_AUTHN_PASSWORD_KEY) == 0)
+ {
+ if (show_passwords)
+ SVN_ERR(svn_cmdline_printf(iterpool,
+ _("Password: %s\n"), value->data));
+ else
+ SVN_ERR(svn_cmdline_printf(iterpool, _("Password: [not shown]\n")));
+ }
+ else if (strcmp(key, SVN_CONFIG_AUTHN_PASSPHRASE_KEY) == 0)
+ {
+ if (show_passwords)
+ SVN_ERR(svn_cmdline_printf(iterpool,
+ _("Passphrase: %s\n"), value->data));
+ else
+ SVN_ERR(svn_cmdline_printf(iterpool,
+ _("Passphrase: [not shown]\n")));
+ }
+ else if (strcmp(key, SVN_CONFIG_AUTHN_PASSTYPE_KEY) == 0)
+ SVN_ERR(svn_cmdline_printf(iterpool, _("Password cache: %s\n"),
+ value->data));
+ else if (strcmp(key, SVN_CONFIG_AUTHN_USERNAME_KEY) == 0)
+ SVN_ERR(svn_cmdline_printf(iterpool, _("Username: %s\n"), value->data));
+ else if (strcmp(key, SVN_CONFIG_AUTHN_ASCII_CERT_KEY) == 0)
+ SVN_ERR(show_cert(certinfo, value, iterpool));
+ else if (strcmp(key, SVN_CONFIG_AUTHN_FAILURES_KEY) == 0)
+ SVN_ERR(show_cert_failures(value->data, iterpool));
+ else
+ SVN_ERR(svn_cmdline_printf(iterpool, "%s: %s\n", key, value->data));
+ }
+ svn_pool_destroy(iterpool);
+
+ SVN_ERR(svn_cmdline_printf(scratch_pool, "\n"));
+ return SVN_NO_ERROR;
+}
+
+/* This implements `svn_config_auth_walk_func_t` */
+static svn_error_t *
+walk_credentials(svn_boolean_t *delete_cred,
+ void *baton,
+ const char *cred_kind,
+ const char *realmstring,
+ apr_hash_t *cred_hash,
+ apr_pool_t *scratch_pool)
+{
+ struct walk_credentials_baton_t *b = baton;
+ apr_array_header_t *sorted_cred_items;
+ svn_x509_certinfo_t *certinfo = NULL;
+
+ *delete_cred = FALSE;
+
+ sorted_cred_items = svn_sort__hash(cred_hash,
+ svn_sort_compare_items_lexically,
+ scratch_pool);
+ if (b->patterns->nelts > 0)
+ {
+ svn_boolean_t match;
+
+ SVN_ERR(match_credential(&match, &certinfo, cred_kind, realmstring,
+ b->patterns, sorted_cred_items,
+ scratch_pool, scratch_pool));
+ if (!match)
+ return SVN_NO_ERROR;
+ }
+
+ b->matches++;
+
+ if (b->list)
+ SVN_ERR(list_credential(cred_kind, realmstring, sorted_cred_items,
+ b->show_passwords, certinfo, scratch_pool));
+ if (b->delete)
+ {
+ *delete_cred = TRUE;
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("Deleting %s credential for realm '%s'\n"),
+ cred_kind, realmstring));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+/* This implements `svn_opt_subcommand_t'. */
+svn_error_t *
+svn_cl__auth(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ const char *config_path;
+ struct walk_credentials_baton_t b;
+
+ b.matches = 0;
+ b.show_passwords = opt_state->show_passwords;
+ b.list = !opt_state->remove;
+ b.delete = opt_state->remove;
+ b.patterns = apr_array_make(pool, 1, sizeof(const char *));
+ for (; os->ind < os->argc; os->ind++)
+ {
+ /* The apr_getopt targets are still in native encoding. */
+ const char *raw_target = os->argv[os->ind];
+ const char *utf8_target;
+
+ SVN_ERR(svn_utf_cstring_to_utf8(&utf8_target,
+ raw_target, pool));
+ APR_ARRAY_PUSH(b.patterns, const char *) = utf8_target;
+ }
+
+ SVN_ERR(svn_config_get_user_config_path(&config_path,
+ opt_state->config_dir, NULL,
+ pool));
+
+ if (b.delete && b.patterns->nelts < 1)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ SVN_ERR(svn_config_walk_auth_data(config_path, walk_credentials, &b, pool));
+
+ if (b.list)
+ {
+ if (b.matches == 0)
+ {
+ if (b.patterns->nelts == 0)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Credentials cache in '%s' is empty\n"),
+ svn_dirent_local_style(config_path, pool)));
+ else
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, 0,
+ _("Credentials cache in '%s' contains "
+ "no matching credentials"),
+ svn_dirent_local_style(config_path, pool));
+ }
+ else
+ {
+ if (b.patterns->nelts == 0)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Credentials cache in '%s' contains %d credentials\n"),
+ svn_dirent_local_style(config_path, pool), b.matches));
+ else
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Credentials cache in '%s' contains %d matching "
+ "credentials\n"),
+ svn_dirent_local_style(config_path, pool), b.matches));
+ }
+
+ }
+
+ if (b.delete)
+ {
+ if (b.matches == 0)
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, 0,
+ _("Credentials cache in '%s' contains "
+ "no matching credentials"),
+ svn_dirent_local_style(config_path, pool));
+ else
+ SVN_ERR(svn_cmdline_printf(pool, _("Deleted %d matching credentials "
+ "from '%s'\n"), b.matches,
+ svn_dirent_local_style(config_path, pool)));
+ }
+
+ return SVN_NO_ERROR;
+}