86 lines
2.6 KiB
Go
86 lines
2.6 KiB
Go
package checker
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
tls "git.happydns.org/checker-tls/checker"
|
|
)
|
|
|
|
// usageCoherentRule flags TLSA records whose declared usage contradicts the
|
|
// chain slot their hash actually matches, typically a record published as
|
|
// usage 1 or 3 (end-entity) whose hash in fact matches an intermediate.
|
|
// That is almost always a publisher error: the intended usage was 0 or 2.
|
|
type usageCoherentRule struct{}
|
|
|
|
func (r *usageCoherentRule) Name() string { return "dane.usage_coherent" }
|
|
func (r *usageCoherentRule) Description() string {
|
|
return "Flags TLSA records whose declared usage does not match the chain slot they actually hash (e.g. usage 3 matching an intermediate)."
|
|
}
|
|
|
|
func (r *usageCoherentRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, _ sdk.CheckerOptions) []sdk.CheckState {
|
|
rc := loadRuleContext(ctx, obs)
|
|
if rc.err != nil {
|
|
return []sdk.CheckState{observationErrorState(rc.err)}
|
|
}
|
|
var out []sdk.CheckState
|
|
tested := 0
|
|
for _, t := range rc.data.Targets {
|
|
probe := rc.probes[t.Ref]
|
|
if !probeUsable(probe) || len(probe.Chain) < 2 {
|
|
continue
|
|
}
|
|
tested++
|
|
warn := suspiciousUsage(t, probe)
|
|
if warn != "" {
|
|
out = append(out, sdk.CheckState{
|
|
Status: sdk.StatusWarn,
|
|
Code: "dane_usage_incoherent",
|
|
Subject: targetSubject(t),
|
|
Message: warn,
|
|
Meta: targetMeta(t),
|
|
})
|
|
}
|
|
}
|
|
if len(out) == 0 {
|
|
if tested == 0 {
|
|
return []sdk.CheckState{{
|
|
Status: sdk.StatusUnknown,
|
|
Code: "dane_usage_coherent_skipped",
|
|
Message: "No multi-cert chain probed yet; cannot assess usage coherence.",
|
|
}}
|
|
}
|
|
return []sdk.CheckState{{
|
|
Status: sdk.StatusOK,
|
|
Code: "dane_usage_coherent_ok",
|
|
Message: "End-entity TLSA records match end-entity certificates on every probed chain.",
|
|
}}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// suspiciousUsage returns a human-readable hint when a record hash matches a
|
|
// chain slot that contradicts its declared usage (e.g. usage 3 whose hash
|
|
// actually matches the intermediate), almost always a publisher error. Used
|
|
// by both usageCoherentRule and the HTML report.
|
|
func suspiciousUsage(t TargetResult, p *tls.TLSProbe) string {
|
|
if p == nil || len(p.Chain) < 2 {
|
|
return ""
|
|
}
|
|
for _, r := range t.Records {
|
|
if r.Usage != UsageDANEEE && r.Usage != UsagePKIXEE {
|
|
continue
|
|
}
|
|
for _, c := range p.Chain[1:] {
|
|
cand, err := recordCandidate(r, c)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if strings.EqualFold(cand, r.Certificate) {
|
|
return "A record declared with usage 1/3 (end-entity) actually matches an intermediate certificate. It should probably use usage 0 or 2 (trust-anchor) instead."
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|