25#include <freeradius-devel/bio/fd_priv.h>
26#include <freeradius-devel/util/file.h>
27#include <freeradius-devel/util/cap.h>
28#include <freeradius-devel/util/rand.h>
33#include <netinet/tcp.h>
34#include <netinet/in.h>
58 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt,
sizeof(opt)) < 0) {
71 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt,
sizeof(opt)) < 0) {
93 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on,
sizeof(on)) < 0) {
103# if !defined(SOL_TCP) && defined(IPPROTO_TCP)
104# define SOL_TCP IPPROTO_TCP
114 if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &on,
sizeof(on)) < 0) {
138 if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS, &on,
sizeof(
int)) < 0) {
143#elif defined(SO_TIMESTAMP)
149 if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &on,
sizeof(
int)) < 0) {
174 if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on,
sizeof(on)) < 0) {
201#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
207 int flag = IP_PMTUDISC_DONT;
209 if (setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &flag,
sizeof(flag)) < 0) {
216#if defined(IP_DONTFRAG)
223 if (setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &off,
sizeof(off)) < 0) {
241 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on,
sizeof(on)) < 0) {
255 if (IN6_IS_ADDR_UNSPECIFIED(
UNCONST(
struct in6_addr *, &sock->inet.src_ipaddr.addr.v6))) {
258 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (
char *)&on,
sizeof(on)) < 0) {
286 if (fstatat(dirfd, filename, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
287 if (errno != ENOENT) {
298 if (!S_ISSOCK(buf.st_mode)) {
307 if (buf.st_uid != uid) {
308 fr_strerror_printf(
"Failed opening domain socket %s: incorrect UID (have %u, expected %u)",
309 cfg->
path, buf.st_uid, uid);
331 if (unlinkat(dirfd, filename, 0) < 0) {
345 mode_t dir_perm, uid_t uid)
348 char const *path = cfg->
path;
349 char *p, *dir = NULL;
358 if (
fr_dirfd(dirfd, filename, path) == 0) {
364 if (fstat(*dirfd, &buf) < 0) {
372 if (buf.st_uid != uid) {
373 fr_strerror_printf(
"Failed reading parent directory for file %s: Incorrect UID (have %u, expected %u)",
374 path, buf.st_uid, uid);
378 if (buf.st_gid != cfg->
gid) {
379 fr_strerror_printf(
"Failed reading parent directory for file %s: Incorrect GID (have %u, expected %u)",
380 path, buf.st_gid, cfg->
gid);
389 if (fchmod(*dirfd, dir_perm) < 0) {
406 slashes[0] = slashes[1] = NULL;
407 for (p = dir; *p !=
'\0'; p++) {
409 slashes[0] = slashes[1];
442 parent_fd = open(dir, O_DIRECTORY | O_NOFOLLOW);
453 if (mkdirat(parent_fd, slashes[0] + 1, 0700) < 0) {
460 fd = openat(parent_fd, slashes[0] + 1, O_DIRECTORY);
466 if (fchmod(fd, dir_perm) < 0) {
478 if (fchown(fd, uid, cfg->
gid) < 0) {
484 path = strrchr(cfg->
path,
'/');
535#define FILE_PERMS(_cfg) \
537 file_perm = (_cfg)->perm ? (_cfg)->perm : S_IRGRP | S_IWGRP; \
538 file_perm |= S_IRUSR | S_IWUSR; \
539 dir_perm = file_perm & (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_ISGID | S_ISVTX); \
540 file_perm &= (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); \
541 if ((dir_perm & (S_IRUSR | S_IWUSR)) != 0) dir_perm |= S_IXUSR; \
542 if ((dir_perm & (S_IRGRP | S_IWGRP)) != 0) dir_perm |= S_IXGRP; \
560 mode_t file_perm, dir_perm;
561 char const *filename, *p;
564 struct sockaddr_un sun;
575 if (uid == (uid_t) -1) {
579 if (cfg->
gid == (gid_t) -1) {
589 p = strrchr(cfg->
path,
'/');
592 filename = cfg->
path;
594 }
else if (p == cfg->
path) {
598 fr_strerror_printf(
"Failed opening domain socket %s: cannot exist at file system root", p);
610 if (dirfd != AT_FDCWD) close(dirfd);
623 rcode = bindat(dirfd, my->
info.
socket.
fd, (
struct sockaddr *) &sun, sunlen);
630 rcode = bind(my->
info.
socket.
fd, (
struct sockaddr *) &sun, sunlen);
662 if (dirfd != AT_FDCWD) close(dirfd);
671#undef SO_BINDTODEVICE
674#ifdef SO_BINDTODEVICE
694#ifdef HAVE_CAPABILITY_H
706#elif defined(IP_BOUND_IF) || defined(IPV6_BOUND_IF)
720 rcode = setsockopt(my->
info.
socket.
fd, IPPROTO_IP, IP_BOUND_IF, &opt,
sizeof(opt));
724 rcode = setsockopt(my->
info.
socket.
fd, IPPROTO_IPV6, IPV6_BOUND_IF, &opt,
sizeof(opt));
729 errno = EAFNOSUPPORT;
764 struct sockaddr_storage salocal;
787 do_suid = (my->
info.
socket.inet.src_port > 0) && (my->
info.
socket.inet.src_port < 1024) && (geteuid() != 0);
789#ifdef HAVE_CAPABILITY_H
793 if (do_suid) do_suid = (fr_cap_enable(CAP_NET_BIND_SERVICE,
CAP_EFFECTIVE) < 0);
817 rcode = bind(my->
info.
socket.
fd, (
struct sockaddr *) &salocal, salen);
827 struct sockaddr_in *sin = (
struct sockaddr_in *) &salocal;
847 rcode = bind(my->
info.
socket.
fd, (
struct sockaddr *) &salocal, salen);
858 if (current == range) {
867 if (current == src_port)
break;
871 rcode = bind(my->
info.
socket.
fd, (
struct sockaddr *) &salocal, salen);
923 my->
info.
name =
"??? invalid BIO ???";
942 (cfg->
socket_type == SOCK_DGRAM) ?
"(datagram) " :
"",
949 if ((cfg->
flags & O_ACCMODE) == O_RDONLY) {
953 }
else if ((cfg->
flags & O_ACCMODE) == O_WRONLY) {
964 my->
info.
name =
"??? invalid BIO ???";
986 my->
info.
name =
"??? invalid BIO ???";
1029 switch (cfg->
type) {
1039 fr_strerror_const(
"Destination IP address must be specified for a connected socket");
1052 fr_strerror_printf(
"Source and destination IP addresses are not from the same IP address family");
1069 fr_strerror_const(
"Source IP address must be specified for an unconnected socket");
1106 switch (cfg->
type) {
1117 .prefix = (my->
info.
socket.inet.dst_ipaddr.
af == AF_INET) ? 32 : 128,
1147 fr_strerror_const(
"Cannot set 'reuse_port' for connected UDP sockets with wildcard IP");
1160 protocol = IPPROTO_TCP;
1162 protocol = IPPROTO_UDP;
1213 if (strcmp(cfg->
filename,
"/dev/stdout") == 0) {
1214 if ((cfg->
flags & O_ACCMODE) != O_WRONLY) {
1220 fd = dup(STDOUT_FILENO);
1222 }
else if (strcmp(cfg->
filename,
"/dev/stderr") == 0) {
1223 if ((cfg->
flags & O_ACCMODE) != O_WRONLY)
goto fail_dev;
1225 fd = dup(STDERR_FILENO);
1227 }
else if (strcmp(cfg->
filename,
"/dev/stdin") == 0) {
1228 if ((cfg->
flags & O_ACCMODE) != O_RDONLY) {
1233 fd = dup(STDIN_FILENO);
1332 if (fd < 0)
return fd;
1373 switch (cfg->
type) {
1384 fr_strerror_const(
"Failed configuring socket: unconnected sockets must be UDP");
1466 switch (cfg->
type) {
1484 if (rcode < 0)
goto fail;
1505 mode_t file_perm, dir_perm;
1516 if ((flags & O_ACCMODE) != O_RDONLY) flags |= O_CREAT;
1525 fd = open(cfg->
filename, flags, file_perm);
1536 char const *p = strrchr(cfg->
filename,
'/');
1539 if (!p)
goto do_open;
1543 .uid = cfg->uid != (uid_t) -1 ? cfg->uid : getuid(),
1544 .gid = cfg->gid != (gid_t) -1 ? cfg->gid : getgid(),
1549 fd = openat(dir_fd, p + 1, flags, file_perm);
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
int fr_bio_fd_socket_name(fr_bio_fd_t *my)
int fr_bio_fd_init_connected(fr_bio_fd_t *my)
int fr_filename_to_sockaddr(struct sockaddr_un *sun, socklen_t *sunlen, char const *filename)
int fr_bio_fd_init_listen(fr_bio_fd_t *my)
int fr_bio_fd_init_common(fr_bio_fd_t *my)
fr_socket_t socket
as connected socket
char const * name
printable name of this BIO
uint16_t src_port
our port
fr_bio_fd_type_t type
type of the socket
fr_bio_fd_state_t state
connecting, open, closed, etc.
uint16_t src_port_start
limit source port ranges for client BIOs
uint32_t recv_buff
How big the kernel's receive buffer should be.
bool mkdir
make intermediate directories
fr_ipaddr_t dst_ipaddr
their IP address
fr_bio_fd_type_t type
accept, connected, unconnected, etc.
char const * path
for Unix domain sockets
uint32_t send_buff
How big the kernel's send buffer should be.
char const * filename
for files
uid_t uid
who owns the socket
@ FR_BIO_FD_CONNECTED
connected client sockets (UDP or TCP)
@ FR_BIO_FD_INVALID
not set
@ FR_BIO_FD_UNCONNECTED
unconnected UDP / datagram only
@ FR_BIO_FD_LISTEN
call fr_bio_fd_accept() for each new connection
fr_bio_fd_config_t const * cfg
so we know what was asked, vs what was granted.
@ FR_BIO_FD_TRANSPORT_TCP
TCP over IPv4 or IPv6.
@ FR_BIO_FD_TRANSPORT_UDP
UDP over IPv4 or IPv6.
@ FR_BIO_FD_TRANSPORT_INVALID
not set
@ FR_BIO_FD_TRANSPORT_UNIX
Unix domain socket, named by 'path'.
@ FR_BIO_FD_TRANSPORT_FILE
regular file, named by 'filename'
gid_t gid
who owns the socket
char const * interface
for binding to an interface
uint16_t src_port_end
limit source port ranges for client BIOs
int socket_type
SOCK_STREAM or SOCK_DGRAM.
uint16_t dst_port
their port
bool tcp_delay
We do tcp_nodelay by default.
fr_bio_fd_transport_t transport_type
which transport, as a number instead of a name
@ FR_BIO_FD_STATE_CONNECTING
bool reuse_port
whether or not we re-use the same destination port for datagram sockets
bool exceed_mtu
Whether we allow packets which exceed the local MTU.
fr_ipaddr_t src_ipaddr
our IP address
Configuration for sockets.
Run-time status of the socket.
static int fr_bio_fd_socket_unix_bind(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
Bind to a Unix domain socket.
static int fr_bio_fd_socket_bind_to_device(fr_bio_fd_t *my, UNUSED fr_bio_fd_config_t const *cfg)
This system is missing SO_BINDTODEVICE, IP_BOUND_IF, IPV6_BOUND_IF.
int fr_bio_fd_unix_shutdown(fr_bio_t *bio)
Remove the Unix domain socket which this bio created.
static int fr_bio_fd_socket_unix_mkdir(int *dirfd, char const **filename, fr_bio_fd_config_t const *cfg, mode_t dir_perm, uid_t uid)
static int fr_bio_fd_common_rcvsndbuf(int fd, fr_bio_fd_config_t const *cfg)
static int fr_bio_fd_common_udp(int fd, fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
Initialize a UDP socket.
int fr_bio_fd_reopen(fr_bio_t *bio)
Reopen a file BIO.
static int fr_bio_fd_server_tcp(int fd, UNUSED fr_socket_t const *sock)
Initialize a TCP server socket.
static int fr_bio_fd_unix_open(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
Open a Unix domain socket.
static int fr_bio_fd_server_v6only(UNUSED int fd, UNUSED fr_socket_t const *sock)
Stop a wildcard IPv6 listener from accepting IPv4 packets.
void fr_bio_fd_name(fr_bio_fd_t *my)
Set the name of an FD BIO.
static int fr_bio_fd_socket_bind(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
static int fr_bio_fd_socket_unix_verify(int dirfd, char const *filename, fr_bio_fd_config_t const *cfg, uid_t uid)
Verify or clean up a pre-existing domain socket.
static int fr_bio_fd_common_tcp(int fd, UNUSED fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
Initialize common datagram information.
static int fr_bio_fd_file_open(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
Open a file.
int fr_bio_fd_open(fr_bio_t *bio, fr_bio_fd_config_t const *cfg)
Opens a socket and updates sock->fd.
int fr_bio_fd_check_config(fr_bio_fd_config_t const *cfg)
Checks the configuration without modifying anything.
static int fr_bio_fd_socket_open(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
Open a TCP or UDP socket.
static int fr_bio_fd_common_datagram(int fd, UNUSED fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
Initialize common datagram information.
int fr_ipaddr_is_inaddr_any(fr_ipaddr_t const *ipaddr)
Determine if an address is the INADDR_ANY address for its address family.
int fr_ipaddr_to_sockaddr(struct sockaddr_storage *sa, socklen_t *salen, fr_ipaddr_t const *ipaddr, uint16_t port)
Convert our internal ip address representation to a sockaddr.
ssize_t fr_mkdir(int *fd_out, char const *path, ssize_t len, mode_t mode, fr_mkdir_func_t func, void *uctx)
Create directories that are missing in the specified path.
int fr_mkdir_chown(int fd, char const *path, void *uctx)
Callback for the common case of chown() of the directory.
int fr_dirfd(int *dirfd, char const **filename, char const *pathname)
From a pathname, return fd and filename needed for *at() functions.
int fr_nonblock(UNUSED int fd)
int fr_cloexec(UNUSED int fd)
char * fr_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Special version of asprintf which implements custom format specifiers.
uint32_t fr_rand(void)
Return a 32-bit random number.
int fr_socket_client_unix(UNUSED char const *path, UNUSED bool async)
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
#define talloc_strdup(_ctx, _str)
int af
AF_INET, AF_INET6, or AF_UNIX.
int fd
File descriptor if this is a live socket.
int type
SOCK_STREAM, SOCK_DGRAM, etc.
Holds information necessary for binding or connecting to a socket.
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
#define fr_strerror_const(_msg)
#define fr_box_ipaddr(_val)