Ubuntu · Desktop environment

Disable XDMCP in the display manager

When it fails: The display manager accepts XDMCP remote logins. XDMCP sends the login screen, passwords and the whole session unencrypted over the network.

CB-UBU-DESK-005highactiveautomatedautomated fixprofile desktop

As released in v0.1.0 (beta). Current state.

Description

XDMCP (X Display Manager Control Protocol) lets a remote X terminal request a login screen from the display manager. It is off by default in GDM ([xdmcp] Enable in /etc/gdm3/custom.conf) and in LightDM ([XDMCPServer] enabled). This control checks that nobody turned it on.

From Ubuntu 26.04 (GDM 50), GDM is built without XDMCP and the Ubuntu desktop session runs on Wayland only, so the GDM check applies to 20.04, 22.04 and 24.04. LightDM, used with desktops that still run on Xorg, is checked on every version.

Ubuntu's GDM reads /etc/gdm3/custom.conf, not daemon.conf as on Debian: a daemon.conf file is ignored. SDDM has no XDMCP server. The LightDM check reads /usr/share/lightdm/lightdm.conf.d/, /etc/xdg/lightdm/lightdm.conf.d/, /etc/lightdm/lightdm.conf.d/ and /etc/lightdm/lightdm.conf in that order; the last value wins. The automated fix covers GDM only; the LightDM check was written from its documentation and not tested in a lab.

Rationale

XDMCP and the X11 session it opens are not encrypted or authenticated: anyone on the network path can read the password typed at login and every keystroke after. Remote graphical access should go through an encrypted channel such as SSH or a VPN.

Impact

None with the defaults. Thin clients that rely on XDMCP stop working.

Setting

Location/etc/gdm3/custom.conf [xdmcp] Enable; LightDM [XDMCPServer] enabled
Defaultfalse
Recommendedfalse or unset

Audit

  1. GDM: in /etc/gdm3/custom.conf, the [xdmcp] section must not set Enable=true.
  2. LightDM: no file of /etc/lightdm/lightdm.conf.d/ or /etc/lightdm/lightdm.conf may set enabled=true in an [XDMCPServer] section.

Automated: every check must pass. Scanners report each check by its id.

gdm

GDM does not enable XDMCP.

script: |
  [ -f /etc/gdm3/custom.conf ] || { echo "no /etc/gdm3/custom.conf"; exit 0; }
  v=$(awk '/^[ \t]*\[/ { h = $0; gsub(/^[ \t]+|[ \t]+$/, "", h); s = (h == "[xdmcp]"); next }
    s && /^[ \t]*Enable[ \t]*=/ { v = $0; sub(/^[^=]*=[ \t]*/, "", v); sub(/[ \t]+$/, "", v) }
    END { print v }' /etc/gdm3/custom.conf)
  echo "[xdmcp] Enable=${v:-unset}"
  case $v in [Tt][Rr][Uu][Ee] | 1) exit 1 ;; esac

lightdm

LightDM does not enable its XDMCP server.

script: |
  set -- /usr/share/lightdm/lightdm.conf.d/*.conf /etc/xdg/lightdm/lightdm.conf.d/*.conf \
    /etc/lightdm/lightdm.conf.d/*.conf /etc/lightdm/lightdm.conf
  files=
  for f in "$@"; do [ -f "$f" ] && files="$files $f"; done
  [ -n "$files" ] || { echo "no LightDM configuration file"; exit 0; }
  # shellcheck disable=SC2086 # one word per file
  v=$(awk 'FNR == 1 { s = 0 } /^[ \t]*\[/ { h = $0; gsub(/^[ \t]+|[ \t]+$/, "", h); s = (h == "[XDMCPServer]"); next }
    s && /^[ \t]*enabled[ \t]*=/ { v = $0; sub(/^[^=]*=[ \t]*/, "", v); sub(/[ \t]+$/, "", v) }
    END { print v }' $files)
  echo "[XDMCPServer] enabled=${v:-unset}"
  case $v in [Tt][Rr][Uu][Ee] | 1) exit 1 ;; esac

Remediation

  • GDM: in /etc/gdm3/custom.conf, set Enable=false in the [xdmcp] section (or remove the line), then restart GDM at a convenient time (systemctl restart gdm closes every graphical session).
  • LightDM: remove enabled=true from the [XDMCPServer] sections, then restart LightDM.
Restartsystem reboot
Disruptionlow

Rollback

Restore /etc/gdm3/custom.conf from the backup directory.

gdm

# Turn off XDMCP in GDM. Only edits /etc/gdm3/custom.conf when XDMCP is enabled.
f=/etc/gdm3/custom.conf
[ -f "$f" ] || { echo "$f not found" >&2; exit 1; }

# ini_get SECTION KEY: last value of KEY in [SECTION] of custom.conf, empty when unset.
ini_get() {
  S="[$1]" K=$2 awk '
    BEGIN { s = ENVIRON["S"]; k = ENVIRON["K"] }
    /^[ \t]*\[/ { h = $0; gsub(/^[ \t]+|[ \t]+$/, "", h); in_s = (h == s); next }
    in_s && /=/ && !/^[ \t]*[#;]/ {
      key = substr($0, 1, index($0, "=") - 1); gsub(/^[ \t]+|[ \t]+$/, "", key)
      if (key == k) { v = substr($0, index($0, "=") + 1); gsub(/^[ \t]+|[ \t]+$/, "", v) }
    }
    END { print v }
  ' "$f"
}

# ini_set SECTION KEY VALUE: set KEY=VALUE right after the [SECTION] header, adding the section if missing.
ini_set() {
  S="[$1]" K=$2 V=$3 awk '
    BEGIN { s = ENVIRON["S"]; k = ENVIRON["K"]; v = ENVIRON["V"] }
    function out() { if (!done) { print k "=" v; done = 1 } }
    /^[ \t]*\[/ { print; h = $0; gsub(/^[ \t]+|[ \t]+$/, "", h); in_s = (h == s); if (in_s) out(); next }
    in_s && $0 ~ "^[ \t]*" k "[ \t]*=" { next }
    { print }
    END { if (!done) { print s; out() } }
  ' "$f" >"$f.cirsec-tmp" && cat "$f.cirsec-tmp" >"$f"
  rc=$?
  rm -f "$f.cirsec-tmp"
  return "$rc"
}

is_true() { case $1 in [Tt][Rr][Uu][Ee] | 1) return 0 ;; esac; return 1; }

is_true "$(ini_get xdmcp Enable)" || exit 0
cb_backup "$f"
ini_set xdmcp Enable false

Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.

References

Mappings

FrameworkIDRelationNote
NIST SP 800-53 Rev. 55.1.1CM-7related
NIST SP 800-53 Rev. 55.1.1SC-8related

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

People and lifecycle

Authors
Verified2026-10-19 by Sn0wAlice on 20.04, Proxmox VM with Ubuntu Desktop (GNOME 3.36, gdm3), Ubuntu 20.04.6, kernel 5.4.0-216-generic. GDM check and fix tested (desktop-hardened) in /etc/gdm3/custom.conf, including Enable=true in [xdmcp] turned off without touching [debug]; GDM starts after reboot. The LightDM check was not tested (no LightDM machine), only reported not applicable.
Verified2026-10-19 by Sn0wAlice on 22.04, Proxmox VM with Ubuntu Desktop (GNOME 42, gdm3), Ubuntu 22.04.5, kernel 5.15.0-198-generic. GDM check and fix tested (desktop-hardened) in /etc/gdm3/custom.conf, including Enable=true in [xdmcp] turned off without touching [debug]; GDM starts after reboot. The LightDM check was not tested (no LightDM machine), only reported not applicable.
Verified2026-10-19 by Sn0wAlice on 24.04, Proxmox VM with Ubuntu Desktop (GNOME 46, gdm3), Ubuntu 24.04.5, kernel 6.8.0-147-generic. GDM check and fix tested (desktop-hardened) in /etc/gdm3/custom.conf, including Enable=true in [xdmcp] turned off without touching [debug]; GDM starts after reboot. The LightDM check was not tested (no LightDM machine), only reported not applicable.
Verified2026-10-19 by Sn0wAlice on 26.04, Proxmox VM with Ubuntu Desktop (GNOME 50, gdm3), Ubuntu 26.04.1, kernel 7.0.0-38-generic. GDM 50 has no XDMCP: the GDM check and fix are reported not applicable. The LightDM check was not tested (no LightDM machine), only reported not applicable.
Introduced inv0.1.0
Created, updated2026-10-19, 2026-10-19
Translationsfr