Skip to content

Commit

Permalink
Add --single-file flag (emikulic#63)
Browse files Browse the repository at this point in the history
* add working chroot functionality for --single-file
* create change_root helper function
  • Loading branch information
g-rden authored Mar 23, 2024
1 parent 64fe4cc commit 5031bf1
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 19 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ Run in the background and create a pidfile:
./darkhttpd /var/www/htdocs --pidfile /var/run/httpd.pid --daemon
```

Serve only one file instead of a whole directory:

```
./darkhttpd ~/public_html/index.html --single-file
```

Web forward (301) requests for some hosts:

```
Expand Down
76 changes: 57 additions & 19 deletions darkhttpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ static char *logfile_name = NULL; /* NULL = no logging */
static FILE *logfile = NULL;
static char *pidfile_name = NULL; /* NULL = no pidfile */
static int want_chroot = 0, want_daemon = 0, want_accf = 0,
want_keepalive = 1, want_server_id = 1;
want_keepalive = 1, want_server_id = 1, want_single_file = 0;
static char *server_hdr = NULL;
static char *auth_key = NULL; /* NULL or "Basic base64_of_password" */
static char *custom_hdrs = NULL;
Expand Down Expand Up @@ -966,6 +966,9 @@ static void usage(const char *argv0) {
#endif
printf("\t--no-keepalive\n"
"\t\tDisables HTTP Keep-Alive functionality.\n\n");
printf("\t--single-file\n"
"\t\tOnly serve a single file provided as /path/to/file instead\n"
"\t\tof a whole directory.\n\n");
printf("\t--forward host url (default: don't forward)\n"
"\t\tWeb forward (301 redirect).\n"
"\t\tRequests to the host are redirected to the corresponding url.\n"
Expand Down Expand Up @@ -1176,6 +1179,9 @@ static void parse_commandline(const int argc, char *argv[]) {
else if (strcmp(argv[i], "--syslog") == 0) {
syslog_enabled = 1;
}
else if (strcmp(argv[i], "--single-file") == 0) {
want_single_file = 1;
}
else if (strcmp(argv[i], "--forward") == 0) {
const char *host, *url;
if (++i >= argc)
Expand Down Expand Up @@ -2170,8 +2176,12 @@ static void process_get(struct connection *conn) {
return;
}

/* does it end in a slash? serve up url/index_name */
if (decoded_url[strlen(decoded_url)-1] == '/') {
if (want_single_file) {
target = xstrdup(wwwroot);
mimetype = url_content_type(wwwroot);
}
else if (decoded_url[strlen(decoded_url)-1] == '/') {
/* does it end in a slash? serve up url/index_name */
xasprintf(&target, "%s%s%s", wwwroot, decoded_url, index_name);
if (!file_exists(target)) {
free(target);
Expand Down Expand Up @@ -2231,7 +2241,7 @@ static void process_get(struct connection *conn) {
}

/* make sure it's a regular file */
if (S_ISDIR(filestat.st_mode)) {
if ((S_ISDIR(filestat.st_mode)) && (!want_single_file)) {
redirect(conn, "%s/", conn->url);
return;
}
Expand Down Expand Up @@ -2906,6 +2916,48 @@ static void pidfile_create(void) {
}
/* [<-] end of pidfile helpers. */

static void change_root(void) {
#ifdef HAVE_NON_ROOT_CHROOT
/* We run this even as root, which should never be a bad thing. */
int arg = PROC_NO_NEW_PRIVS_ENABLE;
int error = procctl(P_PID, (int)getpid(), PROC_NO_NEW_PRIVS_CTL, &arg);
if (error != 0)
err(1, "procctl");
#endif

tzset(); /* read /etc/localtime before we chroot */
if (want_single_file) {
off_t ofs;
size_t len = strlen(wwwroot) + 1;
char *path = xstrdup(wwwroot);
for (ofs = strlen(wwwroot);
(ofs >= 0) && (wwwroot[ofs] != '/');
ofs--)
;
/* wwwroot file is not in current directory */
if (ofs >= 0) {
path[ofs + 1] = '\0';
if (chdir(path) == -1)
err(1, "chdir(%s)", path);
memmove(wwwroot, &wwwroot[ofs], len - ofs);
} else {
path[0] = '.';
path[1] = '\0';
}
if (chroot(path) == -1)
err(1, "chroot(%s)", path);
printf("chrooted to `%s'\n", path);
free(path);
} else {
if (chdir(wwwroot) == -1)
err(1, "chdir(%s)", wwwroot);
if (chroot(wwwroot) == -1)
err(1, "chroot(%s)", wwwroot);
printf("chrooted to `%s'\n", wwwroot);
wwwroot[0] = '\0'; /* empty string */
}
}

/* Close all sockets and FILEs and exit. */
static void stop_running(int sig unused) {
running = 0;
Expand Down Expand Up @@ -2949,21 +3001,7 @@ int main(int argc, char **argv) {

/* security */
if (want_chroot) {
#ifdef HAVE_NON_ROOT_CHROOT
/* We run this even as root, which should never be a bad thing. */
int arg = PROC_NO_NEW_PRIVS_ENABLE;
int error = procctl(P_PID, (int)getpid(), PROC_NO_NEW_PRIVS_CTL, &arg);
if (error != 0)
err(1, "procctl");
#endif

tzset(); /* read /etc/localtime before we chroot */
if (chdir(wwwroot) == -1)
err(1, "chdir(%s)", wwwroot);
if (chroot(wwwroot) == -1)
err(1, "chroot(%s)", wwwroot);
printf("chrooted to `%s'\n", wwwroot);
wwwroot[0] = '\0'; /* empty string */
change_root();
}
if (drop_gid != INVALID_GID) {
gid_t list[1];
Expand Down

0 comments on commit 5031bf1

Please sign in to comment.