meillo@367: /* meillo@367: ** MasqMail meillo@367: ** Copyright (C) 1999-2001 Oliver Kurth meillo@367: ** Copyright (C) 2010 markus schnalke meillo@367: ** meillo@367: ** This program is free software; you can redistribute it and/or modify meillo@367: ** it under the terms of the GNU General Public License as published by meillo@367: ** the Free Software Foundation; either version 2 of the License, or meillo@367: ** (at your option) any later version. meillo@367: ** meillo@367: ** This program is distributed in the hope that it will be useful, meillo@367: ** but WITHOUT ANY WARRANTY; without even the implied warranty of meillo@367: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the meillo@367: ** GNU General Public License for more details. meillo@367: ** meillo@367: ** You should have received a copy of the GNU General Public License meillo@367: ** along with this program; if not, write to the Free Software meillo@367: ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. meillo@0: */ meillo@0: meillo@15: #include meillo@169: #include meillo@15: meillo@0: #include "masqmail.h" meillo@0: #include "peopen.h" meillo@0: meillo@10: static void meillo@366: message_stream(FILE *out, message *msg, GList *hdr_list, guint flags) meillo@0: { meillo@10: time_t now = time(NULL); meillo@10: GList *node; meillo@10: meillo@10: if (flags & MSGSTR_FROMLINE) { meillo@408: fprintf(out, "From <%s@%s> %s", msg->return_path->local_part, meillo@408: msg->return_path->domain, ctime(&now)); meillo@10: } meillo@10: meillo@10: foreach(hdr_list, node) { meillo@10: header *hdr = (header *) (node->data); meillo@10: fputs(hdr->header, out); meillo@10: } meillo@10: putc('\n', out); meillo@10: foreach(msg->data_list, node) { meillo@10: /* From hack: */ meillo@10: if (flags & MSGSTR_FROMHACK) { meillo@10: if (strncmp(node->data, "From ", 5) == 0) meillo@10: putc('>', out); meillo@10: } meillo@10: fputs(node->data, out); meillo@10: } meillo@10: putc('\n', out); meillo@0: } meillo@0: meillo@10: gboolean meillo@366: append_file(message *msg, GList *hdr_list, gchar *user) meillo@0: { meillo@10: struct passwd *pw; meillo@10: gboolean ok = FALSE; meillo@332: uid_t saved_uid = geteuid(); meillo@332: gid_t saved_gid = getegid(); meillo@332: gboolean uid_ok = TRUE, gid_ok = TRUE; meillo@332: gchar *filename; meillo@332: FILE *out; meillo@0: meillo@10: /* headers may be special for a local delivery */ meillo@332: if (!hdr_list) meillo@10: hdr_list = msg->hdr_list; meillo@0: meillo@332: if (!(pw = getpwnam(user))) { meillo@408: logwrite(LOG_ALERT, "could not find password entry for " meillo@408: "user %s\n", user); meillo@10: errno = ENOENT; /* getpwnam does not set errno correctly */ meillo@332: return FALSE; meillo@0: } meillo@0: meillo@332: if (!conf.run_as_user) { meillo@332: uid_ok = (seteuid(0) == 0); meillo@332: if (uid_ok) { meillo@332: gid_ok = (setegid(conf.mail_gid) == 0); meillo@332: uid_ok = (seteuid(pw->pw_uid) == 0); meillo@332: } meillo@332: if (!uid_ok || !gid_ok) { meillo@408: logwrite(LOG_ALERT, "could not set uid or gid for " meillo@408: "local delivery, uid = %d: %s\n", meillo@408: pw->pw_uid, strerror(errno)); meillo@332: return FALSE; meillo@332: } meillo@332: } meillo@332: meillo@332: DEBUG(5) debugf("running as euid %d, egid %d\n", geteuid(), getegid()); meillo@332: meillo@332: filename = g_strdup_printf("%s/%s", conf.mail_dir, user); meillo@332: if (!(out = fopen(filename, "a"))) { meillo@408: logwrite(LOG_ALERT, "could not open file %s: %s\n", meillo@408: filename, strerror(errno)); meillo@332: } else { meillo@332: #ifdef USE_LIBLOCKFILE meillo@332: gint err; meillo@332: /* lock file using liblockfile */ meillo@332: err = maillock(user, 3); meillo@332: if (err == 0) { meillo@332: #else meillo@332: /* lock file: */ meillo@332: struct flock lock; meillo@332: lock.l_type = F_WRLCK; meillo@332: lock.l_whence = SEEK_END; meillo@332: lock.l_start = lock.l_len = 0; meillo@332: if (fcntl(fileno(out), F_SETLK, &lock) != -1) { meillo@332: #endif meillo@332: fchmod(fileno(out), 0600); meillo@408: message_stream(out, msg, hdr_list, meillo@408: MSGSTR_FROMLINE | MSGSTR_FROMHACK); meillo@332: ok = TRUE; meillo@332: meillo@332: /* close when still user */ meillo@332: fclose(out); meillo@332: #ifdef USE_LIBLOCKFILE meillo@332: mailunlock(); meillo@332: #endif meillo@332: } else { meillo@332: fclose(out); meillo@332: #ifdef USE_LIBLOCKFILE meillo@408: DEBUG(3) debugf("could not lock file %s: error %d\n", meillo@408: filename, err); meillo@332: } /* XEmacs indenting convenience... */ meillo@332: #else meillo@408: DEBUG(3) debugf("could not lock file %s: %s\n", meillo@408: filename, strerror(errno)); meillo@332: } meillo@332: #endif meillo@332: } meillo@332: g_free(filename); meillo@332: meillo@332: if (!conf.run_as_user) { meillo@332: uid_ok = (seteuid(0) == 0); meillo@332: if (uid_ok) { meillo@332: gid_ok = (setegid(saved_gid) == 0); meillo@332: uid_ok = (seteuid(saved_uid) == 0); meillo@332: } meillo@332: } meillo@332: meillo@332: if (!uid_ok || !gid_ok) { meillo@367: /* meillo@367: ** FIXME: if this fails we HAVE to exit, because we shall meillo@367: ** not run with some users id. But we do not return, and so meillo@367: ** this message will not be finished, so the user will get meillo@367: ** the message again next time a delivery is attempted... meillo@367: */ meillo@408: logwrite(LOG_ALERT, "could not set back uid or gid after " meillo@408: "local delivery: %s\n", strerror(errno)); meillo@408: logwrite(LOG_ALERT, "uid=%d, gid=%d, euid=%d, egid=%d, " meillo@408: "want = %d, %d\n", getuid(), getgid(), meillo@408: geteuid(), getegid(), saved_uid, saved_gid); meillo@408: logwrite(LOG_ALERT, "In case of trouble, see " meillo@408: "local.c:append_file() for details.\n", meillo@408: strerror(errno)); meillo@332: exit(1); meillo@332: } meillo@10: return ok; meillo@0: } meillo@0: meillo@0: gboolean meillo@366: pipe_out(message *msg, GList *hdr_list, address *rcpt, gchar *cmd, guint flags) meillo@0: { meillo@10: gchar *envp[40]; meillo@10: FILE *out; meillo@10: uid_t saved_uid = geteuid(); meillo@10: gid_t saved_gid = getegid(); meillo@10: gboolean ok = FALSE; meillo@10: gint i, n; meillo@10: pid_t pid; meillo@10: void (*old_signal) (int); meillo@10: int status; meillo@332: address *ancestor = addr_find_ancestor(rcpt); meillo@0: meillo@10: /* set uid and gid to the mail ids */ meillo@10: if (!conf.run_as_user) { meillo@408: set_euidgid(conf.mail_uid, conf.mail_gid, meillo@408: &saved_uid, &saved_gid); meillo@10: } meillo@0: meillo@10: /* set environment */ meillo@332: n = 0; meillo@408: envp[n++] = g_strdup_printf("SENDER=%s@%s", meillo@408: msg->return_path->local_part, meillo@408: msg->return_path->domain); meillo@408: envp[n++] = g_strdup_printf("SENDER_DOMAIN=%s", meillo@408: msg->return_path->domain); meillo@408: envp[n++] = g_strdup_printf("SENDER_LOCAL=%s", meillo@408: msg->return_path->local_part); meillo@408: envp[n++] = g_strdup_printf("RECEIVED_HOST=%s", meillo@408: msg->received_host ? msg->received_host : ""); meillo@0: meillo@408: envp[n++] = g_strdup_printf("RETURN_PATH=%s@%s", meillo@408: msg->return_path->local_part, meillo@408: msg->return_path->domain); meillo@408: envp[n++] = g_strdup_printf("DOMAIN=%s", meillo@408: ancestor->domain); meillo@0: meillo@332: envp[n++] = g_strdup_printf("LOCAL_PART=%s", ancestor->local_part); meillo@332: envp[n++] = g_strdup_printf("USER=%s", ancestor->local_part); meillo@332: envp[n++] = g_strdup_printf("LOGNAME=%s", ancestor->local_part); meillo@0: meillo@332: envp[n++] = g_strdup_printf("MESSAGE_ID=%s", msg->uid); meillo@332: envp[n++] = g_strdup_printf("QUALIFY_DOMAIN=%s", conf.host_name); meillo@0: meillo@332: envp[n] = NULL; meillo@0: meillo@10: old_signal = signal(SIGCHLD, SIG_DFL); meillo@0: meillo@10: out = peidopen(cmd, "w", envp, &pid, conf.mail_uid, conf.mail_gid); meillo@332: if (!out) { meillo@408: logwrite(LOG_ALERT, "could not open pipe '%s': %s\n", meillo@408: cmd, strerror(errno)); meillo@332: } else { meillo@10: message_stream(out, msg, hdr_list, flags); meillo@0: meillo@10: fclose(out); meillo@0: meillo@10: waitpid(pid, &status, 0); meillo@0: meillo@10: if (WEXITSTATUS(status) != 0) { meillo@10: int exstat = WEXITSTATUS(status); meillo@408: logwrite(LOG_ALERT, "process returned %d (%s)\n", meillo@408: exstat, ext_strerror(1024 + exstat)); meillo@10: errno = 1024 + exstat; meillo@10: } else if (WIFSIGNALED(status)) { meillo@408: logwrite(LOG_ALERT, "process got signal %d\n", meillo@408: WTERMSIG(status)); meillo@10: } else meillo@10: ok = TRUE; meillo@0: meillo@332: } meillo@0: meillo@10: signal(SIGCHLD, old_signal); meillo@0: meillo@10: /* free environment */ meillo@10: for (i = 0; i < n; i++) { meillo@10: g_free(envp[i]); meillo@10: } meillo@0: meillo@10: /* set uid and gid back */ meillo@10: if (!conf.run_as_user) { meillo@10: set_euidgid(saved_uid, saved_gid, NULL, NULL); meillo@10: } meillo@0: meillo@10: return ok; meillo@0: }