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