masqmail

annotate src/local.c @ 366:41958685480d

Switched to `type *name' style Andrew Koenig's ``C Traps and Pitfalls'' (Ch.2.1) convinced me that it is best to go with the way C had been designed. The ``declaration reflects use'' concept conflicts with a ``type* name'' notation. Hence I switched.
author markus schnalke <meillo@marmaro.de>
date Thu, 22 Sep 2011 15:07:40 +0200
parents 63efd381e27b
children b27f66555ba8
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@366 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@366 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@332 57 uid_t saved_uid = geteuid();
meillo@332 58 gid_t saved_gid = getegid();
meillo@332 59 gboolean uid_ok = TRUE, gid_ok = TRUE;
meillo@332 60 gchar *filename;
meillo@332 61 FILE *out;
meillo@0 62
meillo@10 63 /* headers may be special for a local delivery */
meillo@332 64 if (!hdr_list)
meillo@10 65 hdr_list = msg->hdr_list;
meillo@0 66
meillo@332 67 if (!(pw = getpwnam(user))) {
meillo@10 68 logwrite(LOG_ALERT, "could not find password entry for user %s\n", user);
meillo@10 69 errno = ENOENT; /* getpwnam does not set errno correctly */
meillo@332 70 return FALSE;
meillo@0 71 }
meillo@0 72
meillo@332 73 if (!conf.run_as_user) {
meillo@332 74 uid_ok = (seteuid(0) == 0);
meillo@332 75 if (uid_ok) {
meillo@332 76 gid_ok = (setegid(conf.mail_gid) == 0);
meillo@332 77 uid_ok = (seteuid(pw->pw_uid) == 0);
meillo@332 78 }
meillo@332 79 if (!uid_ok || !gid_ok) {
meillo@332 80 logwrite(LOG_ALERT, "could not set uid or gid for local delivery, uid = %d: %s\n", pw->pw_uid, strerror(errno));
meillo@332 81 return FALSE;
meillo@332 82 }
meillo@332 83 }
meillo@332 84
meillo@332 85 DEBUG(5) debugf("running as euid %d, egid %d\n", geteuid(), getegid());
meillo@332 86
meillo@332 87 filename = g_strdup_printf("%s/%s", conf.mail_dir, user);
meillo@332 88 if (!(out = fopen(filename, "a"))) {
meillo@332 89 logwrite(LOG_ALERT, "could not open file %s: %s\n", filename, strerror(errno));
meillo@332 90 } else {
meillo@332 91 #ifdef USE_LIBLOCKFILE
meillo@332 92 gint err;
meillo@332 93 /* lock file using liblockfile */
meillo@332 94 err = maillock(user, 3);
meillo@332 95 if (err == 0) {
meillo@332 96 #else
meillo@332 97 /* lock file: */
meillo@332 98 struct flock lock;
meillo@332 99 lock.l_type = F_WRLCK;
meillo@332 100 lock.l_whence = SEEK_END;
meillo@332 101 lock.l_start = lock.l_len = 0;
meillo@332 102 if (fcntl(fileno(out), F_SETLK, &lock) != -1) {
meillo@332 103 #endif
meillo@332 104 fchmod(fileno(out), 0600);
meillo@332 105 message_stream(out, msg, hdr_list, MSGSTR_FROMLINE | MSGSTR_FROMHACK);
meillo@332 106 ok = TRUE;
meillo@332 107
meillo@332 108 /* close when still user */
meillo@332 109 fclose(out);
meillo@332 110 #ifdef USE_LIBLOCKFILE
meillo@332 111 mailunlock();
meillo@332 112 #endif
meillo@332 113 } else {
meillo@332 114 fclose(out);
meillo@332 115 #ifdef USE_LIBLOCKFILE
meillo@332 116 DEBUG(3) debugf("could not lock file %s: error %d\n", filename, err);
meillo@332 117 } /* XEmacs indenting convenience... */
meillo@332 118 #else
meillo@332 119 DEBUG(3) debugf("could not lock file %s: %s\n", filename, strerror(errno));
meillo@332 120 }
meillo@332 121 #endif
meillo@332 122 }
meillo@332 123 g_free(filename);
meillo@332 124
meillo@332 125 if (!conf.run_as_user) {
meillo@332 126 uid_ok = (seteuid(0) == 0);
meillo@332 127 if (uid_ok) {
meillo@332 128 gid_ok = (setegid(saved_gid) == 0);
meillo@332 129 uid_ok = (seteuid(saved_uid) == 0);
meillo@332 130 }
meillo@332 131 }
meillo@332 132
meillo@332 133 if (!uid_ok || !gid_ok) {
meillo@332 134 /* FIXME: if this fails we HAVE to exit, because we shall not run
meillo@332 135 with some users id. But we do not return, and so this message
meillo@332 136 will not be finished, so the user will get the message again
meillo@332 137 next time a delivery is attempted... */
meillo@332 138 logwrite(LOG_ALERT, "could not set back uid or gid after local delivery: %s\n", strerror(errno));
meillo@332 139 logwrite(LOG_ALERT, "uid=%d, gid=%d, euid=%d, egid=%d, want = %d, %d\n",
meillo@332 140 getuid(), getgid(), geteuid(), getegid(), saved_uid, saved_gid);
meillo@332 141 logwrite(LOG_ALERT, "In case of trouble, see local.c:append_file() for details.\n", strerror(errno));
meillo@332 142 exit(1);
meillo@332 143 }
meillo@10 144 return ok;
meillo@0 145 }
meillo@0 146
meillo@0 147 gboolean
meillo@366 148 pipe_out(message *msg, GList *hdr_list, address *rcpt, gchar *cmd, guint flags)
meillo@0 149 {
meillo@10 150 gchar *envp[40];
meillo@10 151 FILE *out;
meillo@10 152 uid_t saved_uid = geteuid();
meillo@10 153 gid_t saved_gid = getegid();
meillo@10 154 gboolean ok = FALSE;
meillo@10 155 gint i, n;
meillo@10 156 pid_t pid;
meillo@10 157 void (*old_signal) (int);
meillo@10 158 int status;
meillo@332 159 address *ancestor = addr_find_ancestor(rcpt);
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@332 167 n = 0;
meillo@332 168 envp[n++] = g_strdup_printf("SENDER=%s@%s", msg->return_path->local_part, msg->return_path->domain);
meillo@332 169 envp[n++] = g_strdup_printf("SENDER_DOMAIN=%s", msg->return_path->domain);
meillo@332 170 envp[n++] = g_strdup_printf("SENDER_LOCAL=%s", msg->return_path->local_part);
meillo@332 171 envp[n++] = g_strdup_printf("RECEIVED_HOST=%s", msg->received_host ? msg->received_host : "");
meillo@0 172
meillo@332 173 envp[n++] = g_strdup_printf("RETURN_PATH=%s@%s", msg->return_path->local_part, msg->return_path->domain);
meillo@332 174 envp[n++] = g_strdup_printf("DOMAIN=%s", ancestor->domain);
meillo@0 175
meillo@332 176 envp[n++] = g_strdup_printf("LOCAL_PART=%s", ancestor->local_part);
meillo@332 177 envp[n++] = g_strdup_printf("USER=%s", ancestor->local_part);
meillo@332 178 envp[n++] = g_strdup_printf("LOGNAME=%s", ancestor->local_part);
meillo@0 179
meillo@332 180 envp[n++] = g_strdup_printf("MESSAGE_ID=%s", msg->uid);
meillo@332 181 envp[n++] = g_strdup_printf("QUALIFY_DOMAIN=%s", conf.host_name);
meillo@0 182
meillo@332 183 envp[n] = NULL;
meillo@0 184
meillo@10 185 old_signal = signal(SIGCHLD, SIG_DFL);
meillo@0 186
meillo@10 187 out = peidopen(cmd, "w", envp, &pid, conf.mail_uid, conf.mail_gid);
meillo@332 188 if (!out) {
meillo@332 189 logwrite(LOG_ALERT, "could not open pipe '%s': %s\n", cmd, strerror(errno));
meillo@332 190 } else {
meillo@10 191 message_stream(out, msg, hdr_list, flags);
meillo@0 192
meillo@10 193 fclose(out);
meillo@0 194
meillo@10 195 waitpid(pid, &status, 0);
meillo@0 196
meillo@10 197 if (WEXITSTATUS(status) != 0) {
meillo@10 198 int exstat = WEXITSTATUS(status);
meillo@10 199 logwrite(LOG_ALERT, "process returned %d (%s)\n", exstat, ext_strerror(1024 + exstat));
meillo@10 200 errno = 1024 + exstat;
meillo@10 201 } else if (WIFSIGNALED(status)) {
meillo@10 202 logwrite(LOG_ALERT, "process got signal %d\n", WTERMSIG(status));
meillo@10 203 } else
meillo@10 204 ok = TRUE;
meillo@0 205
meillo@332 206 }
meillo@0 207
meillo@10 208 signal(SIGCHLD, old_signal);
meillo@0 209
meillo@10 210 /* free environment */
meillo@10 211 for (i = 0; i < n; i++) {
meillo@10 212 g_free(envp[i]);
meillo@10 213 }
meillo@0 214
meillo@10 215 /* set uid and gid back */
meillo@10 216 if (!conf.run_as_user) {
meillo@10 217 set_euidgid(saved_uid, saved_gid, NULL, NULL);
meillo@10 218 }
meillo@0 219
meillo@10 220 return ok;
meillo@0 221 }