veyron/services/mgmt/node/impl: identity management for child apps (agent-less).

Implementation of principal creation/blessing for apps in the node manager.
This version does not integrate with the agent, but rather uses the local
filesystem to create and store the principal, and passes it to the app using
VeyronCredentials env.

The app principal is created upon instance creation (during Start). The
principal is blessed with (1) the blessing granted by the called of Start and
(2) a blessing derived from the node manager's default blessing (so that the
node manager can talk to the app's cycle manager; and the app can talk back to
the node manager).

Because the node manager now provides apps with credentials, we don't need to
'hack' identities into the app's envelopes in the unit test.

Change-Id: Ifea29e9177729c9fa0dba9353d90c58066278024
diff --git a/runtimes/google/rt/mgmt.go b/runtimes/google/rt/mgmt.go
index 33dc8c2..0438791 100644
--- a/runtimes/google/rt/mgmt.go
+++ b/runtimes/google/rt/mgmt.go
@@ -10,6 +10,7 @@
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/mgmt"
 	"veyron.io/veyron/veyron2/naming"
+	"veyron.io/veyron/veyron2/options"
 
 	"veyron.io/veyron/veyron/lib/exec"
 	"veyron.io/veyron/veyron/runtimes/google/appcycle"
@@ -60,8 +61,16 @@
 	if err != nil {
 		return err
 	}
+	var serverOpts []ipc.ServerOpt
+	parentPeerPattern, err := handle.Config.Get(mgmt.ParentBlessingConfigKey)
+	if err == nil && parentPeerPattern != "" {
+		// Grab the blessing from our blessing store that the parent
+		// told us to use so they can talk to us.
+		serverBlessing := rt.Principal().BlessingStore().ForPeer(parentPeerPattern)
+		serverOpts = append(serverOpts, options.ServerBlessings{serverBlessing})
+	}
 	m.rt = rt
-	m.server, err = rt.NewServer()
+	m.server, err = rt.NewServer(serverOpts...)
 	if err != nil {
 		return err
 	}
diff --git a/services/mgmt/node/impl/app_invoker.go b/services/mgmt/node/impl/app_invoker.go
index 6ff19f7..55882ae 100644
--- a/services/mgmt/node/impl/app_invoker.go
+++ b/services/mgmt/node/impl/app_invoker.go
@@ -20,9 +20,10 @@
 //       current                    - symbolic link to the current version
 //       instances/
 //         instance-<id a>/         - instances are labelled with ids
+//           credentials/           - holds veyron credentials
 //           root/                  - workspace that the instance is run from
 //           logs/                  - stderr/stdout and log files generated by instance
-//           info                   - app manager name and process id for the instance (if running)
+//           info                   - metadata for the instance (such as app cycle manager name and process id)
 //           version                - symbolic link to installation version for the instance
 //           <status>               - one of the values for instanceState enum
 //           systemname             - the system name used to execute this instance
@@ -85,8 +86,10 @@
 
 import (
 	"crypto/md5"
+	"crypto/rand"
 	"encoding/base64"
 	"encoding/binary"
+	"encoding/hex"
 	"encoding/json"
 	"fmt"
 	"hash/crc64"
@@ -104,6 +107,7 @@
 	"veyron.io/veyron/veyron2/mgmt"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/rt"
+	"veyron.io/veyron/veyron2/security"
 	"veyron.io/veyron/veyron2/services/mgmt/appcycle"
 	"veyron.io/veyron/veyron2/services/mgmt/application"
 	"veyron.io/veyron/veyron2/services/mounttable"
@@ -112,14 +116,17 @@
 	"veyron.io/veyron/veyron2/vlog"
 
 	vexec "veyron.io/veyron/veyron/lib/exec"
+	"veyron.io/veyron/veyron/lib/flags/consts"
 	"veyron.io/veyron/veyron/lib/glob"
+	vsecurity "veyron.io/veyron/veyron/security"
 	iconfig "veyron.io/veyron/veyron/services/mgmt/node/config"
 )
 
 // instanceInfo holds state about a running instance.
 type instanceInfo struct {
-	AppCycleMgrName string
-	Pid             int
+	AppCycleMgrName        string
+	Pid                    int
+	NodeManagerPeerPattern string
 }
 
 func saveInstanceInfo(dir string, info *instanceInfo) error {
@@ -217,6 +224,16 @@
 	return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=")
 }
 
+// generateRandomString returns a cryptographically-strong random string.
+func generateRandomString() (string, error) {
+	b := make([]byte, 16)
+	_, err := rand.Read(b)
+	if err != nil {
+		return "", err
+	}
+	return hex.EncodeToString(b), nil
+}
+
 // TODO(caprita): Nothing prevents different applications from sharing the same
 // title, and thereby being installed in the same app dir.  Do we want to
 // prevent that for the same user or across users?
@@ -361,8 +378,84 @@
 	return installationDir, nil
 }
 
+// setupIdentity sets up the instance's principal, with the right blessings.
+func setupIdentity(instanceDir, versionDir string, call ipc.ServerContext, info *instanceInfo) error {
+	credentialsDir := filepath.Join(instanceDir, "credentials")
+	// TODO(caprita): The app's system user id needs access to this dir.
+	// Use the suidhelper to chown it.
+	p, err := vsecurity.CreatePersistentPrincipal(credentialsDir, nil)
+	if err != nil {
+		vlog.Errorf("CreatePersistentPrincipal(%v, nil) failed: %v", credentialsDir, err)
+		return errOperationFailed
+	}
+	// Read the app installation version's envelope to obtain the app title.
+	//
+	// NOTE: we could have gotten this from the suffix as well, but the
+	// format of the object name suffix may change in the future: there's no
+	// guarantee it will always include the title.
+	envelope, err := loadEnvelope(versionDir)
+	if err != nil {
+		return err
+	}
+	nmPrincipal := call.LocalPrincipal()
+	// Take the blessings conferred upon us by the Start-er, extend them
+	// with the app title.
+	// TODO(caprita): Revisit UnconstrainedUse.
+	appBlessings, err := nmPrincipal.Bless(p.PublicKey(), call.Blessings(), envelope.Title, security.UnconstrainedUse())
+	if err != nil {
+		vlog.Errorf("Bless() failed: %v", err)
+		return errOperationFailed
+	}
+	// The blessings we extended from the blessings that the Start-er
+	// granted are the default blessings for the app.
+	if err := p.BlessingStore().SetDefault(appBlessings); err != nil {
+		vlog.Errorf("BlessingStore.SetDefault() failed: %v", err)
+		return errOperationFailed
+	}
+	if _, err := p.BlessingStore().Set(appBlessings, security.AllPrincipals); err != nil {
+		vlog.Errorf("BlessingStore.Set() failed: %v", err)
+		return errOperationFailed
+	}
+	if err := p.AddToRoots(appBlessings); err != nil {
+		vlog.Errorf("AddToRoots() failed: %v", err)
+		return errOperationFailed
+	}
+	// In addition, we give the app separate blessings for the purpose of
+	// communicating with the node manager.
+	nmBlessings, err := nmPrincipal.Bless(p.PublicKey(), nmPrincipal.BlessingStore().Default(), "callback", security.UnconstrainedUse())
+	// Put the names of the node manager's default blessings as patterns for
+	// the child, so that the child uses the right blessing when talking
+	// back to the node manager.
+	names := nmPrincipal.BlessingStore().Default().ForContext(call)
+	for _, n := range names {
+		if _, err := p.BlessingStore().Set(nmBlessings, security.BlessingPattern(n)); err != nil {
+			vlog.Errorf("BlessingStore.Set() failed: %v", err)
+			return errOperationFailed
+		}
+	}
+	// We also want to override the app cycle manager's server blessing in
+	// the child (so that the node manager can send RPCs to it).  We signal
+	// to the child's app manager to use a randomly generated pattern to
+	// extract the right blessing to use from its store for this purpose.
+	randomPattern, err := generateRandomString()
+	if err != nil {
+		vlog.Errorf("generateRandomString() failed: %v", err)
+		return errOperationFailed
+	}
+	if _, err := p.BlessingStore().Set(nmBlessings, security.BlessingPattern(randomPattern)); err != nil {
+		vlog.Errorf("BlessingStore.Set() failed: %v", err)
+		return errOperationFailed
+	}
+	info.NodeManagerPeerPattern = randomPattern
+	if err := p.AddToRoots(nmBlessings); err != nil {
+		vlog.Errorf("AddToRoots() failed: %v", err)
+		return errOperationFailed
+	}
+	return nil
+}
+
 // newInstance sets up the directory for a new application instance.
-func (i *appInvoker) newInstance() (string, string, error) {
+func (i *appInvoker) newInstance(call ipc.ServerContext) (string, string, error) {
 	installationDir, err := i.installationDir()
 	if err != nil {
 		return "", "", err
@@ -386,6 +479,13 @@
 		vlog.Errorf("Symlink(%v, %v) failed: %v", versionDir, versionLink, err)
 		return instanceDir, instanceID, errOperationFailed
 	}
+	instanceInfo := new(instanceInfo)
+	if err := setupIdentity(instanceDir, versionDir, call, instanceInfo); err != nil {
+		return instanceDir, instanceID, err
+	}
+	if err := saveInstanceInfo(instanceDir, instanceInfo); err != nil {
+		return instanceDir, instanceID, err
+	}
 	if err := initializeInstance(instanceDir, suspended); err != nil {
 		return instanceDir, instanceID, err
 	}
@@ -440,8 +540,6 @@
 	return "", errOperationFailed
 }
 
-// TODO(rjkroege): Turning on the setuid feature of the suidhelper
-// requires an installer with root permissions to install it in <config.Root>/helper
 func genCmd(instanceDir, helperPath, systemName string) (*exec.Cmd, error) {
 	versionLink := filepath.Join(instanceDir, "version")
 	versionDir, err := filepath.EvalSymlinks(versionLink)
@@ -465,6 +563,7 @@
 	// TODO(caprita): Also pass in configuration info like NAMESPACE_ROOT to
 	// the app (to point to the device mounttable).
 	cmd.Env = envelope.Env
+	cmd.Env = append(cmd.Env, consts.VeyronCredentials+"="+filepath.Join(instanceDir, "credentials"))
 	rootDir := filepath.Join(instanceDir, "root")
 	if err := mkdir(rootDir); err != nil {
 		return nil, err
@@ -497,6 +596,10 @@
 }
 
 func (i *appInvoker) startCmd(instanceDir string, cmd *exec.Cmd) error {
+	info, err := loadInstanceInfo(instanceDir)
+	if err != nil {
+		return err
+	}
 	// Setup up the child process callback.
 	callbackState := i.callback
 	listener := callbackState.listenFor(mgmt.AppCycleManagerConfigKey)
@@ -505,6 +608,7 @@
 	cfg.Set(mgmt.ParentNameConfigKey, listener.name())
 	cfg.Set(mgmt.ProtocolConfigKey, "tcp")
 	cfg.Set(mgmt.AddressConfigKey, "127.0.0.1:0")
+	cfg.Set(mgmt.ParentBlessingConfigKey, info.NodeManagerPeerPattern)
 	handle := vexec.NewParentHandle(cmd, vexec.ConfigOpt{cfg})
 	defer func() {
 		if handle != nil {
@@ -528,11 +632,8 @@
 	if err != nil {
 		return errOperationFailed
 	}
-	instanceInfo := &instanceInfo{
-		AppCycleMgrName: childName,
-		Pid:             handle.Pid(),
-	}
-	if err := saveInstanceInfo(instanceDir, instanceInfo); err != nil {
+	info.AppCycleMgrName, info.Pid = childName, handle.Pid()
+	if err := saveInstanceInfo(instanceDir, info); err != nil {
 		return err
 	}
 	// TODO(caprita): Spin up a goroutine to reap child status upon exit and
@@ -559,7 +660,7 @@
 
 func (i *appInvoker) Start(call ipc.ServerContext) ([]string, error) {
 	helper := i.config.Helper
-	instanceDir, instanceID, err := i.newInstance()
+	instanceDir, instanceID, err := i.newInstance(call)
 	if err != nil {
 		cleanupDir(instanceDir, helper)
 		return nil, err
diff --git a/services/mgmt/node/impl/impl_test.go b/services/mgmt/node/impl/impl_test.go
index 8f44059..9b1f878 100644
--- a/services/mgmt/node/impl/impl_test.go
+++ b/services/mgmt/node/impl/impl_test.go
@@ -312,7 +312,7 @@
 	// convenient to put it there so we have everything in one place.
 	currLink := filepath.Join(root, "current_link")
 
-	crDir, crEnv := credentialsForChild("anyvalue")
+	crDir, crEnv := credentialsForChild("nodemanager")
 	defer os.RemoveAll(crDir)
 	nmArgs := []string{"factoryNM", root, "unused_helper", mockApplicationRepoName, currLink}
 	args, env := sh.CommandEnvelope(nodeManagerCmd, crEnv, nmArgs...)
@@ -350,7 +350,7 @@
 
 	// Set up a second version of the node manager. The information in the
 	// envelope will be used by the node manager to stage the next version.
-	crDir, crEnv = credentialsForChild("anyvalue")
+	crDir, crEnv = credentialsForChild("nodemanager")
 	defer os.RemoveAll(crDir)
 	*envelope = envelopeFromShell(sh, crEnv, nodeManagerCmd, application.NodeManagerTitle, "v2NM")
 	updateNode(t, "factoryNM")
@@ -398,7 +398,7 @@
 	}
 
 	// Create a third version of the node manager and issue an update.
-	crDir, crEnv = credentialsForChild("anyvalue")
+	crDir, crEnv = credentialsForChild("nodemanager")
 	defer os.RemoveAll(crDir)
 	*envelope = envelopeFromShell(sh, crEnv, nodeManagerCmd, application.NodeManagerTitle,
 		"v3NM")
@@ -462,9 +462,9 @@
 	nms.ExpectEOF()
 }
 
-type pingServerDisp chan<- string
+type pingServer chan<- string
 
-func (p pingServerDisp) Ping(_ ipc.ServerCall, arg string) {
+func (p pingServer) Ping(_ ipc.ServerCall, arg string) {
 	p <- arg
 }
 
@@ -474,7 +474,7 @@
 func setupPingServer(t *testing.T) (<-chan string, func()) {
 	server, _ := newServer()
 	pingCh := make(chan string, 1)
-	if err := server.Serve("pingserver", pingServerDisp(pingCh), nil); err != nil {
+	if err := server.Serve("pingserver", pingServer(pingCh), nil); err != nil {
 		t.Fatalf("Serve(%q, <dispatcher>) failed: %v", "pingserver", err)
 	}
 	return pingCh, func() {
@@ -546,7 +546,7 @@
 	// Create a script wrapping the test target that implements suidhelper.
 	helperPath := generateSuidHelperScript(t, root)
 
-	crDir, crEnv := credentialsForChild("anyvalue")
+	crDir, crEnv := credentialsForChild("nodemanager")
 	defer os.RemoveAll(crDir)
 
 	// Set up the node manager.  Since we won't do node manager updates,
@@ -560,10 +560,8 @@
 
 	resolve(t, "pingserver", 1)
 
-	crDir, crEnv = credentialsForChild("anyvalue")
-	defer os.RemoveAll(crDir)
 	// Create an envelope for a first version of the app.
-	*envelope = envelopeFromShell(sh, crEnv, appCmd, "google naps", "appV1")
+	*envelope = envelopeFromShell(sh, nil, appCmd, "google naps", "appV1")
 
 	// Install the app.
 	appID := installApp(t)
@@ -623,9 +621,7 @@
 	updateAppExpectError(t, appID, verror.BadArg)
 
 	// Create a second version of the app and update the app to it.
-	crDir, crEnv = credentialsForChild("anyvalue")
-	defer os.RemoveAll(crDir)
-	*envelope = envelopeFromShell(sh, crEnv, appCmd, "google naps", "appV2")
+	*envelope = envelopeFromShell(sh, nil, appCmd, "google naps", "appV2")
 
 	updateApp(t, appID)
 
@@ -704,16 +700,6 @@
 	nms.ExpectEOF()
 }
 
-type granter struct {
-	ipc.CallOpt
-	p         security.Principal
-	extension string
-}
-
-func (g *granter) Grant(other security.Blessings) (security.Blessings, error) {
-	return g.p.Bless(other.PublicKey(), g.p.BlessingStore().Default(), g.extension, security.UnconstrainedUse())
-}
-
 func newRuntime(t *testing.T) veyron2.Runtime {
 	runtime, err := rt.New()
 	if err != nil {
@@ -744,7 +730,7 @@
 	root, cleanup := setupRootDir(t)
 	defer cleanup()
 
-	crDir, crEnv := credentialsForChild("anyvalue")
+	crDir, crEnv := credentialsForChild("nodemanager")
 	defer os.RemoveAll(crDir)
 
 	// Create a script wrapping the test target that implements suidhelper.
@@ -756,9 +742,7 @@
 	pid := readPID(t, nms)
 	defer syscall.Kill(pid, syscall.SIGINT)
 
-	crDir, crEnv = credentialsForChild("mydevice/anyvalue")
-	defer os.RemoveAll(crDir)
-	*envelope = envelopeFromShell(sh, crEnv, appCmd, "google naps", "trapp")
+	*envelope = envelopeFromShell(sh, nil, appCmd, "google naps", "trapp")
 
 	nodeStub := node.NodeClient("nm//nm")
 	selfRT := rt.R()
@@ -831,7 +815,7 @@
 		t.Fatal(err)
 	}
 
-	crDir, crEnv := credentialsForChild("anyvalue")
+	crDir, crEnv := credentialsForChild("nodemanager")
 	defer os.RemoveAll(crDir)
 
 	// Set up the node manager.  Since we won't do node manager updates,
@@ -841,9 +825,7 @@
 	defer syscall.Kill(pid, syscall.SIGINT)
 
 	// Create an envelope for an app.
-	crDir, crEnv = credentialsForChild("anyvalue")
-	defer os.RemoveAll(crDir)
-	*envelope = envelopeFromShell(sh, crEnv, appCmd, "google naps")
+	*envelope = envelopeFromShell(sh, nil, appCmd, "google naps")
 
 	nodeStub := node.NodeClient("nm//nm")
 	acl, etag, err := nodeStub.GetACL(selfRT.NewContext())
@@ -959,7 +941,7 @@
 	root, cleanup := setupRootDir(t)
 	defer cleanup()
 
-	crDir, crEnv := credentialsForChild("anyvalue")
+	crDir, crEnv := credentialsForChild("nodemanager")
 	defer os.RemoveAll(crDir)
 
 	// Create a script wrapping the test target that implements suidhelper.
@@ -976,7 +958,7 @@
 	defer cleanup()
 
 	// Create the envelope for the first version of the app.
-	*envelope = envelopeFromShell(sh, crEnv, appCmd, "google naps", "appV1")
+	*envelope = envelopeFromShell(sh, nil, appCmd, "google naps", "appV1")
 
 	// Install the app.
 	appID := installApp(t)
@@ -1153,7 +1135,7 @@
 	if err := idp.Bless(otherRT.Principal(), "other"); err != nil {
 		t.Fatal(err)
 	}
-	crFile, crEnv := credentialsForChild("anyvalue")
+	crFile, crEnv := credentialsForChild("nodemanager")
 	defer os.RemoveAll(crFile)
 
 	_, nms := runShellCommand(t, sh, crEnv, nodeManagerCmd, "nm", root, "unused_helper", "unused_app_repo_name", "unused_curr_link")
@@ -1259,7 +1241,7 @@
 		t.Fatal(err)
 	}
 
-	crDir, crEnv := credentialsForChild("anyvalue")
+	crDir, crEnv := credentialsForChild("nodemanager")
 	defer os.RemoveAll(crDir)
 
 	// Create a script wrapping the test target that implements
@@ -1277,15 +1259,12 @@
 	server, _ := newServer()
 	defer server.Stop()
 	pingCh := make(chan string, 1)
-	if err := server.Serve("pingserver", pingServerDisp(pingCh), nil); err != nil {
+	if err := server.Serve("pingserver", pingServer(pingCh), nil); err != nil {
 		t.Fatalf("Serve(%q, <dispatcher>) failed: %v", "pingserver", err)
 	}
 
-	// Create an envelope for a first version of the app with an
-	// appropriate blessing.
-	crDir, crEnv = credentialsForChild("alice/child")
-	defer os.RemoveAll(crDir)
-	*envelope = envelopeFromShell(sh, crEnv, appCmd, "google naps", "appV1")
+	// Create an envelope for a first version of the app.
+	*envelope = envelopeFromShell(sh, nil, appCmd, "google naps", "appV1")
 
 	// Install and start the app as root/self.
 	appID := installApp(t, selfRT)
diff --git a/services/mgmt/node/impl/util_test.go b/services/mgmt/node/impl/util_test.go
index 00762a5..b9b9ad3 100644
--- a/services/mgmt/node/impl/util_test.go
+++ b/services/mgmt/node/impl/util_test.go
@@ -15,6 +15,7 @@
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/rt"
+	"veyron.io/veyron/veyron2/security"
 	"veyron.io/veyron/veyron2/services/mgmt/node"
 	"veyron.io/veyron/veyron2/verror"
 	"veyron.io/veyron/veyron2/vlog"
@@ -23,7 +24,7 @@
 	"veyron.io/veyron/veyron/lib/flags/consts"
 	"veyron.io/veyron/veyron/lib/modules"
 	"veyron.io/veyron/veyron/lib/modules/core"
-	"veyron.io/veyron/veyron/lib/testutil/security"
+	tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
 	"veyron.io/veyron/veyron/profiles/static"
 	"veyron.io/veyron/veyron/services/mgmt/node/impl"
 	"veyron.io/veyron/veyron2/services/mgmt/application"
@@ -53,7 +54,7 @@
 }
 
 func credentialsForChild(blessing string) (string, []string) {
-	creds := security.NewVeyronCredentials(rt.R().Principal(), blessing)
+	creds := tsecurity.NewVeyronCredentials(rt.R().Principal(), blessing)
 	return creds, []string{consts.VeyronCredentials + "=" + creds}
 }
 
@@ -229,8 +230,18 @@
 	return appID
 }
 
+type granter struct {
+	ipc.CallOpt
+	p         security.Principal
+	extension string
+}
+
+func (g *granter) Grant(other security.Blessings) (security.Blessings, error) {
+	return g.p.Bless(other.PublicKey(), g.p.BlessingStore().Default(), g.extension, security.UnconstrainedUse())
+}
+
 func startAppImpl(t *testing.T, appID string, opt []veyron2.Runtime) (string, error) {
-	if instanceIDs, err := appStub(appID).Start(ort(opt).NewContext()); err != nil {
+	if instanceIDs, err := appStub(appID).Start(ort(opt).NewContext(), &granter{p: ort(opt).Principal(), extension: "forapp"}); err != nil {
 		return "", err
 	} else {
 		if want, got := 1, len(instanceIDs); want != got {