runtimes/google/ipc: Encode expected server blessings in the name instead of options.RemoteID
Change-Id: Idfe32371b9cfd51e8d06b2ff67cd516759de799c
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index 34f8bb5..4e12fb9 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -5,6 +5,7 @@
"io"
"math"
"math/rand"
+ "regexp"
"strings"
"sync"
"time"
@@ -31,6 +32,11 @@
errNonRootedName = verror.BadArgf("ipc: cannot connect to a non-rooted name")
)
+var serverPatternRegexp = regexp.MustCompile("^\\[([^\\]]+)\\](.*)")
+
+// TODO(ribrdb): Flip this to true once everything is updated.
+const enableSecureServerAuth = false
+
type client struct {
streamMgr stream.Manager
ns naming.Namespace
@@ -228,6 +234,9 @@
// tryCall makes a single attempt at a call.
func (c *client) tryCall(ctx context.T, name, method string, args []interface{}, opts []ipc.CallOpt) (ipc.Call, verror.E) {
ctx, _ = vtrace.WithNewSpan(ctx, fmt.Sprintf("Client Call: %s.%s", name, method))
+
+ _, serverPattern, name := splitObjectName(name)
+
// Resolve name unless told not to.
var servers []string
if getNoResolveOpt(opts) {
@@ -258,7 +267,7 @@
// and thus wanted to skip authorization as well.
if flow.LocalPrincipal() != nil {
// Validate caveats on the server's identity for the context associated with this call.
- if serverB, grantedB, err = c.authorizeServer(flow, name, suffix, method, opts); err != nil {
+ if serverB, grantedB, err = c.authorizeServer(flow, name, suffix, method, serverPattern, opts); err != nil {
lastErr = verror.NoAccessf("ipc: client unwilling to invoke %q.%q on server %v: %v", name, method, flow.RemoteBlessings(), err)
flow.Close()
continue
@@ -306,17 +315,23 @@
// the RPC name.method for the client (local end of the flow). It returns the blessings at the
// server that are authorized for this purpose and any blessings that are to be granted to
// the server (via ipc.Granter implementations in opts.)
-func (c *client) authorizeServer(flow stream.Flow, name, suffix, method string, opts []ipc.CallOpt) (serverBlessings []string, grantedBlessings security.Blessings, err error) {
+func (c *client) authorizeServer(flow stream.Flow, name, suffix, method string, serverPattern security.BlessingPattern, opts []ipc.CallOpt) (serverBlessings []string, grantedBlessings security.Blessings, err error) {
if flow.RemoteBlessings() == nil {
return nil, nil, fmt.Errorf("server has not presented any blessings")
}
- serverBlessings = flow.RemoteBlessings().ForContext(serverAuthContext{flow, time.Now()})
+ ctxt := serverAuthContext{flow, time.Now()}
+ serverBlessings = flow.RemoteBlessings().ForContext(ctxt)
+ if serverPattern != "" {
+ if !serverPattern.MatchedBy(serverBlessings...) {
+ return nil, nil, fmt.Errorf("server %v does not match the provided pattern %q", serverBlessings, serverPattern)
+ }
+ } else if enableSecureServerAuth {
+ if err := (defaultAuthorizer{}).Authorize(ctxt); err != nil {
+ return nil, nil, fmt.Errorf("default authorization precludes talking to server %v", serverBlessings)
+ }
+ }
for _, o := range opts {
switch v := o.(type) {
- case options.RemoteID:
- if !security.BlessingPattern(v).MatchedBy(serverBlessings...) {
- return nil, nil, fmt.Errorf("server %v does not match the provided pattern %q", serverBlessings, v)
- }
case ipc.Granter:
if b, err := v.Grant(flow.RemoteBlessings()); err != nil {
return nil, nil, fmt.Errorf("failed to grant blessing to server %v: %v", serverBlessings, err)
@@ -580,3 +595,28 @@
func (serverAuthContext) Suffix() string { return "" }
func (serverAuthContext) Label() (l security.Label) { return l }
func (serverAuthContext) Discharges() map[string]security.Discharge { return nil }
+
+func splitObjectName(name string) (mtPattern, serverPattern security.BlessingPattern, objectName string) {
+ objectName = name
+ match := serverPatternRegexp.FindSubmatch([]byte(name))
+ if match != nil {
+ objectName = string(match[2])
+ if naming.Rooted(objectName) {
+ mtPattern = security.BlessingPattern(match[1])
+ } else {
+ serverPattern = security.BlessingPattern(match[1])
+ return
+ }
+ }
+ if !naming.Rooted(objectName) {
+ return
+ }
+
+ address, relative := naming.SplitAddressName(objectName)
+ match = serverPatternRegexp.FindSubmatch([]byte(relative))
+ if match != nil {
+ serverPattern = security.BlessingPattern(match[1])
+ objectName = naming.JoinAddressName(address, string(match[2]))
+ }
+ return
+}
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 1e0b892..cd83055 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -358,7 +358,7 @@
t.Errorf("%s: failed ot create client: %v", name, err)
continue
}
- if call, err := client.StartCall(testContext(), "mountpoint/server/suffix", "Method", nil, options.RemoteID(test.pattern)); !matchesErrorPattern(err, test.err) {
+ if call, err := client.StartCall(testContext(), fmt.Sprintf("[%s]mountpoint/server/suffix", test.pattern), "Method", nil); !matchesErrorPattern(err, test.err) {
t.Errorf(`%s: client.StartCall: got error "%v", want to match "%v"`, name, err, test.err)
} else if call != nil {
blessings, proof := call.RemoteBlessings()
@@ -1088,6 +1088,46 @@
}
}
+func TestSplitObjectName(t *testing.T) {
+ cases := []struct {
+ input, mt, server, name string
+ }{
+ {"[foo/bar]", "", "foo/bar", ""},
+ {"[x/y/...]/", "x/y/...", "", "/"},
+ {"[foo/...]//", "", "foo/...", "//"},
+ {"[foo]//abc@@/foo", "", "foo", "//abc@@/foo"},
+ {"[foo]a", "", "foo", "a"},
+ {"[foo]/a", "foo", "", "/a"},
+ {"[foo]/a/[bar]", "foo", "bar", "/a"},
+ {"a/b", "", "", "a/b"},
+ {"[foo]a/b", "", "foo", "a/b"},
+ {"/a/b", "", "", "/a/b"},
+ {"[foo]/a/b", "foo", "", "/a/b"},
+ {"/a/[bar]b", "", "bar", "/a/b"},
+ {"[foo]/a/[bar]b", "foo", "bar", "/a/b"},
+ {"/a/b[foo]", "", "", "/a/b[foo]"},
+ {"/a/b/[foo]c", "", "", "/a/b/[foo]c"},
+ {"/[01:02::]:444", "", "", "/[01:02::]:444"},
+ {"[foo]/[01:02::]:444", "foo", "", "/[01:02::]:444"},
+ {"/[01:02::]:444/foo", "", "", "/[01:02::]:444/foo"},
+ {"[a]/[01:02::]:444/foo", "a", "", "/[01:02::]:444/foo"},
+ {"/[01:02::]:444/[b]foo", "", "b", "/[01:02::]:444/foo"},
+ {"[c]/[01:02::]:444/[d]foo", "c", "d", "/[01:02::]:444/foo"},
+ }
+ for _, c := range cases {
+ mt, server, name := splitObjectName(c.input)
+ if string(mt) != c.mt {
+ t.Errorf("%q: unexpected mt pattern: %q not %q", c.input, mt, c.mt)
+ }
+ if string(server) != c.server {
+ t.Errorf("%q: unexpected server pattern: %q not %q", c.input, server, c.server)
+ }
+ if name != c.name {
+ t.Errorf("%q: unexpected name: %q not %q", c.input, name, c.name)
+ }
+ }
+}
+
func init() {
testutil.Init()
vom.Register(fakeTimeCaveat(0))