diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dc2bbef..6f0371b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v0.10.3 (May 12, 2022) +Fixed a bug about storing private keys behavior and validation of certificate mismatch + +# v0.10.2 (March 24, 2022) +Fixed issue with revocation while disabling secrets engine + # v0.10.1 (March 10, 2022) Fix for a bug with the use of a synchronized block in pathVenafiCertObtain function. diff --git a/go.mod b/go.mod index 12aecde6..fcb2ae31 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,12 @@ go 1.13 require ( github.com/Venafi/vcert v3.18.4+incompatible - github.com/Venafi/vcert/v4 v4.17.1 + github.com/Venafi/vcert/v4 v4.19.0 github.com/hashicorp/go-hclog v0.14.1 github.com/hashicorp/vault/api v1.0.4 github.com/hashicorp/vault/sdk v0.1.13 github.com/onsi/ginkgo v1.14.0 github.com/onsi/gomega v1.10.1 github.com/rendon/testcli v0.0.0-20161027181003-6283090d169f + github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a ) diff --git a/go.sum b/go.sum index abebaf1b..98a8b5cc 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Venafi/vcert v3.18.4+incompatible h1:mDXSjd+EpXa8YEkEo9Oad19E270aiPJJMhjoKs63b+8= github.com/Venafi/vcert v3.18.4+incompatible/go.mod h1:3dpfrCI+31cDZosD+1UX8GFziVFORaegByXtzT1dwNo= -github.com/Venafi/vcert/v4 v4.17.0 h1:iynxM067DeV37c3AzRjcElNVJeDxwf/Vx2wc3bsBqoM= -github.com/Venafi/vcert/v4 v4.17.0/go.mod h1:VcojF47VAzBnYHSRrb0SwOCmMpWJczajTuPiZNDJJSo= +github.com/Venafi/vcert/v4 v4.19.0 h1:/zIl9+s6uIjtI/LazPplrcSgThbwJkUx1XbyET3u8Iw= +github.com/Venafi/vcert/v4 v4.19.0/go.mod h1:VcojF47VAzBnYHSRrb0SwOCmMpWJczajTuPiZNDJJSo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= diff --git a/plugin/pki/path_venafi_cert_enroll.go b/plugin/pki/path_venafi_cert_enroll.go index cd764634..81b91c51 100644 --- a/plugin/pki/path_venafi_cert_enroll.go +++ b/plugin/pki/path_venafi_cert_enroll.go @@ -2,6 +2,8 @@ package pki import ( "context" + "crypto/rand" + "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/pem" @@ -120,10 +122,6 @@ func (b *backend) pathVenafiIssue(ctx context.Context, req *logical.Request, dat return logical.ErrorResponse("role key type \"any\" not allowed for issuing certificates, only signing"), nil } - if role.ServiceGenerated && data.Get("key_password").(string) == "" { - return logical.ErrorResponse("for service generated csr, \"key_password\" must not be empty"), nil - } - return b.pathVenafiCertObtain(ctx, req, data, role, false) } @@ -156,6 +154,8 @@ func (b *backend) pathVenafiCertObtain(ctx context.Context, req *logical.Request return nil, logical.ErrReadOnly } + keyPass := fmt.Sprintf("t%d-%s.tem.pwd", time.Now().Unix(), randRunes(4)) + b.Logger().Debug("Getting the role\n") roleName := data.Get("role").(string) @@ -281,7 +281,7 @@ func (b *backend) pathVenafiCertObtain(ctx context.Context, req *logical.Request if role.ServiceGenerated { pickupReq.FetchPrivateKey = true - pickupReq.KeyPassword = data.Get("key_password").(string) + pickupReq.KeyPassword = keyPass } pcc, err := cl.RetrieveCertificate(pickupReq) @@ -306,30 +306,48 @@ func (b *backend) pathVenafiCertObtain(ctx context.Context, req *logical.Request format := "" privateKeyFormat, ok := data.GetOk("private_key_format") if ok { - if privateKeyFormat == "der" { + if privateKeyFormat == LEGACY_PEM { format = "legacy-pem" } } + // Local generated if !signCSR && !role.ServiceGenerated { - err = pcc.AddPrivateKey(certReq.PrivateKey, []byte(data.Get("key_password").(string)), format) + privateKeyPemBytes, err := certificate.GetPrivateKeyPEMBock(certReq.PrivateKey, format) if err != nil { return nil, err } - } else { - if reqData.keyPassword != "" && privateKeyFormat == "der" { - privateKey, err := util.DecryptPkcs8PrivateKey(pcc.PrivateKey, reqData.keyPassword) + privateKeyPem := string(pem.EncodeToMemory(privateKeyPemBytes)) + pcc.PrivateKey = privateKeyPem + } else if role.ServiceGenerated { + // Service generated + privateKey, err := DecryptPkcs8PrivateKey(pcc.PrivateKey, keyPass) + if err != nil { + return nil, err + } + block, _ := pem.Decode([]byte(privateKey)) + if privateKeyFormat == LEGACY_PEM { + encrypted, err := util.X509EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", block.Bytes, []byte(keyPass), util.PEMCipherAES256) if err != nil { return nil, err } - privateKey, err = util.EncryptPkcs1PrivateKey(privateKey, reqData.keyPassword) + encryptedPem := pem.EncodeToMemory(encrypted) + privateKeyBytes, err := getPrivateKey(encryptedPem, keyPass) if err != nil { return nil, err } - pcc.PrivateKey = privateKey + privateKey = string(privateKeyBytes) } + pcc.PrivateKey = privateKey + } else { + b.Logger().Debug("CSR is being provided, not processing private key") } + _, err = tls.X509KeyPair([]byte(pcc.Certificate), []byte(pcc.PrivateKey)) + if err != nil { + fmt.Errorf("the certificate returned by Venafi did not contain the requested private key," + + " key pair has been discarded") + } if role.StorePrivateKey && !signCSR { entry, err = logical.StorageEntryJSON("", VenafiCert{ Certificate: pcc.Certificate, @@ -390,7 +408,16 @@ func (b *backend) pathVenafiCertObtain(ctx context.Context, req *logical.Request "expiration": expirationSec, } if !signCSR { - respData["private_key"] = pcc.PrivateKey + keyPassword := data.Get("key_password").(string) + if keyPassword == "" { + respData["private_key"] = pcc.PrivateKey + } else { + encryptedPrivateKeyPem, err := encryptPrivateKey(pcc.PrivateKey, keyPassword) + if err != nil { + return nil, err + } + respData["private_key"] = encryptedPrivateKeyPem + } } var logResp *logical.Response diff --git a/plugin/pki/path_venafi_cert_read.go b/plugin/pki/path_venafi_cert_read.go index 69a1e10f..20074098 100644 --- a/plugin/pki/path_venafi_cert_read.go +++ b/plugin/pki/path_venafi_cert_read.go @@ -15,6 +15,10 @@ func pathVenafiCertRead(b *backend) *framework.Path { Type: framework.TypeString, Description: "Common name or serial number of desired certificate", }, + "key_password": { + Type: framework.TypeString, + Description: "Password for encrypting private key", + }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ logical.ReadOperation: b.pathVenafiCertRead, @@ -61,6 +65,14 @@ func (b *backend) pathVenafiCertRead(ctx context.Context, req *logical.Request, "certificate": cert.Certificate, "private_key": cert.PrivateKey, } + keyPassword := data.Get("key_password").(string) + if keyPassword != "" { + encryptedPrivateKeyPem, err := encryptPrivateKey(cert.PrivateKey, keyPassword) + if err != nil { + return nil, err + } + respData["private_key"] = encryptedPrivateKeyPem + } return &logical.Response{ //Data: structs.New(cert).Map(), diff --git a/plugin/pki/util.go b/plugin/pki/util.go index e5d4f4c5..125f36e1 100644 --- a/plugin/pki/util.go +++ b/plugin/pki/util.go @@ -3,14 +3,19 @@ package pki import ( "bytes" "context" + "crypto/rand" "crypto/tls" "crypto/x509" + "encoding/pem" "fmt" "github.com/Venafi/vcert/v4" "github.com/Venafi/vcert/v4/pkg/endpoint" + "github.com/Venafi/vcert/v4/pkg/util" "github.com/Venafi/vcert/v4/pkg/venafi/tpp" "github.com/hashicorp/vault/sdk/logical" + "github.com/youmark/pkcs8" "io/ioutil" + mathrand "math/rand" "net" "net/http" "os" @@ -24,6 +29,7 @@ const ( role_ttl_test_property = int(120) ttl_test_property = int(48) HTTP_UNAUTHORIZED = 401 + LEGACY_PEM = "der" ) func sliceContains(slice []string, item string) bool { @@ -422,3 +428,113 @@ func getAccessData(cfg *vcert.Config) (tpp.OauthRefreshAccessTokenResponse, erro return tokenInfoResponse, err } + +func randRunes(n int) string { + var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz") + b := make([]rune, n) + for i := range b { + /* #nosec */ + b[i] = letterRunes[mathrand.Intn(len(letterRunes))] + } + return string(b) +} + +func getPrivateKey(keyBytes []byte, passphrase string) ([]byte, error) { + // this section makes some small changes to code from notary/tuf/utils/x509.go + pemBlock, _ := pem.Decode(keyBytes) + if pemBlock == nil { + return nil, fmt.Errorf("no valid private key found") + } + + var err error + if util.X509IsEncryptedPEMBlock(pemBlock) { + keyBytes, err = util.X509DecryptPEMBlock(pemBlock, []byte(passphrase)) + if err != nil { + return nil, fmt.Errorf("private key is encrypted, but could not decrypt it: %s", err.Error()) + } + keyBytes = pem.EncodeToMemory(&pem.Block{Type: pemBlock.Type, Bytes: keyBytes}) + } + + return keyBytes, nil +} + +func encryptPrivateKey(privateKey string, password string) (string, error) { + var encryptedPrivateKeyPem string + var err error + encryptedPrivateKeyPem, err = EncryptPkcs1PrivateKey(privateKey, password) + if err != nil { + // We try PKCS8 + encryptedPrivateKeyPem, err = encryptPkcs8PrivateKey(privateKey, password) + if err != nil { + return "", err + } + } + return encryptedPrivateKeyPem, nil +} + +func DecryptPkcs8PrivateKey(privateKey string, password string) (string, error) { + + block, _ := pem.Decode([]byte(privateKey)) + key, _, err := pkcs8.ParsePrivateKey(block.Bytes, []byte(password)) + + if err != nil { + return "", err + } + + pemType := "PRIVATE KEY" + + privateKeyBytes, err := pkcs8.MarshalPrivateKey(key, nil, nil) + + if err != nil { + return "", err + } + + pemBytes := pem.EncodeToMemory(&pem.Block{Type: pemType, Bytes: privateKeyBytes}) + + return string(pemBytes), nil +} + +func EncryptPkcs1PrivateKey(privateKey string, password string) (string, error) { + + block, _ := pem.Decode([]byte(privateKey)) + + keyType := util.GetPrivateKeyType(privateKey, password) + var encrypted *pem.Block + var err error + if keyType == "RSA PRIVATE KEY" { + encrypted, err = util.X509EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", block.Bytes, []byte(password), util.PEMCipherAES256) + if err != nil { + return "", nil + } + } else if keyType == "EC PRIVATE KEY" { + encrypted, err = util.X509EncryptPEMBlock(rand.Reader, "EC PRIVATE KEY", block.Bytes, []byte(password), util.PEMCipherAES256) + if err != nil { + return "", nil + } + } else { + return "", fmt.Errorf("unable to encrypt key in PKCS1 format") + } + return string(pem.EncodeToMemory(encrypted)), nil +} + +func encryptPkcs8PrivateKey(privateKey string, password string) (string, error) { + block, _ := pem.Decode([]byte(privateKey)) + key, _, err := pkcs8.ParsePrivateKey(block.Bytes, []byte("")) + if err != nil { + return "", err + } + privateKeyBytes1, err := pkcs8.MarshalPrivateKey(key, []byte(password), nil) + if err != nil { + return "", err + } + + keyType := "ENCRYPTED PRIVATE KEY" + + // Generate a pem block with the private key + keyPemBytes := pem.EncodeToMemory(&pem.Block{ + Type: keyType, + Bytes: privateKeyBytes1, + }) + encryptedPrivateKeyPem := string(keyPemBytes) + return encryptedPrivateKeyPem, nil +} diff --git a/vendor/github.com/Venafi/vcert/v4/CODEOWNERS b/vendor/github.com/Venafi/vcert/v4/CODEOWNERS deleted file mode 100644 index dba3863e..00000000 --- a/vendor/github.com/Venafi/vcert/v4/CODEOWNERS +++ /dev/null @@ -1,2 +0,0 @@ -*.go @angelmoo @rvelaVenafi @marcos-albornoz -*.md @tr1ck3r @jdw2465VEN diff --git a/vendor/github.com/Venafi/vcert/v4/Dockerfile b/vendor/github.com/Venafi/vcert/v4/Dockerfile index b786b8f7..d327412a 100644 --- a/vendor/github.com/Venafi/vcert/v4/Dockerfile +++ b/vendor/github.com/Venafi/vcert/v4/Dockerfile @@ -1,6 +1,19 @@ FROM golang:latest +ENV SONAR_SCANNER_VERSION="4.6.2.2472" + +# Installing sonar-scanner tool +WORKDIR /root +RUN apt-get update +RUN apt-get install -y wget unzip +RUN wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip +RUN unzip sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip +RUN rm sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip +RUN mv ./sonar-scanner-${SONAR_SCANNER_VERSION}-linux ./sonar-scanner +ENV PATH="/root/sonar-scanner/bin:${PATH}" + COPY . /go/src/github.com/Venafi/vcert/v4 WORKDIR /go/src/github.com/Venafi/vcert/v4 +CMD ["/bin/bash" ] diff --git a/vendor/github.com/Venafi/vcert/v4/Makefile b/vendor/github.com/Venafi/vcert/v4/Makefile index a3893a71..02e0a3f2 100644 --- a/vendor/github.com/Venafi/vcert/v4/Makefile +++ b/vendor/github.com/Venafi/vcert/v4/Makefile @@ -26,6 +26,7 @@ build: get env GOOS=linux GOARCH=amd64 go build $(GO_LDFLAGS) -o bin/linux/vcert ./cmd/vcert env GOOS=linux GOARCH=386 go build $(GO_LDFLAGS) -o bin/linux/vcert86 ./cmd/vcert env GOOS=darwin GOARCH=amd64 go build $(GO_LDFLAGS) -o bin/darwin/vcert ./cmd/vcert + env GOOS=darwin GOARCH=arm64 go build $(GO_LDFLAGS) -o bin/darwin/vcert_arm ./cmd/vcert env GOOS=windows GOARCH=amd64 go build $(GO_LDFLAGS) -o bin/windows/vcert.exe ./cmd/vcert env GOOS=windows GOARCH=386 go build $(GO_LDFLAGS) -o bin/windows/vcert86.exe ./cmd/vcert @@ -43,20 +44,32 @@ gofmt: ! gofmt -l . | grep -v ^vendor/ | grep . test: get linter - go test -v -cover . - go test -v -cover ./pkg/certificate - go test -v -cover ./pkg/endpoint - go test -v -cover ./pkg/venafi/fake - go test -v -cover ./cmd/vcert + go test -v -coverprofile=cov1.out . + go tool cover -func=cov1.out + go test -v -coverprofile=cov2.out ./pkg/certificate + go tool cover -func=cov2.out + go test -v -coverprofile=cov3.out ./pkg/endpoint + go tool cover -func=cov3.out + go test -v -coverprofile=cov4.out ./pkg/venafi/fake + go tool cover -func=cov4.out + go test -v -coverprofile=cov5.out ./pkg/policy + go tool cover -func=cov5.out + go test -v -coverprofile=cov6.out ./pkg/util + go tool cover -func=cov6.out + go test -v -coverprofile=cov_cmd.out ./cmd/vcert + go tool cover -func=cov_cmd.out tpp_test: get - go test -v $(GOFLAGS) ./pkg/venafi/tpp + go test -v $(GOFLAGS) -coverprofile=cov_tpp.out ./pkg/venafi/tpp + go tool cover -func=cov_tpp.out cloud_test: get - go test -v $(GOFLAGS) ./pkg/venafi/cloud + go test -v $(GOFLAGS) -coverprofile=cov_vaas.out ./pkg/venafi/cloud + go tool cover -func=cov_vaas.out cmd_test: get - go test -v $(GOFLAGS) ./cmd/vcert + go test -v $(GOFLAGS) -coverprofile=cov_cmd.out ./cmd/vcert + go tool cover -func=cov_cmd.out collect_artifacts: rm -rf artifacts diff --git a/vendor/github.com/Venafi/vcert/v4/README-CLI-CLOUD.md b/vendor/github.com/Venafi/vcert/v4/README-CLI-CLOUD.md index 79af7322..2864fbf0 100644 --- a/vendor/github.com/Venafi/vcert/v4/README-CLI-CLOUD.md +++ b/vendor/github.com/Venafi/vcert/v4/README-CLI-CLOUD.md @@ -86,12 +86,17 @@ Options: | `--format` | Use to specify the output format. The `--file` option must be used with the PKCS#12 and JKS formats to specify the keystore file. JKS format also requires `--jks-alias` and at least one password (see `--key-password` and `--jks-password`)
Options: `pem` (default), `json`, `pkcs12`, `jks` | | `--jks-alias` | Use to specify the alias of the entry in the JKS file when `--format jks` is used | | `--jks-password` | Use to specify the keystore password of the JKS file when `--format jks` is used. If not specified, the `--key-password` value is used for both the key and store passwords | +| `--key-curve` | Use to specify the elliptic curve for key generation when `--key-type` is ECDSA.
Options: `p256` (default), `p384`, `p521` | | `--key-file` | Use to specify the name and location of an output file that will contain only the private key.
Example: `--key-file /path-to/example.key` | | `--key-password` | Use to specify a password for encrypting the private key. For a non-encrypted private key, specify `--no-prompt` without specifying this option. You can specify the password using one of three methods: at the command line, when prompted, or by using a password file.
Example: `--key-password file:/path-to/passwd.txt` | | `--key-size` | Use to specify a key size for RSA keys. Default is 2048. | +| `--key-type` | Use to specify the key algorithm.
Options: `rsa` (default), `ecdsa` | | `--no-pickup` | Use to disable the feature of VCert that repeatedly tries to retrieve the issued certificate. When this is used you must run VCert again in pickup mode to retrieve the certificate that was requested. | | `--pickup-id-file` | Use to specify a file name where the unique identifier for the certificate will be stored for subsequent use by pickup, renew, and revoke actions. Default is to write the Pickup ID to STDOUT. | | `--san-dns` | Use to specify a DNS Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-dns one.example.com` `--san-dns two.example.com` | +| `--san-email` | Use to specify an Email Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-email me@example.com` `--san-email you@example.com` | +| `--san-ip` | Use to specify an IP Address Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-ip 10.20.30.40` `--san-ip 192.168.192.168` | +| `--san-uri` | Use to specify a Uniform Resource Indicator Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-uri spiffe://workload1.example.com` `--san-uri spiffe://workload2.example.com` | | `--valid-days` | Use to specify the number of days a certificate needs to be valid.
Example: `--valid-days 30` | | `-z` | Use to specify the name of the Application to which the certificate will be assigned and the API Alias of the Issuing Template that will handle the certificate request.
Example: `-z "Business App\\Enterprise CIT"` | @@ -130,13 +135,18 @@ Options: | `--id` | Use to specify the unique identifier of the certificate returned by the enroll or renew actions. Value may be specified as a string or read from a file by using the file: prefix.
Example: `--id file:cert_id.txt` | | `--jks-alias` | Use to specify the alias of the entry in the JKS file when `--format jks` is used | | `--jks-password` | Use to specify the keystore password of the JKS file when `--format jks` is used. If not specified, the `--key-password` value is used for both the key and store passwords | +| `--key-curve` | Use to specify the elliptic curve for key generation when `--key-type` is ECDSA.
Options: `p256` (default), `p384`, `p521` | | `--key-file` | Use to specify the name and location of an output file that will contain only the private key.
Example: `--key-file /path-to/example.key` | | `--key-password` | Use to specify a password for encrypting the private key. For a non-encrypted private key, specify `--no-prompt` without specifying this option. You can specify the password using one of three methods: at the command line, when prompted, or by using a password file. | | `--key-size` | Use to specify a key size for RSA keys. Default is 2048. | +| `--key-type` | Use to specify the key algorithm.
Options: `rsa` (default), `ecdsa` | | `--no-pickup` | Use to disable the feature of VCert that repeatedly tries to retrieve the issued certificate. When this is used you must run VCert again in pickup mode to retrieve the certificate that was requested. | | `--omit-sans` | Ignore SANs in the previous certificate when preparing the renewal request. Workaround for CAs that forbid any SANs even when the SANs match those the CA automatically adds to the issued certificate. | | `--pickup-id-file` | Use to specify a file name where the unique identifier for the certificate will be stored for subsequent use by `pickup`, `renew`, and `revoke` actions. By default it is written to STDOUT. | | `--san-dns` | Use to specify a DNS Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-dns one.example.com` `--san-dns two.example.com` | +| `--san-email` | Use to specify an Email Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-email me@example.com` `--san-email you@example.com` | +| `--san-ip` | Use to specify an IP Address Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-ip 10.20.30.40` `--san-ip 192.168.192.168` | +| `--san-uri` | Use to specify a Uniform Resource Indicator Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-uri spiffe://workload1.example.com` `--san-uri spiffe://workload2.example.com` | | `--thumbprint` | Use to specify the SHA1 thumbprint of the certificate to renew. Value may be specified as a string or read from the certificate file using the `file:` prefix. | @@ -267,4 +277,5 @@ Options: | `--san-dns` | Use to specify a DNS Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-dns one.example.com` `--san-dns two.example.com` | | `--san-email` | Use to specify an Email Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-email me@example.com` `--san-email you@example.com` | | `--san-ip` | Use to specify an IP Address Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-ip 10.20.30.40` `--san-ip 192.168.192.168` | +| `--san-uri` | Use to specify a Uniform Resource Indicator Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-uri spiffe://workload1.example.com` `--san-uri spiffe://workload2.example.com` | | `--st` | Use to specify the state or province (ST) for the Subject DN. | diff --git a/vendor/github.com/Venafi/vcert/v4/README-CLI-PLATFORM.md b/vendor/github.com/Venafi/vcert/v4/README-CLI-PLATFORM.md index 55e49d92..382421d1 100644 --- a/vendor/github.com/Venafi/vcert/v4/README-CLI-PLATFORM.md +++ b/vendor/github.com/Venafi/vcert/v4/README-CLI-PLATFORM.md @@ -319,6 +319,7 @@ vcert revoke -u https://tpp.venafi.example -t "ql8AEpCtGSv61XGfAknXIA==" --thumb ## Appendix ### Obtaining an Authorization Token +![Minimum Patch Level: TPP 20.2.2+ and 20.3.3+](https://img.shields.io/badge/Minimum%20Patch%20Level-%20TPP%2020.2.2%20and%2020.3.3-f9a90c) ``` vcert getcred -u --username --password @@ -340,7 +341,7 @@ Options: | `--username` | Use to specify the username of a Venafi Platform user. Required if `--p12-file` or `--t` is not present and may not be combined with either. | ### Checking the validity of an Authorization Token -![Minimum Patch Level: TPP 20.1.7+ and 20.2.2+](https://img.shields.io/badge/Minimum%20Patch%20Level-%20TPP%2020.1.7%20and%2020.2.2-f9a90c) +![Minimum Patch Level: TPP 20.2.2+ and 20.3.3+](https://img.shields.io/badge/Minimum%20Patch%20Level-%20TPP%2020.2.2%20and%2020.3.3-f9a90c) ``` vcert checkcred -u -t ``` @@ -390,4 +391,5 @@ Options: | `--san-dns` | Use to specify a DNS Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-dns one.example.com` `--san-dns two.example.com` | | `--san-email` | Use to specify an Email Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-email me@example.com` `--san-email you@example.com` | | `--san-ip` | Use to specify an IP Address Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-ip 10.20.30.40` `--san-ip 192.168.192.168` | +| `--san-uri` | Use to specify a Uniform Resource Indicator Subject Alternative Name. To specify more than one, simply repeat this parameter for each value.
Example: `--san-uri spiffe://workload1.example.com` `--san-uri spiffe://workload2.example.com` | | `--st` | Use to specify the state or province (ST) for the Subject DN. | diff --git a/vendor/github.com/Venafi/vcert/v4/README-CLI-TPP-SSH.md b/vendor/github.com/Venafi/vcert/v4/README-CLI-TPP-SSH.md index e9f21e26..7c889e91 100644 --- a/vendor/github.com/Venafi/vcert/v4/README-CLI-TPP-SSH.md +++ b/vendor/github.com/Venafi/vcert/v4/README-CLI-TPP-SSH.md @@ -192,9 +192,9 @@ vcert sshpickup -u https://tpp.venafi.example -t "ql8AEpCtGSv61XGfAknXIA==" --gu ### Obtaining an Authorization Token ``` -vcert getcred -u --username --password +vcert getcred -u --username --password --ssh -vcert getcred -u --p12-file --p12-password +vcert getcred -u --p12-file --p12-password --ssh ``` Options: @@ -204,8 +204,9 @@ Options: | `--format` | Specify "json" to get JSON formatted output instead of the plain text default. | | `--password` | Use to specify the Venafi Platform user's password. | | `--p12-file` | Use to specify a PKCS#12 file containing a client certificate (and private key) of a Venafi Platform user to be used for mutual TLS. Required if `--username` or `--t` is not present and may not be combined with either. Must specify `--trust-bundle` if the chain for the client certificate is not in the PKCS#12 file. | -| `--p12-password` | Use to specify the password of the PKCS#12 file containing the client certificate. | -| `--scope` | Use to request specific scopes and restrictions. "certificate:manage,revoke;" is the default which is the minimum required to perform any actions supported by the VCert CLI. | +| `--p12-password` | Use to specify the password of the PKCS#12 file containing the client certificate +| `--ssh` | Use to request a token that can be used for ssh actions. This is the equivalent of `--scope ssh:manage;`| +| `--scope` | Use to request specific scopes and restrictions. If not specified, tokens with the "certificate:manage,revoke;" scope are returned. When requesting a token for ssh actions `--scope ssh:manage;` is required or alternatively, the `--ssh` option can be specified.| | `-t` | Use to specify a refresh token for a Venafi Platform user. Required if `--username` or `--p12-file` is not present and may not be combined with either. | | `--trust-bundle` | Use to specify a PEM file name to be used as trust anchors when communicating with the Venafi Platform API server. | | `-u` | Use to specify the URL of the Venafi Trust Protection Platform API server.
Example: `-u https://tpp.venafi.example` | @@ -235,4 +236,4 @@ Options: | ---------------- | ------------------------------------------------------------ | | `-t` | Use to specify an access token for a Venafi Platform user. | | `--trust-bundle` | Use to specify a PEM file name to be used as trust anchors when communicating with the Venafi Platform API server. | -| `-u` | Use to specify the URL of the Venafi Trust Protection Platform API server.
Example: `-u https://tpp.venafi.example` | \ No newline at end of file +| `-u` | Use to specify the URL of the Venafi Trust Protection Platform API server.
Example: `-u https://tpp.venafi.example` | diff --git a/vendor/github.com/Venafi/vcert/v4/README-POLICY-SPEC.md b/vendor/github.com/Venafi/vcert/v4/README-POLICY-SPEC.md index 29fa4eab..5a952b6c 100644 --- a/vendor/github.com/Venafi/vcert/v4/README-POLICY-SPEC.md +++ b/vendor/github.com/Venafi/vcert/v4/README-POLICY-SPEC.md @@ -87,16 +87,16 @@ specification and results in a policy that uses TPP or VaaS defaults. |   `states` | string array | State/Province (ST) values that are permitted | |   `countries` | string array | [ISO 3166 2-Alpha](https://www.iso.org/obp/ui/#search/code/) Country (C) code values that are permitted | |   `keyPair` ||| -|   `keyTypes` | string array | Key algorithm: "RSA" and/or _"ECDSA"_ ![TPP Only](https://img.shields.io/badge/TPP%20Only-orange.svg) | +|   `keyTypes` | string array | Key algorithm: "RSA" and/or "ECDSA" | |   `rsaKeySizes` | integer array | Permitted number of bits for RSA keys: 512, 1024, 2048, 3072, and/or 4096 | -|   `ellipticCurves` | string array | ![TPP Only](https://img.shields.io/badge/TPP%20Only-orange.svg) Permitted elliptic curves: "P256", "P384", and/or "P521" | -|   `serviceGenerated` | boolean | ![TPP Only](https://img.shields.io/badge/TPP%20Only-orange.svg) Indicates whether key pair and CSR must be generated by the Venafi machine identity service | +|   `ellipticCurves` | string array | Permitted elliptic curves: "P256", "P384", and/or "P521" | +|   `serviceGenerated` | boolean | Indicates whether key pair and CSR must be generated by the Venafi machine identity service | |   `reuseAllowed` | boolean | Indicates whether new certificate requests are permitted to reuse a key pair of a known certificate | |  `subjectAltNames` ||| |   `dnsAllowed` | boolean | Indicates whether DNS Subject Alternative Names are permitted| -|   `ipAllowed` | boolean | ![TPP Only](https://img.shields.io/badge/TPP%20Only-orange.svg) Indicates whether IP Address Subject Alternative Names are permitted | -|   `emailAllowed` | boolean | ![TPP Only](https://img.shields.io/badge/TPP%20Only-orange.svg) Indicates whether Email Address (RFC822) Subject Alternative Names are permitted | -|   `uriAllowed` | boolean | ![TPP Only](https://img.shields.io/badge/TPP%20Only-orange.svg) Indicates whether Uniform Resource Indicator (URI) Subject Alternative Names are permitted | +|   `ipAllowed` | boolean | Indicates whether IP Address Subject Alternative Names are permitted | +|   `emailAllowed` | boolean | Indicates whether Email Address (RFC822) Subject Alternative Names are permitted | +|   `uriAllowed` | boolean | Indicates whether Uniform Resource Indicator (URI) Subject Alternative Names are permitted | |   `upnAllowed` | boolean | ![TPP Only](https://img.shields.io/badge/TPP%20Only-orange.svg) Indicates whether User Principal Name (UPN) Subject Alternative Names are permitted | | `defaults` ||| |  `domain` |string| Domain suffix that should be used by default (e.g. "example.com")| @@ -107,7 +107,7 @@ specification and results in a policy that uses TPP or VaaS defaults. |   `state` | string | State/Province (ST) value that should be used by default (e.g. "Utah")| |   `country` | string |[ISO 3166 2-Alpha](https://www.iso.org/obp/ui/#search/code/) Country (C) code value that should be used by default (e.g. "US")| |  `keyPair` ||| -|   `keyType` | string | Key algorithm that should be used by default, "RSA" or _"ECDSA"_ ![TPP Only](https://img.shields.io/badge/TPP%20Only-orange.svg)| +|   `keyType` | string | Key algorithm that should be used by default, "RSA" or "ECDSA"| |   `rsaKeySize` | integer | Number of bits that should be used by default for RSA keys: 512, 1024, 2048, 3072, or 4096| -|   `ellipticCurve` | string | ![TPP Only](https://img.shields.io/badge/TPP%20Only-orange.svg) The elliptic curve that should be used by default: "P256", "P384", or "P521"| -|   `serviceGenerated` | boolean | ![TPP Only](https://img.shields.io/badge/TPP%20Only-orange.svg) Indicates whether keys should be generated by the Venafi machine identity service by default| +|   `ellipticCurve` | string | The elliptic curve that should be used by default: "P256", "P384", "P521"
or _"ED25519"_ ![VaaS Only](https://img.shields.io/badge/VaaS%20Only-orange.svg)| +|   `serviceGenerated` | boolean | Indicates whether keys should be generated by the Venafi machine identity service by default| diff --git a/vendor/github.com/Venafi/vcert/v4/README.md b/vendor/github.com/Venafi/vcert/v4/README.md index 0a06a5ba..7a201c42 100644 --- a/vendor/github.com/Venafi/vcert/v4/README.md +++ b/vendor/github.com/Venafi/vcert/v4/README.md @@ -1,4 +1,4 @@ -![Venafi](Venafi_logo.png) +[![Venafi](.github/images/Venafi_logo.png)](https://www.venafi.com/) [![Apache 2.0 License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) ![Community Supported](https://img.shields.io/badge/Support%20Level-Community-brightgreen) ![Compatible with TPP 17.3+ & VaaS](https://img.shields.io/badge/Compatibility-TPP%2017.3+%20%26%20VaaS-f9a90c) diff --git a/vendor/github.com/Venafi/vcert/v4/SECURITY.md b/vendor/github.com/Venafi/vcert/v4/SECURITY.md deleted file mode 100644 index ba7a87c0..00000000 --- a/vendor/github.com/Venafi/vcert/v4/SECURITY.md +++ /dev/null @@ -1 +0,0 @@ -If you believe you have found a security issue, please report it to opensource@venafi.com. Venafi takes security _very_ seriously. diff --git a/vendor/github.com/Venafi/vcert/v4/Venafi_logo.png b/vendor/github.com/Venafi/vcert/v4/Venafi_logo.png deleted file mode 100644 index 302f9477..00000000 Binary files a/vendor/github.com/Venafi/vcert/v4/Venafi_logo.png and /dev/null differ diff --git a/vendor/github.com/Venafi/vcert/v4/go.sum b/vendor/github.com/Venafi/vcert/v4/go.sum index 2ec6b8fb..0f1643e9 100644 --- a/vendor/github.com/Venafi/vcert/v4/go.sum +++ b/vendor/github.com/Venafi/vcert/v4/go.sum @@ -196,7 +196,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= @@ -256,15 +255,12 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -320,7 +316,6 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/certificate/certificate.go b/vendor/github.com/Venafi/vcert/v4/pkg/certificate/certificate.go index 754fce19..afdc47c1 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/certificate/certificate.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/certificate/certificate.go @@ -1,5 +1,5 @@ /* - * Copyright 2018 Venafi, Inc. + * Copyright 2022 Venafi, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -206,7 +206,7 @@ type Request struct { //SSH Certificate structures -//This request is a standard one, it will hold data for tpp request +// SshCertRequest This request is a standard one, it will hold data for tpp request //and in the future it will hold VaS data. type SshCertRequest struct { Template string @@ -361,6 +361,75 @@ type CertSearchResponse struct { Count int `json:"TotalCount"` } +type CertificateMetaData struct { + Approver []string `json:"Approver"` + CreatedOn string `json:"CreatedOn"` + CertificateAuthorityDN string `json:"CertificateAuthorityDN"` + Contact []string `json:"Contact"` + CreatedBy []string `json:"CreatedBy"` + CertificateDetails struct { + AIACAIssuerURL []string `json:"AIACAIssuerURL"` + AIAKeyIdentifier string `json:"AIAKeyIdentifier"` + C string `json:"C"` + CDPURI string `json:"CDPURI"` + CN string `json:"CN"` + EnhancedKeyUsage string `json:"EnhancedKeyUsage"` + Issuer string `json:"Issuer"` + KeyAlgorithm string `json:"KeyAlgorithm"` + KeySize int `json:"KeySize"` + KeyUsage string `json:"KeyUsage"` + L string `json:"L"` + O string `json:"O"` + OU []string `json:"OU"` + PublicKeyHash string `json:"PublicKeyHash"` + S string `json:"S"` + SKIKeyIdentifier string `json:"SKIKeyIdentifier"` + Serial string `json:"Serial"` + SignatureAlgorithm string `json:"SignatureAlgorithm"` + SignatureAlgorithmOID string `json:"SignatureAlgorithmOID"` + StoreAdded time.Time `json:"StoreAdded"` + Subject string `json:"Subject"` + TemplateMajorVersion string `json:"TemplateMajorVersion"` + TemplateMinorVersion string `json:"TemplateMinorVersion"` + TemplateName string `json:"TemplateName"` + TemplateOID string `json:"TemplateOID"` + Thumbprint string `json:"Thumbprint"` + ValidFrom time.Time `json:"ValidFrom"` + ValidTo time.Time `json:"ValidTo"` + } `json:"CertificateDetails"` + + RenewalDetails struct { + City string `json:"City"` + Country string `json:"Country"` + KeySize int `json:"KeySize"` + Organization string `json:"Organization"` + OrganizationalUnit []string `json:"OrganizationalUnit"` + State string `json:"State"` + Subject string `json:"Subject"` + } `json:"RenewalDetails"` + + ValidationDetails struct { + LastValidationStateUpdate time.Time `json:"LastValidationStateUpdate"` + NetworkValidationDisabled bool `json:"NetworkValidationDisabled"` + ValidationDisabled bool `json:"ValidationDisabled"` + } `json:"ValidationDetails"` + + CustomFields []CustomFieldDetails `json:"CustomFields"` + + DN string `json:"DN"` + Guid string `json:"Guid"` + ManagementType string `json:"ManagementType"` + Name string `json:"Name"` + Origin string `json:"Origin"` + ParentDn string `json:"ParentDn"` + SchemaClass string `json:"SchemaClass"` +} +type CustomFieldDetails struct { + Name string `json:"Name"` + Type string `json:"Type"` + Value []string `json:"Value"` +} + type CertSeachInfo struct { CertificateRequestId string `json:"DN"` CertificateRequestGuid string `json:"Guid"` @@ -409,14 +478,7 @@ func (request *Request) GenerateCSR() error { certificateRequest := x509.CertificateRequest{} certificateRequest.Subject = request.Subject if !request.OmitSANs { - certificateRequest.DNSNames = request.DNSNames - certificateRequest.EmailAddresses = request.EmailAddresses - certificateRequest.IPAddresses = request.IPAddresses - certificateRequest.URIs = request.URIs - - if len(request.UPNs) > 0 { - addUserPrincipalNameSANs(&certificateRequest, request.UPNs) - } + addSubjectAltNames(&certificateRequest, request.DNSNames, request.EmailAddresses, request.IPAddresses, request.URIs, request.UPNs) } certificateRequest.Attributes = request.Attributes @@ -551,11 +613,19 @@ func GetPrivateKeyPEMBock(key crypto.Signer, format ...string) (*pem.Block, erro return &pem.Block{Type: "PRIVATE KEY", Bytes: dataBytes}, err } case *ecdsa.PrivateKey: - b, err := x509.MarshalECPrivateKey(k) - if err != nil { - return nil, err + if currentFormat == "legacy-pem" { + b, err := x509.MarshalECPrivateKey(k) + if err != nil { + return nil, err + } + return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}, nil + } else { + dataBytes, err := pkcs8.MarshalPrivateKey(key.(*ecdsa.PrivateKey), nil, nil) + if err != nil { + return nil, err + } + return &pem.Block{Type: "PRIVATE KEY", Bytes: dataBytes}, err } - return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}, nil default: return nil, fmt.Errorf("%w: unable to format Key", verror.VcertError) } @@ -579,11 +649,19 @@ func GetEncryptedPrivateKeyPEMBock(key crypto.Signer, password []byte, format .. return &pem.Block{Type: "ENCRYPTED PRIVATE KEY", Bytes: dataBytes}, err } case *ecdsa.PrivateKey: - b, err := x509.MarshalECPrivateKey(k) - if err != nil { - return nil, err + if currentFormat == "legacy-pem" { + b, err := x509.MarshalECPrivateKey(k) + if err != nil { + return nil, err + } + return util.X509EncryptPEMBlock(rand.Reader, "EC PRIVATE KEY", b, password, util.PEMCipherAES256) + } else { + dataBytes, err := pkcs8.MarshalPrivateKey(key.(*ecdsa.PrivateKey), password, nil) + if err != nil { + return nil, err + } + return &pem.Block{Type: "ENCRYPTED PRIVATE KEY", Bytes: dataBytes}, err } - return util.X509EncryptPEMBlock(rand.Reader, "EC PRIVATE KEY", b, password, util.PEMCipherAES256) default: return nil, fmt.Errorf("%w: unable to format Key", verror.VcertError) } diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/certificate/sshTemplateAvailable.go b/vendor/github.com/Venafi/vcert/v4/pkg/certificate/sshTemplateAvailable.go new file mode 100644 index 00000000..7ea21c3e --- /dev/null +++ b/vendor/github.com/Venafi/vcert/v4/pkg/certificate/sshTemplateAvailable.go @@ -0,0 +1,6 @@ +package certificate + +type SshAvaliableTemplate struct { + DN string `json:"DN,omitempty"` + Guid string `json:"Guid,omitempty"` +} diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/certificate/x509SubjectAltNameUPN.go b/vendor/github.com/Venafi/vcert/v4/pkg/certificate/x509SubjectAltName.go similarity index 86% rename from vendor/github.com/Venafi/vcert/v4/pkg/certificate/x509SubjectAltNameUPN.go rename to vendor/github.com/Venafi/vcert/v4/pkg/certificate/x509SubjectAltName.go index 54199b9d..25ba2a39 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/certificate/x509SubjectAltNameUPN.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/certificate/x509SubjectAltName.go @@ -1,5 +1,5 @@ /* - * Copyright 2020 Venafi, Inc. + * Copyright 2022 Venafi, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,16 +50,20 @@ const ( nameTypeIP = 7 ) -// Workaround for lack of User Principal Name SAN support in crypto/x509 package -func addUserPrincipalNameSANs(req *x509.CertificateRequest, upNames []string) { - sanBytes, err := marshalSANs(req.DNSNames, req.EmailAddresses, req.IPAddresses, req.URIs, upNames) +// Workaround for lack of User Principal Name SAN support and ability to control SAN extension criticality in crypto/x509 package +func addSubjectAltNames(req *x509.CertificateRequest, dnsNames []string, emailAddrs []string, ipAddrs []net.IP, URIs []*url.URL, UPNs []string) { + sanBytes, err := marshalSANs(dnsNames, emailAddrs, ipAddrs, URIs, UPNs) if err != nil { log.Fatal(err) } + // Per RFC 5280, subjectAltName extension MUST be critical if Subject is empty + // https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6 + sanExtCritical := req.Subject.String() == "" + extSubjectAltName := pkix.Extension{ Id: oidExtensionSubjectAltName, - Critical: false, + Critical: sanExtCritical, Value: sanBytes, } @@ -73,7 +77,7 @@ func addUserPrincipalNameSANs(req *x509.CertificateRequest, upNames []string) { } req.ExtraExtensions = updatedExts - // Clear the SAN request attributes to prevent the SAN extension from being clobbered when CSR is generated + // Ensure SAN request attributes are not set to prevent SAN extension from being clobbered when CSR is generated req.DNSNames = nil req.EmailAddresses = nil req.IPAddresses = nil diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/endpoint/endpoint.go b/vendor/github.com/Venafi/vcert/v4/pkg/endpoint/endpoint.go index 3328723e..909d3060 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/endpoint/endpoint.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/endpoint/endpoint.go @@ -103,6 +103,8 @@ type Connector interface { RetrieveSSHCertificate(req *certificate.SshCertRequest) (response *certificate.SshCertificateObject, err error) RetrieveSshConfig(ca *certificate.SshCaTemplateRequest) (*certificate.SshConfig, error) SearchCertificates(req *certificate.SearchRequest) (*certificate.CertSearchResponse, error) + RetrieveAvailableSSHTemplates() ([]certificate.SshAvaliableTemplate, error) + RetrieveCertificateMetaData(dn string) (*certificate.CertificateMetaData, error) } type Filter struct { @@ -442,12 +444,18 @@ func (z *ZoneConfiguration) UpdateCertificateRequest(request *certificate.Reques } if z.KeyConfiguration != nil { - request.KeyType = z.KeyConfiguration.KeyType - if len(z.KeyConfiguration.KeySizes) != 0 && request.KeyLength == 0 { - request.KeyLength = z.KeyConfiguration.KeySizes[0] + if request.KeyType.String() == "" { + request.KeyType = z.KeyConfiguration.KeyType } - if len(z.KeyConfiguration.KeyCurves) != 0 && request.KeyCurve == certificate.EllipticCurveNotSet { - request.KeyCurve = z.KeyConfiguration.KeyCurves[0] + if request.KeyType == certificate.KeyTypeRSA { + if len(z.KeyConfiguration.KeySizes) != 0 && request.KeyLength == 0 { + request.KeyLength = z.KeyConfiguration.KeySizes[0] + } + } + if request.KeyType == certificate.KeyTypeECDSA { + if len(z.KeyConfiguration.KeyCurves) != 0 && request.KeyCurve == certificate.EllipticCurveNotSet { + request.KeyCurve = z.KeyConfiguration.KeyCurves[0] + } } } else { // Zone config has no key length parameters, so we just pass user's -key-size or fall to default 2048 diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/policy/constants.go b/vendor/github.com/Venafi/vcert/v4/pkg/policy/constants.go index f637cdc0..5f78c5db 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/policy/constants.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/policy/constants.go @@ -43,4 +43,13 @@ const ( CloudRequesterName = "Venafi Cloud Service" CloudRequesterEmail = "no-reply@venafi.cloud" CloudRequesterPhone = "801-555-0123" + ipv4 = "\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b" + ipv6 = "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))" + v4private = "^(172\\.(1[6-9]\\.|2[0-9]\\.|3[0-1]\\.)|192\\.168\\.|10\\.).*" + v6private = "^(::1$)|([fF][cCdD]).*" + + IdentityUser = 1 + IdentitySecurityGroup = 2 + IdentityDistributionGroup = 8 + AllIdentities = IdentityUser + IdentitySecurityGroup + IdentityDistributionGroup ) diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/policy/policySpecification.go b/vendor/github.com/Venafi/vcert/v4/pkg/policy/policySpecification.go index 374ed297..94e6f524 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/policy/policySpecification.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/policy/policySpecification.go @@ -37,11 +37,13 @@ type KeyPair struct { } type SubjectAltNames struct { - DnsAllowed *bool `json:"dnsAllowed,omitempty" yaml:"dnsAllowed,omitempty"` - IpAllowed *bool `json:"ipAllowed,omitempty" yaml:"ipAllowed,omitempty"` - EmailAllowed *bool `json:"emailAllowed,omitempty" yaml:"emailAllowed,omitempty"` - UriAllowed *bool `json:"uriAllowed,omitempty" yaml:"uriAllowed,omitempty"` - UpnAllowed *bool `json:"upnAllowed,omitempty" yaml:"upnAllowed,omitempty"` + DnsAllowed *bool `json:"dnsAllowed,omitempty" yaml:"dnsAllowed,omitempty"` + IpAllowed *bool `json:"ipAllowed,omitempty" yaml:"ipAllowed,omitempty"` + EmailAllowed *bool `json:"emailAllowed,omitempty" yaml:"emailAllowed,omitempty"` + UriAllowed *bool `json:"uriAllowed,omitempty" yaml:"uriAllowed,omitempty"` + UpnAllowed *bool `json:"upnAllowed,omitempty" yaml:"uriProtocols,omitempty"` + UriProtocols []string `json:"uriProtocols,omitempty" yaml:"uriProtocols,omitempty"` + IpConstraints []string `json:"ipConstraints,omitempty" yaml:"uriProtocols,omitempty"` } type Default struct { diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/policy/policyStructures.go b/vendor/github.com/Venafi/vcert/v4/pkg/policy/policyStructures.go index 00d898ae..02e82395 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/policy/policyStructures.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/policy/policyStructures.go @@ -54,9 +54,14 @@ type CloudPolicyRequest struct { SubjectSTRegexes []string `json:"subjectSTRegexes"` SubjectCValues []string `json:"subjectCValues"` SanRegexes []string `json:"sanRegexes"` - KeyTypes []KeyTypes `json:"keyTypes"` + SanIpAddressRegexes []string `json:"sanIpAddressRegexes"` + SanRfc822NameRegexes []string `json:"sanRfc822NameRegexes"` + SanUniformResourceIdentifierRegexes []string `json:"sanUniformResourceIdentifierRegexes"` + KeyTypes []KeyType `json:"keyTypes"` KeyReuse *bool `json:"keyReuse"` RecommendedSettings *RecommendedSettings `json:"recommendedSettings"` + CsrUploadAllowed bool `json:"csrUploadAllowed"` + KeyGeneratedByVenafiAllowed bool `json:"keyGeneratedByVenafiAllowed"` } type Product struct { @@ -68,9 +73,10 @@ type Product struct { OrganizationId *int64 `json:"organizationId,omitempty"` } -type KeyTypes struct { - KeyType string `json:"keyType"` - KeyLengths []int `json:"keyLengths"` +type KeyType struct { + KeyType string `json:"keyType"` + KeyLengths []int `json:"keyLengths,omitempty"` + KeyCurves []string `json:"keyCurves,omitempty"` } type TrackingData struct { @@ -93,10 +99,11 @@ type RecommendedSettings struct { type Key struct { Type string `json:"type"` - Length int `json:"length"` + Length int `json:"length,omitempty"` + Curve string `json:"curve,omitempty"` } -type ApplicationCreateRequest struct { +type Application struct { OwnerIdsAndTypes []OwnerIdType `json:"ownerIdsAndTypes"` Name string `json:"name"` Description string `json:"description"` @@ -149,6 +156,42 @@ type TppPolicy struct { WantRenewal *int } +type BrowseIdentitiesRequest struct { + Filter string + Limit int + IdentityType int +} + +type BrowseIdentitiesResponse struct { + Identities []IdentityEntry +} + +type IdentitySelfResponse struct { + Identities []IdentityEntry +} + +type ValidateIdentityRequest struct { + ID IdentityInformation +} + +type ValidateIdentityResponse struct { + ID IdentityEntry +} + +type IdentityInformation struct { + PrefixedUniversal string +} + +type IdentityEntry struct { + FullName string + Name string + Prefix string + PrefixedName string + PrefixedUniversal string + Type int + Universal string +} + type LockedAttribute struct { Value string Locked bool diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/policy/policyUtils.go b/vendor/github.com/Venafi/vcert/v4/pkg/policy/policyUtils.go index d047e99f..38a5bead 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/policy/policyUtils.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/policy/policyUtils.go @@ -1,7 +1,11 @@ package policy import ( + "encoding/json" "fmt" + "gopkg.in/yaml.v2" + "io/ioutil" + "os" "path/filepath" "strconv" "strings" @@ -11,7 +15,7 @@ import ( //this is the nearest to a constant on arrays. var TppKeyType = []string{"RSA", "ECDSA"} var TppRsaKeySize = []int{512, 1024, 2048, 3072, 4096} -var CloudRsaKeySize = []int{1024, 2048, 4096} +var CloudRsaKeySize = []int{1024, 2048, 3072, 4096} var TppEllipticCurves = []string{"P256", "P384", "P521"} func GetFileType(f string) string { @@ -90,8 +94,10 @@ func validatePolicySubject(ps *PolicySpecification) error { return fmt.Errorf("attribute countries has more than one value") } - if len(subject.Countries[0]) != 2 { - return fmt.Errorf("number of country's characters, doesn't match to two characters") + if len(subject.Countries) > 0 { + if len(subject.Countries[0]) != 2 { + return fmt.Errorf("number of country's characters, doesn't match to two characters") + } } return nil @@ -722,19 +728,12 @@ func ValidateCloudPolicySpecification(ps *PolicySpecification) error { //validate key type if ps.Policy != nil { if ps.Policy.KeyPair != nil { - if len(ps.Policy.KeyPair.KeyTypes) > 1 { - return fmt.Errorf("attribute keyTypes has more than one value") - } - - if ps.Policy.KeyPair.KeyTypes[0] != "RSA" { - return fmt.Errorf("specified attribute keyTypes value is not supported on Venafi cloud") - } //validate key KeyTypes:keyLengths if len(ps.Policy.KeyPair.RsaKeySizes) > 0 { unSupported := getInvalidCloudRsaKeySizeValue(ps.Policy.KeyPair.RsaKeySizes) if unSupported != nil { - return fmt.Errorf("specified attribute key lenght value: %s is not supported on Venafi cloud", strconv.Itoa(*(unSupported))) + return fmt.Errorf("specified attribute key length value: %s is not supported on VaaS", strconv.Itoa(*(unSupported))) } } } @@ -744,9 +743,14 @@ func ValidateCloudPolicySpecification(ps *PolicySpecification) error { subjectAltNames := getSubjectAltNames(*(ps.Policy.SubjectAltNames)) if len(subjectAltNames) > 0 { for k, v := range subjectAltNames { - if k != "dnsAllowed" && v { + if k == "upnAllowed" && v { return fmt.Errorf("specified subjectAltNames: %s value is true, this value is not allowed ", k) } + if k == "uriAllowed" && v { + if len(ps.Policy.SubjectAltNames.UriProtocols) == 0 { + return fmt.Errorf("uriAllowed attribute is true, but uriProtocols is not specified or empty") + } + } } } } @@ -810,8 +814,8 @@ func ValidateCloudPolicySpecification(ps *PolicySpecification) error { if ps.Default != nil && ps.Default.KeyPair != nil { if ps.Default.KeyPair.KeyType != nil && *(ps.Default.KeyPair.KeyType) != "" { - if *(ps.Default.KeyPair.KeyType) != "RSA" { - return fmt.Errorf("specified default attribute keyType value is not supported on Venafi cloud") + if *(ps.Default.KeyPair.KeyType) != "RSA" && *(ps.Default.KeyPair.KeyType) != "EC" { + return fmt.Errorf("specified default attribute keyType value is not supported on VaaS") } } @@ -819,7 +823,7 @@ func ValidateCloudPolicySpecification(ps *PolicySpecification) error { if ps.Default.KeyPair.RsaKeySize != nil && *(ps.Default.KeyPair.RsaKeySize) != 0 { unSupported := getInvalidCloudRsaKeySizeValue([]int{*(ps.Default.KeyPair.RsaKeySize)}) if unSupported != nil { - return fmt.Errorf("specified attribute key lenght value: %s is not supported on Venafi cloud", strconv.Itoa(*(unSupported))) + return fmt.Errorf("specified attribute key length value: %s is not supported on VaaS", strconv.Itoa(*(unSupported))) } } } @@ -939,63 +943,169 @@ func BuildCloudCitRequest(ps *PolicySpecification, ca *CADetails) (*CloudPolicyR } else { cloudPolicyRequest.SanRegexes = regexValues //in cloud subject CN and SAN have the same values and we use domains as those values } + + if ps.Policy.SubjectAltNames != nil && ps.Policy.SubjectAltNames.EmailAllowed != nil { + if *(ps.Policy.SubjectAltNames.EmailAllowed) { + rfc882Regex := ConvertToRfc822Regex(ps.Policy.Domains) + cloudPolicyRequest.SanRfc822NameRegexes = rfc882Regex + } else { + cloudPolicyRequest.SanRfc822NameRegexes = nil + } + } + + if ps.Policy != nil && ps.Policy.SubjectAltNames != nil && len(ps.Policy.SubjectAltNames.UriProtocols) > 0 { + uriRegex := convertToUriRegex(ps.Policy.SubjectAltNames.UriProtocols, ps.Policy.Domains) + cloudPolicyRequest.SanUniformResourceIdentifierRegexes = uriRegex + } + } else { cloudPolicyRequest.SubjectCNRegexes = []string{".*"} cloudPolicyRequest.SanRegexes = []string{".*"} + + if ps.Policy != nil { + if ps.Policy.SubjectAltNames != nil && ps.Policy.SubjectAltNames.EmailAllowed != nil { + if *(ps.Policy.SubjectAltNames.EmailAllowed) { + cloudPolicyRequest.SanRfc822NameRegexes = []string{".*@.*"} + } + } + + if ps.Policy.SubjectAltNames != nil && ps.Policy.SubjectAltNames.IpAllowed != nil { + if *(ps.Policy.SubjectAltNames.IpAllowed) { + cloudPolicyRequest.SanIpAddressRegexes = []string{} + } + } + + //to be implemented. + if ps.Policy != nil && ps.Policy.SubjectAltNames != nil && len(ps.Policy.SubjectAltNames.UriProtocols) > 0 { + uriRegex := convertToUriRegex(ps.Policy.SubjectAltNames.UriProtocols, []string{".*"}) + cloudPolicyRequest.SanUniformResourceIdentifierRegexes = uriRegex + } + + } + } + + if ps.Policy != nil && ps.Policy.SubjectAltNames != nil && ps.Policy.SubjectAltNames.IpAllowed != nil { + if *(ps.Policy.SubjectAltNames.IpAllowed) { + if len(ps.Policy.SubjectAltNames.IpConstraints) > 0 { + cloudPolicyRequest.SanIpAddressRegexes = getIpRegexes(ps.Policy.SubjectAltNames.IpConstraints) + } else { + cloudPolicyRequest.SanIpAddressRegexes = []string{ + ipv4, ipv6, + } + } + + } else { + cloudPolicyRequest.SanIpAddressRegexes = nil + } } if ps.Policy != nil && ps.Policy.Subject != nil && len(ps.Policy.Subject.Orgs) > 0 { - cloudPolicyRequest.SubjectORegexes = ps.Policy.Subject.Orgs + if len(ps.Policy.Subject.Orgs) == 1 && ps.Policy.Subject.Orgs[0] == "" { + cloudPolicyRequest.SubjectORegexes = nil + } else { + cloudPolicyRequest.SubjectORegexes = ps.Policy.Subject.Orgs + } + } else { cloudPolicyRequest.SubjectORegexes = []string{".*"} } if ps.Policy != nil && ps.Policy.Subject != nil && len(ps.Policy.Subject.OrgUnits) > 0 { - cloudPolicyRequest.SubjectOURegexes = ps.Policy.Subject.OrgUnits + if len(ps.Policy.Subject.OrgUnits) == 1 && ps.Policy.Subject.OrgUnits[0] == "" { + cloudPolicyRequest.SubjectOURegexes = nil + } else { + cloudPolicyRequest.SubjectOURegexes = ps.Policy.Subject.OrgUnits + } + } else { cloudPolicyRequest.SubjectOURegexes = []string{".*"} } if ps.Policy != nil && ps.Policy.Subject != nil && len(ps.Policy.Subject.Localities) > 0 { - cloudPolicyRequest.SubjectLRegexes = ps.Policy.Subject.Localities + if len(ps.Policy.Subject.Localities) == 1 && ps.Policy.Subject.Localities[0] == "" { + cloudPolicyRequest.SubjectLRegexes = nil + } else { + cloudPolicyRequest.SubjectLRegexes = ps.Policy.Subject.Localities + } + } else { cloudPolicyRequest.SubjectLRegexes = []string{".*"} } if ps.Policy != nil && ps.Policy.Subject != nil && len(ps.Policy.Subject.States) > 0 { - cloudPolicyRequest.SubjectSTRegexes = ps.Policy.Subject.States + if len(ps.Policy.Subject.States) == 1 && ps.Policy.Subject.States[0] == "" { + cloudPolicyRequest.SubjectSTRegexes = nil + } else { + cloudPolicyRequest.SubjectSTRegexes = ps.Policy.Subject.States + } } else { cloudPolicyRequest.SubjectSTRegexes = []string{".*"} } if ps.Policy != nil && ps.Policy.Subject != nil && len(ps.Policy.Subject.Countries) > 0 { - cloudPolicyRequest.SubjectCValues = ps.Policy.Subject.Countries + if len(ps.Policy.Subject.Countries) == 1 && ps.Policy.Subject.Countries[0] == "" { + cloudPolicyRequest.SubjectCValues = nil + } else { + cloudPolicyRequest.SubjectCValues = ps.Policy.Subject.Countries + } } else { cloudPolicyRequest.SubjectCValues = []string{".*"} } - var keyTypes KeyTypes + var keyType *KeyType + var ecKeyType *KeyType if ps.Policy != nil && ps.Policy.KeyPair != nil && len(ps.Policy.KeyPair.KeyTypes) > 0 { - keyTypes.KeyType = ps.Policy.KeyPair.KeyTypes[0] + for _, val := range ps.Policy.KeyPair.KeyTypes { + if val == "RSA" { + keyType = &KeyType{} + keyType.KeyType = val + } else if val == "EC" { + ecKeyType = &KeyType{} + ecKeyType.KeyType = val + } + } + } else { - keyTypes.KeyType = "RSA" + keyType = &KeyType{} + keyType.KeyType = "RSA" } - if ps.Policy != nil && ps.Policy.KeyPair != nil && len(ps.Policy.KeyPair.RsaKeySizes) > 0 { - keyTypes.KeyLengths = ps.Policy.KeyPair.RsaKeySizes - } else { - // on this case we need to look if there is a default if so then we can use it. - if ps.Default != nil && ps.Default.KeyPair != nil && ps.Default.KeyPair.RsaKeySize != nil { - keyTypes.KeyLengths = []int{*(ps.Default.KeyPair.RsaKeySize)} + if keyType != nil { + if ps.Policy != nil && ps.Policy.KeyPair != nil && len(ps.Policy.KeyPair.RsaKeySizes) > 0 { + keyType.KeyLengths = ps.Policy.KeyPair.RsaKeySizes } else { - keyTypes.KeyLengths = []int{2048} + // on this case we need to look if there is a default if so then we can use it. + if ps.Default != nil && ps.Default.KeyPair != nil && ps.Default.KeyPair.RsaKeySize != nil { + keyType.KeyLengths = []int{*(ps.Default.KeyPair.RsaKeySize)} + } else { + keyType.KeyLengths = []int{2048} + } + } + } + if ecKeyType != nil { + if ps.Policy != nil && ps.Policy.KeyPair != nil && len(ps.Policy.KeyPair.EllipticCurves) > 0 { + ecKeyType.KeyCurves = ps.Policy.KeyPair.EllipticCurves + } else { + // on this case we need to look if there is a default if so then we can use it. + if ps.Default != nil && ps.Default.KeyPair != nil && ps.Default.KeyPair.EllipticCurve != nil { + ecKeyType.KeyCurves = []string{*(ps.Default.KeyPair.EllipticCurve)} + } else { + ecKeyType.KeyCurves = []string{"P256"} + } + } } - var keyTypesArr []KeyTypes + var keyTypesArr []KeyType - keyTypesArr = append(keyTypesArr, keyTypes) + if keyType != nil { + keyTypesArr = append(keyTypesArr, *(keyType)) + } + + if ecKeyType != nil { + keyTypesArr = append(keyTypesArr, *(ecKeyType)) + } if len(keyTypesArr) > 0 { cloudPolicyRequest.KeyTypes = keyTypesArr @@ -1047,11 +1157,19 @@ func BuildCloudCitRequest(ps *PolicySpecification, ca *CADetails) (*CloudPolicyR if ps.Default.KeyPair.KeyType != nil { key.Type = *(ps.Default.KeyPair.KeyType) - if ps.Default.KeyPair.RsaKeySize != nil { - key.Length = *(ps.Default.KeyPair.RsaKeySize) - } else { - //default - key.Length = 2048 + if key.Type == "RSA" { + if ps.Default.KeyPair.RsaKeySize != nil { + key.Length = *(ps.Default.KeyPair.RsaKeySize) + } else { + //default + key.Length = 2048 + } + } else if key.Type == "EC" { + if ps.Default.KeyPair.EllipticCurve != nil && *(ps.Default.KeyPair.EllipticCurve) != "" { + key.Curve = *(ps.Default.KeyPair.EllipticCurve) + } else { + key.Curve = "P256" + } } shouldCreateKPRS = true @@ -1067,6 +1185,14 @@ func BuildCloudCitRequest(ps *PolicySpecification, ca *CADetails) (*CloudPolicyR cloudPolicyRequest.RecommendedSettings = &recommendedSettings } + if ps.Policy != nil && ps.Policy.KeyPair != nil && ps.Policy.KeyPair.ServiceGenerated != nil { + cloudPolicyRequest.CsrUploadAllowed = !*(ps.Policy.KeyPair.ServiceGenerated) + cloudPolicyRequest.KeyGeneratedByVenafiAllowed = *(ps.Policy.KeyPair.ServiceGenerated) + } else { + cloudPolicyRequest.CsrUploadAllowed = true + cloudPolicyRequest.KeyGeneratedByVenafiAllowed = true + } + return &cloudPolicyRequest, nil } @@ -1088,6 +1214,71 @@ func ConvertToRegex(values []string, wildcardAllowed bool) []string { return nil } +func getIpRegexes(supportedIps []string) (ipRegexes []string) { + + ipRegexes = make([]string, 0) + + for _, val := range supportedIps { + + if val == "v4" { + ipRegexes = append(ipRegexes, ipv4) + } + if val == "v6" { + ipRegexes = append(ipRegexes, ipv6) + + } + if val == "v4private" { + ipRegexes = append(ipRegexes, v4private) + + } + if val == "v6private" { + ipRegexes = append(ipRegexes, v6private) + + } + } + + return ipRegexes +} + +func ConvertToRfc822Regex(values []string) []string { + var regexVals []string + for _, current := range values { + + currentRegex := strings.ReplaceAll(current, ".", "\\.") + currentRegex = fmt.Sprint(".*@", currentRegex) + + regexVals = append(regexVals, currentRegex) + } + + if len(regexVals) > 0 { + return regexVals + } + + return nil +} + +func convertToUriRegex(protocols, domains []string) []string { + + var regexVals []string + + protocolsS := strings.Join(protocols, "|") + protocolsS = fmt.Sprint("(", protocolsS, ")://.*\\.") + + for _, current := range domains { + + currentRegex := strings.ReplaceAll(current, ".", "\\.") + currentRegex = fmt.Sprint(protocolsS, currentRegex) + + regexVals = append(regexVals, currentRegex) + } + + if len(regexVals) > 0 { + return regexVals + } + + return nil +} + func RemoveRegex(values []string) []string { var regexVals []string for _, current := range values { @@ -1179,6 +1370,14 @@ func IsPolicyEmpty(ps *PolicySpecification) bool { if san.UpnAllowed != nil { return false } + + if len(san.IpConstraints) > 0 { + return false + } + + if len(san.UriProtocols) > 0 { + return false + } } if policy.CertificateAuthority != nil && *(policy.CertificateAuthority) != "" { @@ -1296,3 +1495,94 @@ func IsDefaultEmpty(ps *PolicySpecification) bool { return true } + +func VerifyPolicySpec(bytes []byte, fileExt string) error { + + var err error + var policySpecification PolicySpecification + + if fileExt == JsonExtension { + err = json.Unmarshal(bytes, &policySpecification) + if err != nil { + return err + } + } else if fileExt == YamlExtension { + err = yaml.Unmarshal(bytes, &policySpecification) + if err != nil { + return err + } + } else { + return fmt.Errorf("the specified file is not supported") + } + + return nil +} + +func GetFileAndBytes(p string) (*os.File, []byte, error) { + file, err := os.Open(p) + if err != nil { + return nil, nil, err + } + + bytes, err := ioutil.ReadAll(file) + if err != nil { + return nil, nil, err + } + return file, bytes, nil +} +func GetPolicySpec() *PolicySpecification { + + emptyString := "" + intVal := 0 + falseBool := false + + specification := PolicySpecification{ + Policy: &Policy{ + CertificateAuthority: &emptyString, + Domains: []string{""}, + WildcardAllowed: &falseBool, + AutoInstalled: &falseBool, + MaxValidDays: &intVal, + Subject: &Subject{ + Orgs: []string{""}, + OrgUnits: []string{""}, + Localities: []string{""}, + States: []string{""}, + Countries: []string{""}, + }, + KeyPair: &KeyPair{ + KeyTypes: []string{""}, + RsaKeySizes: []int{0}, + ServiceGenerated: &falseBool, + ReuseAllowed: &falseBool, + EllipticCurves: []string{""}, + }, + SubjectAltNames: &SubjectAltNames{ + DnsAllowed: &falseBool, + IpAllowed: &falseBool, + EmailAllowed: &falseBool, + UriAllowed: &falseBool, + UpnAllowed: &falseBool, + UriProtocols: []string{""}, + IpConstraints: []string{""}, + }, + }, + Default: &Default{ + Domain: &emptyString, + Subject: &DefaultSubject{ + Org: &emptyString, + OrgUnits: []string{""}, + Locality: &emptyString, + State: &emptyString, + Country: &emptyString, + }, + KeyPair: &DefaultKeyPair{ + KeyType: &emptyString, + RsaKeySize: &intVal, + EllipticCurve: &emptyString, + ServiceGenerated: &falseBool, + }, + }, + } + return &specification +} diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/util/utils.go b/vendor/github.com/Venafi/vcert/v4/pkg/util/utils.go index 470e0887..202a327a 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/util/utils.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/util/utils.go @@ -60,10 +60,47 @@ func EncryptPkcs1PrivateKey(privateKey, password string) (string, error) { block, _ := pem.Decode([]byte(privateKey)) - encrypted, err := X509EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", block.Bytes, []byte(password), PEMCipherAES256) - - if err != nil { - return "", nil + keyType := GetPrivateKeyType(privateKey, password) + var encrypted *pem.Block + var err error + if keyType == "RSA PRIVATE KEY" { + encrypted, err = X509EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", block.Bytes, []byte(password), PEMCipherAES256) + if err != nil { + return "", nil + } + } else if keyType == "EC PRIVATE KEY" { + encrypted, err = X509EncryptPEMBlock(rand.Reader, "EC PRIVATE KEY", block.Bytes, []byte(password), PEMCipherAES256) + if err != nil { + return "", nil + } } return string(pem.EncodeToMemory(encrypted)), nil } + +func GetBooleanRef(val bool) *bool { + return &val +} + +func GetIntRef(val int) *int { + return &val +} + +func GetPrivateKeyType(pk, pass string) string { + + p, _ := pem.Decode([]byte(pk)) + if p == nil { + return "" + } + + var keyType string + switch p.Type { + case "EC PRIVATE KEY": + keyType = "EC PRIVATE KEY" + case "RSA PRIVATE KEY": + keyType = "RSA PRIVATE KEY" + default: + keyType = "" + } + + return keyType +} diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/certificatePolicies.go b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/certificatePolicies.go index 2304098b..e18b9464 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/certificatePolicies.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/certificatePolicies.go @@ -33,24 +33,27 @@ type certificateTemplate struct { CertificateAuthority string `json:"certificateAuthority"` ProductName string `json:"productName"` } `json:"product"` - Priority int `json:"priority"` - SystemGenerated bool `json:"systemGenerated,omitempty"` - CreationDateString string `json:"creationDate,omitempty"` - CreationDate time.Time `json:"-"` - ModificationDateString string `json:"modificationDate"` - ModificationDate time.Time `json:"-"` - Status string `json:"status"` - Reason string `json:"reason"` - SubjectCNRegexes []string `json:"subjectCNRegexes,omitempty"` - SubjectORegexes []string `json:"subjectORegexes,omitempty"` - SubjectOURegexes []string `json:"subjectOURegexes,omitempty"` - SubjectSTRegexes []string `json:"subjectSTRegexes,omitempty"` - SubjectLRegexes []string `json:"subjectLRegexes,omitempty"` - SubjectCValues []string `json:"subjectCValues,omitempty"` - SANRegexes []string `json:"sanRegexes,omitempty"` - KeyTypes []allowedKeyType `json:"keyTypes,omitempty"` - KeyReuse bool `json:"keyReuse,omitempty"` - RecommendedSettings struct { + Priority int `json:"priority"` + SystemGenerated bool `json:"systemGenerated,omitempty"` + CreationDateString string `json:"creationDate,omitempty"` + CreationDate time.Time `json:"-"` + ModificationDateString string `json:"modificationDate"` + ModificationDate time.Time `json:"-"` + Status string `json:"status"` + Reason string `json:"reason"` + SubjectCNRegexes []string `json:"subjectCNRegexes,omitempty"` + SubjectORegexes []string `json:"subjectORegexes,omitempty"` + SubjectOURegexes []string `json:"subjectOURegexes,omitempty"` + SubjectSTRegexes []string `json:"subjectSTRegexes,omitempty"` + SubjectLRegexes []string `json:"subjectLRegexes,omitempty"` + SubjectCValues []string `json:"subjectCValues,omitempty"` + SANRegexes []string `json:"sanRegexes,omitempty"` + SanRfc822NameRegexes []string `json:"sanRfc822NameRegexes,omitempty"` + SanIpAddressRegexes []string `json:"sanIpAddressRegexes,omitempty"` + SanUniformResourceIdentifierRegexes []string `json:"sanUniformResourceIdentifierRegexes,omitempty"` + KeyTypes []allowedKeyType `json:"keyTypes,omitempty"` + KeyReuse bool `json:"keyReuse,omitempty"` + RecommendedSettings struct { SubjectOValue, SubjectOUValue, SubjectSTValue, SubjectLValue, SubjectCValue string @@ -60,7 +63,9 @@ type certificateTemplate struct { } keyReuse bool } - ValidityPeriod string `json:"validityPeriod,omitempty"` + ValidityPeriod string `json:"validityPeriod,omitempty"` + CsrUploadAllowed bool `json:"csrUploadAllowed"` + KeyGeneratedByVenafiAllowed bool `json:"keyGeneratedByVenafiAllowed"` } type CertificateTemplates struct { @@ -70,6 +75,7 @@ type CertificateTemplates struct { type allowedKeyType struct { KeyType keyType KeyLengths []int + KeyCurves []string `json:"keyCurves,omitempty"` } type keyType string diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/cloud.go b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/cloud.go index 355584f8..38520d9d 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/cloud.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/cloud.go @@ -23,11 +23,13 @@ import ( "encoding/json" "fmt" "github.com/Venafi/vcert/v4/pkg/policy" + "github.com/Venafi/vcert/v4/pkg/util" "io" "io/ioutil" "log" "net" "net/http" + "sort" "strconv" "strings" "time" @@ -57,6 +59,23 @@ type userDetails struct { APIKey *apiKey `json:"apiKey,omitempty"` } +type OwnerType int64 + +const ( + UserType OwnerType = iota + TeamType +) + +func (o OwnerType) String() string { + switch o { + case UserType: + return "USER" + case TeamType: + return "TEAM" + } + return "unknown" +} + type certificateRequestResponse struct { CertificateRequests []certificateRequestResponseData `json:"certificateRequests,omitempty"` } @@ -100,9 +119,20 @@ type CsrAttributes struct { State *string `json:"state,omitempty"` Country *string `json:"country,omitempty"` SubjectAlternativeNamesByType *SubjectAlternativeNamesByType `json:"subjectAlternativeNamesByType,omitempty"` + KeyTypeParameters *KeyTypeParameters `json:"keyTypeParameters,omitempty"` } + +type KeyTypeParameters struct { + KeyType string `json:"keyType,omitempty"` + KeyLength *int `json:"keyLength,omitempty"` + KeyCurve *string `json:"keyCurve,omitempty"` +} + type SubjectAlternativeNamesByType struct { - DnsNames []string `json:"dnsNames,omitempty"` + DnsNames []string `json:"dnsNames,omitempty"` + IpAddresses []string `json:"ipAddresses,omitempty"` + Rfc822Names []string `json:"rfc822Names,omitempty"` + UniformResourceIdentifiers []string `json:"uniformResourceIdentifiers,omitempty"` } type KeyStoreRequest struct { @@ -358,6 +388,81 @@ func parseUserDetailsData(b []byte) (*userDetails, error) { return &data, nil } +func parseUserByIdResult(expectedStatusCode int, httpStatusCode int, httpStatus string, body []byte) (*user, error) { + if httpStatusCode == expectedStatusCode { + return parseUserByIdData(body) + } + respErrors, err := parseResponseErrors(body) + if err != nil { + return nil, err // parseResponseErrors always return verror.ServerError + } + respError := fmt.Sprintf("unexpected status code on retrieval of user by ID. Status: %s\n", httpStatus) + for _, e := range respErrors { + respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) + } + return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) +} + +func parseUserByIdData(b []byte) (*user, error) { + var data user + err := json.Unmarshal(b, &data) + if err != nil { + return nil, fmt.Errorf("%w: %v", verror.ServerError, err) + } + + return &data, nil +} + +func parseUsersByNameResult(expectedStatusCode int, httpStatusCode int, httpStatus string, body []byte) (*users, error) { + if httpStatusCode == expectedStatusCode { + return parseUsersByNameData(body) + } + respErrors, err := parseResponseErrors(body) + if err != nil { + return nil, err // parseResponseErrors always return verror.ServerError + } + respError := fmt.Sprintf("unexpected status code on retrieval of users by name. Status: %s\n", httpStatus) + for _, e := range respErrors { + respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) + } + return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) +} + +func parseUsersByNameData(b []byte) (*users, error) { + var data users + err := json.Unmarshal(b, &data) + if err != nil { + return nil, fmt.Errorf("%w: %v", verror.ServerError, err) + } + + return &data, nil +} + +func parseTeamsResult(expectedStatusCode int, httpStatusCode int, httpStatus string, body []byte) (*teams, error) { + if httpStatusCode == expectedStatusCode { + return parseTeamsData(body) + } + respErrors, err := parseResponseErrors(body) + if err != nil { + return nil, err // parseResponseErrors always return verror.ServerError + } + respError := fmt.Sprintf("unexpected status code on retrieval of teams. Status: %s\n", httpStatus) + for _, e := range respErrors { + respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) + } + return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) +} + +func parseTeamsData(b []byte) (*teams, error) { + var data teams + err := json.Unmarshal(b, &data) + if err != nil { + return nil, fmt.Errorf("%w: %v", verror.ServerError, err) + } + + return &data, nil +} + func parseZoneConfigurationResult(httpStatusCode int, httpStatus string, body []byte) (*zone, error) { switch httpStatusCode { case http.StatusOK: @@ -537,8 +642,8 @@ func (z *cloudZone) parseZone() error { return nil } -func createAppUpdateRequest(applicationDetails *ApplicationDetails, cit *certificateTemplate) policy.ApplicationCreateRequest { - request := policy.ApplicationCreateRequest{ +func createAppUpdateRequest(applicationDetails *ApplicationDetails) policy.Application { + request := policy.Application{ OwnerIdsAndTypes: applicationDetails.OwnerIdType, Name: applicationDetails.Name, Description: applicationDetails.Description, @@ -554,21 +659,18 @@ func createAppUpdateRequest(applicationDetails *ApplicationDetails, cit *certifi OrganizationalUnitId: applicationDetails.OrganizationalUnitId, } - //add new cit values to the map. - citMap := request.CertificateIssuingTemplateAliasIdMap - value := citMap[cit.Name] - if value == "" { - citMap[cit.Name] = cit.ID - } else { - if value != cit.ID { - citMap[cit.Name] = cit.ID - } - } - request.CertificateIssuingTemplateAliasIdMap = citMap - return request } +func getSAN(p *policy.Policy) *policy.SubjectAltNames { + if p == nil || p.SubjectAltNames == nil { + san := policy.SubjectAltNames{} + p.SubjectAltNames = &san + return &san + } + return p.SubjectAltNames +} + func buildPolicySpecification(cit *certificateTemplate, info *policy.CertificateAuthorityInfo, removeRegex bool) *policy.PolicySpecification { if cit == nil { return nil @@ -590,10 +692,39 @@ func buildPolicySpecification(cit *certificateTemplate, info *policy.Certificate pol.WildcardAllowed = &wildCard if len(cit.SANRegexes) > 0 { - subjectAlt := policy.SubjectAltNames{} - trueVal := true - subjectAlt.DnsAllowed = &trueVal - pol.SubjectAltNames = &subjectAlt + subjectAlt := getSAN(&pol) + subjectAlt.DnsAllowed = util.GetBooleanRef(true) + } + + if len(cit.SanRfc822NameRegexes) > 0 { + subjectAlt := getSAN(&pol) + subjectAlt.EmailAllowed = util.GetBooleanRef(true) + } + + if len(cit.SanUniformResourceIdentifierRegexes) > 0 { + subjectAlt := getSAN(&pol) + protocols := make([]string, 0) + for _, val := range cit.SanUniformResourceIdentifierRegexes { + index := strings.Index(val, ")://") + subStr := val[1:index] + currProtocols := strings.Split(subStr, "|") + for _, currentProtocol := range currProtocols { + if len(protocols) == 0 { + protocols = append(protocols, currentProtocol) + } else { + if !contains(protocols, currentProtocol) { + protocols = append(protocols, currentProtocol) + } + } + } + } + subjectAlt.UriProtocols = protocols + subjectAlt.UriAllowed = util.GetBooleanRef(true) + } + + if len(cit.SanIpAddressRegexes) > 0 { + subjectAlt := getSAN(&pol) + subjectAlt.IpAllowed = util.GetBooleanRef(true) } // ps.Policy.WildcardAllowed is pending. @@ -612,36 +743,38 @@ func buildPolicySpecification(cit *certificateTemplate, info *policy.Certificate //subject. var subject policy.Subject - shouldCreateSubject := false if len(cit.SubjectORegexes) > 0 { subject.Orgs = cit.SubjectORegexes - shouldCreateSubject = true + } else if cit.SubjectORegexes == nil { + subject.Orgs = []string{""} } if len(cit.SubjectOURegexes) > 0 { subject.OrgUnits = cit.SubjectOURegexes - shouldCreateSubject = true + } else if cit.SubjectOURegexes == nil { + subject.OrgUnits = []string{""} } if len(cit.SubjectLRegexes) > 0 { subject.Localities = cit.SubjectLRegexes - shouldCreateSubject = true + } else if cit.SubjectLRegexes == nil { + subject.Localities = []string{""} } if len(cit.SubjectSTRegexes) > 0 { subject.States = cit.SubjectSTRegexes - shouldCreateSubject = true + } else if cit.SubjectSTRegexes == nil { + subject.States = []string{""} } if len(cit.SubjectCValues) > 0 { subject.Countries = cit.SubjectCValues - shouldCreateSubject = true + } else if cit.SubjectCValues == nil { + subject.Countries = []string{""} } - if shouldCreateSubject { - pol.Subject = &subject - } + pol.Subject = &subject //key pair var keyPair policy.KeyPair @@ -649,19 +782,44 @@ func buildPolicySpecification(cit *certificateTemplate, info *policy.Certificate if len(cit.KeyTypes) > 0 { var keyTypes []string var keySizes []int + var ellipticCurves []string for _, allowedKT := range cit.KeyTypes { keyType := string(allowedKT.KeyType) keyLengths := allowedKT.KeyLengths + ecKeys := allowedKT.KeyCurves keyTypes = append(keyTypes, keyType) - keySizes = append(keySizes, keyLengths...) + if len(keyLengths) > 0 { + keySizes = append(keySizes, keyLengths...) + } + + if len(ecKeys) > 0 { + ellipticCurves = append(ellipticCurves, ecKeys...) + } } shouldCreateKeyPair = true keyPair.KeyTypes = keyTypes - keyPair.RsaKeySizes = keySizes + if len(keySizes) > 0 { + keyPair.RsaKeySizes = keySizes + } + + if len(ellipticCurves) > 0 { + keyPair.EllipticCurves = ellipticCurves + } + } + + if cit.KeyGeneratedByVenafiAllowed && cit.CsrUploadAllowed { + keyPair.ServiceGenerated = nil + } else if cit.KeyGeneratedByVenafiAllowed { + keyPair.ServiceGenerated = &cit.KeyGeneratedByVenafiAllowed + shouldCreateKeyPair = true + } else if cit.CsrUploadAllowed { + falseVal := false + keyPair.ServiceGenerated = &falseVal + shouldCreateKeyPair = true } if shouldCreateKeyPair { @@ -730,6 +888,30 @@ func buildPolicySpecification(cit *certificateTemplate, info *policy.Certificate return &ps } +func contains(values []string, toSearch string) bool { + copiedValues := make([]string, len(values)) + copy(copiedValues, values) + sort.Strings(copiedValues) + + return binarySearch(copiedValues, toSearch) >= 0 +} + +func binarySearch(values []string, toSearch string) int { + len := len(values) - 1 + min := 0 + for min <= len { + mid := len - (len-min)/2 + if strings.Compare(toSearch, values[mid]) > 0 { + min = mid + 1 + } else if strings.Compare(toSearch, values[mid]) < 0 { + len = mid - 1 + } else { + return mid + } + } + return -1 +} + func parseCitResult(expectedStatusCode int, httpStatusCode int, httpStatus string, body []byte) (*certificateTemplate, error) { if httpStatusCode == expectedStatusCode { return parseCitDetailsData(body, httpStatusCode) diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/cloudUtil.go b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/cloudUtil.go index 3a6e6686..451aadb2 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/cloudUtil.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/cloudUtil.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "github.com/Venafi/vcert/v4/pkg/certificate" + "github.com/Venafi/vcert/v4/pkg/util" "net/http" "regexp" ) @@ -176,14 +177,64 @@ func getCsrAttributes(c *Connector, req *certificate.Request) (*CsrAttributes, e } if len(req.DNSNames) > 0 { - sanByType := SubjectAlternativeNamesByType{} + sanByType := getSANByType(&csrAttr) sanByType.DnsNames = req.DNSNames - csrAttr.SubjectAlternativeNamesByType = &sanByType } + if len(req.IPAddresses) > 0 { + sArray := make([]string, 0) + for _, val := range req.IPAddresses { + sArray = append(sArray, val.String()) + } + sanByType := getSANByType(&csrAttr) + sanByType.IpAddresses = sArray + } + + if len(req.EmailAddresses) > 0 { + sanByType := getSANByType(&csrAttr) + sanByType.Rfc822Names = req.EmailAddresses + } + + if len(req.URIs) > 0 { + sArray := make([]string, 0) + for _, val := range req.URIs { + sArray = append(sArray, val.String()) + } + sanByType := getSANByType(&csrAttr) + sanByType.UniformResourceIdentifiers = sArray + } + + keyTypeParam := &KeyTypeParameters{} + if req.KeyType == certificate.KeyTypeRSA { + keyTypeParam.KeyType = "RSA" + if req.KeyLength > 0 { + keyTypeParam.KeyLength = &req.KeyLength + } else { + keyTypeParam.KeyLength = util.GetIntRef(2048) + } + } else if req.KeyType == certificate.KeyTypeECDSA { + keyTypeParam.KeyType = "EC" + if req.KeyCurve.String() != "" { + keyCurve := req.KeyCurve.String() + keyTypeParam.KeyCurve = &keyCurve + } else { + defaultCurve := certificate.EllipticCurveDefault + defaultCurveStr := defaultCurve.String() + keyTypeParam.KeyCurve = &defaultCurveStr + } + } + csrAttr.KeyTypeParameters = keyTypeParam + return &csrAttr, nil } +func getSANByType(csrAttributes *CsrAttributes) *SubjectAlternativeNamesByType { + if csrAttributes.SubjectAlternativeNamesByType == nil { + csrAttributes.SubjectAlternativeNamesByType = &SubjectAlternativeNamesByType{} + } + return csrAttributes.SubjectAlternativeNamesByType +} + //receives a string regex and a string to test func testRegex(toTest, regex string) (bool, error) { compiledRegex, err := regexp.Compile(regex) diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/connector.go b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/connector.go index 07a95df6..4e2307d2 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/connector.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/connector.go @@ -61,9 +61,13 @@ const ( urlIssuingTemplate urlResource = apiVersion + "certificateissuingtemplates" urlAppRoot urlResource = basePath + "applications" urlCAAccounts urlResource = apiVersion + "certificateauthorities/%s/accounts" - urlCAAccountDetails urlResource = urlCAAccounts + "/%s" + urlCAAccountDetails = urlCAAccounts + "/%s" urlResourceCertificateKS = urlResourceCertificates + "/%s/keystore" urlDekPublicKey urlResource = apiVersion + "edgeencryptionkeys/%s" + urlUsers urlResource = apiVersion + "users" + urlUserById = urlUsers + "/%s" + urlUsersByName = urlUsers + "/username/%s" + urlTeams urlResource = apiVersion + "teams" defaultAppName = "Default" ) @@ -86,8 +90,12 @@ type Connector struct { client *http.Client } +func (c *Connector) RetrieveCertificateMetaData(dn string) (*certificate.CertificateMetaData, error) { + panic("operation is not supported yet") +} + func (c *Connector) SearchCertificates(req *certificate.SearchRequest) (*certificate.CertSearchResponse, error) { - panic("implement me") + panic("operation is not supported yet") } func (c *Connector) IsCSRServiceGenerated(req *certificate.Request) (bool, error) { @@ -185,6 +193,10 @@ func (c *Connector) RequestSSHCertificate(req *certificate.SshCertRequest) (resp panic("operation is not supported yet") } +func (c *Connector) RetrieveAvailableSSHTemplates() (response []certificate.SshAvaliableTemplate, err error) { + panic("operation is not supported yet") +} + func (c *Connector) GetPolicyWithRegex(name string) (*policy.PolicySpecification, error) { cit, err := retrievePolicySpecification(c, name) @@ -246,9 +258,52 @@ func (c *Connector) GetPolicy(name string) (*policy.PolicySpecification, error) log.Println("Building policy") ps := buildPolicySpecification(cit, info, true) + // getting the users to set to the PolicySpecification + users, error := c.getUsers() + if error != nil { + return nil, error + } + ps.Users = users + return ps, nil } +func (c *Connector) getUsers() ([]string, error) { + var usersList []string + appDetails, _, error := c.getAppDetailsByName(c.zone.getApplicationName()) + if error != nil { + return nil, error + } + var teams *teams + for _, owner := range appDetails.OwnerIdType { + if owner.OwnerType == UserType.String() { + user, error := c.retrieveUser(owner.OwnerId) + if error != nil { + return nil, error + } + usersList = append(usersList, user.Username) + } else { + if owner.OwnerType == TeamType.String() { + if teams == nil { + teams, error = c.retrieveTeams() + if error != nil { + return nil, error + } + } + if teams != nil { + for _, team := range teams.Teams { + if team.ID == owner.OwnerId { + usersList = append(usersList, team.Name) + break + } + } + } + } + } + } + return usersList, nil +} + func PolicyExist(policyName string, c *Connector) (bool, error) { c.zone.appName = policy.GetApplicationName(policyName) @@ -371,66 +426,179 @@ func (c *Connector) SetPolicy(name string, ps *policy.PolicySpecification) (stri return "", fmt.Errorf("application name is empty, please provide zone in the format: app_name\\cit_name") } - userDetails, err := getUserDetails(c) - if err != nil { - return "", err - } - appDetails, statusCode, err := c.getAppDetailsByName(appName) if err != nil && statusCode == 404 { //means application was not found. log.Printf("creating application: %s", appName) - ownerId := policy.OwnerIdType{ - OwnerId: userDetails.User.ID, - OwnerType: "USER", - } - appIssuingTemplate := make(map[string]string) - appIssuingTemplate[cit.Name] = cit.ID + _, error := c.createApplication(appName, ps, cit) + if error != nil { + return "", error + } - //create application - //fmt.Println(details.ApplicationId) - appReq := policy.ApplicationCreateRequest{ - OwnerIdsAndTypes: []policy.OwnerIdType{ownerId}, - Name: appName, - CertificateIssuingTemplateAliasIdMap: appIssuingTemplate, + } else { //determine if the application needs to be updated + log.Printf("updating application: %s", appName) + error := c.updateApplication(name, ps, cit, appDetails) + if error != nil { + return "", error } + } - url := c.getURL(urlAppRoot) + log.Printf("policy successfully applied to %s", name) - _, status, _, err = c.request("POST", url, appReq) - if err != nil { - return "", err + return status, nil +} + +func (c *Connector) createApplication(appName string, ps *policy.PolicySpecification, cit *certificateTemplate) (*policy.Application, error) { + appIssuingTemplate := make(map[string]string) + appIssuingTemplate[cit.Name] = cit.ID + + var owners []policy.OwnerIdType + var error error + + //if users were passed to the PS, then it will needed to resolve the related Owners to set them + if len(ps.Users) > 0 { + owners, error = c.resolveOwners(ps.Users) + } else { //if the users were not specified in PS, then the current User should be used as owner + var owner *policy.OwnerIdType + owner, error = c.getOwnerFromUserDetails() + if owner != nil { + owners = []policy.OwnerIdType{*owner} } + } - } else { - //update the application and assign the cit tho the application - exist, err := PolicyExist(name, c) + if error != nil { + return nil, fmt.Errorf("an error happened trying to resolve the owners: %w", error) + } + + //create application + appReq := policy.Application{ + OwnerIdsAndTypes: owners, + Name: appName, + CertificateIssuingTemplateAliasIdMap: appIssuingTemplate, + } + + url := c.getURL(urlAppRoot) + + _, _, _, error = c.request("POST", url, appReq) + if error != nil { + return nil, error + } + + return &appReq, nil +} + +func (c *Connector) updateApplication(name string, ps *policy.PolicySpecification, cit *certificateTemplate, appDetails *ApplicationDetails) error { + + //creating the app to use as request + appReq := createAppUpdateRequest(appDetails) + + //determining if the relationship between application and cit exist + citAddedToApp := false + exist, err := PolicyExist(name, c) + if err != nil { + return err + } + if !exist { + c.addCitToApp(&appReq, cit) + citAddedToApp = true + } + //determining if the owners where provided and should be updated + ownersUpdated := false + //given that the application exists, the only way to update the owners at the application + //is that users in the policy specification were provided + if len(ps.Users) > 0 { + //resolving and setting owners + owners, error := c.resolveOwners(ps.Users) + if error != nil { + return fmt.Errorf("an error happened trying to resolve the owners: %w", error) + } + appReq.OwnerIdsAndTypes = owners + ownersUpdated = true + } + + //if the cit was added to the app or the owners were updated, then is required + //to update the application + if citAddedToApp || ownersUpdated { + url := c.getURL(urlAppRoot) + url = fmt.Sprint(url, "/", appDetails.ApplicationId) + _, _, _, err = c.request("PUT", url, appReq) if err != nil { - return "", err + return err } + } + + return nil +} - if !exist { // relation between app-cit doesn't exist so create it. - log.Printf("updating application: %s", appName) +func (c *Connector) addCitToApp(app *policy.Application, cit *certificateTemplate) { + //add cit to the map. + value, ok := app.CertificateIssuingTemplateAliasIdMap[cit.Name] + if !ok || value != cit.ID { + app.CertificateIssuingTemplateAliasIdMap[cit.Name] = cit.ID + } +} - appReq := createAppUpdateRequest(appDetails, cit) +func (c *Connector) resolveOwners(usersList []string) ([]policy.OwnerIdType, error) { - url := c.getURL(urlAppRoot) + var owners []policy.OwnerIdType + var teams *teams + var err error - url = fmt.Sprint(url, "/", appDetails.ApplicationId) + for _, userName := range usersList { + //The error should be ignored in order to confirm if the userName is not a TeamName + users, _ := c.retrieveUsers(userName) - _, status, _, err = c.request("PUT", url, appReq) + if users != nil { + owners = appendOwner(owners, users.Users[0].ID, UserType) + } else { + if teams == nil { + teams, err = c.retrieveTeams() + } if err != nil { - return "", err + return nil, err + } + if teams != nil { + var found = false + for _, team := range teams.Teams { + if team.Name == userName { + owners = appendOwner(owners, team.ID, TeamType) + found = true + break + } + } + if !found { + return nil, fmt.Errorf("it was not possible to find the user %s", userName) + } } - } } - log.Printf("policy successfully applied to %s", name) + return owners, err +} - return status, nil +func appendOwner(owners []policy.OwnerIdType, ownerId string, ownerType OwnerType) []policy.OwnerIdType { + owner := createOwner(ownerId, ownerType) + return append(owners, *owner) +} + +func (c *Connector) getOwnerFromUserDetails() (*policy.OwnerIdType, error) { + userDetails, err := getUserDetails(c) + if err != nil { + return nil, err + } + owner := createOwner(userDetails.User.ID, UserType) + return owner, nil +} + +func createOwner(ownerId string, ownerType OwnerType) *policy.OwnerIdType { + ownerIdType := policy.OwnerIdType{ + OwnerId: ownerId, + OwnerType: ownerType.String(), + } + + return &ownerIdType } // NewConnector creates a new Venafi Cloud Connector object used to communicate with Venafi Cloud @@ -1372,6 +1540,53 @@ func getUserDetails(c *Connector) (*userDetails, error) { return ud, nil } +func (c *Connector) retrieveUser(id string) (*user, error) { + + url := c.getURL(urlUserById) + url = fmt.Sprintf(url, id) + + statusCode, status, body, err := c.request("GET", url, nil) + if err != nil { + return nil, err + } + user, err := parseUserByIdResult(http.StatusOK, statusCode, status, body) + if err != nil { + return nil, err + } + return user, nil +} + +func (c *Connector) retrieveUsers(userName string) (*users, error) { + + url := c.getURL(urlUsersByName) + url = fmt.Sprintf(url, userName) + + statusCode, status, body, err := c.request("GET", url, nil) + if err != nil { + return nil, err + } + users, err := parseUsersByNameResult(http.StatusOK, statusCode, status, body) + if err != nil { + return nil, err + } + return users, nil +} + +func (c *Connector) retrieveTeams() (*teams, error) { + + url := c.getURL(urlTeams) + + statusCode, status, body, err := c.request("GET", url, nil) + if err != nil { + return nil, err + } + teams, err := parseTeamsResult(http.StatusOK, statusCode, status, body) + if err != nil { + return nil, err + } + return teams, nil +} + func getAccounts(caName string, c *Connector) (*policy.Accounts, *policy.CertificateAuthorityInfo, error) { info, err := policy.GetCertAuthorityInfo(caName) if err != nil { diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/team.go b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/team.go new file mode 100644 index 00000000..e166f4f1 --- /dev/null +++ b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/team.go @@ -0,0 +1,28 @@ +/* + * Copyright 2022 Venafi, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cloud + +type team struct { + ID string `json:"id"` + Name string `json:"name"` + Role string `json:"role"` + CompanyID string `json:"companyId"` +} + +type teams struct { + Teams []team `json:"teams"` +} diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/user.go b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/user.go index 50bff338..9f0f4cba 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/user.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/cloud/user.go @@ -33,6 +33,10 @@ type user struct { CreationDate time.Time `json:"-"` } +type users struct { + Users []user `json:"users"` +} + type userAccount struct { Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/fake/connector.go b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/fake/connector.go index e968786f..76609189 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/fake/connector.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/fake/connector.go @@ -39,8 +39,12 @@ type Connector struct { verbose bool } +func (c *Connector) RetrieveCertificateMetaData(dn string) (*certificate.CertificateMetaData, error) { + panic("operation is not supported yet") +} + func (c *Connector) SearchCertificates(req *certificate.SearchRequest) (*certificate.CertSearchResponse, error) { - panic("implement me") + panic("operation is not supported yet") } func (c *Connector) IsCSRServiceGenerated(req *certificate.Request) (bool, error) { @@ -59,6 +63,10 @@ func (c *Connector) RequestSSHCertificate(req *certificate.SshCertRequest) (resp panic("operation is not supported yet") } +func (c *Connector) RetrieveAvailableSSHTemplates() (response []certificate.SshAvaliableTemplate, err error) { + panic("operation is not supported yet") +} + func (c *Connector) GetPolicy(name string) (*policy.PolicySpecification, error) { caName := "\\VED\\Policy\\Certificate Authorities\\TEST CA\\QA Test CA - Server 90 Days" diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/tpp/connector.go b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/tpp/connector.go index dd58d514..7e35fb98 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/tpp/connector.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/tpp/connector.go @@ -57,6 +57,10 @@ func (c *Connector) RetrieveSshConfig(ca *certificate.SshCaTemplateRequest) (*ce return RetrieveSshConfig(c, ca) } +func (c *Connector) RetrieveAvailableSSHTemplates() (response []certificate.SshAvaliableTemplate, err error) { + return GetAvailableSshTemplates(c) +} + // NewConnector creates a new TPP Connector object used to communicate with TPP func NewConnector(url string, zone string, verbose bool, trust *x509.CertPool) (*Connector, error) { c := Connector{verbose: verbose, trust: trust, zone: zone} @@ -98,8 +102,11 @@ func (c *Connector) GetType() endpoint.ConnectorType { return endpoint.ConnectorTypeTPP } -//Ping attempts to connect to the TPP Server WebSDK API and returns an errror if it cannot +//Ping attempts to connect to the TPP Server WebSDK API and returns an error if it cannot func (c *Connector) Ping() (err error) { + + //Extended timeout to allow the server to wake up + c.getHTTPClient().Timeout = time.Second * 90 statusCode, status, _, err := c.request("GET", "vedsdk/", nil) if err != nil { return @@ -156,7 +163,7 @@ func (c *Connector) Authenticate(auth *endpoint.Authentication) (err error) { return fmt.Errorf("failed to authenticate: can't determine valid credentials set") } -// Get OAuth refresh and access token +// GetRefreshToken Get OAuth refresh and access token func (c *Connector) GetRefreshToken(auth *endpoint.Authentication) (resp OauthGetRefreshTokenResponse, err error) { if auth == nil { @@ -193,7 +200,7 @@ func (c *Connector) GetRefreshToken(auth *endpoint.Authentication) (resp OauthGe return resp, fmt.Errorf("failed to authenticate: missing credentials") } -// Refresh OAuth access token +// RefreshAccessToken Refresh OAuth access token func (c *Connector) RefreshAccessToken(auth *endpoint.Authentication) (resp OauthRefreshAccessTokenResponse, err error) { if auth == nil { @@ -270,6 +277,14 @@ func (c *Connector) RevokeAccessToken(auth *endpoint.Authentication) (err error) func processAuthData(c *Connector, url urlResource, data interface{}) (resp interface{}, err error) { + //isReachable, err := c.isAuthServerReachable() + //if err != nil { + // return nil, err + //} + //if !isReachable { + // return nil, fmt.Errorf("authentication server is not reachable: %s", c.baseURL) + //} + statusCode, status, body, err := c.request("POST", url, data) if err != nil { return resp, err @@ -315,6 +330,22 @@ func processAuthData(c *Connector, url urlResource, data interface{}) (resp inte return resp, nil } +func (c *Connector) isAuthServerReachable() (bool, error) { + url := urlResource(urlResourceAuthorizeIsAuthServer) + + // Extended timeout to allow the server to wake up + c.getHTTPClient().Timeout = time.Second * 90 + statusCode, statusText, _, err := c.request("GET", url, nil) + if err != nil { + return false, fmt.Errorf("error while cheking the authentication server. URL: %s; Error: %v", url, err) + } + + if statusCode == http.StatusAccepted && strings.Contains(statusText, "Venafi Authentication Server") { + return true, nil + } + return false, fmt.Errorf("invalid authentication server. URL: %s; Status Code: %d; Status Text: %s", url, statusCode, statusText) +} + func wrapAltNames(req *certificate.Request) (items []sanItem) { for _, name := range req.EmailAddresses { items = append(items, sanItem{1, name}) @@ -354,7 +385,7 @@ func prepareLegacyMetadata(c *Connector, metaItems []customField, dn string) ([] return requestGUIDData, nil } -//RequestAllMetadataItems returns all possible metadata items for a DN +// requestAllMetadataItems returns all possible metadata items for a DN func (c *Connector) requestAllMetadataItems(dn string) ([]metadataItem, error) { statusCode, status, body, err := c.request("POST", urlResourceAllMetadataGet, metadataGetItemsRequest{dn}) if err != nil { @@ -369,7 +400,7 @@ func (c *Connector) requestAllMetadataItems(dn string) ([]metadataItem, error) { return response.Items, err } -//RequestMetadataItems returns metadata items for a DN that have a value stored +// requestMetadataItems returns metadata items for a DN that have a value stored func (c *Connector) requestMetadataItems(dn string) ([]metadataKeyValueSet, error) { statusCode, status, body, err := c.request("POST", urlResourceMetadataGet, metadataGetItemsRequest{dn}) if err != nil { @@ -383,7 +414,7 @@ func (c *Connector) requestMetadataItems(dn string) ([]metadataKeyValueSet, erro return response.Data, err } -//RequestSystemVersion returns the TPP system version of the connector context +// requestSystemVersion returns the TPP system version of the connector context func (c *Connector) requestSystemVersion() (string, error) { statusCode, status, body, err := c.request("GET", urlResourceSystemStatusVersion, "") if err != nil { @@ -403,7 +434,7 @@ func (c *Connector) requestSystemVersion() (string, error) { return response.Version, err } -//SetCertificateMetadata submits the metadata to TPP for storage returning the lock status of the metadata stored +// setCertificateMetadata submits the metadata to TPP for storage returning the lock status of the metadata stored func (c *Connector) setCertificateMetadata(metadataRequest metadataSetRequest) (bool, error) { if metadataRequest.DN == "" { return false, fmt.Errorf("DN must be provided to setCertificateMetaData") @@ -695,7 +726,7 @@ func (c *Connector) GetPolicy(name string) (*policy.PolicySpecification, error) req := policy.CheckPolicyRequest{ PolicyDN: name, } - _, _, body, err := c.request("POST", urlResourceReadPolicy, req) + _, _, body, err := c.request("POST", urlResourceCheckPolicy, req) if err != nil { return nil, err @@ -716,9 +747,56 @@ func (c *Connector) GetPolicy(name string) (*policy.PolicySpecification, error) return nil, err } + userNames, error := c.retrieveUserNamesForPolicySpecification(name) + if error != nil { + return nil, error + } + ps.Users = userNames + return ps, nil } +func (c *Connector) retrieveUserNamesForPolicySpecification(policyName string) ([]string, error) { + values, _, error := getPolicyAttribute(c, policy.TppContact, policyName) + if error != nil { + return nil, error + } + if values != nil { + var users []string + for _, prefixedUniversal := range values { + validateIdentityRequest := policy.ValidateIdentityRequest{ + ID: policy.IdentityInformation{ + PrefixedUniversal: prefixedUniversal, + }, + } + + validateIdentityResponse, error := c.validateIdentity(validateIdentityRequest) + if error != nil { + return nil, error + } + + users = append(users, validateIdentityResponse.ID.Name) + } + + return users, nil + } + + return nil, nil +} + +func (c *Connector) validateIdentity(validateIdentityRequest policy.ValidateIdentityRequest) (*policy.ValidateIdentityResponse, error) { + + statusCode, status, body, err := c.request("POST", urlResourceValidateIdentity, validateIdentityRequest) + if err != nil { + return nil, err + } + validateIdentityResponse, err := parseValidateIdentityResponse(statusCode, status, body) + if err != nil { + return nil, err + } + return &validateIdentityResponse, nil +} + func PolicyExist(policyName string, c *Connector) (bool, error) { req := policy.PolicyExistPayloadRequest{ @@ -757,7 +835,7 @@ func (c *Connector) SetPolicy(name string, ps *policy.PolicySpecification) (stri } log.Printf("policy specification is valid") - var status = "" + var status string tppPolicy := policy.BuildTppPolicy(ps) if !strings.HasPrefix(name, util.PathSeparator) { name = util.PathSeparator + name @@ -770,16 +848,13 @@ func (c *Connector) SetPolicy(name string, ps *policy.PolicySpecification) (stri tppPolicy.Name = &name - var policyExists = false - //validate if the policy exists - policyExist, err := PolicyExist(name, c) + policyExists, err := PolicyExist(name, c) if err != nil { return "", err } - if policyExist { - policyExists = true + if policyExists { log.Printf("found existing policy folder: %s", name) } else { @@ -808,7 +883,7 @@ func (c *Connector) SetPolicy(name string, ps *policy.PolicySpecification) (stri ObjectDN: *(tppPolicy.Name), } - _, status, _, err = c.request("POST", urlResourceCreatePolicy, req) + _, _, _, err = c.request("POST", urlResourceCreatePolicy, req) if err != nil { return "", err @@ -818,17 +893,9 @@ func (c *Connector) SetPolicy(name string, ps *policy.PolicySpecification) (stri log.Printf("updating certificate policy attributes") - //create Contact - if tppPolicy.Contact != nil { - _, status, _, err = createPolicyAttribute(c, policy.TppContact, tppPolicy.Contact, *(tppPolicy.Name), true) - if err != nil { - return "", err - } - } - //create Approver if tppPolicy.Approver != nil { - _, status, _, err = createPolicyAttribute(c, policy.TppApprover, tppPolicy.Approver, *(tppPolicy.Name), true) + _, _, _, err = createPolicyAttribute(c, policy.TppApprover, tppPolicy.Approver, *(tppPolicy.Name), true) if err != nil { return "", err } @@ -840,6 +907,12 @@ func (c *Connector) SetPolicy(name string, ps *policy.PolicySpecification) (stri } } + //set Contacts + status, err = c.setContact(&tppPolicy) + if err != nil { + return "", err + } + //create Domain Suffix Whitelist if tppPolicy.ManagementType != nil { _, status, _, err = createPolicyAttribute(c, policy.TppManagementType, []string{tppPolicy.ManagementType.Value}, *(tppPolicy.Name), tppPolicy.ManagementType.Locked) @@ -970,6 +1043,79 @@ func (c *Connector) SetPolicy(name string, ps *policy.PolicySpecification) (stri return status, nil } +func (c *Connector) setContact(tppPolicy *policy.TppPolicy) (status string, err error) { + + if tppPolicy.Contact != nil { + contacts, err := c.resolveContacts(tppPolicy.Contact) + if err != nil { + return "", fmt.Errorf("an error happened trying to resolve the contacts: %w", err) + } + if contacts != nil { + tppPolicy.Contact = contacts + + _, status, _, err = createPolicyAttribute(c, policy.TppContact, tppPolicy.Contact, *(tppPolicy.Name), true) + if err != nil { + return "", err + } + } + } + + return status, nil +} + +func (c *Connector) resolveContacts(contacts []string) ([]string, error) { + var identities []string + for _, contact := range contacts { + identity, err := c.getIdentity(contact) + if err != nil { + return nil, err + } + identities = append(identities, identity.PrefixedUniversal) + } + + return identities, nil +} + +func (c *Connector) getIdentity(userName string) (*policy.IdentityEntry, error) { + if userName == "" { + return nil, fmt.Errorf("identity string cannot be null") + } + + req := policy.BrowseIdentitiesRequest{ + Filter: userName, + Limit: 2, + IdentityType: policy.AllIdentities, + } + + resp, err := c.browseIdentities(req) + if err != nil { + return nil, err + } + + if len(resp.Identities) > 1 { + return nil, fmt.Errorf("only one Identity must be returned but it was returned more than one") + } else { + if len(resp.Identities) != 1 { + return nil, fmt.Errorf("it was not possible to find the user %s", userName) + } + } + + return &resp.Identities[0], nil +} + +func (c *Connector) browseIdentities(browseReq policy.BrowseIdentitiesRequest) (*policy.BrowseIdentitiesResponse, error) { + + statusCode, status, body, err := c.request("POST", urlResourceBrowseIdentities, browseReq) + if err != nil { + return nil, err + } + browseIdentitiesResponse, err := parseBrowseIdentitiesResult(statusCode, status, body) + if err != nil { + return nil, err + } + return &browseIdentitiesResponse, nil +} + // RetrieveCertificate attempts to retrieve the requested certificate func (c *Connector) RetrieveCertificate(req *certificate.Request) (certificates *certificate.PEMCollection, err error) { @@ -1553,8 +1699,14 @@ func getPolicyAttribute(c *Connector, at string, n string) (s []string, b *bool, func resetTPPAttributes(zone string, c *Connector) error { + //reset Contact + err := resetTPPAttribute(c, policy.TppContact, zone) + if err != nil { + return err + } + //reset Domain Suffix Whitelist - err := resetTPPAttribute(c, policy.TppDomainSuffixWhitelist, zone) + err = resetTPPAttribute(c, policy.TppDomainSuffixWhitelist, zone) if err != nil { return err } @@ -1689,3 +1841,73 @@ func (c *Connector) RequestSSHCertificate(req *certificate.SshCertRequest) (resp func (c *Connector) RetrieveSSHCertificate(req *certificate.SshCertRequest) (response *certificate.SshCertificateObject, err error) { return RetrieveSshCertificate(c, req) } + +func (c *Connector) RetrieveCertificateMetaData(dn string) (*certificate.CertificateMetaData, error) { + + //first step convert dn to guid + request := DNToGUIDRequest{ObjectDN: dn} + statusCode, status, body, err := c.request("POST", urlResourceDNToGUID, request) + + if err != nil { + return nil, err + } + + guidInfo, err := parseDNToGUIDRequestResponse(statusCode, status, body) + + if err != nil { + return nil, err + } + + //second step get certificate metadata + url := fmt.Sprintf("%s%s", urlResourceCertificate, guidInfo.GUID) + + statusCode, status, body, err = c.request("GET", urlResource(url), nil) + + if err != nil { + return nil, err + } + + data, err := parseCertificateMetaData(statusCode, status, body) + if err != nil { + return nil, err + } + + return data, nil + +} + +func parseDNToGUIDRequestResponse(httpStatusCode int, httpStatus string, body []byte) (*DNToGUIDResponse, error) { + switch httpStatusCode { + case http.StatusOK, http.StatusCreated: + reqData, err := parseDNToGUIDResponseData(body) + if err != nil { + return nil, err + } + return reqData, nil + default: + return nil, fmt.Errorf("Unexpected status code on TPP DN to GUID request.\n Status:\n %s. \n Body:\n %s\n", httpStatus, body) + } +} + +func parseDNToGUIDResponseData(b []byte) (data *DNToGUIDResponse, err error) { + err = json.Unmarshal(b, &data) + return +} + +func parseCertificateMetaData(httpStatusCode int, httpStatus string, body []byte) (*certificate.CertificateMetaData, error) { + switch httpStatusCode { + case http.StatusOK, http.StatusCreated: + reqData, err := parseCertificateMetaDataResponse(body) + if err != nil { + return nil, err + } + return reqData, nil + default: + return nil, fmt.Errorf("Unexpected status code on TPP DN to GUID request.\n Status:\n %s. \n Body:\n %s\n", httpStatus, body) + } +} + +func parseCertificateMetaDataResponse(b []byte) (data *certificate.CertificateMetaData, err error) { + err = json.Unmarshal(b, &data) + return +} diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/tpp/sshCertUtils.go b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/tpp/sshCertUtils.go index d41ee251..f45f76de 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/tpp/sshCertUtils.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/tpp/sshCertUtils.go @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package tpp import ( @@ -303,6 +304,28 @@ func RetrieveSshConfig(c *Connector, ca *certificate.SshCaTemplateRequest) (*cer return &conf, nil } +func GetAvailableSshTemplates(c *Connector) ([]certificate.SshAvaliableTemplate, error) { + var response []certificate.SshAvaliableTemplate + statusCode, status, body, err := c.request("GET", urlResourceSshTemplateAvaliable, nil) + if err != nil { + return nil, err + } + + switch statusCode { + case http.StatusOK: + response, err = parseSshTemplateData(body) + if err != nil { + return nil, err + } + case http.StatusNotFound: + // Return NotFound as this API method is unavailable in SSH Protect versions prior 21.4.0 + return nil, fmt.Errorf(status) + default: + return nil, fmt.Errorf("error while retriving avaliable SSH templates, error body:%s, status:%s and status code:%v", string(body), status, statusCode) + } + return response, nil +} + func RetrieveSshCaPrincipals(c *Connector, ca *certificate.SshCaTemplateRequest) ([]string, error) { tppReq := certificate.SshTppCaTemplateRequest{} @@ -360,6 +383,11 @@ func parseSshCaDetailsRequestData(b []byte) (data *certificate.SshTppCaTemplateR return } +func parseSshTemplateData(b []byte) (data []certificate.SshAvaliableTemplate, err error) { + err = json.Unmarshal(b, &data) + return +} + func getSshCaDN(ca string) string { fullPath := ca diff --git a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/tpp/tpp.go b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/tpp/tpp.go index 4d473767..7774cfa1 100644 --- a/vendor/github.com/Venafi/vcert/v4/pkg/venafi/tpp/tpp.go +++ b/vendor/github.com/Venafi/vcert/v4/pkg/venafi/tpp/tpp.go @@ -23,6 +23,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "github.com/Venafi/vcert/v4/pkg/policy" "io" "io/ioutil" "log" @@ -198,6 +199,7 @@ type OauthGetRefreshTokenResponse struct { Expires int `json:"expires,omitempty"` Identity string `json:"identity,omitempty"` Refresh_token string `json:"refresh_token,omitempty"` + Refresh_until int `json:"refresh_until,omitempty"` Scope string `json:"scope,omitempty"` Token_type string `json:"token_type,omitempty"` } @@ -217,6 +219,7 @@ type OauthRefreshAccessTokenResponse struct { Expires int `json:"expires,omitempty"` Identity string `json:"identity,omitempty"` Refresh_token string `json:"refresh_token,omitempty"` + Refresh_until int `json:"refresh_until,omitempty"` Token_type string `json:"token_type,omitempty"` } @@ -282,12 +285,25 @@ type metadataSetResponse struct { Locked bool `json:",omitempty"` Result int `json:",omitempty"` } + +type DNToGUIDResponse struct { + ClassName string `json:"ClassName"` + GUID string `json:"GUID"` + HierarchicalGUID string `json:"HierarchicalGUID"` + Result int `json:"Result"` + Revision int `json:"Revision"` +} + +type DNToGUIDRequest struct { + ObjectDN string `json:"ObjectDN"` +} type systemStatusVersionResponse string type urlResource string const ( urlResourceAuthorize urlResource = "vedsdk/authorize/" + urlResourceAuthorizeIsAuthServer urlResource = "vedauth/authorize/isAuthServer" urlResourceAuthorizeCertificate urlResource = "vedauth/authorize/certificate" urlResourceAuthorizeOAuth urlResource = "vedauth/authorize/oauth" urlResourceAuthorizeVerify urlResource = "vedauth/authorize/verify" @@ -313,13 +329,18 @@ const ( urlResourceSystemStatusVersion urlResource = "vedsdk/systemstatus/version" urlResourceCreatePolicy urlResource = "vedsdk/Config/Create" urlResourceWritePolicy urlResource = "vedsdk/Config/WritePolicy" + urlResourceReadPolicy urlResource = "vedsdk/Config/ReadPolicy" urlResourceIsValidPolicy urlResource = "vedsdk/Config/isvalid" - urlResourceReadPolicy urlResource = "vedsdk/certificates/checkpolicy" + urlResourceCheckPolicy urlResource = "vedsdk/certificates/checkpolicy" urlResourceCleanPolicy urlResource = "vedsdk/config/clearpolicyattribute" + urlResourceBrowseIdentities urlResource = "vedsdk/Identity/Browse" + urlResourceValidateIdentity urlResource = "vedsdk/Identity/Validate" urlResourceSshCertReq urlResource = "vedsdk/SSHCertificates/request" urlResourceSshCertRet urlResource = "vedsdk/SSHCertificates/retrieve" urlResourceSshCAPubKey urlResource = "vedsdk/SSHCertificates/Template/Retrieve/PublicKeyData" urlResourceSshCADetails urlResource = "vedsdk/SSHCertificates/Template/Retrieve" + urlResourceSshTemplateAvaliable urlResource = "vedsdk/SSHCertificates/Template/Available" + urlResourceDNToGUID urlResource = "vedsdk/Config/DnToGuid" ) const ( @@ -626,6 +647,44 @@ func newPEMCollectionFromResponse(base64Response string, chainOrder certificate. return nil, nil } +func parseBrowseIdentitiesResult(httpStatusCode int, httpStatus string, body []byte) (policy.BrowseIdentitiesResponse, error) { + var browseIdentitiesResponse policy.BrowseIdentitiesResponse + switch httpStatusCode { + case http.StatusOK, http.StatusAccepted: + browseIdentitiesResponse, err := parseBrowseIdentitiesData(body) + if err != nil { + return browseIdentitiesResponse, err + } + return browseIdentitiesResponse, nil + default: + return browseIdentitiesResponse, fmt.Errorf("Unexpected status code on TPP Browse Identities. Status: %s", httpStatus) + } +} + +func parseBrowseIdentitiesData(b []byte) (data policy.BrowseIdentitiesResponse, err error) { + err = json.Unmarshal(b, &data) + return +} + +func parseValidateIdentityResponse(httpStatusCode int, httpStatus string, body []byte) (policy.ValidateIdentityResponse, error) { + var validateIdentityResponse policy.ValidateIdentityResponse + switch httpStatusCode { + case http.StatusOK, http.StatusAccepted: + validateIdentityResponse, err := parseValidateIdentityData(body) + if err != nil { + return validateIdentityResponse, err + } + return validateIdentityResponse, nil + default: + return validateIdentityResponse, fmt.Errorf("Unexpected status code on TPP Validate Identity. Status: %s", httpStatus) + } +} + +func parseValidateIdentityData(b []byte) (data policy.ValidateIdentityResponse, err error) { + err = json.Unmarshal(b, &data) + return +} + type _strValue struct { Locked bool Value string diff --git a/vendor/modules.txt b/vendor/modules.txt index db734668..5fd85c96 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,6 +1,6 @@ # github.com/Venafi/vcert v3.18.4+incompatible github.com/Venafi/vcert/test -# github.com/Venafi/vcert/v4 v4.17.0 +# github.com/Venafi/vcert/v4 v4.19.0 github.com/Venafi/vcert/v4 github.com/Venafi/vcert/v4/pkg/certificate github.com/Venafi/vcert/v4/pkg/endpoint