git-restrict

simple utility for git repo permission management
git clone https://git.parazyd.org/git-restrict
Log | Files | Refs | README | LICENSE

git-restrict.c (2411B)


      1 /* Copyright (c) 2021-2022 Ivan J. <parazyd@dyne.org>
      2  *
      3  * This file is part of git-restrict
      4  *
      5  * This program is free software: you can redistribute it and/or modify
      6  * it under the terms of the GNU Affero General Public License version 3
      7  * as published by the Free Software Foundation.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     12  * GNU Affero General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Affero General Public License
     15  * along with this program. If not, see <https://www.gnu.org/licenses/>.
     16  */
     17 #include <stdio.h>
     18 #include <stdlib.h>
     19 #include <string.h>
     20 #include <unistd.h>
     21 
     22 static void die(const char *msg)
     23 {
     24 	fprintf(stderr, "%s\n", msg);
     25 	exit(1);
     26 }
     27 
     28 static char *strdup(const char *s)
     29 {
     30 	size_t sz = strlen(s)+1;
     31 	char *d = malloc(sz);
     32 	if (!d) return NULL;
     33 	return memcpy(d, s, sz);
     34 }
     35 
     36 int main(int argc, char *argv[])
     37 {
     38 	char *orig_cmd, *cmd, *repo, *buf;
     39 	char git_cmd[20 + 256];
     40 	int i, authorized = 0;
     41 
     42 	if (argc < 2)
     43 		die("usage: git-restrict repo0 repo1 ...");
     44 
     45 	if ((orig_cmd = getenv("SSH_ORIGINAL_COMMAND")) == NULL)
     46 		die("fatal: No $SSH_ORIGINAL_COMMAND in env.");
     47 
     48 	if ((repo = strdup(orig_cmd)) == NULL) die("fatal: Internal error.");
     49 	if ((cmd = strtok(repo, " ")) == NULL) die("fatal: Invalid command.");
     50 	repo = strtok(NULL, " ");
     51 
     52 	if (strcmp("git-upload-pack", cmd) && strcmp("git-receive-pack", cmd))
     53 		die("fatal: Unauthorized command.");
     54 
     55 	/* Repository name should at least be: 'a' */
     56 	if (repo == NULL || (strlen(repo) < 3))
     57 		die("fatal: Invalid repository name.");
     58 
     59 	/* Remove ' and / prefix and ' suffix */
     60 	repo++; if (repo[0] == '/') repo++; repo[strlen(repo) - 1] = 0;
     61 
     62 	for (i = 1; i < argc; i++) {
     63 		/* This is so both "foo" and "foo.git" are supported */
     64 		if ((buf = malloc(strlen(repo) + 4)) == NULL) {
     65 			perror("malloc");
     66 			return 1;
     67 		}
     68 
     69 		snprintf(buf, strlen(repo) + 4, "%s.git", argv[i]);
     70 
     71 		if (!strcmp(argv[i], repo) || !strcmp(buf, repo)) {
     72 			authorized = 1;
     73 			free(buf);
     74 			break;
     75 		}
     76 
     77 		free(buf);
     78 	}
     79 
     80 	if (!authorized)
     81 		die("fatal: Access to repository denied.");
     82 
     83 	snprintf(git_cmd, strlen(cmd) + strlen(repo) + 4, "%s '%s'", cmd, repo);
     84 
     85 	if (execlp("git-shell", "git-shell", "-c", git_cmd, (char *)NULL) == -1)
     86 		perror("execlp");
     87 
     88 	return 1;
     89 }