blob: 9dd69d2b3d64d01f25d6473545f7c3f22a0d2cbc [file] [log] [blame]
// Package pac implements Microsoft Privilege Attribute Certificate (PAC) processing.
package pac
import (
"bytes"
"fmt"
"gopkg.in/jcmturner/rpc.v1/mstypes"
"gopkg.in/jcmturner/rpc.v1/ndr"
)
// KERB_VALIDATION_INFO flags.
const (
USERFLAG_GUEST = 31 // Authentication was done via the GUEST account; no password was used.
USERFLAG_NO_ENCRYPTION_AVAILABLE = 30 // No encryption is available.
USERFLAG_LAN_MANAGER_KEY = 28 // LAN Manager key was used for authentication.
USERFLAG_SUB_AUTH = 25 // Sub-authentication used; session key came from the sub-authentication package.
USERFLAG_EXTRA_SIDS = 26 // Indicates that the ExtraSids field is populated and contains additional SIDs.
USERFLAG_MACHINE_ACCOUNT = 24 // Indicates that the account is a machine account.
USERFLAG_DC_NTLM2 = 23 // Indicates that the domain controller understands NTLMv2.
USERFLAG_RESOURCE_GROUPIDS = 22 // Indicates that the ResourceGroupIds field is populated.
USERFLAG_PROFILEPATH = 21 // Indicates that ProfilePath is populated.
USERFLAG_NTLM2_NTCHALLENGERESP = 20 // The NTLMv2 response from the NtChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and session key generation.
USERFLAG_LM2_LMCHALLENGERESP = 19 // The LMv2 response from the LmChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and session key generation.
USERFLAG_AUTH_LMCHALLENGERESP_KEY_NTCHALLENGERESP = 18 // The LMv2 response from the LmChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and the NTLMv2 response from the NtChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used session key generation.
)
// KerbValidationInfo implement https://msdn.microsoft.com/en-us/library/cc237948.aspx
// The KERB_VALIDATION_INFO structure defines the user's logon and authorization information
// provided by the DC. The KERB_VALIDATION_INFO structure is a subset of the
// NETLOGON_VALIDATION_SAM_INFO4 structure ([MS-NRPC] section 2.2.1.4.13).
// It is a subset due to historical reasons and to the use of the common Active Directory to generate this information.
// The KERB_VALIDATION_INFO structure is marshaled by RPC [MS-RPCE].
type KerbValidationInfo struct {
LogOnTime mstypes.FileTime
LogOffTime mstypes.FileTime
KickOffTime mstypes.FileTime
PasswordLastSet mstypes.FileTime
PasswordCanChange mstypes.FileTime
PasswordMustChange mstypes.FileTime
EffectiveName mstypes.RPCUnicodeString
FullName mstypes.RPCUnicodeString
LogonScript mstypes.RPCUnicodeString
ProfilePath mstypes.RPCUnicodeString
HomeDirectory mstypes.RPCUnicodeString
HomeDirectoryDrive mstypes.RPCUnicodeString
LogonCount uint16
BadPasswordCount uint16
UserID uint32
PrimaryGroupID uint32
GroupCount uint32
GroupIDs []mstypes.GroupMembership `ndr:"pointer,conformant"`
UserFlags uint32
UserSessionKey mstypes.UserSessionKey
LogonServer mstypes.RPCUnicodeString
LogonDomainName mstypes.RPCUnicodeString
LogonDomainID mstypes.RPCSID `ndr:"pointer"`
Reserved1 [2]uint32 // Has 2 elements
UserAccountControl uint32
SubAuthStatus uint32
LastSuccessfulILogon mstypes.FileTime
LastFailedILogon mstypes.FileTime
FailedILogonCount uint32
Reserved3 uint32
SIDCount uint32
ExtraSIDs []mstypes.KerbSidAndAttributes `ndr:"pointer,conformant"`
ResourceGroupDomainSID mstypes.RPCSID `ndr:"pointer"`
ResourceGroupCount uint32
ResourceGroupIDs []mstypes.GroupMembership `ndr:"pointer,conformant"`
}
// Unmarshal bytes into the DeviceInfo struct
func (k *KerbValidationInfo) Unmarshal(b []byte) (err error) {
dec := ndr.NewDecoder(bytes.NewReader(b))
err = dec.Decode(k)
if err != nil {
err = fmt.Errorf("error unmarshaling KerbValidationInfo: %v", err)
}
return
}
// GetGroupMembershipSIDs returns a slice of strings containing the group membership SIDs found in the PAC.
func (k *KerbValidationInfo) GetGroupMembershipSIDs() []string {
var g []string
lSID := k.LogonDomainID.String()
for i := range k.GroupIDs {
g = append(g, fmt.Sprintf("%s-%d", lSID, k.GroupIDs[i].RelativeID))
}
for _, s := range k.ExtraSIDs {
var exists = false
for _, es := range g {
if es == s.SID.String() {
exists = true
break
}
}
if !exists {
g = append(g, s.SID.String())
}
}
for _, r := range k.ResourceGroupIDs {
var exists = false
s := fmt.Sprintf("%s-%d", k.ResourceGroupDomainSID.String(), r.RelativeID)
for _, es := range g {
if es == s {
exists = true
break
}
}
if !exists {
g = append(g, s)
}
}
return g
}