masqmail

annotate src/local.c @ 414:309935f59820

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