xref: /openbsd-src/usr.sbin/smtpd/queue_backend.c (revision ac9b4aacc1da35008afea06a5d23c2f2dea9b93e)
1 /*	$OpenBSD: queue_backend.c,v 1.38 2012/09/01 16:09:14 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2011 Gilles Chehade <gilles@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/tree.h>
22 #include <sys/param.h>
23 #include <sys/socket.h>
24 #include <sys/stat.h>
25 
26 #include <ctype.h>
27 #include <errno.h>
28 #include <event.h>
29 #include <fcntl.h>
30 #include <imsg.h>
31 #include <inttypes.h>
32 #include <libgen.h>
33 #include <pwd.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <time.h>
38 #include <unistd.h>
39 
40 #include "smtpd.h"
41 #include "log.h"
42 
43 static const char* envelope_validate(struct envelope *);
44 
45 extern struct queue_backend	queue_backend_fs;
46 
47 int
48 queue_message_incoming_path(uint32_t msgid, char *buf, size_t len)
49 {
50 	return bsnprintf(buf, len, "%s/%08x",
51 	    PATH_INCOMING,
52 	    msgid);
53 }
54 
55 int
56 queue_envelope_incoming_path(uint64_t evpid, char *buf, size_t len)
57 {
58 	return bsnprintf(buf, len, "%s/%08x%s/%016" PRIx64,
59 	    PATH_INCOMING,
60 	    evpid_to_msgid(evpid),
61 	    PATH_ENVELOPES,
62 	    evpid);
63 }
64 
65 int
66 queue_message_incoming_delete(uint32_t msgid)
67 {
68 	char rootdir[MAXPATHLEN];
69 
70 	if (! queue_message_incoming_path(msgid, rootdir, sizeof(rootdir)))
71 		fatal("queue_message_incoming_delete: snprintf");
72 
73 	if (rmtree(rootdir, 0) == -1)
74 		fatal("queue_message_incoming_delete: rmtree");
75 
76 	return 1;
77 }
78 
79 struct queue_backend *
80 queue_backend_lookup(const char *name)
81 {
82 	if (!strcmp(name, "fs"))
83 		return &queue_backend_fs;
84 
85 	return (NULL);
86 }
87 
88 int
89 queue_message_create(uint32_t *msgid)
90 {
91 	return env->sc_queue->message(QOP_CREATE, msgid);
92 }
93 
94 int
95 queue_message_delete(uint32_t msgid)
96 {
97 	return env->sc_queue->message(QOP_DELETE, &msgid);
98 }
99 
100 int
101 queue_message_commit(uint32_t msgid)
102 {
103 	char	msgpath[MAXPATHLEN];
104 	char	tmppath[MAXPATHLEN];
105 	FILE	*ifp = NULL;
106 	FILE	*ofp = NULL;
107 
108 	queue_message_incoming_path(msgid, msgpath, sizeof msgpath);
109 	strlcat(msgpath, PATH_MESSAGE, sizeof(msgpath));
110 
111 	if (env->sc_queue_flags & QUEUE_COMPRESS) {
112 
113 		bsnprintf(tmppath, sizeof tmppath, "%s.comp", msgpath);
114 		ifp = fopen(msgpath, "r");
115 		ofp = fopen(tmppath, "w+");
116 		if (ifp == NULL || ofp == NULL)
117 			goto err;
118 		if (! compress_file(ifp, ofp))
119 			goto err;
120 		fclose(ifp);
121 		fclose(ofp);
122 		ifp = NULL;
123 		ofp = NULL;
124 
125 		if (rename(tmppath, msgpath) == -1) {
126 			if (errno == ENOSPC)
127 				return (0);
128 			fatal("queue_message_commit: rename");
129 		}
130 	}
131 
132 	return env->sc_queue->message(QOP_COMMIT, &msgid);
133 
134 err:
135 	if (ifp)
136 		fclose(ifp);
137 	if (ofp)
138 		fclose(ofp);
139 	return 0;
140 }
141 
142 int
143 queue_message_corrupt(uint32_t msgid)
144 {
145 	return env->sc_queue->message(QOP_CORRUPT, &msgid);
146 }
147 
148 int
149 queue_message_fd_r(uint32_t msgid)
150 {
151 	int	fdin = -1, fdout = -1;
152 	FILE	*ifp = NULL;
153 	FILE	*ofp = NULL;
154 
155 	fdin = env->sc_queue->message(QOP_FD_R, &msgid);
156 
157 	if (env->sc_queue_flags & QUEUE_COMPRESS) {
158 		fdout = mktmpfile();
159 		ifp = fdopen(fdin, "r");
160 		ofp = fdopen(fdout, "w+");
161 		if (ifp == NULL || ofp == NULL)
162 			goto err;
163 		if (! uncompress_file(ifp, ofp))
164 			goto err;
165 		fseek(ofp, SEEK_SET, 0);
166 		fdin = fileno(ofp);
167 		fclose(ifp);
168 		ifp = NULL;
169 		ofp = NULL;
170 	}
171 
172 	return (fdin);
173 
174 err:
175 	if (fdin != -1)
176 		close(fdin);
177 	if (fdout != -1)
178 		close(fdout);
179 	if (ifp)
180 		fclose(ifp);
181 	if (ofp)
182 		fclose(ofp);
183 	return -1;
184 }
185 
186 int
187 queue_message_fd_rw(uint32_t msgid)
188 {
189 	char msgpath[MAXPATHLEN];
190 
191 	queue_message_incoming_path(msgid, msgpath, sizeof msgpath);
192 	strlcat(msgpath, PATH_MESSAGE, sizeof(msgpath));
193 
194 	return open(msgpath, O_RDWR | O_CREAT | O_EXCL, 0600);
195 }
196 
197 static int
198 queue_envelope_dump_buffer(struct envelope *ep, char *evpbuf, size_t evpbufsize)
199 {
200 	char		 evpbufcom[sizeof(struct envelope)];
201 	char		*evp;
202 	size_t		 evplen;
203 
204 	evp = evpbuf;
205 	evplen = envelope_dump_buffer(ep, evpbuf, evpbufsize);
206 	if (evplen == 0)
207 		return (0);
208 
209 	if (env->sc_queue_flags & QUEUE_COMPRESS) {
210 		evplen = compress_buffer(evp, evplen, evpbufcom, sizeof evpbufcom);
211 		if (evplen == 0)
212 			return (0);
213 		evp = evpbufcom;
214 	}
215 
216 	memmove(evpbuf, evp, evplen);
217 
218 	return (evplen);
219 }
220 
221 static int
222 queue_envelope_load_buffer(struct envelope *ep, char *evpbuf, size_t evpbufsize)
223 {
224 	char		 evpbufcom[sizeof(struct envelope)];
225 	char		*evp;
226 	size_t		 evplen;
227 
228 	evp = evpbuf;
229 	evplen = evpbufsize;
230 
231 	if (env->sc_queue_flags & QUEUE_COMPRESS) {
232 		evplen = uncompress_buffer(evp, evplen, evpbufcom, sizeof evpbufcom);
233 		if (evplen == 0)
234 			return (0);
235 		evp = evpbufcom;
236 	}
237 
238 	return (envelope_load_buffer(ep, evp, evplen));
239 }
240 
241 int
242 queue_envelope_create(struct envelope *ep)
243 {
244 	int		 r;
245 	char		 evpbuf[sizeof(struct envelope)];
246 	size_t		 evplen;
247 
248 	ep->creation = time(NULL);
249 	evplen = queue_envelope_dump_buffer(ep, evpbuf, sizeof evpbuf);
250 	if (evplen == 0)
251 		return (0);
252 
253 	r = env->sc_queue->envelope(QOP_CREATE, &ep->id, evpbuf, evplen);
254 	if (!r) {
255 		ep->creation = 0;
256 		ep->id = 0;
257 	}
258 	return (r);
259 }
260 
261 int
262 queue_envelope_delete(struct envelope *ep)
263 {
264 	return env->sc_queue->envelope(QOP_DELETE, &ep->id, NULL, 0);
265 }
266 
267 int
268 queue_envelope_load(uint64_t evpid, struct envelope *ep)
269 {
270 	const char	*e;
271 	char		 evpbuf[sizeof(struct envelope)];
272 	size_t		 evplen;
273 
274 	ep->id = evpid;
275 	evplen = env->sc_queue->envelope(QOP_LOAD, &ep->id, evpbuf, sizeof evpbuf);
276 	if (evplen == 0)
277 		return (0);
278 
279 	if (queue_envelope_load_buffer(ep, evpbuf, evplen)) {
280 		if ((e = envelope_validate(ep)) == NULL) {
281 			ep->id = evpid;
282 			return (1);
283 		}
284 		log_debug("invalid envelope %016" PRIx64 ": %s", ep->id, e);
285 	}
286 	return (0);
287 }
288 
289 int
290 queue_envelope_update(struct envelope *ep)
291 {
292 	char	 evpbuf[sizeof(struct envelope)];
293 	size_t	 evplen;
294 
295 	evplen = queue_envelope_dump_buffer(ep, evpbuf, sizeof evpbuf);
296 	if (evplen == 0)
297 		return (0);
298 
299 	return env->sc_queue->envelope(QOP_UPDATE, &ep->id, evpbuf, evplen);
300 }
301 
302 void *
303 qwalk_new(uint32_t msgid)
304 {
305 	return env->sc_queue->qwalk_new(msgid);
306 }
307 
308 int
309 qwalk(void *hdl, uint64_t *evpid)
310 {
311 	return env->sc_queue->qwalk(hdl, evpid);
312 }
313 
314 void
315 qwalk_close(void *hdl)
316 {
317 	return env->sc_queue->qwalk_close(hdl);
318 }
319 
320 uint32_t
321 queue_generate_msgid(void)
322 {
323 	uint32_t msgid;
324 
325 	while((msgid = arc4random_uniform(0xffffffff)) == 0)
326 		;
327 
328 	return msgid;
329 }
330 
331 uint64_t
332 queue_generate_evpid(uint32_t msgid)
333 {
334 	uint32_t rnd;
335 	uint64_t evpid;
336 
337 	while((rnd = arc4random_uniform(0xffffffff)) == 0)
338 		;
339 
340 	evpid = msgid;
341 	evpid <<= 32;
342 	evpid |= rnd;
343 
344 	return evpid;
345 }
346 
347 
348 /**/
349 static const char*
350 envelope_validate(struct envelope *ep)
351 {
352 	if (ep->version != SMTPD_ENVELOPE_VERSION)
353 		return "version mismatch";
354 
355 	if (memchr(ep->helo, '\0', sizeof(ep->helo)) == NULL)
356 		return "invalid helo";
357 	if (ep->helo[0] == '\0')
358 		return "empty helo";
359 
360 	if (memchr(ep->hostname, '\0', sizeof(ep->hostname)) == NULL)
361 		return "invalid hostname";
362 	if (ep->hostname[0] == '\0')
363 		return "empty hostname";
364 
365 	if (memchr(ep->errorline, '\0', sizeof(ep->errorline)) == NULL)
366 		return "invalid error line";
367 
368 	return NULL;
369 }
370