blob: beae5dc80822dfb147980155d897e3f71bc2d74c [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package conn
import (
"testing"
"time"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/flow"
"v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/security"
"v.io/v23/verror"
"v.io/x/lib/ibe"
vsecurity "v.io/x/ref/lib/security"
"v.io/x/ref/lib/security/bcrypter"
"v.io/x/ref/runtime/factories/fake"
"v.io/x/ref/test"
"v.io/x/ref/test/goroutines"
"v.io/x/ref/test/testutil"
)
func checkBlessings(t *testing.T, got, want security.Blessings, gotd map[string]security.Discharge) {
if !got.Equivalent(want) {
t.Errorf("got: %v wanted %v", got, want)
}
tpcav := got.ThirdPartyCaveats()
if len(tpcav) != 1 {
t.Errorf("got %#v, wanted one caveat.", tpcav)
}
tpid := got.ThirdPartyCaveats()[0].ThirdPartyDetails().ID()
if _, has := gotd[tpid]; !has {
t.Errorf("got: %#v wanted %s", gotd, tpid)
}
}
func checkFlowBlessings(t *testing.T, df, af flow.Flow, db, ab security.Blessings) {
if msg, err := af.ReadMsg(); err != nil || string(msg) != "hello" {
t.Errorf("Got %s, %v wanted hello, nil", string(msg), err)
}
checkBlessings(t, af.LocalBlessings(), ab, af.LocalDischarges())
checkBlessings(t, af.RemoteBlessings(), db, af.RemoteDischarges())
checkBlessings(t, df.LocalBlessings(), db, df.LocalDischarges())
checkBlessings(t, df.RemoteBlessings(), ab, df.RemoteDischarges())
}
func dialFlow(t *testing.T, ctx *context.T, dc *Conn, b security.Blessings, d map[string]security.Discharge) flow.Flow {
df, err := dc.Dial(ctx, b, d, dc.remote, 0, false)
if err != nil {
t.Fatal(err)
}
if _, err = df.WriteMsg([]byte("hello")); err != nil {
t.Fatal(err)
}
return df
}
func defaultBlessings(ctx *context.T) security.Blessings {
d, _ := v23.GetPrincipal(ctx).BlessingStore().Default()
return d
}
func defaultBlessingName(t *testing.T, ctx *context.T) string {
p := v23.GetPrincipal(ctx)
b, _ := p.BlessingStore().Default()
names := security.BlessingNames(p, b)
if len(names) != 1 {
t.Fatalf("Error in setting up test: default blessings have names %v, want exactly 1 name", names)
}
return names[0]
}
func NewRoot(t *testing.T, ctx *context.T) *bcrypter.Root {
master, err := ibe.SetupBB1()
if err != nil {
t.Fatal(err)
}
return bcrypter.NewRoot(defaultBlessingName(t, ctx), master)
}
func BlessWithTPCaveat(t *testing.T, ctx *context.T, p security.Principal, s string) (security.Blessings, map[string]security.Discharge) {
dp := v23.GetPrincipal(ctx)
expcav, err := security.NewExpiryCaveat(time.Now().Add(time.Hour))
if err != nil {
t.Fatal(err)
}
tpcav, err := security.NewPublicKeyCaveat(dp.PublicKey(), "discharge",
security.ThirdPartyRequirements{}, expcav)
if err != nil {
t.Fatal(err)
}
toextend, _ := dp.BlessingStore().Default()
b, err := dp.Bless(p.PublicKey(), toextend, s, tpcav)
if err != nil {
t.Fatal(err)
}
d, err := dp.MintDischarge(tpcav, security.UnconstrainedUse())
if err != nil {
t.Fatal(err)
}
return b, map[string]security.Discharge{d.ID(): d}
}
func NewPrincipalWithTPCaveat(t *testing.T, rootCtx *context.T, root *bcrypter.Root, s string) (*context.T, map[string]security.Discharge) {
p := testutil.NewPrincipal()
blessings, discharges := BlessWithTPCaveat(t, rootCtx, p, s)
vsecurity.SetDefaultBlessings(p, blessings)
ctx, err := v23.WithPrincipal(rootCtx, p)
if err != nil {
t.Fatal(err)
}
if root == nil {
return ctx, discharges
}
crypter := bcrypter.NewCrypter()
key, err := root.Extract(rootCtx, defaultBlessingName(t, ctx))
if err != nil {
t.Fatal(err)
}
if err := crypter.AddKey(ctx, key); err != nil {
t.Fatal(err)
}
return bcrypter.WithCrypter(ctx, crypter), discharges
}
type fakeDischargeClient struct {
p security.Principal
}
func (fc *fakeDischargeClient) Call(_ *context.T, _, _ string, inArgs, outArgs []interface{}, _ ...rpc.CallOpt) error {
expiry, err := security.NewExpiryCaveat(time.Now().Add(time.Minute))
if err != nil {
panic(err)
}
*(outArgs[0].(*security.Discharge)), err = fc.p.MintDischarge(
inArgs[0].(security.Caveat), expiry)
return err
}
func (fc *fakeDischargeClient) StartCall(*context.T, string, string, []interface{}, ...rpc.CallOpt) (rpc.ClientCall, error) {
return nil, nil
}
func (fc *fakeDischargeClient) PinConnection(*context.T, string, ...rpc.CallOpt) (flow.PinnedConn, error) {
return nil, nil
}
func (fc *fakeDischargeClient) Close() {}
func (fc *fakeDischargeClient) Closed() <-chan struct{} { return nil }
func TestUnidirectional(t *testing.T) {
defer goroutines.NoLeaks(t, leakWaitTime)()
ctx, shutdown := test.V23Init()
defer shutdown()
ctx = fake.SetClientFactory(ctx, func(ctx *context.T, opts ...rpc.ClientOpt) rpc.Client {
return &fakeDischargeClient{v23.GetPrincipal(ctx)}
})
ctx, _, _ = v23.WithNewClient(ctx)
dctx, dd := NewPrincipalWithTPCaveat(t, ctx, nil, "dialer")
actx, _ := NewPrincipalWithTPCaveat(t, ctx, nil, "acceptor")
aflows := make(chan flow.Flow, 2)
dc, ac, derr, aerr := setupConns(t, "local", "", dctx, actx, nil, aflows, nil, nil)
if derr != nil || aerr != nil {
t.Fatal(derr, aerr)
}
defer dc.Close(dctx, nil)
defer ac.Close(actx, nil)
df1 := dialFlow(t, dctx, dc, defaultBlessings(dctx), dd)
af1 := <-aflows
checkFlowBlessings(t, df1, af1, defaultBlessings(dctx), defaultBlessings(actx))
db2, dd2 := BlessWithTPCaveat(t, ctx, v23.GetPrincipal(dctx), "other")
df2 := dialFlow(t, dctx, dc, db2, dd2)
af2 := <-aflows
checkFlowBlessings(t, df2, af2, db2, defaultBlessings(actx))
// We should not be able to dial in the other direction, because that flow
// manager is not willing to accept flows.
_, err := ac.Dial(actx, ac.LocalBlessings(), nil, naming.Endpoint{}, 0, false)
if verror.ErrorID(err) != ErrDialingNonServer.ID {
t.Errorf("got %v, wanted ErrDialingNonServer", err)
}
}
func TestBidirectional(t *testing.T) {
defer goroutines.NoLeaks(t, leakWaitTime)()
ctx, shutdown := test.V23Init()
defer shutdown()
ctx = fake.SetClientFactory(ctx, func(ctx *context.T, opts ...rpc.ClientOpt) rpc.Client {
return &fakeDischargeClient{v23.GetPrincipal(ctx)}
})
ctx, _, _ = v23.WithNewClient(ctx)
dctx, dd := NewPrincipalWithTPCaveat(t, ctx, nil, "dialer")
actx, ad := NewPrincipalWithTPCaveat(t, ctx, nil, "acceptor")
dflows := make(chan flow.Flow, 2)
aflows := make(chan flow.Flow, 2)
dc, ac, derr, aerr := setupConns(t, "local", "", dctx, actx, dflows, aflows, nil, nil)
if derr != nil || aerr != nil {
t.Fatal(derr, aerr)
}
defer dc.Close(dctx, nil)
defer ac.Close(actx, nil)
df1 := dialFlow(t, dctx, dc, defaultBlessings(dctx), dd)
af1 := <-aflows
checkFlowBlessings(t, df1, af1, defaultBlessings(dctx), defaultBlessings(actx))
db2, dd2 := BlessWithTPCaveat(t, ctx, v23.GetPrincipal(dctx), "other")
df2 := dialFlow(t, dctx, dc, db2, dd2)
af2 := <-aflows
checkFlowBlessings(t, df2, af2, db2, defaultBlessings(actx))
af3 := dialFlow(t, actx, ac, defaultBlessings(actx), ad)
df3 := <-dflows
checkFlowBlessings(t, af3, df3, defaultBlessings(actx), defaultBlessings(dctx))
ab2, ad2 := BlessWithTPCaveat(t, ctx, v23.GetPrincipal(actx), "aother")
af4 := dialFlow(t, actx, ac, ab2, ad2)
df4 := <-dflows
checkFlowBlessings(t, af4, df4, ab2, defaultBlessings(dctx))
}
func TestPrivateMutualAuth(t *testing.T) {
defer goroutines.NoLeaks(t, leakWaitTime)()
ctx, shutdown := test.V23Init()
defer shutdown()
var err error
// The following is so that we have a known default blessing name
// for the root context.
if ctx, err = v23.WithPrincipal(ctx, testutil.NewPrincipal("root")); err != nil {
t.Fatal(err)
}
ctx = fake.SetClientFactory(ctx, func(ctx *context.T, opts ...rpc.ClientOpt) rpc.Client {
return &fakeDischargeClient{v23.GetPrincipal(ctx)}
})
ctx, _, _ = v23.WithNewClient(ctx)
root := NewRoot(t, ctx)
dctx, _ := NewPrincipalWithTPCaveat(t, ctx, root, "dialer")
actx, _ := NewPrincipalWithTPCaveat(t, ctx, root, "acceptor")
successTests := []struct {
dAuth, aAuth []security.BlessingPattern
}{
{[]security.BlessingPattern{"root"}, []security.BlessingPattern{"root"}},
{[]security.BlessingPattern{"root"}, []security.BlessingPattern{"root:dialer"}},
{[]security.BlessingPattern{"root:acceptor"}, []security.BlessingPattern{"root"}},
{[]security.BlessingPattern{"root:acceptor"}, []security.BlessingPattern{"root:dialer"}},
{[]security.BlessingPattern{"root:$", "root:acceptor:$"}, []security.BlessingPattern{"root:dialer:$"}},
}
for _, test := range successTests {
dc, ac, derr, aerr := setupConns(t, "local", "", dctx, actx, nil, nil, test.dAuth, test.aAuth)
if derr != nil || aerr != nil {
t.Errorf("test(dAuth: %v, aAuth: %v): setup failed with errors: %v, %v", test.dAuth, test.aAuth, derr, aerr)
}
if dc != nil {
dc.Close(dctx, nil)
}
if ac != nil {
ac.Close(actx, nil)
}
}
failureTests := []struct {
dAuth, aAuth []security.BlessingPattern
}{
{[]security.BlessingPattern{"root:other"}, []security.BlessingPattern{"root"}}, // dialer does not authorize acceptor
{[]security.BlessingPattern{"root:$"}, []security.BlessingPattern{"root:dialer"}}, // dialer does not authorize acceptor
{[]security.BlessingPattern{"root"}, []security.BlessingPattern{"root:other"}}, // acceptor does not authorize dialier
{[]security.BlessingPattern{"root:acceptor"}, []security.BlessingPattern{"root:$"}}, // acceptor does not authorize dialier
}
for _, test := range failureTests {
dc, ac, derr, _ := setupConns(t, "local", "", dctx, actx, nil, nil, test.dAuth, test.aAuth)
if derr == nil {
t.Errorf("test(dAuth: %v, aAuth: %v): dial succeeded unexpectedly", test.dAuth, test.aAuth)
}
if dc != nil {
dc.Close(dctx, nil)
}
if ac != nil {
ac.Close(actx, nil)
}
}
// Setup should fail when acceptor has an authorization policy for its blessings but no
// crypter available.
actx = bcrypter.WithCrypter(actx, nil)
dc, ac, _, aerr := setupConns(t, "local", "", dctx, actx, nil, nil, []security.BlessingPattern{"root"}, []security.BlessingPattern{"root"})
if aerr == nil {
t.Fatal("acceptor with no crypter but non-empty authorization poliocy succeeded unexpectedly")
}
if dc != nil {
dc.Close(dctx, nil)
}
if ac != nil {
ac.Close(actx, nil)
}
}