1289177Speter/* 2289177Speter * auth-cmd.c: Subversion auth creds cache administration 3289177Speter * 4289177Speter * ==================================================================== 5289177Speter * Licensed to the Apache Software Foundation (ASF) under one 6289177Speter * or more contributor license agreements. See the NOTICE file 7289177Speter * distributed with this work for additional information 8289177Speter * regarding copyright ownership. The ASF licenses this file 9289177Speter * to you under the Apache License, Version 2.0 (the 10289177Speter * "License"); you may not use this file except in compliance 11289177Speter * with the License. You may obtain a copy of the License at 12289177Speter * 13289177Speter * http://www.apache.org/licenses/LICENSE-2.0 14289177Speter * 15289177Speter * Unless required by applicable law or agreed to in writing, 16289177Speter * software distributed under the License is distributed on an 17289177Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18289177Speter * KIND, either express or implied. See the License for the 19289177Speter * specific language governing permissions and limitations 20289177Speter * under the License. 21289177Speter * ==================================================================== 22289177Speter */ 23289177Speter 24289177Speter/*** Includes. ***/ 25289177Speter 26289177Speter#include <apr_general.h> 27289177Speter#include <apr_getopt.h> 28289177Speter#include <apr_fnmatch.h> 29289177Speter#include <apr_tables.h> 30289177Speter 31289177Speter#include "svn_private_config.h" 32289177Speter 33289177Speter#include "svn_private_config.h" 34289177Speter#include "svn_pools.h" 35289177Speter#include "svn_error.h" 36289177Speter#include "svn_opt.h" 37289177Speter#include "svn_dirent_uri.h" 38289177Speter#include "svn_hash.h" 39289177Speter#include "svn_utf.h" 40289177Speter#include "svn_cmdline.h" 41289177Speter#include "svn_config.h" 42289177Speter#include "svn_auth.h" 43289177Speter#include "svn_sorts.h" 44289177Speter#include "svn_base64.h" 45289177Speter#include "svn_x509.h" 46289177Speter#include "svn_time.h" 47289177Speter 48289177Speter#include "private/svn_cmdline_private.h" 49289177Speter#include "private/svn_token.h" 50289177Speter#include "private/svn_sorts_private.h" 51289177Speter 52289177Speter#include "cl.h" 53289177Speter 54289177Speter/* The separator between credentials . */ 55289177Speter#define SEP_STRING \ 56289177Speter "------------------------------------------------------------------------\n" 57289177Speter 58289177Speterstatic svn_error_t * 59289177Spetershow_cert_failures(const char *failure_string, 60289177Speter apr_pool_t *scratch_pool) 61289177Speter{ 62289177Speter unsigned int failures; 63289177Speter 64289177Speter SVN_ERR(svn_cstring_atoui(&failures, failure_string)); 65289177Speter 66289177Speter if (0 == (failures & (SVN_AUTH_SSL_NOTYETVALID | SVN_AUTH_SSL_EXPIRED | 67289177Speter SVN_AUTH_SSL_CNMISMATCH | SVN_AUTH_SSL_UNKNOWNCA | 68289177Speter SVN_AUTH_SSL_OTHER))) 69289177Speter return SVN_NO_ERROR; 70289177Speter 71289177Speter SVN_ERR(svn_cmdline_printf( 72289177Speter scratch_pool, _("Automatic certificate validity check failed " 73289177Speter "because:\n"))); 74289177Speter 75289177Speter if (failures & SVN_AUTH_SSL_NOTYETVALID) 76289177Speter SVN_ERR(svn_cmdline_printf( 77289177Speter scratch_pool, _(" The certificate is not yet valid.\n"))); 78289177Speter 79289177Speter if (failures & SVN_AUTH_SSL_EXPIRED) 80289177Speter SVN_ERR(svn_cmdline_printf( 81289177Speter scratch_pool, _(" The certificate has expired.\n"))); 82289177Speter 83289177Speter if (failures & SVN_AUTH_SSL_CNMISMATCH) 84289177Speter SVN_ERR(svn_cmdline_printf( 85289177Speter scratch_pool, _(" The certificate's Common Name (hostname) " 86289177Speter "does not match the remote hostname.\n"))); 87289177Speter 88289177Speter if (failures & SVN_AUTH_SSL_UNKNOWNCA) 89289177Speter SVN_ERR(svn_cmdline_printf( 90289177Speter scratch_pool, _(" The certificate issuer is unknown.\n"))); 91289177Speter 92289177Speter if (failures & SVN_AUTH_SSL_OTHER) 93289177Speter SVN_ERR(svn_cmdline_printf( 94289177Speter scratch_pool, _(" Unknown verification failure.\n"))); 95289177Speter 96289177Speter return SVN_NO_ERROR; 97289177Speter} 98289177Speter 99289177Speter 100289177Speter/* decodes from format we store certs in for auth creds and 101289177Speter * turns parsing errors into warnings if PRINT_WARNING is TRUE 102289177Speter * and ignores them otherwise. returns NULL if it couldn't 103289177Speter * parse a cert for any reason. */ 104289177Speterstatic svn_x509_certinfo_t * 105289177Speterparse_certificate(const svn_string_t *ascii_cert, 106289177Speter svn_boolean_t print_warning, 107289177Speter apr_pool_t *result_pool, 108289177Speter apr_pool_t *scratch_pool) 109289177Speter{ 110289177Speter svn_x509_certinfo_t *certinfo; 111289177Speter const svn_string_t *der_cert; 112289177Speter svn_error_t *err; 113289177Speter 114289177Speter /* Convert header-less PEM to DER by undoing base64 encoding. */ 115289177Speter der_cert = svn_base64_decode_string(ascii_cert, scratch_pool); 116289177Speter 117289177Speter err = svn_x509_parse_cert(&certinfo, der_cert->data, der_cert->len, 118289177Speter result_pool, scratch_pool); 119289177Speter if (err) 120289177Speter { 121289177Speter /* Just display X.509 parsing errors as warnings and continue */ 122289177Speter if (print_warning) 123289177Speter svn_handle_warning2(stderr, err, "svn: "); 124289177Speter svn_error_clear(err); 125289177Speter return NULL; 126289177Speter } 127289177Speter 128289177Speter return certinfo; 129289177Speter} 130289177Speter 131289177Speter 132289177Speterstruct walk_credentials_baton_t 133289177Speter{ 134289177Speter int matches; 135289177Speter svn_boolean_t list; 136289177Speter svn_boolean_t delete; 137289177Speter svn_boolean_t show_passwords; 138289177Speter apr_array_header_t *patterns; 139289177Speter}; 140289177Speter 141289177Speterstatic svn_boolean_t 142289177Spetermatch_pattern(const char *pattern, const char *value, 143289177Speter svn_boolean_t caseblind, apr_pool_t *scratch_pool) 144289177Speter{ 145289177Speter const char *p = apr_psprintf(scratch_pool, "*%s*", pattern); 146289177Speter int flags = (caseblind ? APR_FNM_CASE_BLIND : 0); 147289177Speter 148289177Speter return (apr_fnmatch(p, value, flags) == APR_SUCCESS); 149289177Speter} 150289177Speter 151289177Speterstatic svn_boolean_t 152289177Spetermatch_certificate(svn_x509_certinfo_t **certinfo, 153289177Speter const char *pattern, 154289177Speter const svn_string_t *ascii_cert, 155289177Speter apr_pool_t *result_pool, 156289177Speter apr_pool_t *scratch_pool) 157289177Speter{ 158289177Speter const char *value; 159289177Speter const svn_checksum_t *checksum; 160289177Speter const apr_array_header_t *hostnames; 161289177Speter int i; 162289177Speter 163289177Speter *certinfo = parse_certificate(ascii_cert, FALSE, result_pool, scratch_pool); 164289177Speter if (*certinfo == NULL) 165289177Speter return FALSE; 166289177Speter 167289177Speter value = svn_x509_certinfo_get_subject(*certinfo, scratch_pool); 168289177Speter if (match_pattern(pattern, value, FALSE, scratch_pool)) 169289177Speter return TRUE; 170289177Speter 171289177Speter value = svn_x509_certinfo_get_issuer(*certinfo, scratch_pool); 172289177Speter if (match_pattern(pattern, value, FALSE, scratch_pool)) 173289177Speter return TRUE; 174289177Speter 175289177Speter checksum = svn_x509_certinfo_get_digest(*certinfo); 176289177Speter value = svn_checksum_to_cstring_display(checksum, scratch_pool); 177289177Speter if (match_pattern(pattern, value, TRUE, scratch_pool)) 178289177Speter return TRUE; 179289177Speter 180289177Speter hostnames = svn_x509_certinfo_get_hostnames(*certinfo); 181289177Speter if (hostnames) 182289177Speter { 183289177Speter for (i = 0; i < hostnames->nelts; i++) 184289177Speter { 185289177Speter const char *hostname = APR_ARRAY_IDX(hostnames, i, const char *); 186289177Speter if (match_pattern(pattern, hostname, TRUE, scratch_pool)) 187289177Speter return TRUE; 188289177Speter } 189289177Speter } 190289177Speter 191289177Speter return FALSE; 192289177Speter} 193289177Speter 194289177Speter 195289177Speterstatic svn_error_t * 196289177Spetermatch_credential(svn_boolean_t *match, 197289177Speter svn_x509_certinfo_t **certinfo, 198289177Speter const char *cred_kind, 199289177Speter const char *realmstring, 200289177Speter apr_array_header_t *patterns, 201289177Speter apr_array_header_t *cred_items, 202289177Speter apr_pool_t *result_pool, 203289177Speter apr_pool_t *scratch_pool) 204289177Speter{ 205289177Speter int i; 206289177Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 207289177Speter 208289177Speter *match = FALSE; 209289177Speter 210289177Speter for (i = 0; i < patterns->nelts; i++) 211289177Speter { 212289177Speter const char *pattern = APR_ARRAY_IDX(patterns, i, const char *); 213289177Speter int j; 214289177Speter 215289177Speter *match = match_pattern(pattern, cred_kind, FALSE, iterpool); 216289177Speter if (!*match) 217289177Speter *match = match_pattern(pattern, realmstring, FALSE, iterpool); 218289177Speter if (!*match) 219289177Speter { 220289177Speter svn_pool_clear(iterpool); 221289177Speter for (j = 0; j < cred_items->nelts; j++) 222289177Speter { 223289177Speter svn_sort__item_t item; 224289177Speter const char *key; 225289177Speter svn_string_t *value; 226289177Speter 227289177Speter item = APR_ARRAY_IDX(cred_items, j, svn_sort__item_t); 228289177Speter key = item.key; 229289177Speter value = item.value; 230289177Speter if (strcmp(key, SVN_CONFIG_AUTHN_PASSWORD_KEY) == 0 || 231289177Speter strcmp(key, SVN_CONFIG_AUTHN_PASSPHRASE_KEY) == 0) 232289177Speter continue; /* don't match secrets */ 233289177Speter else if (strcmp(key, SVN_CONFIG_AUTHN_ASCII_CERT_KEY) == 0) 234289177Speter *match = match_certificate(certinfo, pattern, value, 235289177Speter result_pool, iterpool); 236289177Speter else 237289177Speter *match = match_pattern(pattern, value->data, FALSE, iterpool); 238289177Speter 239289177Speter if (*match) 240289177Speter break; 241289177Speter } 242289177Speter } 243289177Speter if (!*match) 244289177Speter break; 245289177Speter } 246289177Speter 247289177Speter return SVN_NO_ERROR; 248289177Speter} 249289177Speter 250289177Speterstatic svn_error_t * 251289177Spetershow_cert(svn_x509_certinfo_t *certinfo, const svn_string_t *pem_cert, 252289177Speter apr_pool_t *scratch_pool) 253289177Speter{ 254289177Speter const apr_array_header_t *hostnames; 255289177Speter 256289177Speter if (certinfo == NULL) 257289177Speter certinfo = parse_certificate(pem_cert, TRUE, scratch_pool, scratch_pool); 258289177Speter if (certinfo == NULL) 259289177Speter return SVN_NO_ERROR; 260289177Speter 261289177Speter SVN_ERR(svn_cmdline_printf(scratch_pool, _("Subject: %s\n"), 262289177Speter svn_x509_certinfo_get_subject(certinfo, scratch_pool))); 263289177Speter SVN_ERR(svn_cmdline_printf(scratch_pool, _("Valid from: %s\n"), 264289177Speter svn_time_to_human_cstring( 265289177Speter svn_x509_certinfo_get_valid_from(certinfo), 266289177Speter scratch_pool))); 267289177Speter SVN_ERR(svn_cmdline_printf(scratch_pool, _("Valid until: %s\n"), 268289177Speter svn_time_to_human_cstring( 269289177Speter svn_x509_certinfo_get_valid_to(certinfo), 270289177Speter scratch_pool))); 271289177Speter SVN_ERR(svn_cmdline_printf(scratch_pool, _("Issuer: %s\n"), 272289177Speter svn_x509_certinfo_get_issuer(certinfo, scratch_pool))); 273289177Speter SVN_ERR(svn_cmdline_printf(scratch_pool, _("Fingerprint: %s\n"), 274289177Speter svn_checksum_to_cstring_display( 275289177Speter svn_x509_certinfo_get_digest(certinfo), 276289177Speter scratch_pool))); 277289177Speter 278289177Speter hostnames = svn_x509_certinfo_get_hostnames(certinfo); 279289177Speter if (hostnames && !apr_is_empty_array(hostnames)) 280289177Speter { 281289177Speter int i; 282289177Speter svn_stringbuf_t *buf = svn_stringbuf_create_empty(scratch_pool); 283289177Speter for (i = 0; i < hostnames->nelts; ++i) 284289177Speter { 285289177Speter const char *hostname = APR_ARRAY_IDX(hostnames, i, const char*); 286289177Speter if (i > 0) 287289177Speter svn_stringbuf_appendbytes(buf, ", ", 2); 288289177Speter svn_stringbuf_appendbytes(buf, hostname, strlen(hostname)); 289289177Speter } 290289177Speter SVN_ERR(svn_cmdline_printf(scratch_pool, _("Hostnames: %s\n"), 291289177Speter buf->data)); 292289177Speter } 293289177Speter 294289177Speter return SVN_NO_ERROR; 295289177Speter} 296289177Speter 297289177Speterstatic svn_error_t * 298289177Speterlist_credential(const char *cred_kind, 299289177Speter const char *realmstring, 300289177Speter apr_array_header_t *cred_items, 301289177Speter svn_boolean_t show_passwords, 302289177Speter svn_x509_certinfo_t *certinfo, 303289177Speter apr_pool_t *scratch_pool) 304289177Speter{ 305289177Speter int i; 306289177Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 307289177Speter 308289177Speter SVN_ERR(svn_cmdline_printf(scratch_pool, SEP_STRING)); 309289177Speter SVN_ERR(svn_cmdline_printf(scratch_pool, 310289177Speter _("Credential kind: %s\n"), cred_kind)); 311289177Speter SVN_ERR(svn_cmdline_printf(scratch_pool, 312289177Speter _("Authentication realm: %s\n"), realmstring)); 313289177Speter 314289177Speter for (i = 0; i < cred_items->nelts; i++) 315289177Speter { 316289177Speter svn_sort__item_t item; 317289177Speter const char *key; 318289177Speter svn_string_t *value; 319289177Speter 320289177Speter svn_pool_clear(iterpool); 321289177Speter item = APR_ARRAY_IDX(cred_items, i, svn_sort__item_t); 322289177Speter key = item.key; 323289177Speter value = item.value; 324289177Speter if (strcmp(value->data, realmstring) == 0) 325289177Speter continue; /* realm string was already shown above */ 326289177Speter else if (strcmp(key, SVN_CONFIG_AUTHN_PASSWORD_KEY) == 0) 327289177Speter { 328289177Speter if (show_passwords) 329289177Speter SVN_ERR(svn_cmdline_printf(iterpool, 330289177Speter _("Password: %s\n"), value->data)); 331289177Speter else 332289177Speter SVN_ERR(svn_cmdline_printf(iterpool, _("Password: [not shown]\n"))); 333289177Speter } 334289177Speter else if (strcmp(key, SVN_CONFIG_AUTHN_PASSPHRASE_KEY) == 0) 335289177Speter { 336289177Speter if (show_passwords) 337289177Speter SVN_ERR(svn_cmdline_printf(iterpool, 338289177Speter _("Passphrase: %s\n"), value->data)); 339289177Speter else 340289177Speter SVN_ERR(svn_cmdline_printf(iterpool, 341289177Speter _("Passphrase: [not shown]\n"))); 342289177Speter } 343289177Speter else if (strcmp(key, SVN_CONFIG_AUTHN_PASSTYPE_KEY) == 0) 344289177Speter SVN_ERR(svn_cmdline_printf(iterpool, _("Password cache: %s\n"), 345289177Speter value->data)); 346289177Speter else if (strcmp(key, SVN_CONFIG_AUTHN_USERNAME_KEY) == 0) 347289177Speter SVN_ERR(svn_cmdline_printf(iterpool, _("Username: %s\n"), value->data)); 348289177Speter else if (strcmp(key, SVN_CONFIG_AUTHN_ASCII_CERT_KEY) == 0) 349289177Speter SVN_ERR(show_cert(certinfo, value, iterpool)); 350289177Speter else if (strcmp(key, SVN_CONFIG_AUTHN_FAILURES_KEY) == 0) 351289177Speter SVN_ERR(show_cert_failures(value->data, iterpool)); 352289177Speter else 353289177Speter SVN_ERR(svn_cmdline_printf(iterpool, "%s: %s\n", key, value->data)); 354289177Speter } 355289177Speter svn_pool_destroy(iterpool); 356289177Speter 357289177Speter SVN_ERR(svn_cmdline_printf(scratch_pool, "\n")); 358289177Speter return SVN_NO_ERROR; 359289177Speter} 360289177Speter 361289177Speter/* This implements `svn_config_auth_walk_func_t` */ 362289177Speterstatic svn_error_t * 363289177Speterwalk_credentials(svn_boolean_t *delete_cred, 364289177Speter void *baton, 365289177Speter const char *cred_kind, 366289177Speter const char *realmstring, 367289177Speter apr_hash_t *cred_hash, 368289177Speter apr_pool_t *scratch_pool) 369289177Speter{ 370289177Speter struct walk_credentials_baton_t *b = baton; 371289177Speter apr_array_header_t *sorted_cred_items; 372289177Speter svn_x509_certinfo_t *certinfo = NULL; 373289177Speter 374289177Speter *delete_cred = FALSE; 375289177Speter 376289177Speter sorted_cred_items = svn_sort__hash(cred_hash, 377289177Speter svn_sort_compare_items_lexically, 378289177Speter scratch_pool); 379289177Speter if (b->patterns->nelts > 0) 380289177Speter { 381289177Speter svn_boolean_t match; 382289177Speter 383289177Speter SVN_ERR(match_credential(&match, &certinfo, cred_kind, realmstring, 384289177Speter b->patterns, sorted_cred_items, 385289177Speter scratch_pool, scratch_pool)); 386289177Speter if (!match) 387289177Speter return SVN_NO_ERROR; 388289177Speter } 389289177Speter 390289177Speter b->matches++; 391289177Speter 392289177Speter if (b->list) 393289177Speter SVN_ERR(list_credential(cred_kind, realmstring, sorted_cred_items, 394289177Speter b->show_passwords, certinfo, scratch_pool)); 395289177Speter if (b->delete) 396289177Speter { 397289177Speter *delete_cred = TRUE; 398289177Speter SVN_ERR(svn_cmdline_printf(scratch_pool, 399289177Speter _("Deleting %s credential for realm '%s'\n"), 400289177Speter cred_kind, realmstring)); 401289177Speter } 402289177Speter 403289177Speter return SVN_NO_ERROR; 404289177Speter} 405289177Speter 406289177Speter 407289177Speter/* This implements `svn_opt_subcommand_t'. */ 408289177Spetersvn_error_t * 409289177Spetersvn_cl__auth(apr_getopt_t *os, void *baton, apr_pool_t *pool) 410289177Speter{ 411289177Speter svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 412289177Speter const char *config_path; 413289177Speter struct walk_credentials_baton_t b; 414289177Speter 415289177Speter b.matches = 0; 416289177Speter b.show_passwords = opt_state->show_passwords; 417289177Speter b.list = !opt_state->remove; 418289177Speter b.delete = opt_state->remove; 419289177Speter b.patterns = apr_array_make(pool, 1, sizeof(const char *)); 420289177Speter for (; os->ind < os->argc; os->ind++) 421289177Speter { 422289177Speter /* The apr_getopt targets are still in native encoding. */ 423289177Speter const char *raw_target = os->argv[os->ind]; 424289177Speter const char *utf8_target; 425289177Speter 426289177Speter SVN_ERR(svn_utf_cstring_to_utf8(&utf8_target, 427289177Speter raw_target, pool)); 428289177Speter APR_ARRAY_PUSH(b.patterns, const char *) = utf8_target; 429289177Speter } 430289177Speter 431289177Speter SVN_ERR(svn_config_get_user_config_path(&config_path, 432289177Speter opt_state->config_dir, NULL, 433289177Speter pool)); 434289177Speter 435289177Speter if (b.delete && b.patterns->nelts < 1) 436289177Speter return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL); 437289177Speter 438289177Speter SVN_ERR(svn_config_walk_auth_data(config_path, walk_credentials, &b, pool)); 439289177Speter 440289177Speter if (b.list) 441289177Speter { 442289177Speter if (b.matches == 0) 443289177Speter { 444289177Speter if (b.patterns->nelts == 0) 445289177Speter SVN_ERR(svn_cmdline_printf(pool, 446289177Speter _("Credentials cache in '%s' is empty\n"), 447289177Speter svn_dirent_local_style(config_path, pool))); 448289177Speter else 449289177Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, 0, 450289177Speter _("Credentials cache in '%s' contains " 451289177Speter "no matching credentials"), 452289177Speter svn_dirent_local_style(config_path, pool)); 453289177Speter } 454289177Speter else 455289177Speter { 456289177Speter if (b.patterns->nelts == 0) 457289177Speter SVN_ERR(svn_cmdline_printf(pool, 458289177Speter _("Credentials cache in '%s' contains %d credentials\n"), 459289177Speter svn_dirent_local_style(config_path, pool), b.matches)); 460289177Speter else 461289177Speter SVN_ERR(svn_cmdline_printf(pool, 462289177Speter _("Credentials cache in '%s' contains %d matching " 463289177Speter "credentials\n"), 464289177Speter svn_dirent_local_style(config_path, pool), b.matches)); 465289177Speter } 466289177Speter 467289177Speter } 468289177Speter 469289177Speter if (b.delete) 470289177Speter { 471289177Speter if (b.matches == 0) 472289177Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, 0, 473289177Speter _("Credentials cache in '%s' contains " 474289177Speter "no matching credentials"), 475289177Speter svn_dirent_local_style(config_path, pool)); 476289177Speter else 477289177Speter SVN_ERR(svn_cmdline_printf(pool, _("Deleted %d matching credentials " 478289177Speter "from '%s'\n"), b.matches, 479289177Speter svn_dirent_local_style(config_path, pool))); 480289177Speter } 481289177Speter 482289177Speter return SVN_NO_ERROR; 483289177Speter} 484