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
do_receiver(enum testtype type,int sock)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
do_sender(enum testtype type,int sock,int fd)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
usage()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
main(int argc,char * argv[])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