Skip to content

Commit 1d713ed

Browse files
authored
Ignore IP CNs in CSRs (#8231)
If a finalize CSR contains a SAN which looks like an IP address, don't actually include that CN in our IssuanceRequest, and don't promote any other SAN to be the CN either. This is similar to how we ignore the CSR's CN when it is too long.
1 parent 83b6b05 commit 1d713ed

File tree

2 files changed

+30
-5
lines changed

2 files changed

+30
-5
lines changed

csr/csr.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"crypto"
66
"crypto/x509"
77
"errors"
8+
"net/netip"
89
"strings"
910

1011
"github.com/letsencrypt/boulder/core"
@@ -86,20 +87,27 @@ func VerifyCSR(ctx context.Context, csr *x509.CertificateRequest, maxNames int,
8687
}
8788

8889
// CNFromCSR returns the lower-cased Subject Common Name from the CSR, if a
89-
// short enough CN was provided. If it was too long, there will be no CN. If
90-
// none was provided, the CN will be the first SAN that is short enough, which
91-
// is done only for backwards compatibility with prior Let's Encrypt behaviour.
90+
// short enough CN was provided. If it was too long or appears to be an IP,
91+
// there will be no CN. If none was provided, the CN will be the first SAN that
92+
// is short enough, which is done only for backwards compatibility with prior
93+
// Let's Encrypt behaviour.
9294
func CNFromCSR(csr *x509.CertificateRequest) string {
9395
if len(csr.Subject.CommonName) > maxCNLength {
9496
return ""
9597
}
9698

9799
if csr.Subject.CommonName != "" {
100+
_, err := netip.ParseAddr(csr.Subject.CommonName)
101+
if err == nil { // inverted; we're looking for successful parsing here
102+
return ""
103+
}
104+
98105
return strings.ToLower(csr.Subject.CommonName)
99106
}
100107

101-
// If there's no CN already, but we want to set one, promote the first SAN
102-
// which is shorter than the maximum acceptable CN length (if any).
108+
// If there's no CN already, but we want to set one, promote the first dnsName
109+
// SAN which is shorter than the maximum acceptable CN length (if any). We
110+
// will never promote an ipAddress SAN to the CN.
103111
for _, name := range csr.DNSNames {
104112
if len(name) <= maxCNLength {
105113
return strings.ToLower(name)

csr/csr_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"encoding/asn1"
1010
"errors"
1111
"net"
12+
"net/netip"
1213
"net/url"
1314
"strings"
1415
"testing"
@@ -239,6 +240,22 @@ func TestCNFromCSR(t *testing.T) {
239240
}},
240241
"",
241242
},
243+
{
244+
"explicit CN that's an IP",
245+
&x509.CertificateRequest{
246+
Subject: pkix.Name{CommonName: "127.0.0.1"},
247+
},
248+
"",
249+
},
250+
{
251+
"no CN, only IP SANs",
252+
&x509.CertificateRequest{
253+
IPAddresses: []net.IP{
254+
netip.MustParseAddr("127.0.0.1").AsSlice(),
255+
},
256+
},
257+
"",
258+
},
242259
}
243260
for _, tc := range cases {
244261
t.Run(tc.name, func(t *testing.T) {

0 commit comments

Comments
 (0)