1/*-
2 * Copyright (c) 2005 Andrey Simonenko
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <sys/un.h>
33#include <inttypes.h>
34#include <stdarg.h>
35#include <stdbool.h>
36#include <stdlib.h>
37
38#include "uc_common.h"
39#include "t_generic.h"
40#include "t_sockcred.h"
41
42static int
43t_sockcred_client(int type, int fd)
44{
45	struct msghdr msghdr;
46	struct iovec iov[1];
47	int rv;
48
49	if (uc_sync_recv() < 0)
50		return (-2);
51
52	rv = -2;
53
54	uc_msghdr_init_client(&msghdr, iov, NULL, 0, 0, 0);
55
56	if (uc_socket_connect(fd) < 0)
57		goto done;
58
59	if (type == 2)
60		if (uc_sync_recv() < 0)
61			goto done;
62
63	if (uc_message_sendn(fd, &msghdr) < 0)
64		goto done;
65
66	rv = 0;
67done:
68	return (rv);
69}
70
71static int
72t_sockcred_server(int type, int fd1)
73{
74	struct msghdr msghdr;
75	struct iovec iov[1];
76	struct cmsghdr *cmsghdr;
77	void *cmsg_data;
78	size_t cmsg_size;
79	u_int i;
80	int fd2, rv, val;
81
82	fd2 = -1;
83	rv = -2;
84
85	cmsg_size = CMSG_SPACE(SOCKCREDSIZE(uc_cfg.proc_cred.gid_num));
86	cmsg_data = malloc(cmsg_size);
87	if (cmsg_data == NULL) {
88		uc_logmsg("malloc");
89		goto done;
90	}
91
92	if (type == 1) {
93		uc_dbgmsg("setting LOCAL_CREDS");
94		val = 1;
95		if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
96			uc_logmsg("setsockopt(LOCAL_CREDS)");
97			goto done;
98		}
99	}
100
101	if (uc_sync_send() < 0)
102		goto done;
103
104	if (uc_cfg.sock_type == SOCK_STREAM) {
105		fd2 = uc_socket_accept(fd1);
106		if (fd2 < 0)
107			goto done;
108	} else
109		fd2 = fd1;
110
111	if (type == 2) {
112		uc_dbgmsg("setting LOCAL_CREDS");
113		val = 1;
114		if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
115			uc_logmsg("setsockopt(LOCAL_CREDS)");
116			goto done;
117		}
118		if (uc_sync_send() < 0)
119			goto done;
120	}
121
122	rv = -1;
123	for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) {
124		uc_dbgmsg("message #%u", i);
125
126		uc_msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
127		if (uc_message_recv(fd2, &msghdr) < 0) {
128			rv = -2;
129			break;
130		}
131
132		if (i > 1 && uc_cfg.sock_type == SOCK_STREAM) {
133			if (uc_check_msghdr(&msghdr, 0) < 0)
134				break;
135		} else {
136			if (uc_check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
137				break;
138
139			cmsghdr = CMSG_FIRSTHDR(&msghdr);
140			if (uc_check_scm_creds_sockcred(cmsghdr) < 0)
141				break;
142		}
143	}
144	if (i > uc_cfg.ipc_msg.msg_num)
145		rv = 0;
146done:
147	free(cmsg_data);
148	if (uc_cfg.sock_type == SOCK_STREAM && fd2 >= 0)
149		if (uc_socket_close(fd2) < 0)
150			rv = -2;
151	return (rv);
152}
153
154int
155t_sockcred_1(void)
156{
157	u_int i;
158	int fd, rv, rv_client;
159
160	switch (uc_client_fork()) {
161	case 0:
162		for (i = 1; i <= 2; ++i) {
163			uc_dbgmsg("client #%u", i);
164			fd = uc_socket_create();
165			if (fd < 0)
166				rv = -2;
167			else {
168				rv = t_sockcred_client(1, fd);
169				if (uc_socket_close(fd) < 0)
170					rv = -2;
171			}
172			if (rv != 0)
173				break;
174		}
175		uc_client_exit(rv);
176		break;
177	case 1:
178		fd = uc_socket_create();
179		if (fd < 0)
180			rv = -2;
181		else {
182			rv = t_sockcred_server(1, fd);
183			if (rv == 0)
184				rv = t_sockcred_server(3, fd);
185			rv_client = uc_client_wait();
186			if (rv == 0 || (rv == -2 && rv_client != 0))
187				rv = rv_client;
188			if (uc_socket_close(fd) < 0)
189				rv = -2;
190		}
191		break;
192	default:
193		rv = -2;
194	}
195
196	return (rv);
197}
198
199static int
200t_sockcred_2_client(int fd)
201{
202	return (t_sockcred_client(2, fd));
203}
204
205static int
206t_sockcred_2_server(int fd)
207{
208	return (t_sockcred_server(2, fd));
209}
210
211int
212t_sockcred_2(void)
213{
214	return (t_generic(t_sockcred_2_client, t_sockcred_2_server));
215}
216