Merge "mounttable: fix Resolve access"
diff --git a/profiles/internal/ipc/full_test.go b/profiles/internal/ipc/full_test.go
index a6573ff..06ed262 100644
--- a/profiles/internal/ipc/full_test.go
+++ b/profiles/internal/ipc/full_test.go
@@ -1118,6 +1118,97 @@
}
}
+// maliciousBlessingStore implements security.BlessingStore. It is a
+// BlessingStore that marks the last blessing that was set on it as
+// shareable with any peer. It does not care about the public key that
+// blessing being set is bound to.
+type maliciousBlessingStore struct {
+ b security.Blessings
+}
+
+func (s *maliciousBlessingStore) Set(b security.Blessings, _ security.BlessingPattern) (security.Blessings, error) {
+ s.b = b
+ return security.Blessings{}, nil
+}
+func (s *maliciousBlessingStore) ForPeer(...string) security.Blessings {
+ return s.b
+}
+func (*maliciousBlessingStore) SetDefault(b security.Blessings) error {
+ return nil
+}
+func (*maliciousBlessingStore) Default() security.Blessings {
+ return security.Blessings{}
+}
+func (*maliciousBlessingStore) PublicKey() security.PublicKey {
+ return nil
+}
+func (*maliciousBlessingStore) DebugString() string {
+ return ""
+}
+func (*maliciousBlessingStore) PeerBlessings() map[security.BlessingPattern]security.Blessings {
+ return nil
+}
+
+// maliciousPrincipal implements security.Principal. It is a wrapper over
+// a security.Principal that intercepts all invocations on the
+// principal's BlessingStore and serves them via a maliciousBlessingStore.
+type maliciousPrincipal struct {
+ security.Principal
+ b maliciousBlessingStore
+}
+
+func (p *maliciousPrincipal) BlessingStore() security.BlessingStore {
+ return &p.b
+}
+
+func TestRPCClientBlessingsPublicKey(t *testing.T) {
+ var (
+ pprovider, pserver = tsecurity.NewPrincipal("root"), tsecurity.NewPrincipal("server")
+ pclient = &maliciousPrincipal{Principal: tsecurity.NewPrincipal("client")}
+
+ bserver = bless(pprovider, pserver, "server")
+ bclient = bless(pprovider, pclient, "client")
+ bvictim = bless(pprovider, tsecurity.NewPrincipal("victim"), "victim")
+
+ b = createBundle(t, pclient, pserver, &testServer{})
+ )
+ defer b.cleanup(t)
+
+ // Make the client and server trust blessings from pprovider.
+ pclient.AddToRoots(pprovider.BlessingStore().Default())
+ pserver.AddToRoots(pprovider.BlessingStore().Default())
+
+ // Make the server present bserver to all clients.
+ pserver.BlessingStore().SetDefault(bserver)
+ tests := []struct {
+ blessings security.Blessings
+ errID verror.IDAction
+ err string
+ }{
+ {blessings: bclient},
+ // server disallows clients from authenticating with blessings not bound to
+ // the client principal's public key
+ {blessings: bvictim, errID: verror.ErrNoAccess, err: "bound to a different public key"},
+ // or authenticating with the server's blessings
+ {blessings: bserver, errID: verror.ErrNoAccess, err: "bound to a different public key"},
+ }
+ for i, test := range tests {
+ name := fmt.Sprintf("%d: Client RPCing with blessings %v", i, test.blessings)
+
+ pclient.BlessingStore().Set(test.blessings, "root")
+
+ call, err := b.client.StartCall(testContext(), "mountpoint/server/suffix", "Closure", nil)
+ if err != nil {
+ t.Errorf("%v: StartCall failed: %v", name, err)
+ continue
+ }
+ if err := call.Finish(); !matchesErrorPattern(err, test.errID, test.err) {
+ t.Errorf("%v: Finish returned error %v", name, err)
+ continue
+ }
+ }
+}
+
func TestDischargePurgeFromCache(t *testing.T) {
var (
pserver = tsecurity.NewPrincipal("server")
diff --git a/profiles/internal/ipc/server.go b/profiles/internal/ipc/server.go
index b4e52ee..9439086 100644
--- a/profiles/internal/ipc/server.go
+++ b/profiles/internal/ipc/server.go
@@ -1139,6 +1139,12 @@
}
func (fs *flowServer) initSecurity(req *ipc.Request) error {
+ // LocalPrincipal is nil which means we are operating under
+ // VCSecurityNone.
+ if fs.flow.LocalPrincipal() == nil {
+ return nil
+ }
+
// If additional credentials are provided, make them available in the context
// Detect unusable blessings now, rather then discovering they are unusable on
// first use.
@@ -1147,10 +1153,11 @@
// the server's identity as the blessing. Figure out what we want to do about
// this - should servers be able to assume that a blessing is something that
// does not have the authorizations that the server's own identity has?
- if b := req.GrantedBlessings; b.PublicKey() != nil && !reflect.DeepEqual(b.PublicKey(), fs.flow.LocalPrincipal().PublicKey()) {
- return verror.New(verror.ErrNoAccess, fs.T, fmt.Sprintf("blessing granted not bound to this server(%v vs %v)", b.PublicKey(), fs.flow.LocalPrincipal().PublicKey()))
+ if got, want := req.GrantedBlessings.PublicKey(), fs.flow.LocalPrincipal().PublicKey(); got != nil && !reflect.DeepEqual(got, want) {
+ return verror.New(verror.ErrNoAccess, fs.T, fmt.Sprintf("blessing granted not bound to this server(%v vs %v)", got, want))
}
fs.grantedBlessings = req.GrantedBlessings
+
var err error
if fs.clientBlessings, err = serverDecodeBlessings(fs.flow.VCDataCache(), req.Blessings, fs.server.stats); err != nil {
// When the server can't access the blessings cache, the client is not following
@@ -1160,6 +1167,11 @@
fs.server.streamMgr.ShutdownEndpoint(fs.RemoteEndpoint())
return verror.New(verror.ErrBadProtocol, fs.T, newErrBadBlessingsCache(fs.T, err))
}
+ // Verify that the blessings sent by the client in the request have the same public
+ // key as those sent by the client during VC establishment.
+ if got, want := fs.clientBlessings.PublicKey(), fs.flow.RemoteBlessings().PublicKey(); got != nil && !reflect.DeepEqual(got, want) {
+ return verror.New(verror.ErrNoAccess, fs.T, fmt.Sprintf("blessings sent with the request are bound to a different public key (%v) from the blessing used during VC establishment (%v)", got, want))
+ }
fs.ackBlessings = true
for _, d := range req.Discharges {