Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FreeBSD : Collisions between redirected ssh X server and xrdp #3381

Open
matt335672 opened this issue Jan 6, 2025 · 11 comments · May be fixed by #3390
Open

FreeBSD : Collisions between redirected ssh X server and xrdp #3381

matt335672 opened this issue Jan 6, 2025 · 11 comments · May be fixed by #3390
Labels
bug FreeBSD FreeBSD specific issue

Comments

@matt335672
Copy link
Member

matt335672 commented Jan 6, 2025

Edit : 2025-1-7 The problem symptoms have been edited. Please consult the issue history for where we started.

xrdp version

0.10.x / devel

Detailed xrdp version, build options

xrdp 0.10.80
  A Remote Desktop Protocol Server.
  Copyright (C) 2004-2024 Jay Sorg, Neutrino Labs, and all contributors.
  See https://github.com/neutrinolabs/xrdp for more information.

  Configure options:
      --enable-devel-all
      --enable-fuse
      --enable-pixman
      --enable-ipv6
      --enable-painter
      --enable-jpeg
      --with-imlib2
      --enable-vsock
      --with-freetype2
      --enable-utmp
      CC=clang
      CFLAGS=-g
      LDFLAGS=-L/usr/local/lib
      CPPFLAGS=-I/usr/local/include

  Compiled with OpenSSL 3.0.15 3 Sep 2024

Operating system & version

FreeBSD 14.2-RELEASE

Installation method

git clone & make install

Which backend do you use?

xorgxrdp

What desktop environment do you use?

XFCE

Environment xrdp running on

VM

What's your client?

Various

Area(s) with issue?

Session manager (sesman)

Steps to reproduce

  1. Ensure sshd is configured to allow X11 forwarding
  2. Log on to system from elsewhere using ssh -X:-
$ ssh -X freebsd14.test.lan
<snipped>
$ echo $DISPLAY
localhost:10
$ xauth list
freebsd14/unix:10  MIT-MAGIC-COOKIE-1  186a734d1d0ea2b8a2ae0c543a50c7c
  1. Log in to system using xrdp and the same user

✔️ Expected Behavior

Following login, xauth list in ssh session shows same information

❌ Actual Behavior

$ xauth list
freebsd14/unix:10  MIT-MAGIC-COOKIE-1  00615f25c6bb6e0c71b20c32d4e9ea12

Anything else?

Thanks to @derekschrock for raising this on Gitter.

An active X server is checked for by xrdp by using the following algorithm:-

  1. Looking for the UNIX Domain Socket opened by the X server
  2. trying to bind to 0.0.0.0:<tport> and/or [::]:<tport> where <tport> is the TCP port which may be used by the X server.

When ssh X11 forwarding is used by ssh, there is no Unix Domain Socket, and the forwarding TCP port is bound to localhost only.

The problem seems to be caused by a difference in behaviour between Linux and FreeBSD when SO_REUSEADDR is set on a TCP socket passed to bind()

On Linux, if a program is listening on localhost:<tport>, and another user tries to listen on 0.0.0.0:<tport>; or [::]:<tport>, the bind() fails with EADDRINUSE

On FreeBSD, with the same conditions, the bind() succeeds.

The following IPv4 test program illustrates the issue:-

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/socket.h>
#include <netinet/in.h>

#include <unistd.h>

static unsigned short
get_port(void)
{
    unsigned short rv = 0;
    const char *d = getenv("DISPLAY");
    if (d == NULL ||  strncmp(d, "localhost:", 10) != 0)
    {
        fprintf(stderr, "DISPLAY is not set by ssh\n");
    }
    else
    {
        rv = atoi(d + 10);
        if (rv == 0)
        {
            fprintf(stderr, "Unable to get port number from DISPLAY\n");
        }
        else
        {
            rv += 6000;
        }
    }
    return rv;
}

static int
g_tcp_socket(void)
{
    int option;
    int sck = (int)socket(AF_INET, SOCK_STREAM, 0);
    if (sck >= 0)
    {
       option = 1;
       if (setsockopt(sck, SOL_SOCKET, SO_REUSEADDR, (char *)&option,
                      sizeof(option)) < 0)
            {
                perror("setsockopt");
                close(sck);
                sck = -1;
            }
 
    }
    else
    {
        perror("socket");
    }
    return sck;
}

static int
g_tcp_bind_address(int sck, unsigned short port, unsigned int addr)
{
    int rv;

    struct sockaddr_in s = {0};

    s.sin_family = AF_INET;
    s.sin_addr.s_addr = htonl(addr);
    s.sin_port = htons(port);
    rv = bind(sck, (struct sockaddr *)&s, sizeof(s));
    if (rv < 0)
    {
        char msg[64];
        char addrstr[32];
        const char *paddr;
        switch (addr)
        {
            case INADDR_ANY:
                paddr = "INADDR_ANY";
                break;
            default:
                snprintf(addrstr, sizeof(addrstr), "%08X", addr);
                paddr = addrstr;
        }
        snprintf(msg, sizeof(msg), "bind addr=%s port=%hu", paddr, port);
        perror(msg);
    }
    return rv; 
}

int main()
{
    unsigned short port = get_port();
    if (port > 0)
    {
        int sck = g_tcp_socket();
        if (sck >= 0)
        {
            if (g_tcp_bind_address(sck, port, INADDR_ANY) == 0)
            {
                printf("Bound successfully to port %hu on INADDR_ANY\n", port);
            }
            close(sck);
        }
    }
}

Log in to Linux or FreeBSD over ssh with X11 forwarding and run the program.

On Linux:-

$ ./temp
bind addr=INADDR_ANY port=6010: Address already in use

On FreeBSD:-

$ ./temp
Bound successfully to port 6010 on INADDR_ANY
@matt335672 matt335672 added the bug label Jan 6, 2025
@matt335672
Copy link
Member Author

@derekschrock - can you have a look at the above and tell me if I've missed anything out?

@matt335672
Copy link
Member Author

I've got another observation to add to this. Again, I'd appreciate your thoughts on this, Derek.

If you create an xrdp session with a DISPLAY of (say) :10, and then log in over ssh with -X to the same machine, you get a DISPLAY in the ssh session of localhost:10. As far as X11 is concerned, this is fine, but it might cause trouble with other system programs that wouldn't be expecting this behaviour.

@derekschrock
Copy link
Contributor

@derekschrock - can you have a look at the above and tell me if I've missed anything out?

It looks good in terms of the issue, you can also look at xauth list to see the xauth cookie is updated. This is what breaks X11 forwarding when the 2nd attempt is RDP. However your sesman logs shows it that you can't create an xrdp desktop post ssh connection. I don't seem to have that issue. In all cases I can ssh or rdp successfully no matter the order. The inability to rdp after an ssh when there's colliding display values might be a different issue. Any chance you're not using IPv6 as well as IPv4? Can you enable IPv6 (ifconfig_em0_ipv6="inet6 accept_rtadv") in /etc/rc.conf assuming your env. supports IPv6 with rtadv. Possible the example program needs to be changed to support IPv6 as well?

I've got another observation to add to this. Again, I'd appreciate your thoughts on this, Derek.

If you create an xrdp session with a DISPLAY of (say) :10, and then log in over ssh with -X to the same machine, you get a > DISPLAY in the ssh session of localhost:10. As far as X11 is concerned, this is fine, but it might cause trouble with other system programs that wouldn't be expecting this behaviour.

Yes, if you RDP first then ssh even though ssh is update the xauth data the xrdp doesn't seem to care. I'd guess that's because it's using the socket file[s]? However, I don't know what exactly might fail else where due to xauth changing. But is easy to back to a bad state by logging out of the RDP desktop and RDPing back in.

@matt335672
Copy link
Member Author

Thanks Derek,

It looks like the session not starting was down to other reasons related to an incomplete FreeBSD 14 development environment. I was also logging in as a separate user! I've fixed all that, and now I'm seeing what you're seeing. I've got an IPv6 GUA on this box, although I'm not using it to connect.

I've updated the problem description to match.

Also, the xauth file gets trampled on, regardless of which login I use first. So if I use rdp, then ssh, the file is changed. This is less of a problem as I think both ends of the ssh tunnel need to match, but this isn't a problem for an xrdp session.

Although X11 is quite happy with one display 10 using TCP and another using UDS, xauth isn't. The xauth file format does not allow for these two transports to be stored in the file together.

I'll summarise where I think we are, which is there are two problems here:-

  1. xrdp doesn't detect when an ssh session is using the same display number. This is bad, as xrdp overwrites the xauth file for the display, and the two ends of the ssh tunnel no longer match.
  2. ssh doesn't detect when xrdp is using the same display number. This is relatively benign, as overwriting the xauth file in this instance doesn't matter for a local xrdp session.

I've also tried -listen tcp for the X server. This is pretty frowned upon these days, but it's still a valid, if dangerous, use-case. In this case, the X server doesn't listen on TCP, but still runs. TCP connections don't work as the ssh tunnel is broken.

I think we need these changes:-

  1. xrdp on FreeBSD should avoid the ssh collision. This should be simple to do, and fixes the biggest problem. This can land in v0.10.x and devel.
  2. xrdp on all platforms should provide a way to have a private XAUTHORITY file. This is now best practice, and has been commented on in the past. This can land in devel for delivery in the next major version.

Does that sound reasonable, or am I missing something else?

@derekschrock
Copy link
Contributor

Looks good. Thanks for the detailed summary.

@matt335672
Copy link
Member Author

I'll proceed along those lines then.

We'll keep this issue open for the first of these, which is xrdp detecting an active X server. I'll raise a separate issue for XAUTHORITY.

@matt335672 matt335672 added the FreeBSD FreeBSD specific issue label Jan 8, 2025
@matt335672
Copy link
Member Author

I think I can see a way to sort this out.

The problem is SO_REUSEADDR which is being set. For the bind() to detect an X server on any interface, we need to bind with INADDR_ANY, and without SO_REUSEADDR.

SO_REUSEADDR only seems to be useful with TCP (over IPv4 or IPv6), and furthermore, it's only used by bind(). I think we only need to bind in two places for xrdp v0.10.x and later:-

  1. When setting up the listening socket(s) for xrdp.
  2. When performing the X server check.

At the moment we're setting SO_REUSEADDR for every socket we create. This seems to be a bit pointless. I propose removing this automatic setting and making it an explicit setting which we can use when creating listening sockets for xrdp only. That should work on all platforms, and improve the code quality.

@matt335672 matt335672 linked a pull request Jan 10, 2025 that will close this issue
@matt335672
Copy link
Member Author

@derekschrock - I've linked a draft PR to close this.

I'd be grateful if you could look at it. If you want a patch making against any previous xrdp version, let me know and I'll sort it out for you.

I've already found another minor problem in the socket code. I'll raise another issue for that!

@matt335672
Copy link
Member Author

Scrub the other issue - I'll look at the other problem as part of this PR. It won't substantially change the fix though.

@matt335672
Copy link
Member Author

Raised another issue #3391 for discussion.

@derekschrock
Copy link
Contributor

Testing #3390 on FreeBSD 14.1 in a jail and on a host we get the expected results where xrdp is not overlapping with any existing connections. So an existing ssh X11 tunnel on 10 xrdp connection uses 11.

...
[2025-01-11T19:08:21.113-0500] [INFO ] [x_server_running_check_ports(session_list.c:203)] Found X server running at 6010
...
[2025-01-11T19:08:21.116-0500] [INFO ] [start_x_server(session.c:515)] Starting X server on display 11: /usr/local/libexec/Xorg :11 -auth .Xauthority -config /opt/xrdp/etc/X11/xrdp/xorg.conf -modulepath /opt/xrdp/lib/xorg/modules/,/usr/local/lib/xorg/modules/ -noreset -nolisten tcp -logfile .xorgxrdp.%s.log
...

The issue with openssh reusing DISPLAY values still remains but I'd like to think that's an openssh issue (with FreeBSD)? There's no way to control non-xrdp sockets correct? If so I can check with opeensh to see if they're interested in fixing this. But I think think this would only be an issue if you were trying to run something X via ssh with env DISPLAY= ... or until xrdp has a private XAUTHORITY (#3383)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug FreeBSD FreeBSD specific issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants