view src/local.c @ 378:5781ba87df95

Removed ident. This had been discussed on the mailing list in Oct 2011. Ident is hardly useful in typical setups for masqmail. Probably Oliver had used it in his setup; that would make sense. Now, I know of nobody who needs it.
author markus schnalke <meillo@marmaro.de>
date Sat, 14 Jan 2012 21:36:58 +0100
parents b27f66555ba8
children f609a05ddff8
line wrap: on
line source

/*
**  MasqMail
**  Copyright (C) 1999-2001 Oliver Kurth
**  Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
**
**  This program is free software; you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation; either version 2 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <sys/wait.h>
#include <sys/stat.h>

#include "masqmail.h"
#include "peopen.h"

static void
message_stream(FILE *out, message *msg, GList *hdr_list, guint flags)
{
	time_t now = time(NULL);
	GList *node;

	if (flags & MSGSTR_FROMLINE) {
		fprintf(out, "From <%s@%s> %s", msg->return_path->local_part, msg->return_path->domain, ctime(&now));
	}

	foreach(hdr_list, node) {
		header *hdr = (header *) (node->data);
		fputs(hdr->header, out);
	}
	putc('\n', out);
	foreach(msg->data_list, node) {
		/* From hack: */
		if (flags & MSGSTR_FROMHACK) {
			if (strncmp(node->data, "From ", 5) == 0)
				putc('>', out);
		}
		fputs(node->data, out);
	}
	putc('\n', out);
}

gboolean
append_file(message *msg, GList *hdr_list, gchar *user)
{
	struct passwd *pw;
	gboolean ok = FALSE;
	uid_t saved_uid = geteuid();
	gid_t saved_gid = getegid();
	gboolean uid_ok = TRUE, gid_ok = TRUE;
	gchar *filename;
	FILE *out;

	/* headers may be special for a local delivery */
	if (!hdr_list)
		hdr_list = msg->hdr_list;

	if (!(pw = getpwnam(user))) {
		logwrite(LOG_ALERT, "could not find password entry for user %s\n", user);
		errno = ENOENT;  /* getpwnam does not set errno correctly */
		return FALSE;
	}

	if (!conf.run_as_user) {
		uid_ok = (seteuid(0) == 0);
		if (uid_ok) {
			gid_ok = (setegid(conf.mail_gid) == 0);
			uid_ok = (seteuid(pw->pw_uid) == 0);
		}
		if (!uid_ok || !gid_ok) {
			logwrite(LOG_ALERT, "could not set uid or gid for local delivery, uid = %d: %s\n", pw->pw_uid, strerror(errno));
			return FALSE;
		}
	}

	DEBUG(5) debugf("running as euid %d, egid %d\n", geteuid(), getegid());

	filename = g_strdup_printf("%s/%s", conf.mail_dir, user);
	if (!(out = fopen(filename, "a"))) {
		logwrite(LOG_ALERT, "could not open file %s: %s\n", filename, strerror(errno));
	} else {
#ifdef USE_LIBLOCKFILE
		gint err;
		/* lock file using liblockfile */
		err = maillock(user, 3);
		if (err == 0) {
#else
		/* lock file: */
		struct flock lock;
		lock.l_type = F_WRLCK;
		lock.l_whence = SEEK_END;
		lock.l_start = lock.l_len = 0;
		if (fcntl(fileno(out), F_SETLK, &lock) != -1) {
#endif
			fchmod(fileno(out), 0600);
			message_stream(out, msg, hdr_list, MSGSTR_FROMLINE | MSGSTR_FROMHACK);
			ok = TRUE;

			/* close when still user */
			fclose(out);
#ifdef USE_LIBLOCKFILE
			mailunlock();
#endif
		} else {
			fclose(out);
#ifdef USE_LIBLOCKFILE
			DEBUG(3) debugf("could not lock file %s: error %d\n", filename, err);
		}  /* XEmacs indenting convenience... */
#else
			DEBUG(3) debugf("could not lock file %s: %s\n", filename, strerror(errno));
		}
#endif
	}
	g_free(filename);

	if (!conf.run_as_user) {
		uid_ok = (seteuid(0) == 0);
		if (uid_ok) {
			gid_ok = (setegid(saved_gid) == 0);
			uid_ok = (seteuid(saved_uid) == 0);
		}
	}

	if (!uid_ok || !gid_ok) {
		/*
		**  FIXME: if this fails we HAVE to exit, because we shall
		**  not run with some users id. But we do not return, and so
		**  this message will not be finished, so the user will get
		**  the message again next time a delivery is attempted...
		*/
		logwrite(LOG_ALERT, "could not set back uid or gid after local delivery: %s\n", strerror(errno));
		logwrite(LOG_ALERT, "uid=%d, gid=%d, euid=%d, egid=%d, want = %d, %d\n",
		         getuid(), getgid(), geteuid(), getegid(), saved_uid, saved_gid);
		logwrite(LOG_ALERT, "In case of trouble, see local.c:append_file() for details.\n", strerror(errno));
		exit(1);
	}
	return ok;
}

gboolean
pipe_out(message *msg, GList *hdr_list, address *rcpt, gchar *cmd, guint flags)
{
	gchar *envp[40];
	FILE *out;
	uid_t saved_uid = geteuid();
	gid_t saved_gid = getegid();
	gboolean ok = FALSE;
	gint i, n;
	pid_t pid;
	void (*old_signal) (int);
	int status;
	address *ancestor = addr_find_ancestor(rcpt);

	/* set uid and gid to the mail ids */
	if (!conf.run_as_user) {
		set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
	}

	/* set environment */
	n = 0;
	envp[n++] = g_strdup_printf("SENDER=%s@%s", msg->return_path->local_part, msg->return_path->domain);
	envp[n++] = g_strdup_printf("SENDER_DOMAIN=%s", msg->return_path->domain);
	envp[n++] = g_strdup_printf("SENDER_LOCAL=%s", msg->return_path->local_part);
	envp[n++] = g_strdup_printf("RECEIVED_HOST=%s", msg->received_host ? msg->received_host : "");

	envp[n++] = g_strdup_printf("RETURN_PATH=%s@%s", msg->return_path->local_part, msg->return_path->domain);
	envp[n++] = g_strdup_printf("DOMAIN=%s", ancestor->domain);

	envp[n++] = g_strdup_printf("LOCAL_PART=%s", ancestor->local_part);
	envp[n++] = g_strdup_printf("USER=%s", ancestor->local_part);
	envp[n++] = g_strdup_printf("LOGNAME=%s", ancestor->local_part);

	envp[n++] = g_strdup_printf("MESSAGE_ID=%s", msg->uid);
	envp[n++] = g_strdup_printf("QUALIFY_DOMAIN=%s", conf.host_name);

	envp[n] = NULL;

	old_signal = signal(SIGCHLD, SIG_DFL);

	out = peidopen(cmd, "w", envp, &pid, conf.mail_uid, conf.mail_gid);
	if (!out) {
		logwrite(LOG_ALERT, "could not open pipe '%s': %s\n", cmd, strerror(errno));
	} else {
		message_stream(out, msg, hdr_list, flags);

		fclose(out);

		waitpid(pid, &status, 0);

		if (WEXITSTATUS(status) != 0) {
			int exstat = WEXITSTATUS(status);
			logwrite(LOG_ALERT, "process returned %d (%s)\n", exstat, ext_strerror(1024 + exstat));
			errno = 1024 + exstat;
		} else if (WIFSIGNALED(status)) {
			logwrite(LOG_ALERT, "process got signal %d\n", WTERMSIG(status));
		} else
			ok = TRUE;

	}

	signal(SIGCHLD, old_signal);

	/* free environment */
	for (i = 0; i < n; i++) {
		g_free(envp[i]);
	}

	/* set uid and gid back */
	if (!conf.run_as_user) {
		set_euidgid(saved_uid, saved_gid, NULL, NULL);
	}

	return ok;
}