Merge "veyron/services/mgmt/node/impl: identity management for child apps (agent-less)."
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 {