aboutsummaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/subversion/subversion/subversion-CVE-2014-3522.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-devtools/subversion/subversion/subversion-CVE-2014-3522.patch')
-rw-r--r--meta/recipes-devtools/subversion/subversion/subversion-CVE-2014-3522.patch439
1 files changed, 439 insertions, 0 deletions
diff --git a/meta/recipes-devtools/subversion/subversion/subversion-CVE-2014-3522.patch b/meta/recipes-devtools/subversion/subversion/subversion-CVE-2014-3522.patch
new file mode 100644
index 0000000000..03d5b9710f
--- /dev/null
+++ b/meta/recipes-devtools/subversion/subversion/subversion-CVE-2014-3522.patch
@@ -0,0 +1,439 @@
+Upstream-Status: Backport
+
+Signed-off-by: Yue Tao <yue.tao@windriver.com>
+
+diff --git a/subversion/libsvn_ra_serf/util.c.old b/subversion/libsvn_ra_serf/util.c
+index b6c0141..8b09770 100644
+--- a/subversion/libsvn_ra_serf/util.c.old
++++ b/subversion/libsvn_ra_serf/util.c
+@@ -21,7 +21,6 @@
+ #define APR_WANT_STRFUNC
+ #include <apr.h>
+ #include <apr_want.h>
+-#include <apr_fnmatch.h>
+
+ #include <serf.h>
+ #include <serf_bucket_types.h>
+@@ -30,6 +29,7 @@
+ #include "svn_private_config.h"
+ #include "svn_xml.h"
+ #include "private/svn_dep_compat.h"
++#include "private/svn_cert.h"
+
+ #include "ra_serf.h"
+
+@@ -113,7 +113,12 @@ ssl_server_cert(void *baton, int failures,
+ apr_uint32_t svn_failures;
+ svn_error_t *err;
+ apr_hash_t *issuer, *subject, *serf_cert;
++ apr_array_header_t *san;
+ void *creds;
++ svn_boolean_t found_matching_hostname = FALSE;
++ svn_boolean_t found_san_entry = FALSE;
++ svn_string_t *actual_hostname =
++ svn_string_create(conn->hostname, scratch_pool);
+
+ /* Implicitly approve any non-server certs. */
+ if (serf_ssl_cert_depth(cert) > 0)
+@@ -129,6 +134,7 @@ ssl_server_cert(void *baton, int failures,
+ serf_cert = serf_ssl_cert_certificate(cert, subpool);
+
+ cert_info.hostname = apr_hash_get(subject, "CN", APR_HASH_KEY_STRING);
++ san = apr_hash_get(serf_cert, "subjectAltName", APR_HASH_KEY_STRING);
+ cert_info.fingerprint = apr_hash_get(serf_cert, "sha1", APR_HASH_KEY_STRING);
+ if (! cert_info.fingerprint)
+ cert_info.fingerprint = apr_pstrdup(subpool, "<unknown>");
+@@ -145,16 +145,43 @@ ssl_server_cert(void *baton, int failures,
+
+ svn_failures = ssl_convert_serf_failures(failures);
+
+- /* Match server certificate CN with the hostname of the server */
+- if (cert_info.hostname)
++ /* Try to find matching server name via subjectAltName first... */
++ if (san)
+ {
+- if (apr_fnmatch(cert_info.hostname, conn->hostinfo,
+- APR_FNM_PERIOD) == APR_FNM_NOMATCH)
++ int i;
++ found_san_entry = san->nelts > 0;
++ for (i = 0; i < san->nelts; i++)
+ {
+- svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
++ char *s = APR_ARRAY_IDX(san, i, char*);
++ svn_string_t *cert_hostname = svn_string_create(s, scratch_pool);
++
++ if (svn_cert__match_dns_identity(cert_hostname, actual_hostname))
++ {
++ found_matching_hostname = TRUE;
++ cert_info.hostname = s;
++ break;
++ }
+ }
+ }
+
++ /* Match server certificate CN with the hostname of the server iff
++ * we didn't find any subjectAltName fields and try to match them.
++ * Per RFC 2818 they are authoritative if present and CommonName
++ * should be ignored. */
++ if (!found_matching_hostname && !found_san_entry && cert_info.hostname)
++ {
++ svn_string_t *cert_hostname = svn_string_create(cert_info.hostname,
++ scratch_pool);
++
++ if (svn_cert__match_dns_identity(cert_hostname, actual_hostname))
++ {
++ found_matching_hostname = TRUE;
++ }
++ }
++
++ if (!found_matching_hostname)
++ svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
++
+ svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
+ SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
+ &svn_failures);
+@@ -261,6 +293,10 @@ svn_ra_serf__conn_setup(apr_socket_t *sock,
+ if (!conn->ssl_context)
+ {
+ conn->ssl_context = serf_bucket_ssl_encrypt_context_get(rb);
++
++#if SERF_VERSION_AT_LEAST(1,0,0)
++ serf_ssl_set_hostname(conn->ssl_context, conn->hostinfo);
++#endif
+
+ serf_ssl_client_cert_provider_set(conn->ssl_context,
+ svn_ra_serf__handle_client_cert,
+diff --git a/subversion/libsvn_subr/dirent_uri.c.old b/subversion/libsvn_subr/dirent_uri.c
+index eef99ba..a5f9e7e 100644
+--- a/subversion/libsvn_subr/dirent_uri.c.old
++++ b/subversion/libsvn_subr/dirent_uri.c
+@@ -30,6 +30,7 @@
+ #include "svn_path.h"
+
+ #include "private_uri.h"
++#include "private/svn_cert.h"
+
+ /* The canonical empty path. Can this be changed? Well, change the empty
+ test below and the path library will work, not so sure about the fs/wc
+@@ -1194,3 +1195,81 @@ svn_uri_is_canonical(const char *uri, apr_pool_t *pool)
+
+ return TRUE;
+ }
++
++
++/* -------------- The cert API (see private/svn_cert.h) ------------- */
++
++svn_boolean_t
++svn_cert__match_dns_identity(svn_string_t *pattern, svn_string_t *hostname)
++{
++ apr_size_t pattern_pos = 0, hostname_pos = 0;
++
++ /* support leading wildcards that composed of the only character in the
++ * left-most label. */
++ if (pattern->len >= 2 &&
++ pattern->data[pattern_pos] == '*' &&
++ pattern->data[pattern_pos + 1] == '.')
++ {
++ while (hostname_pos < hostname->len &&
++ hostname->data[hostname_pos] != '.')
++ {
++ hostname_pos++;
++ }
++ /* Assume that the wildcard must match something. Rule 2 says
++ * that *.example.com should not match example.com. If the wildcard
++ * ends up not matching anything then it matches .example.com which
++ * seems to be essentially the same as just example.com */
++ if (hostname_pos == 0)
++ return FALSE;
++
++ pattern_pos++;
++ }
++
++ while (pattern_pos < pattern->len && hostname_pos < hostname->len)
++ {
++ char pattern_c = pattern->data[pattern_pos];
++ char hostname_c = hostname->data[hostname_pos];
++
++ /* fold case as described in RFC 4343.
++ * Note: We actually convert to lowercase, since our URI
++ * canonicalization code converts to lowercase and generally
++ * most certs are issued with lowercase DNS names, meaning
++ * this avoids the fold operation in most cases. The RFC
++ * suggests the opposite transformation, but doesn't require
++ * any specific implementation in any case. It is critical
++ * that this folding be locale independent so you can't use
++ * tolower(). */
++ pattern_c = canonicalize_to_lower(pattern_c);
++ hostname_c = canonicalize_to_lower(hostname_c);
++
++ if (pattern_c != hostname_c)
++ {
++ /* doesn't match */
++ return FALSE;
++ }
++ else
++ {
++ /* characters match so skip both */
++ pattern_pos++;
++ hostname_pos++;
++ }
++ }
++
++ /* ignore a trailing period on the hostname since this has no effect on the
++ * security of the matching. See the following for the long explanation as
++ * to why:
++ * https://bugzilla.mozilla.org/show_bug.cgi?id=134402#c28
++ */
++ if (pattern_pos == pattern->len &&
++ hostname_pos == hostname->len - 1 &&
++ hostname->data[hostname_pos] == '.')
++ hostname_pos++;
++
++ if (pattern_pos != pattern->len || hostname_pos != hostname->len)
++ {
++ /* end didn't match */
++ return FALSE;
++ }
++
++ return TRUE;
++}
+diff --git a/subversion/tests/libsvn_subr/dirent_uri-test.c.old b/subversion/tests/libsvn_subr/dirent_uri-test.c
+index d71d9c1..370b64a 100644
+--- a/subversion/tests/libsvn_subr/dirent_uri-test.c.old
++++ b/subversion/tests/libsvn_subr/dirent_uri-test.c
+@@ -31,6 +31,7 @@
+
+ #include "svn_pools.h"
+ #include "svn_dirent_uri.h"
++#include "private/svn_cert.h"
+
+ #include "../svn_test.h"
+ #include "../../libsvn_subr/private_uri.h"
+@@ -1671,6 +1672,145 @@ test_uri_internal_style(const char **msg,
+ return SVN_NO_ERROR;
+ }
+
++struct cert_match_dns_test {
++ const char *pattern;
++ const char *hostname;
++ svn_boolean_t expected;
++};
++
++static svn_error_t *
++run_cert_match_dns_tests(struct cert_match_dns_test *tests, apr_pool_t *pool)
++{
++ struct cert_match_dns_test *ct;
++ apr_pool_t *iterpool = svn_pool_create(pool);
++
++ for (ct = tests; ct->pattern; ct++)
++ {
++ svn_boolean_t result;
++ svn_string_t *pattern, *hostname;
++
++ svn_pool_clear(iterpool);
++
++ pattern = svn_string_create(ct->pattern, iterpool);
++ hostname = svn_string_create(ct->hostname, iterpool);
++
++ result = svn_cert__match_dns_identity(pattern, hostname);
++ if (result != ct->expected)
++ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
++ "Expected %s but got %s for pattern '%s' on "
++ "hostname '%s'",
++ ct->expected ? "match" : "no match",
++ result ? "match" : "no match",
++ pattern->data, hostname->data);
++
++ }
++
++ svn_pool_destroy(iterpool);
++
++ return SVN_NO_ERROR;
++}
++
++static struct cert_match_dns_test cert_match_dns_tests[] = {
++ { "foo.example.com", "foo.example.com", TRUE }, /* exact match */
++ { "foo.example.com", "FOO.EXAMPLE.COM", TRUE }, /* case differences */
++ { "FOO.EXAMPLE.COM", "foo.example.com", TRUE },
++ { "*.example.com", "FoO.ExAmPlE.CoM", TRUE },
++ { "*.ExAmPlE.CoM", "foo.example.com", TRUE },
++ { "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", TRUE },
++ { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", TRUE },
++ { "foo.example.com", "bar.example.com", FALSE }, /* difference at start */
++ { "foo.example.com", "foo.example.net", FALSE }, /* difference at end */
++ { "foo.example.com", "foo.example.commercial", FALSE }, /* hostname longer */
++ { "foo.example.commercial", "foo.example.com", FALSE }, /* pattern longer */
++ { "foo.example.comcom", "foo.example.com", FALSE }, /* repeated suffix */
++ { "foo.example.com", "foo.example.comcom", FALSE },
++ { "foo.example.com.com", "foo.example.com", FALSE },
++ { "foo.example.com", "foo.example.com.com", FALSE },
++ { "foofoo.example.com", "foo.example.com", FALSE }, /* repeated prefix */
++ { "foo.example.com", "foofoo.example.com", FALSE },
++ { "foo.foo.example.com", "foo.example.com", FALSE },
++ { "foo.example.com", "foo.foo.example.com", FALSE },
++ { "foo.*.example.com", "foo.bar.example.com", FALSE }, /* RFC 6125 s. 6.4.3
++ Rule 1 */
++ { "*.example.com", "foo.example.com", TRUE }, /* RFC 6125 s. 6.4.3 Rule 2 */
++ { "*.example.com", "bar.foo.example.com", FALSE }, /* Rule 2 */
++ { "*.example.com", "example.com", FALSE }, /* Rule 2 */
++ { "*.example.com", ".example.com", FALSE }, /* RFC doesn't say what to do
++ here and a leading period on
++ a hostname doesn't make sense
++ so we'll just reject this. */
++ { "*", "foo.example.com", FALSE }, /* wildcard must be left-most label,
++ implies that there must be more than
++ one label. */
++ { "*", "example.com", FALSE },
++ { "*", "com", FALSE },
++ { "*.example.com", "foo.example.net", FALSE }, /* difference in literal text
++ with a wildcard. */
++ { "*.com", "example.com", TRUE }, /* See Errata ID 3090 for RFC 6125,
++ probably shouldn't allow this but
++ we do for now. */
++ { "*.", "example.com", FALSE }, /* test some dubious 2 character wildcard
++ patterns */
++ { "*.", "example.", TRUE }, /* This one feels questionable */
++ { "*.", "example", FALSE },
++ { "*.", ".", FALSE },
++ { "a", "a", TRUE }, /* check that single letter exact matches work */
++ { "a", "b", FALSE }, /* and single letter not matches shouldn't */
++ { "*.*.com", "foo.example.com", FALSE }, /* unsupported wildcards */
++ { "*.*.com", "example.com", FALSE },
++ { "**.example.com", "foo.example.com", FALSE },
++ { "**.example.com", "example.com", FALSE },
++ { "f*.example.com", "foo.example.com", FALSE },
++ { "f*.example.com", "bar.example.com", FALSE },
++ { "*o.example.com", "foo.example.com", FALSE },
++ { "*o.example.com", "bar.example.com", FALSE },
++ { "f*o.example.com", "foo.example.com", FALSE },
++ { "f*o.example.com", "bar.example.com", FALSE },
++ { "foo.e*.com", "foo.example.com", FALSE },
++ { "foo.*e.com", "foo.example.com", FALSE },
++ { "foo.e*e.com", "foo.example.com", FALSE },
++ { "foo.example.com", "foo.example.com.", TRUE }, /* trailing dot */
++ { "*.example.com", "foo.example.com.", TRUE },
++ { "foo", "foo.", TRUE },
++ { "foo.example.com.", "foo.example.com", FALSE },
++ { "*.example.com.", "foo.example.com", FALSE },
++ { "foo.", "foo", FALSE },
++ { "foo.example.com", "foo.example.com..", FALSE },
++ { "*.example.com", "foo.example.com..", FALSE },
++ { "foo", "foo..", FALSE },
++ { "foo.example.com..", "foo.example.com", FALSE },
++ { "*.example.com..", "foo.example.com", FALSE },
++ { "foo..", "foo", FALSE },
++ { NULL }
++};
++
++static svn_error_t *
++test_cert_match_dns_identity(apr_pool_t *pool)
++{
++ return run_cert_match_dns_tests(cert_match_dns_tests, pool);
++}
++
++/* This test table implements results that should happen if we supported
++ * RFC 6125 s. 6.4.3 Rule 3. We don't so it's expected to fail for now. */
++static struct cert_match_dns_test rule3_tests[] = {
++ { "baz*.example.net", "baz1.example.net", TRUE },
++ { "*baz.example.net", "foobaz.example.net", TRUE },
++ { "b*z.example.net", "buuz.example.net", TRUE },
++ { "b*z.example.net", "bz.example.net", FALSE }, /* presume wildcard can't
++ match nothing */
++ { "baz*.example.net", "baz.example.net", FALSE },
++ { "*baz.example.net", "baz.example.net", FALSE },
++ { "b*z.example.net", "buuzuuz.example.net", TRUE }, /* presume wildcard
++ should be greedy */
++ { NULL }
++};
++
++static svn_error_t *
++test_rule3(apr_pool_t *pool)
++{
++ return run_cert_match_dns_tests(rule3_tests, pool);
++}
++
+
+ /* The test table. */
+
+@@ -1699,5 +1839,7 @@ struct svn_test_descriptor_t test_funcs[] =
+ SVN_TEST_PASS(test_uri_local_style),
+ SVN_TEST_PASS(test_dirent_internal_style),
+ SVN_TEST_PASS(test_uri_internal_style),
++ SVN_TEST_PASS(test_cert_match_dns_identity),
++ SVN_TEST_XFAIL(test_rule3),
+ SVN_TEST_NULL
+ };
+diff --git a/subversion/include/private/svn_cert.h b/subversion/include/private/svn_cert.h
+new file mode 100644
+index 0000000..32e32a0
+--- /dev/null
++++ b/subversion/include/private/svn_cert.h
+@@ -0,0 +1,68 @@
++/**
++ * @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 svn_cert.h
++ * @brief Implementation of certificate validation functions
++ */
++
++#ifndef SVN_CERT_H
++#define SVN_CERT_H
++
++#include <apr.h>
++
++#include "svn_types.h"
++#include "svn_string.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif /* __cplusplus */
++
++
++/* Return TRUE iff @a pattern matches @a hostname as defined
++ * by the matching rules of RFC 6125. In the context of RFC
++ * 6125 the pattern is the domain name portion of the presented
++ * identifier (which comes from the Common Name or a DNSName
++ * portion of the subjectAltName of an X.509 certificate) and
++ * the hostname is the source domain (i.e. the host portion
++ * of the URI the user entered).
++ *
++ * @note With respect to wildcards we only support matching
++ * wildcards in the left-most label and as the only character
++ * in the left-most label (i.e. we support RFC 6125 ยง 6.4.3
++ * Rule 1 and 2 but not the optional Rule 3). This may change
++ * in the future.
++ *
++ * @note Subversion does not at current support internationalized
++ * domain names. Both values are presumed to be in NR-LDH label
++ * or A-label form (see RFC 5890 for the definition).
++ *
++ * @since New in 1.9.
++ */
++svn_boolean_t
++svn_cert__match_dns_identity(svn_string_t *pattern, svn_string_t *hostname);
++
++
++#ifdef __cplusplus
++}
++#endif /* __cplusplus */
++
++#endif /* SVN_CERT_H */