mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-03 03:58:01 +00:00 
			
		
		
		
	audit: hash time.Time values in map fields (#2689)
This enables audit.Hash to hash time.Time values that may exist as direct fields in the map. This will error (instead of panic) for any time.Time values that don't occur within map values. For example, this does not support a time.Time within a slice. If that needs to be supported then modifications will need to be made. This also requires an update to reflectwalk (included in this PR). This is a minimal change that allows SkipEntry to signal to skip an entire struct. We do this because we don't want to walk any of time.Time since we handle it directly.
This commit is contained in:
		
				
					committed by
					
						
						Jeff Mitchell
					
				
			
			
				
	
			
			
			
						parent
						
							9d04834c9d
						
					
				
				
					commit
					4dc061e923
				
			@@ -1,8 +1,10 @@
 | 
			
		||||
package audit
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/vault/helper/salt"
 | 
			
		||||
	"github.com/hashicorp/vault/helper/wrapping"
 | 
			
		||||
@@ -141,6 +143,12 @@ type hashWalker struct {
 | 
			
		||||
	unknownKeys []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hashTimeType stores a pre-computed reflect.Type for a time.Time so
 | 
			
		||||
// we can quickly compare in hashWalker.Struct. We create an empty/invalid
 | 
			
		||||
// time.Time{} so we don't need to incur any additional startup cost vs.
 | 
			
		||||
// Now() or Unix().
 | 
			
		||||
var hashTimeType = reflect.TypeOf(time.Time{})
 | 
			
		||||
 | 
			
		||||
func (w *hashWalker) Enter(loc reflectwalk.Location) error {
 | 
			
		||||
	w.loc = loc
 | 
			
		||||
	return nil
 | 
			
		||||
@@ -188,6 +196,41 @@ func (w *hashWalker) SliceElem(i int, elem reflect.Value) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *hashWalker) Struct(v reflect.Value) error {
 | 
			
		||||
	// We are looking for time values. If it isn't one, ignore it.
 | 
			
		||||
	if v.Type() != hashTimeType {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we aren't in a map value, return an error to prevent a panic
 | 
			
		||||
	if v.Interface() != w.lastValue.Interface() {
 | 
			
		||||
		return errors.New("time.Time value in a non map key cannot be hashed for audits")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Override location to be a MapValue. loc is set to None since we
 | 
			
		||||
	// already "entered" the struct. We could do better here by keeping
 | 
			
		||||
	// a stack of locations and checking the last entry.
 | 
			
		||||
	w.loc = reflectwalk.MapValue
 | 
			
		||||
 | 
			
		||||
	// Create a string value of the time. IMPORTANT: this must never change
 | 
			
		||||
	// across Vault versions or the hash value of equivalent time.Time will
 | 
			
		||||
	// change.
 | 
			
		||||
	strVal := v.Interface().(time.Time).UTC().Format(time.RFC3339Nano)
 | 
			
		||||
 | 
			
		||||
	// Walk it as if it were a primitive value with the string value.
 | 
			
		||||
	// This will replace the currenty map value (which is a time.Time).
 | 
			
		||||
	if err := w.Primitive(reflect.ValueOf(strVal)); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Skip this entry so that we don't walk the struct.
 | 
			
		||||
	return reflectwalk.SkipEntry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *hashWalker) StructField(reflect.StructField, reflect.Value) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *hashWalker) Primitive(v reflect.Value) error {
 | 
			
		||||
	if w.Callback == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
 
 | 
			
		||||
@@ -140,6 +140,10 @@ func TestHash(t *testing.T) {
 | 
			
		||||
			&logical.Response{
 | 
			
		||||
				Data: map[string]interface{}{
 | 
			
		||||
					"foo": "bar",
 | 
			
		||||
 | 
			
		||||
					// Responses can contain time values, so test that with
 | 
			
		||||
					// a known fixed value.
 | 
			
		||||
					"bar": time.Unix(1494264707, 0),
 | 
			
		||||
				},
 | 
			
		||||
				WrapInfo: &wrapping.ResponseWrapInfo{
 | 
			
		||||
					TTL:             60,
 | 
			
		||||
@@ -151,6 +155,7 @@ func TestHash(t *testing.T) {
 | 
			
		||||
			&logical.Response{
 | 
			
		||||
				Data: map[string]interface{}{
 | 
			
		||||
					"foo": "hmac-sha256:f9320baf0249169e73850cd6156ded0106e2bb6ad8cab01b7bbbebe6d1065317",
 | 
			
		||||
					"bar": "hmac-sha256:b09b815a7d1c3bbcf702f9c9a50ef6408d0935bea0154383a128ca8743eb06fc",
 | 
			
		||||
				},
 | 
			
		||||
				WrapInfo: &wrapping.ResponseWrapInfo{
 | 
			
		||||
					TTL:             60,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/github.com/mitchellh/reflectwalk/reflectwalk.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/mitchellh/reflectwalk/reflectwalk.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -72,6 +72,7 @@ type PointerWalker interface {
 | 
			
		||||
// SkipEntry can be returned from walk functions to skip walking
 | 
			
		||||
// the value of this field. This is only valid in the following functions:
 | 
			
		||||
//
 | 
			
		||||
//   - Struct: skips all fields from being walked
 | 
			
		||||
//   - StructField: skips walking the struct value
 | 
			
		||||
//
 | 
			
		||||
var SkipEntry = errors.New("skip this entry")
 | 
			
		||||
@@ -345,12 +346,19 @@ func walkStruct(v reflect.Value, w interface{}) (err error) {
 | 
			
		||||
		ew.Enter(Struct)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	skip := false
 | 
			
		||||
	if sw, ok := w.(StructWalker); ok {
 | 
			
		||||
		if err = sw.Struct(v); err != nil {
 | 
			
		||||
		err = sw.Struct(v)
 | 
			
		||||
		if err == SkipEntry {
 | 
			
		||||
			skip = true
 | 
			
		||||
			err = nil
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !skip {
 | 
			
		||||
		vt := v.Type()
 | 
			
		||||
		for i := 0; i < vt.NumField(); i++ {
 | 
			
		||||
			sf := vt.Field(i)
 | 
			
		||||
@@ -383,6 +391,7 @@ func walkStruct(v reflect.Value, w interface{}) (err error) {
 | 
			
		||||
				ew.Exit(StructField)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ewok {
 | 
			
		||||
		ew.Exit(Struct)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							@@ -1129,10 +1129,10 @@
 | 
			
		||||
			"revisionTime": "2017-03-07T20:11:23Z"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"checksumSHA1": "67Y6z6rHipvOvFwCZZXqKH+TWao=",
 | 
			
		||||
			"checksumSHA1": "KqsMqI+Y+3EFYPhyzafpIneaVCM=",
 | 
			
		||||
			"path": "github.com/mitchellh/reflectwalk",
 | 
			
		||||
			"revision": "417edcfd99a4d472c262e58f22b4bfe97580f03e",
 | 
			
		||||
			"revisionTime": "2017-01-10T16:52:07Z"
 | 
			
		||||
			"revision": "8d802ff4ae93611b807597f639c19f76074df5c6",
 | 
			
		||||
			"revisionTime": "2017-05-08T17:38:06Z"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"checksumSHA1": "BxxkAJ/Nm61PybCXvQIZJwyTj3Y=",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user