dovecot-2.2-pigeonhole: lib-sieve: Implemented utility functions...

pigeonhole at rename-it.nl pigeonhole at rename-it.nl
Sat Mar 14 13:49:44 UTC 2015


details:   http://hg.rename-it.nl/dovecot-2.2-pigeonhole/rev/163a8fbfdc1a
changeset: 2003:163a8fbfdc1a
user:      Stephan Bosch <stephan at rename-it.nl>
date:      Thu Mar 12 21:03:28 2015 +0100
description:
lib-sieve: Implemented utility functions to normalize filesystem paths.

diffstat:

 src/lib-sieve/util/Makefile.am |    6 +-
 src/lib-sieve/util/realpath.c  |  237 +++++++++++++++++++++++++++++++++++++++++
 src/lib-sieve/util/realpath.h  |   33 +++++
 3 files changed, 274 insertions(+), 2 deletions(-)

diffs (299 lines):

diff -r 986ea6e9c270 -r 163a8fbfdc1a src/lib-sieve/util/Makefile.am
--- a/src/lib-sieve/util/Makefile.am	Wed Mar 11 23:59:51 2015 +0100
+++ b/src/lib-sieve/util/Makefile.am	Thu Mar 12 21:03:28 2015 +0100
@@ -12,13 +12,15 @@
 	rfc2822.c \
 	program-client-local.c \
 	program-client-remote.c \
-	program-client.c
+	program-client.c \
+	realpath.c
 
 headers = \
 	edit-mail.h \
 	rfc2822.h \
 	program-client-private.h \
-	program-client.h
+	program-client.h \
+	realpath.h
 
 pkginc_libdir=$(dovecot_pkgincludedir)/sieve
 pkginc_lib_HEADERS = $(headers)
diff -r 986ea6e9c270 -r 163a8fbfdc1a src/lib-sieve/util/realpath.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-sieve/util/realpath.c	Thu Mar 12 21:03:28 2015 +0100
@@ -0,0 +1,237 @@
+/* Copyright (c) 2009-2015 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+
+#include "realpath.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// FIXME: move/merge to Dovecot
+
+#define REALPATH_MAX_PATH      8*1024
+#define REALPATH_MAX_SYMLINKS  80
+
+static int t_getcwd_alloc(char **dir_r, size_t *asize_r)
+{
+	/* @UNSAFE */
+	char *dir;
+	size_t asize = 128;
+
+	dir = t_buffer_get(asize);
+	while (getcwd(dir, asize) == NULL) {
+		if (errno != ERANGE)
+			return -1;
+		asize = nearest_power(asize+1);
+		dir = t_buffer_get(asize);
+	}
+	*asize_r = asize;
+	*dir_r = dir;
+	return 0;
+}
+
+static int path_normalize(const char *path, bool resolve_links,
+	const char **npath_r)
+{
+	/* @UNSAFE */
+	unsigned int link_count = 0;
+	char *npath, *npath_pos;
+	const char *p;
+	size_t asize;
+
+  if (path[0] != '/') {
+		/* relative; initialize npath with current directory */
+		if (t_getcwd_alloc(&npath, &asize) < 0)
+			return -1;
+		npath_pos = npath + strlen(npath);
+		i_assert(npath[0] == '/');
+  } else {
+		/* absolute; initialize npath with root */
+		asize = 128;
+		npath = t_buffer_get(asize);
+		npath[0] = '/';
+		npath_pos = npath + 1;
+	}
+
+	p = path;
+	while (*p != '\0') {
+		struct stat st;
+		ptrdiff_t seglen;
+		const char *segend;
+
+		/* skip duplicate shashes */
+		while (*p == '/')
+			p++;
+
+		/* find end of path segment */
+		for (segend = p; *segend != '\0' && *segend != '/'; segend++);
+	
+		if (segend == p)
+			break; /* '\0' */
+		seglen = segend - p;
+		if (seglen == 1 && p[0] == '.') {
+			/* a reference to this segment; nothing to do */
+    } else if (seglen == 2 && p[0] == '.' && p[1] == '.') {
+  		/* a reference to parent segment; back up to previous slash */
+			if (npath_pos > npath + 1) {
+				if (*npath_pos == '/')
+					npath_pos--;
+				for (; *npath_pos != '/'; npath_pos--);
+			}
+		} else {
+			/* make sure npath now ends in slash */
+			if (*(npath_pos-1) != '/')
+				*(npath_pos++) = '/';
+
+			/* allocate space if necessary */
+			if ((npath_pos + seglen + 1) >= (npath + asize)) {
+			  ptrdiff_t npath_offset = npath_pos - npath;
+				asize = nearest_power(npath_offset + seglen + 2);
+				npath = t_buffer_reget(npath, asize);
+		    npath_pos = npath + npath_offset;
+		  }
+
+			/* copy segment to normalized path */
+			(void)memmove(npath_pos, p, segend - p);
+			npath_pos += seglen;
+		}
+
+		if (resolve_links) {
+			/* stat path up to here (segend points to tail) */
+			*npath_pos = '\0';
+			if (lstat(npath, &st) < 0)
+			  return -1;
+
+			if (S_ISLNK (st.st_mode)) {
+				/* symlink */
+				char *npath_link;
+				size_t lsize = 128, tlen = strlen(segend), espace;
+				size_t ltlen = (link_count == 0 ? 0 : tlen);
+				ssize_t ret;
+
+				/* limit link dereferences */
+			  if (++link_count > REALPATH_MAX_SYMLINKS) {
+					errno = ELOOP;
+					return -1;
+				}
+
+				/* allocate space for preserving tail of previous symlink and
+				   first attempt at reading symlink with room for the tail
+
+				   buffer will look like this:
+				   [npath][0][preserved tail][link buffer][room for tail][0]
+				 */
+				espace = ltlen + tlen + 2;
+				if ((npath_pos + espace + lsize) >= (npath + asize)) {
+					ptrdiff_t npath_offset = npath_pos - npath;
+					asize = nearest_power((npath_offset + espace + lsize) + 1);
+					lsize = asize - (npath_offset + espace);
+					npath = t_buffer_reget(npath, asize);
+				  npath_pos = npath + npath_offset;
+				}
+
+				if (ltlen > 0) {
+					/* preserve tail just after end of npath */
+					(void)memmove(npath_pos + 1, segend, ltlen);
+				}
+
+				/* read the symlink after the preserved tail */
+				for (;;) {
+					npath_link = (npath_pos + 1) + ltlen;
+
+					/* attempt to read the link */
+					if ((ret=readlink(npath, npath_link, lsize)) < 0)
+						return -1;
+					if ((size_t)ret < lsize)
+						break;
+				
+					/* sum of new symlink content length and path tail length may not
+						 exeed maximum */
+				  if ((size_t)(ret + tlen) >= REALPATH_MAX_PATH) {
+						errno = ENAMETOOLONG;
+						return -1;
+					}
+
+					/* try again with bigger buffer */
+					espace = ltlen + tlen + 2;
+					if ((npath_pos + espace + lsize) >= (npath + asize)) {
+						ptrdiff_t npath_offset = npath_pos - npath;
+						asize = nearest_power((npath_offset + espace + lsize) + 1);
+						lsize = asize - (npath_offset + espace);
+						npath = t_buffer_reget(npath, asize);
+						npath_pos = npath + npath_offset;
+					}
+				}
+
+				/* add tail of previous path at end of symlink */
+				if (ltlen > 0)
+					(void)memcpy(npath_link + ret, npath_pos + 1, tlen);
+				else
+					(void)memcpy(npath_link + ret, segend, tlen);
+				*(npath_link+ret+tlen) = '\0';
+
+				/* use as new source path */
+				path = segend = npath_link;
+			
+				if (path[0] == '/') {
+					/* absolute symlink; start over at root */
+					npath_pos = npath + 1;
+				} else {
+					/* relative symlink; back up to previous segment */
+					if (npath_pos > npath + 1) {
+						if (*npath_pos == '/')
+							npath_pos--;
+						for (; *npath_pos != '/'; npath_pos--);
+					}
+				}
+
+			} else if (*segend != '\0' && !S_ISDIR (st.st_mode)) {
+				/* not last segment, but not a directory either */
+				errno = ENOTDIR;
+				return -1;
+			}
+		}
+
+		p = segend;
+	}
+
+	/* remove any trailing slash */
+ 	if (npath_pos > npath + 1 && *(npath_pos-1) == '/')
+ 	  npath_pos--;
+ 	*npath_pos = '\0';
+
+	t_buffer_alloc(npath_pos - npath + 1);
+	*npath_r = npath;
+	return 0;
+}
+
+int t_normpath(const char *path, const char **npath_r)
+{
+	return path_normalize(path, FALSE, npath_r);
+}
+
+int t_normpath_to(const char *path, const char *root,
+	const char **npath_r)
+{
+	if (*path == '/')
+		return t_normpath(path, npath_r);
+
+	return t_normpath(t_strconcat(root, "/", path, NULL), npath_r);
+}
+
+int t_realpath(const char *path, const char **npath_r)
+{
+	return path_normalize(path, TRUE, npath_r);
+}
+
+int t_realpath_to(const char *path, const char *root,
+	const char **npath_r)
+{
+	if (*path == '/')
+		return t_realpath(path, npath_r);
+
+	return t_realpath(t_strconcat(root, "/", path, NULL), npath_r);
+}
diff -r 986ea6e9c270 -r 163a8fbfdc1a src/lib-sieve/util/realpath.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-sieve/util/realpath.h	Thu Mar 12 21:03:28 2015 +0100
@@ -0,0 +1,33 @@
+#ifndef REALPATH_H
+#define REALPATH_H
+
+/* Returns path as the normalized absolute path, which means that './'
+   and '../' components are resolved, and that duplicate and trailing
+   slashes are removed. If it's not already the absolute path, it's
+   assumed to be relative to the current working directory.
+
+   NOTE: Be careful with this function. The resolution of '../' components
+   with the parent component as if it were a normal directory is not valid
+   if the path contains symbolic links.
+ */
+int t_normpath(const char *path, const char **npath_r);
+/* Like t_normpath(), but path is relative to given root. */
+int t_normpath_to(const char *path, const char *root,
+	const char **npath_r);
+
+/* Returns path as the real normalized absolute path, which means that all
+   symbolic links in the path are resolved, that './' and '../' components
+   are resolved, and that duplicate and trailing slashes are removed. If it's
+   not already the absolute path, it's assumed to be relative to the current
+   working directory.
+
+   NOTE: This function calls stat() for each path component and more when
+   there are symbolic links (just like POSIX realpath()).
+ */
+int t_realpath(const char *path, const char **npath_r);
+/* Like t_realpath(), but path is relative to given root. */
+int t_realpath_to(const char *path, const char *root,
+	const char **npath_r);
+
+#endif
+


More information about the dovecot-cvs mailing list