1/*++ 2/* NAME 3/* unix_send_fd 3 4/* SUMMARY 5/* send file descriptor 6/* SYNOPSIS 7/* #include <iostuff.h> 8/* 9/* int unix_send_fd(fd, sendfd) 10/* int fd; 11/* int sendfd; 12/* DESCRIPTION 13/* unix_send_fd() sends a file descriptor over the specified 14/* UNIX-domain socket. 15/* 16/* Arguments: 17/* .IP fd 18/* File descriptor that connects the sending and receiving processes. 19/* .IP sendfd 20/* The file descriptor to be sent. 21/* DIAGNOSTICS 22/* unix_send_fd() returns -1 upon failure. 23/* LICENSE 24/* .ad 25/* .fi 26/* The Secure Mailer license must be distributed with this software. 27/* AUTHOR(S) 28/* Wietse Venema 29/* IBM T.J. Watson Research 30/* P.O. Box 704 31/* Yorktown Heights, NY 10598, USA 32/*--*/ 33 34/* System library. */ 35 36#include <sys_defs.h> /* includes <sys/types.h> */ 37#include <sys/socket.h> 38#include <sys/uio.h> 39#include <string.h> 40 41/* Utility library. */ 42 43#include <msg.h> 44#include <iostuff.h> 45 46/* unix_send_fd - send file descriptor */ 47 48int unix_send_fd(int fd, int sendfd) 49{ 50 51 /* 52 * This code does not work with version <2.2 Linux kernels, and it does 53 * not compile with version <2 Linux libraries. 54 */ 55#ifdef CANT_USE_SEND_RECV_MSG 56 const char *myname = "unix_send_fd"; 57 58 msg_warn("%s: your system has no support for file descriptor passing", 59 myname); 60 return (-1); 61#else 62 struct msghdr msg; 63 struct iovec iov[1]; 64 65 /* 66 * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1, 67 * Second edition. Except that we use CMSG_LEN instead of CMSG_SPACE, for 68 * portability to some LP64 environments. See also unix_recv_fd.c. 69 */ 70#if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL) 71 union { 72 struct cmsghdr just_for_alignment; 73 char control[CMSG_SPACE(sizeof(sendfd))]; 74 } control_un; 75 struct cmsghdr *cmptr; 76 77 memset((char *) &msg, 0, sizeof(msg)); /* Fix 200512 */ 78 msg.msg_control = control_un.control; 79 if (unix_pass_fd_fix & UNIX_PASS_FD_FIX_CMSG_LEN) { 80 msg.msg_controllen = CMSG_LEN(sizeof(sendfd)); /* Fix 200506 */ 81 } else { 82 msg.msg_controllen = sizeof(control_un.control); /* normal */ 83 } 84 cmptr = CMSG_FIRSTHDR(&msg); 85 cmptr->cmsg_len = CMSG_LEN(sizeof(sendfd)); 86 cmptr->cmsg_level = SOL_SOCKET; 87 cmptr->cmsg_type = SCM_RIGHTS; 88 *(int *) CMSG_DATA(cmptr) = sendfd; 89#else 90 msg.msg_accrights = (char *) &sendfd; 91 msg.msg_accrightslen = sizeof(sendfd); 92#endif 93 94 msg.msg_name = 0; 95 msg.msg_namelen = 0; 96 97 /* 98 * XXX We don't want to pass any data, just a file descriptor. However, 99 * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble. See the 100 * comments in the unix_recv_fd() routine. 101 */ 102 iov->iov_base = ""; 103 iov->iov_len = 1; 104 msg.msg_iov = iov; 105 msg.msg_iovlen = 1; 106 107 /* 108 * The CMSG_LEN send/receive workaround was originally developed for 109 * OpenBSD 3.6 on SPARC64. After the workaround was verified to not break 110 * Solaris 8 on SPARC64, it was hard-coded with Postfix 2.3 for all 111 * platforms because of increasing pressure to work on other things. The 112 * workaround does nothing for 32-bit systems. 113 * 114 * The investigation was reopened with Postfix 2.7 because the workaround 115 * broke with NetBSD 5.0 on 64-bit architectures. This time it was found 116 * that OpenBSD <= 4.3 on AMD64 and SPARC64 needed the workaround for 117 * sending only. The following platforms worked with and without the 118 * workaround: OpenBSD 4.5 on AMD64 and SPARC64, FreeBSD 7.2 on AMD64, 119 * Solaris 8 on SPARC64, and Linux 2.6-11 on x86_64. 120 * 121 * As this appears to have been an OpenBSD-specific problem, we revert to 122 * the Postfix 2.2 behavior. Instead of hard-coding the workaround for 123 * all platforms, we now detect sendmsg() errors at run time and turn on 124 * the workaround dynamically. 125 * 126 * The workaround was made run-time configurable to investigate the problem 127 * on multiple platforms. Though set_unix_pass_fd_fix() is over-kill for 128 * this specific problem, it is left in place so that it can serve as an 129 * example of how to add run-time configurable workarounds to Postfix. 130 */ 131 if (sendmsg(fd, &msg, 0) >= 0) 132 return (0); 133 if (unix_pass_fd_fix == 0) { 134 if (msg_verbose) 135 msg_info("sendmsg error (%m). Trying CMSG_LEN workaround."); 136 unix_pass_fd_fix = UNIX_PASS_FD_FIX_CMSG_LEN; 137 return (unix_send_fd(fd, sendfd)); 138 } else { 139 return (-1); 140 } 141#endif 142} 143 144#ifdef TEST 145 146 /* 147 * Proof-of-concept program. Open a file and send the descriptor, presumably 148 * to the unix_recv_fd test program. 149 */ 150#include <unistd.h> 151#include <string.h> 152#include <stdlib.h> 153#include <fcntl.h> 154#include <split_at.h> 155#include <connect.h> 156 157int main(int argc, char **argv) 158{ 159 char *transport; 160 char *endpoint; 161 char *path; 162 int server_sock; 163 int client_fd; 164 165 msg_verbose = 1; 166 167 if (argc < 3 168 || (endpoint = split_at(transport = argv[1], ':')) == 0 169 || *endpoint == 0 || *transport == 0) 170 msg_fatal("usage: %s transport:endpoint file...", argv[0]); 171 172 if (strcmp(transport, "unix") == 0) { 173 server_sock = unix_connect(endpoint, BLOCKING, 0); 174 } else { 175 msg_fatal("invalid transport name: %s", transport); 176 } 177 if (server_sock < 0) 178 msg_fatal("connect %s:%s: %m", transport, endpoint); 179 180 argv += 2; 181 while ((path = *argv++) != 0) { 182 if ((client_fd = open(path, O_RDONLY, 0)) < 0) 183 msg_fatal("open %s: %m", path); 184 msg_info("path=%s fd=%d", path, client_fd); 185 if (unix_send_fd(server_sock, client_fd) < 0) 186 msg_fatal("send file descriptor: %m"); 187 if (close(client_fd) != 0) 188 msg_fatal("close(%d): %m", client_fd); 189 } 190 exit(0); 191} 192 193#endif 194