// 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"` 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"` }