2023-03-15 16:00:52 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2017-04-26 06:46:01 +00:00
|
|
|
package pki
|
|
|
|
|
|
|
|
import (
|
2018-01-19 06:44:44 +00:00
|
|
|
"context"
|
2017-04-27 21:09:59 +00:00
|
|
|
"fmt"
|
2019-05-02 21:31:29 +00:00
|
|
|
"reflect"
|
2017-04-27 21:09:59 +00:00
|
|
|
"strings"
|
2021-04-08 16:43:39 +00:00
|
|
|
"testing"
|
2017-04-27 21:09:59 +00:00
|
|
|
|
2019-05-02 21:31:29 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/framework"
|
2019-05-02 23:29:41 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/logical"
|
2017-04-26 06:46:01 +00:00
|
|
|
)
|
|
|
|
|
2017-04-27 13:47:56 +00:00
|
|
|
func TestPki_FetchCertBySerial(t *testing.T) {
|
2022-08-01 20:43:38 +00:00
|
|
|
t.Parallel()
|
2022-11-14 23:26:26 +00:00
|
|
|
b, storage := CreateBackendWithStorage(t)
|
2022-12-08 14:27:02 +00:00
|
|
|
sc := b.makeStorageContext(ctx, storage)
|
2017-04-26 06:46:01 +00:00
|
|
|
|
|
|
|
cases := map[string]struct {
|
2017-04-27 21:09:59 +00:00
|
|
|
Req *logical.Request
|
|
|
|
Prefix string
|
|
|
|
Serial string
|
2017-04-26 06:46:01 +00:00
|
|
|
}{
|
2017-04-27 21:09:59 +00:00
|
|
|
"valid cert": {
|
2017-04-26 06:46:01 +00:00
|
|
|
&logical.Request{
|
2017-04-28 12:55:28 +00:00
|
|
|
Storage: storage,
|
2017-04-26 06:46:01 +00:00
|
|
|
},
|
2017-04-27 21:09:59 +00:00
|
|
|
"certs/",
|
|
|
|
"00:00:00:00:00:00:00:00",
|
2017-04-26 06:46:01 +00:00
|
|
|
},
|
2017-04-27 21:09:59 +00:00
|
|
|
"revoked cert": {
|
2017-04-26 06:46:01 +00:00
|
|
|
&logical.Request{
|
2017-04-28 12:55:28 +00:00
|
|
|
Storage: storage,
|
2017-04-26 06:46:01 +00:00
|
|
|
},
|
2017-04-27 21:09:59 +00:00
|
|
|
"revoked/",
|
|
|
|
"11:11:11:11:11:11:11:11",
|
2017-04-26 06:46:01 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2017-04-27 21:09:59 +00:00
|
|
|
// Test for colon-based paths in storage
|
|
|
|
for name, tc := range cases {
|
|
|
|
storageKey := fmt.Sprintf("%s%s", tc.Prefix, tc.Serial)
|
2018-01-19 06:44:44 +00:00
|
|
|
err := storage.Put(context.Background(), &logical.StorageEntry{
|
2017-04-27 21:09:59 +00:00
|
|
|
Key: storageKey,
|
|
|
|
Value: []byte("some data"),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error writing to storage on %s colon-based storage path: %s", name, err)
|
|
|
|
}
|
|
|
|
|
2022-12-08 14:27:02 +00:00
|
|
|
certEntry, err := fetchCertBySerial(sc, tc.Prefix, tc.Serial)
|
2017-04-27 21:09:59 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error on %s for colon-based storage path: %s", name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for non-nil on valid/revoked certs
|
|
|
|
if certEntry == nil {
|
|
|
|
t.Fatalf("nil on %s for colon-based storage path", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that cert serials are converted/updated after fetch
|
2017-05-03 14:12:58 +00:00
|
|
|
expectedKey := tc.Prefix + normalizeSerial(tc.Serial)
|
2018-01-19 06:44:44 +00:00
|
|
|
se, err := storage.Get(context.Background(), expectedKey)
|
2017-04-27 21:09:59 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error on %s for colon-based storage path:%s", name, err)
|
|
|
|
}
|
|
|
|
if strings.Compare(expectedKey, se.Key) != 0 {
|
|
|
|
t.Fatalf("expected: %s, got: %s", expectedKey, certEntry.Key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset storage
|
|
|
|
storage = &logical.InmemStorage{}
|
|
|
|
|
|
|
|
// Test for hyphen-base paths in storage
|
2017-04-26 06:46:01 +00:00
|
|
|
for name, tc := range cases {
|
2017-05-03 14:12:58 +00:00
|
|
|
storageKey := tc.Prefix + normalizeSerial(tc.Serial)
|
2018-01-19 06:44:44 +00:00
|
|
|
err := storage.Put(context.Background(), &logical.StorageEntry{
|
2017-04-27 21:09:59 +00:00
|
|
|
Key: storageKey,
|
2017-04-26 06:46:01 +00:00
|
|
|
Value: []byte("some data"),
|
|
|
|
})
|
|
|
|
if err != nil {
|
2017-04-27 21:09:59 +00:00
|
|
|
t.Fatalf("error writing to storage on %s hyphen-based storage path: %s", name, err)
|
2017-04-26 06:46:01 +00:00
|
|
|
}
|
|
|
|
|
2022-12-08 14:27:02 +00:00
|
|
|
certEntry, err := fetchCertBySerial(sc, tc.Prefix, tc.Serial)
|
2017-04-28 12:55:28 +00:00
|
|
|
if err != nil || certEntry == nil {
|
|
|
|
t.Fatalf("error on %s for hyphen-based storage path: err: %v, entry: %v", name, err, certEntry)
|
|
|
|
}
|
|
|
|
}
|
2017-04-26 06:46:01 +00:00
|
|
|
}
|
2019-05-02 21:31:29 +00:00
|
|
|
|
2019-05-02 23:02:15 +00:00
|
|
|
// Demonstrate that multiple OUs in the name are handled in an
|
2019-05-02 21:31:29 +00:00
|
|
|
// order-preserving way.
|
|
|
|
func TestPki_MultipleOUs(t *testing.T) {
|
2022-08-01 20:43:38 +00:00
|
|
|
t.Parallel()
|
2019-05-02 23:02:15 +00:00
|
|
|
var b backend
|
2019-05-02 21:31:29 +00:00
|
|
|
fields := addCACommonFields(map[string]*framework.FieldSchema{})
|
|
|
|
|
|
|
|
apiData := &framework.FieldData{
|
|
|
|
Schema: fields,
|
2019-05-02 23:29:41 +00:00
|
|
|
Raw: map[string]interface{}{
|
|
|
|
"cn": "example.com",
|
|
|
|
"ttl": 3600,
|
2019-05-02 21:31:29 +00:00
|
|
|
},
|
|
|
|
}
|
2019-05-09 15:59:22 +00:00
|
|
|
input := &inputBundle{
|
2019-05-02 23:29:41 +00:00
|
|
|
apiData: apiData,
|
|
|
|
role: &roleEntry{
|
|
|
|
MaxTTL: 3600,
|
|
|
|
OU: []string{"Z", "E", "V"},
|
2019-05-02 21:31:29 +00:00
|
|
|
},
|
|
|
|
}
|
2022-09-15 19:38:33 +00:00
|
|
|
cb, _, err := generateCreationBundle(&b, input, nil, nil)
|
2019-05-02 21:31:29 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Error: %v", err)
|
|
|
|
}
|
|
|
|
|
2019-05-02 23:29:41 +00:00
|
|
|
expected := []string{"Z", "E", "V"}
|
2019-05-09 15:59:22 +00:00
|
|
|
actual := cb.Params.Subject.OrganizationalUnit
|
2019-05-02 21:31:29 +00:00
|
|
|
|
2019-05-02 23:29:41 +00:00
|
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
|
|
t.Fatalf("Expected %v, got %v", expected, actual)
|
2019-05-02 21:31:29 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-04 22:46:38 +00:00
|
|
|
|
|
|
|
func TestPki_PermitFQDNs(t *testing.T) {
|
2022-08-01 20:43:38 +00:00
|
|
|
t.Parallel()
|
2020-02-04 22:46:38 +00:00
|
|
|
var b backend
|
|
|
|
fields := addCACommonFields(map[string]*framework.FieldSchema{})
|
|
|
|
|
2021-03-10 05:28:27 +00:00
|
|
|
cases := map[string]struct {
|
2021-11-11 00:09:06 +00:00
|
|
|
input *inputBundle
|
2021-10-04 18:02:47 +00:00
|
|
|
expectedDnsNames []string
|
2021-11-11 00:09:06 +00:00
|
|
|
expectedEmails []string
|
2021-03-10 05:28:27 +00:00
|
|
|
}{
|
|
|
|
"base valid case": {
|
|
|
|
input: &inputBundle{
|
|
|
|
apiData: &framework.FieldData{
|
|
|
|
Schema: fields,
|
|
|
|
Raw: map[string]interface{}{
|
|
|
|
"common_name": "example.com.",
|
|
|
|
"ttl": 3600,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
role: &roleEntry{
|
|
|
|
AllowAnyName: true,
|
|
|
|
MaxTTL: 3600,
|
|
|
|
EnforceHostnames: true,
|
|
|
|
},
|
|
|
|
},
|
2021-10-04 18:02:47 +00:00
|
|
|
expectedDnsNames: []string{"example.com."},
|
2021-11-11 00:09:06 +00:00
|
|
|
expectedEmails: []string{},
|
2020-02-04 22:46:38 +00:00
|
|
|
},
|
2021-03-10 05:28:27 +00:00
|
|
|
"case insensitivity validation": {
|
|
|
|
input: &inputBundle{
|
|
|
|
apiData: &framework.FieldData{
|
|
|
|
Schema: fields,
|
|
|
|
Raw: map[string]interface{}{
|
|
|
|
"common_name": "Example.Net",
|
|
|
|
"alt_names": "eXaMPLe.COM",
|
|
|
|
"ttl": 3600,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
role: &roleEntry{
|
|
|
|
AllowedDomains: []string{"example.net", "EXAMPLE.COM"},
|
|
|
|
AllowBareDomains: true,
|
|
|
|
MaxTTL: 3600,
|
|
|
|
},
|
|
|
|
},
|
2021-10-04 18:02:47 +00:00
|
|
|
expectedDnsNames: []string{"Example.Net", "eXaMPLe.COM"},
|
2021-11-11 00:09:06 +00:00
|
|
|
expectedEmails: []string{},
|
2021-10-04 18:02:47 +00:00
|
|
|
},
|
|
|
|
"case email as AllowedDomain with bare domains": {
|
|
|
|
input: &inputBundle{
|
|
|
|
apiData: &framework.FieldData{
|
|
|
|
Schema: fields,
|
|
|
|
Raw: map[string]interface{}{
|
|
|
|
"common_name": "test@testemail.com",
|
|
|
|
"ttl": 3600,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
role: &roleEntry{
|
|
|
|
AllowedDomains: []string{"test@testemail.com"},
|
|
|
|
AllowBareDomains: true,
|
|
|
|
MaxTTL: 3600,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedDnsNames: []string{},
|
2021-11-11 00:09:06 +00:00
|
|
|
expectedEmails: []string{"test@testemail.com"},
|
2021-10-04 18:02:47 +00:00
|
|
|
},
|
|
|
|
"case email common name with bare domains": {
|
|
|
|
input: &inputBundle{
|
|
|
|
apiData: &framework.FieldData{
|
|
|
|
Schema: fields,
|
|
|
|
Raw: map[string]interface{}{
|
|
|
|
"common_name": "test@testemail.com",
|
|
|
|
"ttl": 3600,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
role: &roleEntry{
|
|
|
|
AllowedDomains: []string{"testemail.com"},
|
|
|
|
AllowBareDomains: true,
|
|
|
|
MaxTTL: 3600,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedDnsNames: []string{},
|
2021-11-11 00:09:06 +00:00
|
|
|
expectedEmails: []string{"test@testemail.com"},
|
2020-02-04 22:46:38 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-10-04 18:02:47 +00:00
|
|
|
for name, testCase := range cases {
|
2022-08-01 20:43:38 +00:00
|
|
|
name := name
|
|
|
|
testCase := testCase
|
2021-10-04 18:02:47 +00:00
|
|
|
t.Run(name, func(t *testing.T) {
|
2022-09-15 19:38:33 +00:00
|
|
|
cb, _, err := generateCreationBundle(&b, testCase.input, nil, nil)
|
2021-10-04 18:02:47 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Error: %v", err)
|
|
|
|
}
|
2020-02-04 22:46:38 +00:00
|
|
|
|
2021-10-04 18:02:47 +00:00
|
|
|
actualDnsNames := cb.Params.DNSNames
|
2021-03-10 05:28:27 +00:00
|
|
|
|
2021-10-04 18:02:47 +00:00
|
|
|
if !reflect.DeepEqual(testCase.expectedDnsNames, actualDnsNames) {
|
|
|
|
t.Fatalf("Expected dns names %v, got %v", testCase.expectedDnsNames, actualDnsNames)
|
|
|
|
}
|
|
|
|
|
|
|
|
actualEmails := cb.Params.EmailAddresses
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(testCase.expectedEmails, actualEmails) {
|
|
|
|
t.Fatalf("Expected email addresses %v, got %v", testCase.expectedEmails, actualEmails)
|
|
|
|
}
|
|
|
|
})
|
2020-02-04 22:46:38 +00:00
|
|
|
}
|
|
|
|
}
|