Merge pull request #10412 from poettering/sockaddr-sun-path
various fixes related to struct sockaddr_un handling
This commit is contained in:
@@ -46,6 +46,21 @@ static inline void *mfree(void *memory) {
|
||||
void* memdup(const void *p, size_t l) _alloc_(2);
|
||||
void* memdup_suffix0(const void *p, size_t l) _alloc_(2);
|
||||
|
||||
#define memdupa(p, l) \
|
||||
({ \
|
||||
void *_q_; \
|
||||
_q_ = alloca(l); \
|
||||
memcpy(_q_, p, l); \
|
||||
})
|
||||
|
||||
#define memdupa_suffix0(p, l) \
|
||||
({ \
|
||||
void *_q_; \
|
||||
_q_ = alloca(l + 1); \
|
||||
((uint8_t*) _q_)[l] = 0; \
|
||||
memcpy(_q_, p, l); \
|
||||
})
|
||||
|
||||
static inline void freep(void *p) {
|
||||
free(*(void**) p);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ int socket_address_listen(
|
||||
|
||||
_cleanup_close_ int fd = -1;
|
||||
const char *p;
|
||||
int r, one;
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
|
||||
@@ -74,26 +74,22 @@ int socket_address_listen(
|
||||
return -errno;
|
||||
|
||||
if (reuse_port) {
|
||||
one = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0)
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &const_int_one, sizeof(const_int_one)) < 0)
|
||||
log_warning_errno(errno, "SO_REUSEPORT failed: %m");
|
||||
}
|
||||
|
||||
if (free_bind) {
|
||||
one = 1;
|
||||
if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
|
||||
if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &const_int_one, sizeof(const_int_one)) < 0)
|
||||
log_warning_errno(errno, "IP_FREEBIND failed: %m");
|
||||
}
|
||||
|
||||
if (transparent) {
|
||||
one = 1;
|
||||
if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
|
||||
if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &const_int_one, sizeof(const_int_one)) < 0)
|
||||
log_warning_errno(errno, "IP_TRANSPARENT failed: %m");
|
||||
}
|
||||
}
|
||||
|
||||
one = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one)) < 0)
|
||||
return -errno;
|
||||
|
||||
p = socket_address_get_path(a);
|
||||
|
||||
+100
-27
@@ -57,8 +57,9 @@ int socket_address_parse(SocketAddress *a, const char *s) {
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
zero(*a);
|
||||
a->type = SOCK_STREAM;
|
||||
*a = (SocketAddress) {
|
||||
.type = SOCK_STREAM,
|
||||
};
|
||||
|
||||
if (*s == '[') {
|
||||
uint16_t port;
|
||||
@@ -96,7 +97,9 @@ int socket_address_parse(SocketAddress *a, const char *s) {
|
||||
size_t l;
|
||||
|
||||
l = strlen(s);
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path))
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path)) /* Note that we refuse non-NUL-terminated sockets when
|
||||
* parsing (the kernel itself is less strict here in what it
|
||||
* accepts) */
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.un.sun_family = AF_UNIX;
|
||||
@@ -108,7 +111,11 @@ int socket_address_parse(SocketAddress *a, const char *s) {
|
||||
size_t l;
|
||||
|
||||
l = strlen(s+1);
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminate sockets here
|
||||
* when parsing, even though abstract namespace sockets
|
||||
* explicitly allow embedded NUL bytes and don't consider
|
||||
* them special. But it's simply annoying to debug such
|
||||
* sockets. */
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.un.sun_family = AF_UNIX;
|
||||
@@ -286,19 +293,28 @@ int socket_address_verify(const SocketAddress *a) {
|
||||
case AF_UNIX:
|
||||
if (a->size < offsetof(struct sockaddr_un, sun_path))
|
||||
return -EINVAL;
|
||||
if (a->size > sizeof(struct sockaddr_un)+1) /* Allow one extra byte, since getsockname() on Linux will
|
||||
* append a NUL byte if we have path sockets that are above
|
||||
* sun_path' full size */
|
||||
return -EINVAL;
|
||||
|
||||
if (a->size > offsetof(struct sockaddr_un, sun_path)) {
|
||||
if (a->size > offsetof(struct sockaddr_un, sun_path) &&
|
||||
a->sockaddr.un.sun_path[0] != 0) { /* Only validate file system sockets here */
|
||||
|
||||
if (a->sockaddr.un.sun_path[0] != 0) {
|
||||
char *e;
|
||||
|
||||
/* path */
|
||||
e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
|
||||
if (!e)
|
||||
return -EINVAL;
|
||||
const char *e;
|
||||
|
||||
e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
|
||||
if (e) {
|
||||
/* If there's an embedded NUL byte, make sure the size of the socket addresses matches it */
|
||||
if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
/* If there's no embedded NUL byte, then then the size needs to match the whole
|
||||
* structure or the structure with one extra NUL byte suffixed. (Yeah, Linux is awful,
|
||||
* and considers both equivalent: getsockname() even extends sockaddr_un beyond its
|
||||
* size if the path is non NUL terminated.)*/
|
||||
if (!IN_SET(a->size, sizeof(a->sockaddr.un.sun_path), sizeof(a->sockaddr.un.sun_path)+1))
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,6 +498,10 @@ const char* socket_address_get_path(const SocketAddress *a) {
|
||||
if (a->sockaddr.un.sun_path[0] == 0)
|
||||
return NULL;
|
||||
|
||||
/* Note that this is only safe because we know that there's an extra NUL byte after the sockaddr_un
|
||||
* structure. On Linux AF_UNIX file system socket addresses don't have to be NUL terminated if they take up the
|
||||
* full sun_path space. */
|
||||
assert_cc(sizeof(union sockaddr_union) >= sizeof(struct sockaddr_un)+1);
|
||||
return a->sockaddr.un.sun_path;
|
||||
}
|
||||
|
||||
@@ -747,21 +767,6 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_address_unlink(SocketAddress *a) {
|
||||
assert(a);
|
||||
|
||||
if (socket_address_family(a) != AF_UNIX)
|
||||
return 0;
|
||||
|
||||
if (a->sockaddr.un.sun_path[0] == 0)
|
||||
return 0;
|
||||
|
||||
if (unlink(a->sockaddr.un.sun_path) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char* const netlink_family_table[] = {
|
||||
[NETLINK_ROUTE] = "route",
|
||||
[NETLINK_FIREWALL] = "firewall",
|
||||
@@ -1246,3 +1251,71 @@ int socket_ioctl_fd(void) {
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int sockaddr_un_unlink(const struct sockaddr_un *sa) {
|
||||
const char *p, * nul;
|
||||
|
||||
assert(sa);
|
||||
|
||||
if (sa->sun_family != AF_UNIX)
|
||||
return -EPROTOTYPE;
|
||||
|
||||
if (sa->sun_path[0] == 0) /* Nothing to do for abstract sockets */
|
||||
return 0;
|
||||
|
||||
/* The path in .sun_path is not necessarily NUL terminated. Let's fix that. */
|
||||
nul = memchr(sa->sun_path, 0, sizeof(sa->sun_path));
|
||||
if (nul)
|
||||
p = sa->sun_path;
|
||||
else
|
||||
p = memdupa_suffix0(sa->sun_path, sizeof(sa->sun_path));
|
||||
|
||||
if (unlink(p) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) {
|
||||
size_t l;
|
||||
|
||||
assert(ret);
|
||||
assert(path);
|
||||
|
||||
/* Initialize ret->sun_path from the specified argument. This will interpret paths starting with '@' as
|
||||
* abstract namespace sockets, and those starting with '/' as regular filesystem sockets. It won't accept
|
||||
* anything else (i.e. no relative paths), to avoid ambiguities. Note that this function cannot be used to
|
||||
* reference paths in the abstract namespace that include NUL bytes in the name. */
|
||||
|
||||
l = strlen(path);
|
||||
if (l == 0)
|
||||
return -EINVAL;
|
||||
if (!IN_SET(path[0], '/', '@'))
|
||||
return -EINVAL;
|
||||
if (path[1] == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than
|
||||
* the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket
|
||||
* addresses!), which the kernel doesn't. We do this to reduce chance of incompatibility with other apps that
|
||||
* do not expect non-NUL terminated file system path*/
|
||||
if (l+1 > sizeof(ret->sun_path))
|
||||
return -EINVAL;
|
||||
|
||||
*ret = (struct sockaddr_un) {
|
||||
.sun_family = AF_UNIX,
|
||||
};
|
||||
|
||||
if (path[0] == '@') {
|
||||
/* Abstract namespace socket */
|
||||
memcpy(ret->sun_path + 1, path + 1, l); /* copy *with* trailing NUL byte */
|
||||
return (int) (offsetof(struct sockaddr_un, sun_path) + l); /* 🔥 *don't* 🔥 include trailing NUL in size */
|
||||
|
||||
} else {
|
||||
assert(path[0] == '/');
|
||||
|
||||
/* File system socket */
|
||||
memcpy(ret->sun_path, path, l + 1); /* copy *with* trailing NUL byte */
|
||||
return (int) (offsetof(struct sockaddr_un, sun_path) + l + 1); /* include trailing NUL in size */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,12 @@ int socket_address_parse_and_warn(SocketAddress *a, const char *s);
|
||||
int socket_address_parse_netlink(SocketAddress *a, const char *s);
|
||||
int socket_address_print(const SocketAddress *a, char **p);
|
||||
int socket_address_verify(const SocketAddress *a) _pure_;
|
||||
int socket_address_unlink(SocketAddress *a);
|
||||
|
||||
int sockaddr_un_unlink(const struct sockaddr_un *sa);
|
||||
|
||||
static inline int socket_address_unlink(const SocketAddress *a) {
|
||||
return socket_address_family(a) == AF_UNIX ? sockaddr_un_unlink(&a->sockaddr.un) : 0;
|
||||
}
|
||||
|
||||
bool socket_address_can_accept(const SocketAddress *a) _pure_;
|
||||
|
||||
@@ -179,7 +184,9 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
|
||||
offsetof(struct sockaddr_un, sun_path) + \
|
||||
(_sa->sun_path[0] == 0 ? \
|
||||
1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \
|
||||
strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \
|
||||
strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \
|
||||
})
|
||||
|
||||
int socket_ioctl_fd(void);
|
||||
|
||||
int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path);
|
||||
|
||||
@@ -31,12 +31,11 @@ size_t strpcpy(char **dest, size_t size, const char *src) {
|
||||
if (size > 1)
|
||||
*dest = mempcpy(*dest, src, size-1);
|
||||
size = 0;
|
||||
} else {
|
||||
if (len > 0) {
|
||||
*dest = mempcpy(*dest, src, len);
|
||||
size -= len;
|
||||
}
|
||||
} else if (len > 0) {
|
||||
*dest = mempcpy(*dest, src, len);
|
||||
size -= len;
|
||||
}
|
||||
|
||||
*dest[0] = '\0';
|
||||
return size;
|
||||
}
|
||||
@@ -56,9 +55,8 @@ size_t strpcpyf(char **dest, size_t size, const char *src, ...) {
|
||||
if (i < (int)size) {
|
||||
*dest += i;
|
||||
size -= i;
|
||||
} else {
|
||||
} else
|
||||
size = 0;
|
||||
}
|
||||
va_end(va);
|
||||
return size;
|
||||
}
|
||||
@@ -73,7 +71,7 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
|
||||
do {
|
||||
size = strpcpy(dest, size, src);
|
||||
src = va_arg(va, char *);
|
||||
} while (src != NULL);
|
||||
} while (src);
|
||||
va_end(va);
|
||||
return size;
|
||||
}
|
||||
@@ -100,7 +98,7 @@ size_t strscpyl(char *dest, size_t size, const char *src, ...) {
|
||||
do {
|
||||
size = strpcpy(&s, size, src);
|
||||
src = va_arg(va, char *);
|
||||
} while (src != NULL);
|
||||
} while (src);
|
||||
va_end(va);
|
||||
|
||||
return size;
|
||||
|
||||
@@ -51,6 +51,9 @@ int saved_argc = 0;
|
||||
char **saved_argv = NULL;
|
||||
static int saved_in_initrd = -1;
|
||||
|
||||
const int const_int_zero = 0;
|
||||
const int const_int_one = 1;
|
||||
|
||||
size_t page_size(void) {
|
||||
static thread_local size_t pgsz = 0;
|
||||
long r;
|
||||
|
||||
@@ -232,3 +232,6 @@ int version(void);
|
||||
int str_verscmp(const char *s1, const char *s2);
|
||||
|
||||
void disable_coredumps(void);
|
||||
|
||||
extern const int const_int_zero;
|
||||
extern const int const_int_one;
|
||||
|
||||
Reference in New Issue
Block a user