diff --git a/postfix/HISTORY b/postfix/HISTORY index 11e8501ef..693173fe0 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -15268,3 +15268,13 @@ Apologies for any names omitted. and broke reject_unauthenticated_sender_login_mismatch and reject_sender_login_mismatch. Based on fix by Victor Duchovni. File: smtpd/smtpd_check.c. + +20090603 + + Cleanup: Postfix 2.3 adopted a file descriptor passing + workaround for OpenBSD. This workaround was hard-coded for + all platforms because there were no have adverse effects. + This is no longer the case: OpenBSD is fixed, and NetBSD + does not like the workaround. We now default back to the + non-workaround code and turn on the workaround dynamically. + Files: util/unix_send_fd.c, unix_recv_fd.c, unix_pass_fd_fix.c. diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 8a6f55a6d..f17139bfc 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20090528" +#define MAIL_RELEASE_DATE "20090603" #define MAIL_VERSION_NUMBER "2.7" #ifdef SNAPSHOT diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index de0172e2f..96ca508b8 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -31,7 +31,8 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \ vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \ write_buf.c write_wait.c sane_basename.c format_tv.c allspace.c \ allascii.c load_file.c killme_after.c vstream_tweak.c upass_connect.c \ - upass_listen.c upass_trigger.c edit_file.c inet_windowsize.c + upass_listen.c upass_trigger.c edit_file.c inet_windowsize.c \ + unix_pass_fd_fix.c OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ @@ -64,7 +65,8 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \ write_buf.o write_wait.o sane_basename.o format_tv.o allspace.o \ allascii.o load_file.o killme_after.o vstream_tweak.o upass_connect.o \ - upass_listen.o upass_trigger.o edit_file.o inet_windowsize.o + upass_listen.o upass_trigger.o edit_file.o inet_windowsize.o \ + unix_pass_fd_fix.o HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \ chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \ dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \ @@ -86,7 +88,7 @@ HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \ vstring_vstream.h watchdog.h format_tv.h load_file.h killme_after.h \ edit_file.h TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \ - stream_test.c dup2_pass_on_exec.c + stream_test.c dup2_pass_on_exec.c test_send_fd test_recv_fd DEFS = -I. -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) FILES = Makefile $(SRCS) $(HDRS) @@ -101,7 +103,8 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ inet_addr_list attr_print64 attr_scan64 base64_code attr_print0 \ attr_scan0 host_port attr_scan_plain attr_print_plain htable \ unix_recv_fd unix_send_fd stream_recv_fd stream_send_fd hex_code \ - myaddrinfo myaddrinfo4 inet_proto sane_basename format_tv + myaddrinfo myaddrinfo4 inet_proto sane_basename format_tv \ + test_send_fd test_recv_fd LIB_DIR = ../../lib INC_DIR = ../../include @@ -568,8 +571,8 @@ argv.o: mymalloc.h argv.o: sys_defs.h argv_split.o: argv.h argv_split.o: argv_split.c -argv_split.o: mymalloc.h argv_split.o: msg.h +argv_split.o: mymalloc.h argv_split.o: stringops.h argv_split.o: sys_defs.h argv_split.o: vbuf.h @@ -1552,6 +1555,12 @@ unix_listen.o: msg.h unix_listen.o: sane_accept.h unix_listen.o: sys_defs.h unix_listen.o: unix_listen.c +unix_pass_fd_fix.o: iostuff.h +unix_pass_fd_fix.o: name_mask.h +unix_pass_fd_fix.o: sys_defs.h +unix_pass_fd_fix.o: unix_pass_fd_fix.c +unix_pass_fd_fix.o: vbuf.h +unix_pass_fd_fix.o: vstring.h unix_recv_fd.o: iostuff.h unix_recv_fd.o: msg.h unix_recv_fd.o: sys_defs.h diff --git a/postfix/src/util/iostuff.h b/postfix/src/util/iostuff.h index da0955c66..8a2704a96 100644 --- a/postfix/src/util/iostuff.h +++ b/postfix/src/util/iostuff.h @@ -45,6 +45,12 @@ extern void set_inet_windowsize(int, int); #define CLOSE_ON_EXEC 1 #define PASS_ON_EXEC 0 +extern int unix_pass_fd_fix; +extern void set_unix_pass_fd_fix(const char *); + +#define UNIX_PASS_FD_FIX_NONE (0) +#define UNIX_PASS_FD_FIX_CMSG_LEN (1<<0) + /* LICENSE /* .ad /* .fi diff --git a/postfix/src/util/unix_pass_fd_fix.c b/postfix/src/util/unix_pass_fd_fix.c new file mode 100644 index 000000000..d59f08982 --- /dev/null +++ b/postfix/src/util/unix_pass_fd_fix.c @@ -0,0 +1,67 @@ +/*++ +/* NAME +/* unix_pass_fd_fix 3 +/* SUMMARY +/* file descriptor passing bug workarounds +/* SYNOPSIS +/* #include +/* +/* void set_unix_pass_fd_fix(workarounds) +/* const char *workarounds; +/* DESCRIPTION +/* This module supports programmatic control over workarounds +/* for sending or receiving file descriptors over UNIX-domain +/* sockets. +/* +/* set_unix_pass_fd_fix() takes a list of workarouds in external +/* form, and stores their internal representation. The result +/* is used by unix_send_fd() and unix_recv_fd(). +/* +/* Arguments: +/* .IP workarounds +/* List of zero or more of the following, separated by comma +/* or whitespace. +/* .RS +/* .IP cmsg_len +/* Send the CMSG_LEN of the file descriptor, instead of +/* the total message buffer length. +/* .RE +/* SEE ALSO +/* unix_send_fd(3) send file descriptor +/* unix_recv_fd(3) receive file descriptor +/* DIAGNOSTICS +/* Fatal errors: non-existent workaround. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +int unix_pass_fd_fix = 0; + +/* set_unix_pass_fd_fix - set workaround programmatically */ + +void set_unix_pass_fd_fix(const char *workarounds) +{ + const static NAME_MASK table[] = { + "cmsg_len", UNIX_PASS_FD_FIX_CMSG_LEN, + 0, + }; + + unix_pass_fd_fix = name_mask("descriptor passing workarounds", + table, workarounds); +} diff --git a/postfix/src/util/unix_recv_fd.c b/postfix/src/util/unix_recv_fd.c index 7958489c4..cb6c8aaff 100644 --- a/postfix/src/util/unix_recv_fd.c +++ b/postfix/src/util/unix_recv_fd.c @@ -63,7 +63,7 @@ int unix_recv_fd(int fd) /* * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1, * Second edition. Except that we use CMSG_LEN instead of CMSG_SPACE, for - * portability to LP64 environments. + * portability to some LP64 environments. See also unix_send_fd.c. */ #if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL) union { @@ -74,7 +74,11 @@ int unix_recv_fd(int fd) memset((char *) &msg, 0, sizeof(msg)); /* Fix 200512 */ msg.msg_control = control_un.control; - msg.msg_controllen = CMSG_LEN(sizeof(newfd)); /* Fix 200506 */ + if (unix_pass_fd_fix & UNIX_PASS_FD_FIX_CMSG_LEN) { + msg.msg_controllen = CMSG_LEN(sizeof(newfd)); /* Fix 200506 */ + } else { + msg.msg_controllen = sizeof(control_un.control); /* normal */ + } #else msg.msg_accrights = (char *) &newfd; msg.msg_accrightslen = sizeof(newfd); @@ -141,10 +145,10 @@ int main(int argc, char **argv) ssize_t read_count; char buf[1024]; - if (argc != 2 + if (argc < 2 || argc > 3 || (endpoint = split_at(transport = argv[1], ':')) == 0 || *endpoint == 0 || *transport == 0) - msg_fatal("usage: %s transport:endpoint", argv[0]); + msg_fatal("usage: %s transport:endpoint [workaround]", argv[0]); if (strcmp(transport, "unix") == 0) { listen_sock = unix_listen(endpoint, 10, BLOCKING); @@ -158,8 +162,10 @@ int main(int argc, char **argv) if (client_sock < 0) msg_fatal("accept: %m"); + set_unix_pass_fd_fix(argv[2] ? argv[2] : ""); + while ((client_fd = unix_recv_fd(client_sock)) >= 0) { - msg_info("client_fd = %d", client_fd); + msg_info("client_fd = %d, fix=%d", client_fd, unix_pass_fd_fix); while ((read_count = read(client_fd, buf, sizeof(buf))) > 0) write(1, buf, read_count); if (read_count < 0) diff --git a/postfix/src/util/unix_send_fd.c b/postfix/src/util/unix_send_fd.c index a598b4496..d9266244a 100644 --- a/postfix/src/util/unix_send_fd.c +++ b/postfix/src/util/unix_send_fd.c @@ -64,8 +64,8 @@ int unix_send_fd(int fd, int sendfd) /* * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1, - * Second edition. Except that we use CMSG_LEN instead of CMSG_SPACE; the - * latter breaks on LP64 systems. + * Second edition. Except that we use CMSG_LEN instead of CMSG_SPACE, for + * portability to some LP64 environments. See also unix_recv_fd.c. */ #if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL) union { @@ -74,10 +74,13 @@ int unix_send_fd(int fd, int sendfd) } control_un; struct cmsghdr *cmptr; - memset((char *) &msg, 0, sizeof(msg)); /* Fix 200512 */ + memset((char *) &msg, 0, sizeof(msg)); /* Fix 200512 */ msg.msg_control = control_un.control; - msg.msg_controllen = CMSG_LEN(sizeof(sendfd)); /* Fix 200506 */ - + if (unix_pass_fd_fix & UNIX_PASS_FD_FIX_CMSG_LEN) { + msg.msg_controllen = CMSG_LEN(sizeof(sendfd)); /* Fix 200506 */ + } else { + msg.msg_controllen = sizeof(control_un.control); /* normal */ + } cmptr = CMSG_FIRSTHDR(&msg); cmptr->cmsg_len = CMSG_LEN(sizeof(sendfd)); cmptr->cmsg_level = SOL_SOCKET; @@ -101,7 +104,34 @@ int unix_send_fd(int fd, int sendfd) msg.msg_iov = iov; msg.msg_iovlen = 1; - return (sendmsg(fd, &msg, 0)); + /* + * The CMSG_LEN workaround was originally developed for OpenBSD 3.6 on + * 64-bit SPARC (later also confirmed on AMD64). It was hard-coded with + * Postfix 2.3 for all platforms because of increasing pressure to work + * on other things. + * + * The investigation was reopened with Postfix 2.7 because the workaround + * broke on NetBSD 5.0. This time it was found that OpenBSD on AMD64 + * needs the workaround for sending only. We assume that OpenBSD on AMD64 + * and SPARC64 are similar, and turn on the workaround on-the-fly. The + * OpenBSD problem was fixed for AMD64 and SPARC64 with OpenBSD 4.4 or + * 4.5. + * + * The workaround was made run-time configurable to make the investigation + * possible on many different platforms. Though the code is over-kill for + * this particular problem, it is left in place so that it can serve as + * an example of how to add run-time configurable workarounds to Postfix. + */ + if (sendmsg(fd, &msg, 0) >= 0) + return (0); + if (unix_pass_fd_fix == 0) { + if (msg_verbose) + msg_info("sendmsg error (%m). Trying CMSG_LEN workaround."); + unix_pass_fd_fix = UNIX_PASS_FD_FIX_CMSG_LEN; + return (unix_send_fd(fd, sendfd)); + } else { + return (-1); + } #endif } @@ -126,6 +156,8 @@ int main(int argc, char **argv) int server_sock; int client_fd; + msg_verbose = 1; + if (argc < 3 || (endpoint = split_at(transport = argv[1], ':')) == 0 || *endpoint == 0 || *transport == 0)