From 3f49a27f8a19269297606d8d2027246c747b07d1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Am=C3=A9lia=20Coutard-Sander?= Date: Thu, 5 Dec 2024 16:53:15 +0100 Subject: [PATCH] Extensible protocol. Counts as a v1.0.0 methinks ! --- README.txt | 3 ++- git-remote-gnunet | 59 ++++++++++++++++++++++++----------------------- handle-request.sh | 46 +++++++++++++++--------------------- 3 files changed, 51 insertions(+), 57 deletions(-) diff --git a/README.txt b/README.txt index 9ca31aa..b967639 100644 --- a/README.txt +++ b/README.txt @@ -32,7 +32,7 @@ Features: - Authenticated push to repo ! TODO: - - Pulling from anyone using a commit hash ? + - Pulling from anyone, using a commit hash ? - DHT to figure out who has what commits ? - Question of trust for pulling from peers ? @@ -54,3 +54,4 @@ Authentication (also done when pulling): - The client decyphers and hashes the passphrase, and sends the hash back. - This avoids the request by a malicious server of decyphering of arbitrary strings. - The server checks the correctness of the hash. + PS: The authentication protocol should be extensible in a backwards-compatible way, should there be a need. diff --git a/git-remote-gnunet b/git-remote-gnunet index b01e65a..521a95e 100755 --- a/git-remote-gnunet +++ b/git-remote-gnunet @@ -10,24 +10,16 @@ fi | awk ' match($0, /(([^@]*)@)?([^/]*)(\/.*)?/, a) { printf "%s\n%s\n%s\n", a[2], a[3], a[4] } ' | { read -r username && read -r gnsname && read -r path && ! grep '.'; } if test "$?" != "0"; then - echo >&2 "Invalid address: \`$2'." - echo >&2 "URI format: \`[gnunet://][user@]gns.name[/path/to/repo]'." + echo >&2 "Invalid URI: \`$2'." + echo >&2 "Format: \`[gnunet://][user@]gns.name[/path/to/repo]'." exit 1 fi -pkey="$(gnunet-identity -qde "$username")" while read -r cmd arg; do case "$cmd" in - capabilities) - echo 'connect' - echo '' - ;; - connect) - break - ;; - *) - echo >&2 "Error !" - exit 1 + capabilities) printf '%s\n' 'connect' '' ;; + connect) break ;; + *) echo >&2 "Error: Git remote helper protocol."; exit 1 ;; esac done @@ -43,19 +35,28 @@ echo >&2 "Trying service \`$service' on peer \`$peer'." ip="$(gnunet-vpn -s "$service" -p "$peer" -t)" echo >&2 "Using tunnel ip \`$ip'." -case "$username" in -"") - { printf '%s\n' "$path" "$arg" "$pkey"; cat; } | socat stdio tcp4:"$ip":9418 - ;; -*) - fifo="$(mktemp -u)" - mkfifo "$fifo" || { echo 2>&1 "Failed to create the fifo."; exit 1; } - { printf '%s\n' "$path" "$arg" "$pkey"; head -n1 "$fifo"; cat; } \ - | socat stdio tcp4:"$ip":9418 \ - | { head -n 1 \ - | { xargs gnunet-identity -e "$username" -R || { echo >&2 'Error !'; exit 1; }; } \ - | sha256sum > "$fifo"; - cat; } - rm "$fifo" - ;; -esac +fifo="$(mktemp -u "git-over-gnunet.XXXXXXXXXX" --tmpdir)" +mkfifo "$fifo" || { echo 2>&1 "Failed to create the fifo."; exit 1; } +{ + test "" = "$username" || pkey="$(gnunet-identity -qde "$username")" || { echo 2>&1 "Failed to get pkey for username \`$username'."; rm "$fifo"; exit 1; } + printf '%s\n' "Path:$path" "Command:$arg" "Key:$pkey"; + echo; + cat "$fifo" -; +} | socat stdio tcp4:"$ip":9418 | { + { if test "" != "$username"; then + read -r cyphertext + case "$cyphertext" in + "Error: "*) + echo >&2 "$cyphertext" + rm "$fifo" + exit 1 + ;; + "Key:"*) + plaintext="$(gnunet-identity -e "$username" -R "${cyphertext:4}")" || { rm "$fifo"; exit 1; } + printf '%s\n' "$plaintext" | sha256sum + ;; + esac; + fi; } > "$fifo"; + cat; +} +rm "$fifo" diff --git a/handle-request.sh b/handle-request.sh index e47975b..83fdcbc 100755 --- a/handle-request.sh +++ b/handle-request.sh @@ -7,53 +7,45 @@ fi base_path="$1" pkeys="$2" -read -r path || { echo "No path specified." | tee /dev/stderr; exit 1; } -read -r comm || { echo "No command specified." | tee /dev/stderr; exit 1; } -read -r pkey || { echo "No pkey specified." | tee /dev/stderr; exit 1; } +while read -r line; do + case "$line" in + "Path:"*) path="${line:5}" ;; + "Command:"*) comm="${line:8}" ;; + "Key:"*) pkey="${line:4}" ;; + "") break ;; + *) echo "Error: Unknown information key: \`$line'."; exit ;; + esac +done full_path="$(realpath -sm "$base_path$path")" case "$full_path" in "$base_path") ;; "$base_path/"*) ;; -*) echo "Invalid path: \`$full_path'." | tee /dev/stderr - exit 1 +*) echo "Error: Invalid path: \`$full_path'." + exit ;; esac if test "" != "$pkey"; then - pkey="$(grep -xm1 -- "$pkey" < "$pkeys")" || { echo "Unknown public key." | tee /dev/stderr; exit 1; } + grep -qxF -- "$pkey" "$pkeys" || { echo "Error: Unknown public key."; exit; } keyphrase="$( /dev/null | head -c 64)" hashed="$(printf '%s\n' "$keyphrase" | sha256sum)" encrypted="$(gnunet-identity -k "$pkey" -W "$keyphrase")" - printf '%s\n' "$encrypted" + printf 'Key:%s\n' "$encrypted" read -r returned_hash case "$returned_hash" in - "$hashed") - ;; - *) - echo "Incorrect passphrase decryption." | tee /dev/stderr - exit 1 - ;; + "$hashed") ;; + *) echo "Error: Incorrect passphrase decryption."; exit ;; esac fi case "$comm" in -git-upload-pack) - # Unidentified pull is allowed. - ;; -git-receive-pack) - if test "" = "$pkey"; then - echo "Unidentified push is not allowed." | tee /dev/stderr - exit 1 - fi - ;; -*) - echo "Unknown command: \`$comm'." | tee /dev/stderr - exit 1 - ;; +git-upload-pack) ;; # Unidentified pull is allowed. +git-receive-pack) test "" = "$pkey" && { echo "Error: Unidentified push is not allowed."; exit; } ;; +*) echo "Error: Unknown command: \`$comm'."; exit ;; esac -git_dir="$(git -C "$full_path" rev-parse --path-format=absolute --git-dir)" || { echo "Not a git repository: \`$full_path'." | tee /dev/stderr; exit 1; } +git_dir="$(git -C "$full_path" rev-parse --path-format=absolute --git-dir)" || { echo "Error: Not a git repository: \`$full_path'."; exit; } echo '' exec "$comm" "$git_dir" -- 2.46.0