vault-auth-tee/path_tees.go
Harald Hoyer 94c86df4d5
fix: enable clearing the sgx_mrsigner and sgx_mrenclave field
Add the ability to clear the `sgx_mrsigner` and `sgx_mrenclave` field.

Otherwise we cannot switch from `sgx_mrenclave` to `sgx_mrsigner` based
authentication.

Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
2024-02-27 11:58:55 +01:00

333 lines
9.3 KiB
Go

// SPDX-License-Identifier: MPL-2.0
// Copyright (c) HashiCorp, Inc.
// Copyright (c) Matter Labs
package vault_auth_tee
import "C"
import (
"context"
"encoding/hex"
"fmt"
"strings"
"time"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/tokenutil"
"github.com/hashicorp/vault/sdk/logical"
)
func pathListTees(b *backend) *framework.Path {
return &framework.Path{
Pattern: "tees/?",
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: operationPrefixTee,
OperationSuffix: "tees",
Navigation: true,
ItemType: "Tee",
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.ListOperation: b.pathTeeList,
},
HelpSynopsis: pathTeeHelpSyn,
HelpDescription: pathTeeHelpDesc,
}
}
func pathTees(b *backend) *framework.Path {
p := &framework.Path{
Pattern: "tees/" + framework.GenericNameRegex("name"),
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: operationPrefixTee,
OperationSuffix: "tee",
Action: "Create",
ItemType: "Tee",
},
Fields: map[string]*framework.FieldSchema{
"name": {
Type: framework.TypeString,
Description: "The name of the Tee, which passes remote attestation verification",
},
"types": {
Type: framework.TypeCommaStringSlice,
Description: "The types of the TEE.",
},
"sgx_mrsigner": {
Type: framework.TypeString,
Description: `The SGX mrsigner hex value to check the attestation report against`,
},
"sgx_mrenclave": {
Type: framework.TypeString,
Description: `The SGX mrenclave hex value to check the attestation report against`,
},
"sgx_isv_prodid": {
Type: framework.TypeInt,
Description: `The SGX isv_prodid value to check the attestation report against`,
},
"sgx_min_isv_svn": {
Type: framework.TypeInt,
Description: `The SGX minimum isv_svn value to check the attestation report against`,
},
"sgx_allowed_tcb_levels": {
Type: framework.TypeCommaStringSlice,
Description: `A comma seperated list of allowed SGX TCB states.
Allowed values are: ConfigNeeded, OutOfDate, OutOfDateConfigNeeded, SwHardeningNeeded, ConfigAndSwHardeningNeeded`,
},
"display_name": {
Type: framework.TypeString,
Description: `The display name to use for clients using this certificate.`,
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.DeleteOperation: b.pathTeeDelete,
logical.ReadOperation: b.pathTeeRead,
logical.UpdateOperation: b.pathTeeWrite,
},
HelpSynopsis: pathTeeHelpSyn,
HelpDescription: pathTeeHelpDesc,
}
tokenutil.AddTokenFields(p.Fields)
return p
}
func (b *backend) Tee(ctx context.Context, s logical.Storage, n string) (*TeeEntry, error) {
entry, err := s.Get(ctx, "tee/"+strings.ToLower(n))
if err != nil {
return nil, err
}
if entry == nil {
return nil, nil
}
var result TeeEntry
if err := entry.DecodeJSON(&result); err != nil {
return nil, err
}
return &result, nil
}
func (b *backend) pathTeeDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
err := req.Storage.Delete(ctx, "tee/"+strings.ToLower(d.Get("name").(string)))
if err != nil {
return nil, err
}
return nil, nil
}
func (b *backend) pathTeeList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
tees, err := req.Storage.List(ctx, "tee/")
if err != nil {
return nil, err
}
return logical.ListResponse(tees), nil
}
func (b *backend) pathTeeRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
tee, err := b.Tee(ctx, req.Storage, strings.ToLower(d.Get("name").(string)))
if err != nil {
return nil, err
}
if tee == nil {
return nil, nil
}
data := map[string]interface{}{
"display_name": tee.DisplayName,
"types": tee.Types,
"sgx_mrsigner": tee.SgxMrsigner,
"sgx_mrenclave": tee.SgxMrenclave,
"sgx_isv_prodid": tee.SgxIsvProdid,
"sgx_min_isv_svn": tee.SgxMinIsvSvn,
"sgx_allowed_tcb_levels": tee.SgxAllowedTcbLevels,
}
tee.PopulateTokenData(data)
return &logical.Response{
Data: data,
}, nil
}
func (b *backend) pathTeeWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
name := strings.ToLower(d.Get("name").(string))
tee, err := b.Tee(ctx, req.Storage, name)
if err != nil {
return nil, err
}
if tee == nil {
tee = &TeeEntry{
Name: name,
}
}
// Get non tokenutil fields
if displayNameRaw, ok := d.GetOk("display_name"); ok {
tee.DisplayName = displayNameRaw.(string)
}
if teeTypes, ok := d.GetOk("types"); ok {
tee.Types = make(map[string]bool)
handled := make(map[string]bool)
for _, t := range teeTypes.([]string) {
// only SGX supported for now
if _, ok = handled[t]; ok {
return logical.ErrorResponse(fmt.Sprintf("duplicate TEE type `%s`", t)), nil
}
if t == "sgx" {
tee.Types[t] = true
handled[t] = true
response, err := handleSGXConfig(d, tee)
if response != nil || err != nil {
return response, err
}
} else {
return logical.ErrorResponse(fmt.Sprintf("invalid TEE type `%s`", t)), nil
}
}
} else {
return logical.ErrorResponse("missing TEE types"), nil
}
// Get tokenutil fields
if err := tee.ParseTokenFields(req, d); err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
var resp logical.Response
systemDefaultTTL := b.System().DefaultLeaseTTL()
if tee.TokenTTL > systemDefaultTTL {
resp.AddWarning(fmt.Sprintf("Given ttl of %d seconds is greater than current mount/system default of %d seconds", tee.TokenTTL/time.Second, systemDefaultTTL/time.Second))
}
systemMaxTTL := b.System().MaxLeaseTTL()
if tee.TokenMaxTTL > systemMaxTTL {
resp.AddWarning(fmt.Sprintf("Given max_ttl of %d seconds is greater than current mount/system default of %d seconds", tee.TokenMaxTTL/time.Second, systemMaxTTL/time.Second))
}
if tee.TokenMaxTTL != 0 && tee.TokenTTL > tee.TokenMaxTTL {
return logical.ErrorResponse("ttl should be shorter than max_ttl"), nil
}
if tee.TokenPeriod > systemMaxTTL {
resp.AddWarning(fmt.Sprintf("Given period of %d seconds is greater than the backend's maximum TTL of %d seconds", tee.TokenPeriod/time.Second, systemMaxTTL/time.Second))
}
// Default the display name to the certificate name if not given
if tee.DisplayName == "" {
tee.DisplayName = name
}
// Store it
entry, err := logical.StorageEntryJSON("tee/"+name, tee)
if err != nil {
return nil, err
}
if err := req.Storage.Put(ctx, entry); err != nil {
return nil, err
}
if len(resp.Warnings) == 0 {
return nil, nil
}
return &resp, nil
}
func handleSGXConfig(d *framework.FieldData, tee *TeeEntry) (*logical.Response, error) {
if sgxMrsignerRaw, ok := d.GetOk("sgx_mrsigner"); ok {
tee.SgxMrsigner = strings.ToLower(sgxMrsignerRaw.(string))
if tee.SgxMrsigner != "" {
b, err := hex.DecodeString(tee.SgxMrsigner)
if err != nil || len(b) != 32 {
return logical.ErrorResponse("`sgx_mrsigner` must be 32 byte hex encoded"), nil
}
}
}
if sgxMrenclaveRaw, ok := d.GetOk("sgx_mrenclave"); ok {
tee.SgxMrenclave = strings.ToLower(sgxMrenclaveRaw.(string))
if tee.SgxMrenclave != "" {
b, err := hex.DecodeString(tee.SgxMrenclave)
if err != nil || len(b) != 32 {
return logical.ErrorResponse("`sgx_mrenclave` must be 32 byte hex encoded"), nil
}
}
}
if tee.SgxMrsigner == "" && tee.SgxMrenclave == "" {
return logical.ErrorResponse("either `sgx_mrsigner` or `sgx_mrenclave` must be set"), nil
}
if sgxIsvProdidRaw, ok := d.GetOk("sgx_isv_prodid"); ok {
tee.SgxIsvProdid = sgxIsvProdidRaw.(int)
}
if sgxMinIsvSvnRaw, ok := d.GetOk("sgx_min_isv_svn"); ok {
tee.SgxMinIsvSvn = sgxMinIsvSvnRaw.(int)
}
if sgxAllowedTcbLevelsRaw, ok := d.GetOk("sgx_allowed_tcb_levels"); ok {
tee.SgxAllowedTcbLevels = make(map[SgxQlQvResult]bool)
for _, v := range sgxAllowedTcbLevelsRaw.([]string) {
var state SgxQlQvResult
switch v {
case "Ok":
state = SgxQlQvResultOk
case "ConfigNeeded":
state = SgxQlQvResultConfigNeeded
case "OutOfDate":
state = SgxQlQvResultOutOfDate
case "OutOfDateConfigNeeded":
state = SgxQlQvResultOutOfDateConfigNeeded
case "SwHardeningNeeded":
state = SgxQlQvResultSwHardeningNeeded
case "ConfigAndSwHardeningNeeded":
state = SgxQlQvResultConfigAndSwHardeningNeeded
default:
return logical.ErrorResponse("invalid sgx_allowed_tcb_levels value"), logical.ErrInvalidRequest
}
tee.SgxAllowedTcbLevels[state] = true
}
}
return nil, nil
}
type TeeEntry struct {
tokenutil.TokenParams
Name string
DisplayName string
Types map[string]bool
SgxMrsigner string
SgxMrenclave string
SgxIsvProdid int
SgxMinIsvSvn int
SgxAllowedTcbLevels map[SgxQlQvResult]bool
}
const pathTeeHelpSyn = `
Manage TEE remote attestation parameters used for authentication.`
const pathTeeHelpDesc = `
This endpoint allows you to create, read, update, and delete TEEs
that are allowed to authenticate.
Deleting a TEE will not revoke auth for prior authenticated connections.
To do this, do a revoke on "login". If you don't need to revoke login immediately,
then the next renew will cause the lease to expire.
`