From 35423fcf69c6fa5ce534786f9be9043fa7f20402 Mon Sep 17 00:00:00 2001 From: Percy Wegmann Date: Thu, 29 Aug 2024 15:32:11 -0500 Subject: [PATCH] drive/driveimpl: use su instead of sudo This allows Taildrive to work on systems like Busybox that don't have sudo. Fixes #12282 Signed-off-by: Percy Wegmann --- drive/driveimpl/remote_impl.go | 42 +++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/drive/driveimpl/remote_impl.go b/drive/driveimpl/remote_impl.go index debdf8a36..7fd5d3325 100644 --- a/drive/driveimpl/remote_impl.go +++ b/drive/driveimpl/remote_impl.go @@ -333,11 +333,15 @@ func (s *userServer) run() error { args = append(args, s.Name, s.Path) } var cmd *exec.Cmd - if s.canSudo() { + if su := s.canSU(); su != "" { s.logf("starting taildrive file server as user %q", s.username) - allArgs := []string{"-n", "-u", s.username, s.executable} - allArgs = append(allArgs, args...) - cmd = exec.Command("sudo", allArgs...) + // Quote and escape arguments. Use single quotes to prevent shell substitutions. + for i, arg := range args { + args[i] = "'" + strings.ReplaceAll(arg, "'", "'\"'\"'") + "'" + } + cmdString := fmt.Sprintf("%s %s", s.executable, strings.Join(args, " ")) + allArgs := []string{s.username, "-c", cmdString} + cmd = exec.Command(su, allArgs...) } else { // If we were root, we should have been able to sudo as a specific // user, but let's check just to make sure, since we never want to @@ -405,16 +409,28 @@ var writeMethods = map[string]bool{ "DELETE": true, } -// canSudo checks wether we can sudo -u the configured executable as the -// configured user by attempting to call the executable with the '-h' flag to -// print help. -func (s *userServer) canSudo() bool { - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - defer cancel() - if err := exec.CommandContext(ctx, "sudo", "-n", "-u", s.username, s.executable, "-h").Run(); err != nil { - return false +// canSU checks whether the current process can run su with the right username. +// If su can be run, this returns the path to the su command. +// If not, this returns the empty string "". +func (s *userServer) canSU() string { + su, err := exec.LookPath("su") + if err != nil { + s.logf("can't find su command: %v", err) + return "" + } + + // First try to execute su -c true to make sure we can su. + err = exec.Command( + su, + s.username, + "-c", "true", + ).Run() + if err != nil { + s.logf("su check failed: %s", err) + return "" } - return true + + return su } // assertNotRoot returns an error if the current user has UID 0 or if we cannot