Mercurial > masqmail-0.2
comparison src/local.c @ 0:08114f7dcc23 0.2.21
this is masqmail-0.2.21 from oliver kurth
author | meillo@marmaro.de |
---|---|
date | Fri, 26 Sep 2008 17:05:23 +0200 |
parents | |
children | 26e34ae9a3e3 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:08114f7dcc23 |
---|---|
1 /* MasqMail | |
2 Copyright (C) 1999-2001 Oliver Kurth | |
3 | |
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. | |
8 | |
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. | |
13 | |
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 */ | |
18 | |
19 #include "masqmail.h" | |
20 #include "peopen.h" | |
21 #include <sys/wait.h> | |
22 | |
23 static | |
24 void message_stream(FILE *out, message *msg, GList *hdr_list, guint flags) | |
25 { | |
26 time_t now = time(NULL); | |
27 GList *node; | |
28 | |
29 if(flags & MSGSTR_FROMLINE){ | |
30 fprintf(out, "From <%s@%s> %s", msg->return_path->local_part, | |
31 msg->return_path->domain, ctime(&now)); | |
32 } | |
33 | |
34 foreach(hdr_list, node){ | |
35 header *hdr = (header *)(node->data); | |
36 fputs(hdr->header, out); | |
37 } | |
38 putc('\n', out); | |
39 foreach(msg->data_list, node){ | |
40 /* From hack: */ | |
41 if(flags & MSGSTR_FROMHACK){ | |
42 if(strncmp(node->data, "From ", 5) == 0) | |
43 putc('>', out); | |
44 } | |
45 fputs(node->data, out); | |
46 } | |
47 putc('\n', out); | |
48 } | |
49 | |
50 gboolean append_file(message *msg, GList *hdr_list, gchar *user) | |
51 { | |
52 struct passwd *pw; | |
53 gboolean ok = FALSE; | |
54 | |
55 /* headers may be special for a local delivery */ | |
56 if(hdr_list == NULL) | |
57 hdr_list = msg->hdr_list; | |
58 | |
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; | |
63 | |
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 } | |
71 | |
72 DEBUG(5) debugf("running as euid %d\n", geteuid()); | |
73 DEBUG(5) debugf("running as egid %d\n", getegid()); | |
74 | |
75 if(uid_ok && gid_ok){ | |
76 gchar *filename; | |
77 FILE *out; | |
78 | |
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); | |
95 | |
96 message_stream(out, msg, hdr_list, MSGSTR_FROMLINE|MSGSTR_FROMHACK); | |
97 | |
98 ok = TRUE; | |
99 | |
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", | |
109 filename, err); | |
110 } /* XEmacs indenting convenience... */ | |
111 #else | |
112 DEBUG(3) debugf("could not lock file %s: %s\n", | |
113 filename, strerror(errno)); | |
114 } | |
115 #endif | |
116 }else{ | |
117 logwrite(LOG_ALERT, "could not open file %s: %s\n", | |
118 filename, strerror(errno)); | |
119 } | |
120 g_free(filename); | |
121 | |
122 if(!conf.run_as_user){ | |
123 uid_ok = (seteuid(0) == 0); | |
124 if(uid_ok){ | |
125 gid_ok = (setegid(saved_gid) == 0); | |
126 uid_ok = (seteuid(saved_uid) == 0); | |
127 } | |
128 } | |
129 | |
130 if(!uid_ok || !gid_ok){ | |
131 /* FIXME: if this fails we HAVE to exit, because we shall not run | |
132 with some users id. But we do not return, and so this message | |
133 will not be finished, so the user will get the message again | |
134 next time a delivery is attempted... */ | |
135 logwrite(LOG_ALERT, | |
136 "could not set back uid or gid after local delivery: %s\n", | |
137 strerror(errno)); | |
138 logwrite(LOG_ALERT, | |
139 "uid=%d, gid=%d, euid=%d, egid=%d, want = %d, %d\n", | |
140 getuid(), getgid(), geteuid(), getegid(), saved_uid, saved_gid); | |
141 exit(EXIT_FAILURE); | |
142 } | |
143 }else{ | |
144 logwrite(LOG_ALERT, | |
145 "could not set uid or gid for local delivery, uid = %d: %s\n", | |
146 pw->pw_uid, strerror(errno)); | |
147 } | |
148 }else{ | |
149 logwrite(LOG_ALERT, "could not find password entry for user %s\n", user); | |
150 errno = ENOENT; /* getpwnam does not set errno correctly */ | |
151 } | |
152 | |
153 return ok; | |
154 } | |
155 | |
156 #ifdef ENABLE_MAILDIR | |
157 gboolean maildir_out(message *msg, GList *hdr_list, gchar *user, guint flags) | |
158 { | |
159 struct passwd *pw; | |
160 gboolean ok = FALSE; | |
161 | |
162 /* headers may be special for a local delivery */ | |
163 if(hdr_list == NULL) | |
164 hdr_list = msg->hdr_list; | |
165 | |
166 if((pw = getpwnam(user))){ | |
167 uid_t saved_uid = geteuid(); | |
168 gid_t saved_gid = getegid(); | |
169 gboolean uid_ok = TRUE, gid_ok = TRUE; | |
170 | |
171 if(!conf.run_as_user){ | |
172 uid_ok = (seteuid(0) == 0); | |
173 if(uid_ok){ | |
174 gid_ok = (setegid(conf.mail_gid) == 0); | |
175 uid_ok = (seteuid(pw->pw_uid) == 0); | |
176 } | |
177 } | |
178 | |
179 DEBUG(5) debugf("running as euid %d\n", geteuid()); | |
180 DEBUG(5) debugf("running as egid %d\n", getegid()); | |
181 | |
182 if(uid_ok && gid_ok){ | |
183 char *path = g_strdup_printf("%s/Maildir", pw->pw_dir); | |
184 struct stat statbuf; | |
185 int ret; | |
186 | |
187 DEBUG(5) debugf("path = %s\n", path); | |
188 | |
189 ok = TRUE; | |
190 ret = stat(path, &statbuf); | |
191 if(ret != 0){ | |
192 ok = FALSE; | |
193 if(errno == ENOENT){ | |
194 logwrite(LOG_NOTICE, "directory %s does not exist, creating\n", path); | |
195 if(mkdir(path, 0700) == 0) | |
196 ok = TRUE; | |
197 }else | |
198 logwrite(LOG_ALERT, "stat of %s failed: %s\n", path, strerror(errno)); | |
199 } | |
200 if(ok){ | |
201 ok = FALSE; | |
202 ret = stat(path, &statbuf); | |
203 if(S_ISDIR(statbuf.st_mode)){ | |
204 gchar *subdirs[] = {"tmp", "new", "cur"}; | |
205 int i; | |
206 for(i = 0; i < 3; i++){ | |
207 char *path1 = g_strdup_printf("%s/%s", path, subdirs[i]); | |
208 ret = stat(path1, &statbuf); | |
209 if(ret != 0){ | |
210 if(errno == ENOENT){ | |
211 logwrite(LOG_NOTICE, "directory %s does not exist, creating\n", path1); | |
212 if(mkdir(path1, 0700) != 0) break; | |
213 } | |
214 } | |
215 g_free(path1); | |
216 } | |
217 if(i == 3){ | |
218 FILE *out; | |
219 mode_t saved_mode = umask(066); | |
220 /* the qmail style unique works only if delivering | |
221 with different process. We do not fork for each delivery, | |
222 so our uid is more unique. Hope it is compatible with all | |
223 MUAs. | |
224 */ | |
225 gchar *filename = g_strdup_printf("%s/tmp/%s.%s", path, msg->uid, conf.host_name); | |
226 | |
227 DEBUG(5) debugf("filename = %s\n", filename); | |
228 | |
229 if((out = fopen(filename, "w"))){ | |
230 gchar *newname = | |
231 g_strdup_printf("%s/new/%s.%s", path, msg->uid, conf.host_name); | |
232 message_stream(out, msg, hdr_list, flags); | |
233 ok = TRUE; | |
234 if(fflush(out) == EOF) ok = FALSE; | |
235 else if(fdatasync(fileno(out)) != 0){ | |
236 if(errno != EINVAL) /* some fs do not support this.. | |
237 I hope this also means that it is not necessary */ | |
238 ok = FALSE; | |
239 } | |
240 fclose(out); | |
241 if(rename(filename, newname) != 0){ | |
242 ok = FALSE; | |
243 logwrite(LOG_ALERT, "moving %s to %s failed: %s", | |
244 filename, newname, strerror(errno)); | |
245 } | |
246 g_free(newname); | |
247 } | |
248 umask(saved_mode); | |
249 g_free(filename); | |
250 } | |
251 }else{ | |
252 logwrite(LOG_ALERT, "%s is not a directory\n", path); | |
253 errno = ENOTDIR; | |
254 } | |
255 } | |
256 if(!conf.run_as_user){ | |
257 uid_ok = (seteuid(0) == 0); | |
258 if(uid_ok){ | |
259 gid_ok = (setegid(saved_gid) == 0); | |
260 uid_ok = (seteuid(saved_uid) == 0); | |
261 } | |
262 } | |
263 if(!uid_ok || !gid_ok){ | |
264 /* FIXME: if this fails we HAVE to exit, because we shall not run | |
265 with some users id. But we do not return, and so this message | |
266 will not be finished, so the user will get the message again | |
267 next time a delivery is attempted... */ | |
268 logwrite(LOG_ALERT, | |
269 "could not set back uid or gid after local delivery: %s\n", | |
270 strerror(errno)); | |
271 exit(EXIT_FAILURE); | |
272 } | |
273 g_free(path); | |
274 }else{ | |
275 logwrite(LOG_ALERT, | |
276 "could not set uid or gid for local delivery, uid = %d: %s\n", | |
277 pw->pw_uid, strerror(errno)); | |
278 } | |
279 }else{ | |
280 logwrite(LOG_ALERT, "could not find password entry for user %s\n", user); | |
281 errno = ENOENT; /* getpwnam does not set errno correctly */ | |
282 } | |
283 return ok; | |
284 } | |
285 #endif | |
286 | |
287 gboolean | |
288 pipe_out(message *msg, GList *hdr_list, address *rcpt, gchar *cmd, guint flags) | |
289 { | |
290 gchar *envp[40]; | |
291 FILE *out; | |
292 uid_t saved_uid = geteuid(); | |
293 gid_t saved_gid = getegid(); | |
294 gboolean ok = FALSE; | |
295 gint i, n; | |
296 pid_t pid; | |
297 void (*old_signal)(int); | |
298 int status; | |
299 | |
300 /* set uid and gid to the mail ids */ | |
301 if(!conf.run_as_user){ | |
302 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); | |
303 } | |
304 | |
305 /* set environment */ | |
306 { | |
307 gint i = 0; | |
308 address *ancestor = addr_find_ancestor(rcpt); | |
309 | |
310 envp[i++] = g_strdup_printf("SENDER=%s@%s", msg->return_path->local_part, msg->return_path->domain); | |
311 envp[i++] = g_strdup_printf("SENDER_DOMAIN=%s", msg->return_path->domain); | |
312 envp[i++] = g_strdup_printf("SENDER_LOCAL=%s", msg->return_path->local_part); | |
313 envp[i++] = g_strdup_printf("RECEIVED_HOST=%s", msg->received_host ? msg->received_host : ""); | |
314 | |
315 envp[i++] = g_strdup_printf("RETURN_PATH=%s@%s", | |
316 msg->return_path->local_part, msg->return_path->domain); | |
317 envp[i++] = g_strdup_printf("DOMAIN=%s", ancestor->domain); | |
318 | |
319 envp[i++] = g_strdup_printf("LOCAL_PART=%s", ancestor->local_part); | |
320 envp[i++] = g_strdup_printf("USER=%s", ancestor->local_part); | |
321 envp[i++] = g_strdup_printf("LOGNAME=%s", ancestor->local_part); | |
322 | |
323 envp[i++] = g_strdup_printf("MESSAGE_ID=%s", msg->uid); | |
324 envp[i++] = g_strdup_printf("QUALIFY_DOMAIN=%s", conf.host_name); | |
325 | |
326 envp[i] = NULL; | |
327 n = i; | |
328 } | |
329 | |
330 old_signal = signal(SIGCHLD, SIG_DFL); | |
331 | |
332 out = peidopen(cmd, "w", envp, &pid, conf.mail_uid, conf.mail_gid); | |
333 if(out != NULL){ | |
334 message_stream(out, msg, hdr_list, flags); | |
335 | |
336 fclose(out); | |
337 | |
338 waitpid(pid, &status, 0); | |
339 | |
340 if(WEXITSTATUS(status) != 0){ | |
341 int exstat = WEXITSTATUS(status); | |
342 logwrite(LOG_ALERT, "process returned %d (%s)\n", exstat, ext_strerror(1024 + exstat)); | |
343 errno = 1024 + exstat; | |
344 }else if(WIFSIGNALED(status)){ | |
345 logwrite(LOG_ALERT, "process got signal %d\n", WTERMSIG(status)); | |
346 }else | |
347 ok = TRUE; | |
348 | |
349 }else | |
350 logwrite(LOG_ALERT, "could not open pipe '%s': %s\n", cmd, strerror(errno)); | |
351 | |
352 signal(SIGCHLD, old_signal); | |
353 | |
354 /* free environment */ | |
355 for(i = 0; i < n; i++){ | |
356 g_free(envp[i]); | |
357 } | |
358 | |
359 /* set uid and gid back */ | |
360 if(!conf.run_as_user){ | |
361 set_euidgid(saved_uid, saved_gid, NULL, NULL); | |
362 } | |
363 | |
364 return ok; | |
365 } | |
366 |