initial commit
[glibc.git] / sysdeps / mach / hurd / recvmsg.c
1 /* Copyright (C) 2001-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If
16 not, see <https://www.gnu.org/licenses/>. */
17
18 #include <errno.h>
19 #include <string.h>
20 #include <sys/socket.h>
21
22 #include <hurd.h>
23 #include <hurd/fd.h>
24 #include <hurd/socket.h>
25 #include <sysdep-cancel.h>
26
27 static unsigned
28 contains_uid (unsigned int n, __uid_t uids[n], __uid_t uid)
29 {
30 unsigned i;
31
32 for (i = 0; i < n; i++)
33 if (uids[i] == uid)
34 return 1;
35 return 0;
36 }
37
38 static unsigned
39 contains_gid (unsigned int n, __gid_t gids[n], __gid_t gid)
40 {
41 unsigned i;
42
43 for (i = 0; i < n; i++)
44 if (gids[i] == gid)
45 return 1;
46 return 0;
47 }
48
49 /* Check the passed credentials. */
50 static error_t
51 check_auth (mach_port_t rendezvous,
52 __pid_t pid,
53 __uid_t uid, __uid_t euid,
54 __gid_t gid,
55 int ngroups, __gid_t groups[ngroups])
56 {
57 error_t err;
58 size_t neuids = CMGROUP_MAX, nauids = CMGROUP_MAX;
59 size_t negids = CMGROUP_MAX, nagids = CMGROUP_MAX;
60 __uid_t euids_buf[neuids], auids_buf[nauids];
61 __gid_t egids_buf[negids], agids_buf[nagids];
62 __uid_t *euids = euids_buf, *auids = auids_buf;
63 __gid_t *egids = egids_buf, *agids = agids_buf;
64
65 struct procinfo *pi = NULL;
66 mach_msg_type_number_t pi_size = 0;
67 int flags = PI_FETCH_TASKINFO;
68 char *tw = NULL;
69 size_t tw_size = 0;
70 unsigned i;
71
72 err = __mach_port_mod_refs (mach_task_self (), rendezvous,
73 MACH_PORT_RIGHT_SEND, 1);
74 if (err)
75 goto out;
76
77 do
78 err = __USEPORT
79 (AUTH, __auth_server_authenticate (port,
80 rendezvous, MACH_MSG_TYPE_COPY_SEND,
81 MACH_PORT_NULL, 0,
82 &euids, &neuids, &auids, &nauids,
83 &egids, &negids, &agids, &nagids));
84 while (err == EINTR);
85 if (err)
86 goto out;
87
88 /* Check whether this process indeed has these IDs */
89 if ( !contains_uid (neuids, euids, uid)
90 && !contains_uid (nauids, auids, uid)
91 || !contains_uid (neuids, euids, euid)
92 && !contains_uid (nauids, auids, euid)
93 || !contains_gid (negids, egids, gid)
94 && !contains_gid (nagids, agids, gid)
95 )
96 {
97 err = EIO;
98 goto out;
99 }
100
101 /* Check groups */
102 for (i = 0; i < ngroups; i++)
103 if ( !contains_gid (negids, egids, groups[i])
104 && !contains_gid (nagids, agids, groups[i]))
105 {
106 err = EIO;
107 goto out;
108 }
109
110 /* Check PID */
111 /* XXX: Using proc_getprocinfo until
112 proc_user_authenticate proc_server_authenticate is implemented
113 */
114 /* Get procinfo to check the owner. Maybe he faked the pid, but at least we
115 check the owner. */
116 err = __USEPORT (PROC, __proc_getprocinfo (port, pid, &flags,
117 (procinfo_t *)&pi,
118 &pi_size, &tw, &tw_size));
119 if (err)
120 goto out;
121
122 if ( !contains_uid (neuids, euids, pi->owner)
123 && !contains_uid (nauids, auids, pi->owner))
124 err = EIO;
125
126 out:
127 __mach_port_deallocate (__mach_task_self (), rendezvous);
128 if (euids != euids_buf)
129 __vm_deallocate (__mach_task_self(), (vm_address_t) euids, neuids * sizeof(uid_t));
130 if (auids != auids_buf)
131 __vm_deallocate (__mach_task_self(), (vm_address_t) auids, nauids * sizeof(uid_t));
132 if (egids != egids_buf)
133 __vm_deallocate (__mach_task_self(), (vm_address_t) egids, negids * sizeof(uid_t));
134 if (agids != agids_buf)
135 __vm_deallocate (__mach_task_self(), (vm_address_t) agids, nagids * sizeof(uid_t));
136 if (tw_size)
137 __vm_deallocate (__mach_task_self(), (vm_address_t) tw, tw_size);
138 if (pi_size)
139 __vm_deallocate (__mach_task_self(), (vm_address_t) pi, pi_size);
140
141 return err;
142 }
143
144 /* Receive a message as described by MESSAGE from socket FD.
145 Returns the number of bytes read or -1 for errors. */
146 ssize_t
147 __libc_recvmsg (int fd, struct msghdr *message, int flags)
148 {
149 error_t err;
150 addr_port_t aport;
151 char *data = NULL;
152 mach_msg_type_number_t len = 0;
153 mach_port_t *ports, *newports = NULL;
154 mach_msg_type_number_t nports = 0;
155 struct cmsghdr *cmsg;
156 char *cdata = NULL;
157 mach_msg_type_number_t clen = 0;
158 size_t amount;
159 char *buf;
160 int nfds, *opened_fds = NULL;
161 int i, ii, j;
162 int newfds;
163 int cancel_oldtype;
164
165 error_t reauthenticate (mach_port_t port, mach_port_t *result)
166 {
167 error_t err;
168 mach_port_t ref;
169 ref = __mach_reply_port ();
170 int cancel_oldtype;
171
172 cancel_oldtype = LIBC_CANCEL_ASYNC();
173 do
174 err = __io_reauthenticate (port, ref, MACH_MSG_TYPE_MAKE_SEND);
175 while (err == EINTR);
176 if (!err)
177 do
178 err = __USEPORT_CANCEL (AUTH, __auth_user_authenticate (port,
179 ref, MACH_MSG_TYPE_MAKE_SEND,
180 result));
181 while (err == EINTR);
182 LIBC_CANCEL_RESET (cancel_oldtype);
183
184 __mach_port_destroy (__mach_task_self (), ref);
185 return err;
186 }
187
188 /* Find the total number of bytes to be read. */
189 amount = 0;
190 for (i = 0; i < message->msg_iovlen; i++)
191 {
192 amount += message->msg_iov[i].iov_len;
193
194 /* As an optimization, we set the initial values of DATA and LEN
195 from the first non-empty iovec. This kicks-in in the case
196 where the whole packet fits into that iovec buffer. */
197 if (data == NULL && message->msg_iov[i].iov_len > 0)
198 {
199 data = message->msg_iov[i].iov_base;
200 len = message->msg_iov[i].iov_len;
201 }
202 }
203
204 buf = data;
205 cancel_oldtype = LIBC_CANCEL_ASYNC();
206 err = HURD_DPORT_USE_CANCEL (fd, __socket_recv (port, &aport,
207 flags, &data, &len,
208 &ports, &nports,
209 &cdata, &clen,
210 &message->msg_flags, amount));
211 LIBC_CANCEL_RESET (cancel_oldtype);
212 if (err)
213 return __hurd_sockfail (fd, flags, err);
214
215 if (message->msg_name != NULL && aport != MACH_PORT_NULL)
216 {
217 char *buf = message->msg_name;
218 mach_msg_type_number_t buflen = message->msg_namelen;
219 int type;
220
221 cancel_oldtype = LIBC_CANCEL_ASYNC();
222 err = __socket_whatis_address (aport, &type, &buf, &buflen);
223 LIBC_CANCEL_RESET (cancel_oldtype);
224
225 if (err == EOPNOTSUPP)
226 /* If the protocol server can't tell us the address, just return a
227 zero-length one. */
228 {
229 buf = message->msg_name;
230 buflen = 0;
231 err = 0;
232 }
233
234 if (err)
235 {
236 __mach_port_deallocate (__mach_task_self (), aport);
237 return __hurd_sockfail (fd, flags, err);
238 }
239
240 if (message->msg_namelen > buflen)
241 message->msg_namelen = buflen;
242
243 if (buf != message->msg_name)
244 {
245 memcpy (message->msg_name, buf, message->msg_namelen);
246 __vm_deallocate (__mach_task_self (), (vm_address_t) buf, buflen);
247 }
248
249 if (buflen > 0)
250 ((struct sockaddr *) message->msg_name)->sa_family = type;
251 }
252 else if (message->msg_name != NULL)
253 message->msg_namelen = 0;
254
255 __mach_port_deallocate (__mach_task_self (), aport);
256
257 if (buf == data)
258 buf += len;
259 else
260 {
261 /* Copy the data into MSG. */
262 if (len > amount)
263 message->msg_flags |= MSG_TRUNC;
264 else
265 amount = len;
266
267 buf = data;
268 for (i = 0; i < message->msg_iovlen; i++)
269 {
270 #define min(a, b) ((a) > (b) ? (b) : (a))
271 size_t copy = min (message->msg_iov[i].iov_len, amount);
272
273 memcpy (message->msg_iov[i].iov_base, buf, copy);
274
275 buf += copy;
276 amount -= copy;
277 if (len == 0)
278 break;
279 }
280
281 __vm_deallocate (__mach_task_self (), (vm_address_t) data, len);
282 }
283
284 /* Copy the control message into MSG. */
285 if (clen > message->msg_controllen)
286 message->msg_flags |= MSG_CTRUNC;
287 else
288 message->msg_controllen = clen;
289 memcpy (message->msg_control, cdata, message->msg_controllen);
290
291 if (nports > 0)
292 {
293 newports = __alloca (nports * sizeof (mach_port_t));
294 opened_fds = __alloca (nports * sizeof (int));
295 }
296
297 /* This counts how many ports we processed completely. */
298 i = 0;
299 /* This counts how many new fds we create. */
300 newfds = 0;
301
302 for (cmsg = CMSG_FIRSTHDR (message);
303 cmsg;
304 cmsg = CMSG_NXTHDR (message, cmsg))
305 {
306 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
307 {
308 /* SCM_RIGHTS support. */
309 /* The fd's flags are passed in the control data. */
310 int *fds = (int *) CMSG_DATA (cmsg);
311 nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
312 / sizeof (int);
313
314 for (j = 0; j < nfds; j++)
315 {
316 err = reauthenticate (ports[i], &newports[newfds]);
317 if (err)
318 goto cleanup;
319 fds[j] = opened_fds[newfds] = _hurd_intern_fd (newports[newfds],
320 fds[j], 0);
321 if (fds[j] == -1)
322 {
323 err = errno;
324 __mach_port_deallocate (__mach_task_self (), newports[newfds]);
325 goto cleanup;
326 }
327 i++;
328 newfds++;
329 }
330 }
331 else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS)
332 {
333 /* SCM_CREDS support. */
334 /* Check received credentials */
335 struct cmsgcred *ucredp = (struct cmsgcred *) CMSG_DATA(cmsg);
336
337 err = check_auth (ports[i],
338 ucredp->cmcred_pid,
339 ucredp->cmcred_uid, ucredp->cmcred_euid,
340 ucredp->cmcred_gid,
341 ucredp->cmcred_ngroups, ucredp->cmcred_groups);
342 if (err)
343 goto cleanup;
344 i++;
345 }
346 }
347
348 for (i = 0; i < nports; i++)
349 __mach_port_deallocate (mach_task_self (), ports[i]);
350
351 __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen);
352
353 return (buf - data);
354
355 cleanup:
356 /* Clean up all the file descriptors from port 0 to i-1. */
357 if (nports > 0)
358 {
359 ii = 0;
360 newfds = 0;
361 for (cmsg = CMSG_FIRSTHDR (message);
362 cmsg;
363 cmsg = CMSG_NXTHDR (message, cmsg))
364 {
365 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
366 {
367 nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
368 / sizeof (int);
369 for (j = 0; j < nfds && ii < i; j++, ii++, newfds++)
370 {
371 _hurd_fd_close (_hurd_fd_get (opened_fds[newfds]));
372 __mach_port_deallocate (__mach_task_self (), newports[newfds]);
373 __mach_port_deallocate (__mach_task_self (), ports[ii]);
374 }
375 }
376 else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS)
377 {
378 __mach_port_deallocate (__mach_task_self (), ports[ii]);
379 ii++;
380 }
381 }
382 }
383
384 __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen);
385 return __hurd_fail (err);
386 }
387
388 weak_alias (__libc_recvmsg, recvmsg)
389 weak_alias (__libc_recvmsg, __recvmsg)