From a7eb27d30951364b3f12c56d8375fb0c62236feb Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Mon, 31 Jan 2022 15:34:02 +0100 Subject: [PATCH] Fix URI domains IDNA support --- policy/engine_test.go | 70 ++++++++++++++++++++++++++++++++++++++++-- policy/options.go | 4 ++- policy/options_test.go | 33 ++++++++++++++++++++ 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/policy/engine_test.go b/policy/engine_test.go index e42c589d..1f8be691 100755 --- a/policy/engine_test.go +++ b/policy/engine_test.go @@ -864,7 +864,7 @@ func TestNamePolicyEngine_X509_AllAllowed(t *testing.T) { wantErr: true, }, { - name: "fail/permitted-uri-domain-wildcard", + name: "fail/uri-permitted-domain-wildcard", options: []NamePolicyOption{ AddPermittedURIDomain("*.local"), }, @@ -880,7 +880,7 @@ func TestNamePolicyEngine_X509_AllAllowed(t *testing.T) { wantErr: true, }, { - name: "fail/permitted-uri", + name: "fail/uri-permitted", options: []NamePolicyOption{ AddPermittedURIDomain("test.local"), }, @@ -896,7 +896,7 @@ func TestNamePolicyEngine_X509_AllAllowed(t *testing.T) { wantErr: true, }, { - name: "fail/permitted-uri-with-literal-wildcard", // don't allow literal wildcard in URI, e.g. xxxx://*.domain.tld + name: "fail/uri-permitted-with-literal-wildcard", // don't allow literal wildcard in URI, e.g. xxxx://*.domain.tld options: []NamePolicyOption{ AddPermittedURIDomain("*.local"), }, @@ -911,6 +911,22 @@ func TestNamePolicyEngine_X509_AllAllowed(t *testing.T) { want: false, wantErr: true, }, + { + name: "fail/uri-permitted-idna-internationalized-domain", + options: []NamePolicyOption{ + AddPermittedURIDomain("*.bücher.example.com"), + }, + cert: &x509.Certificate{ + URIs: []*url.URL{ + { + Scheme: "https", + Host: "abc.bücher.example.com", + }, + }, + }, + want: false, + wantErr: true, + }, // SINGLE SAN TYPE EXCLUDED FAILURE TESTS { name: "fail/dns-excluded", @@ -997,6 +1013,22 @@ func TestNamePolicyEngine_X509_AllAllowed(t *testing.T) { want: false, wantErr: true, }, + { + name: "fail/uri-excluded-with-literal-wildcard", // don't allow literal wildcard in URI, e.g. xxxx://*.domain.tld + options: []NamePolicyOption{ + AddExcludedURIDomain("*.local"), + }, + cert: &x509.Certificate{ + URIs: []*url.URL{ + { + Scheme: "https", + Host: "*.local", + }, + }, + }, + want: false, + wantErr: true, + }, // SUBJECT FAILURE TESTS { name: "fail/subject-dns-permitted", @@ -1645,6 +1677,38 @@ func TestNamePolicyEngine_X509_AllAllowed(t *testing.T) { want: true, wantErr: false, }, + { + name: "ok/uri-permitted-idna-internationalized-domain", + options: []NamePolicyOption{ + AddPermittedURIDomain("*.bücher.example.com"), + }, + cert: &x509.Certificate{ + URIs: []*url.URL{ + { + Scheme: "https", + Host: "abc.xn--bcher-kva.example.com", + }, + }, + }, + want: true, + wantErr: false, + }, + { + name: "ok/uri-permitted-idna-internationalized-domain", + options: []NamePolicyOption{ + AddPermittedURIDomain("bücher.example.com"), + }, + cert: &x509.Certificate{ + URIs: []*url.URL{ + { + Scheme: "https", + Host: "xn--bcher-kva.example.com", + }, + }, + }, + want: true, + wantErr: false, + }, // SINGLE SAN TYPE EXCLUDED SUCCESS TESTS { name: "ok/dns-excluded", diff --git a/policy/options.go b/policy/options.go index d37b206f..fe8f470e 100755 --- a/policy/options.go +++ b/policy/options.go @@ -666,6 +666,9 @@ func normalizeAndValidateURIDomainConstraint(constraint string) (string, error) if normalizedConstraint == "" { return "", errors.Errorf("URI domain contraint %q cannot be empty or white space string", constraint) } + if strings.Contains(normalizedConstraint, "://") { + return "", errors.Errorf("URI domain constraint %q contains scheme (not supported yet)", constraint) + } if strings.Contains(normalizedConstraint, "..") { return "", errors.Errorf("URI domain constraint %q cannot have empty labels", constraint) } @@ -687,7 +690,6 @@ func normalizeAndValidateURIDomainConstraint(constraint string) (string, error) if net.ParseIP(normalizedConstraint) != nil { return "", errors.Errorf("URI domain constraint %q cannot be an IP", constraint) } - // TODO(hs): verify that this is OK for URI (IRI) domains too normalizedConstraint, err := idna.Lookup.ToASCII(normalizedConstraint) if err != nil { return "", errors.Wrapf(err, "URI domain constraint %q cannot be converted to ASCII", constraint) diff --git a/policy/options_test.go b/policy/options_test.go index af4aeb3a..0fc54aa2 100644 --- a/policy/options_test.go +++ b/policy/options_test.go @@ -196,6 +196,12 @@ func Test_normalizeAndValidateURIDomainConstraint(t *testing.T) { want: "", wantErr: true, }, + { + name: "fail/scheme-https", + constraint: `https://*.local`, + want: "", + wantErr: true, + }, { name: "fail/too-many-asterisks", constraint: "**.local", @@ -262,6 +268,18 @@ func Test_normalizeAndValidateURIDomainConstraint(t *testing.T) { want: "example.local", wantErr: false, }, + { + name: "ok/idna-internationalized-domain-name-lookup", + constraint: `*.bücher.example.com`, + want: ".xn--bcher-kva.example.com", + wantErr: false, + }, + { + name: "ok/idna-internationalized-domain-name-lookup-deviation", + constraint: `*.faß.de`, + want: ".fass.de", // IDNA2003 vs. 2008 deviation: https://unicode.org/reports/tr46/#Deviations + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1447,6 +1465,21 @@ func TestNew(t *testing.T) { wantErr: false, } }, + "ok/with-permitted-uri-idna": func(t *testing.T) test { + options := []NamePolicyOption{ + WithPermittedURIDomain("*.bücher.example.com"), + } + return test{ + options: options, + want: &NamePolicyEngine{ + permittedURIDomains: []string{".xn--bcher-kva.example.com"}, + numberOfURIDomainConstraints: 1, + totalNumberOfPermittedConstraints: 1, + totalNumberOfConstraints: 1, + }, + wantErr: false, + } + }, "ok/add-permitted-uri": func(t *testing.T) test { options := []NamePolicyOption{ WithPermittedURIDomain("host.local"),