]>
git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/af_unix.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
13 #include <sys/socket.h>
14 #include <sys/syscall.h>
21 #include "memory_utils.h"
22 #include "process_utils.h"
26 #include "include/strlcpy.h"
29 lxc_log_define(af_unix
, lxc
);
31 static ssize_t
lxc_abstract_unix_set_sockaddr(struct sockaddr_un
*addr
,
37 return ret_errno(EINVAL
);
39 /* Clear address structure */
40 memset(addr
, 0, sizeof(*addr
));
42 addr
->sun_family
= AF_UNIX
;
44 len
= strlen(&path
[1]);
46 /* do not enforce \0-termination */
47 if (len
>= INT_MAX
|| len
>= sizeof(addr
->sun_path
))
48 return ret_errno(ENAMETOOLONG
);
50 /* do not enforce \0-termination */
51 memcpy(&addr
->sun_path
[1], &path
[1], len
);
55 int lxc_abstract_unix_open(const char *path
, int type
, int flags
)
57 __do_close
int fd
= -EBADF
;
60 struct sockaddr_un addr
;
62 fd
= socket(PF_UNIX
, type
| SOCK_CLOEXEC
, 0);
69 len
= lxc_abstract_unix_set_sockaddr(&addr
, path
);
73 ret
= bind(fd
, (struct sockaddr
*)&addr
,
74 offsetof(struct sockaddr_un
, sun_path
) + len
+ 1);
78 if (type
== SOCK_STREAM
) {
79 ret
= listen(fd
, 100);
87 void lxc_abstract_unix_close(int fd
)
92 int lxc_abstract_unix_connect(const char *path
)
94 __do_close
int fd
= -EBADF
;
97 struct sockaddr_un addr
;
99 fd
= socket(PF_UNIX
, SOCK_STREAM
| SOCK_CLOEXEC
, 0);
103 len
= lxc_abstract_unix_set_sockaddr(&addr
, path
);
107 ret
= connect(fd
, (struct sockaddr
*)&addr
,
108 offsetof(struct sockaddr_un
, sun_path
) + len
+ 1);
115 int lxc_abstract_unix_send_fds_iov(int fd
, const int *sendfds
, int num_sendfds
,
116 struct iovec
*const iov
, size_t iovlen
)
118 __do_free
char *cmsgbuf
= NULL
;
120 struct msghdr msg
= {};
121 struct cmsghdr
*cmsg
= NULL
;
122 size_t cmsgbufsize
= CMSG_SPACE(num_sendfds
* sizeof(int));
124 if (num_sendfds
<= 0)
125 return ret_errno(EINVAL
);
127 cmsgbuf
= malloc(cmsgbufsize
);
129 return ret_errno(-ENOMEM
);
131 msg
.msg_control
= cmsgbuf
;
132 msg
.msg_controllen
= cmsgbufsize
;
134 cmsg
= CMSG_FIRSTHDR(&msg
);
135 cmsg
->cmsg_level
= SOL_SOCKET
;
136 cmsg
->cmsg_type
= SCM_RIGHTS
;
137 cmsg
->cmsg_len
= CMSG_LEN(num_sendfds
* sizeof(int));
139 msg
.msg_controllen
= cmsg
->cmsg_len
;
141 memcpy(CMSG_DATA(cmsg
), sendfds
, num_sendfds
* sizeof(int));
144 msg
.msg_iovlen
= iovlen
;
147 ret
= sendmsg(fd
, &msg
, MSG_NOSIGNAL
);
148 } while (ret
< 0 && errno
== EINTR
);
153 int lxc_abstract_unix_send_fds(int fd
, const int *sendfds
, int num_sendfds
,
154 void *data
, size_t size
)
158 .iov_base
= data
? data
: buf
,
159 .iov_len
= data
? size
: sizeof(buf
),
161 return lxc_abstract_unix_send_fds_iov(fd
, sendfds
, num_sendfds
, &iov
, 1);
164 int lxc_unix_send_fds(int fd
, int *sendfds
, int num_sendfds
, void *data
,
167 return lxc_abstract_unix_send_fds(fd
, sendfds
, num_sendfds
, data
, size
);
170 int __lxc_abstract_unix_send_two_fds(int fd
, int fd_first
, int fd_second
,
171 void *data
, size_t size
)
177 return lxc_abstract_unix_send_fds(fd
, fd_send
, 2, data
, size
);
180 static ssize_t
lxc_abstract_unix_recv_fds_iov(int fd
,
181 struct unix_fds
*ret_fds
,
182 struct iovec
*ret_iov
,
185 __do_free
char *cmsgbuf
= NULL
;
187 struct msghdr msg
= {};
188 struct cmsghdr
*cmsg
= NULL
;
189 size_t cmsgbufsize
= CMSG_SPACE(sizeof(struct ucred
)) +
190 CMSG_SPACE(ret_fds
->fd_count_max
* sizeof(int));
192 if (ret_fds
->flags
& ~UNIX_FDS_ACCEPT_MASK
)
193 return ret_errno(EINVAL
);
195 if (hweight32((ret_fds
->flags
& ~UNIX_FDS_ACCEPT_NONE
)) > 1)
196 return ret_errno(EINVAL
);
198 if (ret_fds
->fd_count_max
>= KERNEL_SCM_MAX_FD
)
199 return ret_errno(EINVAL
);
201 if (ret_fds
->fd_count_ret
!= 0)
202 return ret_errno(EINVAL
);
204 cmsgbuf
= zalloc(cmsgbufsize
);
206 return ret_errno(ENOMEM
);
208 msg
.msg_control
= cmsgbuf
;
209 msg
.msg_controllen
= cmsgbufsize
;
211 msg
.msg_iov
= ret_iov
;
212 msg
.msg_iovlen
= size_ret_iov
;
215 ret
= recvmsg(fd
, &msg
, MSG_CMSG_CLOEXEC
);
220 return syserror("Failed to receive response");
225 /* If SO_PASSCRED is set we will always get a ucred message. */
226 for (cmsg
= CMSG_FIRSTHDR(&msg
); cmsg
; cmsg
= CMSG_NXTHDR(&msg
, cmsg
)) {
227 if (cmsg
->cmsg_level
== SOL_SOCKET
&& cmsg
->cmsg_type
== SCM_RIGHTS
) {
230 * This causes some compilers to complain about
231 * increased alignment requirements but I haven't found
232 * a better way to deal with this yet. Suggestions
235 #pragma GCC diagnostic push
236 #pragma GCC diagnostic ignored "-Wcast-align"
237 int *fds_raw
= (int *)CMSG_DATA(cmsg
);
238 #pragma GCC diagnostic pop
239 __u32 num_raw
= (cmsg
->cmsg_len
- CMSG_LEN(0)) / sizeof(int);
242 * We received an insane amount of file descriptors
243 * which exceeds the kernel limit we know about so
244 * close them and return an error.
246 if (num_raw
>= KERNEL_SCM_MAX_FD
) {
247 for (idx
= 0; idx
< num_raw
; idx
++)
250 return syserror_set(-EFBIG
, "Received excessive number of file descriptors");
253 if (msg
.msg_flags
& MSG_CTRUNC
) {
254 for (idx
= 0; idx
< num_raw
; idx
++)
257 return syserror_set(-EFBIG
, "Control message was truncated; closing all fds and rejecting incomplete message");
260 if (ret_fds
->fd_count_max
> num_raw
) {
261 if (!(ret_fds
->flags
& UNIX_FDS_ACCEPT_LESS
)) {
262 for (idx
= 0; idx
< num_raw
; idx
++)
265 return syserror_set(-EINVAL
, "Received fewer file descriptors than we expected %u != %u",
266 ret_fds
->fd_count_max
, num_raw
);
270 * Make sure any excess entries in the fd array
271 * are set to -EBADF so our cleanup functions
272 * can safely be called.
274 for (idx
= num_raw
; idx
< ret_fds
->fd_count_max
; idx
++)
275 ret_fds
->fd
[idx
] = -EBADF
;
277 ret_fds
->flags
|= UNIX_FDS_RECEIVED_LESS
;
278 } else if (ret_fds
->fd_count_max
< num_raw
) {
279 if (!(ret_fds
->flags
& UNIX_FDS_ACCEPT_MORE
)) {
280 for (idx
= 0; idx
< num_raw
; idx
++)
283 return syserror_set(-EINVAL
, "Received more file descriptors than we expected %u != %u",
284 ret_fds
->fd_count_max
, num_raw
);
287 /* Make sure we close any excess fds we received. */
288 for (idx
= ret_fds
->fd_count_max
; idx
< num_raw
; idx
++)
291 /* Cap the number of received file descriptors. */
292 num_raw
= ret_fds
->fd_count_max
;
293 ret_fds
->flags
|= UNIX_FDS_RECEIVED_MORE
;
295 ret_fds
->flags
|= UNIX_FDS_RECEIVED_EXACT
;
298 if (hweight32((ret_fds
->flags
& ~UNIX_FDS_ACCEPT_MASK
)) > 1) {
299 for (idx
= 0; idx
< num_raw
; idx
++)
302 return syserror_set(-EINVAL
, "Invalid flag combination; closing to not risk leaking fds %u != %u",
303 ret_fds
->fd_count_max
, num_raw
);
306 memcpy(ret_fds
->fd
, CMSG_DATA(cmsg
), num_raw
* sizeof(int));
307 ret_fds
->fd_count_ret
= num_raw
;
312 if (ret_fds
->fd_count_ret
== 0) {
313 ret_fds
->flags
|= UNIX_FDS_RECEIVED_NONE
;
315 /* We expected to receive file descriptors. */
316 if ((ret_fds
->flags
& UNIX_FDS_ACCEPT_MASK
) &&
317 !(ret_fds
->flags
& UNIX_FDS_ACCEPT_NONE
))
318 return syserror_set(-EINVAL
, "Received no file descriptors");
324 ssize_t
lxc_abstract_unix_recv_fds(int fd
, struct unix_fds
*ret_fds
,
325 void *ret_data
, size_t size_ret_data
)
329 .iov_base
= ret_data
? ret_data
: buf
,
330 .iov_len
= ret_data
? size_ret_data
: sizeof(buf
),
334 ret
= lxc_abstract_unix_recv_fds_iov(fd
, ret_fds
, &iov
, 1);
341 ssize_t
lxc_abstract_unix_recv_one_fd(int fd
, int *ret_fd
, void *ret_data
,
342 size_t size_ret_data
)
344 call_cleaner(put_unix_fds
) struct unix_fds
*fds
= NULL
;
347 .iov_base
= ret_data
? ret_data
: buf
,
348 .iov_len
= ret_data
? size_ret_data
: sizeof(buf
),
352 fds
= &(struct unix_fds
){
356 ret
= lxc_abstract_unix_recv_fds_iov(fd
, fds
, &iov
, 1);
361 return ret_errno(ENODATA
);
363 if (fds
->fd_count_ret
!= fds
->fd_count_max
)
366 *ret_fd
= move_fd(fds
->fd
[0]);
371 ssize_t
__lxc_abstract_unix_recv_two_fds(int fd
, int *fd_first
, int *fd_second
,
372 void *data
, size_t size
)
374 call_cleaner(put_unix_fds
) struct unix_fds
*fds
= NULL
;
377 .iov_base
= data
?: buf
,
378 .iov_len
= size
?: sizeof(buf
),
382 fds
= &(struct unix_fds
){
386 ret
= lxc_abstract_unix_recv_fds_iov(fd
, fds
, &iov
, 1);
391 return ret_errno(ENODATA
);
393 if (fds
->fd_count_ret
!= fds
->fd_count_max
) {
397 *fd_first
= move_fd(fds
->fd
[0]);
398 *fd_second
= move_fd(fds
->fd
[1]);
404 int lxc_abstract_unix_send_credential(int fd
, void *data
, size_t size
)
406 struct msghdr msg
= {0};
408 struct cmsghdr
*cmsg
;
409 struct ucred cred
= {
410 .pid
= lxc_raw_getpid(),
414 char cmsgbuf
[CMSG_SPACE(sizeof(cred
))] = {0};
417 msg
.msg_control
= cmsgbuf
;
418 msg
.msg_controllen
= sizeof(cmsgbuf
);
420 cmsg
= CMSG_FIRSTHDR(&msg
);
421 cmsg
->cmsg_len
= CMSG_LEN(sizeof(struct ucred
));
422 cmsg
->cmsg_level
= SOL_SOCKET
;
423 cmsg
->cmsg_type
= SCM_CREDENTIALS
;
424 memcpy(CMSG_DATA(cmsg
), &cred
, sizeof(cred
));
429 iov
.iov_base
= data
? data
: buf
;
430 iov
.iov_len
= data
? size
: sizeof(buf
);
434 return sendmsg(fd
, &msg
, MSG_NOSIGNAL
);
437 int lxc_abstract_unix_rcv_credential(int fd
, void *data
, size_t size
)
439 struct msghdr msg
= {0};
441 struct cmsghdr
*cmsg
;
444 char cmsgbuf
[CMSG_SPACE(sizeof(cred
))] = {0};
449 msg
.msg_control
= cmsgbuf
;
450 msg
.msg_controllen
= sizeof(cmsgbuf
);
452 iov
.iov_base
= data
? data
: buf
;
453 iov
.iov_len
= data
? size
: sizeof(buf
);
457 ret
= recvmsg(fd
, &msg
, 0);
461 cmsg
= CMSG_FIRSTHDR(&msg
);
463 if (cmsg
&& cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
)) &&
464 cmsg
->cmsg_level
== SOL_SOCKET
&&
465 cmsg
->cmsg_type
== SCM_CREDENTIALS
) {
466 memcpy(&cred
, CMSG_DATA(cmsg
), sizeof(cred
));
468 if (cred
.uid
&& (cred
.uid
!= getuid() || cred
.gid
!= getgid()))
469 return log_error_errno(-1, EACCES
,
470 "Message denied for '%d/%d'",
477 int lxc_unix_sockaddr(struct sockaddr_un
*ret
, const char *path
)
483 return ret_set_errno(-1, EINVAL
);
484 if (path
[0] != '/' && path
[0] != '@')
485 return ret_set_errno(-1, EINVAL
);
487 return ret_set_errno(-1, EINVAL
);
489 if (len
+ 1 > sizeof(ret
->sun_path
))
490 return ret_set_errno(-1, EINVAL
);
492 *ret
= (struct sockaddr_un
){
493 .sun_family
= AF_UNIX
,
496 if (path
[0] == '@') {
497 memcpy(ret
->sun_path
+ 1, path
+ 1, len
);
498 return (int)(offsetof(struct sockaddr_un
, sun_path
) + len
);
501 memcpy(ret
->sun_path
, path
, len
+ 1);
502 return (int)(offsetof(struct sockaddr_un
, sun_path
) + len
+ 1);
505 int lxc_unix_connect_type(struct sockaddr_un
*addr
, int type
)
507 __do_close
int fd
= -EBADF
;
511 fd
= socket(AF_UNIX
, type
| SOCK_CLOEXEC
, 0);
513 return log_error_errno(-1, errno
,
514 "Failed to open new AF_UNIX socket");
516 if (addr
->sun_path
[0] == '\0')
517 len
= strlen(&addr
->sun_path
[1]);
519 len
= strlen(&addr
->sun_path
[0]);
521 ret
= connect(fd
, (struct sockaddr
*)addr
,
522 offsetof(struct sockaddr_un
, sun_path
) + len
);
524 return log_error_errno(-1, errno
,
525 "Failed to bind new AF_UNIX socket");
530 int lxc_unix_connect(struct sockaddr_un
*addr
)
532 return lxc_unix_connect_type(addr
, SOCK_STREAM
);
535 int lxc_socket_set_timeout(int fd
, int rcv_timeout
, int snd_timeout
)
537 struct timeval out
= {0};
540 out
.tv_sec
= snd_timeout
;
541 ret
= setsockopt(fd
, SOL_SOCKET
, SO_SNDTIMEO
, (const void *)&out
,
546 out
.tv_sec
= rcv_timeout
;
547 ret
= setsockopt(fd
, SOL_SOCKET
, SO_RCVTIMEO
, (const void *)&out
,