masqmail

annotate src/local.c @ 281:ea5f86e0a81c

modes are now enforced exclusive Other MTAs (exim, postfix) are more relaxing, but as combinations of exclusive modes are senseless we behave more obvious if we fail early. This makes understanding the behavior easier too.
author markus schnalke <meillo@marmaro.de>
date Tue, 07 Dec 2010 14:04:56 -0300
parents 996b53a50f55
children 63efd381e27b
rev   line source
meillo@0 1 /* MasqMail
meillo@0 2 Copyright (C) 1999-2001 Oliver Kurth
meillo@224 3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
meillo@0 4
meillo@0 5 This program is free software; you can redistribute it and/or modify
meillo@0 6 it under the terms of the GNU General Public License as published by
meillo@0 7 the Free Software Foundation; either version 2 of the License, or
meillo@0 8 (at your option) any later version.
meillo@0 9
meillo@0 10 This program is distributed in the hope that it will be useful,
meillo@0 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@0 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@0 13 GNU General Public License for more details.
meillo@0 14
meillo@0 15 You should have received a copy of the GNU General Public License
meillo@0 16 along with this program; if not, write to the Free Software
meillo@0 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
meillo@0 18 */
meillo@0 19
meillo@15 20 #include <sys/wait.h>
meillo@169 21 #include <sys/stat.h>
meillo@15 22
meillo@0 23 #include "masqmail.h"
meillo@0 24 #include "peopen.h"
meillo@0 25
meillo@10 26 static void
meillo@10 27 message_stream(FILE * out, message * msg, GList * hdr_list, guint flags)
meillo@0 28 {
meillo@10 29 time_t now = time(NULL);
meillo@10 30 GList *node;
meillo@10 31
meillo@10 32 if (flags & MSGSTR_FROMLINE) {
meillo@10 33 fprintf(out, "From <%s@%s> %s", msg->return_path->local_part, msg->return_path->domain, ctime(&now));
meillo@10 34 }
meillo@10 35
meillo@10 36 foreach(hdr_list, node) {
meillo@10 37 header *hdr = (header *) (node->data);
meillo@10 38 fputs(hdr->header, out);
meillo@10 39 }
meillo@10 40 putc('\n', out);
meillo@10 41 foreach(msg->data_list, node) {
meillo@10 42 /* From hack: */
meillo@10 43 if (flags & MSGSTR_FROMHACK) {
meillo@10 44 if (strncmp(node->data, "From ", 5) == 0)
meillo@10 45 putc('>', out);
meillo@10 46 }
meillo@10 47 fputs(node->data, out);
meillo@10 48 }
meillo@10 49 putc('\n', out);
meillo@0 50 }
meillo@0 51
meillo@10 52 gboolean
meillo@10 53 append_file(message * msg, GList * hdr_list, gchar * user)
meillo@0 54 {
meillo@10 55 struct passwd *pw;
meillo@10 56 gboolean ok = FALSE;
meillo@0 57
meillo@10 58 /* headers may be special for a local delivery */
meillo@10 59 if (hdr_list == NULL)
meillo@10 60 hdr_list = msg->hdr_list;
meillo@0 61
meillo@10 62 if ((pw = getpwnam(user))) {
meillo@10 63 uid_t saved_uid = geteuid();
meillo@10 64 gid_t saved_gid = getegid();
meillo@10 65 gboolean uid_ok = TRUE, gid_ok = TRUE;
meillo@0 66
meillo@10 67 if (!conf.run_as_user) {
meillo@10 68 uid_ok = (seteuid(0) == 0);
meillo@10 69 if (uid_ok) {
meillo@10 70 gid_ok = (setegid(conf.mail_gid) == 0);
meillo@10 71 uid_ok = (seteuid(pw->pw_uid) == 0);
meillo@10 72 }
meillo@10 73 }
meillo@0 74
meillo@114 75 DEBUG(5) debugf("running as euid %d, egid %d\n", geteuid(), getegid());
meillo@0 76
meillo@10 77 if (uid_ok && gid_ok) {
meillo@10 78 gchar *filename;
meillo@10 79 FILE *out;
meillo@10 80
meillo@10 81 filename = g_strdup_printf("%s/%s", conf.mail_dir, user);
meillo@10 82 if ((out = fopen(filename, "a"))) {
meillo@0 83 #ifdef USE_LIBLOCKFILE
meillo@10 84 gint err;
meillo@10 85 /* lock file using liblockfile */
meillo@10 86 err = maillock(user, 3);
meillo@10 87 if (err == 0) {
meillo@0 88 #else
meillo@10 89 /* lock file: */
meillo@10 90 struct flock lock;
meillo@10 91 lock.l_type = F_WRLCK;
meillo@10 92 lock.l_whence = SEEK_END;
meillo@10 93 lock.l_start = lock.l_len = 0;
meillo@10 94 if (fcntl(fileno(out), F_SETLK, &lock) != -1) {
meillo@0 95 #endif
meillo@10 96 fchmod(fileno(out), 0600);
meillo@10 97 message_stream(out, msg, hdr_list, MSGSTR_FROMLINE | MSGSTR_FROMHACK);
meillo@10 98 ok = TRUE;
meillo@10 99
meillo@10 100 /* close when still user */
meillo@10 101 fclose(out);
meillo@0 102 #ifdef USE_LIBLOCKFILE
meillo@10 103 mailunlock();
meillo@0 104 #endif
meillo@10 105 } else {
meillo@10 106 fclose(out);
meillo@0 107 #ifdef USE_LIBLOCKFILE
meillo@10 108 DEBUG(3) debugf("could not lock file %s: error %d\n", filename, err);
meillo@10 109 } /* XEmacs indenting convenience... */
meillo@0 110 #else
meillo@10 111 DEBUG(3) debugf("could not lock file %s: %s\n", filename, strerror(errno));
meillo@10 112 }
meillo@10 113 #endif
meillo@10 114 } else {
meillo@10 115 logwrite(LOG_ALERT, "could not open file %s: %s\n", filename, strerror(errno));
meillo@10 116 }
meillo@10 117 g_free(filename);
meillo@10 118
meillo@10 119 if (!conf.run_as_user) {
meillo@10 120 uid_ok = (seteuid(0) == 0);
meillo@10 121 if (uid_ok) {
meillo@10 122 gid_ok = (setegid(saved_gid) == 0);
meillo@10 123 uid_ok = (seteuid(saved_uid) == 0);
meillo@10 124 }
meillo@10 125 }
meillo@10 126
meillo@10 127 if (!uid_ok || !gid_ok) {
meillo@10 128 /* FIXME: if this fails we HAVE to exit, because we shall not run
meillo@10 129 with some users id. But we do not return, and so this message
meillo@10 130 will not be finished, so the user will get the message again
meillo@10 131 next time a delivery is attempted... */
meillo@10 132 logwrite(LOG_ALERT, "could not set back uid or gid after local delivery: %s\n", strerror(errno));
meillo@10 133 logwrite(LOG_ALERT, "uid=%d, gid=%d, euid=%d, egid=%d, want = %d, %d\n",
meillo@10 134 getuid(), getgid(), geteuid(), getegid(), saved_uid, saved_gid);
meillo@262 135 exit(1);
meillo@10 136 }
meillo@10 137 } else {
meillo@10 138 logwrite(LOG_ALERT, "could not set uid or gid for local delivery, uid = %d: %s\n", pw->pw_uid, strerror(errno));
meillo@10 139 }
meillo@10 140 } else {
meillo@10 141 logwrite(LOG_ALERT, "could not find password entry for user %s\n", user);
meillo@10 142 errno = ENOENT; /* getpwnam does not set errno correctly */
meillo@0 143 }
meillo@0 144
meillo@10 145 return ok;
meillo@0 146 }
meillo@0 147
meillo@0 148 gboolean
meillo@10 149 pipe_out(message * msg, GList * hdr_list, address * rcpt, gchar * cmd, guint flags)
meillo@0 150 {
meillo@10 151 gchar *envp[40];
meillo@10 152 FILE *out;
meillo@10 153 uid_t saved_uid = geteuid();
meillo@10 154 gid_t saved_gid = getegid();
meillo@10 155 gboolean ok = FALSE;
meillo@10 156 gint i, n;
meillo@10 157 pid_t pid;
meillo@10 158 void (*old_signal) (int);
meillo@10 159 int status;
meillo@0 160
meillo@10 161 /* set uid and gid to the mail ids */
meillo@10 162 if (!conf.run_as_user) {
meillo@10 163 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
meillo@10 164 }
meillo@0 165
meillo@10 166 /* set environment */
meillo@10 167 {
meillo@10 168 gint i = 0;
meillo@10 169 address *ancestor = addr_find_ancestor(rcpt);
meillo@0 170
meillo@10 171 envp[i++] = g_strdup_printf("SENDER=%s@%s", msg->return_path->local_part, msg->return_path->domain);
meillo@10 172 envp[i++] = g_strdup_printf("SENDER_DOMAIN=%s", msg->return_path->domain);
meillo@10 173 envp[i++] = g_strdup_printf("SENDER_LOCAL=%s", msg->return_path->local_part);
meillo@10 174 envp[i++] = g_strdup_printf("RECEIVED_HOST=%s", msg->received_host ? msg->received_host : "");
meillo@0 175
meillo@10 176 envp[i++] = g_strdup_printf("RETURN_PATH=%s@%s", msg->return_path->local_part, msg->return_path->domain);
meillo@10 177 envp[i++] = g_strdup_printf("DOMAIN=%s", ancestor->domain);
meillo@0 178
meillo@10 179 envp[i++] = g_strdup_printf("LOCAL_PART=%s", ancestor->local_part);
meillo@10 180 envp[i++] = g_strdup_printf("USER=%s", ancestor->local_part);
meillo@10 181 envp[i++] = g_strdup_printf("LOGNAME=%s", ancestor->local_part);
meillo@0 182
meillo@10 183 envp[i++] = g_strdup_printf("MESSAGE_ID=%s", msg->uid);
meillo@10 184 envp[i++] = g_strdup_printf("QUALIFY_DOMAIN=%s", conf.host_name);
meillo@0 185
meillo@10 186 envp[i] = NULL;
meillo@10 187 n = i;
meillo@10 188 }
meillo@0 189
meillo@10 190 old_signal = signal(SIGCHLD, SIG_DFL);
meillo@0 191
meillo@10 192 out = peidopen(cmd, "w", envp, &pid, conf.mail_uid, conf.mail_gid);
meillo@10 193 if (out != NULL) {
meillo@10 194 message_stream(out, msg, hdr_list, flags);
meillo@0 195
meillo@10 196 fclose(out);
meillo@0 197
meillo@10 198 waitpid(pid, &status, 0);
meillo@0 199
meillo@10 200 if (WEXITSTATUS(status) != 0) {
meillo@10 201 int exstat = WEXITSTATUS(status);
meillo@10 202 logwrite(LOG_ALERT, "process returned %d (%s)\n", exstat, ext_strerror(1024 + exstat));
meillo@10 203 errno = 1024 + exstat;
meillo@10 204 } else if (WIFSIGNALED(status)) {
meillo@10 205 logwrite(LOG_ALERT, "process got signal %d\n", WTERMSIG(status));
meillo@10 206 } else
meillo@10 207 ok = TRUE;
meillo@0 208
meillo@10 209 } else
meillo@10 210 logwrite(LOG_ALERT, "could not open pipe '%s': %s\n", cmd, strerror(errno));
meillo@0 211
meillo@10 212 signal(SIGCHLD, old_signal);
meillo@0 213
meillo@10 214 /* free environment */
meillo@10 215 for (i = 0; i < n; i++) {
meillo@10 216 g_free(envp[i]);
meillo@10 217 }
meillo@0 218
meillo@10 219 /* set uid and gid back */
meillo@10 220 if (!conf.run_as_user) {
meillo@10 221 set_euidgid(saved_uid, saved_gid, NULL, NULL);
meillo@10 222 }
meillo@0 223
meillo@10 224 return ok;
meillo@0 225 }