1 /* $OpenBSD: sendrecvfd.c,v 1.1 2017/02/22 11:30:00 tb Exp $ */ 2 /* 3 * Copyright (c) 2017 Sebastien Marie <semarie@online.fr> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/socket.h> 20 #include <sys/stat.h> 21 #include <sys/wait.h> 22 23 #include <err.h> 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 enum testtype { 32 nopledge, 33 sendfd, 34 recvfd, 35 nosendfd, 36 norecvfd, 37 }; 38 39 static void do_receiver(enum testtype type, int sock); 40 static void do_sender(enum testtype type, int sock, int fd); 41 __dead static void usage(); 42 43 44 static void 45 do_receiver(enum testtype type, int sock) 46 { 47 struct msghdr msg; 48 struct cmsghdr *cmsg; 49 union { 50 struct cmsghdr hdr; 51 unsigned char buf[CMSG_SPACE(sizeof(int))]; 52 } cmsgbuf; 53 54 /* pledge */ 55 switch(type) { 56 case recvfd: 57 if (pledge("stdio recvfd", NULL) == -1) 58 err(EXIT_FAILURE, "receiver: pledge"); 59 break; 60 61 case norecvfd: 62 if (pledge("stdio", NULL) == -1) 63 err(EXIT_FAILURE, "receiver: pledge"); 64 break; 65 66 default: 67 /* no pledge */ 68 break; 69 } 70 71 memset(&msg, 0, sizeof(msg)); 72 msg.msg_control = &cmsgbuf.buf; 73 msg.msg_controllen = sizeof(cmsgbuf.buf); 74 75 if (recvmsg(sock, &msg, 0) == -1) 76 err(EXIT_FAILURE, "receiver: recvmsg"); 77 78 if ((msg.msg_flags & MSG_TRUNC) || (msg.msg_flags & MSG_CTRUNC)) 79 errx(EXIT_FAILURE, "receiver: control message truncated"); 80 81 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 82 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 83 if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) && 84 cmsg->cmsg_level == SOL_SOCKET && 85 cmsg->cmsg_type == SCM_RIGHTS) { 86 87 int fd = *(int *)CMSG_DATA(cmsg); 88 struct stat sb; 89 90 /* test received fd */ 91 if (fstat(fd, &sb) == -1) 92 err(EXIT_FAILURE, "receiver: fstat"); 93 } 94 } 95 } 96 97 static void 98 do_sender(enum testtype type, int sock, int fd) 99 { 100 struct msghdr msg; 101 struct cmsghdr *cmsg; 102 union { 103 struct cmsghdr hdr; 104 unsigned char buf[CMSG_SPACE(sizeof(int))]; 105 } cmsgbuf; 106 107 /* pledge */ 108 switch (type) { 109 case sendfd: 110 if (pledge("stdio sendfd", NULL) == -1) 111 err(EXIT_FAILURE, "sender: pledge"); 112 break; 113 114 case nosendfd: 115 if (pledge("stdio", NULL) == -1) 116 err(EXIT_FAILURE, "sender: pledge"); 117 break; 118 119 default: 120 /* no pledge */ 121 break; 122 } 123 124 memset(&msg, 0, sizeof(msg)); 125 msg.msg_control = &cmsgbuf.buf; 126 msg.msg_controllen = sizeof(cmsgbuf.buf); 127 128 cmsg = CMSG_FIRSTHDR(&msg); 129 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 130 cmsg->cmsg_level = SOL_SOCKET; 131 cmsg->cmsg_type = SCM_RIGHTS; 132 *(int *)CMSG_DATA(cmsg) = fd; 133 134 if (sendmsg(sock, &msg, 0) == -1) 135 err(EXIT_FAILURE, "sender: sendmsg"); 136 } 137 138 __dead static void 139 usage() 140 { 141 printf("usage: %s testtype vnodetype\n", getprogname()); 142 printf(" testtype = nopledge sendfd recvfd nosendfd norecvfd\n"); 143 printf(" vnodetype = VREG VDIR VBLK VCHAR VLNK VSOCK VFIFO\n"); 144 exit(EXIT_FAILURE); 145 } 146 147 int 148 main(int argc, char *argv[]) 149 { 150 enum testtype type; 151 int fd; 152 int sv[2], status; 153 pid_t child; 154 155 /* 156 * parse arguments 157 */ 158 159 if (argc != 3) 160 usage(); 161 162 if (strcmp(argv[1], "nopledge") == 0 ) { 163 /* test sendfd/recvfd without pledge */ 164 type = nopledge; 165 166 } else if (strcmp(argv[1], "sendfd") == 0) { 167 /* test sendfd process with "stdio sendfd" */ 168 type = sendfd; 169 170 } else if (strcmp(argv[1], "recvfd") == 0) { 171 /* test recvfd process with "stdio recvfd" */ 172 type = recvfd; 173 174 } else if (strcmp(argv[1], "nosendfd") == 0) { 175 /* test sendfd process with "stdio" (without "sendfd") */ 176 type = nosendfd; 177 178 } else if (strcmp(argv[1], "norecvfd") == 0) { 179 /* test recvfd process with "stdio" (without "recvfd") */ 180 type = norecvfd; 181 182 } else 183 usage(); 184 185 /* open a file descriptor according to vnodetype requested */ 186 if (strcmp(argv[2], "VREG") == 0) { 187 if ((fd = open("/etc/passwd", O_RDONLY)) == -1) 188 err(EXIT_FAILURE, "open: VREG: /etc/passwd"); 189 190 } else if (strcmp(argv[2], "VDIR") == 0) { 191 if ((fd = open("/dev", O_RDONLY)) == -1) 192 err(EXIT_FAILURE, "open: VDIR: /dev"); 193 194 } else if (strcmp(argv[2], "VBLK") == 0) { 195 if ((fd = open("/dev/vnd0c", O_RDONLY)) == -1) 196 err(EXIT_FAILURE, "open: VBLK: /dev/vnd0c"); 197 198 } else if (strcmp(argv[2], "VCHR") == 0) { 199 if ((fd = open("/dev/null", O_RDONLY)) == -1) 200 err(EXIT_FAILURE, "open: VCHR: /dev/null"); 201 202 } else if (strcmp(argv[2], "VLNK") == 0) { 203 if ((fd = open("/etc/termcap", O_RDONLY)) == -1) 204 err(EXIT_FAILURE, "open: VCHR: /etc/termcap"); 205 206 } else if (strcmp(argv[2], "VSOCK") == 0) { 207 /* create socket */ 208 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 209 err(EXIT_FAILURE, "socket: VSOCK"); 210 211 } else if (strcmp(argv[2], "VFIFO") == 0) { 212 /* unlink possibly existing file (from previous run) */ 213 unlink("fifo"); 214 215 /* create a new named fifo */ 216 if (mkfifo("fifo", 0600) == -1) 217 err(EXIT_FAILURE, "mkfifo: VFIFO"); 218 219 /* open it */ 220 if ((fd = open("fifo", O_RDONLY|O_NONBLOCK)) == -1) 221 err(EXIT_FAILURE, "open: VFIFO: fifo"); 222 223 /* unlink the file now */ 224 unlink("fifo"); 225 } else 226 usage(); 227 228 229 /* 230 * do test 231 */ 232 233 /* communication socket */ 234 if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, sv) == -1) 235 err(EXIT_FAILURE, "socketpair"); 236 237 /* create two procs and pass fd from one to another */ 238 switch (child = fork()) { 239 case -1: /* error */ 240 err(EXIT_FAILURE, "fork"); 241 242 case 0: /* child: receiver */ 243 close(fd); 244 close(sv[0]); 245 246 do_receiver(type, sv[1]); 247 _exit(EXIT_SUCCESS); 248 249 default: /* parent: sender */ 250 close(sv[1]); 251 252 do_sender(type, sv[0], fd); 253 254 /* wait for child */ 255 while (waitpid(child, &status, 0) < 0) { 256 if (errno == EAGAIN) 257 continue; 258 259 err(EXIT_FAILURE, "waitpid"); 260 } 261 262 if (! WIFEXITED(status)) { 263 if (WIFSIGNALED(status)) 264 errx(EXIT_FAILURE, 265 "child (receiver): WTERMSIG(): %d", 266 WTERMSIG(status)); 267 268 errx(EXIT_FAILURE, "child (receiver): !WIFEXITED"); 269 } 270 271 if (WEXITSTATUS(status) != 0) 272 errx(EXIT_FAILURE, "child(receiver): WEXITSTATUS(): %d", 273 WEXITSTATUS(status)); 274 275 exit(EXIT_SUCCESS); 276 } 277 } 278