diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/svn/auth-cmd.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/svn/auth-cmd.c')
-rw-r--r-- | subversion/svn/auth-cmd.c | 483 |
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; +} |