Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 519202 | Differences between
and this patch

Collapse All | Expand All

(-)subversion/include/private/svn_cert.h (+68 lines)
Line 0 Link Here
1
/**
2
 * @copyright
3
 * ====================================================================
4
 *    Licensed to the Apache Software Foundation (ASF) under one
5
 *    or more contributor license agreements.  See the NOTICE file
6
 *    distributed with this work for additional information
7
 *    regarding copyright ownership.  The ASF licenses this file
8
 *    to you under the Apache License, Version 2.0 (the
9
 *    "License"); you may not use this file except in compliance
10
 *    with the License.  You may obtain a copy of the License at
11
 *
12
 *      http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 *    Unless required by applicable law or agreed to in writing,
15
 *    software distributed under the License is distributed on an
16
 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
 *    KIND, either express or implied.  See the License for the
18
 *    specific language governing permissions and limitations
19
 *    under the License.
20
 * ====================================================================
21
 * @endcopyright
22
 *
23
 * @file svn_cert.h
24
 * @brief Implementation of certificate validation functions
25
 */
26
27
#ifndef SVN_CERT_H
28
#define SVN_CERT_H
29
30
#include <apr.h>
31
32
#include "svn_types.h"
33
#include "svn_string.h"
34
35
#ifdef __cplusplus
36
extern "C" {
37
#endif /* __cplusplus */
38
39
40
/* Return TRUE iff @a pattern matches @a hostname as defined
41
 * by the matching rules of RFC 6125.  In the context of RFC
42
 * 6125 the pattern is the domain name portion of the presented
43
 * identifier (which comes from the Common Name or a DNSName
44
 * portion of the subjectAltName of an X.509 certificate) and
45
 * the hostname is the source domain (i.e. the host portion
46
 * of the URI the user entered).
47
 *
48
 * @note With respect to wildcards we only support matching
49
 * wildcards in the left-most label and as the only character
50
 * in the left-most label (i.e. we support RFC 6125 s. 6.4.3
51
 * Rule 1 and 2 but not the optional Rule 3).  This may change
52
 * in the future.
53
 *
54
 * @note Subversion does not at current support internationalized
55
 * domain names.  Both values are presumed to be in NR-LDH label
56
 * or A-label form (see RFC 5890 for the definition).
57
 *
58
 * @since New in 1.9.
59
 */
60
svn_boolean_t
61
svn_cert__match_dns_identity(svn_string_t *pattern, svn_string_t *hostname);
62
63
64
#ifdef __cplusplus
65
}
66
#endif /* __cplusplus */
67
68
#endif /* SVN_CERT_H */
(-)subversion/libsvn_ra_serf/util.c (-14 / +29 lines)
Lines 28-34 Link Here
28
#define APR_WANT_STRFUNC
28
#define APR_WANT_STRFUNC
29
#include <apr.h>
29
#include <apr.h>
30
#include <apr_want.h>
30
#include <apr_want.h>
31
#include <apr_fnmatch.h>
32
31
33
#include <serf.h>
32
#include <serf.h>
34
#include <serf_bucket_types.h>
33
#include <serf_bucket_types.h>
Lines 40-45 Link Here
40
#include "svn_xml.h"
39
#include "svn_xml.h"
41
#include "private/svn_dep_compat.h"
40
#include "private/svn_dep_compat.h"
42
#include "private/svn_fspath.h"
41
#include "private/svn_fspath.h"
42
#include "private/svn_cert.h"
43
43
44
#include "ra_serf.h"
44
#include "ra_serf.h"
45
45
Lines 202-208 ssl_server_cert(void *baton, int failures, Link Here
202
  apr_hash_t *issuer, *subject, *serf_cert;
202
  apr_hash_t *issuer, *subject, *serf_cert;
203
  apr_array_header_t *san;
203
  apr_array_header_t *san;
204
  void *creds;
204
  void *creds;
205
  int found_matching_hostname = 0;
205
  svn_boolean_t found_matching_hostname = FALSE;
206
  svn_boolean_t found_san_entry = FALSE;
207
  svn_string_t *actual_hostname =
208
      svn_string_create(conn->hostname, scratch_pool);
206
209
207
  /* Implicitly approve any non-server certs. */
210
  /* Implicitly approve any non-server certs. */
208
  if (serf_ssl_cert_depth(cert) > 0)
211
  if (serf_ssl_cert_depth(cert) > 0)
Lines 237-266 ssl_server_cert(void *baton, int failures, Link Here
237
                  | conn->server_cert_failures);
240
                  | conn->server_cert_failures);
238
241
239
  /* Try to find matching server name via subjectAltName first... */
242
  /* Try to find matching server name via subjectAltName first... */
240
  if (san) {
243
  if (san)
244
    {
241
      int i;
245
      int i;
242
      for (i = 0; i < san->nelts; i++) {
246
      found_san_entry = san->nelts > 0;
247
      for (i = 0; i < san->nelts; i++)
248
        {
243
          char *s = APR_ARRAY_IDX(san, i, char*);
249
          char *s = APR_ARRAY_IDX(san, i, char*);
244
          if (apr_fnmatch(s, conn->hostname,
250
          svn_string_t *cert_hostname = svn_string_create(s, scratch_pool);
245
                          APR_FNM_PERIOD | APR_FNM_CASE_BLIND) == APR_SUCCESS)
251
252
          if (svn_cert__match_dns_identity(cert_hostname, actual_hostname))
246
            {
253
            {
247
              found_matching_hostname = 1;
254
              found_matching_hostname = TRUE;
248
              cert_info.hostname = s;
255
              cert_info.hostname = s;
249
              break;
256
              break;
250
            }
257
            }
251
      }
258
        }
252
  }
259
    }
253
260
254
  /* Match server certificate CN with the hostname of the server */
261
  /* Match server certificate CN with the hostname of the server iff
255
  if (!found_matching_hostname && cert_info.hostname)
262
   * we didn't find any subjectAltName fields and try to match them.
263
   * Per RFC 2818 they are authoritative if present and CommonName
264
   * should be ignored. */
265
  if (!found_matching_hostname && !found_san_entry && cert_info.hostname)
256
    {
266
    {
257
      if (apr_fnmatch(cert_info.hostname, conn->hostname,
267
      svn_string_t *cert_hostname = svn_string_create(cert_info.hostname,
258
                      APR_FNM_PERIOD | APR_FNM_CASE_BLIND) == APR_FNM_NOMATCH)
268
                                                      scratch_pool);
269
270
      if (svn_cert__match_dns_identity(cert_hostname, actual_hostname))
259
        {
271
        {
260
          svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
272
          found_matching_hostname = TRUE;
261
        }
273
        }
262
    }
274
    }
263
275
276
  if (!found_matching_hostname)
277
    svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
278
264
  svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
279
  svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
265
                         SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
280
                         SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
266
                         &svn_failures);
281
                         &svn_failures);
(-)subversion/libsvn_subr/dirent_uri.c (+79 lines)
Lines 38-43 Link Here
38
38
39
#include "dirent_uri.h"
39
#include "dirent_uri.h"
40
#include "private/svn_fspath.h"
40
#include "private/svn_fspath.h"
41
#include "private/svn_cert.h"
41
42
42
/* The canonical empty path.  Can this be changed?  Well, change the empty
43
/* The canonical empty path.  Can this be changed?  Well, change the empty
43
   test below and the path library will work, not so sure about the fs/wc
44
   test below and the path library will work, not so sure about the fs/wc
Lines 2631-2633 svn_urlpath__canonicalize(const char *uri, Link Here
2631
    }
2632
    }
2632
  return uri;
2633
  return uri;
2633
}
2634
}
2635
2636
2637
/* -------------- The cert API (see private/svn_cert.h) ------------- */
2638
2639
svn_boolean_t
2640
svn_cert__match_dns_identity(svn_string_t *pattern, svn_string_t *hostname)
2641
{
2642
  apr_size_t pattern_pos = 0, hostname_pos = 0;
2643
2644
  /* support leading wildcards that composed of the only character in the
2645
   * left-most label. */
2646
  if (pattern->len >= 2 &&
2647
      pattern->data[pattern_pos] == '*' &&
2648
      pattern->data[pattern_pos + 1] == '.')
2649
    {
2650
      while (hostname_pos < hostname->len &&
2651
             hostname->data[hostname_pos] != '.')
2652
        {
2653
          hostname_pos++;
2654
        }
2655
      /* Assume that the wildcard must match something.  Rule 2 says
2656
       * that *.example.com should not match example.com.  If the wildcard
2657
       * ends up not matching anything then it matches .example.com which
2658
       * seems to be essentially the same as just example.com */
2659
      if (hostname_pos == 0)
2660
        return FALSE;
2661
2662
      pattern_pos++;
2663
    }
2664
2665
  while (pattern_pos < pattern->len && hostname_pos < hostname->len)
2666
    {
2667
      char pattern_c = pattern->data[pattern_pos];
2668
      char hostname_c = hostname->data[hostname_pos];
2669
2670
      /* fold case as described in RFC 4343.
2671
       * Note: We actually convert to lowercase, since our URI
2672
       * canonicalization code converts to lowercase and generally
2673
       * most certs are issued with lowercase DNS names, meaning
2674
       * this avoids the fold operation in most cases.  The RFC
2675
       * suggests the opposite transformation, but doesn't require
2676
       * any specific implementation in any case.  It is critical
2677
       * that this folding be locale independent so you can't use
2678
       * tolower(). */
2679
      pattern_c = canonicalize_to_lower(pattern_c);
2680
      hostname_c = canonicalize_to_lower(hostname_c);
2681
2682
      if (pattern_c != hostname_c)
2683
        {
2684
          /* doesn't match */
2685
          return FALSE;
2686
        }
2687
      else
2688
        {
2689
          /* characters match so skip both */
2690
          pattern_pos++;
2691
          hostname_pos++;
2692
        }
2693
    }
2694
2695
  /* ignore a trailing period on the hostname since this has no effect on the
2696
   * security of the matching.  See the following for the long explanation as
2697
   * to why:
2698
   * https://bugzilla.mozilla.org/show_bug.cgi?id=134402#c28
2699
   */
2700
  if (pattern_pos == pattern->len &&
2701
      hostname_pos == hostname->len - 1 &&
2702
      hostname->data[hostname_pos] == '.')
2703
    hostname_pos++;
2704
2705
  if (pattern_pos != pattern->len || hostname_pos != hostname->len)
2706
    {
2707
      /* end didn't match */
2708
      return FALSE;
2709
    }
2710
2711
  return TRUE;
2712
}
(-)subversion/tests/libsvn_subr/dirent_uri-test.c (+144 lines)
Lines 37-42 Link Here
37
#include "svn_pools.h"
37
#include "svn_pools.h"
38
#include "svn_dirent_uri.h"
38
#include "svn_dirent_uri.h"
39
#include "private/svn_fspath.h"
39
#include "private/svn_fspath.h"
40
#include "private/svn_cert.h"
40
41
41
#include "../svn_test.h"
42
#include "../svn_test.h"
42
43
Lines 2821-2826 test_fspath_get_longest_ancestor(apr_pool_t *pool) Link Here
2821
  return SVN_NO_ERROR;
2822
  return SVN_NO_ERROR;
2822
}
2823
}
2823
2824
2825
struct cert_match_dns_test {
2826
  const char *pattern;
2827
  const char *hostname;
2828
  svn_boolean_t expected;
2829
};
2830
2831
static svn_error_t *
2832
run_cert_match_dns_tests(struct cert_match_dns_test *tests, apr_pool_t *pool)
2833
{
2834
  struct cert_match_dns_test *ct;
2835
  apr_pool_t *iterpool = svn_pool_create(pool);
2836
2837
  for (ct = tests; ct->pattern; ct++)
2838
    {
2839
      svn_boolean_t result;
2840
      svn_string_t *pattern, *hostname;
2841
2842
      svn_pool_clear(iterpool);
2843
2844
      pattern = svn_string_create(ct->pattern, iterpool);
2845
      hostname = svn_string_create(ct->hostname, iterpool);
2846
2847
      result = svn_cert__match_dns_identity(pattern, hostname);
2848
      if (result != ct->expected)
2849
        return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
2850
                                 "Expected %s but got %s for pattern '%s' on "
2851
                                 "hostname '%s'",
2852
                                 ct->expected ? "match" : "no match",
2853
                                 result ? "match" : "no match",
2854
                                 pattern->data, hostname->data);
2855
2856
    }
2857
2858
  svn_pool_destroy(iterpool);
2859
2860
  return SVN_NO_ERROR;
2861
}
2862
2863
static struct cert_match_dns_test cert_match_dns_tests[] = {
2864
  { "foo.example.com", "foo.example.com", TRUE }, /* exact match */
2865
  { "foo.example.com", "FOO.EXAMPLE.COM", TRUE }, /* case differences */
2866
  { "FOO.EXAMPLE.COM", "foo.example.com", TRUE },
2867
  { "*.example.com", "FoO.ExAmPlE.CoM", TRUE },
2868
  { "*.ExAmPlE.CoM", "foo.example.com", TRUE },
2869
  { "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", TRUE },
2870
  { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", TRUE },
2871
  { "foo.example.com", "bar.example.com", FALSE }, /* difference at start */
2872
  { "foo.example.com", "foo.example.net", FALSE }, /* difference at end */
2873
  { "foo.example.com", "foo.example.commercial", FALSE }, /* hostname longer */
2874
  { "foo.example.commercial", "foo.example.com", FALSE }, /* pattern longer */
2875
  { "foo.example.comcom", "foo.example.com", FALSE }, /* repeated suffix */
2876
  { "foo.example.com", "foo.example.comcom", FALSE },
2877
  { "foo.example.com.com", "foo.example.com", FALSE },
2878
  { "foo.example.com", "foo.example.com.com", FALSE },
2879
  { "foofoo.example.com", "foo.example.com", FALSE }, /* repeated prefix */
2880
  { "foo.example.com", "foofoo.example.com", FALSE },
2881
  { "foo.foo.example.com", "foo.example.com", FALSE },
2882
  { "foo.example.com", "foo.foo.example.com", FALSE },
2883
  { "foo.*.example.com", "foo.bar.example.com", FALSE }, /* RFC 6125 s. 6.4.3
2884
                                                            Rule 1 */
2885
  { "*.example.com", "foo.example.com", TRUE }, /* RFC 6125 s. 6.4.3 Rule 2 */
2886
  { "*.example.com", "bar.foo.example.com", FALSE }, /* Rule 2 */
2887
  { "*.example.com", "example.com", FALSE }, /* Rule 2 */
2888
  { "*.example.com", ".example.com", FALSE }, /* RFC doesn't say what to do
2889
                                                 here and a leading period on
2890
                                                 a hostname doesn't make sense
2891
                                                 so we'll just reject this. */
2892
  { "*", "foo.example.com", FALSE }, /* wildcard must be left-most label,
2893
                                        implies that there must be more than
2894
                                        one label. */
2895
  { "*", "example.com", FALSE },
2896
  { "*", "com", FALSE },
2897
  { "*.example.com", "foo.example.net", FALSE }, /* difference in literal text
2898
                                                    with a wildcard. */
2899
  { "*.com", "example.com", TRUE }, /* See Errata ID 3090 for RFC 6125,
2900
                                       probably shouldn't allow this but
2901
                                       we do for now. */
2902
  { "*.", "example.com", FALSE }, /* test some dubious 2 character wildcard
2903
                                     patterns */
2904
  { "*.", "example.", TRUE }, /* This one feels questionable */
2905
  { "*.", "example", FALSE },
2906
  { "*.", ".", FALSE },
2907
  { "a", "a", TRUE }, /* check that single letter exact matches work */
2908
  { "a", "b", FALSE }, /* and single letter not matches shouldn't */
2909
  { "*.*.com", "foo.example.com", FALSE }, /* unsupported wildcards */
2910
  { "*.*.com", "example.com", FALSE },
2911
  { "**.example.com", "foo.example.com", FALSE },
2912
  { "**.example.com", "example.com", FALSE },
2913
  { "f*.example.com", "foo.example.com", FALSE },
2914
  { "f*.example.com", "bar.example.com", FALSE },
2915
  { "*o.example.com", "foo.example.com", FALSE },
2916
  { "*o.example.com", "bar.example.com", FALSE },
2917
  { "f*o.example.com", "foo.example.com", FALSE },
2918
  { "f*o.example.com", "bar.example.com", FALSE },
2919
  { "foo.e*.com", "foo.example.com", FALSE },
2920
  { "foo.*e.com", "foo.example.com", FALSE },
2921
  { "foo.e*e.com", "foo.example.com", FALSE },
2922
  { "foo.example.com", "foo.example.com.", TRUE }, /* trailing dot */
2923
  { "*.example.com", "foo.example.com.", TRUE },
2924
  { "foo", "foo.", TRUE },
2925
  { "foo.example.com.", "foo.example.com", FALSE },
2926
  { "*.example.com.", "foo.example.com", FALSE },
2927
  { "foo.", "foo", FALSE },
2928
  { "foo.example.com", "foo.example.com..", FALSE },
2929
  { "*.example.com", "foo.example.com..", FALSE },
2930
  { "foo", "foo..", FALSE },
2931
  { "foo.example.com..", "foo.example.com", FALSE },
2932
  { "*.example.com..", "foo.example.com", FALSE },
2933
  { "foo..", "foo", FALSE },
2934
  { NULL }
2935
};
2936
2937
static svn_error_t *
2938
test_cert_match_dns_identity(apr_pool_t *pool)
2939
{
2940
  return run_cert_match_dns_tests(cert_match_dns_tests, pool);
2941
}
2942
2943
/* This test table implements results that should happen if we supported
2944
 * RFC 6125 s. 6.4.3 Rule 3.  We don't so it's expected to fail for now. */
2945
static struct cert_match_dns_test rule3_tests[] = {
2946
  { "baz*.example.net", "baz1.example.net", TRUE },
2947
  { "*baz.example.net", "foobaz.example.net", TRUE },
2948
  { "b*z.example.net", "buuz.example.net", TRUE },
2949
  { "b*z.example.net", "bz.example.net", FALSE }, /* presume wildcard can't
2950
                                                     match nothing */
2951
  { "baz*.example.net", "baz.example.net", FALSE },
2952
  { "*baz.example.net", "baz.example.net", FALSE },
2953
  { "b*z.example.net", "buuzuuz.example.net", TRUE }, /* presume wildcard
2954
                                                         should be greedy */
2955
  { NULL }
2956
};
2957
2958
static svn_error_t *
2959
test_rule3(apr_pool_t *pool)
2960
{
2961
  return run_cert_match_dns_tests(rule3_tests, pool);
2962
}
2963
2824
2964
2825
/* The test table.  */
2965
/* The test table.  */
2826
2966
Lines 2925-2929 struct svn_test_descriptor_t test_funcs[] = Link Here
2925
                   "test svn_fspath__dirname/basename/split"),
3065
                   "test svn_fspath__dirname/basename/split"),
2926
    SVN_TEST_PASS2(test_fspath_get_longest_ancestor,
3066
    SVN_TEST_PASS2(test_fspath_get_longest_ancestor,
2927
                   "test svn_fspath__get_longest_ancestor"),
3067
                   "test svn_fspath__get_longest_ancestor"),
3068
    SVN_TEST_PASS2(test_cert_match_dns_identity,
3069
                   "test svn_cert__match_dns_identity"),
3070
    SVN_TEST_XFAIL2(test_rule3,
3071
                    "test match with RFC 6125 s. 6.4.3 Rule 3"),
2928
    SVN_TEST_NULL
3072
    SVN_TEST_NULL
2929
  };
3073
  };
2930
 Patch against 1.8.9:
3074
 Patch against 1.8.9:
(-)subversion/include/private/svn_cert.h (+68 lines)
Line 0 Link Here
1
/**
2
 * @copyright
3
 * ====================================================================
4
 *    Licensed to the Apache Software Foundation (ASF) under one
5
 *    or more contributor license agreements.  See the NOTICE file
6
 *    distributed with this work for additional information
7
 *    regarding copyright ownership.  The ASF licenses this file
8
 *    to you under the Apache License, Version 2.0 (the
9
 *    "License"); you may not use this file except in compliance
10
 *    with the License.  You may obtain a copy of the License at
11
 *
12
 *      http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 *    Unless required by applicable law or agreed to in writing,
15
 *    software distributed under the License is distributed on an
16
 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
 *    KIND, either express or implied.  See the License for the
18
 *    specific language governing permissions and limitations
19
 *    under the License.
20
 * ====================================================================
21
 * @endcopyright
22
 *
23
 * @file svn_cert.h
24
 * @brief Implementation of certificate validation functions
25
 */
26
27
#ifndef SVN_CERT_H
28
#define SVN_CERT_H
29
30
#include <apr.h>
31
32
#include "svn_types.h"
33
#include "svn_string.h"
34
35
#ifdef __cplusplus
36
extern "C" {
37
#endif /* __cplusplus */
38
39
40
/* Return TRUE iff @a pattern matches @a hostname as defined
41
 * by the matching rules of RFC 6125.  In the context of RFC
42
 * 6125 the pattern is the domain name portion of the presented
43
 * identifier (which comes from the Common Name or a DNSName
44
 * portion of the subjectAltName of an X.509 certificate) and
45
 * the hostname is the source domain (i.e. the host portion
46
 * of the URI the user entered).
47
 *
48
 * @note With respect to wildcards we only support matching
49
 * wildcards in the left-most label and as the only character
50
 * in the left-most label (i.e. we support RFC 6125 s. 6.4.3
51
 * Rule 1 and 2 but not the optional Rule 3).  This may change
52
 * in the future.
53
 *
54
 * @note Subversion does not at current support internationalized
55
 * domain names.  Both values are presumed to be in NR-LDH label
56
 * or A-label form (see RFC 5890 for the definition).
57
 *
58
 * @since New in 1.9.
59
 */
60
svn_boolean_t
61
svn_cert__match_dns_identity(svn_string_t *pattern, svn_string_t *hostname);
62
63
64
#ifdef __cplusplus
65
}
66
#endif /* __cplusplus */
67
68
#endif /* SVN_CERT_H */
(-)subversion/libsvn_ra_serf/util.c (-20 / +37 lines)
Lines 28-34 Link Here
28
#define APR_WANT_STRFUNC
28
#define APR_WANT_STRFUNC
29
#include <apr.h>
29
#include <apr.h>
30
#include <apr_want.h>
30
#include <apr_want.h>
31
#include <apr_fnmatch.h>
32
31
33
#include <serf.h>
32
#include <serf.h>
34
#include <serf_bucket_types.h>
33
#include <serf_bucket_types.h>
Lines 49-54 Link Here
49
#include "private/svn_fspath.h"
48
#include "private/svn_fspath.h"
50
#include "private/svn_subr_private.h"
49
#include "private/svn_subr_private.h"
51
#include "private/svn_auth_private.h"
50
#include "private/svn_auth_private.h"
51
#include "private/svn_cert.h"
52
52
53
#include "ra_serf.h"
53
#include "ra_serf.h"
54
54
Lines 274-280 ssl_server_cert(void *baton, int failures, Link Here
274
  apr_hash_t *subject = NULL;
274
  apr_hash_t *subject = NULL;
275
  apr_hash_t *serf_cert = NULL;
275
  apr_hash_t *serf_cert = NULL;
276
  void *creds;
276
  void *creds;
277
  int found_matching_hostname = 0;
278
277
279
  svn_failures = (ssl_convert_serf_failures(failures)
278
  svn_failures = (ssl_convert_serf_failures(failures)
280
      | conn->server_cert_failures);
279
      | conn->server_cert_failures);
Lines 286-311 ssl_server_cert(void *baton, int failures, Link Here
286
      ### This should really be handled by serf, which should pass an error
285
      ### This should really be handled by serf, which should pass an error
287
          for this case, but that has backwards compatibility issues. */
286
          for this case, but that has backwards compatibility issues. */
288
      apr_array_header_t *san;
287
      apr_array_header_t *san;
288
      svn_boolean_t found_san_entry = FALSE;
289
      svn_boolean_t found_matching_hostname = FALSE;
290
      svn_string_t *actual_hostname =
291
          svn_string_create(conn->session->session_url.hostname, scratch_pool);
289
292
290
      serf_cert = serf_ssl_cert_certificate(cert, scratch_pool);
293
      serf_cert = serf_ssl_cert_certificate(cert, scratch_pool);
291
294
292
      san = svn_hash_gets(serf_cert, "subjectAltName");
295
      san = svn_hash_gets(serf_cert, "subjectAltName");
293
      /* Try to find matching server name via subjectAltName first... */
296
      /* Try to find matching server name via subjectAltName first... */
294
      if (san) {
297
      if (san)
298
        {
295
          int i;
299
          int i;
296
          for (i = 0; i < san->nelts; i++) {
300
          found_san_entry = san->nelts > 0;
301
          for (i = 0; i < san->nelts; i++)
302
            {
297
              const char *s = APR_ARRAY_IDX(san, i, const char*);
303
              const char *s = APR_ARRAY_IDX(san, i, const char*);
298
              if (apr_fnmatch(s, conn->session->session_url.hostname,
304
              svn_string_t *cert_hostname = svn_string_create(s, scratch_pool);
299
                  APR_FNM_PERIOD | APR_FNM_CASE_BLIND) == APR_SUCCESS)
305
300
              {
306
              if (svn_cert__match_dns_identity(cert_hostname, actual_hostname))
301
                  found_matching_hostname = 1;
307
                {
308
                  found_matching_hostname = TRUE;
302
                  break;
309
                  break;
303
              }
310
                }
304
          }
311
            }
305
      }
312
        }
306
313
307
      /* Match server certificate CN with the hostname of the server */
314
      /* Match server certificate CN with the hostname of the server iff
308
      if (!found_matching_hostname)
315
       * we didn't find any subjectAltName fields and try to match them.
316
       * Per RFC 2818 they are authoritative if present and CommonName
317
       * should be ignored. */
318
      if (!found_matching_hostname && !found_san_entry)
309
        {
319
        {
310
          const char *hostname = NULL;
320
          const char *hostname = NULL;
311
321
Lines 314-326 ssl_server_cert(void *baton, int failures, Link Here
314
          if (subject)
324
          if (subject)
315
            hostname = svn_hash_gets(subject, "CN");
325
            hostname = svn_hash_gets(subject, "CN");
316
326
317
          if (!hostname
327
          if (hostname)
318
              || apr_fnmatch(hostname, conn->session->session_url.hostname,
328
            {
319
                             APR_FNM_PERIOD | APR_FNM_CASE_BLIND) != APR_SUCCESS)
329
              svn_string_t *cert_hostname = svn_string_create(hostname,
320
          {
330
                                                              scratch_pool);
321
              svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
331
322
          }
332
              if (svn_cert__match_dns_identity(cert_hostname, actual_hostname))
323
      }
333
                {
334
                  found_matching_hostname = TRUE;
335
                }
336
            }
337
        }
338
339
      if (!found_matching_hostname)
340
        svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
324
    }
341
    }
325
342
326
  if (!svn_failures)
343
  if (!svn_failures)
(-)subversion/libsvn_subr/dirent_uri.c (+79 lines)
Lines 38-43 Link Here
38
38
39
#include "dirent_uri.h"
39
#include "dirent_uri.h"
40
#include "private/svn_fspath.h"
40
#include "private/svn_fspath.h"
41
#include "private/svn_cert.h"
41
42
42
/* The canonical empty path.  Can this be changed?  Well, change the empty
43
/* The canonical empty path.  Can this be changed?  Well, change the empty
43
   test below and the path library will work, not so sure about the fs/wc
44
   test below and the path library will work, not so sure about the fs/wc
Lines 2597-2599 svn_urlpath__canonicalize(const char *uri, Link Here
2597
    }
2598
    }
2598
  return uri;
2599
  return uri;
2599
}
2600
}
2601
2602
2603
/* -------------- The cert API (see private/svn_cert.h) ------------- */
2604
2605
svn_boolean_t
2606
svn_cert__match_dns_identity(svn_string_t *pattern, svn_string_t *hostname)
2607
{
2608
  apr_size_t pattern_pos = 0, hostname_pos = 0;
2609
2610
  /* support leading wildcards that composed of the only character in the
2611
   * left-most label. */
2612
  if (pattern->len >= 2 &&
2613
      pattern->data[pattern_pos] == '*' &&
2614
      pattern->data[pattern_pos + 1] == '.')
2615
    {
2616
      while (hostname_pos < hostname->len &&
2617
             hostname->data[hostname_pos] != '.')
2618
        {
2619
          hostname_pos++;
2620
        }
2621
      /* Assume that the wildcard must match something.  Rule 2 says
2622
       * that *.example.com should not match example.com.  If the wildcard
2623
       * ends up not matching anything then it matches .example.com which
2624
       * seems to be essentially the same as just example.com */
2625
      if (hostname_pos == 0)
2626
        return FALSE;
2627
2628
      pattern_pos++;
2629
    }
2630
2631
  while (pattern_pos < pattern->len && hostname_pos < hostname->len)
2632
    {
2633
      char pattern_c = pattern->data[pattern_pos];
2634
      char hostname_c = hostname->data[hostname_pos];
2635
2636
      /* fold case as described in RFC 4343.
2637
       * Note: We actually convert to lowercase, since our URI
2638
       * canonicalization code converts to lowercase and generally
2639
       * most certs are issued with lowercase DNS names, meaning
2640
       * this avoids the fold operation in most cases.  The RFC
2641
       * suggests the opposite transformation, but doesn't require
2642
       * any specific implementation in any case.  It is critical
2643
       * that this folding be locale independent so you can't use
2644
       * tolower(). */
2645
      pattern_c = canonicalize_to_lower(pattern_c);
2646
      hostname_c = canonicalize_to_lower(hostname_c);
2647
2648
      if (pattern_c != hostname_c)
2649
        {
2650
          /* doesn't match */
2651
          return FALSE;
2652
        }
2653
      else
2654
        {
2655
          /* characters match so skip both */
2656
          pattern_pos++;
2657
          hostname_pos++;
2658
        }
2659
    }
2660
2661
  /* ignore a trailing period on the hostname since this has no effect on the
2662
   * security of the matching.  See the following for the long explanation as
2663
   * to why:
2664
   * https://bugzilla.mozilla.org/show_bug.cgi?id=134402#c28
2665
   */
2666
  if (pattern_pos == pattern->len &&
2667
      hostname_pos == hostname->len - 1 &&
2668
      hostname->data[hostname_pos] == '.')
2669
    hostname_pos++;
2670
2671
  if (pattern_pos != pattern->len || hostname_pos != hostname->len)
2672
    {
2673
      /* end didn't match */
2674
      return FALSE;
2675
    }
2676
2677
  return TRUE;
2678
}
(-)subversion/tests/libsvn_subr/dirent_uri-test.c (+144 lines)
Lines 37-42 Link Here
37
#include "svn_pools.h"
37
#include "svn_pools.h"
38
#include "svn_dirent_uri.h"
38
#include "svn_dirent_uri.h"
39
#include "private/svn_fspath.h"
39
#include "private/svn_fspath.h"
40
#include "private/svn_cert.h"
40
41
41
#include "../svn_test.h"
42
#include "../svn_test.h"
42
43
Lines 2714-2719 test_fspath_get_longest_ancestor(apr_pool_t *pool) Link Here
2714
  return SVN_NO_ERROR;
2715
  return SVN_NO_ERROR;
2715
}
2716
}
2716
2717
2718
struct cert_match_dns_test {
2719
  const char *pattern;
2720
  const char *hostname;
2721
  svn_boolean_t expected;
2722
};
2723
2724
static svn_error_t *
2725
run_cert_match_dns_tests(struct cert_match_dns_test *tests, apr_pool_t *pool)
2726
{
2727
  struct cert_match_dns_test *ct;
2728
  apr_pool_t *iterpool = svn_pool_create(pool);
2729
2730
  for (ct = tests; ct->pattern; ct++)
2731
    {
2732
      svn_boolean_t result;
2733
      svn_string_t *pattern, *hostname;
2734
2735
      svn_pool_clear(iterpool);
2736
2737
      pattern = svn_string_create(ct->pattern, iterpool);
2738
      hostname = svn_string_create(ct->hostname, iterpool);
2739
2740
      result = svn_cert__match_dns_identity(pattern, hostname);
2741
      if (result != ct->expected)
2742
        return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
2743
                                 "Expected %s but got %s for pattern '%s' on "
2744
                                 "hostname '%s'",
2745
                                 ct->expected ? "match" : "no match",
2746
                                 result ? "match" : "no match",
2747
                                 pattern->data, hostname->data);
2748
2749
    }
2750
2751
  svn_pool_destroy(iterpool);
2752
2753
  return SVN_NO_ERROR;
2754
}
2755
2756
static struct cert_match_dns_test cert_match_dns_tests[] = {
2757
  { "foo.example.com", "foo.example.com", TRUE }, /* exact match */
2758
  { "foo.example.com", "FOO.EXAMPLE.COM", TRUE }, /* case differences */
2759
  { "FOO.EXAMPLE.COM", "foo.example.com", TRUE },
2760
  { "*.example.com", "FoO.ExAmPlE.CoM", TRUE },
2761
  { "*.ExAmPlE.CoM", "foo.example.com", TRUE },
2762
  { "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", TRUE },
2763
  { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", TRUE },
2764
  { "foo.example.com", "bar.example.com", FALSE }, /* difference at start */
2765
  { "foo.example.com", "foo.example.net", FALSE }, /* difference at end */
2766
  { "foo.example.com", "foo.example.commercial", FALSE }, /* hostname longer */
2767
  { "foo.example.commercial", "foo.example.com", FALSE }, /* pattern longer */
2768
  { "foo.example.comcom", "foo.example.com", FALSE }, /* repeated suffix */
2769
  { "foo.example.com", "foo.example.comcom", FALSE },
2770
  { "foo.example.com.com", "foo.example.com", FALSE },
2771
  { "foo.example.com", "foo.example.com.com", FALSE },
2772
  { "foofoo.example.com", "foo.example.com", FALSE }, /* repeated prefix */
2773
  { "foo.example.com", "foofoo.example.com", FALSE },
2774
  { "foo.foo.example.com", "foo.example.com", FALSE },
2775
  { "foo.example.com", "foo.foo.example.com", FALSE },
2776
  { "foo.*.example.com", "foo.bar.example.com", FALSE }, /* RFC 6125 s. 6.4.3
2777
                                                            Rule 1 */
2778
  { "*.example.com", "foo.example.com", TRUE }, /* RFC 6125 s. 6.4.3 Rule 2 */
2779
  { "*.example.com", "bar.foo.example.com", FALSE }, /* Rule 2 */
2780
  { "*.example.com", "example.com", FALSE }, /* Rule 2 */
2781
  { "*.example.com", ".example.com", FALSE }, /* RFC doesn't say what to do
2782
                                                 here and a leading period on
2783
                                                 a hostname doesn't make sense
2784
                                                 so we'll just reject this. */
2785
  { "*", "foo.example.com", FALSE }, /* wildcard must be left-most label,
2786
                                        implies that there must be more than
2787
                                        one label. */
2788
  { "*", "example.com", FALSE },
2789
  { "*", "com", FALSE },
2790
  { "*.example.com", "foo.example.net", FALSE }, /* difference in literal text
2791
                                                    with a wildcard. */
2792
  { "*.com", "example.com", TRUE }, /* See Errata ID 3090 for RFC 6125,
2793
                                       probably shouldn't allow this but
2794
                                       we do for now. */
2795
  { "*.", "example.com", FALSE }, /* test some dubious 2 character wildcard
2796
                                     patterns */
2797
  { "*.", "example.", TRUE }, /* This one feels questionable */
2798
  { "*.", "example", FALSE },
2799
  { "*.", ".", FALSE },
2800
  { "a", "a", TRUE }, /* check that single letter exact matches work */
2801
  { "a", "b", FALSE }, /* and single letter not matches shouldn't */
2802
  { "*.*.com", "foo.example.com", FALSE }, /* unsupported wildcards */
2803
  { "*.*.com", "example.com", FALSE },
2804
  { "**.example.com", "foo.example.com", FALSE },
2805
  { "**.example.com", "example.com", FALSE },
2806
  { "f*.example.com", "foo.example.com", FALSE },
2807
  { "f*.example.com", "bar.example.com", FALSE },
2808
  { "*o.example.com", "foo.example.com", FALSE },
2809
  { "*o.example.com", "bar.example.com", FALSE },
2810
  { "f*o.example.com", "foo.example.com", FALSE },
2811
  { "f*o.example.com", "bar.example.com", FALSE },
2812
  { "foo.e*.com", "foo.example.com", FALSE },
2813
  { "foo.*e.com", "foo.example.com", FALSE },
2814
  { "foo.e*e.com", "foo.example.com", FALSE },
2815
  { "foo.example.com", "foo.example.com.", TRUE }, /* trailing dot */
2816
  { "*.example.com", "foo.example.com.", TRUE },
2817
  { "foo", "foo.", TRUE },
2818
  { "foo.example.com.", "foo.example.com", FALSE },
2819
  { "*.example.com.", "foo.example.com", FALSE },
2820
  { "foo.", "foo", FALSE },
2821
  { "foo.example.com", "foo.example.com..", FALSE },
2822
  { "*.example.com", "foo.example.com..", FALSE },
2823
  { "foo", "foo..", FALSE },
2824
  { "foo.example.com..", "foo.example.com", FALSE },
2825
  { "*.example.com..", "foo.example.com", FALSE },
2826
  { "foo..", "foo", FALSE },
2827
  { NULL }
2828
};
2829
2830
static svn_error_t *
2831
test_cert_match_dns_identity(apr_pool_t *pool)
2832
{
2833
  return run_cert_match_dns_tests(cert_match_dns_tests, pool);
2834
}
2835
2836
/* This test table implements results that should happen if we supported
2837
 * RFC 6125 s. 6.4.3 Rule 3.  We don't so it's expected to fail for now. */
2838
static struct cert_match_dns_test rule3_tests[] = {
2839
  { "baz*.example.net", "baz1.example.net", TRUE },
2840
  { "*baz.example.net", "foobaz.example.net", TRUE },
2841
  { "b*z.example.net", "buuz.example.net", TRUE },
2842
  { "b*z.example.net", "bz.example.net", FALSE }, /* presume wildcard can't
2843
                                                     match nothing */
2844
  { "baz*.example.net", "baz.example.net", FALSE },
2845
  { "*baz.example.net", "baz.example.net", FALSE },
2846
  { "b*z.example.net", "buuzuuz.example.net", TRUE }, /* presume wildcard
2847
                                                         should be greedy */
2848
  { NULL }
2849
};
2850
2851
static svn_error_t *
2852
test_rule3(apr_pool_t *pool)
2853
{
2854
  return run_cert_match_dns_tests(rule3_tests, pool);
2855
}
2856
2717
2857
2718
/* The test table.  */
2858
/* The test table.  */
2719
2859
Lines 2812-2816 struct svn_test_descriptor_t test_funcs[] = Link Here
2812
                   "test svn_fspath__dirname/basename/split"),
2952
                   "test svn_fspath__dirname/basename/split"),
2813
    SVN_TEST_PASS2(test_fspath_get_longest_ancestor,
2953
    SVN_TEST_PASS2(test_fspath_get_longest_ancestor,
2814
                   "test svn_fspath__get_longest_ancestor"),
2954
                   "test svn_fspath__get_longest_ancestor"),
2955
    SVN_TEST_PASS2(test_cert_match_dns_identity,
2956
                   "test svn_cert__match_dns_identity"),
2957
    SVN_TEST_XFAIL2(test_rule3,
2958
                    "test match with RFC 6125 s. 6.4.3 Rule 3"),
2815
    SVN_TEST_NULL
2959
    SVN_TEST_NULL
2816
  };
2960
  };

Return to bug 519202