masqmail

annotate src/local.c @ 398:b033fd9b96e4

Typographic rework in man page and line-breaking.
author markus schnalke <meillo@marmaro.de>
date Tue, 21 Feb 2012 14:44:00 +0100
parents 41958685480d
children f609a05ddff8
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@10 34 fprintf(out, "From <%s@%s> %s", msg->return_path->local_part, msg->return_path->domain, ctime(&now));
meillo@10 35 }
meillo@10 36
meillo@10 37 foreach(hdr_list, node) {
meillo@10 38 header *hdr = (header *) (node->data);
meillo@10 39 fputs(hdr->header, out);
meillo@10 40 }
meillo@10 41 putc('\n', out);
meillo@10 42 foreach(msg->data_list, node) {
meillo@10 43 /* From hack: */
meillo@10 44 if (flags & MSGSTR_FROMHACK) {
meillo@10 45 if (strncmp(node->data, "From ", 5) == 0)
meillo@10 46 putc('>', out);
meillo@10 47 }
meillo@10 48 fputs(node->data, out);
meillo@10 49 }
meillo@10 50 putc('\n', out);
meillo@0 51 }
meillo@0 52
meillo@10 53 gboolean
meillo@366 54 append_file(message *msg, GList *hdr_list, gchar *user)
meillo@0 55 {
meillo@10 56 struct passwd *pw;
meillo@10 57 gboolean ok = FALSE;
meillo@332 58 uid_t saved_uid = geteuid();
meillo@332 59 gid_t saved_gid = getegid();
meillo@332 60 gboolean uid_ok = TRUE, gid_ok = TRUE;
meillo@332 61 gchar *filename;
meillo@332 62 FILE *out;
meillo@0 63
meillo@10 64 /* headers may be special for a local delivery */
meillo@332 65 if (!hdr_list)
meillo@10 66 hdr_list = msg->hdr_list;
meillo@0 67
meillo@332 68 if (!(pw = getpwnam(user))) {
meillo@10 69 logwrite(LOG_ALERT, "could not find password entry for user %s\n", user);
meillo@10 70 errno = ENOENT; /* getpwnam does not set errno correctly */
meillo@332 71 return FALSE;
meillo@0 72 }
meillo@0 73
meillo@332 74 if (!conf.run_as_user) {
meillo@332 75 uid_ok = (seteuid(0) == 0);
meillo@332 76 if (uid_ok) {
meillo@332 77 gid_ok = (setegid(conf.mail_gid) == 0);
meillo@332 78 uid_ok = (seteuid(pw->pw_uid) == 0);
meillo@332 79 }
meillo@332 80 if (!uid_ok || !gid_ok) {
meillo@332 81 logwrite(LOG_ALERT, "could not set uid or gid for local delivery, uid = %d: %s\n", pw->pw_uid, strerror(errno));
meillo@332 82 return FALSE;
meillo@332 83 }
meillo@332 84 }
meillo@332 85
meillo@332 86 DEBUG(5) debugf("running as euid %d, egid %d\n", geteuid(), getegid());
meillo@332 87
meillo@332 88 filename = g_strdup_printf("%s/%s", conf.mail_dir, user);
meillo@332 89 if (!(out = fopen(filename, "a"))) {
meillo@332 90 logwrite(LOG_ALERT, "could not open file %s: %s\n", filename, strerror(errno));
meillo@332 91 } else {
meillo@332 92 #ifdef USE_LIBLOCKFILE
meillo@332 93 gint err;
meillo@332 94 /* lock file using liblockfile */
meillo@332 95 err = maillock(user, 3);
meillo@332 96 if (err == 0) {
meillo@332 97 #else
meillo@332 98 /* lock file: */
meillo@332 99 struct flock lock;
meillo@332 100 lock.l_type = F_WRLCK;
meillo@332 101 lock.l_whence = SEEK_END;
meillo@332 102 lock.l_start = lock.l_len = 0;
meillo@332 103 if (fcntl(fileno(out), F_SETLK, &lock) != -1) {
meillo@332 104 #endif
meillo@332 105 fchmod(fileno(out), 0600);
meillo@332 106 message_stream(out, msg, hdr_list, MSGSTR_FROMLINE | MSGSTR_FROMHACK);
meillo@332 107 ok = TRUE;
meillo@332 108
meillo@332 109 /* close when still user */
meillo@332 110 fclose(out);
meillo@332 111 #ifdef USE_LIBLOCKFILE
meillo@332 112 mailunlock();
meillo@332 113 #endif
meillo@332 114 } else {
meillo@332 115 fclose(out);
meillo@332 116 #ifdef USE_LIBLOCKFILE
meillo@332 117 DEBUG(3) debugf("could not lock file %s: error %d\n", filename, err);
meillo@332 118 } /* XEmacs indenting convenience... */
meillo@332 119 #else
meillo@332 120 DEBUG(3) debugf("could not lock file %s: %s\n", filename, strerror(errno));
meillo@332 121 }
meillo@332 122 #endif
meillo@332 123 }
meillo@332 124 g_free(filename);
meillo@332 125
meillo@332 126 if (!conf.run_as_user) {
meillo@332 127 uid_ok = (seteuid(0) == 0);
meillo@332 128 if (uid_ok) {
meillo@332 129 gid_ok = (setegid(saved_gid) == 0);
meillo@332 130 uid_ok = (seteuid(saved_uid) == 0);
meillo@332 131 }
meillo@332 132 }
meillo@332 133
meillo@332 134 if (!uid_ok || !gid_ok) {
meillo@367 135 /*
meillo@367 136 ** FIXME: if this fails we HAVE to exit, because we shall
meillo@367 137 ** not run with some users id. But we do not return, and so
meillo@367 138 ** this message will not be finished, so the user will get
meillo@367 139 ** the message again next time a delivery is attempted...
meillo@367 140 */
meillo@332 141 logwrite(LOG_ALERT, "could not set back uid or gid after local delivery: %s\n", strerror(errno));
meillo@332 142 logwrite(LOG_ALERT, "uid=%d, gid=%d, euid=%d, egid=%d, want = %d, %d\n",
meillo@332 143 getuid(), getgid(), geteuid(), getegid(), saved_uid, saved_gid);
meillo@332 144 logwrite(LOG_ALERT, "In case of trouble, see local.c:append_file() for details.\n", strerror(errno));
meillo@332 145 exit(1);
meillo@332 146 }
meillo@10 147 return ok;
meillo@0 148 }
meillo@0 149
meillo@0 150 gboolean
meillo@366 151 pipe_out(message *msg, GList *hdr_list, address *rcpt, gchar *cmd, guint flags)
meillo@0 152 {
meillo@10 153 gchar *envp[40];
meillo@10 154 FILE *out;
meillo@10 155 uid_t saved_uid = geteuid();
meillo@10 156 gid_t saved_gid = getegid();
meillo@10 157 gboolean ok = FALSE;
meillo@10 158 gint i, n;
meillo@10 159 pid_t pid;
meillo@10 160 void (*old_signal) (int);
meillo@10 161 int status;
meillo@332 162 address *ancestor = addr_find_ancestor(rcpt);
meillo@0 163
meillo@10 164 /* set uid and gid to the mail ids */
meillo@10 165 if (!conf.run_as_user) {
meillo@10 166 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
meillo@10 167 }
meillo@0 168
meillo@10 169 /* set environment */
meillo@332 170 n = 0;
meillo@332 171 envp[n++] = g_strdup_printf("SENDER=%s@%s", msg->return_path->local_part, msg->return_path->domain);
meillo@332 172 envp[n++] = g_strdup_printf("SENDER_DOMAIN=%s", msg->return_path->domain);
meillo@332 173 envp[n++] = g_strdup_printf("SENDER_LOCAL=%s", msg->return_path->local_part);
meillo@332 174 envp[n++] = g_strdup_printf("RECEIVED_HOST=%s", msg->received_host ? msg->received_host : "");
meillo@0 175
meillo@332 176 envp[n++] = g_strdup_printf("RETURN_PATH=%s@%s", msg->return_path->local_part, msg->return_path->domain);
meillo@332 177 envp[n++] = g_strdup_printf("DOMAIN=%s", ancestor->domain);
meillo@0 178
meillo@332 179 envp[n++] = g_strdup_printf("LOCAL_PART=%s", ancestor->local_part);
meillo@332 180 envp[n++] = g_strdup_printf("USER=%s", ancestor->local_part);
meillo@332 181 envp[n++] = g_strdup_printf("LOGNAME=%s", ancestor->local_part);
meillo@0 182
meillo@332 183 envp[n++] = g_strdup_printf("MESSAGE_ID=%s", msg->uid);
meillo@332 184 envp[n++] = g_strdup_printf("QUALIFY_DOMAIN=%s", conf.host_name);
meillo@0 185
meillo@332 186 envp[n] = NULL;
meillo@0 187
meillo@10 188 old_signal = signal(SIGCHLD, SIG_DFL);
meillo@0 189
meillo@10 190 out = peidopen(cmd, "w", envp, &pid, conf.mail_uid, conf.mail_gid);
meillo@332 191 if (!out) {
meillo@332 192 logwrite(LOG_ALERT, "could not open pipe '%s': %s\n", cmd, strerror(errno));
meillo@332 193 } else {
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@332 209 }
meillo@0 210
meillo@10 211 signal(SIGCHLD, old_signal);
meillo@0 212
meillo@10 213 /* free environment */
meillo@10 214 for (i = 0; i < n; i++) {
meillo@10 215 g_free(envp[i]);
meillo@10 216 }
meillo@0 217
meillo@10 218 /* set uid and gid back */
meillo@10 219 if (!conf.run_as_user) {
meillo@10 220 set_euidgid(saved_uid, saved_gid, NULL, NULL);
meillo@10 221 }
meillo@0 222
meillo@10 223 return ok;
meillo@0 224 }