|
|
|
@ -99,7 +99,7 @@ uridecode(char *uri)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
drop_privileges(const char *user, const char *chroot_dir, const char *cgi_dir)
|
|
|
|
|
drop_privileges(const char *user)
|
|
|
|
|
{
|
|
|
|
|
struct passwd *pw;
|
|
|
|
|
|
|
|
|
@ -107,22 +107,38 @@ drop_privileges(const char *user, const char *chroot_dir, const char *cgi_dir)
|
|
|
|
|
* use chroot() if an user is specified requires root user to be
|
|
|
|
|
* running the program to run chroot() and then drop privileges
|
|
|
|
|
*/
|
|
|
|
|
if (*user) {
|
|
|
|
|
|
|
|
|
|
/* is root? */
|
|
|
|
|
if (getuid() != 0) {
|
|
|
|
|
status(41, "privileges issue, see logs");
|
|
|
|
|
stop(EXIT_FAILURE, "%s",
|
|
|
|
|
"chroot requires program to be run as root");
|
|
|
|
|
}
|
|
|
|
|
/* is root? */
|
|
|
|
|
if (getuid() != 0) {
|
|
|
|
|
status(41, "privileges issue, see logs");
|
|
|
|
|
stop(EXIT_FAILURE, "%s",
|
|
|
|
|
"chroot requires program to be run as root");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* search user uid from name */
|
|
|
|
|
if ((pw = getpwnam(user)) == NULL) {
|
|
|
|
|
status(41, "privileges issue, see logs");
|
|
|
|
|
stop(EXIT_FAILURE,
|
|
|
|
|
"the user %s can't be found on the system", user);
|
|
|
|
|
}
|
|
|
|
|
/* search user uid from name */
|
|
|
|
|
if ((pw = getpwnam(user)) == NULL) {
|
|
|
|
|
status(41, "privileges issue, see logs");
|
|
|
|
|
stop(EXIT_FAILURE,
|
|
|
|
|
"the user %s can't be found on the system", user);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* drop privileges */
|
|
|
|
|
if (setgroups(1, &pw->pw_gid) ||
|
|
|
|
|
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
|
|
|
|
|
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
|
|
|
|
|
status(41, "privileges issue, see logs");
|
|
|
|
|
stop(EXIT_FAILURE,
|
|
|
|
|
"dropping privileges to user %s (uid=%i) failed", \
|
|
|
|
|
user, pw->pw_uid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
set_rootdir(const char *chroot_dir, const char *cgi_dir, const char *user)
|
|
|
|
|
{
|
|
|
|
|
char capsule_dir[PATH_MAX] = {'\0'};
|
|
|
|
|
|
|
|
|
|
if (*user) {
|
|
|
|
|
/* chroot worked? */
|
|
|
|
|
if (chroot(chroot_dir) != 0) {
|
|
|
|
|
status(41, "privileges issue, see logs");
|
|
|
|
@ -130,26 +146,18 @@ drop_privileges(const char *user, const char *chroot_dir, const char *cgi_dir)
|
|
|
|
|
"the chroot_dir %s can't be used for chroot", chroot_dir);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chrooted = 1;
|
|
|
|
|
echdir("/");
|
|
|
|
|
/* drop privileges */
|
|
|
|
|
if (setgroups(1, &pw->pw_gid) ||
|
|
|
|
|
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
|
|
|
|
|
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
|
|
|
|
|
status(41, "privileges issue, see logs");
|
|
|
|
|
stop(EXIT_FAILURE,
|
|
|
|
|
"dropping privileges to user %s (uid=%i) failed", \
|
|
|
|
|
user, pw->pw_uid);
|
|
|
|
|
}
|
|
|
|
|
/* now chroot_dir is / */
|
|
|
|
|
esnprintf(capsule_dir, sizeof(capsule_dir), "%s", "/");
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
esnprintf(capsule_dir, sizeof(capsule_dir), "%s", chroot_dir);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef __OpenBSD__
|
|
|
|
|
/*
|
|
|
|
|
* prevent access to files other than the one in chroot_dir
|
|
|
|
|
*/
|
|
|
|
|
if (chrooted)
|
|
|
|
|
eunveil("/", "r");
|
|
|
|
|
else
|
|
|
|
|
eunveil(chroot_dir, "r");
|
|
|
|
|
eunveil(capsule_dir, "r");
|
|
|
|
|
|
|
|
|
|
/* permission to execute what's inside cgi_dir */
|
|
|
|
|
if (*cgi_dir)
|
|
|
|
@ -163,8 +171,7 @@ drop_privileges(const char *user, const char *chroot_dir, const char *cgi_dir)
|
|
|
|
|
else
|
|
|
|
|
epledge("stdio rpath", NULL);
|
|
|
|
|
#endif
|
|
|
|
|
if (!chrooted)
|
|
|
|
|
echdir(chroot_dir); /* move to the gemini data directory */
|
|
|
|
|
echdir(capsule_dir); /* move to the gemini data directory */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
@ -204,7 +211,7 @@ display_file(const char *path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
do_cgi(const char *chroot_dir, const char *cgi_dir, const char *path, const char *hostname, const char *query)
|
|
|
|
|
do_cgi(const char *rel_cgi_dir, const char *path, const char *hostname, const char *query)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* WARNING : this function is fragile since it
|
|
|
|
@ -213,21 +220,11 @@ do_cgi(const char *chroot_dir, const char *cgi_dir, const char *path, const char
|
|
|
|
|
* if two path refer to the same inode
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
char cgirp[PATH_MAX] = {'\0'}; /* cgi dir path in chroot */
|
|
|
|
|
char cgifp[PATH_MAX] = {'\0'}; /* cgi file to execute */
|
|
|
|
|
char *path_info = NULL;
|
|
|
|
|
|
|
|
|
|
/* check if path starts with cgi_dir
|
|
|
|
|
* compare beginning of path with cgi_dir
|
|
|
|
|
* cgi_dir + strlen(chrootdir) (skip chrootdir)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
esnprintf(cgirp, sizeof(cgirp), "%s", cgi_dir + strlen(chroot_dir));
|
|
|
|
|
/* ensure there is no leading / if user didn't end chrootdir with */
|
|
|
|
|
while (*cgirp == '/')
|
|
|
|
|
memmove(cgirp, cgirp+1, strlen(cgirp+1)+1);
|
|
|
|
|
|
|
|
|
|
if (strncmp(cgirp, path, strlen(cgirp)) != 0)
|
|
|
|
|
/* check if path starts with rel_cgi_dir */
|
|
|
|
|
if (strncmp(rel_cgi_dir, path, strlen(rel_cgi_dir)) != 0)
|
|
|
|
|
return 1; /* not in cgi_dir, go to display_file */
|
|
|
|
|
|
|
|
|
|
/* set env variables for CGI
|
|
|
|
@ -248,11 +245,11 @@ do_cgi(const char *chroot_dir, const char *cgi_dir, const char *path, const char
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* find next item after cgi_dir in path:
|
|
|
|
|
* path + strlen(cgirp) + 1 (skip '/')
|
|
|
|
|
* path + strlen(rel_cgi_dir) + 1 (skip '/')
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* cgi file to execute */
|
|
|
|
|
esnprintf(cgifp, sizeof(cgifp), "%s", path + strlen(cgirp) + 1);
|
|
|
|
|
esnprintf(cgifp, sizeof(cgifp), "%s", path + strlen(rel_cgi_dir) + 1);
|
|
|
|
|
if (!(*cgifp)) /* problem with cgi file, abort */
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
@ -267,7 +264,7 @@ do_cgi(const char *chroot_dir, const char *cgi_dir, const char *path, const char
|
|
|
|
|
esetenv("SCRIPT_NAME", cgifp, 1);
|
|
|
|
|
esetenv("SERVER_NAME", hostname, 1);
|
|
|
|
|
|
|
|
|
|
echdir(cgirp);
|
|
|
|
|
echdir(rel_cgi_dir);
|
|
|
|
|
|
|
|
|
|
cgi(cgifp);
|
|
|
|
|
return 0;
|
|
|
|
@ -394,25 +391,8 @@ remove_double_dot(char *request)
|
|
|
|
|
memmove(request, pos + 3, strlen(pos) + 1 - 3); /* "/.." = 3 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
set_path(char *path, size_t pathsiz, int virtualhost, const char *hostname)
|
|
|
|
|
{
|
|
|
|
|
/* path is in a subdir named hostname */
|
|
|
|
|
if (virtualhost) {
|
|
|
|
|
char tmp[GEMINI_REQUEST_MAX] = {'\0'};
|
|
|
|
|
esnprintf(tmp, sizeof(tmp), "%s/%s", hostname, path);
|
|
|
|
|
esnprintf(path, pathsiz, "%s", tmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strlen(path) == 0) /* this is root dir */
|
|
|
|
|
esnprintf(path, pathsiz, "./");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
check_path(char *path, size_t pathsiz, int virtualhost, size_t hstnm_o)
|
|
|
|
|
check_path(char *path, size_t pathsiz)
|
|
|
|
|
{
|
|
|
|
|
struct stat sb = {0};
|
|
|
|
|
char tmp[PATH_MAX] = {'\0'};
|
|
|
|
@ -431,11 +411,7 @@ check_path(char *path, size_t pathsiz, int virtualhost, size_t hstnm_o)
|
|
|
|
|
if (S_ISDIR(sb.st_mode)) {
|
|
|
|
|
/* check if dir path end with "/" */
|
|
|
|
|
if (path[strlen(path) - 1] != '/') {
|
|
|
|
|
/* redirect to the dir with appropriate ending '/' */
|
|
|
|
|
if (virtualhost) /* skip hostname */
|
|
|
|
|
esnprintf(tmp, sizeof(tmp), "/%s/", path+hstnm_o+1);
|
|
|
|
|
else
|
|
|
|
|
esnprintf(tmp, sizeof(tmp), "/%s/", path);
|
|
|
|
|
esnprintf(tmp, sizeof(tmp), "/%s/", path);
|
|
|
|
|
status(31, "%s", tmp);
|
|
|
|
|
stop(EXIT_SUCCESS, NULL);
|
|
|
|
|
}
|
|
|
|
|