veyron2/ipc: Support for granting credentials to servers in the form of blessings.

This change makes it possible for a client to provide additional credentials,
bound to the identity of the server, in the form of a blessed identity to
a server when making the request.

The server can access this credential via ipc.Context.Blessing.

Change-Id: I67dde80b7038b0ebb9b27f4917ae218b62ec13f0
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index 16a711c..d974020 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -1,6 +1,7 @@
 package ipc
 
 import (
+	"fmt"
 	"io"
 	"sync"
 	"time"
@@ -141,32 +142,15 @@
 		}
 
 		// Validate caveats on the server's identity for the context associated with this call.
-		remoteID := flow.RemoteID()
-		if remoteID == nil {
-			lastErr = verror.NotAuthorizedf("ipc: server identity cannot be nil")
-			continue
-		}
-		// TODO(ataly): Fetch third-party discharges from the server.
-		// TODO(ataly): What should the label be for the context? Typically the label is the security.Label
-		// of the method but we don't have that information here at the client.
-		authorizedRemoteID, err := remoteID.Authorize(isecurity.NewContext(
-			isecurity.ContextArgs{
-				LocalID:  flow.LocalID(),
-				RemoteID: remoteID,
-			}))
+		blessing, err := authorizeServer(flow.LocalID(), flow.RemoteID(), opts)
 		if err != nil {
-			lastErr = verror.NotAuthorizedf("ipc: server identity %q has one or more invalid caveats: %v", remoteID, err)
+			lastErr = verror.NotAuthorizedf("ipc: client unwilling to talk to server %q: %v", flow.RemoteID(), err)
+			flow.Close()
 			continue
 		}
-
-		if lastErr = matchServerID(authorizedRemoteID, opts); lastErr != nil {
-			continue
-		}
-
-		// remoteID is authorized for the context associated with this call.
 		lastErr = nil
 		fc := newFlowClient(flow)
-		if verr := fc.start(suffix, method, args, timeout); verr != nil {
+		if verr := fc.start(suffix, method, args, timeout, blessing); verr != nil {
 			return nil, verr
 		}
 		return fc, nil
@@ -177,6 +161,45 @@
 	return nil, errNoServers
 }
 
+// authorizeServer validates that server has an identity that the client is willing to converse
+// with, and if so returns a blessing to be provided to the server. This blessing can be nil,
+// which indicates that the client does wish to talk to the server but not provide any blessings.
+func authorizeServer(client, server security.PublicID, opts []ipc.CallOpt) (security.PublicID, error) {
+	if server == nil {
+		return nil, fmt.Errorf("server identity cannot be nil")
+	}
+	// TODO(ataly): Fetch third-party discharges from the server.
+	// TODO(ataly): What should the label be for the context? Typically the label is the security.Label
+	// of the method but we don't have that information here at the client.
+	authID, err := server.Authorize(isecurity.NewContext(isecurity.ContextArgs{
+		LocalID:  client,
+		RemoteID: server,
+	}))
+	if err != nil {
+		return nil, err
+	}
+	var granter ipc.Granter
+	for _, o := range opts {
+		switch v := o.(type) {
+		case veyron2.RemoteID:
+			if !authID.Match(security.PrincipalPattern(v)) {
+				return nil, fmt.Errorf("server %q does not match the provided pattern %q", authID, v)
+			}
+		case ipc.Granter:
+			// Later Granters take precedence over earlier ones.
+			// Or should fail if there are multiple provided?
+			granter = v
+		}
+	}
+	var blessing security.PublicID
+	if granter != nil {
+		if blessing, err = granter.Grant(authID); err != nil {
+			return nil, fmt.Errorf("failed to grant credentials to server %q: %v", authID, err)
+		}
+	}
+	return blessing, nil
+}
+
 func (c *client) getCallTimeout(opts []ipc.CallOpt) time.Duration {
 	timeout := c.callTimeout
 	for _, opt := range opts {
@@ -229,16 +252,22 @@
 	return verr
 }
 
-func (fc *flowClient) start(suffix, method string, args []interface{}, timeout time.Duration) verror.E {
+func (fc *flowClient) start(suffix, method string, args []interface{}, timeout time.Duration, blessing security.PublicID) verror.E {
 	req := ipc.Request{
-		Suffix:     suffix,
-		Method:     method,
-		NumPosArgs: uint64(len(args)),
-		Timeout:    int64(timeout),
+		Suffix:      suffix,
+		Method:      method,
+		NumPosArgs:  uint64(len(args)),
+		Timeout:     int64(timeout),
+		HasBlessing: blessing != nil,
 	}
 	if err := fc.enc.Encode(req); err != nil {
 		return fc.close(verror.BadProtocolf("ipc: request encoding failed: %v", err))
 	}
+	if blessing != nil {
+		if err := fc.enc.Encode(blessing); err != nil {
+			return fc.close(verror.BadProtocolf("ipc: blessing encoding failed: %v", err))
+		}
+	}
 	for ix, arg := range args {
 		if err := fc.enc.Encode(arg); err != nil {
 			return fc.close(verror.BadProtocolf("ipc: arg %d encoding failed: %v", ix, err))
@@ -357,12 +386,3 @@
 func (fc *flowClient) Cancel() {
 	fc.flow.Cancel()
 }
-
-func matchServerID(id security.PublicID, opts []ipc.CallOpt) verror.E {
-	for _, opt := range opts {
-		if pattern, ok := opt.(veyron2.RemoteID); ok && !id.Match(security.PrincipalPattern(pattern)) {
-			return verror.NotAuthorizedf("ipc: server identity %q does not have a name matching the provided pattern %q", id, pattern)
-		}
-	}
-	return nil
-}
diff --git a/runtimes/google/ipc/flow_test.go b/runtimes/google/ipc/flow_test.go
index 0a60b99..35f7662 100644
--- a/runtimes/google/ipc/flow_test.go
+++ b/runtimes/google/ipc/flow_test.go
@@ -122,7 +122,7 @@
 		clientFlow, serverFlow := newTestFlows()
 		client := newFlowClient(clientFlow)
 		server := newFlowServer(serverFlow, ipcServer)
-		err := client.start(test.suffix, test.method, test.args, time.Duration(0))
+		err := client.start(test.suffix, test.method, test.args, time.Duration(0), nil)
 		if err != nil {
 			t.Errorf("%s client.start unexpected error: %v", name(test), err)
 		}
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 4b53246..89af1cd 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -63,6 +63,10 @@
 	return fmt.Sprintf("%v", call.LocalID()), fmt.Sprintf("%v", call.RemoteID())
 }
 
+func (*testServer) EchoBlessing(call ipc.ServerCall, arg string) (result, blessing string) {
+	return arg, fmt.Sprintf("%v", call.Blessing())
+}
+
 func (*testServer) EchoAndError(call ipc.ServerCall, arg string) (string, error) {
 	result := fmt.Sprintf("method:%q,suffix:%q,arg:%q", call.Method(), call.Suffix(), arg)
 	if arg == "error" {
@@ -278,16 +282,20 @@
 	return
 }
 
+func bless(blessor security.PrivateID, blessee security.PublicID, name string, caveats ...security.ServiceCaveat) security.PublicID {
+	blessed, err := blessor.Bless(blessee, name, 24*time.Hour, caveats)
+	if err != nil {
+		panic(err)
+	}
+	return blessed
+}
+
 func derive(blessor security.PrivateID, name string, caveats ...security.ServiceCaveat) security.PrivateID {
 	id, err := isecurity.NewPrivateID("irrelevant")
 	if err != nil {
 		panic(err)
 	}
-	blessedID, err := blessor.Bless(id.PublicID(), name, 5*time.Minute, caveats)
-	if err != nil {
-		panic(err)
-	}
-	derivedID, err := id.Derive(blessedID)
+	derivedID, err := id.Derive(bless(blessor, id.PublicID(), name, caveats...))
 	if err != nil {
 		panic(err)
 	}
@@ -302,8 +310,8 @@
 }
 
 func TestStartCall(t *testing.T) {
-	authorizeErr := "has one or more invalid caveats"
-	nameErr := "does not have a name matching the provided pattern"
+	authorizeErr := "not authorized because"
+	nameErr := "does not match the provided pattern"
 
 	cavOnlyV1 := security.UniversalCaveat(caveat.PeerIdentity{"client/v1"})
 	now := time.Now()
@@ -435,6 +443,49 @@
 	}
 }
 
+// granter implements ipc.Granter, returning a fixed (security.PublicID, error) pair.
+type granter struct {
+	ipc.CallOpt
+	id  security.PublicID
+	err error
+}
+
+func (g granter) Grant(id security.PublicID) (security.PublicID, error) { return g.id, g.err }
+
+func TestBlessing(t *testing.T) {
+	b := createBundle(t, clientID, serverID, &testServer{})
+	defer b.cleanup(t)
+
+	tests := []struct {
+		granter                       ipc.CallOpt
+		blessing, starterr, finisherr string
+	}{
+		{blessing: "<nil>"},
+		{granter: granter{id: bless(clientID, serverID.PublicID(), "blessed")}, blessing: "client/blessed"},
+		{granter: granter{err: errors.New("hell no")}, starterr: "hell no"},
+		{granter: granter{id: clientID.PublicID()}, finisherr: "blessing provided not bound to this server"},
+	}
+	for _, test := range tests {
+		call, err := b.client.StartCall(&fakeContext{}, "mountpoint/server/suffix", "EchoBlessing", []interface{}{"argument"}, test.granter)
+		if !matchesErrorPattern(err, test.starterr) {
+			t.Errorf("%+v: StartCall returned error %v", test, err)
+		}
+		if err != nil {
+			continue
+		}
+		var result, blessing string
+		if err = call.Finish(&result, &blessing); !matchesErrorPattern(err, test.finisherr) {
+			t.Errorf("%+v: Finish returned error %v", test, err)
+		}
+		if err != nil {
+			continue
+		}
+		if result != "argument" || blessing != test.blessing {
+			t.Errorf("%+v: Got (%q, %q)", test, result, blessing)
+		}
+	}
+}
+
 func TestRPCAuthorization(t *testing.T) {
 	cavOnlyEcho := security.ServiceCaveat{
 		Service: security.AllPrincipals,
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index cb406cb..222bdac 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -4,6 +4,7 @@
 	"fmt"
 	"io"
 	"net"
+	"reflect"
 	"strings"
 	"sync"
 	"time"
@@ -245,6 +246,7 @@
 	// authorizedRemoteID is the PublicID obtained after authorizing the remoteID
 	// of the underlying flow for the current request context.
 	authorizedRemoteID   security.PublicID
+	blessing             security.PublicID
 	method, name, suffix string
 	label                security.Label
 	discharges           security.CaveatDischargeMap
@@ -363,6 +365,20 @@
 			return nil, verr
 		}
 	}
+	// If additional credentials are provided, make them available in the context
+	if req.HasBlessing {
+		if err := fs.dec.Decode(&fs.blessing); err != nil {
+			return nil, verror.BadProtocolf("ipc: blessing decoding failed: %v", err)
+		}
+		// Detect unusable blessings now, rather then discovering they are unusable on first use.
+		if !reflect.DeepEqual(fs.blessing.PublicKey(), fs.flow.LocalID().PublicKey()) {
+			return nil, verror.BadProtocolf("ipc: blessing provided not bound to this server")
+		}
+		// TODO(ashankar,ataly): Potential confused deputy attack: The client provides 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?
+	}
 	// Lookup the invoker.
 	invoker, auth, name, suffix, verr := fs.lookup(req.Suffix)
 	fs.name = name
@@ -484,6 +500,7 @@
 func (fs *flowServer) LocalID() security.PublicID                    { return fs.flow.LocalID() }
 func (fs *flowServer) RemoteID() security.PublicID                   { return fs.authorizedRemoteID }
 func (fs *flowServer) Deadline() time.Time                           { return fs.deadline }
+func (fs *flowServer) Blessing() security.PublicID                   { return fs.blessing }
 func (fs *flowServer) LocalAddr() net.Addr                           { return fs.flow.LocalAddr() }
 func (fs *flowServer) RemoteAddr() net.Addr                          { return fs.flow.RemoteAddr() }