fix(native): D-Bus logins work and Xwayland still admits root once peers read as root

This commit is contained in:
Kurt Himebauch committed 2026-11-09 09:06:11 -05:00
1 parent 1008da4e8d
commit ea51a373d7
2 files changed
+45 -2

No files matched your search

+8 -2
View File
@@ -715,11 +715,17 @@ int DDNATV_NSS_SYM(getgrnam_r)(const char *name, struct group *grp, char *buf, s
// setgid() only pretend to), and sshd's unprivileged part checks after dropping them that it cannot
// take them back, which ended it ("permanently_set_uid: was able to restore old [e]gid"). Like apt's
// sandbox user (APT::Sandbox::User "root"), it then runs as the rest does.
//
// The app's own ids are root's as well: the rootfs lists root with them for proot, and a server
// that allows "localuser:root" compares getpwnam("root") with a peer's credentials, which the hook
// reports as 0. Xwayland refused gamescope's own connection when the two disagreed.
static struct passwd *present_privsep_user_as_root(struct passwd *pwd) {
if (pwd && pwd->pw_name && strcmp(pwd->pw_name, "sshd") == 0) {
pwd->pw_uid = 0;
pwd->pw_gid = 0;
}
if (pwd && pwd->pw_uid == real_uid()) pwd->pw_uid = 0;
if (pwd && pwd->pw_gid == real_gid()) pwd->pw_gid = 0;
return pwd;
}
@@ -728,11 +734,11 @@ struct passwd *DDNATV_NSS_SYM(getpwuid)(uid_t uid) {
static struct passwd *(*orig_getpwuid)(uid_t) = NULL;
if (!orig_getpwuid) orig_getpwuid = (struct passwd *(*)(uid_t))dlsym(RTLD_NEXT, "getpwuid");
struct passwd *res = orig_getpwuid ? orig_getpwuid(uid) : NULL;
if (res) return res;
if (res) return present_privsep_user_as_root(res);
#endif
int err = ddnatv_lookup_passwd(NULL, uid, 0, &s_pwd_storage, s_pwd_buf, sizeof(s_pwd_buf));
if (err == 0) return &s_pwd_storage;
if (err == 0) return present_privsep_user_as_root(&s_pwd_storage);
return NULL;
}
+37
View File
@@ -221,6 +221,42 @@ ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
return orig_sendmsg ? orig_sendmsg(sockfd, msg, flags) : -1;
}
static void present_peer_as_root(struct ucred *cred) {
if (cred->uid == (uid_t)call_orig_syscall(DDNATV_NR_REAL_GETUID, 0, 0, 0, 0, 0, 0)) cred->uid = 0;
if (cred->gid == (gid_t)call_orig_syscall(DDNATV_NR_REAL_GETGID, 0, 0, 0, 0, 0, 0)) cred->gid = 0;
}
// A server compares a peer's credentials with the user the peer claims, which is getuid()'s 0
// here, while the kernel reports the app's ids: dbus-daemon refused every EXTERNAL login ("Did not
// receive a reply"), so the Plasma desktop never started.
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) {
static int (*orig_getsockopt)(int, int, int, void *, socklen_t *) = NULL;
if (!orig_getsockopt) orig_getsockopt = (int (*)(int, int, int, void *, socklen_t *))dlsym(RTLD_NEXT, "getsockopt");
if (!orig_getsockopt) {
errno = ENOSYS;
return -1;
}
int ret = orig_getsockopt(sockfd, level, optname, optval, optlen);
if (ret == 0 && level == SOL_SOCKET && optname == SO_PEERCRED && optval && optlen && *optlen >= sizeof(struct ucred)) {
struct ucred cred;
memcpy(&cred, optval, sizeof(cred));
present_peer_as_root(&cred);
memcpy(optval, &cred, sizeof(cred));
}
return ret;
}
static void received_credentials_as_root(struct msghdr *msg) {
if (!msg || !msg->msg_control || msg->msg_controllen < sizeof(struct cmsghdr)) return;
for (struct cmsghdr *c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) {
if (c->cmsg_level != SOL_SOCKET || c->cmsg_type != SCM_CREDENTIALS || c->cmsg_len < CMSG_LEN(sizeof(struct ucred))) continue;
struct ucred cred;
memcpy(&cred, CMSG_DATA(c), sizeof(cred));
present_peer_as_root(&cred);
memcpy(CMSG_DATA(c), &cred, sizeof(cred));
}
}
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen) {
static ssize_t (*orig_recvfrom)(int, void *, size_t, int, struct sockaddr *, socklen_t *) = NULL;
@@ -251,6 +287,7 @@ ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) {
socklen_t cap = msg ? msg->msg_namelen : 0;
ssize_t ret = orig_recvmsg ? orig_recvmsg(sockfd, msg, flags) : -1;
if (ret >= 0) received_credentials_as_root(msg);
if (ret > 0 && msg && msg->msg_name) {
int saved_errno = errno;
restore_dns_source(sockfd, (struct sockaddr *)msg->msg_name, cap, &msg->msg_namelen);