code: plan9front

Download patch

ref: 674a47f7bfced5a91c621d139b0551826fae299a
parent: ace4e4defa70671fc0747c9899cfa80036efbb3d
author: Ori Bernstein <ori@eigenstate.org>
date: Sat Feb 3 10:06:38 EST 2024

git/serve: provide better errors

--- a/sys/src/cmd/git/serve.c
+++ b/sys/src/cmd/git/serve.c
@@ -8,6 +8,19 @@
 char	*pathpfx = nil;
 int	allowwrite;
 
+_Noreturn static void
+fail(Conn *c, char *fmt, ...)
+{
+	char msg[ERRMAX];
+	va_list ap;
+
+	va_start(ap, fmt);
+	vsnprint(msg, sizeof(msg), fmt, ap);
+	va_end(ap);
+	fmtpkt(c, "ERR %s\n", msg);
+	sysfatal("%s", msg);
+}
+
 int
 showrefs(Conn *c)
 {
@@ -24,7 +37,7 @@
 			goto error;
 
 	if((nrefs = listrefs(&refs, &names)) == -1)
-		sysfatal("listrefs: %r");
+		fail(c, "listrefs: %r");
 	for(i = 0; i < nrefs; i++){
 		if(strncmp(names[i], "heads/", strlen("heads/")) != 0)
 			continue;
@@ -128,10 +141,10 @@
 
 	dprint(1, "negotiating pack\n");
 	if(servnegotiate(c, &head, &nhead, &tail, &ntail) == -1)
-		sysfatal("negotiate: %r");
+		fail(c, "negotiate: %r");
 	dprint(1, "writing pack\n");
 	if(writepack(c->wfd, head, nhead, tail, ntail, &h) == -1)
-		sysfatal("send: %r");
+		fail(c, "send: %r");
 	return 0;
 }
 
@@ -138,6 +151,7 @@
 int
 validref(char *s)
 {
+	cleanname(s);
 	if(strncmp(s, "refs/", 5) != 0)
 		return 0;
 	for(; *s != '\0'; s++)
@@ -149,7 +163,7 @@
 int
 recvnegotiate(Conn *c, Hash **cur, Hash **upd, char ***ref, int *nupd)
 {
-	char pkt[Pktmax], *sp[4];
+	char pkt[Pktmax], refpath[512], *sp[4];
 	Hash old, new;
 	int n, i;
 
@@ -186,6 +200,16 @@
 		(*cur)[*nupd] = old;
 		(*upd)[*nupd] = new;
 		(*ref)[*nupd] = estrdup(sp[2]);
+		n = snprint(refpath, sizeof(refpath), ".git/%s", sp[2]);
+		if(n >= sizeof(refpath)-1){
+			fmtpkt(c, "ERR invalid ref %s\n", sp[2]);
+			goto error;
+		}
+		if(access(refpath, AWRITE) == -1
+		&& access(refpath, AEXIST) == 0){
+			fmtpkt(c, "ERR read-only ref %s\n", sp[2]);
+			goto error;
+		}
 		*nupd += 1;
 	}		
 	return 0;
@@ -446,8 +470,10 @@
 	char **ref;
 	int nupd;
 
+	if(!allowwrite)
+		fail(c, "read-only repo");
 	if(recvnegotiate(c, &cur, &upd, &ref, &nupd) == -1)
-		sysfatal("negotiate refs: %r");
+		fail(c, "negotiate refs: %r");
 	if(nupd != 0 && updatepack(c) == -1)
 		sysfatal("update pack: %r");
 	if(nupd != 0 && updaterefs(c, cur, upd, ref, nupd) == -1)
@@ -520,20 +546,18 @@
 	repo = parsecmd(buf, cmd, sizeof(cmd));
 	cleanname(repo);
 	if(strncmp(repo, "../", 3) == 0)
-		sysfatal("invalid path %s\n", repo);
-	if(bind(repo, "/", MREPL) == -1){
-		fmtpkt(&c, "ERR no repo %r\n");
-		sysfatal("enter %s: %r", repo);
-	}
+		fail(&c, "invalid path %s\n", repo);
+	if(bind(repo, "/", MREPL) == -1)
+		fail(&c, "no such repo", repo);
 	if(chdir("/") == -1)
-		sysfatal("chdir: %r");
+		fail(&c, "no such repo");
 	if(access(".git", AREAD) == -1)
-		sysfatal("no git repository");
-	if(strcmp(cmd, "git-receive-pack") == 0 && allowwrite)
+		fail(&c, "no such repo");
+	if(strcmp(cmd, "git-receive-pack") == 0)
 		recvpack(&c);
 	else if(strcmp(cmd, "git-upload-pack") == 0)
 		servpack(&c);
 	else
-		sysfatal("unsupported command '%s'", cmd);
+		fail(&c, "unsupported command '%s'", cmd);
 	exits(nil);
 }