]> ruderich.org/simon Gitweb - safcm/safcm.git/blobdiff - rpc/dial.go
Use SPDX license identifiers
[safcm/safcm.git] / rpc / dial.go
index ef280191ef57b23ca8f03461a97af206e23ecc67..fa1efcdeb6c5597edcbfd0c9d4a20a0f17b4680c 100644 (file)
@@ -1,19 +1,7 @@
 // Simple RPC-like protocol: establish new connection and upload helper
 
-// Copyright (C) 2021  Simon Ruderich
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright (C) 2021-2024  Simon Ruderich
 
 package rpc
 
@@ -24,22 +12,35 @@ import (
        "encoding/hex"
        "fmt"
        "io"
+       "io/fs"
        "os/exec"
        "strconv"
        "strings"
 
        "ruderich.org/simon/safcm"
-       "ruderich.org/simon/safcm/remote"
 )
 
-func (c *Conn) DialSSH(user, host string) error {
+type SSHConfig struct {
+       Host      string
+       User      string // optional
+       SshConfig string // optional
+
+       RemoteHelpers fs.FS
+}
+
+func (c *Conn) DialSSH(cfg SSHConfig) error {
        if c.events == nil {
                return fmt.Errorf("cannot reuse Conn")
        }
 
-       remote := host
-       if user != "" {
-               remote = user + "@" + host
+       if cfg.RemoteHelpers == nil {
+               return fmt.Errorf("SSHConfig.RemoteHelpers not set")
+       }
+       c.remoteHelpers = cfg.RemoteHelpers
+
+       remote := cfg.Host
+       if cfg.User != "" {
+               remote = cfg.User + "@" + cfg.Host
        }
        c.debugf("DialSSH: connecting to %q", remote)
 
@@ -48,8 +49,14 @@ func (c *Conn) DialSSH(user, host string) error {
                // Help debugging by showing executed shell commands
                opts += "x"
        }
-       c.cmd = exec.Command("ssh", remote, "/bin/sh", opts)
-       c.remote = remote
+
+       c.sshRemote = remote
+       if cfg.SshConfig != "" {
+               c.sshOpts = []string{"-F", cfg.SshConfig}
+       }
+       c.cmd = exec.Command("ssh",
+               append(append([]string{}, c.sshOpts...),
+                       c.sshRemote, "/bin/sh", opts)...)
 
        stdin, err := c.cmd.StdinPipe()
        if err != nil {
@@ -71,7 +78,7 @@ func (c *Conn) DialSSH(user, host string) error {
 
        err = c.dialSSH(stdin, stdout)
        if err != nil {
-               c.Kill()
+               c.Kill() //nolint:errcheck
                return err
        }
        c.conn = safcm.NewGobConn(stdout, stdin)
@@ -135,13 +142,13 @@ compat_sha512sum() {
        //
        // The target directory must no permit other users to delete our files
        // or symlink attacks and arbitrary code execution is possible. For
-       // /tmp this is guaranteed by the sticky bit. Make sure it has the
-       // proper permissions.
+       // /tmp this is guaranteed by the sticky bit. The code verifies the
+       // directory has the proper permissions.
        //
        // We cannot use `test -f && test -O` because this is open to TOCTOU
        // attacks. `stat` gives use the full file state. If the file is owned
-       // by us and not a symlink then it's safe to use (assuming sticky or
-       // directory not writable by others).
+       // by us and not a symlink then it's safe to use (assuming sticky
+       // directory or directory not writable by others).
        //
        // `test -e` is only used to prevent error messages if the file
        // doesn't exist. It does not guard against any races.
@@ -171,7 +178,6 @@ f() {
                tmp="$(mktemp "$x.XXXXXX")"
                # Report filename for upload
                echo "$tmp"
-
                # Wait for upload to complete
                read unused
 
@@ -181,6 +187,8 @@ f() {
                rm "$tmp"
                # Make file executable
                chmod 0700 "$x"
+               # Some BSD create files with group wheel in /tmp
+               chgrp "$(id -g)" "$x"
        fi
 
        exec "$x" sync
@@ -195,9 +203,9 @@ f
                return err
        }
 
-       // Get embedded helper binary
-       helper, err := remote.Helpers.ReadFile(
-               fmt.Sprintf("helpers/%s-%s", goos, goarch))
+       // Get remote helper binary
+       helper, err := fs.ReadFile(c.remoteHelpers,
+               fmt.Sprintf("%s-%s", goos, goarch))
        if err != nil {
                return fmt.Errorf("remote not built for GOOS/GOARCH %s/%s",
                        goos, goarch)
@@ -249,12 +257,14 @@ f
                path = strings.TrimSuffix(path, "\n")
 
                c.debugf("DialSSH: uploading new remote to %q at %q",
-                       c.remote, path)
+                       c.sshRemote, path)
 
-               cmd := exec.Command("ssh", c.remote,
-                       fmt.Sprintf("cat > %q", path))
+               cmd := exec.Command("ssh",
+                       append(append([]string{}, c.sshOpts...),
+                               c.sshRemote,
+                               fmt.Sprintf("cat > %q", path))...)
                cmd.Stdin = bytes.NewReader(helper)
-               err = c.handleStderrAsEvents(cmd)
+               err = c.handleStderrAsEvents(cmd) // cmd.Stderr
                if err != nil {
                        return err
                }