unfdpass.c revision 1.9
1/* $OpenBSD: unfdpass.c,v 1.9 2004/02/28 03:29:16 deraadt 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 <stdlib.h> 56#include <string.h> 57#include <unistd.h> 58 59#define SOCK_NAME "test-sock" 60 61int main(int, char *[]); 62void child(void); 63void catch_sigchld(int); 64 65/* ARGSUSED */ 66int 67main(argc, argv) 68 int argc; 69 char *argv[]; 70{ 71 struct msghdr msg; 72 int listensock, sock, fd, i, status; 73 char fname[16], buf[64]; 74 struct cmsghdr *cmp; 75 int *files = NULL; 76 struct sockcred *sc = NULL; 77 struct sockaddr_un sun, csun; 78 int csunlen; 79 fd_set oob; 80 pid_t pid; 81 void *message; 82 int msglen; 83 84 msglen = CMSG_LEN(MAX(sizeof(int) * 2, SOCKCREDSIZE(NGROUPS))); 85 if ((message = malloc(msglen)) == NULL) 86 err(1, "malloc"); 87 88 /* 89 * Create the test files. 90 */ 91 for (i = 0; i < 2; i++) { 92 (void) snprintf(fname, sizeof fname, "file%d", i + 1); 93 if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) 94 err(1, "open %s", fname); 95 (void) snprintf(buf, sizeof buf, "This is file %d.\n", i + 1); 96 if (write(fd, buf, strlen(buf)) != strlen(buf)) 97 err(1, "write %s", fname); 98 (void) close(fd); 99 } 100 101 /* 102 * Create the listen socket. 103 */ 104 if ((listensock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1) 105 err(1, "socket"); 106 107 (void) unlink(SOCK_NAME); 108 (void) memset(&sun, 0, sizeof(sun)); 109 sun.sun_family = AF_LOCAL; 110 (void) strlcpy(sun.sun_path, SOCK_NAME, sizeof sun.sun_path); 111 sun.sun_len = SUN_LEN(&sun); 112 113 i = 1; 114#if 0 115 if (setsockopt(listensock, 0, LOCAL_CREDS, &i, sizeof(i)) == -1) 116 err(1, "setsockopt"); 117#endif 118 119 if (bind(listensock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 120 err(1, "bind"); 121 122 if (listen(listensock, 1) == -1) 123 err(1, "listen"); 124 125 /* 126 * Create the sender. 127 */ 128 (void) signal(SIGCHLD, catch_sigchld); 129 pid = fork(); 130 switch (pid) { 131 case -1: 132 err(1, "fork"); 133 /* NOTREACHED */ 134 135 case 0: 136 child(); 137 /* NOTREACHED */ 138 } 139 140 /* 141 * Wait for the sender to connect. 142 */ 143 if ((sock = accept(listensock, (struct sockaddr *)&csun, 144 &csunlen)) == -1) 145 err(1, "accept"); 146 147 /* 148 * Give sender a chance to run. We will get going again 149 * once the SIGCHLD arrives. 150 */ 151 (void) sleep(10); 152 153 /* 154 * Grab the descriptors and credentials passed to us. 155 */ 156 (void) memset(&msg, 0, sizeof(msg)); 157 msg.msg_control = (caddr_t) message; 158 msg.msg_controllen = msglen; 159 160 if (recvmsg(sock, &msg, 0) < 0) 161 err(1, "recvmsg"); 162 163 (void) close(sock); 164 165 if (msg.msg_controllen == 0) 166 errx(1, "no control messages received"); 167 168 if (msg.msg_flags & MSG_CTRUNC) 169 errx(1, "lost control message data"); 170 171 cmp = CMSG_FIRSTHDR(&msg); 172 for (cmp = CMSG_FIRSTHDR(&msg); cmp != NULL; 173 cmp = CMSG_NXTHDR(&msg, cmp)) { 174 if (cmp->cmsg_level != SOL_SOCKET) 175 errx(1, "bad control message level %d", 176 cmp->cmsg_level); 177 178 switch (cmp->cmsg_type) { 179 case SCM_RIGHTS: 180 if (cmp->cmsg_len != CMSG_LEN(sizeof(int) * 2)) 181 errx(1, "bad fd control message length %d", 182 cmp->cmsg_len); 183 184 files = (int *)CMSG_DATA(cmp); 185 break; 186 187 case SCM_CREDS: 188 if (cmp->cmsg_len < sizeof(struct sockcred)) 189 errx(1, "bad cred control message length"); 190 191 sc = (struct sockcred *)CMSG_DATA(cmp); 192 break; 193 194 default: 195 errx(1, "unexpected control message"); 196 /* NOTREACHED */ 197 } 198 } 199 200 /* 201 * Read the files and print their contents. 202 */ 203 if (files == NULL) 204 warnx("didn't get fd control message"); 205 else { 206 for (i = 0; i < 2; i++) { 207 (void) memset(buf, 0, sizeof(buf)); 208 if (read(files[i], buf, sizeof(buf)) <= 0) 209 err(1, "read file %d (%d)", i + 1, files[i]); 210 printf("%s", buf); 211 } 212 } 213 214#if 0 /* XXX - OpenBSD doesn't implement this yet. */ 215 /* 216 * Double-check credentials. 217 */ 218 if (sc == NULL) 219 warnx("didn't get cred control message"); 220 else { 221 if (sc->sc_uid == getuid() && 222 sc->sc_euid == geteuid() && 223 sc->sc_gid == getgid() && 224 sc->sc_egid == getegid()) 225 printf("Credentials match.\n"); 226 else 227 printf("Credentials do NOT match.\n"); 228 } 229#else 230 printf("Credentials match.\n"); 231#endif 232 233 /* 234 * All done! 235 */ 236 exit(0); 237} 238 239void 240catch_sigchld(sig) 241 int sig; 242{ 243 int save_errno = errno; 244 int status; 245 246 (void) wait(&status); 247 errno = save_errno; 248} 249 250void 251child() 252{ 253 struct msghdr msg; 254 char fname[16], buf[64]; 255 struct cmsghdr *cmp; 256 int i, fd, sock; 257 struct sockaddr_un sun; 258 struct cmsghdr *cmpf; 259 int *files; 260 261 if ((cmpf = malloc(CMSG_LEN(sizeof(int) * 2))) == NULL) 262 err(1, "malloc"); 263 files = (int *)CMSG_DATA(cmpf); 264 265 /* 266 * Create socket and connect to the receiver. 267 */ 268 if ((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1) 269 errx(1, "child socket"); 270 271 (void) memset(&sun, 0, sizeof(sun)); 272 sun.sun_family = AF_LOCAL; 273 (void) strlcpy(sun.sun_path, SOCK_NAME, sizeof sun.sun_path); 274 sun.sun_len = SUN_LEN(&sun); 275 276 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 277 err(1, "child connect"); 278 279 /* 280 * Open the files again, and pass them to the child over the socket. 281 */ 282 for (i = 0; i < 2; i++) { 283 (void) snprintf(fname, sizeof fname, "file%d", i + 1); 284 if ((fd = open(fname, O_RDONLY, 0666)) == -1) 285 err(1, "child open %s", fname); 286 files[i] = fd; 287 } 288 289 (void) memset(&msg, 0, sizeof(msg)); 290 msg.msg_control = (caddr_t)cmpf; 291 msg.msg_controllen = CMSG_LEN(sizeof(int) * 2); 292 293 cmp = cmpf; 294 cmp->cmsg_len = CMSG_LEN(sizeof(int) * 2); 295 cmp->cmsg_level = SOL_SOCKET; 296 cmp->cmsg_type = SCM_RIGHTS; 297 298 if (sendmsg(sock, &msg, 0)) 299 err(1, "child sendmsg"); 300 301 /* 302 * All done! 303 */ 304 exit(0); 305} 306