services/device: Fix multiuser mode

In multiuser mode the security agent socket needs to be chown'ed
to the userid the app is running as by using the suidhelper.

Change-Id: Ia2c7f6c432fec9e3645a608caa4a2caad87835c6
diff --git a/services/device/deviced/doc.go b/services/device/deviced/doc.go
index b89a60b..efea027 100644
--- a/services/device/deviced/doc.go
+++ b/services/device/deviced/doc.go
@@ -41,6 +41,8 @@
    generate a pairing token for the device manager that will need to be provided
    when a device is claimed
 
+ -agentsock=
+   Path to the application's security agent socket.
  -alsologtostderr=true
    log to standard error as well as files
  -chown=false
diff --git a/services/device/deviced/internal/impl/app_service.go b/services/device/deviced/internal/impl/app_service.go
index dca7113..56dd7bc 100644
--- a/services/device/deviced/internal/impl/app_service.go
+++ b/services/device/deviced/internal/impl/app_service.go
@@ -834,7 +834,7 @@
 	return instanceDir, instanceID, nil
 }
 
-func genCmd(ctx *context.T, instanceDir string, nsRoot string) (*exec.Cmd, error) {
+func genCmd(ctx *context.T, instanceDir, nsRoot string, usingSocketAgent bool) (*exec.Cmd, error) {
 	systemName, err := readSystemNameForInstance(instanceDir)
 	if err != nil {
 		return nil, err
@@ -855,6 +855,11 @@
 	}
 
 	saArgs := suidAppCmdArgs{targetUser: systemName, binpath: binPath}
+	if usingSocketAgent {
+		if saArgs.sockPath, err = sockPath(instanceDir); err != nil {
+			return nil, verror.New(errors.ErrOperationFailed, ctx, fmt.Sprintf("failed to obtain agent socket path: %v", err))
+		}
+	}
 
 	// Pass the displayed name of the program (argv0 as seen in ps output)
 	// Envelope data comes from the user so we sanitize it for safety
@@ -1053,13 +1058,17 @@
 	return pid, nil
 }
 
+func (i *appRunner) usingSocketAgent() bool {
+	return i.securityAgent != nil && i.securityAgent.keyMgr != nil
+}
+
 func (i *appRunner) run(ctx *context.T, instanceDir string) error {
 	if err := transitionInstance(instanceDir, device.InstanceStateNotRunning, device.InstanceStateLaunching); err != nil {
 		return err
 	}
 	var pid int
 
-	cmd, err := genCmd(ctx, instanceDir, i.mtAddress)
+	cmd, err := genCmd(ctx, instanceDir, i.mtAddress, i.usingSocketAgent())
 	if err == nil {
 		pid, err = i.startCmd(ctx, instanceDir, cmd)
 	}
@@ -1736,7 +1745,7 @@
 	} else {
 		debugInfo.Info = info
 	}
-	if cmd, err := genCmd(ctx, instanceDir, i.runner.mtAddress); err != nil {
+	if cmd, err := genCmd(ctx, instanceDir, i.runner.mtAddress, i.runner.usingSocketAgent()); err != nil {
 		return "", err
 	} else {
 		debugInfo.Cmd = cmd
diff --git a/services/device/deviced/internal/impl/helper_manager.go b/services/device/deviced/internal/impl/helper_manager.go
index 45829d0..2d65694 100644
--- a/services/device/deviced/internal/impl/helper_manager.go
+++ b/services/device/deviced/internal/impl/helper_manager.go
@@ -76,7 +76,7 @@
 
 type suidAppCmdArgs struct {
 	// args to helper
-	targetUser, progname, workspace, logdir, binpath string
+	targetUser, progname, workspace, logdir, binpath, sockPath string
 	// fields in exec.Cmd
 	env            []string
 	stdout, stderr io.Writer
@@ -105,6 +105,9 @@
 	cmd.Args = append(cmd.Args, "--progname", a.progname)
 	cmd.Args = append(cmd.Args, "--workspace", a.workspace)
 	cmd.Args = append(cmd.Args, "--logdir", a.logdir)
+	if a.sockPath != "" {
+		cmd.Args = append(cmd.Args, "--agentsock", a.sockPath)
+	}
 
 	cmd.Args = append(cmd.Args, "--run", a.binpath)
 	cmd.Args = append(cmd.Args, "--")
diff --git a/services/device/internal/suid/args.go b/services/device/internal/suid/args.go
index 1473654..d9ab2fb 100644
--- a/services/device/internal/suid/args.go
+++ b/services/device/internal/suid/args.go
@@ -33,6 +33,7 @@
 	uid       int
 	gid       int
 	workspace string
+	agentsock string
 	logDir    string
 	argv0     string
 	argv      []string
@@ -54,9 +55,9 @@
 const SavedArgs = "V23_SAVED_ARGS"
 
 var (
-	flagUsername, flagWorkspace, flagLogDir, flagRun, flagProgName *string
-	flagMinimumUid                                                 *int64
-	flagRemove, flagKill, flagChown, flagDryrun                    *bool
+	flagUsername, flagWorkspace, flagLogDir, flagRun, flagProgName, flagAgentSock *string
+	flagMinimumUid                                                                *int64
+	flagRemove, flagKill, flagChown, flagDryrun                                   *bool
 )
 
 func init() {
@@ -67,6 +68,7 @@
 	const uidThreshold = 501
 	flagUsername = fs.String("username", "", "The UNIX user name used for the other functions of this tool.")
 	flagWorkspace = fs.String("workspace", "", "Path to the application's workspace directory.")
+	flagAgentSock = fs.String("agentsock", "", "Path to the application's security agent socket.")
 	flagLogDir = fs.String("logdir", "", "Path to the log directory.")
 	flagRun = fs.String("run", "", "Path to the application to exec.")
 	flagProgName = fs.String("progname", "unnamed_app", "Visible name of the application, used in argv[0]")
@@ -218,6 +220,7 @@
 	}
 
 	wp.workspace = *flagWorkspace
+	wp.agentsock = *flagAgentSock
 	wp.argv0 = *flagRun
 	wp.logDir = *flagLogDir
 	wp.argv = append([]string{*flagProgName}, fs.Args()...)
diff --git a/services/device/internal/suid/args_test.go b/services/device/internal/suid/args_test.go
index 491a171..d910b51 100644
--- a/services/device/internal/suid/args_test.go
+++ b/services/device/internal/suid/args_test.go
@@ -35,6 +35,7 @@
 				uid:       testUid,
 				gid:       testGid,
 				workspace: "",
+				agentsock: "",
 				logDir:    "",
 				argv0:     "",
 				argv:      []string{"unnamed_app"},
@@ -49,13 +50,14 @@
 
 		{
 			[]string{"setuidhelper", "--minuid", "1", "--username", testUserName, "--workspace", "/hello",
-				"--logdir", "/logging", "--run", "/bin/v23", "--", "one", "two"},
+				"--logdir", "/logging", "--agentsock", "/tmp/sXXXX", "--run", "/bin/v23", "--", "one", "two"},
 			[]string{"A=B"},
 			"",
 			WorkParameters{
 				uid:       testUid,
 				gid:       testGid,
 				workspace: "/hello",
+				agentsock: "/tmp/sXXXX",
 				logDir:    "/logging",
 				argv0:     "/bin/v23",
 				argv:      []string{"unnamed_app", "one", "two"},
@@ -83,6 +85,7 @@
 				uid:       0,
 				gid:       0,
 				workspace: "",
+				agentsock: "",
 				logDir:    "",
 				argv0:     "",
 				argv:      []string{"hello", "vanadium"},
@@ -103,6 +106,7 @@
 				uid:       testUid,
 				gid:       testGid,
 				workspace: "",
+				agentsock: "",
 				logDir:    "",
 				argv0:     "",
 				argv:      []string{"/tmp/foo", "/tmp/bar"},
@@ -123,6 +127,7 @@
 				uid:       0,
 				gid:       0,
 				workspace: "",
+				agentsock: "",
 				logDir:    "",
 				argv0:     "",
 				argv:      nil,
@@ -143,6 +148,7 @@
 				uid:       0,
 				gid:       0,
 				workspace: "",
+				agentsock: "",
 				logDir:    "",
 				argv0:     "",
 				argv:      nil,
@@ -157,13 +163,14 @@
 
 		{
 			[]string{"setuidhelper", "--minuid", "1", "--username", testUserName, "--workspace", "/hello", "--progname", "binaryd/vanadium/app/testapp",
-				"--logdir", "/logging", "--run", "/bin/v23", "--dryrun", "--", "one", "two"},
+				"--logdir", "/logging", "--agentsock", "/tmp/2981298123/s", "--run", "/bin/v23", "--dryrun", "--", "one", "two"},
 			[]string{"A=B"},
 			"",
 			WorkParameters{
 				uid:       testUid,
 				gid:       testGid,
 				workspace: "/hello",
+				agentsock: "/tmp/2981298123/s",
 				logDir:    "/logging",
 				argv0:     "/bin/v23",
 				argv:      []string{"binaryd/vanadium/app/testapp", "one", "two"},
diff --git a/services/device/internal/suid/system.go b/services/device/internal/suid/system.go
index c647558..0b80e4d 100644
--- a/services/device/internal/suid/system.go
+++ b/services/device/internal/suid/system.go
@@ -42,9 +42,13 @@
 	chownPaths := hw.argv
 	if !hw.chown {
 		// Chown was invoked as part of regular suid execution, rather than directly
-		// via --chown. In that case, we chown the workspace and log directory
+		// via --chown. In that case, we chown the workspace, log directory, and,
+		// if specified, the agent socket path
 		// TODO(rjkroege): Ensure that the device manager can read log entries.
 		chownPaths = []string{hw.workspace, hw.logDir}
+		if hw.agentsock != "" {
+			chownPaths = append(chownPaths, hw.agentsock)
+		}
 	}
 
 	for _, p := range chownPaths {