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 |
| Default | false |
| Recommended | false or unset |
Audit
- GDM: in
/etc/gdm3/daemon.conf, the[xdmcp]section must not setEnable=true. - LightDM: no file of
/etc/lightdm/lightdm.conf.d/or/etc/lightdm/lightdm.confmay setenabled=truein 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, setEnable=falsein the[xdmcp]section (or remove the line), then restart GDM at a convenient time (systemctl restart gdmcloses every graphical session). - LightDM: remove
enabled=truefrom the[XDMCPServer]sections, then restart LightDM.
| Restart | system reboot |
| Disruption | low |
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
- gdm3(8), Debian manual pages Debian, vendor-doc, accessed 2026-10-18
- GDM - configuration keys (gdm.schemas) The GNOME Project, vendor-doc, accessed 2026-10-18
- Debian Wiki - LightDM Debian, vendor-doc, accessed 2026-10-18
Mappings
| Framework | ID | Relation |
|---|---|---|
| NIST SP 800-53 Rev. 55.1.1 | CM-7 | related |
| NIST SP 800-53 Rev. 55.1.1 | SC-8 | related |
How this control relates to the other framework's: equivalent, stricter, looser, partial or related.
Threats mitigated
People and lifecycle
| Authors |
|
| Verified | 2026-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. |
| Verified | 2026-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 in | v0.1.0 |
| Created, updated | 2026-10-18, 2026-10-18 |
| Translations | fr |