veyron/lib/modules: Use the agent for managing credentials in modules.Shell.

Change-Id: I17737af8d4fe2281d2f0e5a267cbff268758f788
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index 0c6a5eb..ecaefde 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -29,20 +29,10 @@
 // In particular stdin, stdout and stderr are provided as parameters, as is
 // a map representation of the shell's environment.
 //
-// Every Shell created by NewShell is initialized with its VeyronCredentials
-// environment variable set. The variable is set to the os's VeyronCredentials
-// if that is set, otherwise to a freshly created credentials directory. The
-// shell's VeyronCredentials can be set and cleared using the SetVar and
-// ClearVar methods respectively.
-//
-// By default, the VeyronCredentials for each command Start-ed by the shell are
-// set to a freshly created credentials directory that is blessed by the shell's
-// credentials (i.e., if shell's credentials have not been cleared). Thus, each
-// child of the shell gets its own credentials directory with a blessing from the
-// shell of the form
-//   <shell's default blessing>/child
-// These default credentials provided by the shell to each command can be
-// overridden by specifying VeyronCredentials in the environment provided as a
+// By default, every Shell created by NewShell starts a security agent
+// to manage principals for child processes.  These default
+// credentials can be overridden by passing a nil context to NewShell
+// then specifying VeyronCredentials in the environment provided as a
 // parameter to the Start method.
 package modules
 
@@ -53,13 +43,17 @@
 	"math/rand"
 	"os"
 	"sync"
+	"syscall"
 	"time"
 
 	"v.io/core/veyron2/security"
 
 	"v.io/core/veyron/lib/exec"
 	"v.io/core/veyron/lib/flags/consts"
-	vsecurity "v.io/core/veyron/security"
+	"v.io/core/veyron/security/agent"
+	"v.io/core/veyron/security/agent/keymgr"
+	"v.io/core/veyron2"
+	"v.io/core/veyron2/context"
 )
 
 const (
@@ -72,38 +66,55 @@
 	mu      sync.Mutex
 	env     map[string]string
 	handles map[Handle]struct{}
-	// tmpCredDirs are the temporary directories created by this
-	// shell. These must be removed when the shell is cleaned up.
-	tempCredDirs              []string
+	// tmpCredDir is the temporary directory created by this
+	// shell. This must be removed when the shell is cleaned up.
+	tempCredDir               string
 	startTimeout, waitTimeout time.Duration
 	config                    exec.Config
 	principal                 security.Principal
 	blessing                  security.Blessings
+	agent                     *keymgr.Agent
+	ctx                       *context.T
+	cancelCtx                 func()
 }
 
-// NewShell creates a new instance of Shell. It will also add a blessing
-// to the supplied principal and ensure that any child processes are
-// created with principals that are in turn blessed with it. A typical
-// use case is to pass in the runtime's principal and hence allow the
-// process hosting the shell to interact with its children and vice
-// versa. If a nil principal is passed in then NewShell will create a new
-// principal that shares a blessing with all of its children, in this mode,
-// any child processes can interact with each other but not with the parent.
-func NewShell(p security.Principal) (*Shell, error) {
+// NewShell creates a new instance of Shell.
+// If ctx is non-nil, the shell will manage Principals for child processes.
+// By default it adds a blessing to ctx's principal to ensure isolation. Any child processes
+// are created with principals derived from this new blessing.
+// However, if p is non-nil the shell will skip the new blessing and child processes
+// will have their principals derived from p's default blessing(s).
+func NewShell(ctx *context.T, p security.Principal) (*Shell, error) {
 	sh := &Shell{
 		env:          make(map[string]string),
 		handles:      make(map[Handle]struct{}),
 		startTimeout: time.Minute,
 		waitTimeout:  10 * time.Second,
 		config:       exec.NewConfig(),
-		principal:    p,
 	}
-	if p == nil {
-		if err := sh.initShellCredentials(); err != nil {
-			return nil, err
-		}
+	if ctx == nil {
 		return sh, nil
 	}
+	var err error
+	ctx, sh.cancelCtx = context.WithCancel(ctx)
+	if ctx, _, err = veyron2.SetNewStreamManager(ctx); err != nil {
+		return nil, err
+	}
+	sh.ctx = ctx
+
+	if sh.tempCredDir, err = ioutil.TempDir("", "shell_credentials"); err != nil {
+		return nil, err
+	}
+	if sh.agent, err = keymgr.NewLocalAgent(ctx, sh.tempCredDir, nil); err != nil {
+		return nil, err
+	}
+	if p != nil {
+		sh.principal = p
+		sh.blessing = p.BlessingStore().Default()
+		return sh, nil
+	}
+	p = veyron2.GetPrincipal(ctx)
+	sh.principal = p
 	gen := rand.New(rand.NewSource(time.Now().UnixNano()))
 	// Use a unique blessing tree per shell.
 	blessingName := fmt.Sprintf("%s-%d", shellBlessingExtension, gen.Int63())
@@ -124,60 +135,42 @@
 	return sh, nil
 }
 
-func (sh *Shell) initShellCredentials() error {
-	sh.mu.Lock()
-	defer sh.mu.Unlock()
-	if dir := os.Getenv(consts.VeyronCredentials); len(dir) != 0 {
-		sh.env[consts.VeyronCredentials] = dir
-		return nil
+func (sh *Shell) getChildCredentials() (*os.File, error) {
+	if sh.ctx == nil {
+		return nil, nil
 	}
-
-	dir, err := ioutil.TempDir("", "shell_credentials")
-	if err != nil {
-		return err
-	}
-	sh.env[consts.VeyronCredentials] = dir
-	sh.tempCredDirs = append(sh.tempCredDirs, dir)
-	return nil
-}
-
-func (sh *Shell) getChildCredentials(shellCredDir string) (string, error) {
 	root := sh.principal
 	rootBlessing := sh.blessing
-	if root == nil {
-		r, err := principalFromDir(shellCredDir)
-		if err != nil {
-			return "", err
-		}
-		root = r
-		rootBlessing = root.BlessingStore().Default()
-	}
-
-	dir, err := ioutil.TempDir("", "shell_child_credentials")
+	_, conn, err := sh.agent.NewPrincipal(sh.ctx, true)
 	if err != nil {
-		return "", err
+		return nil, err
 	}
-	sh.tempCredDirs = append(sh.tempCredDirs, dir)
-	// Create a principal and default blessing for the child that is
-	// derived from the blessing created for this shell. This can
-	// be used by the parent to invoke RPCs on any children and for the
-	// children to invoke RPCs on each other.
-	p, err := vsecurity.CreatePersistentPrincipal(dir, nil)
+	ctx, cancel := context.WithCancel(sh.ctx)
+	if ctx, _, err = veyron2.SetNewStreamManager(ctx); err != nil {
+		return nil, err
+	}
+	defer cancel()
+	fd, err := syscall.Dup(int(conn.Fd()))
 	if err != nil {
-		return "", err
+		return nil, err
+	}
+	syscall.CloseOnExec(fd)
+	p, err := agent.NewAgentPrincipal(ctx, fd)
+	if err != nil {
+		return nil, err
 	}
 	blessingForChild, err := root.Bless(p.PublicKey(), rootBlessing, childBlessingExtension, security.UnconstrainedUse())
 	if err != nil {
-		return "", err
+		return nil, err
 	}
 	if err := p.BlessingStore().SetDefault(blessingForChild); err != nil {
-		return "", err
+		return nil, err
 	}
 	if _, err := p.BlessingStore().Set(blessingForChild, security.AllPrincipals); err != nil {
-		return "", err
+		return nil, err
 	}
 	if err := p.AddToRoots(blessingForChild); err != nil {
-		return "", err
+		return nil, err
 	}
 
 	if sh.blessing != nil {
@@ -185,7 +178,7 @@
 		// by the parent, should the child choose to invoke RPCs on the parent.
 		blessingFromChild, err := root.Bless(p.PublicKey(), root.BlessingStore().Default(), childBlessingExtension, security.UnconstrainedUse())
 		if err != nil {
-			return "", err
+			return nil, err
 		}
 		// We store this blessing as the one to use with a pattern that matches
 		// the root's name.
@@ -194,14 +187,14 @@
 		ctx := security.NewContext(&security.ContextParams{LocalPrincipal: root})
 		rootName := root.BlessingStore().Default().ForContext(ctx)[0]
 		if _, err := p.BlessingStore().Set(blessingFromChild, security.BlessingPattern(rootName)); err != nil {
-			return "", err
+			return nil, err
 		}
 
 		if err := p.AddToRoots(blessingFromChild); err != nil {
-			return "", err
+			return nil, err
 		}
 	}
-	return dir, nil
+	return conn, nil
 }
 
 type Main func(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error
@@ -244,12 +237,16 @@
 	if err != nil {
 		return nil, err
 	}
+	p, err := sh.getChildCredentials()
+	if err != nil {
+		return nil, err
+	}
 	cmd := registry.getCommand(name)
 	if cmd == nil {
 		return nil, fmt.Errorf("%s: not registered", name)
 	}
 	expanded := append([]string{name}, sh.expand(args...)...)
-	h, err := cmd.factory().start(sh, cenv, expanded...)
+	h, err := cmd.factory().start(sh, p, cenv, expanded...)
 	if err != nil {
 		// If the error is a timeout, then h can be used to recover
 		// any output from the process.
@@ -399,18 +396,14 @@
 		}
 	}
 
-	// TODO(ataly, ashankar, caprita): The following code may lead to
-	// removing the credential directories for child processes that are
-	// still alive (this can happen e.g. if the Wait on the child timed
-	// out). While we can hope that some error will reach the caller of
-	// Cleanup (because we harvest the error codes from Shutdown), it may
-	// lead to failures that are harder to debug because the original issue
-	// will get masked by the credentials going away. One possibility is
-	// to only remove the credentials dir when Shutdown returns no timeout
-	// error.
-	for _, dir := range sh.tempCredDirs {
-		os.RemoveAll(dir)
+	if sh.cancelCtx != nil {
+		// Note(ribrdb, caprita): This will shutdown the agents.  If there
+		// were errors shutting down it is possible there could be child
+		// processes still running, and stopping the agent may cause
+		// additional failures.
+		sh.cancelCtx()
 	}
+	os.RemoveAll(sh.tempCredDir)
 	return err
 }
 
@@ -425,15 +418,7 @@
 	// want the child to directly use the directory specified
 	// by the shell's VeyronCredentials.
 	delete(m1, consts.VeyronCredentials)
-
-	// Set the VeyronCredentials environment variable for the child
-	// if it is not already set and the shell's VeyronCredentials are set.
-	if len(evmap[consts.VeyronCredentials]) == 0 && (len(sh.env[consts.VeyronCredentials]) != 0 || sh.principal != nil) {
-		var err error
-		if evmap[consts.VeyronCredentials], err = sh.getChildCredentials(sh.env[consts.VeyronCredentials]); err != nil {
-			return nil, err
-		}
-	}
+	delete(m1, agent.FdVarName)
 
 	m2 := mergeMaps(m1, evmap)
 	r := []string{}
@@ -477,5 +462,5 @@
 // commands.
 type command interface {
 	envelope(sh *Shell, env []string, args ...string) ([]string, []string)
-	start(sh *Shell, env []string, args ...string) (Handle, error)
+	start(sh *Shell, agent *os.File, env []string, args ...string) (Handle, error)
 }