Merge "services/agent:  Add a vbecome command to run with derived principals."
diff --git a/services/agent/vbecome/doc.go b/services/agent/vbecome/doc.go
new file mode 100644
index 0000000..02e0b6d
--- /dev/null
+++ b/services/agent/vbecome/doc.go
@@ -0,0 +1,66 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file was auto-generated via go generate.
+// DO NOT UPDATE MANUALLY
+
+/*
+Command vbecome executes commands with a derived Vanadium principal.
+
+Usage:
+   vbecome [flags] <command> [command args...]
+
+The vbecome flags are:
+ -duration=1h0m0s
+   Duration for the blessing.
+ -name=
+   Name to use for the blessing.
+ -role=
+   Role object from which to request the blessing. If set, the blessings from
+   this role server are used and --name is ignored. If not set, the default
+   blessings of the calling principal are extended with --name.
+
+The global flags are:
+ -alsologtostderr=true
+   log to standard error as well as files
+ -log_backtrace_at=:0
+   when logging hits line file:N, emit a stack trace
+ -log_dir=
+   if non-empty, write log files to this directory
+ -logtostderr=false
+   log to standard error instead of files
+ -max_stack_buf_size=4292608
+   max size in bytes of the buffer to use for logging stack traces
+ -stderrthreshold=2
+   logs at or above this threshold go to stderr
+ -v=0
+   log level for V logs
+ -v23.credentials=
+   directory to use for storing security credentials
+ -v23.i18n-catalogue=
+   18n catalogue files to load, comma separated
+ -v23.metadata=<just specify -v23.metadata to activate>
+   Displays metadata for the program and exits.
+ -v23.namespace.root=[/(dev.v.io/role/vprod/service/mounttabled)@ns.dev.v.io:8101]
+   local namespace root; can be repeated to provided multiple roots
+ -v23.proxy=
+   object name of proxy service to use to export services across network
+   boundaries
+ -v23.tcp.address=
+   address to listen on
+ -v23.tcp.protocol=wsh
+   protocol to listen with
+ -v23.vtrace.cache-size=1024
+   The number of vtrace traces to store in memory.
+ -v23.vtrace.collect-regexp=
+   Spans and annotations that match this regular expression will trigger trace
+   collection.
+ -v23.vtrace.dump-on-shutdown=true
+   If true, dump all stored traces on runtime shutdown.
+ -v23.vtrace.sample-rate=0
+   Rate (from 0.0 to 1.0) to sample vtrace traces.
+ -vmodule=
+   comma-separated list of pattern=N settings for file-filtered logging
+*/
+package main
diff --git a/services/agent/vbecome/v23_test.go b/services/agent/vbecome/v23_test.go
new file mode 100644
index 0000000..35ff567
--- /dev/null
+++ b/services/agent/vbecome/v23_test.go
@@ -0,0 +1,29 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file was auto-generated via go generate.
+// DO NOT UPDATE MANUALLY
+package main_test
+
+import "testing"
+import "os"
+
+import "v.io/x/ref/test"
+import "v.io/x/ref/test/v23tests"
+
+func TestMain(m *testing.M) {
+	test.Init()
+	cleanup := v23tests.UseSharedBinDir()
+	r := m.Run()
+	cleanup()
+	os.Exit(r)
+}
+
+func TestV23BecomeRole(t *testing.T) {
+	v23tests.RunTest(t, V23TestBecomeRole)
+}
+
+func TestV23BecomeName(t *testing.T) {
+	v23tests.RunTest(t, V23TestBecomeName)
+}
diff --git a/services/agent/vbecome/vbecome.go b/services/agent/vbecome/vbecome.go
new file mode 100644
index 0000000..929d6d1
--- /dev/null
+++ b/services/agent/vbecome/vbecome.go
@@ -0,0 +1,179 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The following enables go generate to generate the doc.go file.
+//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go . -help
+
+package main
+
+import (
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"fmt"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"syscall"
+	"time"
+
+	"v.io/v23"
+	"v.io/v23/context"
+	"v.io/v23/security"
+	"v.io/x/lib/cmdline"
+	"v.io/x/lib/vlog"
+	"v.io/x/ref/envvar"
+	vsecurity "v.io/x/ref/lib/security"
+	"v.io/x/ref/services/agent/internal/server"
+	"v.io/x/ref/services/role"
+
+	_ "v.io/x/ref/profiles"
+)
+
+var (
+	durationFlag time.Duration
+	nameFlag     string
+	roleFlag     string
+)
+
+var cmdVbecome = &cmdline.Command{
+	Run:      vbecome,
+	Name:     "vbecome",
+	Short:    "executes commands with a derived Vanadium principal",
+	Long:     "Command vbecome executes commands with a derived Vanadium principal.",
+	ArgsName: "<command> [command args...]",
+}
+
+const childAgentFd = 3
+const keyServerFd = 4
+
+func main() {
+	cmdline.HideGlobalFlagsExcept()
+	syscall.CloseOnExec(childAgentFd)
+	syscall.CloseOnExec(keyServerFd)
+
+	cmdVbecome.Flags.DurationVar(&durationFlag, "duration", 1*time.Hour, "Duration for the blessing.")
+	cmdVbecome.Flags.StringVar(&nameFlag, "name", "", "Name to use for the blessing.")
+	cmdVbecome.Flags.StringVar(&roleFlag, "role", "", "Role object from which to request the blessing. If set, the blessings from this role server are used and --name is ignored. If not set, the default blessings of the calling principal are extended with --name.")
+
+	os.Exit(cmdVbecome.Main())
+}
+
+func vbecome(cmd *cmdline.Command, args []string) error {
+	ctx, shutdown := v23.Init()
+	defer shutdown()
+
+	if len(args) == 0 {
+		if shell := os.Getenv("SHELL"); shell != "" {
+			args = []string{shell}
+		} else {
+			return fmt.Errorf("You must specify a command to run.")
+		}
+	}
+
+	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		return err
+	}
+	signer := security.NewInMemoryECDSASigner(key)
+	principal, err := vsecurity.NewPrincipalFromSigner(signer, nil)
+	if err != nil {
+		return err
+	}
+
+	if len(roleFlag) == 0 {
+		if len(nameFlag) == 0 {
+			nameFlag = filepath.Base(args[0])
+		}
+		if err := bless(ctx, principal, nameFlag); err != nil {
+			return err
+		}
+		ctx, err = v23.WithPrincipal(ctx, principal)
+		if err != nil {
+			return err
+		}
+	} else {
+		// The role server expects the client's blessing name to end
+		// with RoleSuffix. This is to avoid accidentally granting role
+		// access to anything else that might have been blessed by the
+		// same principal.
+		if err := bless(ctx, principal, role.RoleSuffix); err != nil {
+			return err
+		}
+		ctx, err = v23.WithPrincipal(ctx, principal)
+		if err != nil {
+			return err
+		}
+		if err = setupRoleBlessings(ctx, roleFlag); err != nil {
+			return err
+		}
+	}
+
+	// Start an agent server.
+	sock, endpoint, err := server.RunAnonymousAgent(ctx, principal, childAgentFd)
+	if err != nil {
+		return err
+	}
+	if err = os.Setenv(envvar.AgentEndpoint, endpoint); err != nil {
+		vlog.Fatalf("setenv: %v", err)
+	}
+
+	return doExec(args, sock)
+}
+
+func bless(ctx *context.T, p security.Principal, name string) error {
+	caveat, err := security.NewExpiryCaveat(time.Now().Add(durationFlag))
+	if err != nil {
+		vlog.Errorf("Couldn't create caveat")
+		return err
+	}
+
+	rp := v23.GetPrincipal(ctx)
+	blessing, err := rp.Bless(p.PublicKey(), rp.BlessingStore().Default(), name, caveat)
+	if err != nil {
+		vlog.Errorf("Couldn't bless")
+		return err
+	}
+
+	if err = p.BlessingStore().SetDefault(blessing); err != nil {
+		vlog.Errorf("Couldn't set default blessing")
+		return err
+	}
+	if _, err = p.BlessingStore().Set(blessing, security.AllPrincipals); err != nil {
+		vlog.Errorf("Couldn't set default client blessing")
+		return err
+	}
+	if err = p.AddToRoots(blessing); err != nil {
+		vlog.Errorf("Couldn't set trusted roots")
+		return err
+	}
+	return nil
+}
+
+func doExec(args []string, sock *os.File) error {
+	cmd := exec.Command(args[0], args[1:]...)
+	cmd.Stdin = os.Stdin
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	cmd.ExtraFiles = []*os.File{sock}
+	return cmd.Run()
+}
+
+func setupRoleBlessings(ctx *context.T, roleStr string) error {
+	b, err := role.RoleClient(roleStr).SeekBlessings(ctx)
+	if err != nil {
+		return err
+	}
+	p := v23.GetPrincipal(ctx)
+	// TODO(rthellend,ashankar): Revisit this configuration.
+	// SetDefault: Should we expect users to want to act as a server on behalf of the role (by default?)
+	// AllPrincipals: Do we not want to be discriminating about which services we use the role blessing at.
+	if err := p.BlessingStore().SetDefault(b); err != nil {
+		return err
+	}
+	if _, err := p.BlessingStore().Set(b, security.AllPrincipals); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/services/agent/vbecome/vbecome_v23_test.go b/services/agent/vbecome/vbecome_v23_test.go
new file mode 100644
index 0000000..298af90
--- /dev/null
+++ b/services/agent/vbecome/vbecome_v23_test.go
@@ -0,0 +1,67 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main_test
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"regexp"
+
+	_ "v.io/x/ref/profiles"
+	"v.io/x/ref/test/v23tests"
+)
+
+//go:generate v23 test generate .
+
+func writeRoledConfig() (path string, shutdown func(), err error) {
+	dir, err := ioutil.TempDir("", "")
+	if err != nil {
+		return dir, nil, err
+	}
+	err = ioutil.WriteFile(filepath.Join(dir, "therole.conf"), []byte(`
+{
+  "Members": ["root/child"],
+  "Extend": true
+}
+`), 0644)
+	return dir, func() { os.RemoveAll(dir) }, err
+}
+
+func V23TestBecomeRole(t *v23tests.T) {
+	vbecome := t.BuildV23Pkg("v.io/x/ref/services/agent/vbecome")
+	principal := t.BuildV23Pkg("v.io/x/ref/cmd/principal")
+
+	roled := t.BuildV23Pkg("v.io/x/ref/services/role/roled")
+	roledCreds, _ := t.Shell().NewChildCredentials("master")
+	roled = roled.WithStartOpts(roled.StartOpts().WithCustomCredentials(roledCreds))
+
+	v23tests.RunRootMT(t, "--v23.tcp.address=127.0.0.1:0")
+
+	dir, shutdown, err := writeRoledConfig()
+	if err != nil {
+		t.Fatalf("Couldn't write roled config: %v", err)
+	}
+	defer shutdown()
+	roled.Start("--v23.tcp.address=127.0.0.1:0", "--config-dir", dir, "--name", "roled")
+
+	output := vbecome.Run("--role=roled/therole", principal.Path(), "dump")
+	want := regexp.MustCompile(`Default Blessings\s+root/master/therole/root/child`)
+	if !want.MatchString(output) {
+		t.Errorf("Principal didn't have the role blessing:\n %s", output)
+	}
+}
+
+func V23TestBecomeName(t *v23tests.T) {
+	vbecome := t.BuildV23Pkg("v.io/x/ref/services/agent/vbecome")
+	principal := t.BuildV23Pkg("v.io/x/ref/cmd/principal")
+
+	v23tests.RunRootMT(t, "--v23.tcp.address=127.0.0.1:0")
+	output := vbecome.Run("--name=bob", principal.Path(), "dump")
+	want := regexp.MustCompile(`Default Blessings\s+root/child/bob`)
+	if !want.MatchString(output) {
+		t.Errorf("Principal didn't have the expected blessing:\n %s", output)
+	}
+}