-#include <signal.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/poll.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <syslog.h>
-#include <pwd.h>
-#include <grp.h>
-#include <limits.h>
-#include "pkt-line.h"
#include "cache.h"
+#include "pkt-line.h"
#include "exec_cmd.h"
#include "interpolate.h"
+#include <syslog.h>
+
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif
+#ifndef NI_MAXSERV
+#define NI_MAXSERV 32
+#endif
+
static int log_syslog;
static int verbose;
static int reuseaddr;
static const char daemon_usage[] =
"git-daemon [--verbose] [--syslog] [--export-all]\n"
" [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
-" [--base-path=path] [--user-path | --user-path=path]\n"
+" [--base-path=path] [--base-path-relaxed]\n"
+" [--user-path | --user-path=path]\n"
" [--interpolated-path=path]\n"
" [--reuseaddr] [--detach] [--pid-file=file]\n"
" [--[enable|disable|allow-override|forbid-override]=service]\n"
/* Take all paths relative to this one if non-NULL */
static char *base_path;
static char *interpolated_path;
+static int base_path_relaxed;
/* Flag indicating client sent extra args. */
static int saw_extended_args;
buf[buflen++] = '\n';
buf[buflen] = '\0';
- write(2, buf, buflen);
+ write_in_full(2, buf, buflen);
}
static void logerror(const char *err, ...)
{
int sl, ndot;
- /*
+ /*
* This resurrects the belts and suspenders paranoia check by HPA
* done in <435560F7.4080006@zytor.com> thread, now enter_repo()
* does not do getcwd() based path canonicalizations.
{
static char rpath[PATH_MAX];
static char interp_path[PATH_MAX];
+ int retried_path = 0;
char *path;
char *dir;
dir = rpath;
}
- path = enter_repo(dir, strict_paths);
+ do {
+ path = enter_repo(dir, strict_paths);
+ if (path)
+ break;
+
+ /*
+ * if we fail and base_path_relaxed is enabled, try without
+ * prefixing the base path
+ */
+ if (base_path && base_path_relaxed && !retried_path) {
+ dir = itable[INTERP_SLOT_DIR].value;
+ retried_path = 1;
+ continue;
+ }
+ break;
+ } while (1);
if (!path) {
logerror("'%s': unable to chdir or not a git archive", dir);
int pathlen = strlen(path);
/* The validation is done on the paths after enter_repo
- * appends optional {.git,.git/.git} and friends, but
+ * appends optional {.git,.git/.git} and friends, but
* it does not use getcwd(). So if your /pub is
* a symlink to /mnt/pub, you can whitelist /pub and
* do not have to say /mnt/pub.
static int git_daemon_config(const char *var, const char *value)
{
- if (!strncmp(var, "daemon.", 7) &&
+ if (!prefixcmp(var, "daemon.") &&
!strcmp(var + 7, service_looking_at->config_name)) {
service_enabled = git_config_bool(var, value);
return 0;
return -1;
}
+static int receive_pack(void)
+{
+ execl_git_cmd("receive-pack", ".", NULL);
+ return -1;
+}
+
static struct daemon_service daemon_service[] = {
{ "upload-archive", "uploadarch", upload_archive, 0, 1 },
{ "upload-pack", "uploadpack", upload_pack, 1, 1 },
+ { "receive-pack", "receivepack", receive_pack, 0, 1 },
};
-static void enable_service(const char *name, int ena) {
+static void enable_service(const char *name, int ena)
+{
int i;
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
if (!strcmp(daemon_service[i].name, name)) {
die("No such service %s", name);
}
-static void make_service_overridable(const char *name, int ena) {
+static void make_service_overridable(const char *name, int ena)
+{
int i;
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
if (!strcmp(daemon_service[i].name, name)) {
/*
* Separate the "extra args" information as supplied by the client connection.
- * Any resulting data is squirrelled away in the given interpolation table.
+ * Any resulting data is squirreled away in the given interpolation table.
*/
static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
{
}
}
-void fill_in_extra_table_entries(struct interp *itable)
+static void fill_in_extra_table_entries(struct interp *itable)
{
char *hp;
* Replace literal host with lowercase-ized hostname.
*/
hp = interp_table[INTERP_SLOT_HOST].value;
+ if (!hp)
+ return;
for ( ; *hp; hp++)
*hp = tolower(*hp);
if (addr->sa_family == AF_INET) {
struct sockaddr_in *sin_addr = (void *) addr;
inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
- port = sin_addr->sin_port;
+ port = ntohs(sin_addr->sin_port);
#ifndef NO_IPV6
} else if (addr && addr->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6_addr = (void *) addr;
inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
strcat(buf, "]");
- port = sin6_addr->sin6_port;
+ port = ntohs(sin6_addr->sin6_port);
#endif
}
loginfo("Connection from %s:%d", addrbuf, port);
loginfo("Extended attributes (%d bytes) exist <%.*s>",
(int) pktlen - len,
(int) pktlen - len, line + len + 1);
- if (len && line[len-1] == '\n')
+ if (len && line[len-1] == '\n') {
line[--len] = 0;
+ pktlen--;
+ }
/*
* Initialize the path interpolation table for this connection.
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
struct daemon_service *s = &(daemon_service[i]);
int namelen = strlen(s->name);
- if (!strncmp("git-", line, 4) &&
+ if (!prefixcmp(line, "git-") &&
!strncmp(s->name, line + 4, namelen) &&
line[namelen + 4] == ' ') {
/*
char pbuf[NI_MAXSERV];
struct addrinfo hints, *ai0, *ai;
int gai;
+ long flags;
sprintf(pbuf, "%d", listen_port);
memset(&hints, 0, sizeof(hints));
continue; /* not fatal */
}
+ flags = fcntl(sockfd, F_GETFD, 0);
+ if (flags >= 0)
+ fcntl(sockfd, F_SETFD, flags | FD_CLOEXEC);
+
socklist = xrealloc(socklist, sizeof(int) * (socknum + 1));
socklist[socknum++] = sockfd;
{
struct sockaddr_in sin;
int sockfd;
+ long flags;
memset(&sin, 0, sizeof sin);
sin.sin_family = AF_INET;
return 0;
}
+ flags = fcntl(sockfd, F_GETFD, 0);
+ if (flags >= 0)
+ fcntl(sockfd, F_SETFD, flags | FD_CLOEXEC);
+
*socklist_p = xmalloc(sizeof(int));
**socklist_p = sockfd;
return 1;
FILE *f = fopen(path, "w");
if (!f)
die("cannot open pid file %s: %s", path, strerror(errno));
- fprintf(f, "%d\n", getpid());
- fclose(f);
+ if (fprintf(f, "%d\n", getpid()) < 0 || fclose(f) != 0)
+ die("failed to write pid file %s: %s", path, strerror(errno));
}
static int serve(char *listen_addr, int listen_port, struct passwd *pass, gid_t gid)
for (i = 1; i < argc; i++) {
char *arg = argv[i];
- if (!strncmp(arg, "--listen=", 9)) {
+ if (!prefixcmp(arg, "--listen=")) {
char *p = arg + 9;
char *ph = listen_addr = xmalloc(strlen(arg + 9) + 1);
while (*p)
*ph = 0;
continue;
}
- if (!strncmp(arg, "--port=", 7)) {
+ if (!prefixcmp(arg, "--port=")) {
char *end;
unsigned long n;
n = strtoul(arg+7, &end, 0);
export_all_trees = 1;
continue;
}
- if (!strncmp(arg, "--timeout=", 10)) {
+ if (!prefixcmp(arg, "--timeout=")) {
timeout = atoi(arg+10);
continue;
}
- if (!strncmp(arg, "--init-timeout=", 15)) {
+ if (!prefixcmp(arg, "--init-timeout=")) {
init_timeout = atoi(arg+15);
continue;
}
strict_paths = 1;
continue;
}
- if (!strncmp(arg, "--base-path=", 12)) {
+ if (!prefixcmp(arg, "--base-path=")) {
base_path = arg+12;
continue;
}
- if (!strncmp(arg, "--interpolated-path=", 20)) {
+ if (!strcmp(arg, "--base-path-relaxed")) {
+ base_path_relaxed = 1;
+ continue;
+ }
+ if (!prefixcmp(arg, "--interpolated-path=")) {
interpolated_path = arg+20;
continue;
}
user_path = "";
continue;
}
- if (!strncmp(arg, "--user-path=", 12)) {
+ if (!prefixcmp(arg, "--user-path=")) {
user_path = arg + 12;
continue;
}
- if (!strncmp(arg, "--pid-file=", 11)) {
+ if (!prefixcmp(arg, "--pid-file=")) {
pid_file = arg + 11;
continue;
}
log_syslog = 1;
continue;
}
- if (!strncmp(arg, "--user=", 7)) {
+ if (!prefixcmp(arg, "--user=")) {
user_name = arg + 7;
continue;
}
- if (!strncmp(arg, "--group=", 8)) {
+ if (!prefixcmp(arg, "--group=")) {
group_name = arg + 8;
continue;
}
- if (!strncmp(arg, "--enable=", 9)) {
+ if (!prefixcmp(arg, "--enable=")) {
enable_service(arg + 9, 1);
continue;
}
- if (!strncmp(arg, "--disable=", 10)) {
+ if (!prefixcmp(arg, "--disable=")) {
enable_service(arg + 10, 0);
continue;
}
- if (!strncmp(arg, "--allow-override=", 17)) {
+ if (!prefixcmp(arg, "--allow-override=")) {
make_service_overridable(arg + 17, 1);
continue;
}
- if (!strncmp(arg, "--forbid-override=", 18)) {
+ if (!prefixcmp(arg, "--forbid-override=")) {
make_service_overridable(arg + 18, 0);
continue;
}
usage(daemon_usage);
}
+ if (log_syslog) {
+ openlog("git-daemon", 0, LOG_DAEMON);
+ set_die_routine(daemon_die);
+ }
+
if (inetd_mode && (group_name || user_name))
die("--user and --group are incompatible with --inetd");
}
}
- if (log_syslog) {
- openlog("git-daemon", 0, LOG_DAEMON);
- set_die_routine(daemon_die);
- }
-
if (strict_paths && (!ok_paths || !*ok_paths))
die("option --strict-paths requires a whitelist");
+ if (base_path) {
+ struct stat st;
+
+ if (stat(base_path, &st) || !S_ISDIR(st.st_mode))
+ die("base-path '%s' does not exist or "
+ "is not a directory", base_path);
+ }
+
if (inetd_mode) {
struct sockaddr_storage ss;
struct sockaddr *peer = (struct sockaddr *)&ss;