summaryrefslogtreecommitdiff
path: root/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp')
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp492
1 files changed, 492 insertions, 0 deletions
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp
new file mode 100644
index 0000000..0630098
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp
@@ -0,0 +1,492 @@
+/**
+ * @copyright
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_util_ConfigLib.cpp
+ * @brief Implementation of the native methods in the Java class ConfigLib
+ */
+
+#include "../include/org_apache_subversion_javahl_util_ConfigLib.h"
+
+#include <apr_fnmatch.h>
+#include <apr_strings.h>
+
+#include "jniwrapper/jni_list.hpp"
+#include "jniwrapper/jni_stack.hpp"
+
+#include "AuthnCallback.hpp"
+#include "Credential.hpp"
+#include "SubversionException.hpp"
+
+#include "GlobalConfig.h"
+#include "JNIUtil.h"
+#include "JNICriticalSection.h"
+
+#include "svn_auth.h"
+#include "svn_base64.h"
+#include "svn_config.h"
+#include "svn_hash.h"
+#include "svn_x509.h"
+
+#include "svn_private_config.h"
+
+namespace {
+bool g_ignore_native_credentials = false;
+} // anonymous namespace
+
+bool GlobalConfig::useNativeCredentialsStore()
+{
+ JNICriticalSection lock(*JNIUtil::g_configMutex);
+ return !g_ignore_native_credentials;
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_util_ConfigLib_enableNativeCredentialsStore(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(ConfigLib, enableNativeCredentialsStore)
+ {
+ JNICriticalSection lock(*JNIUtil::g_configMutex);
+ g_ignore_native_credentials = false;
+ }
+ SVN_JAVAHL_JNI_CATCH;
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_util_ConfigLib_disableNativeCredentialsStore(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(ConfigLib, disableNativeCredentialsStore)
+ {
+ JNICriticalSection lock(*JNIUtil::g_configMutex);
+ g_ignore_native_credentials = true;
+ }
+ SVN_JAVAHL_JNI_CATCH;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_org_apache_subversion_javahl_util_ConfigLib_isNativeCredentialsStoreEnabled(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(ConfigLib, isNativeCredentialsStoreEnabled)
+ {
+ return jboolean(GlobalConfig::useNativeCredentialsStore());
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return JNI_FALSE;
+}
+
+
+namespace {
+jobject build_credential(Java::Env env, apr_hash_t* cred,
+ const char* cred_kind, const char* realm,
+ apr_pool_t* scratch_pool)
+{
+ svn_string_t* entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_REALMSTRING_KEY));
+ if (!entry || !realm || 0 != strcmp(realm, entry->data))
+ {
+ JavaHL::SubversionException(env).throw_java_exception(
+ apr_psprintf(scratch_pool,
+ "Unexpected realm; got: [%s], expected: [%s]",
+ (entry ? entry->data : "(null)"),
+ (realm ? realm : "(null)")));
+ }
+
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_PASSTYPE_KEY));
+
+ const char* store = (entry ? entry->data : NULL);
+ const char* username = NULL;
+ const char* password = NULL;
+ jobject info = NULL;
+ jobject failures = NULL;
+ const char* passphrase = NULL;
+
+ if (0 == strcmp(cred_kind, SVN_AUTH_CRED_USERNAME))
+ {
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_USERNAME_KEY));
+ if (entry)
+ username = entry->data;
+ }
+ else if (0 == strcmp(cred_kind, SVN_AUTH_CRED_SIMPLE))
+ {
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_USERNAME_KEY));
+ if (entry)
+ username = entry->data;
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_PASSWORD_KEY));
+ if (entry)
+ password = entry->data;
+ }
+ else if (0 == strcmp(cred_kind, SVN_AUTH_CRED_SSL_SERVER_TRUST))
+ {
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_ASCII_CERT_KEY));
+ const char* ascii_cert = (entry ? entry->data : NULL);
+
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_FAILURES_KEY));
+ jint failflags = (!entry ? 0 : jint(apr_atoi64(entry->data)));
+
+ info = JavaHL::AuthnCallback::SSLServerCertInfo(env, ascii_cert).get();
+ failures = JavaHL::AuthnCallback
+ ::SSLServerCertFailures(env, failflags).get();
+ }
+ else if (0 == strcmp(cred_kind, SVN_AUTH_CRED_SSL_CLIENT_CERT_PW))
+ {
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_PASSPHRASE_KEY));
+ if (entry)
+ passphrase = entry->data;
+ }
+ else
+ {
+ JavaHL::SubversionException(env).throw_java_exception(
+ apr_psprintf(scratch_pool,
+ "Invalid credential type: [%s]",
+ cred_kind));
+ }
+
+ return JavaHL::Credential(
+ env,
+ JavaHL::Credential::Kind(env, Java::String(env, cred_kind)).get(),
+ Java::String(env, realm), Java::String(env, store),
+ Java::String(env, username), Java::String(env, password),
+ info, failures, Java::String(env, passphrase)).get();
+}
+
+class WalkCredentialsCallback
+{
+public:
+ static svn_error_t* walk_func(svn_boolean_t *delete_cred,
+ void *walk_baton,
+ const char *cred_kind,
+ const char *realmstring,
+ apr_hash_t *cred,
+ apr_pool_t *scratch_pool)
+ {
+ WalkCredentialsCallback& self =
+ *static_cast<WalkCredentialsCallback*>(walk_baton);
+ return self(delete_cred, cred_kind, realmstring, cred, scratch_pool);
+ }
+
+ virtual svn_error_t* operator()(svn_boolean_t *delete_cred,
+ const char *cred_kind,
+ const char *realmstring,
+ apr_hash_t *cred_hash,
+ apr_pool_t *scratch_pool) = 0;
+};
+
+class SimpleSearchCallback : public WalkCredentialsCallback
+{
+ const ::Java::Env m_env;
+ const char* const m_cred_kind;
+ const char* const m_realm;
+ const bool m_delete_when_found;
+ jobject m_credential;
+
+public:
+ explicit SimpleSearchCallback(::Java::Env env,
+ const char* cred_kind, const char* realm,
+ bool delete_when_found)
+ : m_env(env),
+ m_cred_kind(cred_kind),
+ m_realm(realm),
+ m_delete_when_found(delete_when_found),
+ m_credential(NULL)
+ {}
+
+ jobject credential() const { return m_credential; }
+
+ virtual svn_error_t* operator()(svn_boolean_t *delete_cred,
+ const char *cred_kind,
+ const char *realmstring,
+ apr_hash_t *cred_hash,
+ apr_pool_t *scratch_pool)
+ {
+ if (0 == strcmp(cred_kind, m_cred_kind)
+ && 0 == strcmp(realmstring, m_realm))
+ {
+ m_credential = build_credential(m_env, cred_hash,
+ cred_kind, realmstring,
+ scratch_pool);
+ *delete_cred = m_delete_when_found;
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, "");
+ }
+
+ *delete_cred = false;
+ return SVN_NO_ERROR;
+ }
+};
+} // anonymous namespace
+
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_util_ConfigLib_nativeGetCredential(
+ JNIEnv* jenv, jobject jthis,
+ jstring jconfig_dir, jstring jcred_kind, jstring jrealm)
+{
+ SVN_JAVAHL_JNI_TRY(ConfigLib, nativeGetCredential)
+ {
+ if (!GlobalConfig::useNativeCredentialsStore())
+ return NULL;
+
+ const Java::Env env(jenv);
+ const Java::String config_dir(env, jconfig_dir);
+ const Java::String cred_kind(env, jcred_kind);
+ const Java::String realm(env, jrealm);
+
+ // Using a "global" request pool since we don't keep a context
+ // with its own pool around for these functions.
+ SVN::Pool pool;
+
+ SimpleSearchCallback cb(env,
+ cred_kind.strdup(pool.getPool()),
+ realm.strdup(pool.getPool()),
+ false);
+
+ SVN_JAVAHL_CHECK(env,
+ svn_config_walk_auth_data(
+ Java::String::Contents(config_dir).c_str(),
+ cb.walk_func, &cb, pool.getPool()));
+ return cb.credential();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_util_ConfigLib_nativeRemoveCredential(
+ JNIEnv* jenv, jobject jthis,
+ jstring jconfig_dir, jstring jcred_kind, jstring jrealm)
+{
+ SVN_JAVAHL_JNI_TRY(ConfigLib, nativeRemoveCredential)
+ {
+ if (!GlobalConfig::useNativeCredentialsStore())
+ return NULL;
+
+ const Java::Env env(jenv);
+ const Java::String config_dir(env, jconfig_dir);
+ const Java::String cred_kind(env, jcred_kind);
+ const Java::String realm(env, jrealm);
+
+ // Using a "global" request pool since we don't keep a context
+ // with its own pool around for these functions.
+ SVN::Pool pool;
+
+ SimpleSearchCallback cb(env,
+ cred_kind.strdup(pool.getPool()),
+ realm.strdup(pool.getPool()),
+ true);
+
+ SVN_JAVAHL_CHECK(env,
+ svn_config_walk_auth_data(
+ Java::String::Contents(config_dir).c_str(),
+ cb.walk_func, &cb, pool.getPool()));
+ return cb.credential();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_util_ConfigLib_nativeSearchCredentials(
+ JNIEnv* jenv, jobject jthis,
+ jstring jconfig_dir, jstring jcred_kind,
+ jstring jrealm_pattern, jstring jusername_pattern,
+ jstring jhostname_pattern, jstring jtext_pattern)
+{
+ SVN_JAVAHL_JNI_TRY(ConfigLib, iterateCredentials)
+ {
+ if (!GlobalConfig::useNativeCredentialsStore())
+ return NULL;
+
+ const Java::Env env(jenv);
+ const Java::String config_dir(env, jconfig_dir);
+ const Java::String cred_kind(env, jcred_kind);
+ const Java::String realm_pattern(env, jrealm_pattern);
+ const Java::String username_pattern(env, jusername_pattern);
+ const Java::String hostname_pattern(env, jhostname_pattern);
+ const Java::String text_pattern(env, jtext_pattern);
+
+ // Using a "global" request pool since we don't keep a context
+ // with its own pool around for these functions.
+ SVN::Pool pool;
+
+ class Callback : public WalkCredentialsCallback
+ {
+ const char* const m_cred_kind;
+ const char* const m_realm_pattern;
+ const char* const m_username_pattern;
+ const char* const m_hostname_pattern;
+ const char* const m_text_pattern;
+
+ const ::Java::Env m_env;
+ ::Java::List<JavaHL::Credential> m_credentials;
+
+ bool match_array(const char* pattern,
+ const apr_array_header_t* hostnames)
+ {
+ for (int i = 0; i < hostnames->nelts; ++i)
+ {
+ const char* const hostname =
+ APR_ARRAY_IDX(hostnames, i, const char*);
+ if (!apr_fnmatch(pattern, hostname, 0))
+ return true;
+ }
+ return false;
+ }
+
+ public:
+ explicit Callback(::Java::Env ctor_env,
+ const char* ctor_cred_kind,
+ const char* ctor_realm_pattern,
+ const char* ctor_username_pattern,
+ const char* ctor_hostname_pattern,
+ const char* ctor_text_pattern)
+ : m_cred_kind(ctor_cred_kind),
+ m_realm_pattern(ctor_realm_pattern),
+ m_username_pattern(ctor_username_pattern),
+ m_hostname_pattern(ctor_hostname_pattern),
+ m_text_pattern(ctor_text_pattern),
+ m_env(ctor_env),
+ m_credentials(ctor_env)
+ {}
+
+ jobject credentials() const
+ {
+ if (m_credentials.is_empty())
+ return NULL;
+ return m_credentials.get();
+ }
+
+ virtual svn_error_t* operator()(svn_boolean_t *delete_cred,
+ const char *cb_cred_kind,
+ const char *cb_realmstring,
+ apr_hash_t *cb_cred_hash,
+ apr_pool_t *cb_scratch_pool)
+ {
+ *delete_cred = false;
+ if (m_cred_kind && 0 != strcmp(cb_cred_kind, m_cred_kind))
+ return SVN_NO_ERROR;
+
+ svn_string_t* entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cb_cred_hash, SVN_CONFIG_AUTHN_USERNAME_KEY));
+ const char* const username = (entry ? entry->data : NULL);
+
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cb_cred_hash, SVN_CONFIG_AUTHN_PASSTYPE_KEY));
+ const char* const store = (entry ? entry->data : NULL);
+
+ const svn_string_t* ascii_cert = static_cast<svn_string_t*>(
+ svn_hash_gets(cb_cred_hash, SVN_CONFIG_AUTHN_ASCII_CERT_KEY));
+
+ /* Parsed certificate data. */
+ const char* subject = NULL;
+ const char* issuer = NULL;
+ const char* fingerprint = NULL;
+ const apr_array_header_t* hostnames = NULL;
+
+ if (ascii_cert)
+ {
+ const svn_string_t* const der =
+ svn_base64_decode_string(ascii_cert, cb_scratch_pool);
+ svn_x509_certinfo_t* certinfo;
+ svn_error_t* err = svn_x509_parse_cert(
+ &certinfo, der->data, der->len,
+ cb_scratch_pool, cb_scratch_pool);
+ if (err)
+ {
+ // Ignore credentials that can't be parsed.
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ else
+ {
+ subject = svn_x509_certinfo_get_subject(
+ certinfo, cb_scratch_pool);
+ issuer = svn_x509_certinfo_get_issuer(
+ certinfo, cb_scratch_pool);
+ fingerprint = svn_checksum_to_cstring_display(
+ svn_x509_certinfo_get_digest(certinfo),
+ cb_scratch_pool);
+ hostnames = svn_x509_certinfo_get_hostnames(certinfo);
+ }
+ }
+
+ bool match = (m_realm_pattern
+ && !apr_fnmatch(m_realm_pattern, cb_realmstring, 0));
+
+ if (!match && m_username_pattern)
+ match = (match
+ || (username
+ && !apr_fnmatch(m_username_pattern, username, 0)));
+
+ if (!match && m_hostname_pattern)
+ match = (match
+ || (hostnames
+ && match_array(m_hostname_pattern, hostnames)));
+
+ if (!match && m_text_pattern)
+ match = (match
+ || (username
+ && !apr_fnmatch(m_text_pattern, username, 0))
+ || (store
+ && !apr_fnmatch(m_text_pattern, store, 0))
+ || (subject
+ && !apr_fnmatch(m_text_pattern, subject, 0))
+ || (issuer
+ && !apr_fnmatch(m_text_pattern, issuer, 0))
+ || (fingerprint
+ && !apr_fnmatch(m_text_pattern, fingerprint, 0))
+ || (hostnames
+ && match_array(m_text_pattern, hostnames)));
+
+ if (match)
+ m_credentials.add(
+ ::JavaHL::Credential(m_env,
+ build_credential(m_env,
+ cb_cred_hash,
+ cb_cred_kind,
+ cb_realmstring,
+ cb_scratch_pool)));
+
+ return SVN_NO_ERROR;
+ }
+ } cb(env,
+ cred_kind.strdup(pool.getPool()),
+ realm_pattern.strdup(pool.getPool()),
+ username_pattern.strdup(pool.getPool()),
+ hostname_pattern.strdup(pool.getPool()),
+ text_pattern.strdup(pool.getPool()));
+
+ SVN_JAVAHL_CHECK(env,
+ svn_config_walk_auth_data(
+ Java::String::Contents(config_dir).c_str(),
+ cb.walk_func, &cb, pool.getPool()));
+ return cb.credentials();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}