On Thu, Oct 03, 2019 at 08:35:37PM +0100, Ken Moffat via blfs-dev wrote:
> On Wed, Oct 02, 2019 at 09:53:21PM -0500, Douglas R. Reno wrote:
> > 
> > https://ch1p.io/blog/1/ (WARNING: The Security Certificate expired on 10/1.
> > The site and the links within (attached) are safe.)
> > https://stackoverflow.com/questions/29708596/drmdropmaster-requires-root-privileges
> > https://github.com/gch1p/drm_master_util
> > https://f.ch1p.io/fnHHUx0b/rootless_modesetting_1.20.3.patch
> > 
> > From what I gather, the patch puts a call out over a file descriptor and
> > socket to spawn a suid wrapper indirectly, which only affects AMD cards. I'm
> > not 100% sure if my interpretation is correct though, since this is all so
> > complex...
> > 
> > - Doug
> 
> I'll be trying this later (as well as fixing up my build)

 
> The hack is actually for Xorg's own modesetting driver, but if it
> also fixes things for ati/amdgpu then all will be good.
> 

I'm attaching a headed version of the patch: AFAICS it is less
dangerous than the alternative of running X as root in the old way.

I can just dump this in patches/ but I'd like to mention it on the
xorg-server page for use on the server's own modesetting driver as
well as (most) radeon and amdgpu chipsets.  The original post in the
debian bug I pointed to suggested that for at least one chipset,
with the wrapper debian used to use for non-systemd, rootless xorg
worked - but not for others.

It seems to me that this is better than running Xorg itself with
root privileges.

Comments, please.

ĸen
-- 
Truth, in front of her huge walk-in wardrobe, selected black leather
boots with stiletto heels for such a barefaced truth.
                                     - Unseen Academicals
Not Yet Submitted By: Ken Moffat <ken at linuxfromscratch dot org>
Date: 2019-10-04
Initial Package Version: 1.20.3
Upstream Status: Unknown
Origin: Evgeny Zinoviev, https://github.com/gch1p/drm_master_util
Description: A workaround for rootless xorg using the server's
modesetting driver, also useful on many ati/radeon and amdgpu
chipsets if systemd is not used (they use different code paths
than intel and nouveau). A few ati/amdgpu chipsets might not need
this.

>From the notes at https://ch1p.io/blog/1/

(Using rootless Xorg without systemd works with the intel driver,
but not with Xorg's own modesetting) :

This is because drmSetMaster and drmDropMaster require root privileges.
So in order to make it work we have to hack Xorg and make it call these
functions under root.

How about we write a suid helper program that will receive a file
descriptor, call the desired function and exit? Sounds like a very dirty
hack, but hey, it's better than the whole X server running under root.

So here's how it's gonna work. Instead of just calling drmSetMaster(ms->fd),
the X server will create a new UNIX domain socket and wait for a connection.
At the same time it'll launch our siud helper program from another thread.
The helper program will connect to that socket. The X server will accept the
connection and write ms->fd to the socket. The helper program will read the fd,
call drmSetMaster (or drmDropMaster) and exit. The X server will then close the
socket and continue execution as normally.

(Note by Ken: the alternative is to create /etc/X11/Xwrapper.config with the
line:
needs_root_rights = yes

That will cause Xorg to run with root privileges.)

diff --git a/hw/xfree86/drivers/modesetting/driver.c 
b/hw/xfree86/drivers/modesetting/driver.c
index 8d29b13..1034ef9 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -34,6 +34,11 @@
 #endif
 
 #include <unistd.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 #include <fcntl.h>
 #include "xf86.h"
 #include "xf86Priv.h"
@@ -62,6 +67,8 @@
 
 #include "driver.h"
 
+#define DRM_HACK_SOCKET_NAME "xorg_drm_master_util"
+
 static void AdjustFrame(ScrnInfoPtr pScrn, int x, int y);
 static Bool CloseScreen(ScreenPtr pScreen);
 static Bool EnterVT(ScrnInfoPtr pScrn);
@@ -1513,11 +1520,66 @@ msSharedPixmapNotifyDamage(PixmapPtr ppix)
     return ret;
 }
 
+static int
+send_fd(int sock, int fd)
+{
+    // This function does the arcane magic for sending
+    // file descriptors over unix domain sockets
+    struct msghdr msg;
+    struct iovec iov[1];
+    struct cmsghdr *cmsg = NULL;
+    char ctrl_buf[CMSG_SPACE(sizeof(int))];
+    char data[1];
+
+    memset(&msg, 0, sizeof(struct msghdr));
+    memset(ctrl_buf, 0, CMSG_SPACE(sizeof(int)));
+
+    data[0] = ' ';
+    iov[0].iov_base = data;
+    iov[0].iov_len = sizeof(data);
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+    msg.msg_controllen =  CMSG_SPACE(sizeof(int));
+    msg.msg_control = ctrl_buf;
+
+    cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+
+    *((int *) CMSG_DATA(cmsg)) = fd;
+
+    return sendmsg(sock, &msg, 0);
+}
+
+static void*
+thread_func(void* argument)
+{
+    int ret;
+    int option = *(int *)argument;
+    char cmd[32];
+    sprintf(cmd, "/usr/bin/drm_master_util %s -r", (!option ? "-s" : "-d"));
+
+    ret = system(cmd);
+    if (ret == -1 || WEXITSTATUS(ret) != 0) {
+        fprintf(stderr, "%s\n", strerror(errno));
+        //exit(1);
+    }
+
+    pthread_exit(NULL); // you could also return NULL here to exit no 
difference
+}
+
 static Bool
 SetMaster(ScrnInfoPtr pScrn)
 {
     modesettingPtr ms = modesettingPTR(pScrn);
-    int ret;
+    int ret = 0;
+    pthread_t my_thread;
+    struct sockaddr_un addr;
+    int sock, conn, option = 0;
 
 #ifdef XF86_PDEV_SERVER_FD
     if (ms->pEnt->location.type == BUS_PLATFORM &&
@@ -1528,10 +1590,21 @@ SetMaster(ScrnInfoPtr pScrn)
     if (ms->fd_passed)
         return TRUE;
 
-    ret = drmSetMaster(ms->fd);
-    if (ret)
-        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "drmSetMaster failed: %s\n",
-                   strerror(errno));
+    sock = socket(AF_UNIX, SOCK_STREAM, 0);
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strcpy(&addr.sun_path[1], DRM_HACK_SOCKET_NAME);
+    bind(sock, (struct sockaddr *)&addr, sizeof(addr));
+
+    listen(sock, 1);
+    pthread_create(&my_thread, NULL, thread_func, &option);
+
+    conn = accept(sock, NULL, 0);
+    send_fd(conn, ms->fd);
+    close(conn);
+    close(sock);
+
+    pthread_join(my_thread, NULL);
 
     return ret == 0;
 }
@@ -1769,6 +1842,9 @@ static void
 LeaveVT(ScrnInfoPtr pScrn)
 {
     modesettingPtr ms = modesettingPTR(pScrn);
+    pthread_t my_thread;
+    struct sockaddr_un addr;
+    int sock, conn, option = 1;
 
     xf86_hide_cursors(pScrn);
 
@@ -1780,8 +1856,21 @@ LeaveVT(ScrnInfoPtr pScrn)
         return;
 #endif
 
-    if (!ms->fd_passed)
-        drmDropMaster(ms->fd);
+    sock = socket(AF_UNIX, SOCK_STREAM, 0);
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strcpy(&addr.sun_path[1], DRM_HACK_SOCKET_NAME);
+    bind(sock, (struct sockaddr *)&addr, sizeof(addr));
+
+    listen(sock, 1);
+    pthread_create(&my_thread, NULL, thread_func, &option);
+
+    conn = accept(sock, NULL, 0);
+    send_fd(conn, ms->fd);
+    close(conn);
+    close(sock);
+
+    pthread_join(my_thread, NULL);
 }
 
 /*
-- 
http://lists.linuxfromscratch.org/listinfo/blfs-dev
FAQ: http://www.linuxfromscratch.org/blfs/faq.html
Unsubscribe: See the above information page

Reply via email to