summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/subversion
diff options
context:
space:
mode:
authorYue Tao <Yue.Tao@windriver.com>2014-10-22 03:37:28 -0400
committerRichard Purdie <richard.purdie@linuxfoundation.org>2014-11-04 10:27:07 +0000
commit514a91df49b0d358c26818c40e9e281a98fba8db (patch)
tree8daabd208de71c5e6342c426eefdd5a2c4c52f0b /meta/recipes-devtools/subversion
parentd962e4550b8a21704818e2f3e9cc49bbed6e43e8 (diff)
downloadpoky-514a91df49b0d358c26818c40e9e281a98fba8db.tar.gz
subversion: Security Advisory - subversion - CVE-2014-3522
The Serf RA layer in Apache Subversion 1.4.0 through 1.7.x before 1.7.18 and 1.8.x before 1.8.10 does not properly handle wildcards in the Common Name (CN) or subjectAltName field of the X.509 certificate, which allows man-in-the-middle attackers to spoof servers via a crafted certificate.<a href=http://cwe.mitre.org/data/definitions/297.html target=_blank>CWE-297: Improper Validation of Certificate with Host Mismatch</a> http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-3522 (From OE-Core rev: 06a33cd00ea11abec1ebe9d5883e44778075ccc6) Signed-off-by: Yue Tao <Yue.Tao@windriver.com> Signed-off-by: Jackie Huang <jackie.huang@windriver.com> Signed-off-by: Ross Burton <ross.burton@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-devtools/subversion')
-rw-r--r--meta/recipes-devtools/subversion/subversion-1.8.9/subversion-CVE-2014-3522.patch444
-rw-r--r--meta/recipes-devtools/subversion/subversion/subversion-CVE-2014-3522.patch439
-rw-r--r--meta/recipes-devtools/subversion/subversion_1.6.15.bb4
-rw-r--r--meta/recipes-devtools/subversion/subversion_1.8.9.bb1
4 files changed, 887 insertions, 1 deletions
diff --git a/meta/recipes-devtools/subversion/subversion-1.8.9/subversion-CVE-2014-3522.patch b/meta/recipes-devtools/subversion/subversion-1.8.9/subversion-CVE-2014-3522.patch
new file mode 100644
index 0000000000..f259e5490a
--- /dev/null
+++ b/meta/recipes-devtools/subversion/subversion-1.8.9/subversion-CVE-2014-3522.patch
@@ -0,0 +1,444 @@
1Upstream-Status: Backport
2
3Signed-off-by: Jackie Huang <jackie.huang@windriver.com>
4
5Index: subversion/include/private/svn_cert.h
6===================================================================
7--- subversion/include/private/svn_cert.h (nonexistent)
8+++ subversion/include/private/svn_cert.h (working copy)
9@@ -0,0 +1,68 @@
10+/**
11+ * @copyright
12+ * ====================================================================
13+ * Licensed to the Apache Software Foundation (ASF) under one
14+ * or more contributor license agreements. See the NOTICE file
15+ * distributed with this work for additional information
16+ * regarding copyright ownership. The ASF licenses this file
17+ * to you under the Apache License, Version 2.0 (the
18+ * "License"); you may not use this file except in compliance
19+ * with the License. You may obtain a copy of the License at
20+ *
21+ * http://www.apache.org/licenses/LICENSE-2.0
22+ *
23+ * Unless required by applicable law or agreed to in writing,
24+ * software distributed under the License is distributed on an
25+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
26+ * KIND, either express or implied. See the License for the
27+ * specific language governing permissions and limitations
28+ * under the License.
29+ * ====================================================================
30+ * @endcopyright
31+ *
32+ * @file svn_cert.h
33+ * @brief Implementation of certificate validation functions
34+ */
35+
36+#ifndef SVN_CERT_H
37+#define SVN_CERT_H
38+
39+#include <apr.h>
40+
41+#include "svn_types.h"
42+#include "svn_string.h"
43+
44+#ifdef __cplusplus
45+extern "C" {
46+#endif /* __cplusplus */
47+
48+
49+/* Return TRUE iff @a pattern matches @a hostname as defined
50+ * by the matching rules of RFC 6125. In the context of RFC
51+ * 6125 the pattern is the domain name portion of the presented
52+ * identifier (which comes from the Common Name or a DNSName
53+ * portion of the subjectAltName of an X.509 certificate) and
54+ * the hostname is the source domain (i.e. the host portion
55+ * of the URI the user entered).
56+ *
57+ * @note With respect to wildcards we only support matching
58+ * wildcards in the left-most label and as the only character
59+ * in the left-most label (i.e. we support RFC 6125 s. 6.4.3
60+ * Rule 1 and 2 but not the optional Rule 3). This may change
61+ * in the future.
62+ *
63+ * @note Subversion does not at current support internationalized
64+ * domain names. Both values are presumed to be in NR-LDH label
65+ * or A-label form (see RFC 5890 for the definition).
66+ *
67+ * @since New in 1.9.
68+ */
69+svn_boolean_t
70+svn_cert__match_dns_identity(svn_string_t *pattern, svn_string_t *hostname);
71+
72+
73+#ifdef __cplusplus
74+}
75+#endif /* __cplusplus */
76+
77+#endif /* SVN_CERT_H */
78Index: subversion/libsvn_ra_serf/util.c
79===================================================================
80--- subversion/libsvn_ra_serf/util.c (revision 1615128)
81+++ subversion/libsvn_ra_serf/util.c (working copy)
82@@ -28,7 +28,6 @@
83 #define APR_WANT_STRFUNC
84 #include <apr.h>
85 #include <apr_want.h>
86-#include <apr_fnmatch.h>
87
88 #include <serf.h>
89 #include <serf_bucket_types.h>
90@@ -49,6 +48,7 @@
91 #include "private/svn_fspath.h"
92 #include "private/svn_subr_private.h"
93 #include "private/svn_auth_private.h"
94+#include "private/svn_cert.h"
95
96 #include "ra_serf.h"
97
98@@ -274,7 +274,6 @@ ssl_server_cert(void *baton, int failures,
99 apr_hash_t *subject = NULL;
100 apr_hash_t *serf_cert = NULL;
101 void *creds;
102- int found_matching_hostname = 0;
103
104 svn_failures = (ssl_convert_serf_failures(failures)
105 | conn->server_cert_failures);
106@@ -286,26 +285,37 @@ ssl_server_cert(void *baton, int failures,
107 ### This should really be handled by serf, which should pass an error
108 for this case, but that has backwards compatibility issues. */
109 apr_array_header_t *san;
110+ svn_boolean_t found_san_entry = FALSE;
111+ svn_boolean_t found_matching_hostname = FALSE;
112+ svn_string_t *actual_hostname =
113+ svn_string_create(conn->session->session_url.hostname, scratch_pool);
114
115 serf_cert = serf_ssl_cert_certificate(cert, scratch_pool);
116
117 san = svn_hash_gets(serf_cert, "subjectAltName");
118 /* Try to find matching server name via subjectAltName first... */
119- if (san) {
120+ if (san)
121+ {
122 int i;
123- for (i = 0; i < san->nelts; i++) {
124+ found_san_entry = san->nelts > 0;
125+ for (i = 0; i < san->nelts; i++)
126+ {
127 const char *s = APR_ARRAY_IDX(san, i, const char*);
128- if (apr_fnmatch(s, conn->session->session_url.hostname,
129- APR_FNM_PERIOD | APR_FNM_CASE_BLIND) == APR_SUCCESS)
130- {
131- found_matching_hostname = 1;
132+ svn_string_t *cert_hostname = svn_string_create(s, scratch_pool);
133+
134+ if (svn_cert__match_dns_identity(cert_hostname, actual_hostname))
135+ {
136+ found_matching_hostname = TRUE;
137 break;
138- }
139- }
140- }
141+ }
142+ }
143+ }
144
145- /* Match server certificate CN with the hostname of the server */
146- if (!found_matching_hostname)
147+ /* Match server certificate CN with the hostname of the server iff
148+ * we didn't find any subjectAltName fields and try to match them.
149+ * Per RFC 2818 they are authoritative if present and CommonName
150+ * should be ignored. */
151+ if (!found_matching_hostname && !found_san_entry)
152 {
153 const char *hostname = NULL;
154
155@@ -314,13 +324,20 @@ ssl_server_cert(void *baton, int failures,
156 if (subject)
157 hostname = svn_hash_gets(subject, "CN");
158
159- if (!hostname
160- || apr_fnmatch(hostname, conn->session->session_url.hostname,
161- APR_FNM_PERIOD | APR_FNM_CASE_BLIND) != APR_SUCCESS)
162- {
163- svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
164- }
165- }
166+ if (hostname)
167+ {
168+ svn_string_t *cert_hostname = svn_string_create(hostname,
169+ scratch_pool);
170+
171+ if (svn_cert__match_dns_identity(cert_hostname, actual_hostname))
172+ {
173+ found_matching_hostname = TRUE;
174+ }
175+ }
176+ }
177+
178+ if (!found_matching_hostname)
179+ svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
180 }
181
182 if (!svn_failures)
183Index: subversion/libsvn_subr/dirent_uri.c
184===================================================================
185--- subversion/libsvn_subr/dirent_uri.c (revision 1615128)
186+++ subversion/libsvn_subr/dirent_uri.c (working copy)
187@@ -38,6 +38,7 @@
188
189 #include "dirent_uri.h"
190 #include "private/svn_fspath.h"
191+#include "private/svn_cert.h"
192
193 /* The canonical empty path. Can this be changed? Well, change the empty
194 test below and the path library will work, not so sure about the fs/wc
195@@ -2597,3 +2598,81 @@ svn_urlpath__canonicalize(const char *uri,
196 }
197 return uri;
198 }
199+
200+
201+/* -------------- The cert API (see private/svn_cert.h) ------------- */
202+
203+svn_boolean_t
204+svn_cert__match_dns_identity(svn_string_t *pattern, svn_string_t *hostname)
205+{
206+ apr_size_t pattern_pos = 0, hostname_pos = 0;
207+
208+ /* support leading wildcards that composed of the only character in the
209+ * left-most label. */
210+ if (pattern->len >= 2 &&
211+ pattern->data[pattern_pos] == '*' &&
212+ pattern->data[pattern_pos + 1] == '.')
213+ {
214+ while (hostname_pos < hostname->len &&
215+ hostname->data[hostname_pos] != '.')
216+ {
217+ hostname_pos++;
218+ }
219+ /* Assume that the wildcard must match something. Rule 2 says
220+ * that *.example.com should not match example.com. If the wildcard
221+ * ends up not matching anything then it matches .example.com which
222+ * seems to be essentially the same as just example.com */
223+ if (hostname_pos == 0)
224+ return FALSE;
225+
226+ pattern_pos++;
227+ }
228+
229+ while (pattern_pos < pattern->len && hostname_pos < hostname->len)
230+ {
231+ char pattern_c = pattern->data[pattern_pos];
232+ char hostname_c = hostname->data[hostname_pos];
233+
234+ /* fold case as described in RFC 4343.
235+ * Note: We actually convert to lowercase, since our URI
236+ * canonicalization code converts to lowercase and generally
237+ * most certs are issued with lowercase DNS names, meaning
238+ * this avoids the fold operation in most cases. The RFC
239+ * suggests the opposite transformation, but doesn't require
240+ * any specific implementation in any case. It is critical
241+ * that this folding be locale independent so you can't use
242+ * tolower(). */
243+ pattern_c = canonicalize_to_lower(pattern_c);
244+ hostname_c = canonicalize_to_lower(hostname_c);
245+
246+ if (pattern_c != hostname_c)
247+ {
248+ /* doesn't match */
249+ return FALSE;
250+ }
251+ else
252+ {
253+ /* characters match so skip both */
254+ pattern_pos++;
255+ hostname_pos++;
256+ }
257+ }
258+
259+ /* ignore a trailing period on the hostname since this has no effect on the
260+ * security of the matching. See the following for the long explanation as
261+ * to why:
262+ * https://bugzilla.mozilla.org/show_bug.cgi?id=134402#c28
263+ */
264+ if (pattern_pos == pattern->len &&
265+ hostname_pos == hostname->len - 1 &&
266+ hostname->data[hostname_pos] == '.')
267+ hostname_pos++;
268+
269+ if (pattern_pos != pattern->len || hostname_pos != hostname->len)
270+ {
271+ /* end didn't match */
272+ return FALSE;
273+ }
274+
275+ return TRUE;
276+}
277Index: subversion/tests/libsvn_subr/dirent_uri-test.c
278===================================================================
279--- subversion/tests/libsvn_subr/dirent_uri-test.c (revision 1615128)
280+++ subversion/tests/libsvn_subr/dirent_uri-test.c (working copy)
281@@ -37,6 +37,7 @@
282 #include "svn_pools.h"
283 #include "svn_dirent_uri.h"
284 #include "private/svn_fspath.h"
285+#include "private/svn_cert.h"
286
287 #include "../svn_test.h"
288
289@@ -2714,6 +2715,145 @@ test_fspath_get_longest_ancestor(apr_pool_t *pool)
290 return SVN_NO_ERROR;
291 }
292
293+struct cert_match_dns_test {
294+ const char *pattern;
295+ const char *hostname;
296+ svn_boolean_t expected;
297+};
298+
299+static svn_error_t *
300+run_cert_match_dns_tests(struct cert_match_dns_test *tests, apr_pool_t *pool)
301+{
302+ struct cert_match_dns_test *ct;
303+ apr_pool_t *iterpool = svn_pool_create(pool);
304+
305+ for (ct = tests; ct->pattern; ct++)
306+ {
307+ svn_boolean_t result;
308+ svn_string_t *pattern, *hostname;
309+
310+ svn_pool_clear(iterpool);
311+
312+ pattern = svn_string_create(ct->pattern, iterpool);
313+ hostname = svn_string_create(ct->hostname, iterpool);
314+
315+ result = svn_cert__match_dns_identity(pattern, hostname);
316+ if (result != ct->expected)
317+ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
318+ "Expected %s but got %s for pattern '%s' on "
319+ "hostname '%s'",
320+ ct->expected ? "match" : "no match",
321+ result ? "match" : "no match",
322+ pattern->data, hostname->data);
323+
324+ }
325+
326+ svn_pool_destroy(iterpool);
327+
328+ return SVN_NO_ERROR;
329+}
330+
331+static struct cert_match_dns_test cert_match_dns_tests[] = {
332+ { "foo.example.com", "foo.example.com", TRUE }, /* exact match */
333+ { "foo.example.com", "FOO.EXAMPLE.COM", TRUE }, /* case differences */
334+ { "FOO.EXAMPLE.COM", "foo.example.com", TRUE },
335+ { "*.example.com", "FoO.ExAmPlE.CoM", TRUE },
336+ { "*.ExAmPlE.CoM", "foo.example.com", TRUE },
337+ { "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", TRUE },
338+ { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", TRUE },
339+ { "foo.example.com", "bar.example.com", FALSE }, /* difference at start */
340+ { "foo.example.com", "foo.example.net", FALSE }, /* difference at end */
341+ { "foo.example.com", "foo.example.commercial", FALSE }, /* hostname longer */
342+ { "foo.example.commercial", "foo.example.com", FALSE }, /* pattern longer */
343+ { "foo.example.comcom", "foo.example.com", FALSE }, /* repeated suffix */
344+ { "foo.example.com", "foo.example.comcom", FALSE },
345+ { "foo.example.com.com", "foo.example.com", FALSE },
346+ { "foo.example.com", "foo.example.com.com", FALSE },
347+ { "foofoo.example.com", "foo.example.com", FALSE }, /* repeated prefix */
348+ { "foo.example.com", "foofoo.example.com", FALSE },
349+ { "foo.foo.example.com", "foo.example.com", FALSE },
350+ { "foo.example.com", "foo.foo.example.com", FALSE },
351+ { "foo.*.example.com", "foo.bar.example.com", FALSE }, /* RFC 6125 s. 6.4.3
352+ Rule 1 */
353+ { "*.example.com", "foo.example.com", TRUE }, /* RFC 6125 s. 6.4.3 Rule 2 */
354+ { "*.example.com", "bar.foo.example.com", FALSE }, /* Rule 2 */
355+ { "*.example.com", "example.com", FALSE }, /* Rule 2 */
356+ { "*.example.com", ".example.com", FALSE }, /* RFC doesn't say what to do
357+ here and a leading period on
358+ a hostname doesn't make sense
359+ so we'll just reject this. */
360+ { "*", "foo.example.com", FALSE }, /* wildcard must be left-most label,
361+ implies that there must be more than
362+ one label. */
363+ { "*", "example.com", FALSE },
364+ { "*", "com", FALSE },
365+ { "*.example.com", "foo.example.net", FALSE }, /* difference in literal text
366+ with a wildcard. */
367+ { "*.com", "example.com", TRUE }, /* See Errata ID 3090 for RFC 6125,
368+ probably shouldn't allow this but
369+ we do for now. */
370+ { "*.", "example.com", FALSE }, /* test some dubious 2 character wildcard
371+ patterns */
372+ { "*.", "example.", TRUE }, /* This one feels questionable */
373+ { "*.", "example", FALSE },
374+ { "*.", ".", FALSE },
375+ { "a", "a", TRUE }, /* check that single letter exact matches work */
376+ { "a", "b", FALSE }, /* and single letter not matches shouldn't */
377+ { "*.*.com", "foo.example.com", FALSE }, /* unsupported wildcards */
378+ { "*.*.com", "example.com", FALSE },
379+ { "**.example.com", "foo.example.com", FALSE },
380+ { "**.example.com", "example.com", FALSE },
381+ { "f*.example.com", "foo.example.com", FALSE },
382+ { "f*.example.com", "bar.example.com", FALSE },
383+ { "*o.example.com", "foo.example.com", FALSE },
384+ { "*o.example.com", "bar.example.com", FALSE },
385+ { "f*o.example.com", "foo.example.com", FALSE },
386+ { "f*o.example.com", "bar.example.com", FALSE },
387+ { "foo.e*.com", "foo.example.com", FALSE },
388+ { "foo.*e.com", "foo.example.com", FALSE },
389+ { "foo.e*e.com", "foo.example.com", FALSE },
390+ { "foo.example.com", "foo.example.com.", TRUE }, /* trailing dot */
391+ { "*.example.com", "foo.example.com.", TRUE },
392+ { "foo", "foo.", TRUE },
393+ { "foo.example.com.", "foo.example.com", FALSE },
394+ { "*.example.com.", "foo.example.com", FALSE },
395+ { "foo.", "foo", FALSE },
396+ { "foo.example.com", "foo.example.com..", FALSE },
397+ { "*.example.com", "foo.example.com..", FALSE },
398+ { "foo", "foo..", FALSE },
399+ { "foo.example.com..", "foo.example.com", FALSE },
400+ { "*.example.com..", "foo.example.com", FALSE },
401+ { "foo..", "foo", FALSE },
402+ { NULL }
403+};
404+
405+static svn_error_t *
406+test_cert_match_dns_identity(apr_pool_t *pool)
407+{
408+ return run_cert_match_dns_tests(cert_match_dns_tests, pool);
409+}
410+
411+/* This test table implements results that should happen if we supported
412+ * RFC 6125 s. 6.4.3 Rule 3. We don't so it's expected to fail for now. */
413+static struct cert_match_dns_test rule3_tests[] = {
414+ { "baz*.example.net", "baz1.example.net", TRUE },
415+ { "*baz.example.net", "foobaz.example.net", TRUE },
416+ { "b*z.example.net", "buuz.example.net", TRUE },
417+ { "b*z.example.net", "bz.example.net", FALSE }, /* presume wildcard can't
418+ match nothing */
419+ { "baz*.example.net", "baz.example.net", FALSE },
420+ { "*baz.example.net", "baz.example.net", FALSE },
421+ { "b*z.example.net", "buuzuuz.example.net", TRUE }, /* presume wildcard
422+ should be greedy */
423+ { NULL }
424+};
425+
426+static svn_error_t *
427+test_rule3(apr_pool_t *pool)
428+{
429+ return run_cert_match_dns_tests(rule3_tests, pool);
430+}
431+
432
433 /* The test table. */
434
435@@ -2812,5 +2952,9 @@ struct svn_test_descriptor_t test_funcs[] =
436 "test svn_fspath__dirname/basename/split"),
437 SVN_TEST_PASS2(test_fspath_get_longest_ancestor,
438 "test svn_fspath__get_longest_ancestor"),
439+ SVN_TEST_PASS2(test_cert_match_dns_identity,
440+ "test svn_cert__match_dns_identity"),
441+ SVN_TEST_XFAIL2(test_rule3,
442+ "test match with RFC 6125 s. 6.4.3 Rule 3"),
443 SVN_TEST_NULL
444 };
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 @@
1Upstream-Status: Backport
2
3Signed-off-by: Yue Tao <yue.tao@windriver.com>
4
5diff --git a/subversion/libsvn_ra_serf/util.c.old b/subversion/libsvn_ra_serf/util.c
6index b6c0141..8b09770 100644
7--- a/subversion/libsvn_ra_serf/util.c.old
8+++ b/subversion/libsvn_ra_serf/util.c
9@@ -21,7 +21,6 @@
10 #define APR_WANT_STRFUNC
11 #include <apr.h>
12 #include <apr_want.h>
13-#include <apr_fnmatch.h>
14
15 #include <serf.h>
16 #include <serf_bucket_types.h>
17@@ -30,6 +29,7 @@
18 #include "svn_private_config.h"
19 #include "svn_xml.h"
20 #include "private/svn_dep_compat.h"
21+#include "private/svn_cert.h"
22
23 #include "ra_serf.h"
24
25@@ -113,7 +113,12 @@ ssl_server_cert(void *baton, int failures,
26 apr_uint32_t svn_failures;
27 svn_error_t *err;
28 apr_hash_t *issuer, *subject, *serf_cert;
29+ apr_array_header_t *san;
30 void *creds;
31+ svn_boolean_t found_matching_hostname = FALSE;
32+ svn_boolean_t found_san_entry = FALSE;
33+ svn_string_t *actual_hostname =
34+ svn_string_create(conn->hostname, scratch_pool);
35
36 /* Implicitly approve any non-server certs. */
37 if (serf_ssl_cert_depth(cert) > 0)
38@@ -129,6 +134,7 @@ ssl_server_cert(void *baton, int failures,
39 serf_cert = serf_ssl_cert_certificate(cert, subpool);
40
41 cert_info.hostname = apr_hash_get(subject, "CN", APR_HASH_KEY_STRING);
42+ san = apr_hash_get(serf_cert, "subjectAltName", APR_HASH_KEY_STRING);
43 cert_info.fingerprint = apr_hash_get(serf_cert, "sha1", APR_HASH_KEY_STRING);
44 if (! cert_info.fingerprint)
45 cert_info.fingerprint = apr_pstrdup(subpool, "<unknown>");
46@@ -145,16 +145,43 @@ ssl_server_cert(void *baton, int failures,
47
48 svn_failures = ssl_convert_serf_failures(failures);
49
50- /* Match server certificate CN with the hostname of the server */
51- if (cert_info.hostname)
52+ /* Try to find matching server name via subjectAltName first... */
53+ if (san)
54 {
55- if (apr_fnmatch(cert_info.hostname, conn->hostinfo,
56- APR_FNM_PERIOD) == APR_FNM_NOMATCH)
57+ int i;
58+ found_san_entry = san->nelts > 0;
59+ for (i = 0; i < san->nelts; i++)
60 {
61- svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
62+ char *s = APR_ARRAY_IDX(san, i, char*);
63+ svn_string_t *cert_hostname = svn_string_create(s, scratch_pool);
64+
65+ if (svn_cert__match_dns_identity(cert_hostname, actual_hostname))
66+ {
67+ found_matching_hostname = TRUE;
68+ cert_info.hostname = s;
69+ break;
70+ }
71 }
72 }
73
74+ /* Match server certificate CN with the hostname of the server iff
75+ * we didn't find any subjectAltName fields and try to match them.
76+ * Per RFC 2818 they are authoritative if present and CommonName
77+ * should be ignored. */
78+ if (!found_matching_hostname && !found_san_entry && cert_info.hostname)
79+ {
80+ svn_string_t *cert_hostname = svn_string_create(cert_info.hostname,
81+ scratch_pool);
82+
83+ if (svn_cert__match_dns_identity(cert_hostname, actual_hostname))
84+ {
85+ found_matching_hostname = TRUE;
86+ }
87+ }
88+
89+ if (!found_matching_hostname)
90+ svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
91+
92 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
93 SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
94 &svn_failures);
95@@ -261,6 +293,10 @@ svn_ra_serf__conn_setup(apr_socket_t *sock,
96 if (!conn->ssl_context)
97 {
98 conn->ssl_context = serf_bucket_ssl_encrypt_context_get(rb);
99+
100+#if SERF_VERSION_AT_LEAST(1,0,0)
101+ serf_ssl_set_hostname(conn->ssl_context, conn->hostinfo);
102+#endif
103
104 serf_ssl_client_cert_provider_set(conn->ssl_context,
105 svn_ra_serf__handle_client_cert,
106diff --git a/subversion/libsvn_subr/dirent_uri.c.old b/subversion/libsvn_subr/dirent_uri.c
107index eef99ba..a5f9e7e 100644
108--- a/subversion/libsvn_subr/dirent_uri.c.old
109+++ b/subversion/libsvn_subr/dirent_uri.c
110@@ -30,6 +30,7 @@
111 #include "svn_path.h"
112
113 #include "private_uri.h"
114+#include "private/svn_cert.h"
115
116 /* The canonical empty path. Can this be changed? Well, change the empty
117 test below and the path library will work, not so sure about the fs/wc
118@@ -1194,3 +1195,81 @@ svn_uri_is_canonical(const char *uri, apr_pool_t *pool)
119
120 return TRUE;
121 }
122+
123+
124+/* -------------- The cert API (see private/svn_cert.h) ------------- */
125+
126+svn_boolean_t
127+svn_cert__match_dns_identity(svn_string_t *pattern, svn_string_t *hostname)
128+{
129+ apr_size_t pattern_pos = 0, hostname_pos = 0;
130+
131+ /* support leading wildcards that composed of the only character in the
132+ * left-most label. */
133+ if (pattern->len >= 2 &&
134+ pattern->data[pattern_pos] == '*' &&
135+ pattern->data[pattern_pos + 1] == '.')
136+ {
137+ while (hostname_pos < hostname->len &&
138+ hostname->data[hostname_pos] != '.')
139+ {
140+ hostname_pos++;
141+ }
142+ /* Assume that the wildcard must match something. Rule 2 says
143+ * that *.example.com should not match example.com. If the wildcard
144+ * ends up not matching anything then it matches .example.com which
145+ * seems to be essentially the same as just example.com */
146+ if (hostname_pos == 0)
147+ return FALSE;
148+
149+ pattern_pos++;
150+ }
151+
152+ while (pattern_pos < pattern->len && hostname_pos < hostname->len)
153+ {
154+ char pattern_c = pattern->data[pattern_pos];
155+ char hostname_c = hostname->data[hostname_pos];
156+
157+ /* fold case as described in RFC 4343.
158+ * Note: We actually convert to lowercase, since our URI
159+ * canonicalization code converts to lowercase and generally
160+ * most certs are issued with lowercase DNS names, meaning
161+ * this avoids the fold operation in most cases. The RFC
162+ * suggests the opposite transformation, but doesn't require
163+ * any specific implementation in any case. It is critical
164+ * that this folding be locale independent so you can't use
165+ * tolower(). */
166+ pattern_c = canonicalize_to_lower(pattern_c);
167+ hostname_c = canonicalize_to_lower(hostname_c);
168+
169+ if (pattern_c != hostname_c)
170+ {
171+ /* doesn't match */
172+ return FALSE;
173+ }
174+ else
175+ {
176+ /* characters match so skip both */
177+ pattern_pos++;
178+ hostname_pos++;
179+ }
180+ }
181+
182+ /* ignore a trailing period on the hostname since this has no effect on the
183+ * security of the matching. See the following for the long explanation as
184+ * to why:
185+ * https://bugzilla.mozilla.org/show_bug.cgi?id=134402#c28
186+ */
187+ if (pattern_pos == pattern->len &&
188+ hostname_pos == hostname->len - 1 &&
189+ hostname->data[hostname_pos] == '.')
190+ hostname_pos++;
191+
192+ if (pattern_pos != pattern->len || hostname_pos != hostname->len)
193+ {
194+ /* end didn't match */
195+ return FALSE;
196+ }
197+
198+ return TRUE;
199+}
200diff --git a/subversion/tests/libsvn_subr/dirent_uri-test.c.old b/subversion/tests/libsvn_subr/dirent_uri-test.c
201index d71d9c1..370b64a 100644
202--- a/subversion/tests/libsvn_subr/dirent_uri-test.c.old
203+++ b/subversion/tests/libsvn_subr/dirent_uri-test.c
204@@ -31,6 +31,7 @@
205
206 #include "svn_pools.h"
207 #include "svn_dirent_uri.h"
208+#include "private/svn_cert.h"
209
210 #include "../svn_test.h"
211 #include "../../libsvn_subr/private_uri.h"
212@@ -1671,6 +1672,145 @@ test_uri_internal_style(const char **msg,
213 return SVN_NO_ERROR;
214 }
215
216+struct cert_match_dns_test {
217+ const char *pattern;
218+ const char *hostname;
219+ svn_boolean_t expected;
220+};
221+
222+static svn_error_t *
223+run_cert_match_dns_tests(struct cert_match_dns_test *tests, apr_pool_t *pool)
224+{
225+ struct cert_match_dns_test *ct;
226+ apr_pool_t *iterpool = svn_pool_create(pool);
227+
228+ for (ct = tests; ct->pattern; ct++)
229+ {
230+ svn_boolean_t result;
231+ svn_string_t *pattern, *hostname;
232+
233+ svn_pool_clear(iterpool);
234+
235+ pattern = svn_string_create(ct->pattern, iterpool);
236+ hostname = svn_string_create(ct->hostname, iterpool);
237+
238+ result = svn_cert__match_dns_identity(pattern, hostname);
239+ if (result != ct->expected)
240+ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
241+ "Expected %s but got %s for pattern '%s' on "
242+ "hostname '%s'",
243+ ct->expected ? "match" : "no match",
244+ result ? "match" : "no match",
245+ pattern->data, hostname->data);
246+
247+ }
248+
249+ svn_pool_destroy(iterpool);
250+
251+ return SVN_NO_ERROR;
252+}
253+
254+static struct cert_match_dns_test cert_match_dns_tests[] = {
255+ { "foo.example.com", "foo.example.com", TRUE }, /* exact match */
256+ { "foo.example.com", "FOO.EXAMPLE.COM", TRUE }, /* case differences */
257+ { "FOO.EXAMPLE.COM", "foo.example.com", TRUE },
258+ { "*.example.com", "FoO.ExAmPlE.CoM", TRUE },
259+ { "*.ExAmPlE.CoM", "foo.example.com", TRUE },
260+ { "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", TRUE },
261+ { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", TRUE },
262+ { "foo.example.com", "bar.example.com", FALSE }, /* difference at start */
263+ { "foo.example.com", "foo.example.net", FALSE }, /* difference at end */
264+ { "foo.example.com", "foo.example.commercial", FALSE }, /* hostname longer */
265+ { "foo.example.commercial", "foo.example.com", FALSE }, /* pattern longer */
266+ { "foo.example.comcom", "foo.example.com", FALSE }, /* repeated suffix */
267+ { "foo.example.com", "foo.example.comcom", FALSE },
268+ { "foo.example.com.com", "foo.example.com", FALSE },
269+ { "foo.example.com", "foo.example.com.com", FALSE },
270+ { "foofoo.example.com", "foo.example.com", FALSE }, /* repeated prefix */
271+ { "foo.example.com", "foofoo.example.com", FALSE },
272+ { "foo.foo.example.com", "foo.example.com", FALSE },
273+ { "foo.example.com", "foo.foo.example.com", FALSE },
274+ { "foo.*.example.com", "foo.bar.example.com", FALSE }, /* RFC 6125 s. 6.4.3
275+ Rule 1 */
276+ { "*.example.com", "foo.example.com", TRUE }, /* RFC 6125 s. 6.4.3 Rule 2 */
277+ { "*.example.com", "bar.foo.example.com", FALSE }, /* Rule 2 */
278+ { "*.example.com", "example.com", FALSE }, /* Rule 2 */
279+ { "*.example.com", ".example.com", FALSE }, /* RFC doesn't say what to do
280+ here and a leading period on
281+ a hostname doesn't make sense
282+ so we'll just reject this. */
283+ { "*", "foo.example.com", FALSE }, /* wildcard must be left-most label,
284+ implies that there must be more than
285+ one label. */
286+ { "*", "example.com", FALSE },
287+ { "*", "com", FALSE },
288+ { "*.example.com", "foo.example.net", FALSE }, /* difference in literal text
289+ with a wildcard. */
290+ { "*.com", "example.com", TRUE }, /* See Errata ID 3090 for RFC 6125,
291+ probably shouldn't allow this but
292+ we do for now. */
293+ { "*.", "example.com", FALSE }, /* test some dubious 2 character wildcard
294+ patterns */
295+ { "*.", "example.", TRUE }, /* This one feels questionable */
296+ { "*.", "example", FALSE },
297+ { "*.", ".", FALSE },
298+ { "a", "a", TRUE }, /* check that single letter exact matches work */
299+ { "a", "b", FALSE }, /* and single letter not matches shouldn't */
300+ { "*.*.com", "foo.example.com", FALSE }, /* unsupported wildcards */
301+ { "*.*.com", "example.com", FALSE },
302+ { "**.example.com", "foo.example.com", FALSE },
303+ { "**.example.com", "example.com", FALSE },
304+ { "f*.example.com", "foo.example.com", FALSE },
305+ { "f*.example.com", "bar.example.com", FALSE },
306+ { "*o.example.com", "foo.example.com", FALSE },
307+ { "*o.example.com", "bar.example.com", FALSE },
308+ { "f*o.example.com", "foo.example.com", FALSE },
309+ { "f*o.example.com", "bar.example.com", FALSE },
310+ { "foo.e*.com", "foo.example.com", FALSE },
311+ { "foo.*e.com", "foo.example.com", FALSE },
312+ { "foo.e*e.com", "foo.example.com", FALSE },
313+ { "foo.example.com", "foo.example.com.", TRUE }, /* trailing dot */
314+ { "*.example.com", "foo.example.com.", TRUE },
315+ { "foo", "foo.", TRUE },
316+ { "foo.example.com.", "foo.example.com", FALSE },
317+ { "*.example.com.", "foo.example.com", FALSE },
318+ { "foo.", "foo", FALSE },
319+ { "foo.example.com", "foo.example.com..", FALSE },
320+ { "*.example.com", "foo.example.com..", FALSE },
321+ { "foo", "foo..", FALSE },
322+ { "foo.example.com..", "foo.example.com", FALSE },
323+ { "*.example.com..", "foo.example.com", FALSE },
324+ { "foo..", "foo", FALSE },
325+ { NULL }
326+};
327+
328+static svn_error_t *
329+test_cert_match_dns_identity(apr_pool_t *pool)
330+{
331+ return run_cert_match_dns_tests(cert_match_dns_tests, pool);
332+}
333+
334+/* This test table implements results that should happen if we supported
335+ * RFC 6125 s. 6.4.3 Rule 3. We don't so it's expected to fail for now. */
336+static struct cert_match_dns_test rule3_tests[] = {
337+ { "baz*.example.net", "baz1.example.net", TRUE },
338+ { "*baz.example.net", "foobaz.example.net", TRUE },
339+ { "b*z.example.net", "buuz.example.net", TRUE },
340+ { "b*z.example.net", "bz.example.net", FALSE }, /* presume wildcard can't
341+ match nothing */
342+ { "baz*.example.net", "baz.example.net", FALSE },
343+ { "*baz.example.net", "baz.example.net", FALSE },
344+ { "b*z.example.net", "buuzuuz.example.net", TRUE }, /* presume wildcard
345+ should be greedy */
346+ { NULL }
347+};
348+
349+static svn_error_t *
350+test_rule3(apr_pool_t *pool)
351+{
352+ return run_cert_match_dns_tests(rule3_tests, pool);
353+}
354+
355
356 /* The test table. */
357
358@@ -1699,5 +1839,7 @@ struct svn_test_descriptor_t test_funcs[] =
359 SVN_TEST_PASS(test_uri_local_style),
360 SVN_TEST_PASS(test_dirent_internal_style),
361 SVN_TEST_PASS(test_uri_internal_style),
362+ SVN_TEST_PASS(test_cert_match_dns_identity),
363+ SVN_TEST_XFAIL(test_rule3),
364 SVN_TEST_NULL
365 };
366diff --git a/subversion/include/private/svn_cert.h b/subversion/include/private/svn_cert.h
367new file mode 100644
368index 0000000..32e32a0
369--- /dev/null
370+++ b/subversion/include/private/svn_cert.h
371@@ -0,0 +1,68 @@
372+/**
373+ * @copyright
374+ * ====================================================================
375+ * Licensed to the Apache Software Foundation (ASF) under one
376+ * or more contributor license agreements. See the NOTICE file
377+ * distributed with this work for additional information
378+ * regarding copyright ownership. The ASF licenses this file
379+ * to you under the Apache License, Version 2.0 (the
380+ * "License"); you may not use this file except in compliance
381+ * with the License. You may obtain a copy of the License at
382+ *
383+ * http://www.apache.org/licenses/LICENSE-2.0
384+ *
385+ * Unless required by applicable law or agreed to in writing,
386+ * software distributed under the License is distributed on an
387+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
388+ * KIND, either express or implied. See the License for the
389+ * specific language governing permissions and limitations
390+ * under the License.
391+ * ====================================================================
392+ * @endcopyright
393+ *
394+ * @file svn_cert.h
395+ * @brief Implementation of certificate validation functions
396+ */
397+
398+#ifndef SVN_CERT_H
399+#define SVN_CERT_H
400+
401+#include <apr.h>
402+
403+#include "svn_types.h"
404+#include "svn_string.h"
405+
406+#ifdef __cplusplus
407+extern "C" {
408+#endif /* __cplusplus */
409+
410+
411+/* Return TRUE iff @a pattern matches @a hostname as defined
412+ * by the matching rules of RFC 6125. In the context of RFC
413+ * 6125 the pattern is the domain name portion of the presented
414+ * identifier (which comes from the Common Name or a DNSName
415+ * portion of the subjectAltName of an X.509 certificate) and
416+ * the hostname is the source domain (i.e. the host portion
417+ * of the URI the user entered).
418+ *
419+ * @note With respect to wildcards we only support matching
420+ * wildcards in the left-most label and as the only character
421+ * in the left-most label (i.e. we support RFC 6125 ยง 6.4.3
422+ * Rule 1 and 2 but not the optional Rule 3). This may change
423+ * in the future.
424+ *
425+ * @note Subversion does not at current support internationalized
426+ * domain names. Both values are presumed to be in NR-LDH label
427+ * or A-label form (see RFC 5890 for the definition).
428+ *
429+ * @since New in 1.9.
430+ */
431+svn_boolean_t
432+svn_cert__match_dns_identity(svn_string_t *pattern, svn_string_t *hostname);
433+
434+
435+#ifdef __cplusplus
436+}
437+#endif /* __cplusplus */
438+
439+#endif /* SVN_CERT_H */
diff --git a/meta/recipes-devtools/subversion/subversion_1.6.15.bb b/meta/recipes-devtools/subversion/subversion_1.6.15.bb
index 1bc637473b..6680ab6d34 100644
--- a/meta/recipes-devtools/subversion/subversion_1.6.15.bb
+++ b/meta/recipes-devtools/subversion/subversion_1.6.15.bb
@@ -17,7 +17,9 @@ SRC_URI = "http://subversion.tigris.org/downloads/${BPN}-${PV}.tar.bz2 \
17 file://subversion-CVE-2013-4505.patch \ 17 file://subversion-CVE-2013-4505.patch \
18 file://subversion-CVE-2013-1845.patch \ 18 file://subversion-CVE-2013-1845.patch \
19 file://subversion-CVE-2013-1847-CVE-2013-1846.patch \ 19 file://subversion-CVE-2013-1847-CVE-2013-1846.patch \
20 file://subversion-CVE-2013-4277.patch" 20 file://subversion-CVE-2013-4277.patch \
21 file://subversion-CVE-2014-3522.patch \
22"
21 23
22SRC_URI[md5sum] = "113fca1d9e4aa389d7dc2b210010fa69" 24SRC_URI[md5sum] = "113fca1d9e4aa389d7dc2b210010fa69"
23SRC_URI[sha256sum] = "b2919d603a5f3c19f42e3265c4b930e2376c43b3969b90ef9c42b2f72d5aaa45" 25SRC_URI[sha256sum] = "b2919d603a5f3c19f42e3265c4b930e2376c43b3969b90ef9c42b2f72d5aaa45"
diff --git a/meta/recipes-devtools/subversion/subversion_1.8.9.bb b/meta/recipes-devtools/subversion/subversion_1.8.9.bb
index d134a5352c..e1ab945896 100644
--- a/meta/recipes-devtools/subversion/subversion_1.8.9.bb
+++ b/meta/recipes-devtools/subversion/subversion_1.8.9.bb
@@ -12,6 +12,7 @@ inherit gettext
12SRC_URI = "${APACHE_MIRROR}/${BPN}/${BPN}-${PV}.tar.bz2 \ 12SRC_URI = "${APACHE_MIRROR}/${BPN}/${BPN}-${PV}.tar.bz2 \
13 file://libtool2.patch \ 13 file://libtool2.patch \
14 file://disable_macos.patch \ 14 file://disable_macos.patch \
15 file://subversion-CVE-2014-3522.patch;striplevel=0 \
15" 16"
16SRC_URI[md5sum] = "bd495517a760ddd764ce449a891971db" 17SRC_URI[md5sum] = "bd495517a760ddd764ce449a891971db"
17SRC_URI[sha256sum] = "45d708a5c3ffbef4b2a1044c4716a053e680763743d1f7ba99d0369f6da49e33" 18SRC_URI[sha256sum] = "45d708a5c3ffbef4b2a1044c4716a053e680763743d1f7ba99d0369f6da49e33"