Debian GNU/Linux · 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-DEB-DESK-005highactiveautomatedautomated fixprofile desktop

As released in v0.1.1 beta · pre-release. 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/daemon.conf) and in LightDM ([XDMCPServer] enabled). This control checks that nobody turned it on.

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/daemon.conf [xdmcp] Enable; LightDM [XDMCPServer] enabled
Defaultfalse
Recommendedfalse or unset

Audit

  1. GDM: in /etc/gdm3/daemon.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/daemon.conf ] || { echo "no /etc/gdm3/daemon.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/daemon.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/daemon.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/daemon.conf from the backup directory.

gdm

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

# ini_get SECTION KEY: last value of KEY in [SECTION] of daemon.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

FrameworkIDRelation
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-18 by Sn0wAlice on 12, Proxmox VM with minimal GNOME (gdm3, gnome-shell), Debian 12 genericcloud image, kernel 6.1.0-54-cloud-amd64. GDM check and fix tested (desktop-hardened), including Enable=true in [xdmcp] fixed without touching [debug]; the LightDM check was not tested (no LightDM machine), only reported not applicable.
Verified2026-10-18 by Sn0wAlice on 13, Proxmox VM with minimal GNOME (gdm3, gnome-shell), Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64. GDM check and fix tested (desktop-hardened), including Enable=true in [xdmcp] fixed without touching [debug]; the LightDM check was not tested (no LightDM machine), only reported not applicable.
Introduced inv0.1.0
Created, updated2026-10-18, 2026-10-18
Translationsfr