Merge branch 'jc/daemon-access-hook'
Allow an external command to tell git-daemon to decline service based on the client address, repository path, etc. * jc/daemon-access-hook: daemon: --access-hook option
This commit is contained in:
commit
19801d6a27
@ -16,6 +16,7 @@ SYNOPSIS
|
|||||||
[--reuseaddr] [--detach] [--pid-file=<file>]
|
[--reuseaddr] [--detach] [--pid-file=<file>]
|
||||||
[--enable=<service>] [--disable=<service>]
|
[--enable=<service>] [--disable=<service>]
|
||||||
[--allow-override=<service>] [--forbid-override=<service>]
|
[--allow-override=<service>] [--forbid-override=<service>]
|
||||||
|
[--access-hook=<path>]
|
||||||
[--inetd | [--listen=<host_or_ipaddr>] [--port=<n>] [--user=<user> [--group=<group>]]
|
[--inetd | [--listen=<host_or_ipaddr>] [--port=<n>] [--user=<user> [--group=<group>]]
|
||||||
[<directory>...]
|
[<directory>...]
|
||||||
|
|
||||||
@ -171,6 +172,21 @@ the facility of inet daemon to achieve the same before spawning
|
|||||||
errors are not enabled, all errors report "access denied" to the
|
errors are not enabled, all errors report "access denied" to the
|
||||||
client. The default is --no-informative-errors.
|
client. The default is --no-informative-errors.
|
||||||
|
|
||||||
|
--access-hook=<path>::
|
||||||
|
Every time a client connects, first run an external command
|
||||||
|
specified by the <path> with service name (e.g. "upload-pack"),
|
||||||
|
path to the repository, hostname (%H), canonical hostname
|
||||||
|
(%CH), ip address (%IP), and tcp port (%P) as its command line
|
||||||
|
arguments. The external command can decide to decline the
|
||||||
|
service by exiting with a non-zero status (or to allow it by
|
||||||
|
exiting with a zero status). It can also look at the $REMOTE_ADDR
|
||||||
|
and $REMOTE_PORT environment variables to learn about the
|
||||||
|
requestor when making this decision.
|
||||||
|
+
|
||||||
|
The external command can optionally write a single line to its
|
||||||
|
standard output to be sent to the requestor as an error message when
|
||||||
|
it declines the service.
|
||||||
|
|
||||||
<directory>::
|
<directory>::
|
||||||
A directory to add to the whitelist of allowed directories. Unless
|
A directory to add to the whitelist of allowed directories. Unless
|
||||||
--strict-paths is specified this will also include subdirectories
|
--strict-paths is specified this will also include subdirectories
|
||||||
|
77
daemon.c
77
daemon.c
@ -30,6 +30,7 @@ static const char daemon_usage[] =
|
|||||||
" [--interpolated-path=<path>]\n"
|
" [--interpolated-path=<path>]\n"
|
||||||
" [--reuseaddr] [--pid-file=<file>]\n"
|
" [--reuseaddr] [--pid-file=<file>]\n"
|
||||||
" [--(enable|disable|allow-override|forbid-override)=<service>]\n"
|
" [--(enable|disable|allow-override|forbid-override)=<service>]\n"
|
||||||
|
" [--access-hook=<path>]\n"
|
||||||
" [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>]\n"
|
" [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>]\n"
|
||||||
" [--detach] [--user=<user> [--group=<group>]]\n"
|
" [--detach] [--user=<user> [--group=<group>]]\n"
|
||||||
" [<directory>...]";
|
" [<directory>...]";
|
||||||
@ -256,6 +257,71 @@ static int daemon_error(const char *dir, const char *msg)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *access_hook;
|
||||||
|
|
||||||
|
static int run_access_hook(struct daemon_service *service, const char *dir, const char *path)
|
||||||
|
{
|
||||||
|
struct child_process child;
|
||||||
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
const char *argv[8];
|
||||||
|
const char **arg = argv;
|
||||||
|
char *eol;
|
||||||
|
int seen_errors = 0;
|
||||||
|
|
||||||
|
#define STRARG(x) ((x) ? (x) : "")
|
||||||
|
*arg++ = access_hook;
|
||||||
|
*arg++ = service->name;
|
||||||
|
*arg++ = path;
|
||||||
|
*arg++ = STRARG(hostname);
|
||||||
|
*arg++ = STRARG(canon_hostname);
|
||||||
|
*arg++ = STRARG(ip_address);
|
||||||
|
*arg++ = STRARG(tcp_port);
|
||||||
|
*arg = NULL;
|
||||||
|
#undef STRARG
|
||||||
|
|
||||||
|
memset(&child, 0, sizeof(child));
|
||||||
|
child.use_shell = 1;
|
||||||
|
child.argv = argv;
|
||||||
|
child.no_stdin = 1;
|
||||||
|
child.no_stderr = 1;
|
||||||
|
child.out = -1;
|
||||||
|
if (start_command(&child)) {
|
||||||
|
logerror("daemon access hook '%s' failed to start",
|
||||||
|
access_hook);
|
||||||
|
goto error_return;
|
||||||
|
}
|
||||||
|
if (strbuf_read(&buf, child.out, 0) < 0) {
|
||||||
|
logerror("failed to read from pipe to daemon access hook '%s'",
|
||||||
|
access_hook);
|
||||||
|
strbuf_reset(&buf);
|
||||||
|
seen_errors = 1;
|
||||||
|
}
|
||||||
|
if (close(child.out) < 0) {
|
||||||
|
logerror("failed to close pipe to daemon access hook '%s'",
|
||||||
|
access_hook);
|
||||||
|
seen_errors = 1;
|
||||||
|
}
|
||||||
|
if (finish_command(&child))
|
||||||
|
seen_errors = 1;
|
||||||
|
|
||||||
|
if (!seen_errors) {
|
||||||
|
strbuf_release(&buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_return:
|
||||||
|
strbuf_ltrim(&buf);
|
||||||
|
if (!buf.len)
|
||||||
|
strbuf_addstr(&buf, "service rejected");
|
||||||
|
eol = strchr(buf.buf, '\n');
|
||||||
|
if (eol)
|
||||||
|
*eol = '\0';
|
||||||
|
errno = EACCES;
|
||||||
|
daemon_error(dir, buf.buf);
|
||||||
|
strbuf_release(&buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int run_service(char *dir, struct daemon_service *service)
|
static int run_service(char *dir, struct daemon_service *service)
|
||||||
{
|
{
|
||||||
const char *path;
|
const char *path;
|
||||||
@ -303,6 +369,13 @@ static int run_service(char *dir, struct daemon_service *service)
|
|||||||
return daemon_error(dir, "service not enabled");
|
return daemon_error(dir, "service not enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Optionally, a hook can choose to deny access to the
|
||||||
|
* repository depending on the phase of the moon.
|
||||||
|
*/
|
||||||
|
if (access_hook && run_access_hook(service, dir, path))
|
||||||
|
return -1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We'll ignore SIGTERM from now on, we have a
|
* We'll ignore SIGTERM from now on, we have a
|
||||||
* good client.
|
* good client.
|
||||||
@ -1142,6 +1215,10 @@ int main(int argc, char **argv)
|
|||||||
export_all_trees = 1;
|
export_all_trees = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!prefixcmp(arg, "--access-hook=")) {
|
||||||
|
access_hook = arg + 14;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!prefixcmp(arg, "--timeout=")) {
|
if (!prefixcmp(arg, "--timeout=")) {
|
||||||
timeout = atoi(arg+10);
|
timeout = atoi(arg+10);
|
||||||
continue;
|
continue;
|
||||||
|
Loading…
Reference in New Issue
Block a user