mirror of
https://git.FreeBSD.org/ports.git
synced 2024-12-14 03:10:47 +00:00
81eba088c7
While playing with mod_dav, I found out that it didn't follow symlinks properly. Looking through various mailinglists I found this patch by Mark Montague (markmont@umich.edu) I do not say we should include this patch by default, but if a knob called WITH_SYMLINK_PATCH could be made which adds these patches it would help people already. PR: ports/59163 Submitted by: Edwin Groothuis <edwin@mavetju.org> Approved by: maintainer timeout
219 lines
7.1 KiB
C
219 lines
7.1 KiB
C
--- dav_fs_repos.c.orig Mon Nov 5 16:20:32 2001
|
|
+++ dav_fs_repos.c Tue Nov 11 14:14:11 2003
|
|
@@ -154,6 +154,30 @@
|
|
**
|
|
** PRIVATE REPOSITORY FUNCTIONS
|
|
*/
|
|
+
|
|
+static int symlink_aware_rmdir(const char *pathname)
|
|
+{
|
|
+
|
|
+ struct stat finfo;
|
|
+
|
|
+ /* Is this a symbolic link? */
|
|
+ /* If not, just call rmdir() and return. */
|
|
+
|
|
+ if (lstat(pathname, &finfo) != 0) {
|
|
+ return -1; /* and errno set by lstat() */
|
|
+ }
|
|
+
|
|
+ if (!S_ISLNK(finfo.st_mode)) {
|
|
+ return rmdir(pathname); /* and errno set by rmdir() */
|
|
+ }
|
|
+
|
|
+ /* It's a symlink. */
|
|
+ /* Remove the link itself, leaving the link's target untouched. */
|
|
+ return unlink(pathname); /* and errno set by unlink() */
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
pool *dav_fs_pool(const dav_resource *resource)
|
|
{
|
|
return resource->info->pool;
|
|
@@ -258,13 +282,25 @@
|
|
/* chmod() the destination if the source is executable, and the
|
|
* destination already exists. */
|
|
if ((mode & DAV_FS_MODE_XUSR) && (dst_finfo != NULL) &&
|
|
- (dst_finfo->st_mode != 0)) {
|
|
+ (dst_finfo->st_mode != 0) && !S_ISLNK(dst_finfo->st_mode)) {
|
|
if (chmod(dst, mode) == -1) {
|
|
return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
|
|
"Could not set permissions on destination");
|
|
}
|
|
}
|
|
|
|
+ /* If the destination is a symlink, break it so that we don't overwrite
|
|
+ * its target. */
|
|
+ if ((dst_finfo != NULL) && S_ISLNK(dst_finfo->st_mode)) {
|
|
+ if (unlink(dst) != 0) {
|
|
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
|
|
+ "Could not break destination symlink");
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ dav_set_bufsize(p, pbuf, DAV_FS_COPY_BLOCKSIZE);
|
|
+
|
|
dav_set_bufsize(p, pbuf, DAV_FS_COPY_BLOCKSIZE);
|
|
|
|
if ((fdi = open(src, O_RDONLY | O_BINARY)) == -1) {
|
|
@@ -565,8 +601,6 @@
|
|
ctx->pool = r->pool;
|
|
ctx->finfo = r->finfo;
|
|
|
|
- (void) ap_update_mtime(r, r->finfo.st_mtime);
|
|
-
|
|
/* Preserve case on OSes which fold canonical filenames */
|
|
#if MODULE_MAGIC_NUMBER_MAJOR > 19990320 || (MODULE_MAGIC_NUMBER_MAJOR == 19990320 && MODULE_MAGIC_NUMBER_MINOR >= 8)
|
|
filename = r->case_preserved_filename;
|
|
@@ -606,9 +640,18 @@
|
|
resource->uri = r->uri;
|
|
}
|
|
|
|
- if (r->finfo.st_mode != 0) {
|
|
+ if (r->finfo.st_mode == 0) {
|
|
+ /* Apache says it does not exists. Is it maybe a dangling symlink? */
|
|
+ if (lstat(ctx->pathname, &(ctx->finfo)) != 0) {
|
|
+ ctx->finfo.st_mode = 0; /* does not exist */
|
|
+ }
|
|
+ }
|
|
+
|
|
+ (void) ap_update_mtime(r, ctx->finfo.st_mtime);
|
|
+
|
|
+ if (ctx->finfo.st_mode != 0) {
|
|
resource->exists = 1;
|
|
- resource->collection = S_ISDIR(r->finfo.st_mode);
|
|
+ resource->collection = S_ISDIR(ctx->finfo.st_mode);
|
|
|
|
/* unused info in the URL will indicate a null resource */
|
|
|
|
@@ -979,7 +1022,7 @@
|
|
* Note: when copying, we do not enable the postfix-traversal.
|
|
*/
|
|
/* ### we are ignoring any error here; what should we do? */
|
|
- (void) rmdir(srcinfo->pathname);
|
|
+ (void) symlink_aware_rmdir(srcinfo->pathname);
|
|
}
|
|
else {
|
|
/* copy/move of a collection. Create the new, target collection */
|
|
@@ -1157,6 +1200,15 @@
|
|
}
|
|
}
|
|
|
|
+ /* Renaming a symlink will only work in some circumstances (e.g., if
|
|
+ * old and new names are in the same directory). Be safe and break
|
|
+ * the link.
|
|
+ */
|
|
+ if (S_ISLNK(srcinfo->finfo.st_mode)) {
|
|
+ can_rename = 0;
|
|
+ }
|
|
+
|
|
+
|
|
/* if we can't simply renamed, then do it the hard way... */
|
|
if (!can_rename) {
|
|
if ((err = dav_fs_copymove_resource(1, src, dst, DAV_INFINITY, response)) == NULL) {
|
|
@@ -1226,8 +1278,31 @@
|
|
|
|
static dav_error * dav_fs_delete_walker(dav_walker_ctx *ctx, int calltype)
|
|
{
|
|
+ struct stat finfo;
|
|
dav_resource_private *info = ctx->resource->info;
|
|
|
|
+ /* If this is a symlink to a directory, immediately remove the link
|
|
+ * during any "prefix traversal", then signal our caller, dav_fs_walker(),
|
|
+ * that we're done and not to follow the link to the target's children.
|
|
+ */
|
|
+ if (ctx->resource->exists && ctx->resource->collection) {
|
|
+ if (lstat(info->pathname, &finfo) != 0) {
|
|
+ dav_add_response(ctx, ctx->resource->uri, HTTP_FORBIDDEN, NULL);
|
|
+ return NULL; /* failure */
|
|
+ }
|
|
+ if (S_ISLNK(finfo.st_mode)) {
|
|
+ if (unlink(info->pathname) != 0) {
|
|
+ /* ### assume there is a permissions problem */
|
|
+ /* ### use errno to generate DAV:responsedescription? */
|
|
+ dav_add_response(ctx, ctx->resource->uri, HTTP_FORBIDDEN, NULL);
|
|
+ return NULL; /* failure */
|
|
+ }
|
|
+ ctx->is_dir = 0; /* signal dav_fs_walker() that we're done */
|
|
+ return NULL; /* success */
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
/* do not attempt to remove a null resource,
|
|
* or a collection with children
|
|
*/
|
|
@@ -1237,7 +1312,7 @@
|
|
int result;
|
|
|
|
result = ctx->resource->collection
|
|
- ? rmdir(info->pathname)
|
|
+ ? symlink_aware_rmdir(info->pathname)
|
|
: remove(info->pathname);
|
|
|
|
/*
|
|
@@ -1320,20 +1395,25 @@
|
|
{
|
|
dav_error *err = NULL;
|
|
dav_walker_ctx *wctx = fsctx->wctx;
|
|
- int isdir = wctx->resource->collection;
|
|
DIR *dirp;
|
|
struct dirent *ep;
|
|
|
|
- /* ensure the context is prepared properly, then call the func */
|
|
+ /* ensure the context is prepared properly, then call the func.
|
|
+ * Note that if the resource is a symbolic link to a directory,
|
|
+ * and we should not recurse (for example, because we're deleting)
|
|
+ * func() will change wctx->is_dir to 0 to signal this.
|
|
+ */
|
|
+ wctx->is_dir = wctx->resource->collection;
|
|
err = (*wctx->func)(wctx,
|
|
- isdir
|
|
+ wctx->is_dir
|
|
? DAV_CALLTYPE_COLLECTION
|
|
: DAV_CALLTYPE_MEMBER);
|
|
if (err != NULL) {
|
|
return err;
|
|
}
|
|
|
|
- if (!isdir) {
|
|
+ /* At this point, wctx->is_dir actually means "should we recurse?" */
|
|
+ if (!wctx->is_dir) {
|
|
return NULL;
|
|
}
|
|
|
|
@@ -1397,11 +1477,15 @@
|
|
dav_buffer_place_mem(wctx->pool,
|
|
&fsctx->path1, ep->d_name, len + 1, 0);
|
|
|
|
- if (lstat(fsctx->path1.buf, &fsctx->info1.finfo) != 0) {
|
|
- /* woah! where'd it go? */
|
|
- /* ### should have a better error here */
|
|
- err = dav_new_error(wctx->pool, HTTP_NOT_FOUND, 0, NULL);
|
|
- break;
|
|
+ /* stat() not lstat() because we want to follow symlinks */
|
|
+ /* BUT, ignore dangling symlinks (symlinks whose targets don't exist) */
|
|
+ if (stat(fsctx->path1.buf, &fsctx->info1.finfo) != 0) {
|
|
+ if (lstat(fsctx->path1.buf, &fsctx->info1.finfo) != 0) {
|
|
+ /* woah! where'd it go? */
|
|
+ /* ### should have a better error here */
|
|
+ err = dav_new_error(wctx->pool, HTTP_NOT_FOUND, 0, NULL);
|
|
+ break;
|
|
+ }
|
|
}
|
|
|
|
/* copy the file to the URI, too. NOTE: we will pad an extra byte
|
|
@@ -1421,8 +1505,8 @@
|
|
/* set up the URI for the current resource */
|
|
fsctx->res1.uri = wctx->uri.buf;
|
|
|
|
- /* ### for now, only process regular files (e.g. skip symlinks) */
|
|
- if (S_ISREG(fsctx->info1.finfo.st_mode)) {
|
|
+ if (S_ISREG(fsctx->info1.finfo.st_mode) ||
|
|
+ S_ISLNK(fsctx->info1.finfo.st_mode)) {
|
|
/* call the function for the specified dir + file */
|
|
if ((err = (*wctx->func)(wctx, DAV_CALLTYPE_MEMBER)) != NULL) {
|
|
/* ### maybe add a higher-level description? */
|