Skip to content

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

@matt335672

Description

@matt335672

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    FreeBSDFreeBSD specific issuebug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions