* they change, all the way down.
*/
+
+/*
+ * We don't want people able to serve up pipes, or unix sockets, or other
+ * scary things. Note that symlink tests are performed later.
+ */
+static int check_safe_file(request_rec *r)
+{
+ if (r->finfo.st_mode == 0 /* doesn't exist */
+ || S_ISDIR (r->finfo.st_mode)
+ || S_ISREG (r->finfo.st_mode)
+ || S_ISLNK (r->finfo.st_mode)) {
+ return OK;
+ }
+ log_reason("object is not a file, directory or symlink", r->filename, r);
+ return HTTP_FORBIDDEN;
+}
+
+
int check_symlinks (char *d, int opts)
{
struct stat lfi, fi;
if (res != OK) {
return res;
}
-
+
+ if ((res = check_safe_file(r))) {
+ return res;
+ }
+
if (test_filename[strlen(test_filename)-1] == '/')
--num_dirs;
- if (S_ISDIR (r->finfo.st_mode)) ++num_dirs;
+ if (S_ISDIR (r->finfo.st_mode)) {
+ ++num_dirs;
+ }
for (i = 1; i <= num_dirs; ++i) {
core_dir_config *core_dir =
r->per_dir_config = per_dir_defaults;
- if ((res = check_symlinks (r->filename, allow_options(r))))
- {
+ /* Symlink permissions are determined by the parent. If the request is for
+ * a directory then applying the symlink test here would use the
+ * permissions of the directory as opposed to its parent. Consider a
+ * symlink pointing to a dir with a .htaccess disallowing symlinks. If you
+ * access /symlink (or /symlink/) you would get a 403 without this S_ISDIR
+ * test. But if you accessed /symlink/index.html, for example, you would
+ * *not* get the 403.
+ */
+ if (!S_ISDIR (r->finfo.st_mode)
+ && (res = check_symlinks (r->filename, allow_options(r)))) {
log_reason("Symbolic link not allowed", r->filename, r);
return res;
}
rnew->finfo.st_mode = 0;
}
+ if ((res = check_safe_file(rnew))) {
+ rnew->status = res;
+ return rnew;
+ }
+
rnew->per_dir_config = r->per_dir_config;
if ((res = check_symlinks (rnew->filename, allow_options (rnew)))) {
#include <sys/stat.h>
#include "util_script.h"
#include "http_log.h"
+#include "http_request.h"
#define DEFAULT_METADIR ".web"
#define DEFAULT_METASUFFIX ".meta"
char *last_slash;
char *real_file;
char *scrap_book;
- struct stat meta_stat;
FILE *f;
cern_meta_config *cmc ;
int rv;
+ request_rec *rr;
cmc = get_module_config (r->server->module_config,
&cern_meta_module);
metafilename = pstrcat(r->pool, "/", scrap_book, "/", cmc->metadir, "/", real_file, cmc->metasuffix, NULL);
- /*
- * stat can legitimately fail for a bewildering number of reasons,
- * only one of which implies the file isn't there. A hardened
- * version of this module should test for all conditions, but later...
+ /* XXX: it sucks to require this subrequest to complete, because this
+ * means people must leave their meta files accessible to the world.
+ * A better solution might be a "safe open" feature of pfopen to avoid
+ * pipes, symlinks, and crap like that.
*/
- if (stat(metafilename, &meta_stat) == -1) {
- /* stat failed, possibly file missing */
+ rr = sub_req_lookup_file (metafilename, r);
+ if (rr->status != HTTP_OK) {
+ destroy_sub_req (rr);
return DECLINED;
- };
-
- /*
- * this check is to be found in other Jan/96 Apache code, I've
- * not been able to find any corroboration in the man pages but
- * I've been wrong before so I'll put it in anyway. Never
- * admit to being clueless...
- */
- if ( meta_stat.st_mode == 0 ) {
- /* stat failed, definately file missing */
- return DECLINED;
- };
+ }
+ destroy_sub_req (rr);
f = pfopen (r->pool, metafilename, "r");
-
if (f == NULL) {
+ if (errno == ENOENT) {
+ return DECLINED;
+ }
log_reason("meta file permissions deny server access", metafilename, r);
return FORBIDDEN;
};
}
const char *add_header(cmd_parms *cmd, void *d, char *name) {
+ if (strchr (name, '/')) {
+ return "HeaderName cannot contain a /";
+ }
push_item(((dir_config_rec *)d)->hdr_list, 0, NULL, cmd->path, name);
return NULL;
}
const char *add_readme(cmd_parms *cmd, void *d, char *name) {
+ if (strchr (name, '/')) {
+ return "ReadmeName cannot contain a /";
+ }
push_item(((dir_config_rec *)d)->rdme_list, 0, NULL, cmd->path, name);
return NULL;
}
FILE *f;
struct stat finfo;
int plaintext=0;
+ request_rec *rr;
+ /* XXX: this is a load of crap, it needs to do a full sub_req_lookup_uri */
fn = make_full_path(r->pool, name, readme_fname);
fn = pstrcat(r->pool, fn, ".html", NULL);
if(stat(fn,&finfo) == -1) {
rputs("<PRE>\n", r);
}
else if (rule) rputs("<HR>\n", r);
+ /* XXX: when the above is rewritten properly, this necessary security
+ * check will be redundant. -djg */
+ rr = sub_req_lookup_file (fn, r);
+ if (rr->status != HTTP_OK) {
+ destroy_sub_req (rr);
+ return 0;
+ }
+ destroy_sub_req (rr);
if(!(f = pfopen(r->pool,fn,"r")))
return 0;
if (!plaintext)
FILE *thefile = NULL;
int x,y,n,p;
+ if (r->status != HTTP_OK) {
+ return NULL;
+ }
if (r->content_type && !strcmp(r->content_type,"text/html") && !r->content_encoding) {
if(!(thefile = pfopen(r->pool, r->filename,"r")))
return NULL;
}
const char *add_header(cmd_parms *cmd, void *d, char *name) {
+ if (strchr (name, '/')) {
+ return "HeaderName cannot contain a /";
+ }
push_item(((dir_config_rec *)d)->hdr_list, 0, NULL, cmd->path, name);
return NULL;
}
const char *add_readme(cmd_parms *cmd, void *d, char *name) {
+ if (strchr (name, '/')) {
+ return "ReadmeName cannot contain a /";
+ }
push_item(((dir_config_rec *)d)->rdme_list, 0, NULL, cmd->path, name);
return NULL;
}
FILE *f;
struct stat finfo;
int plaintext=0;
+ request_rec *rr;
+ /* XXX: this is a load of crap, it needs to do a full sub_req_lookup_uri */
fn = make_full_path(r->pool, name, readme_fname);
fn = pstrcat(r->pool, fn, ".html", NULL);
if(stat(fn,&finfo) == -1) {
rputs("<PRE>\n", r);
}
else if (rule) rputs("<HR>\n", r);
+ /* XXX: when the above is rewritten properly, this necessary security
+ * check will be redundant. -djg */
+ rr = sub_req_lookup_file (fn, r);
+ if (rr->status != HTTP_OK) {
+ destroy_sub_req (rr);
+ return 0;
+ }
+ destroy_sub_req (rr);
if(!(f = pfopen(r->pool,fn,"r")))
return 0;
if (!plaintext)
FILE *thefile = NULL;
int x,y,n,p;
+ if (r->status != HTTP_OK) {
+ return NULL;
+ }
if (r->content_type && !strcmp(r->content_type,"text/html") && !r->content_encoding) {
if(!(thefile = pfopen(r->pool, r->filename,"r")))
return NULL;