masqmail-0.2

view src/local.c @ 10:26e34ae9a3e3

changed indention and line wrapping to a more consistent style
author meillo@marmaro.de
date Mon, 27 Oct 2008 16:23:10 +0100
parents 08114f7dcc23
children f671821d8222
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 "masqmail.h"
20 #include "peopen.h"
21 #include <sys/wait.h>
23 static void
24 message_stream(FILE * out, message * msg, GList * hdr_list, guint flags)
25 {
26 time_t now = time(NULL);
27 GList *node;
29 if (flags & MSGSTR_FROMLINE) {
30 fprintf(out, "From <%s@%s> %s", msg->return_path->local_part, msg->return_path->domain, ctime(&now));
31 }
33 foreach(hdr_list, node) {
34 header *hdr = (header *) (node->data);
35 fputs(hdr->header, out);
36 }
37 putc('\n', out);
38 foreach(msg->data_list, node) {
39 /* From hack: */
40 if (flags & MSGSTR_FROMHACK) {
41 if (strncmp(node->data, "From ", 5) == 0)
42 putc('>', out);
43 }
44 fputs(node->data, out);
45 }
46 putc('\n', out);
47 }
49 gboolean
50 append_file(message * msg, GList * hdr_list, gchar * user)
51 {
52 struct passwd *pw;
53 gboolean ok = FALSE;
55 /* headers may be special for a local delivery */
56 if (hdr_list == NULL)
57 hdr_list = msg->hdr_list;
59 if ((pw = getpwnam(user))) {
60 uid_t saved_uid = geteuid();
61 gid_t saved_gid = getegid();
62 gboolean uid_ok = TRUE, gid_ok = TRUE;
64 if (!conf.run_as_user) {
65 uid_ok = (seteuid(0) == 0);
66 if (uid_ok) {
67 gid_ok = (setegid(conf.mail_gid) == 0);
68 uid_ok = (seteuid(pw->pw_uid) == 0);
69 }
70 }
72 DEBUG(5) debugf("running as euid %d\n", geteuid());
73 DEBUG(5) debugf("running as egid %d\n", getegid());
75 if (uid_ok && gid_ok) {
76 gchar *filename;
77 FILE *out;
79 filename = g_strdup_printf("%s/%s", conf.mail_dir, user);
80 if ((out = fopen(filename, "a"))) {
81 #ifdef USE_LIBLOCKFILE
82 gint err;
83 /* lock file using liblockfile */
84 err = maillock(user, 3);
85 if (err == 0) {
86 #else
87 /* lock file: */
88 struct flock lock;
89 lock.l_type = F_WRLCK;
90 lock.l_whence = SEEK_END;
91 lock.l_start = lock.l_len = 0;
92 if (fcntl(fileno(out), F_SETLK, &lock) != -1) {
93 #endif
94 fchmod(fileno(out), 0600);
96 message_stream(out, msg, hdr_list, MSGSTR_FROMLINE | MSGSTR_FROMHACK);
98 ok = TRUE;
100 /* close when still user */
101 fclose(out);
102 #ifdef USE_LIBLOCKFILE
103 mailunlock();
104 #endif
105 } else {
106 fclose(out);
107 #ifdef USE_LIBLOCKFILE
108 DEBUG(3) debugf("could not lock file %s: error %d\n", filename, err);
109 } /* XEmacs indenting convenience... */
110 #else
111 DEBUG(3) debugf("could not lock file %s: %s\n", filename, strerror(errno));
112 }
113 #endif
114 } else {
115 logwrite(LOG_ALERT, "could not open file %s: %s\n", filename, strerror(errno));
116 }
117 g_free(filename);
119 if (!conf.run_as_user) {
120 uid_ok = (seteuid(0) == 0);
121 if (uid_ok) {
122 gid_ok = (setegid(saved_gid) == 0);
123 uid_ok = (seteuid(saved_uid) == 0);
124 }
125 }
127 if (!uid_ok || !gid_ok) {
128 /* FIXME: if this fails we HAVE to exit, because we shall not run
129 with some users id. But we do not return, and so this message
130 will not be finished, so the user will get the message again
131 next time a delivery is attempted... */
132 logwrite(LOG_ALERT, "could not set back uid or gid after local delivery: %s\n", strerror(errno));
133 logwrite(LOG_ALERT, "uid=%d, gid=%d, euid=%d, egid=%d, want = %d, %d\n",
134 getuid(), getgid(), geteuid(), getegid(), saved_uid, saved_gid);
135 exit(EXIT_FAILURE);
136 }
137 } else {
138 logwrite(LOG_ALERT, "could not set uid or gid for local delivery, uid = %d: %s\n", pw->pw_uid, strerror(errno));
139 }
140 } else {
141 logwrite(LOG_ALERT, "could not find password entry for user %s\n", user);
142 errno = ENOENT; /* getpwnam does not set errno correctly */
143 }
145 return ok;
146 }
148 #ifdef ENABLE_MAILDIR
149 gboolean
150 maildir_out(message * msg, GList * hdr_list, gchar * user, guint flags)
151 {
152 struct passwd *pw;
153 gboolean ok = FALSE;
155 /* headers may be special for a local delivery */
156 if (hdr_list == NULL)
157 hdr_list = msg->hdr_list;
159 if ((pw = getpwnam(user))) {
160 uid_t saved_uid = geteuid();
161 gid_t saved_gid = getegid();
162 gboolean uid_ok = TRUE, gid_ok = TRUE;
164 if (!conf.run_as_user) {
165 uid_ok = (seteuid(0) == 0);
166 if (uid_ok) {
167 gid_ok = (setegid(conf.mail_gid) == 0);
168 uid_ok = (seteuid(pw->pw_uid) == 0);
169 }
170 }
172 DEBUG(5) debugf("running as euid %d\n", geteuid());
173 DEBUG(5) debugf("running as egid %d\n", getegid());
175 if (uid_ok && gid_ok) {
176 char *path = g_strdup_printf("%s/Maildir", pw->pw_dir);
177 struct stat statbuf;
178 int ret;
180 DEBUG(5) debugf("path = %s\n", path);
182 ok = TRUE;
183 ret = stat(path, &statbuf);
184 if (ret != 0) {
185 ok = FALSE;
186 if (errno == ENOENT) {
187 logwrite(LOG_NOTICE, "directory %s does not exist, creating\n", path);
188 if (mkdir(path, 0700) == 0)
189 ok = TRUE;
190 } else
191 logwrite(LOG_ALERT, "stat of %s failed: %s\n", path, strerror(errno));
192 }
193 if (ok) {
194 ok = FALSE;
195 ret = stat(path, &statbuf);
196 if (S_ISDIR(statbuf.st_mode)) {
197 gchar *subdirs[] = { "tmp", "new", "cur" };
198 int i;
199 for (i = 0; i < 3; i++) {
200 char *path1 = g_strdup_printf("%s/%s", path, subdirs[i]);
201 ret = stat(path1, &statbuf);
202 if (ret != 0) {
203 if (errno == ENOENT) {
204 logwrite(LOG_NOTICE, "directory %s does not exist, creating\n", path1);
205 if (mkdir(path1, 0700) != 0)
206 break;
207 }
208 }
209 g_free(path1);
210 }
211 if (i == 3) {
212 FILE *out;
213 mode_t saved_mode = umask(066);
214 /* the qmail style unique works only if delivering
215 with different process. We do not fork for each delivery,
216 so our uid is more unique. Hope it is compatible with all
217 MUAs.
218 */
219 gchar *filename = g_strdup_printf("%s/tmp/%s.%s", path, msg->uid, conf.host_name);
221 DEBUG(5) debugf("filename = %s\n", filename);
223 if ((out = fopen(filename, "w"))) {
224 gchar *newname = g_strdup_printf("%s/new/%s.%s", path, msg->uid, conf.host_name);
225 message_stream(out, msg, hdr_list, flags);
226 ok = TRUE;
227 if (fflush(out) == EOF)
228 ok = FALSE;
229 else if (fdatasync(fileno(out)) != 0) {
230 if (errno != EINVAL) /* some fs do not support this.. I hope this also means that it is not necessary */
231 ok = FALSE;
232 }
233 fclose(out);
234 if (rename(filename, newname) != 0) {
235 ok = FALSE;
236 logwrite(LOG_ALERT, "moving %s to %s failed: %s", filename, newname, strerror(errno));
237 }
238 g_free(newname);
239 }
240 umask(saved_mode);
241 g_free(filename);
242 }
243 } else {
244 logwrite(LOG_ALERT, "%s is not a directory\n", path);
245 errno = ENOTDIR;
246 }
247 }
248 if (!conf.run_as_user) {
249 uid_ok = (seteuid(0) == 0);
250 if (uid_ok) {
251 gid_ok = (setegid(saved_gid) == 0);
252 uid_ok = (seteuid(saved_uid) == 0);
253 }
254 }
255 if (!uid_ok || !gid_ok) {
256 /* FIXME: if this fails we HAVE to exit, because we shall not run
257 with some users id. But we do not return, and so this message
258 will not be finished, so the user will get the message again
259 next time a delivery is attempted... */
260 logwrite(LOG_ALERT, "could not set back uid or gid after local delivery: %s\n", strerror(errno));
261 exit(EXIT_FAILURE);
262 }
263 g_free(path);
264 } else {
265 logwrite(LOG_ALERT, "could not set uid or gid for local delivery, uid = %d: %s\n", pw->pw_uid, strerror(errno));
266 }
267 } else {
268 logwrite(LOG_ALERT, "could not find password entry for user %s\n", user);
269 errno = ENOENT; /* getpwnam does not set errno correctly */
270 }
271 return ok;
272 }
273 #endif
275 gboolean
276 pipe_out(message * msg, GList * hdr_list, address * rcpt, gchar * cmd, guint flags)
277 {
278 gchar *envp[40];
279 FILE *out;
280 uid_t saved_uid = geteuid();
281 gid_t saved_gid = getegid();
282 gboolean ok = FALSE;
283 gint i, n;
284 pid_t pid;
285 void (*old_signal) (int);
286 int status;
288 /* set uid and gid to the mail ids */
289 if (!conf.run_as_user) {
290 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
291 }
293 /* set environment */
294 {
295 gint i = 0;
296 address *ancestor = addr_find_ancestor(rcpt);
298 envp[i++] = g_strdup_printf("SENDER=%s@%s", msg->return_path->local_part, msg->return_path->domain);
299 envp[i++] = g_strdup_printf("SENDER_DOMAIN=%s", msg->return_path->domain);
300 envp[i++] = g_strdup_printf("SENDER_LOCAL=%s", msg->return_path->local_part);
301 envp[i++] = g_strdup_printf("RECEIVED_HOST=%s", msg->received_host ? msg->received_host : "");
303 envp[i++] = g_strdup_printf("RETURN_PATH=%s@%s", msg->return_path->local_part, msg->return_path->domain);
304 envp[i++] = g_strdup_printf("DOMAIN=%s", ancestor->domain);
306 envp[i++] = g_strdup_printf("LOCAL_PART=%s", ancestor->local_part);
307 envp[i++] = g_strdup_printf("USER=%s", ancestor->local_part);
308 envp[i++] = g_strdup_printf("LOGNAME=%s", ancestor->local_part);
310 envp[i++] = g_strdup_printf("MESSAGE_ID=%s", msg->uid);
311 envp[i++] = g_strdup_printf("QUALIFY_DOMAIN=%s", conf.host_name);
313 envp[i] = NULL;
314 n = i;
315 }
317 old_signal = signal(SIGCHLD, SIG_DFL);
319 out = peidopen(cmd, "w", envp, &pid, conf.mail_uid, conf.mail_gid);
320 if (out != NULL) {
321 message_stream(out, msg, hdr_list, flags);
323 fclose(out);
325 waitpid(pid, &status, 0);
327 if (WEXITSTATUS(status) != 0) {
328 int exstat = WEXITSTATUS(status);
329 logwrite(LOG_ALERT, "process returned %d (%s)\n", exstat, ext_strerror(1024 + exstat));
330 errno = 1024 + exstat;
331 } else if (WIFSIGNALED(status)) {
332 logwrite(LOG_ALERT, "process got signal %d\n", WTERMSIG(status));
333 } else
334 ok = TRUE;
336 } else
337 logwrite(LOG_ALERT, "could not open pipe '%s': %s\n", cmd, strerror(errno));
339 signal(SIGCHLD, old_signal);
341 /* free environment */
342 for (i = 0; i < n; i++) {
343 g_free(envp[i]);
344 }
346 /* set uid and gid back */
347 if (!conf.run_as_user) {
348 set_euidgid(saved_uid, saved_gid, NULL, NULL);
349 }
351 return ok;
352 }