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:
+6
-21
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user