summaryrefslogtreecommitdiffstats
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 @@
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 */