dovecot-2.0: master: Handle better chdir()ing to home dir.
dovecot at dovecot.org
dovecot at dovecot.org
Sun Jul 26 21:37:21 EEST 2009
details: http://hg.dovecot.org/dovecot-2.0/rev/dfe15bb24fdb
changeset: 9657:dfe15bb24fdb
user: Timo Sirainen <tss at iki.fi>
date: Sun Jul 26 14:37:13 2009 -0400
description:
master: Handle better chdir()ing to home dir.
diffstat:
1 file changed, 68 insertions(+), 8 deletions(-)
src/master/service-process.c | 76 +++++++++++++++++++++++++++++++++++++-----
diffs (107 lines):
diff -r dc6d35b51b68 -r dfe15bb24fdb src/master/service-process.c
--- a/src/master/service-process.c Sun Jul 26 14:17:30 2009 -0400
+++ b/src/master/service-process.c Sun Jul 26 14:37:13 2009 -0400
@@ -15,6 +15,7 @@
#include "fd-close-on-exec.h"
#include "restrict-access.h"
#include "restrict-process-size.h"
+#include "eacces-error.h"
#include "master-service.h"
#include "master-service-settings.h"
#include "dup2-array.h"
@@ -33,6 +34,12 @@
#include <syslog.h>
#include <signal.h>
#include <sys/wait.h>
+
+/* Timeout chdir() completely after this many seconds */
+#define CHDIR_TIMEOUT 30
+/* Give a warning about chdir() taking a while if it took longer than this
+ many seconds to finish. */
+#define CHDIR_WARN_SECS 10
static void
service_dup_fds(struct service *service, int auth_fd, int std_fd,
@@ -247,6 +254,65 @@ static void auth_success_write(void)
auth_success_written = TRUE;
}
+static void chdir_to_home(const struct restrict_access_settings *rset,
+ const char *user, const char *home)
+{
+ unsigned int left;
+ int ret, chdir_errno;
+
+ if (*home != '/') {
+ i_fatal("user %s: Relative home directory paths not supported: "
+ "%s", user, home);
+ }
+
+ /* if home directory is NFS-mounted, we might not have access to it as
+ root. Change the effective UID and GID temporarily to make it
+ work. */
+ if (rset->uid != master_uid) {
+ if (setegid(rset->gid) < 0)
+ i_fatal("setegid(%s) failed: %m", dec2str(rset->gid));
+ if (seteuid(rset->uid) < 0)
+ i_fatal("seteuid(%s) failed: %m", dec2str(rset->uid));
+ }
+
+ alarm(CHDIR_TIMEOUT);
+ ret = chdir(home);
+ chdir_errno = errno;
+ if ((left = alarm(0)) < CHDIR_TIMEOUT - CHDIR_WARN_SECS) {
+ i_warning("user %s: chdir(%s) blocked for %u secs",
+ user, home, CHDIR_TIMEOUT - left);
+ }
+
+ errno = chdir_errno;
+ if (ret == 0) {
+ /* chdir succeeded */
+ } else if ((errno == ENOENT || errno == ENOTDIR || errno == EINTR) &&
+ rset->chroot_dir == NULL) {
+ /* Not chrooted, fallback to using /tmp.
+
+ ENOENT: No home directory yet, but it might be automatically
+ created by the service process, so don't complain.
+ ENOTDIR: This check is mainly for /dev/null home directory.
+ EINTR: chdir() timed out. */
+ } else if (errno == EACCES) {
+ i_fatal("user %s: %s", user, eacces_error_get("chdir", home));
+ } else {
+ i_fatal("user %s: chdir(%s) failed with uid %s: %m",
+ user, home, dec2str(rset->uid));
+ }
+ /* Change UID back. No need to change GID back, it doesn't
+ really matter. */
+ if (rset->uid != master_uid && seteuid(master_uid) < 0)
+ i_fatal("seteuid(%s) failed: %m", dec2str(master_uid));
+
+ if (ret < 0) {
+ /* We still have to change to some directory where we have
+ rx-access. /tmp should exist everywhere. */
+ if (chdir("/tmp") < 0)
+ i_fatal("chdir(/tmp) failed: %m");
+ }
+}
+
static void drop_privileges(struct service *service,
const char *const *auth_args)
{
@@ -286,14 +352,8 @@ static void drop_privileges(struct servi
validate_uid_gid(master_set, rset.uid, rset.gid, user);
}
- if (home != NULL) {
- if (*home != '/') {
- i_fatal("Relative home directory paths not supported "
- "(user %s): %s", user, home);
- }
- if (chdir(home) < 0 && errno != ENOENT)
- i_error("chdir(%s) failed: %m", home);
- }
+ if (home != NULL)
+ chdir_to_home(&rset, user, home);
if (service->set->drop_priv_before_exec) {
disallow_root = service->type == SERVICE_TYPE_AUTH_SERVER ||
More information about the dovecot-cvs
mailing list