120 lines
4.8 KiB
Go
120 lines
4.8 KiB
Go
// Package checker implements the Kerberos realm checker for happyDomain.
|
|
//
|
|
// Given a realm / DNS domain, the checker performs a sequence of anonymous
|
|
// probes (SRV layout, KDC reachability, AS-REQ exchange, enctype & clock-
|
|
// skew discovery) and optionally an authenticated round-trip. Results are
|
|
// stored as a single KerberosData observation and rendered as an HTML
|
|
// report.
|
|
package checker
|
|
|
|
import (
|
|
"time"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
)
|
|
|
|
// ObservationKeyKerberos is the observation key for Kerberos realm test data.
|
|
const ObservationKeyKerberos sdk.ObservationKey = "kerberos_realm"
|
|
|
|
// SRVRecord mirrors a *dns.SRV row we want to report on.
|
|
type SRVRecord struct {
|
|
Target string `json:"target"`
|
|
Port uint16 `json:"port"`
|
|
Priority uint16 `json:"priority"`
|
|
Weight uint16 `json:"weight"`
|
|
}
|
|
|
|
// SRVBucket is the resolution outcome for one SRV prefix.
|
|
type SRVBucket struct {
|
|
Prefix string `json:"prefix"`
|
|
Records []SRVRecord `json:"records,omitempty"`
|
|
// Error describes why the lookup failed. NXDOMAIN is reported as an
|
|
// empty slice + Error="" so the UI can tell "no records" from
|
|
// "lookup failed".
|
|
Error string `json:"error,omitempty"`
|
|
NXDomain bool `json:"nxdomain,omitempty"`
|
|
LookupName string `json:"lookupName"`
|
|
}
|
|
|
|
// HostResolution is the A/AAAA resolution outcome for a single SRV target.
|
|
type HostResolution struct {
|
|
Target string `json:"target"`
|
|
IPv4 []string `json:"ipv4,omitempty"`
|
|
IPv6 []string `json:"ipv6,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// KDCProbe captures the outcome of a single L4 probe.
|
|
type KDCProbe struct {
|
|
Target string `json:"target"`
|
|
Port uint16 `json:"port"`
|
|
Proto string `json:"proto"` // "tcp" | "udp"
|
|
Role string `json:"role"` // "kdc" | "kadmin" | "kpasswd" | "master"
|
|
OK bool `json:"ok"`
|
|
RTT time.Duration `json:"rtt_ns"`
|
|
Error string `json:"error,omitempty"`
|
|
KrbSeen bool `json:"krbSeen,omitempty"` // true when a KRB message was actually parsed
|
|
}
|
|
|
|
// EnctypeEntry describes one advertised enctype.
|
|
type EnctypeEntry struct {
|
|
ID int32 `json:"id"`
|
|
Name string `json:"name"`
|
|
Weak bool `json:"weak,omitempty"`
|
|
Salt string `json:"salt,omitempty"`
|
|
Source string `json:"source"` // "etype-info2" | "etype-info" | "pw-salt"
|
|
}
|
|
|
|
// ASProbeResult summarizes what the AS-REQ probe taught us.
|
|
type ASProbeResult struct {
|
|
Attempted bool `json:"attempted"`
|
|
Target string `json:"target,omitempty"`
|
|
Proto string `json:"proto,omitempty"`
|
|
ErrorCode int32 `json:"errorCode,omitempty"`
|
|
ErrorName string `json:"errorName,omitempty"`
|
|
ServerRealm string `json:"serverRealm,omitempty"`
|
|
ServerTime time.Time `json:"serverTime,omitempty"`
|
|
ClockSkew time.Duration `json:"clockSkew_ns,omitempty"`
|
|
Enctypes []EnctypeEntry `json:"enctypes,omitempty"`
|
|
PreauthReq bool `json:"preauthRequired"`
|
|
PKINITOffered bool `json:"pkinitOffered,omitempty"`
|
|
PrincipalFound bool `json:"principalFound,omitempty"` // KDC returned an AS-REP without preauth (rare / AS-REP roasting exposure)
|
|
Raw string `json:"raw,omitempty"` // informative: e.g. "KRB-ERROR (25) PREAUTH_REQUIRED"
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// AuthProbeResult is filled only when credentials were supplied.
|
|
type AuthProbeResult struct {
|
|
Attempted bool `json:"attempted"`
|
|
Principal string `json:"principal,omitempty"`
|
|
TGTAcquired bool `json:"tgtAcquired"`
|
|
TGSAcquired bool `json:"tgsAcquired"`
|
|
TargetService string `json:"targetService,omitempty"`
|
|
Latency time.Duration `json:"latency_ns,omitempty"`
|
|
ErrorCode int32 `json:"errorCode,omitempty"`
|
|
ErrorName string `json:"errorName,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// KerberosData is the full observation payload.
|
|
type KerberosData struct {
|
|
Realm string `json:"realm"`
|
|
CollectedAt time.Time `json:"collectedAt"`
|
|
LocalTime time.Time `json:"localTime"`
|
|
|
|
SRV []SRVBucket `json:"srv"`
|
|
Resolution map[string]HostResolution `json:"resolution,omitempty"`
|
|
Probes []KDCProbe `json:"probes,omitempty"`
|
|
AS ASProbeResult `json:"as"`
|
|
Auth *AuthProbeResult `json:"auth,omitempty"`
|
|
|
|
Enctypes []EnctypeEntry `json:"enctypes,omitempty"`
|
|
WeakEnctypes []EnctypeEntry `json:"weakEnctypes,omitempty"`
|
|
|
|
// OverallOK is the rule's summary verdict; set by the rule, not the
|
|
// collector. Stored here for the HTML report which is rendered from
|
|
// the observation alone.
|
|
OverallOK bool `json:"overallOK"`
|
|
Warnings []string `json:"warnings,omitempty"`
|
|
Errors []string `json:"errors,omitempty"`
|
|
}
|