masqmail

view src/local.c @ 366:41958685480d

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