masqmail

annotate src/local.c @ 222:8cddc65765bd

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