masqmail

annotate src/local.c @ 323:29de6a1c4538

Fixed an important bug with folded headers! g_strconcat() returns a *copy* of the string, but hdr->value still pointed to the old header (which probably was a memory leak, too). If the folded part had been quite small it was likely that the new string was at the same position as the old one, thus making everything go well. But if pretty long headers were folded several times it was likely that the new string was allocated somewhere else in memory, thus breaking things. In result mails to lots of recipients (folded header) were frequently only sent to the ones in the first line. Sorry for the inconvenience.
author meillo@marmaro.de
date Fri, 03 Jun 2011 09:47:27 +0200
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 }