client: ValidateDomain accepts Windows reserved names used as path components #39

Open
opened 2026-05-20 21:51:02 +02:00 by heiko · 1 comment
Owner

Follow-up from #31 / PR #37, adjacent surface flagged during review of #37.

The client validates per-domain input with list.ValidateDomain at multiple sites:

  • cmd/cert-proxy-client/cert/cert.go:124 — NewReq before building local file paths
  • cmd/cert-proxy-client/init.go:130 — config parsing
  • cmd/cert-proxy-client/main.go:61 — CLI argument
  • cmd/cert-proxy-client/main.go:173 — domain list from server

The validated domain is then composed into a file path under basedir (e.g. basedir//cert.pem) via filepath.Join. On Windows this resolves to the OS namespace, so a domain like CON, NUL, or COM1 maps to a device, not a file.

ValidateDomain accepts CON because con is a legitimate DNS label (con.example.com is a real domain). PR #37 introduced list.ValidateClientName for the server side (cnList), which rejects reserved names; that helper is not appropriate for the client side because legitimate cert-proxy domains include wildcards (*.example.com) and ValidateClientName rejects *.

The actual exploitability

Low. A malicious upstream server feeding domain=CON via /v1/list would cause the client to attempt filesystem operations on the console device. Those operations would fail with platform-dependent errors, not silently overwrite anything. Worst case: a confusing crash. No file disclosure or privilege escalation.

The reason to fix it anyway: clients run as scheduled jobs (cert-proxy-client.timer), and a confusing crash from a half-trusted upstream is a bad failure mode. Better to refuse the input cleanly.

Possible fixes

Option A — Split ValidateDomain into orthogonal checks:

  • ValidateDomain (current behaviour, DNS-only)
  • IsFilesystemSafe(label) — rejects reserved names, applied per label of the domain
  • Client uses both. Wildcard labels (*) pass IsFilesystemSafe because the asterisk isn't a reserved name; the file-path expansion (cert.go's templates) can substitute or escape the * separately.

Option B — Add list.ValidateRemoteDomain that accepts wildcards but rejects reserved names. Use it in the client. Reuse logic shared with ValidateClientName via a helper.

Option C — Don't validate further; rely on Windows filesystem to reject the operation. Lowest-effort, but loses the "fail cleanly with a meaningful error" property.

Scope

Touches list/validate.go and four call sites in cmd/cert-proxy-client/. Does not touch the server.

  • #31 — the original CN traversal bug
  • #37 — server-side fix (introduces ValidateClientName)
  • #38 — server-side serve.go follow-up (URL-derived domain)

(co)authored by ai (claude-opus-4-7)

Follow-up from #31 / PR #37, adjacent surface flagged during review of #37. The client validates per-domain input with list.ValidateDomain at multiple sites: - cmd/cert-proxy-client/cert/cert.go:124 — NewReq before building local file paths - cmd/cert-proxy-client/init.go:130 — config parsing - cmd/cert-proxy-client/main.go:61 — CLI argument - cmd/cert-proxy-client/main.go:173 — domain list from server The validated domain is then composed into a file path under basedir (e.g. basedir/<domain>/cert.pem) via filepath.Join. On Windows this resolves to the OS namespace, so a domain like CON, NUL, or COM1 maps to a device, not a file. ValidateDomain accepts CON because con is a legitimate DNS label (con.example.com is a real domain). PR #37 introduced list.ValidateClientName for the *server* side (cnList), which rejects reserved names; that helper is not appropriate for the client side because legitimate cert-proxy domains include wildcards (*.example.com) and ValidateClientName rejects *. ## The actual exploitability Low. A malicious upstream server feeding domain=CON via /v1/list would cause the client to attempt filesystem operations on the console device. Those operations would fail with platform-dependent errors, not silently overwrite anything. Worst case: a confusing crash. No file disclosure or privilege escalation. The reason to fix it anyway: clients run as scheduled jobs (cert-proxy-client.timer), and a confusing crash from a half-trusted upstream is a bad failure mode. Better to refuse the input cleanly. ## Possible fixes Option A — Split ValidateDomain into orthogonal checks: - ValidateDomain (current behaviour, DNS-only) - IsFilesystemSafe(label) — rejects reserved names, applied per label of the domain - Client uses both. Wildcard labels (*) pass IsFilesystemSafe because the asterisk isn't a reserved name; the file-path expansion (cert.go's templates) can substitute or escape the * separately. Option B — Add list.ValidateRemoteDomain that accepts wildcards but rejects reserved names. Use it in the client. Reuse logic shared with ValidateClientName via a helper. Option C — Don't validate further; rely on Windows filesystem to reject the operation. Lowest-effort, but loses the "fail cleanly with a meaningful error" property. ## Scope Touches list/validate.go and four call sites in cmd/cert-proxy-client/. Does not touch the server. ## Related - #31 — the original CN traversal bug - #37 — server-side fix (introduces ValidateClientName) - #38 — server-side serve.go follow-up (URL-derived domain) (co)authored by ai (claude-opus-4-7)
Author
Owner

AI attribution comment added per repository instruction for this open issue.\n\n(co)authored by ai:gpt-5-codex

AI attribution comment added per repository instruction for this open issue.\n\n(co)authored by ai:gpt-5-codex
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
heiko/cert-proxy#39
No description provided.