Skip to content

Commit

Permalink
Fix nil pointer dereference for PEM certificates
Browse files Browse the repository at this point in the history
According to the documentation
[`Decode`](https://pkg.go.dev/encoding/pem#Decode) returns nil if no pem
data is found.

This is possible if the certificate contains additional data which is
not part of the certificate. If this data occurs at the end the code
before this commit panics. This commit ensures that such data is
ignored. Data between two certificates in the chain is ignored as well.
Leading data leads to an error being returned.

Closes #359
  • Loading branch information
fhofherr committed May 28, 2021
1 parent 2587e3e commit beea700
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 0 deletions.
4 changes: 4 additions & 0 deletions internal/certificate/certdiff.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ func parseCertificates(cert string) ([]*x509.Certificate, error) {
var blk *pem.Block

blk, rest = pem.Decode(rest)
if blk == nil {
// No (further) PEM data found. We are done.
break
}
c, err := x509.ParseCertificate(blk.Bytes)
if err != nil {
return nil, fmt.Errorf("%s: %v", op, err)
Expand Down
39 changes: 39 additions & 0 deletions internal/certificate/certdiff_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package certificate

import (
"strings"
"testing"

"github.com/hetznercloud/terraform-provider-hcloud/internal/testsupport"
"github.com/stretchr/testify/assert"
)

// TestParseCertificates_CertificateChain tries to parse a chain of PEM
// encoded certificates with interspersed and terminating data.
// See: https://github.com/hetznercloud/terraform-provider-hcloud/issues/359
func TestParseCertificates_CertificateChain(t *testing.T) {
pem1, _, err := testsupport.RandTLSCert("example.com")
assert.NoError(t, err)
cert1, err := parseCertificates(pem1)
assert.NoError(t, err)

pem2, _, err := testsupport.RandTLSCert("ca.example.com")
if !assert.NoError(t, err) {
return
}
cert2, err := parseCertificates(pem2)
if !assert.NoError(t, err) {
return
}

// Not really a certificate chain, but enough for our testing purposes
chain := strings.Join([]string{pem1, pem2}, "\nIntermediate data\n") + "Terminating data"
actual, err := parseCertificates(chain)
assert.NoError(t, err)

if !assert.Len(t, actual, 2) {
return
}
assert.Equal(t, cert1[0], actual[0])
assert.Equal(t, cert2[0], actual[1])
}

0 comments on commit beea700

Please sign in to comment.