Merge pull request #23339 from poettering/sockaddr-size-limit

tree-wide: add support for connecting to AF_UNIX sockets in the file system beyond the 108ch limit
This commit is contained in:
Luca Boccassi
2022-05-21 15:09:04 +01:00
committed by GitHub
13 changed files with 123 additions and 125 deletions
+6 -21
View File
@@ -763,8 +763,7 @@ int read_full_file_full(
r = xfopenat(dir_fd, filename, "re", 0, &f);
if (r < 0) {
_cleanup_close_ int dfd = -1, sk = -1;
union sockaddr_union sa;
_cleanup_close_ int sk = -1;
/* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */
if (r != -ENXIO)
@@ -778,22 +777,6 @@ int read_full_file_full(
if (offset != UINT64_MAX)
return -ENXIO;
if (dir_fd == AT_FDCWD)
r = sockaddr_un_set_path(&sa.un, filename);
else {
/* If we shall operate relative to some directory, then let's use O_PATH first to
* open the socket inode, and then connect to it via /proc/self/fd/. We have to do
* this since there's not connectat() that takes a directory fd as first arg. */
dfd = openat(dir_fd, filename, O_PATH|O_CLOEXEC);
if (dfd < 0)
return -errno;
r = sockaddr_un_set_path(&sa.un, FORMAT_PROC_FD_PATH(dfd));
}
if (r < 0)
return r;
sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
if (sk < 0)
return -errno;
@@ -812,9 +795,11 @@ int read_full_file_full(
return -errno;
}
if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is
* not a socket after all */
r = connect_unix_path(sk, dir_fd, filename);
if (IN_SET(r, -ENOTSOCK, -EINVAL)) /* propagate original error if this is not a socket after all */
return -ENXIO;
if (r < 0)
return r;
if (shutdown(sk, SHUT_WR) < 0)
return -errno;
+8 -22
View File
@@ -149,12 +149,6 @@ static int create_log_socket(int type) {
}
static int log_open_syslog(void) {
static const union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/dev/log",
};
int r;
if (syslog_fd >= 0)
@@ -166,22 +160,21 @@ static int log_open_syslog(void) {
goto fail;
}
if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
r = connect_unix_path(syslog_fd, AT_FDCWD, "/dev/log");
if (r < 0) {
safe_close(syslog_fd);
/* Some legacy syslog systems still use stream
* sockets. They really shouldn't. But what can we
* do... */
/* Some legacy syslog systems still use stream sockets. They really shouldn't. But what can
* we do... */
syslog_fd = create_log_socket(SOCK_STREAM);
if (syslog_fd < 0) {
r = syslog_fd;
goto fail;
}
if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
r = -errno;
r = connect_unix_path(syslog_fd, AT_FDCWD, "/dev/log");
if (r < 0)
goto fail;
}
syslog_is_stream = true;
} else
@@ -199,12 +192,6 @@ static void log_close_journal(void) {
}
static int log_open_journal(void) {
static const union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/socket",
};
int r;
if (journal_fd >= 0)
@@ -216,10 +203,9 @@ static int log_open_journal(void) {
goto fail;
}
if (connect(journal_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
r = -errno;
r = connect_unix_path(journal_fd, AT_FDCWD, "/run/systemd/journal/socket");
if (r < 0)
goto fail;
}
return 0;
+48
View File
@@ -1426,3 +1426,51 @@ int socket_get_mtu(int fd, int af, size_t *ret) {
*ret = (size_t) mtu;
return 0;
}
int connect_unix_path(int fd, int dir_fd, const char *path) {
_cleanup_close_ int inode_fd = -1;
union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
};
size_t path_len;
socklen_t salen;
assert(fd >= 0);
assert(dir_fd == AT_FDCWD || dir_fd >= 0);
assert(path);
/* Connects to the specified AF_UNIX socket in the file system. Works around the 108 byte size limit
* in sockaddr_un, by going via O_PATH if needed. This hence works for any kind of path. */
path_len = strlen(path);
/* Refuse zero length path early, to make sure AF_UNIX stack won't mistake this for an abstract
* namespace path, since first char is NUL */
if (path_len <= 0)
return -EINVAL;
if (dir_fd == AT_FDCWD && path_len < sizeof(sa.un.sun_path)) {
memcpy(sa.un.sun_path, path, path_len + 1);
salen = offsetof(struct sockaddr_un, sun_path) + path_len + 1;
} else {
const char *proc;
size_t proc_len;
/* If dir_fd is specified, then we need to go the indirect O_PATH route, because connectat()
* does not exist. If the path is too long, we also need to take the indirect route, since we
* can't fit this into a sockaddr_un directly. */
inode_fd = openat(dir_fd, path, O_PATH|O_CLOEXEC);
if (inode_fd < 0)
return -errno;
proc = FORMAT_PROC_FD_PATH(inode_fd);
proc_len = strlen(proc);
assert(proc_len < sizeof(sa.un.sun_path));
memcpy(sa.un.sun_path, proc, proc_len + 1);
salen = offsetof(struct sockaddr_un, sun_path) + proc_len + 1;
}
return RET_NERRNO(connect(fd, &sa.sa, salen));
}
+2
View File
@@ -332,3 +332,5 @@ int socket_get_mtu(int fd, int af, size_t *ret);
/* an initializer for struct ucred that initialized all fields to the invalid value appropriate for each */
#define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID }
int connect_unix_path(int fd, int dir_fd, const char *path);