dovecot-2.0: eacces_error_get*() works now properly when process...

dovecot at dovecot.org dovecot at dovecot.org
Mon Jun 22 06:32:04 EEST 2009


details:   http://hg.dovecot.org/dovecot-2.0/rev/e22e36a61fdd
changeset: 9501:e22e36a61fdd
user:      Timo Sirainen <tss at iki.fi>
date:      Sun Jun 21 23:31:46 2009 -0400
description:
eacces_error_get*() works now properly when process's real uid != effective uid.

diffstat:

1 file changed, 77 insertions(+), 10 deletions(-)
src/lib/eacces-error.c |   87 ++++++++++++++++++++++++++++++++++++++++++------

diffs (127 lines):

diff -r 5074914f2dba -r e22e36a61fdd src/lib/eacces-error.c
--- a/src/lib/eacces-error.c	Sun Jun 21 23:31:10 2009 -0400
+++ b/src/lib/eacces-error.c	Sun Jun 21 23:31:46 2009 -0400
@@ -2,12 +2,83 @@
 
 #include "lib.h"
 #include "str.h"
+#include "restrict-access.h"
 #include "eacces-error.h"
 
 #include <sys/stat.h>
 #include <unistd.h>
 #include <pwd.h>
 #include <grp.h>
+
+static bool is_in_group(gid_t gid)
+{
+	const gid_t *gids;
+	unsigned int i, count;
+
+	if (getegid() == gid)
+		return TRUE;
+
+	gids = restrict_get_groups_list(&count);
+	for (i = 0; i < count; i++) {
+		if (gids[i] == gid)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static int test_access(const char *path, int mode, string_t *errmsg)
+{
+	struct stat st;
+
+	if (getuid() == geteuid()) {
+		if (access(path, mode) == 0)
+			return 0;
+
+		if (errno != EACCES) {
+			str_printfa(errmsg, " access(%s, %d) failed: %m",
+				    path, mode);
+		}
+		return -1;
+	} 
+
+	/* access() uses real uid, not effective uid.
+	   we'll have to do these checks manually. */
+	switch (mode) {
+	case X_OK:
+		if (stat(t_strconcat(path, "/test", NULL), &st) == 0)
+			return 0;
+		if (errno == ENOENT || errno == ENOTDIR)
+			return 0;
+		if (errno != EACCES)
+			str_printfa(errmsg, " stat(%s/test) failed: %m", path);
+		return -1;
+	case R_OK:
+		mode = 04;
+		break;
+	case W_OK:
+		mode = 02;
+		break;
+	default:
+		i_unreached();
+	}
+
+	if (stat(path, &st) < 0) {
+		str_printfa(errmsg, " stat(%s) failed: %m", path);
+		return -1;
+	}
+
+	if (st.st_uid == geteuid())
+		st.st_mode = (st.st_mode & 0700) >> 6;
+	else if (is_in_group(st.st_gid))
+		st.st_mode = (st.st_mode & 0070) >> 3;
+	else
+		st.st_mode = (st.st_mode & 0007);
+
+	if ((st.st_mode & mode) != 0)
+		return 0;
+	errno = EACCES;
+	return -1;
+}
 
 static const char *
 eacces_error_get_full(const char *func, const char *path, bool creating)
@@ -16,7 +87,7 @@ eacces_error_get_full(const char *func, 
 	const struct passwd *pw;
 	const struct group *group;
 	string_t *errmsg;
-	struct stat st;
+	struct stat st, dir_st;
 	int ret = -1;
 
 	errmsg = t_str_new(256);
@@ -49,25 +120,21 @@ eacces_error_get_full(const char *func, 
 		}
 		prev_path = dir;
 		dir = "/";
+		dir_st = st;
 	}
 
 	if (ret == 0) {
 		/* dir is the first parent directory we can stat() */
-		if (access(dir, X_OK) < 0) {
+		if (test_access(dir, X_OK, errmsg) < 0) {
 			if (errno == EACCES)
 				str_printfa(errmsg, " missing +x perm: %s", dir);
-			else
-				str_printfa(errmsg, " access(%s, x) failed: %m", dir);
-		} else if (creating && access(dir, W_OK) < 0) {
+		} else if (creating && test_access(dir, W_OK, errmsg) < 0) {
 			if (errno == EACCES)
 				str_printfa(errmsg, " missing +w perm: %s", dir);
-			else
-				str_printfa(errmsg, " access(%s, w) failed: %m", dir);
-		} else if (prev_path == path && access(path, R_OK) < 0) {
+		} else if (prev_path == path &&
+			   test_access(path, R_OK, errmsg) < 0) {
 			if (errno == EACCES)
 				str_printfa(errmsg, " missing +r perm: %s", path);
-			else
-				str_printfa(errmsg, " access(%s, r) failed: %m", path);
 		} else
 			str_printfa(errmsg, " UNIX perms seem ok, ACL problem?");
 	}


More information about the dovecot-cvs mailing list