From eb1883a8e6241bf811de13a978ebb6af79210967 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Mon, 17 Feb 2025 21:57:09 +0800 Subject: hooks, etc.: Authenticate hooks, and handle them in the spawning thread --- git_hooks_client/git_hooks_client.c | 29 ++++++++++++++++++++++++++++- git_hooks_handle.go | 24 +++++++++++++++++++++++- ssh_handle_receive_pack.go | 37 ++++++++++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/git_hooks_client/git_hooks_client.c b/git_hooks_client/git_hooks_client.c index fa3e04c..541e606 100644 --- a/git_hooks_client/git_hooks_client.c +++ b/git_hooks_client/git_hooks_client.c @@ -14,6 +14,16 @@ int main(int argc, char *argv[]) { dprintf(STDERR_FILENO, "environment variable LINDENII_FORGE_HOOKS_SOCKET_PATH undefined\n"); return EXIT_FAILURE; } + const char *cookie = getenv("LINDENII_FORGE_HOOKS_COOKIE"); + if (cookie == NULL) { + dprintf(STDERR_FILENO, "environment variable LINDENII_FORGE_HOOKS_COOKIE undefined\n"); + return EXIT_FAILURE; + } + if (strlen(cookie) != 64) { + dprintf(STDERR_FILENO, "environment variable LINDENII_FORGE_HOOKS_COOKIE is not 64 characters long, something has gone wrong\n"); + dprintf(STDERR_FILENO, "%s\n", cookie); + return EXIT_FAILURE; + } /* * All hooks in git (see builtin/receive-pack.c) use a pipe by setting @@ -83,7 +93,24 @@ int main(int argc, char *argv[]) { } /* - * First we report argc and argv to the UNIX domain socket. + * We first send the 64-byte cookie to the UNIX domain socket + */ + ssize_t cookie_bytes_sent = send(sock, cookie, 64, 0); + switch (cookie_bytes_sent) { + case -1: + perror("send cookie"); + close(sock); + return EXIT_FAILURE; + case 64: + break; + default: + dprintf(STDERR_FILENO, "send returned unexpected value on internal socket\n"); + close(sock); + return EXIT_FAILURE; + } + + /* + * Next we can report argc and argv to the UNIX domain socket. */ uint64_t argc64 = (uint64_t)argc; ssize_t bytes_sent = send(sock, &argc64, sizeof(argc64), 0); diff --git a/git_hooks_handle.go b/git_hooks_handle.go index 5cb59ce..4e3d93d 100644 --- a/git_hooks_handle.go +++ b/git_hooks_handle.go @@ -31,6 +31,21 @@ func hooks_handle_connection(conn net.Conn) { return } + cookie := make([]byte, 64) + _, err = conn.Read(cookie) + if err != nil { + conn.Write([]byte{1}) + fmt.Fprintln(conn, "Failed to read cookie:", err.Error()) + return + } + + deployer_chan, ok := hooks_cookie_deployer.Load(string(cookie)) + if !ok { + conn.Write([]byte{1}) + fmt.Fprintln(conn, "Invalid cookie") + return + } + var argc64 uint64 err = binary.Read(conn, binary.NativeEndian, &argc64) if err != nil { @@ -57,7 +72,14 @@ func hooks_handle_connection(conn net.Conn) { args = append(args, arg.String()) } - conn.Write([]byte{0}) + callback := make(chan struct{}) + + deployer_chan <- hooks_cookie_deployer_return{ + args: args, + callback: callback, + conn: conn, + } + <-callback } func serve_git_hooks(listener net.Listener) error { diff --git a/ssh_handle_receive_pack.go b/ssh_handle_receive_pack.go index 58f99da..214fa2a 100644 --- a/ssh_handle_receive_pack.go +++ b/ssh_handle_receive_pack.go @@ -1,16 +1,27 @@ package main import ( + "crypto/rand" "errors" "fmt" + "net" "os" "os/exec" glider_ssh "github.com/gliderlabs/ssh" + "go.lindenii.runxiyu.org/lindenii-common/cmap" ) var err_unauthorized_push = errors.New("You are not authorized to push to this repository") +type hooks_cookie_deployer_return struct { + args []string + callback chan struct{} + conn net.Conn +} + +var hooks_cookie_deployer = cmap.ComparableMap[string, chan hooks_cookie_deployer_return]{} + func ssh_handle_receive_pack(session glider_ssh.Session, pubkey string, repo_identifier string) (err error) { repo_path, access, err := get_repo_path_perms_from_ssh_path_pubkey(session.Context(), repo_identifier, pubkey) if err != nil { @@ -20,8 +31,20 @@ func ssh_handle_receive_pack(session glider_ssh.Session, pubkey string, repo_ide return err_unauthorized_push } + cookie, err := random_urlsafe_string(16) + if err != nil { + fmt.Fprintln(session.Stderr(), "Error while generating cookie:", err) + } + + c := make(chan hooks_cookie_deployer_return) + hooks_cookie_deployer.Store(cookie, c) + defer hooks_cookie_deployer.Delete(cookie) + proc := exec.CommandContext(session.Context(), "git-receive-pack", repo_path) - proc.Env = append(os.Environ(), "LINDENII_FORGE_HOOKS_SOCKET_PATH="+config.Hooks.Socket) + proc.Env = append(os.Environ(), + "LINDENII_FORGE_HOOKS_SOCKET_PATH="+config.Hooks.Socket, + "LINDENII_FORGE_HOOKS_COOKIE="+cookie, + ) proc.Stdin = session proc.Stdout = session proc.Stderr = session.Stderr() @@ -32,6 +55,12 @@ func ssh_handle_receive_pack(session glider_ssh.Session, pubkey string, repo_ide return err } + deployer := <-c + + deployer.conn.Write([]byte{1}) + + deployer.callback <- struct{}{} + err = proc.Wait() if exitError, ok := err.(*exec.ExitError); ok { fmt.Fprintln(session.Stderr(), "Process exited with error", exitError.ExitCode()) @@ -41,3 +70,9 @@ func ssh_handle_receive_pack(session glider_ssh.Session, pubkey string, repo_ide return err } + +func random_string(sz int) (string, error) { + r := make([]byte, sz) + _, err := rand.Read(r) + return string(r), err +} -- cgit v1.2.3