Skip to content

Commit

Permalink
Perform validation when issuing or signing certificates.
Browse files Browse the repository at this point in the history
  • Loading branch information
victorr committed Nov 15, 2024
1 parent e309098 commit bc46a87
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 71 deletions.
91 changes: 46 additions & 45 deletions builtin/logical/pki/cert_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ func TestParseCertificate(t *testing.T) {
"other_sans": "1.3.6.1.4.1.311.20.2.3;utf8:[email protected]",
"ttl": "2h",
"max_path_length": 2,
"permitted_dns_domains": ".example.com,.www.example.com",
"permitted_dns_domains": "example.com,.example.com,.www.example.com",
"ou": "unit1, unit2",
"organization": "org1, org2",
"country": "US, CA",
Expand Down Expand Up @@ -409,7 +409,7 @@ func TestParseCertificate(t *testing.T) {
UsePSS: true,
ForceAppendCaChain: false,
UseCSRValues: false,
PermittedDNSDomains: []string{".example.com", ".www.example.com"},
PermittedDNSDomains: []string{"example.com", ".example.com", ".www.example.com"},
URLs: nil,
MaxPathLength: 2,
NotBeforeDuration: 45 * time.Second,
Expand All @@ -433,7 +433,7 @@ func TestParseCertificate(t *testing.T) {
"serial_number": "",
"ttl": "2h0m45s",
"max_path_length": 2,
"permitted_dns_domains": ".example.com,.www.example.com",
"permitted_dns_domains": "example.com,.example.com,.www.example.com",
"use_pss": true,
"key_type": "rsa",
"key_bits": 2048,
Expand Down Expand Up @@ -532,49 +532,50 @@ func TestParseCertificate(t *testing.T) {
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b, s := CreateBackendWithStorage(t)

var cert *x509.Certificate
issueTime := time.Now()
if tt.wantParams.IsCA {
resp, err := CBWrite(b, s, "root/generate/internal", tt.data)
require.NoError(t, err)
require.NotNil(t, resp)

certData := resp.Data["certificate"].(string)
cert, err = parsing.ParseCertificateFromString(certData)
require.NoError(t, err)
require.NotNil(t, cert)
} else {
// use the "simple CA" data to create the internal CA
caData := tests[1].data
caData["ttl"] = "3h"
resp, err := CBWrite(b, s, "root/generate/internal", caData)
require.NoError(t, err)
require.NotNil(t, resp)

// create a role
resp, err = CBWrite(b, s, "roles/test", tt.roleData)
require.NoError(t, err)
require.NotNil(t, resp)

// create the cert
resp, err = CBWrite(b, s, "issue/test", tt.data)
require.NoError(t, err)
require.NotNil(t, resp)

certData := resp.Data["certificate"].(string)
cert, err = parsing.ParseCertificateFromString(certData)
require.NoError(t, err)
require.NotNil(t, cert)
}

b, s := CreateBackendWithStorage(t)

var cert *x509.Certificate
issueTime := time.Now()
if tt.wantParams.IsCA {
resp, err := CBWrite(b, s, "root/generate/internal", tt.data)
require.NoError(t, err)
require.NotNil(t, resp)

certData := resp.Data["certificate"].(string)
cert, err = parsing.ParseCertificateFromString(certData)
require.NoError(t, err)
require.NotNil(t, cert)
} else {
// use the "simple CA" data to create the internal CA
caData := tests[1].data
caData["ttl"] = "3h"
resp, err := CBWrite(b, s, "root/generate/internal", caData)
require.NoError(t, err)
require.NotNil(t, resp)

// create a role
resp, err = CBWrite(b, s, "roles/test", tt.roleData)
require.NoError(t, err)
require.NotNil(t, resp)

// create the cert
resp, err = CBWrite(b, s, "issue/test", tt.data)
require.NoError(t, err)
require.NotNil(t, resp)

certData := resp.Data["certificate"].(string)
cert, err = parsing.ParseCertificateFromString(certData)
require.NoError(t, err)
require.NotNil(t, cert)
}

t.Run(tt.name+" parameters", func(t *testing.T) {
testParseCertificateToCreationParameters(t, issueTime, tt, cert)
})
t.Run(tt.name+" fields", func(t *testing.T) {
testParseCertificateToFields(t, issueTime, tt, cert)
t.Run(tt.name+" parameters", func(t *testing.T) {
testParseCertificateToCreationParameters(t, issueTime, tt, cert)
})
t.Run(tt.name+" fields", func(t *testing.T) {
testParseCertificateToFields(t, issueTime, tt, cert)
})
})
}
}
Expand Down
64 changes: 64 additions & 0 deletions builtin/logical/pki/issuing/cert_verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package issuing

import (
"context"
"fmt"
ctx509 "github.com/google/certificate-transparency-go/x509"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/certutil"
"github.com/hashicorp/vault/sdk/logical"
"time"
)

func VerifyCertificate(ctx context.Context, storage logical.Storage, issuerId IssuerID, parsedBundle *certutil.ParsedCertBundle, data *framework.FieldData) error {
certChainPool := ctx509.NewCertPool()
for _, certificate := range parsedBundle.CAChain {
cert, err := convertCertificate(certificate.Bytes)
if err != nil {
return err
}
certChainPool.AddCert(cert)
}

// Validation Code, assuming we need to validate the entire chain of constraints

// Note that we use github.com/google/certificate-transparency-go/x509 to perform certificate verification,
// since that library provides options to disable checks that the standard library does not.

options := ctx509.VerifyOptions{
Intermediates: nil, // We aren't verifying the chain here, this would do more work
Roots: certChainPool,
CurrentTime: time.Time{},
KeyUsages: nil,
MaxConstraintComparisions: 0, // This means infinite
DisableTimeChecks: true,
DisableEKUChecks: true,
DisableCriticalExtensionChecks: false,
DisableNameChecks: false,
DisablePathLenChecks: false,
DisableNameConstraintChecks: false,
}

if err := entSetCertVerifyOptions(ctx, storage, issuerId, &options); err != nil {
return err
}

certificate, err := convertCertificate(parsedBundle.CertificateBytes)
if err != nil {
return err
}

_, err = certificate.Verify(options)
return err
}

func convertCertificate(certBytes []byte) (*ctx509.Certificate, error) {
ret, err := ctx509.ParseCertificate(certBytes)
if err != nil {
return nil, fmt.Errorf("cannot convert certificate for validation: %w", err)
}
return ret, nil
}
17 changes: 17 additions & 0 deletions builtin/logical/pki/issuing/issuing_stubs_oss.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

//go:build !enterprise

package issuing

import (
ctx509 "github.com/google/certificate-transparency-go/x509"
"github.com/hashicorp/vault/sdk/logical"
)

//go:generate go run github.com/hashicorp/vault/tools/stubmaker

func entSetCertVerifyOptions(ctx context.Context, storage logical.Storage, issuerId IssuerID, options *ctx509.VerifyOptions) error {
return nil
}
4 changes: 4 additions & 0 deletions builtin/logical/pki/path_issue_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,10 @@ func (b *backend) pathIssueSignCert(ctx context.Context, req *logical.Request, d
}
}

if err := issuing.VerifyCertificate(sc.GetContext(), sc.GetStorage(), issuerId, parsedBundle, data); err != nil {
return nil, err
}

generateLease := false
if role.GenerateLease != nil && *role.GenerateLease {
generateLease = true
Expand Down
55 changes: 29 additions & 26 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ module github.com/hashicorp/vault
// semantic related to Go module handling), this comment should be updated to explain that.
//
// Whenever this value gets updated, sdk/go.mod should be updated to the same value.
go 1.22.5
go 1.22.7

toolchain go1.23.1

replace github.com/hashicorp/vault/api => ./api

Expand Down Expand Up @@ -61,7 +63,7 @@ require (
github.com/go-git/go-git/v5 v5.11.0
github.com/go-jose/go-jose/v3 v3.0.3
github.com/go-ldap/ldap/v3 v3.4.8
github.com/go-sql-driver/mysql v1.7.1
github.com/go-sql-driver/mysql v1.8.1
github.com/go-test/deep v1.1.1
github.com/go-zookeeper/zk v1.0.3
github.com/gocql/gocql v1.0.0
Expand Down Expand Up @@ -164,7 +166,7 @@ require (
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f
github.com/jefferai/jsonx v1.0.1
github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f
github.com/klauspost/compress v1.17.8
github.com/klauspost/compress v1.17.9
github.com/kr/pretty v0.3.1
github.com/kr/text v0.2.0
github.com/mattn/go-colorable v0.1.13
Expand All @@ -187,7 +189,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/posener/complete v1.2.3
github.com/pquerna/otp v1.2.1-0.20191009055518-468c2dd2b58d
github.com/prometheus/client_golang v1.19.1
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/common v0.55.0
github.com/rboyer/safeio v0.2.1
github.com/robfig/cron/v3 v3.0.1
Expand All @@ -198,29 +200,29 @@ require (
github.com/shirou/gopsutil/v3 v3.22.6
github.com/stretchr/testify v1.9.0
github.com/tink-crypto/tink-go/v2 v2.2.0
go.etcd.io/bbolt v1.3.10
go.etcd.io/etcd/client/pkg/v3 v3.5.13
go.etcd.io/etcd/client/v2 v2.305.5
go.etcd.io/etcd/client/v3 v3.5.13
go.etcd.io/bbolt v1.3.11
go.etcd.io/etcd/client/pkg/v3 v3.5.16
go.etcd.io/etcd/client/v2 v2.305.16
go.etcd.io/etcd/client/v3 v3.5.16
go.mongodb.org/atlas v0.37.0
go.mongodb.org/mongo-driver v1.16.1
go.opentelemetry.io/otel v1.30.0
go.opentelemetry.io/otel/sdk v1.30.0
go.opentelemetry.io/otel/trace v1.30.0
go.uber.org/atomic v1.11.0
go.uber.org/goleak v1.3.0
golang.org/x/crypto v0.27.0
golang.org/x/crypto v0.28.0
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
golang.org/x/net v0.29.0
golang.org/x/net v0.30.0
golang.org/x/oauth2 v0.23.0
golang.org/x/sync v0.8.0
golang.org/x/sys v0.25.0
golang.org/x/term v0.24.0
golang.org/x/text v0.18.0
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
golang.org/x/sys v0.26.0
golang.org/x/term v0.25.0
golang.org/x/text v0.19.0
golang.org/x/tools v0.24.0
google.golang.org/api v0.197.0
google.golang.org/grpc v1.66.1
google.golang.org/protobuf v1.34.2
google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.35.1
gopkg.in/ory-am/dockertest.v3 v3.3.4
k8s.io/apimachinery v0.31.0
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
Expand All @@ -229,12 +231,13 @@ require (
)

require (
cel.dev/expr v0.15.0 // indirect
cel.dev/expr v0.16.0 // indirect
cloud.google.com/go/longrunning v0.6.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/google/certificate-transparency-go v1.2.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/hashicorp/go-secure-stdlib/httputil v0.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
Expand Down Expand Up @@ -322,12 +325,12 @@ require (
github.com/cjlapao/common-go v0.0.39 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cloudfoundry-community/go-cfclient v0.0.0-20220930021109-9c4e6c59ccf1 // indirect
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect
github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20 // indirect
github.com/containerd/continuity v0.4.3 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/coreos/etcd v3.3.27+incompatible // indirect
github.com/coreos/go-oidc/v3 v3.11.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf // indirect
github.com/couchbase/gocb/v2 v2.9.1 // indirect
Expand All @@ -349,8 +352,8 @@ require (
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 // indirect
github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect
github.com/envoyproxy/go-control-plane v0.13.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
Expand Down Expand Up @@ -428,7 +431,7 @@ require (
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jeffchao/backoff v0.0.0-20140404060208-9d7fd7aa17f2 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531 // indirect
github.com/json-iterator/go v1.1.12 // indirect
Expand Down Expand Up @@ -487,7 +490,7 @@ require (
github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/segmentio/fasthash v1.0.3 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect
Expand Down Expand Up @@ -517,15 +520,15 @@ require (
github.com/yusufpapurcu/wmi v1.2.2 // indirect
github.com/zclconf/go-cty v1.12.1 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
go.etcd.io/etcd/api/v3 v3.5.13 // indirect
go.etcd.io/etcd/api/v3 v3.5.16 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
go.opentelemetry.io/otel/metric v1.30.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/time v0.6.0
golang.org/x/time v0.7.0
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect; indirect\
Expand Down
Loading

0 comments on commit bc46a87

Please sign in to comment.