aboutsummaryrefslogtreecommitdiff
path: root/actions
diff options
context:
space:
mode:
authorEric Biggers <ebiggers@google.com>2022-02-23 12:35:04 -0800
committerEric Biggers <ebiggers@google.com>2022-02-23 12:35:04 -0800
commit74e870b7bd1585b4b509da47e0e75db66336e576 (patch)
tree9b67ab42cebbfd25d917852260a5300292f39630 /actions
parent6e355131670ad014e45f879475ddf800f0080d41 (diff)
Strictly validate metadata file ownership by default
The metadata validation checks introduced by the previous commits are good, but to reduce the attack surface it would be much better to avoid reading and parsing files owned by other users in the first place. There are some possible use cases for users sharing fscrypt metadata files, but I think that for the vast majority of users it is unneeded and just opens up attack surface. Thus, make fscrypt (and pam_fscrypt) not process policies or protectors owned by other users by default. Specifically, * If fscrypt or pam_fscrypt is running as a non-root user, only policies and protectors owned by the user or by root can be used. * If fscrypt is running as root, any policy or protector can be used. (This is to match user expectations -- starting a sudo session should gain rights, not remove rights.) * If pam_fscrypt is running as root, only policies and protectors owned by root can be used. Note that this only applies when the root user themselves has an fscrypt login protector, which is rare. Add an option 'allow_cross_user_metadata' to /etc/fscrypt.conf which allows restoring the old behavior for anyone who really needs it.
Diffstat (limited to 'actions')
-rw-r--r--actions/context.go20
-rw-r--r--actions/policy.go8
-rw-r--r--actions/protector.go4
3 files changed, 24 insertions, 8 deletions
diff --git a/actions/context.go b/actions/context.go
index 26295ec..1ee0d60 100644
--- a/actions/context.go
+++ b/actions/context.go
@@ -58,6 +58,12 @@ type Context struct {
// the user for whom the keys are claimed in the filesystem keyring when
// v2 policies are provisioned.
TargetUser *user.User
+ // TrustedUser is the user for whom policies and protectors are allowed
+ // to be read. Specifically, if TrustedUser is set, then only
+ // policies and protectors owned by TrustedUser or by root will be
+ // allowed to be read. If it's nil, then all policies and protectors
+ // the process has filesystem-level read access to will be allowed.
+ TrustedUser *user.User
}
// NewContextFromPath makes a context for the filesystem containing the
@@ -112,6 +118,16 @@ func newContextFromUser(targetUser *user.User) (*Context, error) {
return nil, err
}
+ // By default, when running as a non-root user we only read policies and
+ // protectors owned by the user or root. When running as root, we allow
+ // reading all policies and protectors.
+ if !ctx.Config.GetAllowCrossUserMetadata() && !util.IsUserRoot() {
+ ctx.TrustedUser, err = util.EffectiveUser()
+ if err != nil {
+ return nil, err
+ }
+ }
+
log.Printf("creating context for user %q", targetUser.Username)
return ctx, nil
}
@@ -136,7 +152,7 @@ func (ctx *Context) getKeyringOptions() *keyring.Options {
// getProtectorOption returns the ProtectorOption for the protector on the
// context's mountpoint with the specified descriptor.
func (ctx *Context) getProtectorOption(protectorDescriptor string) *ProtectorOption {
- mnt, data, err := ctx.Mount.GetProtector(protectorDescriptor)
+ mnt, data, err := ctx.Mount.GetProtector(protectorDescriptor, ctx.TrustedUser)
if err != nil {
return &ProtectorOption{ProtectorInfo{}, nil, err}
}
@@ -155,7 +171,7 @@ func (ctx *Context) ProtectorOptions() ([]*ProtectorOption, error) {
if err := ctx.checkContext(); err != nil {
return nil, err
}
- descriptors, err := ctx.Mount.ListProtectors()
+ descriptors, err := ctx.Mount.ListProtectors(ctx.TrustedUser)
if err != nil {
return nil, err
}
diff --git a/actions/policy.go b/actions/policy.go
index 7204380..3b8eb30 100644
--- a/actions/policy.go
+++ b/actions/policy.go
@@ -145,7 +145,7 @@ func PurgeAllPolicies(ctx *Context) error {
if err := ctx.checkContext(); err != nil {
return err
}
- policies, err := ctx.Mount.ListPolicies()
+ policies, err := ctx.Mount.ListPolicies(nil)
if err != nil {
return err
}
@@ -225,7 +225,7 @@ func GetPolicy(ctx *Context, descriptor string) (*Policy, error) {
if err := ctx.checkContext(); err != nil {
return nil, err
}
- data, err := ctx.Mount.GetPolicy(descriptor)
+ data, err := ctx.Mount.GetPolicy(descriptor, ctx.TrustedUser)
if err != nil {
return nil, err
}
@@ -262,7 +262,7 @@ func GetPolicyFromPath(ctx *Context, path string) (*Policy, error) {
descriptor := pathData.KeyDescriptor
log.Printf("found policy %s for %q", descriptor, path)
- mountData, err := ctx.Mount.GetPolicy(descriptor)
+ mountData, err := ctx.Mount.GetPolicy(descriptor, ctx.TrustedUser)
if err != nil {
log.Printf("getting policy metadata: %v", err)
if _, ok := err.(*filesystem.ErrPolicyNotFound); ok {
@@ -428,7 +428,7 @@ func (policy *Policy) AddProtector(protector *Protector) error {
if policy.Context.Mount != protector.Context.Mount {
log.Printf("policy on %s\n protector on %s\n", policy.Context.Mount, protector.Context.Mount)
isNewLink, err := policy.Context.Mount.AddLinkedProtector(
- protector.Descriptor(), protector.Context.Mount)
+ protector.Descriptor(), protector.Context.Mount, protector.Context.TrustedUser)
if err != nil {
return err
}
diff --git a/actions/protector.go b/actions/protector.go
index 3278e63..1171c83 100644
--- a/actions/protector.go
+++ b/actions/protector.go
@@ -199,7 +199,7 @@ func GetProtector(ctx *Context, descriptor string) (*Protector, error) {
}
protector := &Protector{Context: ctx}
- protector.data, err = ctx.Mount.GetRegularProtector(descriptor)
+ protector.data, err = ctx.Mount.GetRegularProtector(descriptor, ctx.TrustedUser)
return protector, err
}
@@ -218,7 +218,7 @@ func GetProtectorFromOption(ctx *Context, option *ProtectorOption) (*Protector,
// Replace the context if this is a linked protector
if option.LinkedMount != nil {
- ctx = &Context{ctx.Config, option.LinkedMount, ctx.TargetUser}
+ ctx = &Context{ctx.Config, option.LinkedMount, ctx.TargetUser, ctx.TrustedUser}
}
return &Protector{Context: ctx, data: option.data}, nil
}