Skip to content

Commit

Permalink
Audit: Use existing cloned object for hashing (don't re-clone) (hashi…
Browse files Browse the repository at this point in the history
…corp#27913)

* unexport hashstructure funcs

* don't re-copy/clone LogInput fields when this object belongs to the pipeline
  • Loading branch information
Peter Wilson authored Jul 31, 2024
1 parent 111647f commit aeae52f
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 105 deletions.
10 changes: 5 additions & 5 deletions audit/entry_formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,12 @@ func (f *entryFormatter) formatRequest(ctx context.Context, in *logical.LogInput

if !f.config.raw {
var err error
auth, err = hashAuth(ctx, f.salter, auth, f.config.hmacAccessor)
err = hashAuth(ctx, f.salter, auth, f.config.hmacAccessor)
if err != nil {
return nil, err
}

req, err = hashRequest(ctx, f.salter, req, f.config.hmacAccessor, in.NonHMACReqDataKeys)
err = hashRequest(ctx, f.salter, req, f.config.hmacAccessor, in.NonHMACReqDataKeys)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -394,17 +394,17 @@ func (f *entryFormatter) formatResponse(ctx context.Context, in *logical.LogInpu
}
} else {
var err error
auth, err = hashAuth(ctx, f.salter, auth, f.config.hmacAccessor)
err = hashAuth(ctx, f.salter, auth, f.config.hmacAccessor)
if err != nil {
return nil, err
}

req, err = hashRequest(ctx, f.salter, req, f.config.hmacAccessor, in.NonHMACReqDataKeys)
err = hashRequest(ctx, f.salter, req, f.config.hmacAccessor, in.NonHMACReqDataKeys)
if err != nil {
return nil, err
}

resp, err = hashResponse(ctx, f.salter, resp, f.config.hmacAccessor, in.NonHMACRespDataKeys, elideListResponseData)
err = hashResponse(ctx, f.salter, resp, f.config.hmacAccessor, in.NonHMACRespDataKeys, elideListResponseData)
if err != nil {
return nil, err
}
Expand Down
161 changes: 76 additions & 85 deletions audit/hashstructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ import (
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/hashicorp/vault/sdk/helper/wrapping"
"github.com/hashicorp/vault/sdk/logical"
"github.com/mitchellh/copystructure"
"github.com/mitchellh/reflectwalk"
)

// hashString hashes the given opaque string and returns it
// hashString uses the Salter to hash the supplied opaque string and returns it.
func hashString(ctx context.Context, salter Salter, data string) (string, error) {
salt, err := salter.Salt(ctx)
if err != nil {
Expand All @@ -27,76 +26,68 @@ func hashString(ctx context.Context, salter Salter, data string) (string, error)
return salt.GetIdentifiedHMAC(data), nil
}

// hashAuth returns a hashed copy of the logical.Auth input.
func hashAuth(ctx context.Context, salter Salter, in *logical.Auth, HMACAccessor bool) (*logical.Auth, error) {
if in == nil {
return nil, nil
// hashAuth uses the Salter to hash the supplied Auth (modifying it).
// hmacAccessor is used to indicate whether the accessor should also be HMAC'd
// when present.
func hashAuth(ctx context.Context, salter Salter, auth *logical.Auth, hmacAccessor bool) error {
if auth == nil {
return nil
}

salt, err := salter.Salt(ctx)
if err != nil {
return nil, err
return err
}

fn := salt.GetIdentifiedHMAC
auth := *in

if auth.ClientToken != "" {
auth.ClientToken = fn(auth.ClientToken)
}
if HMACAccessor && auth.Accessor != "" {
if hmacAccessor && auth.Accessor != "" {
auth.Accessor = fn(auth.Accessor)
}
return &auth, nil

return nil
}

// hashRequest returns a hashed copy of the logical.Request input.
func hashRequest(ctx context.Context, salter Salter, in *logical.Request, HMACAccessor bool, nonHMACDataKeys []string) (*logical.Request, error) {
if in == nil {
return nil, nil
// hashRequest uses the Salter to hash the supplied Request (modifying it).
// nonHMACDataKeys is used when hashing any 'Data' field within the Request which
// prevents those specific keys from HMAC'd.
// hmacAccessor is used to indicate whether some accessors should also be HMAC'd
// when present.
func hashRequest(ctx context.Context, salter Salter, req *logical.Request, hmacAccessor bool, nonHMACDataKeys []string) error {
if req == nil {
return nil
}

salt, err := salter.Salt(ctx)
if err != nil {
return nil, err
return err
}

fn := salt.GetIdentifiedHMAC
req := *in

if req.Auth != nil {
cp, err := copystructure.Copy(req.Auth)
if err != nil {
return nil, err
}

req.Auth, err = hashAuth(ctx, salter, cp.(*logical.Auth), HMACAccessor)
if err != nil {
return nil, err
}
err = hashAuth(ctx, salter, req.Auth, hmacAccessor)
if err != nil {
return err
}

if req.ClientToken != "" {
req.ClientToken = fn(req.ClientToken)
}
if HMACAccessor && req.ClientTokenAccessor != "" {
if hmacAccessor && req.ClientTokenAccessor != "" {
req.ClientTokenAccessor = fn(req.ClientTokenAccessor)
}

if req.Data != nil {
copy, err := copystructure.Copy(req.Data)
err = hashMap(fn, req.Data, nonHMACDataKeys)
if err != nil {
return nil, err
return err
}

err = hashMap(fn, copy.(map[string]interface{}), nonHMACDataKeys)
if err != nil {
return nil, err
}
req.Data = copy.(map[string]interface{})
}

return &req, nil
return nil
}

func hashMap(hashFunc hashCallback, data map[string]interface{}, nonHMACDataKeys []string) error {
Expand All @@ -112,102 +103,96 @@ func hashMap(hashFunc hashCallback, data map[string]interface{}, nonHMACDataKeys
}
}

return HashStructure(data, hashFunc, nonHMACDataKeys)
return hashStructure(data, hashFunc, nonHMACDataKeys)
}

// hashResponse returns a hashed copy of the logical.Request input.
func hashResponse(ctx context.Context, salter Salter, in *logical.Response, HMACAccessor bool, nonHMACDataKeys []string, elideListResponseData bool) (*logical.Response, error) {
if in == nil {
return nil, nil
// hashResponse uses the Salter to hash the supplied Response (modifying it).
// nonHMACDataKeys is used when hashing any 'Data' field within the Request which
// prevents those specific keys from HMAC'd.
// hmacAccessor is used to indicate whether some accessors should also be HMAC'd
// when present.
// elideListResponseData indicates whether any 'keys' or 'key_info' data present in
// the Response should be elided (when the request was a LIST operation).
// See: /vault/docs/audit#eliding-list-response-bodies
func hashResponse(ctx context.Context, salter Salter, resp *logical.Response, hmacAccessor bool, nonHMACDataKeys []string, elideListResponseData bool) error {
if resp == nil {
return nil
}

salt, err := salter.Salt(ctx)
if err != nil {
return nil, err
return err
}

fn := salt.GetIdentifiedHMAC
resp := *in

if resp.Auth != nil {
cp, err := copystructure.Copy(resp.Auth)
if err != nil {
return nil, err
}

resp.Auth, err = hashAuth(ctx, salter, cp.(*logical.Auth), HMACAccessor)
if err != nil {
return nil, err
}
err = hashAuth(ctx, salter, resp.Auth, hmacAccessor)
if err != nil {
return err
}

if resp.Data != nil {
copy, err := copystructure.Copy(resp.Data)
if err != nil {
return nil, err
}

mapCopy := copy.(map[string]interface{})
if b, ok := mapCopy[logical.HTTPRawBody].([]byte); ok {
mapCopy[logical.HTTPRawBody] = string(b)
if b, ok := resp.Data[logical.HTTPRawBody].([]byte); ok {
resp.Data[logical.HTTPRawBody] = string(b)
}

// Processing list response data elision takes place at this point in the code for performance reasons:
// - take advantage of the deep copy of resp.Data that was going to be done anyway for hashing
// - but elide data before potentially spending time hashing it
if elideListResponseData {
doElideListResponseData(mapCopy)
doElideListResponseData(resp.Data)
}

err = hashMap(fn, mapCopy, nonHMACDataKeys)
err = hashMap(fn, resp.Data, nonHMACDataKeys)
if err != nil {
return nil, err
return err
}
resp.Data = mapCopy
}

if resp.WrapInfo != nil {
var err error
resp.WrapInfo, err = hashWrapInfo(fn, resp.WrapInfo, HMACAccessor)
err = hashWrapInfo(fn, resp.WrapInfo, hmacAccessor)
if err != nil {
return nil, err
return err
}
}

return &resp, nil
return nil
}

// hashWrapInfo returns a hashed copy of the wrapping.ResponseWrapInfo input.
func hashWrapInfo(hashFunc hashCallback, in *wrapping.ResponseWrapInfo, HMACAccessor bool) (*wrapping.ResponseWrapInfo, error) {
if in == nil {
return nil, nil
}
// hashWrapInfo returns a hashed copy of the ResponseWrapInfo input.

wrapinfo := *in
// hashWrapInfo uses the supplied hashing function to hash ResponseWrapInfo (modifying it).
// hmacAccessor is used to indicate whether some accessors should also be HMAC'd
// when present.
func hashWrapInfo(hashFunc hashCallback, wrapInfo *wrapping.ResponseWrapInfo, hmacAccessor bool) error {
if wrapInfo == nil {
return nil
}

wrapinfo.Token = hashFunc(wrapinfo.Token)
wrapInfo.Token = hashFunc(wrapInfo.Token)

if HMACAccessor {
wrapinfo.Accessor = hashFunc(wrapinfo.Accessor)
if hmacAccessor {
wrapInfo.Accessor = hashFunc(wrapInfo.Accessor)

if wrapinfo.WrappedAccessor != "" {
wrapinfo.WrappedAccessor = hashFunc(wrapinfo.WrappedAccessor)
if wrapInfo.WrappedAccessor != "" {
wrapInfo.WrappedAccessor = hashFunc(wrapInfo.WrappedAccessor)
}
}

return &wrapinfo, nil
return nil
}

// HashStructure takes an interface and hashes all the values within
// hashStructure takes an interface and hashes all the values within
// the structure. Only _values_ are hashed: keys of objects are not.
//
// For the hashCallback, see the built-in HashCallbacks below.
func HashStructure(s interface{}, cb hashCallback, ignoredKeys []string) error {
func hashStructure(s interface{}, cb hashCallback, ignoredKeys []string) error {
walker := &hashWalker{Callback: cb, IgnoredKeys: ignoredKeys}
return reflectwalk.Walk(s, walker)
}

// hashCallback is the callback called for HashStructure to hash
// hashCallback is the callback called for hashStructure to hash
// a value.
type hashCallback func(string) string

Expand All @@ -219,20 +204,26 @@ type hashWalker struct {
// to be hashed. If there is an error, walking will be halted
// immediately and the error returned.
Callback hashCallback
// IgnoreKeys are the keys that wont have the hashCallback applied

// IgnoreKeys are the keys that won't have the hashCallback applied
IgnoredKeys []string

// MapElem appends the key itself (not the reflect.Value) to key.
// The last element in key is the most recently entered map key.
// Since Exit pops the last element of key, only nesting to another
// structure increases the size of this slice.
key []string
key []string

lastValue reflect.Value

// Enter appends to loc and exit pops loc. The last element of loc is thus
// the current location.
loc []reflectwalk.Location

// Map and Slice append to cs, Exit pops the last element off cs.
// The last element in cs is the most recently entered map or slice.
cs []reflect.Value

// MapElem and SliceElem append to csKey. The last element in csKey is the
// most recently entered map key or slice index. Since Exit pops the last
// element of csKey, only nesting to another structure increases the size of
Expand Down
Loading

0 comments on commit aeae52f

Please sign in to comment.