services/proxy/proxyd: Implement access control at the proxy server.

This change makes proxyd support access control by allowing only servers
that present a blessing that matches AccessList to export themselves
through the proxy.

The change itself is backward compatible, if proxyd built with this
change is started with --access-list='{"In":["..."]}'. However,
my intention is to have this in before the release, so all servers
that use the proxy will already be using this new code.

I admit that I went for a bit of "speed of implementation" over elegance
in an attempt to get this in before we release publicly. I'm happy to do
cleanups after this is in.

MultiPart: 1/3

Closes vanadium/issues#33

Change-Id: Ic6413403f6bbca9c7b2bd330910a5c3e2adb0ef3
diff --git a/profiles/internal/rpc/server.go b/profiles/internal/rpc/server.go
index 102f82a..770b113 100644
--- a/profiles/internal/rpc/server.go
+++ b/profiles/internal/rpc/server.go
@@ -34,6 +34,7 @@
 	"v.io/x/ref/profiles/internal/lib/publisher"
 	inaming "v.io/x/ref/profiles/internal/naming"
 	"v.io/x/ref/profiles/internal/rpc/stream"
+	"v.io/x/ref/profiles/internal/rpc/stream/manager"
 	"v.io/x/ref/profiles/internal/rpc/stream/vc"
 )
 
@@ -88,6 +89,7 @@
 	state             serverState          // track state of the server.
 	streamMgr         stream.Manager       // stream manager to listen for new flows.
 	publisher         publisher.Publisher  // publisher to publish mounttable mounts.
+	dc                vc.DischargeClient   // fetches discharges of blessings
 	listenerOpts      []stream.ListenerOpt // listener opts for Listen.
 	settingsPublisher *pubsub.Publisher    // pubsub publisher for dhcp
 	settingsName      string               // pubwsub stream name for dhcp
@@ -234,8 +236,8 @@
 	}
 	// Make dischargeExpiryBuffer shorter than the VC discharge buffer to ensure we have fetched
 	// the discharges by the time the VC asks for them.`
-	dc := InternalNewDischargeClient(ctx, client, dischargeExpiryBuffer-(5*time.Second))
-	s.listenerOpts = append(s.listenerOpts, dc)
+	s.dc = InternalNewDischargeClient(ctx, client, dischargeExpiryBuffer-(5*time.Second))
+	s.listenerOpts = append(s.listenerOpts, s.dc)
 	s.listenerOpts = append(s.listenerOpts, vc.DialContext{ctx})
 	blessingsStatsName := naming.Join(statsPrefix, "security", "blessings")
 	// TODO(caprita): revist printing the blessings with %s, and
@@ -464,7 +466,8 @@
 	if err != nil {
 		return nil, nil, verror.New(errFailedToResolveProxy, s.ctx, proxy, err)
 	}
-	ln, ep, err := s.streamMgr.Listen(inaming.Network, resolved, s.principal, s.blessings, s.listenerOpts...)
+	opts := append([]stream.ListenerOpt{proxyAuth{s}}, s.listenerOpts...)
+	ln, ep, err := s.streamMgr.Listen(inaming.Network, resolved, s.principal, s.blessings, opts...)
 	if err != nil {
 		return nil, nil, verror.New(errFailedToListenForProxy, s.ctx, resolved, err)
 	}
@@ -1334,3 +1337,41 @@
 	//nologcall
 	return fs.flow.RemoteEndpoint()
 }
+
+type proxyAuth struct {
+	s *server
+}
+
+func (a proxyAuth) RPCStreamListenerOpt() {}
+
+func (a proxyAuth) Login(proxy stream.Flow) (security.Blessings, []security.Discharge, error) {
+	var (
+		principal = a.s.principal
+		dc        = a.s.dc
+		ctx       = a.s.ctx
+	)
+	if principal == nil {
+		return security.Blessings{}, nil, nil
+	}
+	proxyNames, _ := security.RemoteBlessingNames(ctx, security.NewCall(&security.CallParams{
+		LocalPrincipal:   principal,
+		RemoteBlessings:  proxy.RemoteBlessings(),
+		RemoteDischarges: proxy.RemoteDischarges(),
+		RemoteEndpoint:   proxy.RemoteEndpoint(),
+		LocalEndpoint:    proxy.LocalEndpoint(),
+	}))
+	blessings := principal.BlessingStore().ForPeer(proxyNames...)
+	tpc := blessings.ThirdPartyCaveats()
+	if len(tpc) == 0 {
+		return blessings, nil, nil
+	}
+	// Ugh! Have to convert from proxyNames to BlessingPatterns
+	proxyPats := make([]security.BlessingPattern, len(proxyNames))
+	for idx, n := range proxyNames {
+		proxyPats[idx] = security.BlessingPattern(n)
+	}
+	discharges := dc.PrepareDischarges(ctx, tpc, security.DischargeImpetus{Server: proxyPats})
+	return blessings, discharges, nil
+}
+
+var _ manager.ProxyAuthenticator = proxyAuth{}