1 /* $NetBSD: unix_send_fd.c,v 1.7 2017/02/14 01:16:49 christos 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 char control[CMSG_SPACE(sizeof(sendfd))]; 76 } control_un; 77 struct cmsghdr *cmptr; 78 79 memset((void *) &msg, 0, sizeof(msg)); /* Fix 200512 */ 80 msg.msg_control = control_un.control; 81 if (unix_pass_fd_fix & UNIX_PASS_FD_FIX_CMSG_LEN) { 82 msg.msg_controllen = CMSG_LEN(sizeof(sendfd)); /* Fix 200506 */ 83 } else { 84 msg.msg_controllen = CMSG_SPACE(sizeof(sendfd)); /* normal */ 85 } 86 cmptr = CMSG_FIRSTHDR(&msg); 87 cmptr->cmsg_len = CMSG_LEN(sizeof(sendfd)); 88 cmptr->cmsg_level = SOL_SOCKET; 89 cmptr->cmsg_type = SCM_RIGHTS; 90 *(int *) CMSG_DATA(cmptr) = sendfd; 91 #else 92 msg.msg_accrights = (char *) &sendfd; 93 msg.msg_accrightslen = sizeof(sendfd); 94 #endif 95 96 msg.msg_name = 0; 97 msg.msg_namelen = 0; 98 99 /* 100 * XXX We don't want to pass any data, just a file descriptor. However, 101 * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble. See the 102 * comments in the unix_recv_fd() routine. 103 */ 104 iov->iov_base = ""; 105 iov->iov_len = 1; 106 msg.msg_iov = iov; 107 msg.msg_iovlen = 1; 108 109 /* 110 * The CMSG_LEN send/receive workaround was originally developed for 111 * OpenBSD 3.6 on SPARC64. After the workaround was verified to not break 112 * Solaris 8 on SPARC64, it was hard-coded with Postfix 2.3 for all 113 * platforms because of increasing pressure to work on other things. The 114 * workaround does nothing for 32-bit systems. 115 * 116 * The investigation was reopened with Postfix 2.7 because the workaround 117 * broke with NetBSD 5.0 on 64-bit architectures. This time it was found 118 * that OpenBSD <= 4.3 on AMD64 and SPARC64 needed the workaround for 119 * sending only. The following platforms worked with and without the 120 * workaround: OpenBSD 4.5 on AMD64 and SPARC64, FreeBSD 7.2 on AMD64, 121 * Solaris 8 on SPARC64, and Linux 2.6-11 on x86_64. 122 * 123 * As this appears to have been an OpenBSD-specific problem, we revert to 124 * the Postfix 2.2 behavior. Instead of hard-coding the workaround for 125 * all platforms, we now detect sendmsg() errors at run time and turn on 126 * the workaround dynamically. 127 * 128 * The workaround was made run-time configurable to investigate the problem 129 * on multiple platforms. Though set_unix_pass_fd_fix() is over-kill for 130 * this specific problem, it is left in place so that it can serve as an 131 * example of how to add run-time configurable workarounds to Postfix. 132 */ 133 if (sendmsg(fd, &msg, 0) >= 0) 134 return (0); 135 if (unix_pass_fd_fix == 0) { 136 if (msg_verbose) 137 msg_info("sendmsg error (%m). Trying CMSG_LEN workaround."); 138 unix_pass_fd_fix = UNIX_PASS_FD_FIX_CMSG_LEN; 139 return (unix_send_fd(fd, sendfd)); 140 } else { 141 return (-1); 142 } 143 #endif 144 } 145 146 #ifdef TEST 147 148 /* 149 * Proof-of-concept program. Open a file and send the descriptor, presumably 150 * to the unix_recv_fd test program. 151 */ 152 #include <unistd.h> 153 #include <string.h> 154 #include <stdlib.h> 155 #include <fcntl.h> 156 #include <split_at.h> 157 #include <connect.h> 158 159 int main(int argc, char **argv) 160 { 161 char *transport; 162 char *endpoint; 163 char *path; 164 int server_sock; 165 int client_fd; 166 167 msg_verbose = 1; 168 169 if (argc < 3 170 || (endpoint = split_at(transport = argv[1], ':')) == 0 171 || *endpoint == 0 || *transport == 0) 172 msg_fatal("usage: %s transport:endpoint file...", argv[0]); 173 174 if (strcmp(transport, "unix") == 0) { 175 server_sock = unix_connect(endpoint, BLOCKING, 0); 176 } else { 177 msg_fatal("invalid transport name: %s", transport); 178 } 179 if (server_sock < 0) 180 msg_fatal("connect %s:%s: %m", transport, endpoint); 181 182 argv += 2; 183 while ((path = *argv++) != 0) { 184 if ((client_fd = open(path, O_RDONLY, 0)) < 0) 185 msg_fatal("open %s: %m", path); 186 msg_info("path=%s fd=%d", path, client_fd); 187 if (unix_send_fd(server_sock, client_fd) < 0) 188 msg_fatal("send file descriptor: %m"); 189 if (close(client_fd) != 0) 190 msg_fatal("close(%d): %m", client_fd); 191 } 192 exit(0); 193 } 194 195 #endif 196