From: Amélia Coutard-Sander <git@ameliathe1st.gay>
Date: Thu, 5 Dec 2024 09:21:12 +0000 (+0100)
Subject: Authenticated pushing via the identity service
X-Git-Url: https://git.ameliathe1st.gay/?a=commitdiff_plain;h=6723bd0623acd426dcb9ab6d0e1d8f6dfc0059f7;p=git-over-gnunet.git

Authenticated pushing via the identity service
---

diff --git a/README.txt b/README.txt
index 12ba422..9ca31aa 100644
--- a/README.txt
+++ b/README.txt
@@ -1,4 +1,4 @@
-This set of bash scripts allows one to clone/fetch/pull git repositories using gnunet, specifically the GNS and the GNUnet VPN.
+This set of bash scripts allows one to use remote git repositories via gnunet, specifically the GNS, GNUnet VPN, and gnunet-identity.
 In the interest of demonstrating that it works, all the repos on my server are available at
 	`gnunet://git.serv.amelia.gnunet.gns.alt/<repo>.git` (see gitweb for the specific URIs).
 	Try cloning them ! (And do tell me if it fails, or if it works.)
@@ -6,8 +6,8 @@ In the interest of demonstrating that it works, all the repos on my server are a
 Usage:
 Client:
 	You must add the repo folder to the GIT_EXEC_PATH (for example by prepending
-		`GIT_EXEC_PATH=$GIT_EXEC_PATH:<path-to-git-over-gnunet-repo>` before every git command that uses remotes).
-	The URL to give to git is `gnunet://<gns location of the repo>/<name of the repo>`.
+		`GIT_EXEC_PATH=$GIT_EXEC_PATH:/path/to/git/over/gnunet/repo` before every git command that uses remotes).
+	The URL to give to git is `gnunet://[gnunet-identity@]gns.location.of.the.repo[/path/to/the/repo]`.
 Server:
 	- To serve repos, you have to setup a gnunet-vpn exit service.
 	  ```
@@ -20,16 +20,18 @@ Server:
 	- You should also add a gns VPN record containing the required information to connect to the exit service.
 	  `gnunet-namestore -a -t VPN -e 1d -n <gns-name> -V '1 <peer-id> <gns-name>' -z <ego> -p`
 	- Next, you need to run the daemon: it needs to run `handle-request.sh` in the parent folder of all the repos every time someone connects to it.
-	  For example, with socat: `socat tcp4-listen:9418,bind=169.254.86.1,fork  exec:'/path/to/handle-request.sh /path/to/repos'`
-	- This will open the repos for pulling on the url `gnunet://<gns-record-name>/<repo-name>`.
+	  For example, with socat: `socat tcp4-listen:9418,bind=169.254.86.1,fork  exec:'/path/to/handle-request.sh /path/to/repos /path/to/list/of/pkeys'`
+	- This will open the repos for pulling on the url `gnunet://[gnunet-identity@]gns-name.public-key[/path/to/the/repo]`.
+	- Additionally, any owner of a public key listed in /path/to/list/of/pkeys (each line should be a pkey on its own) will be allowed push access
+		(by specifying the corresponding identity before the @).
 
 Features:
 	- Clone repo !
 	- Fetch repo !
 	- Pull repo !
+	- Authenticated push to repo !
 
 TODO:
-	- Pushing, and a gnunet-identity verification system ?
 	- Pulling from anyone using a commit hash ?
 		- DHT to figure out who has what commits ?
 		- Question of trust for pulling from peers ?
@@ -37,9 +39,18 @@ TODO:
 How it works:
 Client:
 	- Uses a git remote helper to give git the ability to interpet the gnunet:// scheme.
-	- Gets the VPN record associated with the GNS name given in gnunet://<gns-name>/<repo-path>.
-	- Connects to the exit service, give it the name of the repo and the command required (e.g. `git-upload-pack`).
+	- Gets the VPN record associated with the GNS name given in the scheme.
+	- Connects to the exit service, give it the name of the repo and the command required
+		(e.g. `git-upload-pack`) (and the public key if an identity was specified).
 Server:
 	- Reads the path and the command.
-	- Checks that the command is git-upload-pack and the path isn't malicious.
+	- Checks that the the path isn't malicious.
+	- Checks that the command is git-upload-pack, or that the client is authenticated and the command is git-receive-pack.
 	- Executes the command and connects it to the client.
+Authentication (also done when pulling):
+	- The server receives the public key.
+	- It checks the key is allowed.
+	- It sends a randomly-generated passphrase to the client, encrypted to the public key.
+	- 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.
diff --git a/git-remote-gnunet b/git-remote-gnunet
index edccd49..b01e65a 100755
--- a/git-remote-gnunet
+++ b/git-remote-gnunet
@@ -43,4 +43,19 @@ echo >&2 "Trying service \`$service' on peer \`$peer'."
 ip="$(gnunet-vpn -s "$service" -p "$peer" -t)"
 echo >&2 "Using tunnel ip \`$ip'."
 
-{ printf '%s\n' "$path" "$arg" "$pkey"; cat; } | socat stdio tcp4:"$ip":9418
+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
diff --git a/handle-request.sh b/handle-request.sh
index 4894910..e47975b 100755
--- a/handle-request.sh
+++ b/handle-request.sh
@@ -1,10 +1,11 @@
 #!/usr/bin/env bash
 
-if test -z $1 || ! test -z $2; then
-	echo >&2 "Usage: $0 /base/path"
+if test -z "$1" || test -z "$2" || ! test -z "$3"; then
+	echo >&2 "Usage: $0 /base/path /file/containing/list/of/allowed/pkeys"
 	exit 1
 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; }
@@ -19,14 +20,32 @@ case "$full_path" in
 	;;
 esac
 
+if test "" != "$pkey"; then
+	pkey="$(grep -xm1 -- "$pkey" < "$pkeys")" || { echo "Unknown public key." | tee /dev/stderr; exit 1; }
+	keyphrase="$(</dev/random base64 2> /dev/null | head -c 64)"
+	hashed="$(printf '%s\n' "$keyphrase" | sha256sum)"
+	encrypted="$(gnunet-identity -k "$pkey" -W "$keyphrase")"
+	printf '%s\n' "$encrypted"
+	read -r returned_hash
+	case "$returned_hash" in
+	"$hashed")
+		;;
+	*)
+		echo "Incorrect passphrase decryption." | tee /dev/stderr
+		exit 1
+		;;
+	esac
+fi
+
 case "$comm" in
 git-upload-pack)
-	# No pkey checking.
+	# Unidentified pull is allowed.
 	;;
 git-receive-pack)
-	# Accept any valid pkey. FIXME
-	echo "Command \`$comm' not supported yet." | tee /dev/stderr
-	exit 1
+	if test "" = "$pkey"; then
+		echo "Unidentified push is not allowed." | tee /dev/stderr
+		exit 1
+	fi
 	;;
 *)
 	echo "Unknown command: \`$comm'." | tee /dev/stderr