masqmail

view src/local.c @ 205:4fd237550525

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