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