masqmail

view src/local.c @ 421:f37384470855

Changed lockdir to /var/lock/masqmail; Create lockdir and piddir on startup. Moved the lockdir out of the spool dir. (When /var/lock is a ramdisk we do well to have the lock files there.) Added the new configure option --with-lockdir to change that location. Nontheless, if we run_as_user, then lock files are always stored in the spool dir directly. Instead of installing the lockdir and piddir at installation time, we create them on startup time now if they are missing. This is necessary if lockdir or piddir are a tmpfs.
author markus schnalke <meillo@marmaro.de>
date Wed, 30 May 2012 09:38:38 +0200
parents b27f66555ba8
children
line source
1 /*
2 ** MasqMail
3 ** Copyright (C) 1999-2001 Oliver Kurth
4 ** Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
21 #include <sys/wait.h>
22 #include <sys/stat.h>
24 #include "masqmail.h"
25 #include "peopen.h"
27 static void
28 message_stream(FILE *out, message *msg, GList *hdr_list, guint flags)
29 {
30 time_t now = time(NULL);
31 GList *node;
33 if (flags & MSGSTR_FROMLINE) {
34 fprintf(out, "From <%s@%s> %s", msg->return_path->local_part,
35 msg->return_path->domain, ctime(&now));
36 }
38 foreach(hdr_list, node) {
39 header *hdr = (header *) (node->data);
40 fputs(hdr->header, out);
41 }
42 putc('\n', out);
43 foreach(msg->data_list, node) {
44 /* From hack: */
45 if (flags & MSGSTR_FROMHACK) {
46 if (strncmp(node->data, "From ", 5) == 0)
47 putc('>', out);
48 }
49 fputs(node->data, out);
50 }
51 putc('\n', out);
52 }
54 gboolean
55 append_file(message *msg, GList *hdr_list, gchar *user)
56 {
57 struct passwd *pw;
58 gboolean ok = FALSE;
59 uid_t saved_uid = geteuid();
60 gid_t saved_gid = getegid();
61 gboolean uid_ok = TRUE, gid_ok = TRUE;
62 gchar *filename;
63 FILE *out;
65 /* headers may be special for a local delivery */
66 if (!hdr_list)
67 hdr_list = msg->hdr_list;
69 if (!(pw = getpwnam(user))) {
70 logwrite(LOG_ALERT, "could not find password entry for "
71 "user %s\n", user);
72 errno = ENOENT; /* getpwnam does not set errno correctly */
73 return FALSE;
74 }
76 if (!conf.run_as_user) {
77 uid_ok = (seteuid(0) == 0);
78 if (uid_ok) {
79 gid_ok = (setegid(conf.mail_gid) == 0);
80 uid_ok = (seteuid(pw->pw_uid) == 0);
81 }
82 if (!uid_ok || !gid_ok) {
83 logwrite(LOG_ALERT, "could not set uid or gid for "
84 "local delivery, uid = %d: %s\n",
85 pw->pw_uid, strerror(errno));
86 return FALSE;
87 }
88 }
90 DEBUG(5) debugf("running as euid %d, egid %d\n", geteuid(), getegid());
92 filename = g_strdup_printf("%s/%s", conf.mail_dir, user);
93 if (!(out = fopen(filename, "a"))) {
94 logwrite(LOG_ALERT, "could not open file %s: %s\n",
95 filename, strerror(errno));
96 } else {
97 #ifdef USE_LIBLOCKFILE
98 gint err;
99 /* lock file using liblockfile */
100 err = maillock(user, 3);
101 if (err == 0) {
102 #else
103 /* lock file: */
104 struct flock lock;
105 lock.l_type = F_WRLCK;
106 lock.l_whence = SEEK_END;
107 lock.l_start = lock.l_len = 0;
108 if (fcntl(fileno(out), F_SETLK, &lock) != -1) {
109 #endif
110 fchmod(fileno(out), 0600);
111 message_stream(out, msg, hdr_list,
112 MSGSTR_FROMLINE | MSGSTR_FROMHACK);
113 ok = TRUE;
115 /* close when still user */
116 fclose(out);
117 #ifdef USE_LIBLOCKFILE
118 mailunlock();
119 #endif
120 } else {
121 fclose(out);
122 #ifdef USE_LIBLOCKFILE
123 DEBUG(3) debugf("could not lock file %s: error %d\n",
124 filename, err);
125 } /* XEmacs indenting convenience... */
126 #else
127 DEBUG(3) debugf("could not lock file %s: %s\n",
128 filename, strerror(errno));
129 }
130 #endif
131 }
132 g_free(filename);
134 if (!conf.run_as_user) {
135 uid_ok = (seteuid(0) == 0);
136 if (uid_ok) {
137 gid_ok = (setegid(saved_gid) == 0);
138 uid_ok = (seteuid(saved_uid) == 0);
139 }
140 }
142 if (!uid_ok || !gid_ok) {
143 /*
144 ** FIXME: if this fails we HAVE to exit, because we shall
145 ** not run with some users id. But we do not return, and so
146 ** this message will not be finished, so the user will get
147 ** the message again next time a delivery is attempted...
148 */
149 logwrite(LOG_ALERT, "could not set back uid or gid after "
150 "local delivery: %s\n", strerror(errno));
151 logwrite(LOG_ALERT, "uid=%d, gid=%d, euid=%d, egid=%d, "
152 "want = %d, %d\n", getuid(), getgid(),
153 geteuid(), getegid(), saved_uid, saved_gid);
154 logwrite(LOG_ALERT, "In case of trouble, see "
155 "local.c:append_file() for details.\n",
156 strerror(errno));
157 exit(1);
158 }
159 return ok;
160 }
162 gboolean
163 pipe_out(message *msg, GList *hdr_list, address *rcpt, gchar *cmd, guint flags)
164 {
165 gchar *envp[40];
166 FILE *out;
167 uid_t saved_uid = geteuid();
168 gid_t saved_gid = getegid();
169 gboolean ok = FALSE;
170 gint i, n;
171 pid_t pid;
172 void (*old_signal) (int);
173 int status;
174 address *ancestor = addr_find_ancestor(rcpt);
176 /* set uid and gid to the mail ids */
177 if (!conf.run_as_user) {
178 set_euidgid(conf.mail_uid, conf.mail_gid,
179 &saved_uid, &saved_gid);
180 }
182 /* set environment */
183 n = 0;
184 envp[n++] = g_strdup_printf("SENDER=%s@%s",
185 msg->return_path->local_part,
186 msg->return_path->domain);
187 envp[n++] = g_strdup_printf("SENDER_DOMAIN=%s",
188 msg->return_path->domain);
189 envp[n++] = g_strdup_printf("SENDER_LOCAL=%s",
190 msg->return_path->local_part);
191 envp[n++] = g_strdup_printf("RECEIVED_HOST=%s",
192 msg->received_host ? msg->received_host : "");
194 envp[n++] = g_strdup_printf("RETURN_PATH=%s@%s",
195 msg->return_path->local_part,
196 msg->return_path->domain);
197 envp[n++] = g_strdup_printf("DOMAIN=%s",
198 ancestor->domain);
200 envp[n++] = g_strdup_printf("LOCAL_PART=%s", ancestor->local_part);
201 envp[n++] = g_strdup_printf("USER=%s", ancestor->local_part);
202 envp[n++] = g_strdup_printf("LOGNAME=%s", ancestor->local_part);
204 envp[n++] = g_strdup_printf("MESSAGE_ID=%s", msg->uid);
205 envp[n++] = g_strdup_printf("QUALIFY_DOMAIN=%s", conf.host_name);
207 envp[n] = NULL;
209 old_signal = signal(SIGCHLD, SIG_DFL);
211 out = peidopen(cmd, "w", envp, &pid, conf.mail_uid, conf.mail_gid);
212 if (!out) {
213 logwrite(LOG_ALERT, "could not open pipe '%s': %s\n",
214 cmd, strerror(errno));
215 } else {
216 message_stream(out, msg, hdr_list, flags);
218 fclose(out);
220 waitpid(pid, &status, 0);
222 if (WEXITSTATUS(status) != 0) {
223 int exstat = WEXITSTATUS(status);
224 logwrite(LOG_ALERT, "process returned %d (%s)\n",
225 exstat, ext_strerror(1024 + exstat));
226 errno = 1024 + exstat;
227 } else if (WIFSIGNALED(status)) {
228 logwrite(LOG_ALERT, "process got signal %d\n",
229 WTERMSIG(status));
230 } else
231 ok = TRUE;
233 }
235 signal(SIGCHLD, old_signal);
237 /* free environment */
238 for (i = 0; i < n; i++) {
239 g_free(envp[i]);
240 }
242 /* set uid and gid back */
243 if (!conf.run_as_user) {
244 set_euidgid(saved_uid, saved_gid, NULL, NULL);
245 }
247 return ok;
248 }