1 /* $OpenBSD: unfdpass.c,v 1.2 2001/01/29 02:05:57 niklas Exp $ */ 2 /* $NetBSD: unfdpass.c,v 1.3 1998/06/24 23:51:30 thorpej Exp $ */ 3 4 /*- 5 * Copyright (c) 1998 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 10 * NASA Ames Research Center. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the NetBSD 23 * Foundation, Inc. and its contributors. 24 * 4. Neither the name of The NetBSD Foundation nor the names of its 25 * contributors may be used to endorse or promote products derived 26 * from this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 29 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 32 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 * POSSIBILITY OF SUCH DAMAGE. 39 */ 40 41 /* 42 * Test passing of file descriptors and credentials over Unix domain sockets. 43 */ 44 45 #include <sys/param.h> 46 #include <sys/socket.h> 47 #include <sys/time.h> 48 #include <sys/wait.h> 49 #include <sys/un.h> 50 #include <err.h> 51 #include <errno.h> 52 #include <fcntl.h> 53 #include <signal.h> 54 #include <stdio.h> 55 #include <string.h> 56 #include <unistd.h> 57 58 #define SOCK_NAME "test-sock" 59 60 int main __P((int, char *[])); 61 void child __P((void)); 62 void catch_sigchld __P((int)); 63 64 struct fdcmessage { 65 struct cmsghdr cm; 66 int files[2]; 67 }; 68 69 struct crcmessage { 70 struct cmsghdr cm; 71 char creds[SOCKCREDSIZE(NGROUPS)]; 72 }; 73 74 /* ARGSUSED */ 75 int 76 main(argc, argv) 77 int argc; 78 char *argv[]; 79 { 80 struct msghdr msg; 81 int listensock, sock, fd, i, status; 82 char fname[16], buf[64]; 83 struct cmsghdr *cmp; 84 struct { 85 struct fdcmessage fdcm; 86 struct crcmessage crcm; 87 } message; 88 int *files = NULL; 89 struct sockcred *sc = NULL; 90 struct sockaddr_un sun, csun; 91 int csunlen; 92 fd_set oob; 93 pid_t pid; 94 95 /* 96 * Create the test files. 97 */ 98 for (i = 0; i < 2; i++) { 99 (void) sprintf(fname, "file%d", i + 1); 100 if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) 101 err(1, "open %s", fname); 102 (void) sprintf(buf, "This is file %d.\n", i + 1); 103 if (write(fd, buf, strlen(buf)) != strlen(buf)) 104 err(1, "write %s", fname); 105 (void) close(fd); 106 } 107 108 /* 109 * Create the listen socket. 110 */ 111 if ((listensock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1) 112 err(1, "socket"); 113 114 (void) unlink(SOCK_NAME); 115 (void) memset(&sun, 0, sizeof(sun)); 116 sun.sun_family = AF_LOCAL; 117 (void) strcpy(sun.sun_path, SOCK_NAME); 118 sun.sun_len = SUN_LEN(&sun); 119 120 i = 1; 121 if (setsockopt(listensock, 0, LOCAL_CREDS, &i, sizeof(i)) == -1) 122 err(1, "setsockopt"); 123 124 if (bind(listensock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 125 err(1, "bind"); 126 127 if (listen(listensock, 1) == -1) 128 err(1, "listen"); 129 130 /* 131 * Create the sender. 132 */ 133 (void) signal(SIGCHLD, catch_sigchld); 134 pid = fork(); 135 switch (pid) { 136 case -1: 137 err(1, "fork"); 138 /* NOTREACHED */ 139 140 case 0: 141 child(); 142 /* NOTREACHED */ 143 } 144 145 /* 146 * Wait for the sender to connect. 147 */ 148 if ((sock = accept(listensock, (struct sockaddr *)&csun, 149 &csunlen)) == -1) 150 err(1, "accept"); 151 152 /* 153 * Give sender a chance to run. We will get going again 154 * once the SIGCHLD arrives. 155 */ 156 (void) sleep(10); 157 158 /* 159 * Grab the descriptors and credentials passed to us. 160 */ 161 (void) memset(&msg, 0, sizeof(msg)); 162 msg.msg_control = (caddr_t) &message; 163 msg.msg_controllen = sizeof(message); 164 165 if (recvmsg(sock, &msg, 0) < 0) 166 err(1, "recvmsg"); 167 168 (void) close(sock); 169 170 if (msg.msg_controllen == 0) 171 errx(1, "no control messages received"); 172 173 if (msg.msg_flags & MSG_CTRUNC) 174 errx(1, "lost control message data"); 175 176 cmp = CMSG_FIRSTHDR(&msg); 177 for (cmp = CMSG_FIRSTHDR(&msg); cmp != NULL; 178 cmp = CMSG_NXTHDR(&msg, cmp)) { 179 if (cmp->cmsg_level != SOL_SOCKET) 180 errx(1, "bad control message level %d", 181 cmp->cmsg_level); 182 183 switch (cmp->cmsg_type) { 184 case SCM_RIGHTS: 185 if (cmp->cmsg_len != sizeof(message.fdcm)) 186 errx(1, "bad fd control message length"); 187 188 files = (int *)CMSG_DATA(cmp); 189 break; 190 191 case SCM_CREDS: 192 if (cmp->cmsg_len < sizeof(struct sockcred)) 193 errx(1, "bad cred control message length"); 194 195 sc = (struct sockcred *)CMSG_DATA(cmp); 196 break; 197 198 default: 199 errx(1, "unexpected control message"); 200 /* NOTREACHED */ 201 } 202 } 203 204 /* 205 * Read the files and print their contents. 206 */ 207 if (files == NULL) 208 warnx("didn't get fd control message"); 209 else { 210 for (i = 0; i < 2; i++) { 211 (void) memset(buf, 0, sizeof(buf)); 212 if (read(files[i], buf, sizeof(buf)) <= 0) 213 err(1, "read file %d", i + 1); 214 printf("%s", buf); 215 } 216 } 217 218 /* 219 * Double-check credentials. 220 */ 221 if (sc == NULL) 222 warnx("didn't get cred control message"); 223 else { 224 if (sc->sc_uid == getuid() && 225 sc->sc_euid == geteuid() && 226 sc->sc_gid == getgid() && 227 sc->sc_egid == getegid()) 228 printf("Credentials match.\n"); 229 else 230 printf("Credentials do NOT match.\n"); 231 } 232 233 /* 234 * All done! 235 */ 236 exit(0); 237 } 238 239 void 240 catch_sigchld(sig) 241 int sig; 242 { 243 int status; 244 245 (void) wait(&status); 246 } 247 248 void 249 child() 250 { 251 struct msghdr msg; 252 char fname[16], buf[64]; 253 struct cmsghdr *cmp; 254 struct fdcmessage fdcm; 255 int i, fd, sock; 256 struct sockaddr_un sun; 257 258 /* 259 * Create socket and connect to the receiver. 260 */ 261 if ((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1) 262 errx(1, "child socket"); 263 264 (void) memset(&sun, 0, sizeof(sun)); 265 sun.sun_family = AF_LOCAL; 266 (void) strcpy(sun.sun_path, SOCK_NAME); 267 sun.sun_len = SUN_LEN(&sun); 268 269 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 270 err(1, "child connect"); 271 272 /* 273 * Open the files again, and pass them to the child over the socket. 274 */ 275 for (i = 0; i < 2; i++) { 276 (void) sprintf(fname, "file%d", i + 1); 277 if ((fd = open(fname, O_RDONLY, 0666)) == -1) 278 err(1, "child open %s", fname); 279 fdcm.files[i] = fd; 280 } 281 282 (void) memset(&msg, 0, sizeof(msg)); 283 msg.msg_control = (caddr_t) &fdcm; 284 msg.msg_controllen = sizeof(fdcm); 285 286 cmp = CMSG_FIRSTHDR(&msg); 287 cmp->cmsg_len = sizeof(fdcm); 288 cmp->cmsg_level = SOL_SOCKET; 289 cmp->cmsg_type = SCM_RIGHTS; 290 291 if (sendmsg(sock, &msg, 0)) 292 err(1, "child sendmsg"); 293 294 /* 295 * All done! 296 */ 297 exit(0); 298 } 299