1 /*	$NetBSD: mail_stream.c,v 1.1.1.1 2009/06/23 10:08:47 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	mail_stream 3
6 /* SUMMARY
7 /*	mail stream management
8 /* SYNOPSIS
9 /*	#include <mail_stream.h>
10 /*
11 /*	typedef struct {
12 /* .in +4
13 /*		VSTREAM	*stream;	/* read/write stream */
14 /*		char	*id;		/* queue ID */
15 /*		struct timeval ctime;	/* create time */
16 /*		private members...
17 /* .in -4
18 /*	} MAIL_STREAM;
19 /*
20 /*	MAIL_STREAM *mail_stream_file(queue, class, service, mode)
21 /*	const char *queue;
22 /*	const char *class;
23 /*	const char *service;
24 /*	int	mode;
25 /*
26 /*	MAIL_STREAM *mail_stream_service(class, service)
27 /*	const char *class;
28 /*	const char *service;
29 /*
30 /*	MAIL_STREAM *mail_stream_command(command)
31 /*	const char *command;
32 /*
33 /*	void	mail_stream_cleanup(info)
34 /*	MAIL_STREAM *info;
35 /*
36 /*	int	mail_stream_finish(info, why)
37 /*	MAIL_STREAM *info;
38 /*	VSTRING	*why;
39 /*
40 /*	void	mail_stream_ctl(info, op, ...)
41 /*	MAIL_STREAM *info;
42 /*	int	op;
43 /* DESCRIPTION
44 /*	This module provides a generic interface to Postfix queue file
45 /*	format messages to file, to Postfix server, or to external command.
46 /*	The routines that open a stream return a handle with an initialized
47 /*	stream and queue id member. The handle is either given to a cleanup
48 /*	routine, to dispose of a failed request, or to a finish routine, to
49 /*	complete the request.
50 /*
51 /*	mail_stream_file() opens a mail stream to a newly-created file and
52 /*	arranges for trigger delivery at finish time. This call never fails.
53 /*	But it may take forever. The mode argument specifies additional
54 /*	file permissions that will be OR-ed in when the file is finished.
55 /*	While embryonic files have mode 0600, finished files have mode 0700.
56 /*
57 /*	mail_stream_command() opens a mail stream to external command,
58 /*	and receives queue ID information from the command. The result
59 /*	is a null pointer when the initial handshake fails. The command
60 /*	is given to the shell only when necessary. At finish time, the
61 /*	command is expected to send a completion status.
62 /*
63 /*	mail_stream_service() opens a mail stream to Postfix service,
64 /*	and receives queue ID information from the command. The result
65 /*	is a null pointer when the initial handshake fails. At finish
66 /*	time, the daemon is expected to send a completion status.
67 /*
68 /*	mail_stream_cleanup() cancels the operation that was started with
69 /*	any of the mail_stream_xxx() routines, and destroys the argument.
70 /*	It is up to the caller to remove incomplete file objects.
71 /*
72 /*	mail_stream_finish() completes the operation that was started with
73 /*	any of the mail_stream_xxx() routines, and destroys the argument.
74 /*	The result is any of the status codes defined in <cleanup_user.h>.
75 /*	It is up to the caller to remove incomplete file objects.
76 /*	The why argument can be a null pointer.
77 /*
78 /*	mail_stream_ctl() selectively overrides information that
79 /*	was specified with mail_stream_file(); none of the attributes
80 /*	are applicable for other mail stream types.  The arguments
81 /*	are a list of (operation, value) pairs, terminated with
82 /*	MAIL_STREAM_CTL_END.  The following lists the operation
83 /*	codes and the types of the corresponding value arguments.
84 /* .IP "MAIL_STREAM_CTL_QUEUE (char *)"
85 /*	The argument specifies an alternate destination queue. The
86 /*	queue file is moved to the specified queue before the call
87 /*	returns. Failure to rename the queue file results in a fatal
88 /*	error.
89 /* .IP "MAIL_STREAM_CTL_CLASS (char *)"
90 /*	The argument specifies an alternate trigger class.
91 /* .IP "MAIL_STREAM_CTL_SERVICE (char *)"
92 /*	The argument specifies an alternate trigger service.
93 /* .IP "MAIL_STREAM_CTL_MODE (int)"
94 /*	The argument specifies alternate permissions that override
95 /*	the permissions specified with mail_stream_file().
96 /* .IP "MAIL_STREAM_CTL_DELAY (int)"
97 /*	Attempt to postpone initial delivery by advancing the queue
98 /*	file modification time stamp by this amount.  This has
99 /*	effect only within the deferred mail queue.
100 /*	This feature may have no effect with remote file systems.
101 /* LICENSE
102 /* .ad
103 /* .fi
104 /*	The Secure Mailer license must be distributed with this software.
105 /* AUTHOR(S)
106 /*	Wietse Venema
107 /*	IBM T.J. Watson Research
108 /*	P.O. Box 704
109 /*	Yorktown Heights, NY 10598, USA
110 /*--*/
111 
112 /* System library. */
113 
114 #include <sys_defs.h>
115 #include <sys/stat.h>
116 #include <unistd.h>
117 #include <errno.h>
118 #include <utime.h>
119 #include <string.h>
120 #include <stdarg.h>
121 
122 /* Utility library. */
123 
124 #include <msg.h>
125 #include <mymalloc.h>
126 #include <vstring.h>
127 #include <vstream.h>
128 #include <stringops.h>
129 #include <argv.h>
130 #include <sane_fsops.h>
131 
132 /* Global library. */
133 
134 #include <cleanup_user.h>
135 #include <mail_proto.h>
136 #include <mail_queue.h>
137 #include <opened.h>
138 #include <mail_params.h>
139 #include <mail_stream.h>
140 
141 /* Application-specific. */
142 
143 static VSTRING *id_buf;
144 
145 #define FREE_AND_WIPE(free, arg) do { if (arg) free(arg); arg = 0; } while (0)
146 
147 #define STR(x)	vstring_str(x)
148 
149 /* mail_stream_cleanup - clean up after success or failure */
150 
151 void    mail_stream_cleanup(MAIL_STREAM *info)
152 {
153     FREE_AND_WIPE(info->close, info->stream);
154     FREE_AND_WIPE(myfree, info->queue);
155     FREE_AND_WIPE(myfree, info->id);
156     FREE_AND_WIPE(myfree, info->class);
157     FREE_AND_WIPE(myfree, info->service);
158     myfree((char *) info);
159 }
160 
161 #if defined(HAS_FUTIMES_AT)
162 #define CAN_STAMP_BY_STREAM
163 
164 /* stamp_stream - update open file [am]time stamp */
165 
166 static int stamp_stream(VSTREAM *fp, time_t when)
167 {
168     struct timeval tv[2];
169 
170     if (when != 0) {
171 	tv[0].tv_sec = tv[1].tv_sec = when;
172 	tv[0].tv_usec = tv[1].tv_usec = 0;
173 	return (futimesat(vstream_fileno(fp), (char *) 0, tv));
174     } else {
175 	return (futimesat(vstream_fileno(fp), (char *) 0, (struct timeval *) 0));
176     }
177 }
178 
179 #elif defined(HAS_FUTIMES)
180 #define CAN_STAMP_BY_STREAM
181 
182 /* stamp_stream - update open file [am]time stamp */
183 
184 static int stamp_stream(VSTREAM *fp, time_t when)
185 {
186     struct timeval tv[2];
187 
188     if (when != 0) {
189 	tv[0].tv_sec = tv[1].tv_sec = when;
190 	tv[0].tv_usec = tv[1].tv_usec = 0;
191 	return (futimes(vstream_fileno(fp), tv));
192     } else {
193 	return (futimes(vstream_fileno(fp), (struct timeval *) 0));
194     }
195 }
196 
197 #endif
198 
199 /* stamp_path - update file [am]time stamp by pathname */
200 
201 static int stamp_path(const char *path, time_t when)
202 {
203     struct utimbuf tbuf;
204 
205     if (when != 0) {
206 	tbuf.actime = tbuf.modtime = when;
207 	return (utime(path, &tbuf));
208     } else {
209 	return (utime(path, (struct utimbuf *) 0));
210     }
211 }
212 
213 /* mail_stream_finish_file - finish file mail stream */
214 
215 static int mail_stream_finish_file(MAIL_STREAM *info, VSTRING *unused_why)
216 {
217     int     status = CLEANUP_STAT_OK;
218     static char wakeup[] = {TRIGGER_REQ_WAKEUP};
219     struct stat st;
220     char   *path_to_reset = 0;
221     static int incoming_fs_clock_ok = 0;
222     static int incoming_clock_warned = 0;
223     int     check_incoming_fs_clock;
224     int     err;
225     time_t  want_stamp;
226     time_t  expect_stamp;
227 
228     /*
229      * Make sure the message makes it to file. Set the execute bit when no
230      * write error was detected. Some people believe that this code has a
231      * problem if the system crashes before fsync() returns; fchmod() could
232      * take effect before all the data blocks are written. Wietse claims that
233      * this is not a problem. Postfix rejects incomplete queue files, even
234      * when the +x attribute is set. Every Postfix queue file record has a
235      * type code and a length field. Files with missing records are rejected,
236      * as are files with unknown record type codes. Every Postfix queue file
237      * must end with an explicit END record. Postfix queue files without END
238      * record are discarded.
239      *
240      * Attempt to detect file system clocks that are ahead of local time, but
241      * don't check the file system clock all the time. The effect of file
242      * system clock drift can be difficult to understand (Postfix ignores new
243      * mail until the local clock catches up with the file mtime stamp).
244      *
245      * This clock drift detection code may not work with file systems that work
246      * on a local copy of the file and that update the server only after the
247      * file is closed.
248      *
249      * Optionally set a cooldown time.
250      *
251      * XXX: We assume that utime() does control the file modification time even
252      * when followed by an fchmod(), fsync(), close() sequence. This may fail
253      * with remote file systems when fsync() actually updates the file. Even
254      * then, we still delay the average message by 1/2 of the
255      * queue_run_delay.
256      *
257      * XXX: Victor does not like running utime() after the close(), since this
258      * creates a race even with local filesystems. But Wietse is not
259      * confident that utime() before fsync() and close() will work reliably
260      * with remote file systems.
261      *
262      * XXX Don't run the clock skew tests with Postfix sendmail submissions.
263      * Don't whine against unsuspecting users or applications.
264      */
265     check_incoming_fs_clock =
266 	(!incoming_fs_clock_ok && !strcmp(info->queue, MAIL_QUEUE_INCOMING));
267 
268 #ifdef DELAY_ACTION
269     if (strcmp(info->queue, MAIL_QUEUE_DEFERRED) != 0)
270 	info->delay = 0;
271     if (info->delay > 0)
272 	want_stamp = time((time_t *) 0) + info->delay;
273     else
274 #endif
275 	want_stamp = 0;
276 
277     /*
278      * If we can cheaply set the file time stamp (no pathname lookup) do it
279      * anyway, so that we can avoid whining later about file server/client
280      * clock skew.
281      *
282      * Otherwise, if we must set the file time stamp for delayed delivery, use
283      * whatever means we have to get the job done, no matter if it is
284      * expensive.
285      *
286      * XXX Unfortunately, Linux futimes() is not usable because it uses /proc.
287      * This may not be available because of chroot, or because of access
288      * restrictions after a process changes privileges.
289      */
290     if (vstream_fflush(info->stream)
291 #ifdef CAN_STAMP_BY_STREAM
292 	|| stamp_stream(info->stream, want_stamp)
293 #else
294 	|| (want_stamp && stamp_path(VSTREAM_PATH(info->stream), want_stamp))
295 #endif
296 	|| fchmod(vstream_fileno(info->stream), 0700 | info->mode)
297 #ifdef HAS_FSYNC
298 	|| fsync(vstream_fileno(info->stream))
299 #endif
300 	|| (check_incoming_fs_clock
301 	    && fstat(vstream_fileno(info->stream), &st) < 0)
302 	)
303 	status = (errno == EFBIG ? CLEANUP_STAT_SIZE : CLEANUP_STAT_WRITE);
304 #ifdef TEST
305     st.st_mtime += 10;
306 #endif
307 
308     /*
309      * Work around file system clock skew. If the file system clock is ahead
310      * of the local clock, Postfix won't deliver mail immediately, which is
311      * bad for performance. If the file system clock falls behind the local
312      * clock, it just looks silly in mail headers.
313      */
314     if (status == CLEANUP_STAT_OK && check_incoming_fs_clock) {
315 	/* Do NOT use time() result from before fsync(). */
316 	expect_stamp = want_stamp ? want_stamp : time((time_t *) 0);
317 	if (st.st_mtime > expect_stamp) {
318 	    path_to_reset = mystrdup(VSTREAM_PATH(info->stream));
319 	    if (incoming_clock_warned == 0) {
320 		msg_warn("file system clock is %d seconds ahead of local clock",
321 			 (int) (st.st_mtime - expect_stamp));
322 		msg_warn("resetting file time stamps - this hurts performance");
323 		incoming_clock_warned = 1;
324 	    }
325 	} else {
326 	    if (st.st_mtime < expect_stamp - 100)
327 		msg_warn("file system clock is %d seconds behind local clock",
328 			 (int) (expect_stamp - st.st_mtime));
329 	    incoming_fs_clock_ok = 1;
330 	}
331     }
332 
333     /*
334      * Close the queue file and mark it as closed. Be prepared for
335      * vstream_fclose() to fail even after vstream_fflush() and fsync()
336      * reported no error. Reason: after a file is closed, some networked file
337      * systems copy the file out to another machine. Running the queue on a
338      * remote file system is not recommended, if only for performance
339      * reasons.
340      */
341     err = info->close(info->stream);
342     info->stream = 0;
343     if (status == CLEANUP_STAT_OK && err != 0)
344 	status = (errno == EFBIG ? CLEANUP_STAT_SIZE : CLEANUP_STAT_WRITE);
345 
346     /*
347      * Work around file system clocks that are ahead of local time.
348      */
349     if (path_to_reset != 0) {
350 	if (status == CLEANUP_STAT_OK) {
351 	    if (stamp_path(path_to_reset, expect_stamp) < 0 && errno != ENOENT)
352 		msg_fatal("%s: update file time stamps: %m", info->id);
353 	}
354 	myfree(path_to_reset);
355     }
356 
357     /*
358      * When all is well, notify the next service that a new message has been
359      * queued.
360      */
361     if (status == CLEANUP_STAT_OK && info->class && info->service)
362 	mail_trigger(info->class, info->service, wakeup, sizeof(wakeup));
363 
364     /*
365      * Cleanup.
366      */
367     mail_stream_cleanup(info);
368     return (status);
369 }
370 
371 /* mail_stream_finish_ipc - finish IPC mail stream */
372 
373 static int mail_stream_finish_ipc(MAIL_STREAM *info, VSTRING *why)
374 {
375     int     status = CLEANUP_STAT_WRITE;
376 
377     /*
378      * Receive the peer's completion status.
379      */
380     if ((why && attr_scan(info->stream, ATTR_FLAG_STRICT,
381 			  ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
382 			  ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
383 			  ATTR_TYPE_END) != 2)
384 	|| (!why && attr_scan(info->stream, ATTR_FLAG_MISSING,
385 			      ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
386 			      ATTR_TYPE_END) != 1))
387 	status = CLEANUP_STAT_WRITE;
388 
389     /*
390      * Cleanup.
391      */
392     mail_stream_cleanup(info);
393     return (status);
394 }
395 
396 /* mail_stream_finish - finish action */
397 
398 int     mail_stream_finish(MAIL_STREAM *info, VSTRING *why)
399 {
400     return (info->finish(info, why));
401 }
402 
403 /* mail_stream_file - destination is file */
404 
405 MAIL_STREAM *mail_stream_file(const char *queue, const char *class,
406 			              const char *service, int mode)
407 {
408     struct timeval tv;
409     MAIL_STREAM *info;
410     VSTREAM *stream;
411 
412     stream = mail_queue_enter(queue, 0600 | mode, &tv);
413     if (msg_verbose)
414 	msg_info("open %s", VSTREAM_PATH(stream));
415 
416     info = (MAIL_STREAM *) mymalloc(sizeof(*info));
417     info->stream = stream;
418     info->finish = mail_stream_finish_file;
419     info->close = vstream_fclose;
420     info->queue = mystrdup(queue);
421     info->id = mystrdup(basename(VSTREAM_PATH(stream)));
422     info->class = mystrdup(class);
423     info->service = mystrdup(service);
424     info->mode = mode;
425 #ifdef DELAY_ACTION
426     info->delay = 0;
427 #endif
428     info->ctime = tv;
429     return (info);
430 }
431 
432 /* mail_stream_service - destination is service */
433 
434 MAIL_STREAM *mail_stream_service(const char *class, const char *name)
435 {
436     VSTREAM *stream;
437     MAIL_STREAM *info;
438 
439     if (id_buf == 0)
440 	id_buf = vstring_alloc(10);
441 
442     stream = mail_connect_wait(class, name);
443     if (attr_scan(stream, ATTR_FLAG_MISSING,
444 		  ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id_buf, 0) != 1) {
445 	vstream_fclose(stream);
446 	return (0);
447     } else {
448 	info = (MAIL_STREAM *) mymalloc(sizeof(*info));
449 	info->stream = stream;
450 	info->finish = mail_stream_finish_ipc;
451 	info->close = vstream_fclose;
452 	info->queue = 0;
453 	info->id = mystrdup(vstring_str(id_buf));
454 	info->class = 0;
455 	info->service = 0;
456 	return (info);
457     }
458 }
459 
460 /* mail_stream_command - destination is command */
461 
462 MAIL_STREAM *mail_stream_command(const char *command)
463 {
464     VSTREAM *stream;
465     MAIL_STREAM *info;
466     ARGV   *export_env;
467     int     status;
468 
469     if (id_buf == 0)
470 	id_buf = vstring_alloc(10);
471 
472     /*
473      * Treat fork() failure as a transient problem. Treat bad handshake as a
474      * permanent error.
475      *
476      * XXX Are we invoking a Postfix process or a non-Postfix process? In the
477      * former case we can share the full environment; in the latter case only
478      * a restricted environment should be propagated. Even though we are
479      * talking a Postfix-internal protocol there is no way we can tell what
480      * is being executed except by duplicating a lot of existing code.
481      */
482     export_env = argv_split(var_export_environ, ", \t\r\n");
483     while ((stream = vstream_popen(O_RDWR,
484 				   VSTREAM_POPEN_COMMAND, command,
485 				   VSTREAM_POPEN_EXPORT, export_env->argv,
486 				   VSTREAM_POPEN_END)) == 0) {
487 	msg_warn("fork: %m");
488 	sleep(10);
489     }
490     argv_free(export_env);
491     vstream_control(stream,
492 		    VSTREAM_CTL_PATH, command,
493 		    VSTREAM_CTL_END);
494 
495     if (attr_scan(stream, ATTR_FLAG_MISSING,
496 		  ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id_buf, 0) != 1) {
497 	if ((status = vstream_pclose(stream)) != 0)
498 	    msg_warn("command \"%s\" exited with status %d", command, status);
499 	return (0);
500     } else {
501 	info = (MAIL_STREAM *) mymalloc(sizeof(*info));
502 	info->stream = stream;
503 	info->finish = mail_stream_finish_ipc;
504 	info->close = vstream_pclose;
505 	info->queue = 0;
506 	info->id = mystrdup(vstring_str(id_buf));
507 	info->class = 0;
508 	info->service = 0;
509 	return (info);
510     }
511 }
512 
513 /* mail_stream_ctl - update file-based mail stream properties */
514 
515 void    mail_stream_ctl(MAIL_STREAM *info, int op,...)
516 {
517     const char *myname = "mail_stream_ctl";
518     va_list ap;
519     char   *new_queue = 0;
520     char   *string_value;
521 
522     /*
523      * Sanity check. None of the attributes below are applicable unless the
524      * target is a file-based stream.
525      */
526     if (info->finish != mail_stream_finish_file)
527 	msg_panic("%s: attempt to update non-file stream %s",
528 		  myname, info->id);
529 
530     for (va_start(ap, op); op != MAIL_STREAM_CTL_END; op = va_arg(ap, int)) {
531 
532 	switch (op) {
533 
534 	    /*
535 	     * Change the queue directory. We do this at the end of this
536 	     * call.
537 	     */
538 	case MAIL_STREAM_CTL_QUEUE:
539 	    if ((new_queue = va_arg(ap, char *)) == 0)
540 		msg_panic("%s: NULL queue",
541 			  myname);
542 	    break;
543 
544 	    /*
545 	     * Change the service that needs to be notified.
546 	     */
547 	case MAIL_STREAM_CTL_CLASS:
548 	    FREE_AND_WIPE(myfree, info->class);
549 	    if ((string_value = va_arg(ap, char *)) != 0)
550 		info->class = mystrdup(string_value);
551 	    break;
552 
553 	case MAIL_STREAM_CTL_SERVICE:
554 	    FREE_AND_WIPE(myfree, info->service);
555 	    if ((string_value = va_arg(ap, char *)) != 0)
556 		info->service = mystrdup(string_value);
557 	    break;
558 
559 	    /*
560 	     * Change the (finished) file access mode.
561 	     */
562 	case MAIL_STREAM_CTL_MODE:
563 	    info->mode = va_arg(ap, int);
564 	    break;
565 
566 	    /*
567 	     * Advance the (finished) file modification time.
568 	     */
569 #ifdef DELAY_ACTION
570 	case MAIL_STREAM_CTL_DELAY:
571 	    if ((info->delay = va_arg(ap, int)) < 0)
572 		msg_panic("%s: bad delay time %d", myname, info->delay);
573 	    break;
574 #endif
575 
576 	default:
577 	    msg_panic("%s: bad op code %d", myname, op);
578 	}
579     }
580     va_end(ap);
581 
582     /*
583      * Rename the queue file after allocating memory for new information, so
584      * that the caller can still remove an embryonic file when memory
585      * allocation fails (there is no risk of deleting the wrong file).
586      *
587      * Wietse opposed the idea to update run-time error handler information
588      * here, because this module wasn't designed to defend against internal
589      * concurrency issues with error handlers that attempt to follow dangling
590      * pointers.
591      *
592      * This code duplicates mail_queue_rename(), except that we need the new
593      * path to update the stream pathname.
594      */
595     if (new_queue != 0 && strcmp(info->queue, new_queue) != 0) {
596 	char   *saved_queue = info->queue;
597 	char   *saved_path = mystrdup(VSTREAM_PATH(info->stream));
598 	VSTRING *new_path = vstring_alloc(100);
599 
600 	(void) mail_queue_path(new_path, new_queue, info->id);
601 	info->queue = mystrdup(new_queue);
602 	vstream_control(info->stream, VSTREAM_CTL_PATH, STR(new_path),
603 			VSTREAM_CTL_END);
604 
605 	if (sane_rename(saved_path, STR(new_path)) == 0
606 	    || (mail_queue_mkdirs(STR(new_path)) == 0
607 		&& sane_rename(saved_path, STR(new_path)) == 0)) {
608 	    if (msg_verbose)
609 		msg_info("%s: placed in %s queue", info->id, info->queue);
610 	} else {
611 	    msg_fatal("%s: move to %s queue failed: %m", info->id,
612 		      info->queue);
613 	}
614 
615 	myfree(saved_path);
616 	myfree(saved_queue);
617 	vstring_free(new_path);
618     }
619 }
620