//go:build linux
// +build linux

// Copyright Microsoft <wastore@microsoft.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package sddl

import (
	"encoding/binary"
	"fmt"
	"math"
	"strconv"
	"unsafe"

	"github.com/Azure/azure-storage-azcopy/v10/common"

	"github.com/pkg/xattr"
)

/*
 * Following constants are used by various Windows functions that deal with SECURITY_DESCRIPTORs and SIDs.
 * Most of these constants are originally defined in winnt.h
 */

/*
 * Valid/supported revision numbers for various object types.
 *
 * TODO: Do we need to support ACL_REVISION_DS (4) with support for Object ACEs?
 *       Are they used for filesystem objects?
 */
const (
	SDDL_REVISION   = 1 // SDDL Revision MUST always be 1.
	SID_REVISION    = 1 // SID Revision MUST always be 1.
	ACL_REVISION    = 2 // ACL revision for support basic ACE type used for filesystem ACLs.
	ACL_REVISION_DS = 4 // ACL revision for supporting stuff like Object ACE. This should ideally not be used with the ACE
	// types we support, but I've seen some objects like that.
)

type SECURITY_INFORMATION uint32

// Valid bitmasks contained in type SECURITY_INFORMATION.
const (
	OWNER_SECURITY_INFORMATION            = 0x00000001
	GROUP_SECURITY_INFORMATION            = 0x00000002
	DACL_SECURITY_INFORMATION             = 0x00000004
	SACL_SECURITY_INFORMATION             = 0x00000008
	LABEL_SECURITY_INFORMATION            = 0x00000010
	ATTRIBUTE_SECURITY_INFORMATION        = 0x00000020
	SCOPE_SECURITY_INFORMATION            = 0x00000040
	BACKUP_SECURITY_INFORMATION           = 0x00010000
	PROTECTED_DACL_SECURITY_INFORMATION   = 0x80000000
	PROTECTED_SACL_SECURITY_INFORMATION   = 0x40000000
	UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000
	UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000
)

// Valid bitmasks contained in type SECURITY_DESCRIPTOR_CONTROL.
const (
	SE_OWNER_DEFAULTED       = 0x0001
	SE_GROUP_DEFAULTED       = 0x0002
	SE_DACL_PRESENT          = 0x0004
	SE_DACL_DEFAULTED        = 0x0008
	SE_SACL_PRESENT          = 0x0010
	SE_SACL_DEFAULTED        = 0x0020
	SE_DACL_AUTO_INHERIT_REQ = 0x0100
	SE_SACL_AUTO_INHERIT_REQ = 0x0200
	SE_DACL_AUTO_INHERITED   = 0x0400
	SE_SACL_AUTO_INHERITED   = 0x0800
	SE_DACL_PROTECTED        = 0x1000
	SE_SACL_PROTECTED        = 0x2000
	SE_RM_CONTROL_VALID      = 0x4000
	SE_SELF_RELATIVE         = 0x8000
)

// Valid AceType values present in ACE_HEADER.
const (
	ACCESS_MIN_MS_ACE_TYPE                  = 0x0
	ACCESS_ALLOWED_ACE_TYPE                 = 0x0
	ACCESS_DENIED_ACE_TYPE                  = 0x1
	SYSTEM_AUDIT_ACE_TYPE                   = 0x2
	SYSTEM_ALARM_ACE_TYPE                   = 0x3
	ACCESS_MAX_MS_V2_ACE_TYPE               = 0x3
	ACCESS_ALLOWED_COMPOUND_ACE_TYPE        = 0x4
	ACCESS_MAX_MS_V3_ACE_TYPE               = 0x4
	ACCESS_MIN_MS_OBJECT_ACE_TYPE           = 0x5
	ACCESS_ALLOWED_OBJECT_ACE_TYPE          = 0x5
	ACCESS_DENIED_OBJECT_ACE_TYPE           = 0x6
	SYSTEM_AUDIT_OBJECT_ACE_TYPE            = 0x7
	SYSTEM_ALARM_OBJECT_ACE_TYPE            = 0x8
	ACCESS_MAX_MS_OBJECT_ACE_TYPE           = 0x8
	ACCESS_MAX_MS_V4_ACE_TYPE               = 0x8
	ACCESS_MAX_MS_ACE_TYPE                  = 0x8
	ACCESS_ALLOWED_CALLBACK_ACE_TYPE        = 0x9
	ACCESS_DENIED_CALLBACK_ACE_TYPE         = 0xA
	ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE = 0xB
	ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE  = 0xC
	SYSTEM_AUDIT_CALLBACK_ACE_TYPE          = 0xD
	SYSTEM_ALARM_CALLBACK_ACE_TYPE          = 0xE
	SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE   = 0xF
	SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE   = 0x10
	SYSTEM_MANDATORY_LABEL_ACE_TYPE         = 0x11
	SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE      = 0x12
	SYSTEM_SCOPED_POLICY_ID_ACE_TYPE        = 0x13
	SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE     = 0x14
	SYSTEM_ACCESS_FILTER_ACE_TYPE           = 0x15
	ACCESS_MAX_MS_V5_ACE_TYPE               = 0x15
)

var aceTypeStringMap = map[string]BYTE{
	"A":  ACCESS_ALLOWED_ACE_TYPE,
	"D":  ACCESS_DENIED_ACE_TYPE,
	"OA": ACCESS_ALLOWED_OBJECT_ACE_TYPE,
	"OD": ACCESS_DENIED_OBJECT_ACE_TYPE,
	"AU": SYSTEM_AUDIT_ACE_TYPE,
	"AL": SYSTEM_ALARM_ACE_TYPE,
	"OU": SYSTEM_AUDIT_OBJECT_ACE_TYPE,
	"OL": SYSTEM_ALARM_OBJECT_ACE_TYPE,
	"ML": SYSTEM_MANDATORY_LABEL_ACE_TYPE,
	"XA": ACCESS_ALLOWED_CALLBACK_ACE_TYPE,
	"XD": ACCESS_DENIED_CALLBACK_ACE_TYPE,
	"RA": SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE,
	"SP": SYSTEM_SCOPED_POLICY_ID_ACE_TYPE,
	"XU": SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE,
	"ZA": ACCESS_ALLOWED_CALLBACK_ACE_TYPE,
	"TL": SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE,
	"FL": SYSTEM_ACCESS_FILTER_ACE_TYPE,
}

// Valid bitmasks contained in AceFlags present in ACE_HEADER.
const (
	OBJECT_INHERIT_ACE       = 0x01
	CONTAINER_INHERIT_ACE    = 0x02
	NO_PROPAGATE_INHERIT_ACE = 0x04
	INHERIT_ONLY_ACE         = 0x08
	INHERITED_ACE            = 0x10
	VALID_INHERIT_FLAGS      = 0x1F
	CRITICAL_ACE_FLAG        = 0x20

	// AceFlags mask for what events we (should) audit. Used by SACL.
	SUCCESSFUL_ACCESS_ACE_FLAG = 0x40
	FAILED_ACCESS_ACE_FLAG     = 0x80

	TRUST_PROTECTED_FILTER_ACE_FLAG = 0x40
)

// Valid bitmasks contained in AccessMask present in type ACCESS_ALLOWED_ACE.
const (
	// Generic access rights.
	GENERIC_READ             = 0x80000000
	GENERIC_WRITE            = 0x40000000
	GENERIC_EXECUTE          = 0x20000000
	GENERIC_ALL              = 0x10000000
	DELETE                   = 0x00010000
	READ_CONTROL             = 0x00020000
	WRITE_DAC                = 0x00040000
	WRITE_OWNER              = 0x00080000
	SYNCHRONIZE              = 0x00100000
	STANDARD_RIGHTS_REQUIRED = 0x000F0000
	STANDARD_RIGHTS_READ     = READ_CONTROL
	STANDARD_RIGHTS_WRITE    = READ_CONTROL
	STANDARD_RIGHTS_EXECUTE  = READ_CONTROL
	STANDARD_RIGHTS_ALL      = 0x001F0000
	SPECIFIC_RIGHTS_ALL      = 0x0000FFFF

	// Access rights for files and directories.
	FILE_READ_DATA        = 0x0001 /* file & pipe */
	FILE_READ_ATTRIBUTES  = 0x0080 /* all */
	FILE_READ_EA          = 0x0008 /* file & directory */
	FILE_WRITE_DATA       = 0x0002 /* file & pipe */
	FILE_WRITE_ATTRIBUTES = 0x0100 /* all */
	FILE_WRITE_EA         = 0x0010 /* file & directory */
	FILE_APPEND_DATA      = 0x0004 /* file */
	FILE_EXECUTE          = 0x0020 /* file */

	FILE_ALL_ACCESS      = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
	FILE_GENERIC_READ    = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE)
	FILE_GENERIC_WRITE   = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE)
	FILE_GENERIC_EXECUTE = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE)

	// Access rights for DS objects.
	ADS_RIGHT_DS_CREATE_CHILD   = 0x0001
	ADS_RIGHT_DS_DELETE_CHILD   = 0x0002
	ADS_RIGHT_ACTRL_DS_LIST     = 0x0004
	ADS_RIGHT_DS_SELF           = 0x0008
	ADS_RIGHT_DS_READ_PROP      = 0x0010
	ADS_RIGHT_DS_WRITE_PROP     = 0x0020
	ADS_RIGHT_DS_DELETE_TREE    = 0x0040
	ADS_RIGHT_DS_LIST_OBJECT    = 0x0080
	ADS_RIGHT_DS_CONTROL_ACCESS = 0x0100

	// Registry Specific Access Rights.
	KEY_QUERY_VALUE        = 0x0001
	KEY_SET_VALUE          = 0x0002
	KEY_CREATE_SUB_KEY     = 0x0004
	KEY_ENUMERATE_SUB_KEYS = 0x0008
	KEY_NOTIFY             = 0x0010
	KEY_CREATE_LINK        = 0x0020
	KEY_WOW64_32KEY        = 0x0200
	KEY_WOW64_64KEY        = 0x0100
	KEY_WOW64_RES          = 0x0300

	KEY_READ       = ((STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) & (^SYNCHRONIZE))
	KEY_WRITE      = ((STANDARD_RIGHTS_WRITE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY) & (^SYNCHRONIZE))
	KEY_EXECUTE    = ((KEY_READ) & (^SYNCHRONIZE))
	KEY_ALL_ACCESS = ((STANDARD_RIGHTS_ALL | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK) & (^SYNCHRONIZE))

	// SYSTEM_ACCESS_FILTER_ACE Access rights.
	SYSTEM_MANDATORY_LABEL_NO_WRITE_UP   = 0x1
	SYSTEM_MANDATORY_LABEL_NO_READ_UP    = 0x2
	SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP = 0x4
)

// Access mask exactly matching the value here will be mapped to the key.
var aceStringToRightsMap = map[string]uint32{
	"GA": GENERIC_ALL,
	"GR": GENERIC_READ,
	"GW": GENERIC_WRITE,
	"GX": GENERIC_EXECUTE,

	"RC": READ_CONTROL,
	"SD": DELETE,
	"WD": WRITE_DAC,
	"WO": WRITE_OWNER,

	"RP": ADS_RIGHT_DS_READ_PROP,
	"WP": ADS_RIGHT_DS_WRITE_PROP,
	"CC": ADS_RIGHT_DS_CREATE_CHILD,
	"DC": ADS_RIGHT_DS_DELETE_CHILD,
	"LC": ADS_RIGHT_ACTRL_DS_LIST,
	"SW": ADS_RIGHT_DS_SELF,
	"LO": ADS_RIGHT_DS_LIST_OBJECT,
	"DT": ADS_RIGHT_DS_DELETE_TREE,
	"CR": ADS_RIGHT_DS_CONTROL_ACCESS,

	"FA": FILE_ALL_ACCESS,
	"FR": FILE_GENERIC_READ,
	"FW": FILE_GENERIC_WRITE,
	"FX": FILE_GENERIC_EXECUTE,

	"KA": KEY_ALL_ACCESS,
	"KR": KEY_READ,
	"KW": KEY_WRITE,
	"KX": KEY_EXECUTE,

	"NR": SYSTEM_MANDATORY_LABEL_NO_READ_UP,
	"NW": SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,
	"NX": SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP,
}

// Access rights to their corresponding friendly names.
// Note that this intentionally has some of the fields left out from aceStringToRightsMap.
var aceRightsToStringMap = map[uint32]string{
	GENERIC_ALL:                 "GA",
	GENERIC_READ:                "GR",
	GENERIC_WRITE:               "GW",
	GENERIC_EXECUTE:             "GX",
	READ_CONTROL:                "RC",
	DELETE:                      "SD",
	WRITE_DAC:                   "WD",
	WRITE_OWNER:                 "WO",
	ADS_RIGHT_DS_READ_PROP:      "RP",
	ADS_RIGHT_DS_WRITE_PROP:     "WP",
	ADS_RIGHT_DS_CREATE_CHILD:   "CC",
	ADS_RIGHT_DS_DELETE_CHILD:   "DC",
	ADS_RIGHT_ACTRL_DS_LIST:     "LC",
	ADS_RIGHT_DS_SELF:           "SW",
	ADS_RIGHT_DS_LIST_OBJECT:    "LO",
	ADS_RIGHT_DS_DELETE_TREE:    "DT",
	ADS_RIGHT_DS_CONTROL_ACCESS: "CR",
}

var (
	SECURITY_NULL_SID_AUTHORITY         = [6]byte{0, 0, 0, 0, 0, 0}
	SECURITY_WORLD_SID_AUTHORITY        = [6]byte{0, 0, 0, 0, 0, 1}
	SECURITY_LOCAL_SID_AUTHORITY        = [6]byte{0, 0, 0, 0, 0, 2}
	SECURITY_CREATOR_SID_AUTHORITY      = [6]byte{0, 0, 0, 0, 0, 3}
	SECURITY_NON_UNIQUE_AUTHORITY       = [6]byte{0, 0, 0, 0, 0, 4}
	SECURITY_NT_AUTHORITY               = [6]byte{0, 0, 0, 0, 0, 5}
	SECURITY_APP_PACKAGE_AUTHORITY      = [6]byte{0, 0, 0, 0, 0, 15}
	SECURITY_MANDATORY_LABEL_AUTHORITY  = [6]byte{0, 0, 0, 0, 0, 16}
	SECURITY_SCOPED_POLICY_ID_AUTHORITY = [6]byte{0, 0, 0, 0, 0, 17}
	SECURITY_AUTHENTICATION_AUTHORITY   = [6]byte{0, 0, 0, 0, 0, 18}
)

const (
	SECURITY_NULL_RID                   = 0
	SECURITY_WORLD_RID                  = 0
	SECURITY_LOCAL_RID                  = 0
	SECURITY_CREATOR_OWNER_RID          = 0
	SECURITY_CREATOR_GROUP_RID          = 1
	SECURITY_DIALUP_RID                 = 1
	SECURITY_NETWORK_RID                = 2
	SECURITY_BATCH_RID                  = 3
	SECURITY_INTERACTIVE_RID            = 4
	SECURITY_LOGON_IDS_RID              = 5
	SECURITY_SERVICE_RID                = 6
	SECURITY_LOCAL_SYSTEM_RID           = 18
	SECURITY_BUILTIN_DOMAIN_RID         = 32
	SECURITY_PRINCIPAL_SELF_RID         = 10
	SECURITY_CREATOR_OWNER_SERVER_RID   = 0x2
	SECURITY_CREATOR_GROUP_SERVER_RID   = 0x3
	SECURITY_LOGON_IDS_RID_COUNT        = 0x3
	SECURITY_ANONYMOUS_LOGON_RID        = 0x7
	SECURITY_PROXY_RID                  = 0x8
	SECURITY_ENTERPRISE_CONTROLLERS_RID = 0x9
	SECURITY_SERVER_LOGON_RID           = SECURITY_ENTERPRISE_CONTROLLERS_RID
	SECURITY_AUTHENTICATED_USER_RID     = 0xb
	SECURITY_RESTRICTED_CODE_RID        = 0xc
	SECURITY_NT_NON_UNIQUE_RID          = 0x15

	SECURITY_CREATOR_OWNER_RIGHTS_RID  = 0x00000004
	SECURITY_LOCAL_SERVICE_RID         = 0x00000013
	SECURITY_NETWORK_SERVICE_RID       = 0x00000014
	SECURITY_WRITE_RESTRICTED_CODE_RID = 0x00000021

	SECURITY_MANDATORY_LOW_RID         = 0x00001000
	SECURITY_MANDATORY_MEDIUM_RID      = 0x00002000
	SECURITY_MANDATORY_MEDIUM_PLUS_RID = (SECURITY_MANDATORY_MEDIUM_RID + 0x100)
	SECURITY_MANDATORY_HIGH_RID        = 0x00003000
	SECURITY_MANDATORY_SYSTEM_RID      = 0x00004000

	SECURITY_APP_PACKAGE_BASE_RID        = 0x00000002
	SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE = 0x00000001
)

// Predefined domain-relative RIDs for local groups.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa379649(v=vs.85).aspx
const (
	DOMAIN_ALIAS_RID_ADMINS                         = 0x220
	DOMAIN_ALIAS_RID_USERS                          = 0x221
	DOMAIN_ALIAS_RID_GUESTS                         = 0x222
	DOMAIN_ALIAS_RID_POWER_USERS                    = 0x223
	DOMAIN_ALIAS_RID_ACCOUNT_OPS                    = 0x224
	DOMAIN_ALIAS_RID_SYSTEM_OPS                     = 0x225
	DOMAIN_ALIAS_RID_PRINT_OPS                      = 0x226
	DOMAIN_ALIAS_RID_BACKUP_OPS                     = 0x227
	DOMAIN_ALIAS_RID_REPLICATOR                     = 0x228
	DOMAIN_ALIAS_RID_RAS_SERVERS                    = 0x229
	DOMAIN_ALIAS_RID_PREW2KCOMPACCESS               = 0x22A
	DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS           = 0x22B
	DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS      = 0x22C
	DOMAIN_ALIAS_RID_INCOMING_FOREST_TRUST_BUILDERS = 0x22D
	DOMAIN_ALIAS_RID_MONITORING_USERS               = 0x22E
	DOMAIN_ALIAS_RID_LOGGING_USERS                  = 0x22F
	DOMAIN_ALIAS_RID_AUTHORIZATIONACCESS            = 0x230
	DOMAIN_ALIAS_RID_TS_LICENSE_SERVERS             = 0x231
	DOMAIN_ALIAS_RID_DCOM_USERS                     = 0x232
	DOMAIN_ALIAS_RID_IUSERS                         = 0x238
	DOMAIN_ALIAS_RID_CRYPTO_OPERATORS               = 0x239
	DOMAIN_ALIAS_RID_CACHEABLE_PRINCIPALS_GROUP     = 0x23B
	DOMAIN_ALIAS_RID_NON_CACHEABLE_PRINCIPALS_GROUP = 0x23C
	DOMAIN_ALIAS_RID_EVENT_LOG_READERS_GROUP        = 0x23D
	DOMAIN_ALIAS_RID_CERTSVC_DCOM_ACCESS_GROUP      = 0x23E
	DOMAIN_ALIAS_RID_RDS_REMOTE_ACCESS_SERVERS      = 0x23F
	DOMAIN_ALIAS_RID_RDS_ENDPOINT_SERVERS           = 0x240
	DOMAIN_ALIAS_RID_RDS_MANAGEMENT_SERVERS         = 0x241
	DOMAIN_ALIAS_RID_HYPER_V_ADMINS                 = 0x242
	DOMAIN_ALIAS_RID_ACCESS_CONTROL_ASSISTANCE_OPS  = 0x243
	DOMAIN_ALIAS_RID_REMOTE_MANAGEMENT_USERS        = 0x244
	DOMAIN_ALIAS_RID_DEFAULT_ACCOUNT                = 0x245
	DOMAIN_ALIAS_RID_STORAGE_REPLICA_ADMINS         = 0x246
	DOMAIN_ALIAS_RID_DEVICE_OWNERS                  = 0x247
)

const (
	DOMAIN_GROUP_RID_ENTERPRISE_READONLY_DOMAIN_CONTROLLERS = 0x1F2 // 498
	DOMAIN_USER_RID_ADMIN                                   = 0x1F4 // 500
	DOMAIN_USER_RID_GUEST                                   = 0x1F5
	DOMAIN_GROUP_RID_ADMINS                                 = 0x200 // 512
	DOMAIN_GROUP_RID_USERS                                  = 0x201
	DOMAIN_GROUP_RID_GUESTS                                 = 0x202
	DOMAIN_GROUP_RID_COMPUTERS                              = 0x203
	DOMAIN_GROUP_RID_CONTROLLERS                            = 0x204
	DOMAIN_GROUP_RID_CERT_ADMINS                            = 0x205
	DOMAIN_GROUP_RID_SCHEMA_ADMINS                          = 0x206
	DOMAIN_GROUP_RID_ENTERPRISE_ADMINS                      = 0x207
	DOMAIN_GROUP_RID_POLICY_ADMINS                          = 0x208
	DOMAIN_GROUP_RID_READONLY_CONTROLLERS                   = 0x209
	DOMAIN_GROUP_RID_CLONEABLE_CONTROLLERS                  = 0x20A
	DOMAIN_GROUP_RID_CDC_RESERVED                           = 0x20C
	DOMAIN_GROUP_RID_PROTECTED_USERS                        = 0x20D
	DOMAIN_GROUP_RID_KEY_ADMINS                             = 0x20E
	DOMAIN_GROUP_RID_ENTERPRISE_KEY_ADMINS                  = 0x20F
)

const (
	SECURITY_AUTHENTICATION_AUTHORITY_ASSERTED_RID       = 0x1
	SECURITY_AUTHENTICATION_SERVICE_ASSERTED_RID         = 0x2
	SECURITY_AUTHENTICATION_FRESH_KEY_AUTH_RID           = 0x3
	SECURITY_AUTHENTICATION_KEY_TRUST_RID                = 0x4
	SECURITY_AUTHENTICATION_KEY_PROPERTY_MFA_RID         = 0x5
	SECURITY_AUTHENTICATION_KEY_PROPERTY_ATTESTATION_RID = 0x6
)

/*
 * Define some Windows type names for increased readability of various Windows structs we use here.
 */
type BYTE byte
type WORD uint16
type DWORD uint32

/****************************************************************************
 * Various binary structures used for conveying SMB objects.
 *
 * ALL MULTI-BYTE VALUES ARE IN LITTLE ENDIAN FORMAT.
 *
 * We don't use these structures in the code but they are there to help reader
 * understand the code.
 ****************************************************************************/

/*
 * This is NT Security Descriptor in "Self Relative" format.
 * This is returned when common.CIFS_XATTR_CIFS_NTSD xattr is queried for a file.
 * The Linux equivalent struct is "struct cifs_ntsd".
 */
type SECURITY_DESCRIPTOR_CONTROL WORD
type SECURITY_DESCRIPTOR_RELATIVE struct {
	// Revision number of this SECURITY_DESCRIPTOR. Must be 1.
	Revision BYTE
	// Zero byte.
	Sbz1 BYTE
	// Flag bits describing this SECURITY_DESCRIPTOR.
	Control SECURITY_DESCRIPTOR_CONTROL
	// Offset of owner sid. There's a SID structure at this offset.
	OffsetOwner DWORD
	// Offset of primary group sid. There's a SID structure at this offset.
	OffsetGroup DWORD
	// Offset of SACL. There's an ACL structure at this offset.
	OffsetSacl DWORD
	// Offset of DACL. There's an ACL structure at this offset.
	OffsetDacl DWORD
	// 0 or more bytes (depending on the various offsets above) follow this structure.
	Data [0]BYTE
}

// Maximum sub authority values present in a SID.
const SID_MAX_SUB_AUTHORITIES = 15

/*
 * SID structure.
 * The Linux equivalent struct is "struct cifs_sid".
 */
type SID struct {
	Revision BYTE
	// How many DWORD SubAuthority values? Cannot be 0, max possible value is SID_MAX_SUB_AUTHORITIES.
	SubAuthorityCount BYTE
	// IdentifierAuthority is in big endian format.
	IdentifierAuthority [6]BYTE
	// SubAuthorityCount SubAuthority DWORDs.
	SubAuthority [1]DWORD
}

/*
 * Header at the beginning of every ACE.
 */
type ACE_HEADER struct {
	AceType  BYTE
	AceFlags BYTE
	AceSize  WORD
}

/*
 * Single ACE (Access Check Entry).
 * One or more of these are contained in ACL.
 * The Linux equivalent struct is "struct cifs_ace".
 */
type ACCESS_ALLOWED_ACE struct {
	Header ACE_HEADER
	// What permissions is this ACE controlling?
	AccessMask DWORD
	// SID to which these permissions apply.
	Sid SID
}

/*
 * Binary ACL format. Used for both DACL and SACL.
 * The Linux equivalent struct is "struct cifs_acl".
 */
type ACL struct {
	AclRevision BYTE
	Sbz1        BYTE
	AclSize     WORD
	AceCount    WORD
	Sbz2        WORD
}

type AnySID struct {
	Revision            byte
	SubAuthorityCount   byte
	IdentifierAuthority [6]byte
	SubAuthority        []uint32
}

// TODO: Validate completeness/correctness.
var wellKnownSidShortcuts = map[string]AnySID{
	"WD": {SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY, []uint32{SECURITY_NULL_RID}},

	"CO": {SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, []uint32{SECURITY_CREATOR_OWNER_RID}},
	"CG": {SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, []uint32{SECURITY_CREATOR_GROUP_RID}},
	"OW": {SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, []uint32{SECURITY_CREATOR_OWNER_RIGHTS_RID}},

	"NU": {SID_REVISION, 1, SECURITY_NT_AUTHORITY, []uint32{SECURITY_NETWORK_RID}},
	"IU": {SID_REVISION, 1, SECURITY_NT_AUTHORITY, []uint32{SECURITY_INTERACTIVE_RID}},
	"SU": {SID_REVISION, 1, SECURITY_NT_AUTHORITY, []uint32{SECURITY_SERVICE_RID}},
	"AN": {SID_REVISION, 1, SECURITY_NT_AUTHORITY, []uint32{SECURITY_ANONYMOUS_LOGON_RID}},
	"ED": {SID_REVISION, 1, SECURITY_NT_AUTHORITY, []uint32{SECURITY_ENTERPRISE_CONTROLLERS_RID}},
	"PS": {SID_REVISION, 1, SECURITY_NT_AUTHORITY, []uint32{SECURITY_PRINCIPAL_SELF_RID}},
	"AU": {SID_REVISION, 1, SECURITY_NT_AUTHORITY, []uint32{SECURITY_AUTHENTICATED_USER_RID}},
	"RC": {SID_REVISION, 1, SECURITY_NT_AUTHORITY, []uint32{SECURITY_RESTRICTED_CODE_RID}},
	"SY": {SID_REVISION, 1, SECURITY_NT_AUTHORITY, []uint32{SECURITY_LOCAL_SYSTEM_RID}},
	"LS": {SID_REVISION, 1, SECURITY_NT_AUTHORITY, []uint32{SECURITY_LOCAL_SERVICE_RID}},
	"NS": {SID_REVISION, 1, SECURITY_NT_AUTHORITY, []uint32{SECURITY_NETWORK_SERVICE_RID}},
	"WR": {SID_REVISION, 1, SECURITY_NT_AUTHORITY, []uint32{SECURITY_WRITE_RESTRICTED_CODE_RID}},

	"BA": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS}},
	"BU": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS}},
	"BG": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS}},
	"PU": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS}},
	"AO": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ACCOUNT_OPS}},
	"SO": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_SYSTEM_OPS}},
	"PO": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_PRINT_OPS}},
	"BO": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_BACKUP_OPS}},
	"RE": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_REPLICATOR}},
	"RU": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_PREW2KCOMPACCESS}},
	"RD": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS}},
	"NO": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS}},

	"MU": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_MONITORING_USERS}},
	"LU": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_LOGGING_USERS}},
	"IS": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_IUSERS}},
	"CY": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_CRYPTO_OPERATORS}},
	"ER": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_EVENT_LOG_READERS_GROUP}},
	"CD": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_CERTSVC_DCOM_ACCESS_GROUP}},
	"RA": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_RDS_REMOTE_ACCESS_SERVERS}},
	"ES": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_RDS_ENDPOINT_SERVERS}},
	"MS": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_RDS_MANAGEMENT_SERVERS}},
	"HA": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_HYPER_V_ADMINS}},
	"AA": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ACCESS_CONTROL_ASSISTANCE_OPS}},
	"RM": {SID_REVISION, 2, SECURITY_NT_AUTHORITY, []uint32{SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_REMOTE_MANAGEMENT_USERS}},

	"LW": {SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, []uint32{SECURITY_MANDATORY_LOW_RID}},
	"ME": {SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, []uint32{SECURITY_MANDATORY_MEDIUM_RID}},
	"MP": {SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, []uint32{SECURITY_MANDATORY_MEDIUM_PLUS_RID}},
	"HI": {SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, []uint32{SECURITY_MANDATORY_HIGH_RID}},
	"SI": {SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, []uint32{SECURITY_MANDATORY_SYSTEM_RID}},
	"AC": {SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, []uint32{SECURITY_APP_PACKAGE_BASE_RID, SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE}},

	"AS": {SID_REVISION, 1, SECURITY_AUTHENTICATION_AUTHORITY, []uint32{SECURITY_AUTHENTICATION_AUTHORITY_ASSERTED_RID}},
	"SS": {SID_REVISION, 1, SECURITY_AUTHENTICATION_AUTHORITY, []uint32{SECURITY_AUTHENTICATION_SERVICE_ASSERTED_RID}},
}

// TODO: Validate completeness/correctness.
var domainRidShortcuts = map[string]uint32{
	"RO": DOMAIN_GROUP_RID_ENTERPRISE_READONLY_DOMAIN_CONTROLLERS,
	"LA": DOMAIN_USER_RID_ADMIN,
	"LG": DOMAIN_USER_RID_GUEST,
	"DA": DOMAIN_GROUP_RID_ADMINS,
	"DU": DOMAIN_GROUP_RID_USERS,
	"DG": DOMAIN_GROUP_RID_GUESTS,
	"DC": DOMAIN_GROUP_RID_COMPUTERS,
	"DD": DOMAIN_GROUP_RID_CONTROLLERS,
	"CA": DOMAIN_GROUP_RID_CERT_ADMINS,
	"SA": DOMAIN_GROUP_RID_SCHEMA_ADMINS,
	"EA": DOMAIN_GROUP_RID_ENTERPRISE_ADMINS,
	"PA": DOMAIN_GROUP_RID_POLICY_ADMINS,
	"CN": DOMAIN_GROUP_RID_CLONEABLE_CONTROLLERS,
	"AP": DOMAIN_GROUP_RID_PROTECTED_USERS,
	"KA": DOMAIN_GROUP_RID_KEY_ADMINS,
	"EK": DOMAIN_GROUP_RID_ENTERPRISE_KEY_ADMINS,
	"RS": DOMAIN_ALIAS_RID_RAS_SERVERS,
}

/****************************************************************************/

// isRangeValid validates that offset+length does not overflow, and lies under descriptorLength
func isRangeValid(offset, length, descriptorLength uint32) bool {
	u64RangeEnd := uint64(offset) + uint64(length)

	return u64RangeEnd <= math.MaxUint32 && // the range is valid, provided we do not overflow
		u64RangeEnd <= uint64(descriptorLength) // and that we line up with the max length of the descriptor.
}

// Test whether sd refers to a valid Security Descriptor.
// We do some basic validations of the SECURITY_DESCRIPTOR_RELATIVE header.
// 'flags' is used to convey what all information does the caller want us to verify in the binary SD.
func sdRelativeIsValid(sd []byte, flags SECURITY_INFORMATION) error {
	if len(sd) < int(unsafe.Sizeof(SECURITY_DESCRIPTOR_RELATIVE{})) {
		return fmt.Errorf("sd too small (%d bytes)", len(sd))
	}

	// Fetch various fields of the Security Descriptor.
	revision := sd[0]
	sbz1 := sd[1]
	control := binary.LittleEndian.Uint16(sd[2:4])
	offsetOwner := binary.LittleEndian.Uint32(sd[4:8])
	offsetGroup := binary.LittleEndian.Uint32(sd[8:12])
	offsetSacl := binary.LittleEndian.Uint32(sd[12:16])
	offsetDacl := binary.LittleEndian.Uint32(sd[16:20])
	sdLenU32 := uint32(len(sd))

	// Now validate sanity of these fields.
	if revision != SDDL_REVISION {
		return fmt.Errorf("Invalid SD revision (%d), expected %d", revision, SDDL_REVISION)
	}

	if sbz1 != 0 {
		return fmt.Errorf("sbz1 must be 0, is %d", sbz1)
	}

	// SE_SELF_RELATIVE must be set.
	if (control & SE_SELF_RELATIVE) == 0 {
		return fmt.Errorf("SE_SELF_RELATIVE control bit must be set (control=0x%x)", control)
	}

	// Caller wants us to validate DACL information?
	if (flags & DACL_SECURITY_INFORMATION) != 0 {
		// SE_DACL_PRESENT bit MUST be *always* set.
		if (control & SE_DACL_PRESENT) == 0 {
			return fmt.Errorf("SE_DACL_PRESENT control bit must always be set (control=0x%x)", control)
		}

		// offsetDacl may be 0 which would mean "No ACLs" aka "allow all users".
		// If non-zero, OffsetDacl must point inside the relative Security Descriptor.
		if offsetDacl != 0 &&
			!isRangeValid(offsetDacl, uint32(unsafe.Sizeof(ACL{})), sdLenU32) {
			return fmt.Errorf("DACL (offsetDacl=%d, end=%d) must lie within sd (length=%d)", offsetDacl, uint64(offsetDacl)+uint64(unsafe.Sizeof(ACL{})), len(sd))
		}
	}

	// Caller wants us to validate SACL information?
	if (flags & SACL_SECURITY_INFORMATION) != 0 {
		// SE_SACL_PRESENT bit is optional. If not set it means there is no SACL present.
		if (control&SE_SACL_PRESENT) != 0 && offsetSacl != 0 {
			// OffsetSacl must point inside the relative Security Descriptor.
			if !isRangeValid(offsetSacl, uint32(unsafe.Sizeof(ACL{})), sdLenU32) {
				return fmt.Errorf("SACL (offsetSacl=%d, end=%d) must lie within sd (length=%d)", offsetSacl, uint64(offsetSacl)+uint64(unsafe.Sizeof(ACL{})), len(sd))
			}
		}
	}

	// Caller wants us to validate OwnerSID?
	if (flags & OWNER_SECURITY_INFORMATION) != 0 {
		if offsetOwner == 0 {
			return fmt.Errorf("offsetOwner must not be 0")
		}

		// OffsetOwner must point inside the relative Security Descriptor.
		if !isRangeValid(offsetOwner, uint32(unsafe.Sizeof(SID{})), sdLenU32) {
			return fmt.Errorf("OwnerSID (offsetOwner=%d, end=%d) must lie within sd (length=%d)",
				offsetOwner, uint64(offsetOwner)+uint64(unsafe.Sizeof(SID{})), len(sd))
		}
	}

	// Caller wants us to validate GroupSID?
	if (flags & GROUP_SECURITY_INFORMATION) != 0 {
		if offsetGroup == 0 {
			return fmt.Errorf("offsetGroup must not be 0")
		}

		// OffsetGroup must point inside the relative Security Descriptor.
		if !isRangeValid(offsetGroup, uint32(unsafe.Sizeof(SID{})), sdLenU32) {
			return fmt.Errorf("GroupSID (offsetGroup=%d, end=%d) must lie within sd (length=%d)",
				offsetGroup, uint64(offsetGroup)+uint64(unsafe.Sizeof(SID{})), len(sd))
		}
	}

	return nil
}

// sidToString returns a stringified version of a binary SID object contained in sidSlice.
// The layout of the binary SID object is as per "SID struct".
func sidToString(sidSlice []byte) (string, error) {
	// Ensure we have enough bytes till SID.IdentifierAuthority.
	if len(sidSlice) < 8 {
		return "", fmt.Errorf("Invalid binary SID [size (%d) < 8]", len(sidSlice))
	}

	// SID.Revision.
	revision := sidSlice[:1][0]

	// SID.SubAuthorityCount.
	subAuthorityCount := sidSlice[1:2][0]

	// Ensure we have enough bytes for subAuthorityCount authority values, where each is a 4-byte DWORD
	// in little endian format.
	if len(sidSlice) < int(8+(4*subAuthorityCount)) {
		return "", fmt.Errorf("Invalid binary SID [subAuthorityCount=%d, size (%d) < %d]",
			subAuthorityCount, len(sidSlice), (8 + (4 * subAuthorityCount)))
	}

	// SID.IdentifierAuthority.
	// The 48-bit authority is laid out in big endian format.
	authorityHigh := uint64(binary.BigEndian.Uint16(sidSlice[2:4]))
	authorityLow := uint64(binary.BigEndian.Uint32(sidSlice[4:8]))
	authority := (authorityHigh<<32 | authorityLow)

	sidString := fmt.Sprintf("S-%d-%d", revision, authority)

	// Offset to start of SID.SubAuthority array.
	offset := 8

	// Parse and include all SubAuthority values in the SID string.
	for i := 0; i < int(subAuthorityCount); i++ {
		sidString += fmt.Sprintf("-%d", binary.LittleEndian.Uint32(sidSlice[offset:offset+4]))
		offset += 4
	}

	return sidString, nil
}

// Return the next token (after '-' till the next '-' or end of string) from 'sidString' and the remaining
// sidString after the token.
func getNextToken(sidString string) (string /* token */, string /* remaining sidString */) {
	token := ""
	charsProcessed := 0

	for _, c := range sidString {
		charsProcessed++
		if c == '-' {
			break
		}
		token += string(c)
	}

	return token, sidString[charsProcessed:]
}

// stringToSid converts the string sid into a byte slice in the form of "struct SID".
// The returned byte slice can be copied to fill the sid in a binary Security Descriptor in the form of
// struct SECURITY_DESCRIPTOR_RELATIVE.
func stringToSid(sidString string) ([]byte, error) {
	// Allocate a byte slice large enough to hold the binary SID.
	maxSidBytes := int(unsafe.Sizeof(SID{}) + (unsafe.Sizeof(uint32(0)) * SID_MAX_SUB_AUTHORITIES))
	sid := make([]byte, maxSidBytes)

	sidStringOriginal := sidString
	offset := 0

	if (sidString[0] == 'S' || sidString[0] == 's') && sidString[1] == '-' { /* S-R-I-S-S */
		// R-I-S-S.
		sidString = sidString[2:]
		var subAuthorityCount byte = 0

		token := ""
		tokenIdx := 0
		for sidString != "" {
			token, sidString = getNextToken(sidString)

			if tokenIdx == 0 {
				// SID.Revision.
				revision, err := strconv.ParseUint(token, 10, 8)
				if err != nil {
					return nil, fmt.Errorf("stringToSid: Error parsing Revision: %v", err)
				}
				if revision != SID_REVISION {
					return nil, fmt.Errorf("stringToSid: Invalid SID Revision %d", revision)
				}
				sid[0] = byte(revision)
				// Increment offset by 2 as we will fill SubAuthorityCount later.
				offset += 2
			} else if tokenIdx == 1 {
				// SID.IdentifierAuthority.
				authority, err := strconv.ParseUint(token, 10, 32)
				if err != nil {
					return nil, fmt.Errorf("stringToSid: Error parsing IdentifierAuthority: %v", err)
				}
				authorityHigh := uint16(authority >> 32)
				authorityLow := uint32(authority & 0xFFFFFFFF)
				binary.BigEndian.PutUint16(sid[2:4], authorityHigh)
				binary.BigEndian.PutUint32(sid[4:8], authorityLow)
				offset += 6
			} else {
				// SID.SubAuthority[].
				subAuth, err := strconv.ParseUint(token, 10, 32)
				if err != nil {
					// If not numeric, maybe domain RID, but domain RID must be the last component.
					if rid, ok := domainRidShortcuts[token]; ok {
						if sidString != "" {
							return nil, fmt.Errorf("Domain RID (%s) seen but is not the last SubAuthority. SID=%s", token, sidStringOriginal)
						}
						subAuth = uint64(rid)
					} else {
						return nil, err
					}
				}
				binary.LittleEndian.PutUint32(sid[offset:offset+4], uint32(subAuth))
				offset += 4
				subAuthorityCount++
			}

			tokenIdx++
		}

		// Now we know SubAuthorityCount, fill it.
		sid[1] = subAuthorityCount

	} else {
		// String SID like "BA"?
		if wks, ok := wellKnownSidShortcuts[sidString]; ok {
			// SID.Revision.
			sid[0] = wks.Revision
			// SID.SubAuthorityCount.
			sid[1] = wks.SubAuthorityCount
			// SID.IdentifierAuthority.
			copy(sid[2:8], wks.IdentifierAuthority[:])

			offset = 8
			for i := 0; i < int(wks.SubAuthorityCount); i++ {
				// SID.SubAuthority[].
				binary.LittleEndian.PutUint32(sid[offset:offset+4], wks.SubAuthority[i])
				offset += 4
			}
		} else if rid, ok := domainRidShortcuts[sidString]; ok {
			// Domain RID like "DU"?
			// TODO: Add domain RID support. We need to prefix the domain SID.
			fmt.Printf("Got well known RID %d\n", rid)

			panic("Domain RIDs not yet implemented!")
		} else {
			return nil, fmt.Errorf("Invalid SID: %s", sidStringOriginal)
		}

	}

	return sid[:offset], nil
}

// Return a string representation of the 4-byte ACE rights.
func aceRightsToString(aceRights uint32) string {
	/*
	 * Check if the aceRights exactly maps to a shorthand name.
	 */
	if v, ok := aceRightsToStringMap[aceRights]; ok {
		return v
	}

	/*
	 * Check if the rights can be expressed as a concatenation of shorthand names.
	 * Only if we can map all the OR'ed rights to shorthand names, we use it.
	 */
	aceRightsString := ""
	var allRights uint32 = 0

	for k, v := range aceRightsToStringMap {
		if (aceRights & k) == k {
			aceRightsString += v
			allRights |= k
		}
	}

	// Use stringified rights only if *all* available rights can be represented with a shorthand name.
	// The else part is commented as it's being hit too often. One such common aceRights value is 0x1200a9.
	if allRights == aceRights {
		return aceRightsString
	}
	/*
		else if allRights != 0 {
			fmt.Printf("aceRightsString: Only partial rights could be stringified (aceRights=0x%x, allRights=0x%x)",
				aceRights, allRights)
		}
	*/

	// Fallback to integral mask value.
	return fmt.Sprintf("0x%x", aceRights)
}

// Does the aceType correspond to an object ACE?
// We don't support object ACEs.
//
//nolint:deadcode,unused
func isObjectAce(aceType byte) bool {
	switch aceType {
	case ACCESS_ALLOWED_OBJECT_ACE_TYPE,
		ACCESS_DENIED_OBJECT_ACE_TYPE,
		SYSTEM_AUDIT_OBJECT_ACE_TYPE,
		SYSTEM_ALARM_OBJECT_ACE_TYPE,
		ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE,
		ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE,
		SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE,
		SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE:
		return true

	default:
		return false
	}
}

// Returns true for aceTypes that we support.
// TODO: Allow SACL ACE type, conditional ACE Types.
func isUnsupportedAceType(aceType byte) bool {
	switch aceType {
	case ACCESS_ALLOWED_ACE_TYPE,
		ACCESS_DENIED_ACE_TYPE:
		return false
	default:
		return true
	}
}

// Convert numeric ace type to string.
func aceTypeToString(aceType BYTE) (string, error) {
	for k, v := range aceTypeStringMap {
		if v == aceType {
			return k, nil
		}
	}

	return "", fmt.Errorf("Unknown aceType: %d", aceType)
}

// aceToString returns a stringified version of a binary ACE object contained in aceSlice.
// The layout of the binary ACE object is as per "struct ACCESS_ALLOWED_ACE".
func aceToString(aceSlice []byte) (string, error) {
	// We access 8 bytes in this function, ensure we have at least 8 bytes.
	if len(aceSlice) < 8 {
		return "", fmt.Errorf("Short aceSlice: %d bytes", len(aceSlice))
	}

	aceString := "("

	// ACCESS_ALLOWED_ACE.Header.AceType.
	aceType := aceSlice[:1][0]

	// This is our gatekeeper for blocking unsupported ace types.
	// We open up ACEs as we add support for them.
	if isUnsupportedAceType(aceType) {
		return "", fmt.Errorf("Unsupported ACE type: 0x%x", aceType)
	}

	// ACCESS_ALLOWED_ACE.Header.AceFlags.
	aceFlags := aceSlice[1:2][0]
	// ACCESS_ALLOWED_ACE.AccessMask.
	aceRights := binary.LittleEndian.Uint32(aceSlice[4:8])

	aceTypeString, err := aceTypeToString(BYTE(aceType))
	if err != nil {
		return "", fmt.Errorf("aceToString: %v", err)
	}
	aceString += aceTypeString
	aceString += ";"

	if (aceFlags & CONTAINER_INHERIT_ACE) != 0 {
		aceString += "CI"
	}
	if (aceFlags & OBJECT_INHERIT_ACE) != 0 {
		aceString += "OI"
	}
	if (aceFlags & NO_PROPAGATE_INHERIT_ACE) != 0 {
		aceString += "NP"
	}
	if (aceFlags & INHERIT_ONLY_ACE) != 0 {
		aceString += "IO"
	}
	if (aceFlags & INHERITED_ACE) != 0 {
		aceString += "ID"
	}
	if (aceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) != 0 {
		aceString += "SA"
	}
	if (aceFlags & FAILED_ACCESS_ACE_FLAG) != 0 {
		aceString += "FA"
	}
	if (aceType == SYSTEM_ACCESS_FILTER_ACE_TYPE) && (aceFlags&TRUST_PROTECTED_FILTER_ACE_FLAG) != 0 {
		aceString += "TP"
	}
	if (aceFlags & CRITICAL_ACE_FLAG) != 0 {
		aceString += "CR"
	}

	aceString += ";"
	aceString += aceRightsToString(aceRights)
	aceString += ";"

	// TODO: Empty object_guid;inherit_object_guid.
	aceString += ";"
	aceString += ";"

	sidoffset := 8
	sidStr, err := sidToString(aceSlice[sidoffset:])
	if err != nil {
		return "", fmt.Errorf("aceToString: sidToString failed: %v", err)
	}
	aceString += sidStr
	aceString += ")"

	return aceString, nil
}

// Given the entrire xattr value buffer, return the SD revision.
func getRevision(sd []byte) BYTE {
	if len(sd) < 1 {
		return 0
	}

	// SECURITY_DESCRIPTOR_RELATIVE.Revision.
	return BYTE(sd[0])
}

// Given the entrire xattr value buffer, return the owner sid string.
func getOwnerSidString(sd []byte) (string, error) {
	// Make sure we have enough bytes to safely read the required fields.
	if len(sd) < int(unsafe.Sizeof(SECURITY_DESCRIPTOR_RELATIVE{})) {
		return "", fmt.Errorf("Short Security Descriptor: %d bytes!", len(sd))
	}

	// Only valid revision is 1, verify that.
	revision := getRevision(sd)
	if revision != SID_REVISION {
		return "", fmt.Errorf("Invalid SID revision (%d), expected %d!", revision, SID_REVISION)
	}

	// SECURITY_DESCRIPTOR_RELATIVE.OffsetOwner.
	offsetOwner := binary.LittleEndian.Uint32(sd[4:8])
	// we don't care as much about validating the range here -- sidToString ensures there's at least 8 bytes after slicing.
	if offsetOwner >= uint32(len(sd)) {
		return "", fmt.Errorf("offsetOwner (%d) points outside Security Descriptor of size %d bytes!",
			offsetOwner, len(sd))
	}

	sidStr, err := sidToString(sd[offsetOwner:])
	if err != nil {
		return "", err
	}
	return "O:" + sidStr, nil
}

// Given the entrire xattr value buffer, return the primary group sid string.
func getGroupSidString(sd []byte) (string, error) {
	// Make sure we have enough bytes to safely read the required fields.
	if len(sd) < int(unsafe.Sizeof(SECURITY_DESCRIPTOR_RELATIVE{})) {
		return "", fmt.Errorf("Short Security Descriptor: %d bytes!", len(sd))
	}

	// Only valid revision is 1, verify that.
	revision := getRevision(sd)
	if revision != 1 {
		return "", fmt.Errorf("Invalid SD revision (%d), expected 1!", revision)
	}

	// SECURITY_DESCRIPTOR_RELATIVE.OffsetGroup.
	offsetGroup := binary.LittleEndian.Uint32(sd[8:12])
	// we don't care as much about validating the range here -- sidToString ensures there's at least 8 bytes after slicing.
	if offsetGroup >= uint32(len(sd)) {
		return "", fmt.Errorf("offsetGroup (%d) points outside Security Descriptor of size %d bytes!",
			offsetGroup, len(sd))
	}

	sidStr, err := sidToString(sd[offsetGroup:])
	if err != nil {
		return "", err
	}
	return "G:" + sidStr, nil
}

// Given the entrire xattr value buffer, return the DACL string.
func getDaclString(sd []byte) (string, error) {
	// Make sure we have enough bytes to safely read the required fields.
	if len(sd) < int(unsafe.Sizeof(SECURITY_DESCRIPTOR_RELATIVE{})) {
		return "", fmt.Errorf("Short Security Descriptor: %d bytes!", len(sd))
	}

	// Only valid revision is 1, verify that.
	revision := getRevision(sd)
	if revision != SDDL_REVISION {
		return "", fmt.Errorf("Invalid SD revision (%d), expected %d!", revision, SDDL_REVISION)
	}

	// SECURITY_DESCRIPTOR_RELATIVE.Control.
	control := binary.LittleEndian.Uint16(sd[2:4])

	// DACL not present?
	//
	// Note: I have observed that Windows always sets SE_DACL_PRESENT even if we save a binary SD with
	//       SE_DACL_PRESENT cleared, so we don't expect the following but we still have it for resilience.
	//       Since user has not specified SE_DACL_PRESENT, it means he doesn't want to set any ACLs, which means
	//       he wants to "allow all users", hence "D:NO_ACCESS_CONTROL" is most appropriate.
	//       If we just return "D:" it would mean user wants access control but has not specified any ACEs, which
	//       would instead mean "allow nobody".
	//
	if (control & SE_DACL_PRESENT) == 0 {
		fmt.Printf("[UNEXPECTED] SE_DACL_PRESENT bit not set, control word is 0x%x", control)
		return "D:NO_ACCESS_CONTROL", nil
	}

	daclString := "D:"

	dacl_flags := ""
	if (control & SE_DACL_PROTECTED) != 0 {
		dacl_flags += "P"
	}
	if (control & SE_DACL_AUTO_INHERIT_REQ) != 0 {
		dacl_flags += "AR"
	}
	if (control & SE_DACL_AUTO_INHERITED) != 0 {
		dacl_flags += "AI"
	}
	daclString += dacl_flags

	// SE_DACL_AUTO_INHERITED.OffsetDacl.
	dacloffset := binary.LittleEndian.Uint32(sd[16:20])

	if dacloffset == 0 {
		// dacloffset==0 means that user doesn't want any explicit ACL to be set, which means "allow all users".
		// This can be represented as "D:<flags>NO_ACCESS_CONTROL".
		daclString += "NO_ACCESS_CONTROL"

		return daclString, nil
	}

	if !isRangeValid(dacloffset, 8, uint32(len(sd))) {
		return "", fmt.Errorf("dacloffset (%d) points outside Security Descriptor of size %d bytes!",
			dacloffset+8, len(sd))
	}

	// ACL.AclRevision.
	aclRevision := sd[dacloffset]

	//
	// Though we support only ACCESS_ALLOWED_ACE_TYPE and ACCESS_DENIED_ACE_TYPE which as per docs should be
	// present with ACL revision 2, but I've seen some objects with these ACE types but acl revision 4.
	// Instead of failing here, we let it proceed. Later isUnsupportedAceType() will catch unsupported ACE types.
	//
	// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/20233ed8-a6c6-4097-aafa-dd545ed24428
	//
	if aclRevision != ACL_REVISION && aclRevision != ACL_REVISION_DS {
		// More importantly we don't support Object ACEs (ACL_REVISION_DS).
		return "", fmt.Errorf("Invalid ACL Revision (%d), valid values are 2 and 4.", aclRevision)
	}

	// ACL.AceCount.
	numAces := binary.LittleEndian.Uint32(sd[dacloffset+4 : dacloffset+8])

	// Offset of the first ACE.
	offset := dacloffset + 8

	// Go over all the ACEs and stringify them.
	// If numAces is 0 it'll result in daclString to have only flags and no ACEs.
	// Such an ACL will mean "allow nobody".
	for i := 0; i < int(numAces); i++ {
		if !isRangeValid(offset, 4, uint32(len(sd))) {
			return "", fmt.Errorf("Short ACE (offset=%d), Security Descriptor size=%d bytes!",
				offset, len(sd))
		}

		// ACCESS_ALLOWED_ACE.Header.AceSize.
		ace_size := uint32(binary.LittleEndian.Uint16(sd[offset+2 : offset+4]))

		if !isRangeValid(offset, ace_size, uint32(len(sd))) {
			return "", fmt.Errorf("ACE (offset=%d, ace_size=%d) lies outside Security Descriptor of size %d bytes (or exceeds uint32 limits)!", offset, ace_size, len(sd))
		}

		aceStr, err := aceToString(sd[offset : offset+ace_size])
		if err != nil {
			return "", err
		}
		daclString += aceStr
		offset += ace_size
	}

	return daclString, nil
}

// getBinarySdSizeFromSDDLString returns the estimated number of bytes enough for binary representation of the
// given SDDLString in the SECURITY_DESCRIPTOR_RELATIVE form.
func getBinarySdSizeFromSDDLString(parsedSDDL SDDLString) uint32 {
	// Maximum possible binary SID size.
	maxSidBytes := uint32(unsafe.Sizeof(SID{}) + (unsafe.Sizeof(uint32(0)) * SID_MAX_SUB_AUTHORITIES))

	sdSize := uint32(unsafe.Sizeof(SECURITY_DESCRIPTOR_RELATIVE{}))

	if parsedSDDL.OwnerSID != "" {
		sdSize += maxSidBytes
	}

	if parsedSDDL.GroupSID != "" {
		sdSize += maxSidBytes
	}

	if parsedSDDL.DACL.Flags != "" || len(parsedSDDL.DACL.ACLEntries) != 0 {
		sdSize += uint32(unsafe.Sizeof(ACL{}))
		sdSize += (uint32(unsafe.Sizeof(ACCESS_ALLOWED_ACE{})) + maxSidBytes) * uint32(len(parsedSDDL.DACL.ACLEntries))
	}

	if parsedSDDL.SACL.Flags != "" || len(parsedSDDL.SACL.ACLEntries) != 0 {
		sdSize += uint32(unsafe.Sizeof(ACL{}))
		sdSize += (uint32(unsafe.Sizeof(ACCESS_ALLOWED_ACE{})) + maxSidBytes) * uint32(len(parsedSDDL.SACL.ACLEntries))
	}

	return sdSize
}

/****************************************************************************
 **                               Exported APIs                            **
 ****************************************************************************/

// GetControl returns the security descriptor control bits.
func GetControl(sd []byte) (SECURITY_DESCRIPTOR_CONTROL, error) {
	if len(sd) < 4 {
		return 0, fmt.Errorf("SECURITY_DESCRIPTOR too small (%d bytes)", len(sd))
	}
	return SECURITY_DESCRIPTOR_CONTROL(binary.LittleEndian.Uint16(sd[2:4])), nil
}

// SetControl sets the requested control bits in the given security descriptor.
func SetControl(sd []byte, controlBitsOfInterest, controlBitsToSet SECURITY_DESCRIPTOR_CONTROL) error {
	// GetControl() also does min length check for sd.
	control, err := GetControl(sd)
	if err != nil {
		return err
	}

	control = (control & ^controlBitsOfInterest) | controlBitsToSet
	binary.LittleEndian.PutUint16(sd[2:4], uint16(control))

	return nil
}

// Convert a possibly non-numeric SID to numeric SID.
func CanonicalizeSid(sidString string) (string, error) {
	// Convert to binary SID and back to canonicalize it.
	sidSlice, err := stringToSid(sidString)
	if err != nil {
		return "", err
	}

	canonicalSid, err := sidToString(sidSlice)
	if err != nil {
		return "", err
	}

	return canonicalSid, nil
}

// SecurityDescriptorToString returns an SDDL format string corresponding to the passed in binary Security Descriptor
// in SECURITY_DESCRIPTOR_RELATIVE format.
func SecurityDescriptorToString(sd []byte) (string, error) {
	// We support only DACL/Owner/Group.
	// TODO: Add support for SACL.
	const flags SECURITY_INFORMATION = (DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)

	// Ensure Security Descriptor is valid so that rest of the code can safely access various fields.
	if err := sdRelativeIsValid(sd, flags); err != nil {
		return "", fmt.Errorf("SecurityDescriptorToString: %v", err)
	}

	ownerSidString, err := getOwnerSidString(sd)
	if err != nil {
		return "", fmt.Errorf("SecurityDescriptorToString: getOwnerSidString failed: %v", err)
	}

	groupSidString, err := getGroupSidString(sd)
	if err != nil {
		return "", fmt.Errorf("SecurityDescriptorToString: getGroupSidString failed: %v", err)
	}

	daclString, err := getDaclString(sd)
	if err != nil {
		return "", fmt.Errorf("SecurityDescriptorToString: getDaclString failed: %v", err)
	}

	sddlString := ownerSidString + groupSidString + daclString

	return sddlString, nil
}

// SecurityDescriptorFromString converts a SDDL formatted string into a binary Security Descriptor in
// SECURITY_DESCRIPTOR_RELATIVE format.
func SecurityDescriptorFromString(sddlString string) ([]byte, error) {

	// Since NO_ACCESS_CONTROL friendly flag does not have a corresponding binary flag, we return it separately
	// as a boolean. Caller can then act appropriately.
	aclFlagsToControlBitmap := func(aclFlags string, forSacl bool) (SECURITY_DESCRIPTOR_CONTROL, bool, error) {
		var control SECURITY_DESCRIPTOR_CONTROL = 0
		var no_access_control bool = false

		for i := 0; i < len(aclFlags); {
			if aclFlags[i] == 'P' {
				if forSacl {
					control |= SE_SACL_PROTECTED
				} else {
					control |= SE_DACL_PROTECTED
				}
				i++
			} else if aclFlags[i] == 'A' {
				if i == len(aclFlags) {
					return 0, false, fmt.Errorf("Incomplete ACL Flags, ends at 'A': %s", aclFlags)
				}
				i++
				if aclFlags[i] == 'R' { // AR.
					if forSacl {
						control |= SE_SACL_AUTO_INHERIT_REQ
					} else {
						control |= SE_DACL_AUTO_INHERIT_REQ
					}
					i++
				} else if aclFlags[i] == 'I' { // AI.
					if forSacl {
						control |= SE_SACL_AUTO_INHERITED
					} else {
						control |= SE_DACL_AUTO_INHERITED
					}
					i++
				} else {
					return 0, false, fmt.Errorf("Encountered unsupported ACL Flag '%s' after 'A'",
						string(aclFlags[i]))
				}
			} else if aclFlags[i] == 'N' {
				nacLen := len("NO_ACCESS_CONTROL")
				if i+nacLen > len(aclFlags) {
					return 0, false, fmt.Errorf("Incomplete NO_ACCESS_CONTROL Flag: %s", aclFlags)
				}
				if aclFlags[i:i+nacLen] == "NO_ACCESS_CONTROL" {
					// NO_ACCESS_CONTROL seen.
					no_access_control = true
				}
				i += nacLen
			} else {
				return 0, false, fmt.Errorf("Encountered unsupported ACL Flag '%s'", string(aclFlags[i]))
			}
		}

		return control, no_access_control, nil
	}

	aceFlagsToByte := func(aceFlags string) (byte, error) {
		var flags byte = 0

		for i := 0; i < len(aceFlags); {
			// Must have even number of characters.
			if i+1 == len(aceFlags) {
				return byte(0), fmt.Errorf("Invalid aceFlags: %s", aceFlags)
			}

			flag := aceFlags[i : i+2]

			if flag == "CI" {
				flags |= CONTAINER_INHERIT_ACE
			} else if flag == "OI" {
				flags |= OBJECT_INHERIT_ACE
			} else if flag == "NP" {
				flags |= NO_PROPAGATE_INHERIT_ACE
			} else if flag == "IO" {
				flags |= INHERIT_ONLY_ACE
			} else if flag == "ID" {
				flags |= INHERITED_ACE
			} else if flag == "SA" {
				flags |= SUCCESSFUL_ACCESS_ACE_FLAG
			} else if flag == "FA" {
				flags |= FAILED_ACCESS_ACE_FLAG
			} else if flag == "TP" {
				flags |= TRUST_PROTECTED_FILTER_ACE_FLAG
			} else if flag == "CR" {
				flags |= CRITICAL_ACE_FLAG
			} else {
				return byte(0), fmt.Errorf("Unsupported aceFlags: %s", aceFlags)
			}

			i += 2
		}

		return flags, nil
	}

	aceRightsToAccessMask := func(aceRights string) (uint32, error) {
		var accessMask uint32 = 0

		// Hex right string will start with 0x or 0X.
		if len(aceRights) > 2 && (aceRights[0:2] == "0x" || aceRights[0:2] == "0X") {
			accessMask, err := strconv.ParseUint(aceRights[2:], 16, 32)
			if err != nil {
				return 0, fmt.Errorf("Failed to parse integral aceRights %s: %v", aceRights, err)
			}
			return uint32(accessMask), nil
		}

		for i := 0; i < len(aceRights); {
			// Must have even number of characters.
			if i+1 == len(aceRights) {
				return 0, fmt.Errorf("Invalid aceRights: %s", aceRights)
			}

			right := aceRights[i : i+2]

			if mask, ok := aceStringToRightsMap[right]; ok {
				accessMask |= mask
			} else {
				return 0, fmt.Errorf("Unknown aceRight(%s): %s", right, aceRights)
			}

			i += 2
		}

		return accessMask, nil
	}

	aclEntryToSlice := func(aclEntry ACLEntry) ([]byte, error) {
		// ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid;(resource_attribute)
		if len(aclEntry.Sections) != 6 {
			return nil, fmt.Errorf("aclEntry has %d sections (expected 6)", len(aclEntry.Sections))
		}
		// Maximum possible binary SID size.
		maxSidBytes := int(unsafe.Sizeof(SID{}) + (unsafe.Sizeof(uint32(0)) * SID_MAX_SUB_AUTHORITIES))

		sliceSize := int(unsafe.Sizeof(ACCESS_ALLOWED_ACE{})) + maxSidBytes
		ace := make([]byte, sliceSize)

		// Base aceSize. We will add SID size to it to get complete ACE size.
		var aceSize uint16 = 8

		// ACCESS_ALLOWED_ACE.Header.AceType.
		if aceType, ok := aceTypeStringMap[aclEntry.Sections[0]]; ok {
			ace[0] = byte(aceType)
		} else {
			return nil, fmt.Errorf("Unknown aceType: %s", aclEntry.Sections[0])
		}

		// ACCESS_ALLOWED_ACE.Header.AceFlags.
		flags, err := aceFlagsToByte(aclEntry.Sections[1])
		if err != nil {
			return nil, fmt.Errorf("Unknown aceFlag %s: %v", aclEntry.Sections[1], err)
		}
		ace[1] = flags

		// ACCESS_ALLOWED_ACE.AccessMask.
		accessMask, err := aceRightsToAccessMask(aclEntry.Sections[2])
		if err != nil {
			return nil, fmt.Errorf("Unknown aceRights %s: %v", aclEntry.Sections[2], err)
		}
		binary.LittleEndian.PutUint32(ace[4:8], accessMask)

		// TODO: Support object ACEs?
		if aclEntry.Sections[3] != "" {
			return nil, fmt.Errorf("object_guid not supported: %s", aclEntry.Sections[3])
		}

		if aclEntry.Sections[4] != "" {
			return nil, fmt.Errorf("inherit_object_guid not supported: %s", aclEntry.Sections[5])
		}

		if aclEntry.Sections[5] != "" {
			sidSlice, err := stringToSid(aclEntry.Sections[5])
			if err != nil {
				return nil, fmt.Errorf("Bad SID (%s): %v", aclEntry.Sections[5], err)
			}
			copy(ace[8:8+len(sidSlice)], sidSlice)
			aceSize += uint16(len(sidSlice))
		}

		// ACCESS_ALLOWED_ACE.Header.AceSize.
		binary.LittleEndian.PutUint16(ace[2:4], aceSize)

		return ace[:aceSize], nil
	}

	// Use sddl.ParseSDDL() instead of reinventing SDDL parsing.
	parsedSDDL, err := ParseSDDL(sddlString)
	if err != nil {
		return nil, fmt.Errorf("ParseSDDL(%s) failed: %v", sddlString, err)
	}

	// Allocate a byte slice large enough to contain the binary Security Descriptor in SECURITY_DESCRIPTOR_RELATIVE
	// format.
	sdSize := getBinarySdSizeFromSDDLString(parsedSDDL)
	sd := make([]byte, sdSize)

	// Returned Security Descriptor is in Self Relative format.
	//
	// Note: We always set SE_DACL_PRESENT as we have observed that Windows always sets that.
	//       It then uses offsetDacl to control whether ACLs are checked or not.
	//       offsetDacl==0 would mean that there are no ACLs and hence the file will have the "allow all users"
	//       permission.
	//       offsetDacl!=0 would cause the ACEs to be inspected from offsetDacl and if there are no ACEs present it
	//       would mean "allow nobody".
	control := SECURITY_DESCRIPTOR_CONTROL(SE_SELF_RELATIVE | SE_DACL_PRESENT)
	offsetOwner := 0
	offsetGroup := 0
	offsetDacl := 0
	offsetSacl := 0

	// sd.Revision.
	sd[0] = SDDL_REVISION
	// sd.Sbz1.
	sd[1] = 0

	// OwnerSID follows immediately after SECURITY_DESCRIPTOR_RELATIVE header.
	offset := 20
	if parsedSDDL.OwnerSID != "" {
		offsetOwner = offset
		sidSlice, err := stringToSid(parsedSDDL.OwnerSID)
		if err != nil {
			return nil, err
		}
		copy(sd[offset:offset+len(sidSlice)], sidSlice)
		offset += len(sidSlice)
	}

	if parsedSDDL.GroupSID != "" {
		offsetGroup = offset
		sidSlice, err := stringToSid(parsedSDDL.GroupSID)
		if err != nil {
			return nil, err
		}
		copy(sd[offset:offset+len(sidSlice)], sidSlice)
		offset += len(sidSlice)
	}

	// TODO: Add and audit SACL support.
	if parsedSDDL.SACL.Flags != "" || len(parsedSDDL.SACL.ACLEntries) != 0 {
		flags, no_access_control, err := aclFlagsToControlBitmap(parsedSDDL.SACL.Flags, true /* forSacl */)
		if err != nil {
			return nil, fmt.Errorf("Failed to parse SACL Flags %s: %v", parsedSDDL.SACL.Flags, err)
		}
		control |= flags

		// If NO_ACCESS_CONTROL flag is set we will skip the following, which will result in offsetSacl to be set as 0
		// in the binary SD, which would mean "No ACLs" aka "allow all users".
		if !no_access_control {
			offsetSacl = offset

			// ACL.AclRevision.
			sd[offsetSacl] = ACL_REVISION
			// ACL.Sbz1.
			sd[offsetSacl+1] = 0

			// Base aclSize. We will add ACE sizes to it to get complete ACL size.
			var aclSize uint16 = 8

			// ACL.AceCount.
			binary.LittleEndian.PutUint16(sd[offsetSacl+4:offsetSacl+6], uint16(len(parsedSDDL.SACL.ACLEntries)))
			// ACL.Sbz2.
			binary.LittleEndian.PutUint16(sd[offsetSacl+6:offsetSacl+8], 0)

			offset += 8 // struct ACL.
			for i := 0; i < len(parsedSDDL.SACL.ACLEntries); i++ {
				aceSlice, err := aclEntryToSlice(parsedSDDL.SACL.ACLEntries[i])
				if err != nil {
					return nil, err
				}
				copy(sd[offset:offset+len(aceSlice)], aceSlice)
				offset += len(aceSlice)
				aclSize += uint16(len(aceSlice))
			}

			// ACL.AclSize.
			binary.LittleEndian.PutUint16(sd[offsetSacl+2:offsetSacl+4], aclSize)

			// Put in the end to prevent "unreachable code" complaints from vet.
			panic("SACLs not supported!")
		} else {
			// If NO_ACCESS_CONTROL flag is set, there shouldn't be any ACEs.
			// TODO: Is it safer to skip/ignore the ACEs?
			if len(parsedSDDL.SACL.ACLEntries) != 0 {
				return nil, fmt.Errorf("%d ACEs present along with NO_ACCESS_CONTROL SACL flag (%s): %v",
					len(parsedSDDL.SACL.ACLEntries), parsedSDDL.SACL.Flags, err)
			}
		}
	}

	if parsedSDDL.DACL.Flags != "" || len(parsedSDDL.DACL.ACLEntries) != 0 {
		flags, no_access_control, err := aclFlagsToControlBitmap(parsedSDDL.DACL.Flags, false /* forSacl */)
		if err != nil {
			return nil, fmt.Errorf("Failed to parse DACL Flags %s: %v", parsedSDDL.DACL.Flags, err)
		}
		control |= flags

		// If NO_ACCESS_CONTROL flag is set we will skip the following, which will result in offsetDacl to be set as 0
		// in the binary SD, which would mean "No ACLs" aka "allow all users".
		if !no_access_control {
			offsetDacl = offset

			// ACL.AclRevision.
			sd[offsetDacl] = ACL_REVISION
			// ACL.Sbz1.
			sd[offsetDacl+1] = 0

			// Base aclSize. We will add ACE sizes to it to get complete ACL size.
			var aclSize uint16 = 8

			// ACL.AceCount.
			binary.LittleEndian.PutUint16(sd[offsetDacl+4:offsetDacl+6], uint16(len(parsedSDDL.DACL.ACLEntries)))
			// ACL.Sbz2.
			binary.LittleEndian.PutUint16(sd[offsetDacl+6:offsetDacl+8], 0)

			offset += 8 // struct ACL.
			for i := 0; i < len(parsedSDDL.DACL.ACLEntries); i++ {
				aceSlice, err := aclEntryToSlice(parsedSDDL.DACL.ACLEntries[i])
				if err != nil {
					return nil, err
				}
				copy(sd[offset:offset+len(aceSlice)], aceSlice)
				offset += len(aceSlice)
				aclSize += uint16(len(aceSlice))
			}

			// ACL.AclSize.
			binary.LittleEndian.PutUint16(sd[offsetDacl+2:offsetDacl+4], aclSize)
		} else {
			// If NO_ACCESS_CONTROL flag is set, there shouldn't be any ACEs.
			// TODO: Is it safer to skip/ignore the ACEs?
			if len(parsedSDDL.DACL.ACLEntries) != 0 {
				return nil, fmt.Errorf("%d ACEs present along with NO_ACCESS_CONTROL DACL flag (%s): %v",
					len(parsedSDDL.DACL.ACLEntries), parsedSDDL.DACL.Flags, err)
			}
		}
	}

	// sd.Control.
	binary.LittleEndian.PutUint16(sd[2:4], uint16(control))
	// sd.OffsetOwner.
	binary.LittleEndian.PutUint32(sd[4:8], uint32(offsetOwner))
	// sd.OffsetGroup.
	binary.LittleEndian.PutUint32(sd[8:12], uint32(offsetGroup))
	// sd.OffsetSacl.
	binary.LittleEndian.PutUint32(sd[12:16], uint32(offsetSacl))
	// sd.OffsetDacl.
	binary.LittleEndian.PutUint32(sd[16:20], uint32(offsetDacl))

	return sd[:offset], nil
}

// SetSecurityObject is the equivalent of ntdll.NtSetSecurityObject method.
// It sets the given SECURITY_DESCRIPTOR for the given file.
// flags instructs what all needs to be set.
// sd should be a valid binary SECURITY_DESCRIPTOR_RELATIVE structure as a byte slice.
func SetSecurityObject(path string, flags SECURITY_INFORMATION, sd []byte) error {
	var xattrKey string

	if len(sd) < int(unsafe.Sizeof(SECURITY_DESCRIPTOR_RELATIVE{})) {
		panic(fmt.Errorf("SetSecurityObject: sd too small (%d bytes)", len(sd)))
	}

	// Pick the right xattr key that allows us to pass the needed information to the cifs client.
	if flags == DACL_SECURITY_INFORMATION {
		// Only DACL.
		xattrKey = common.CIFS_XATTR_CIFS_ACL

		// sd.OffsetOwner = 0.
		binary.LittleEndian.PutUint32(sd[4:8], 0)
		// sd.OffsetGroup = 0.
		binary.LittleEndian.PutUint32(sd[8:12], 0)
		// sd.OffsetSacl = 0.
		binary.LittleEndian.PutUint32(sd[12:16], 0)
	} else if flags == (DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION) {
		// DACL + Owner + Group.
		xattrKey = common.CIFS_XATTR_CIFS_NTSD

		// sd.OffsetSacl = 0.
		binary.LittleEndian.PutUint32(sd[12:16], 0)
	} else if flags == (DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION |
		OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION) {
		// DACL + SACL + Owner + Group.
		xattrKey = common.CIFS_XATTR_CIFS_NTSD_FULL

		// Put in the end to prevent "unreachable code" complaints from vet.
		// TODO: Add support for "DACL + SACL + Owner + Group".
		//       Remove this panic only after rest of the code correctly supports SACL.
		panic(fmt.Errorf("SetSecurityObject: Unsupported flags value 0x%x", flags))

	} else {
		panic(fmt.Errorf("SetSecurityObject: Unsupported flags value 0x%x", flags))
	}

	// Ensure Security Descriptor is valid before writing to the cifs client.
	if err := sdRelativeIsValid(sd, flags); err != nil {
		panic(fmt.Errorf("SetSecurityObject: %v", err))
	}

	err := xattr.Set(path, xattrKey, sd)
	if err != nil {
		return fmt.Errorf("SetSecurityObject: xattr.Set(%s) failed for file %s: %v", xattrKey, path, err)
	}

	return nil
}

// QuerySecurityObject is the equivalent of ntdll.NtQuerySecurityObject method.
// It fetches the binary SECURITY_DESCRIPTOR for the given file.
// 'flags' instructs what parts of the Security Descriptor needs to be queried.
// Returns a valid binary SECURITY_DESCRIPTOR_RELATIVE structure as a byte slice.
func QuerySecurityObject(path string, flags SECURITY_INFORMATION) ([]byte, error) {
	var xattrKey string

	// Pick the right xattr key that allows us to pass the needed information to the cifs client.
	if flags == DACL_SECURITY_INFORMATION {
		// Only DACL.
		xattrKey = common.CIFS_XATTR_CIFS_ACL
	} else if flags == (DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION) {
		// DACL + Owner + Group.
		xattrKey = common.CIFS_XATTR_CIFS_NTSD
	} else if flags == (DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION |
		OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION) {
		// DACL + SACL + Owner + Group.
		xattrKey = common.CIFS_XATTR_CIFS_NTSD_FULL

		// Put in the end to prevent "unreachable code" complaints from vet.
		// TODO: Add support for "DACL + SACL + Owner + Group".
		//       Remove this panic only after rest of the code correctly supports SACL.
		panic(fmt.Errorf("QuerySecurityObject: Unsupported flags value 0x%x", flags))
	} else {
		panic(fmt.Errorf("QuerySecurityObject: Unsupported flags value 0x%x", flags))
	}

	sd, err := xattr.Get(path, xattrKey)
	if err != nil {
		return nil, fmt.Errorf("QuerySecurityObject: xattr.Get(%s, %s) failed: %v", path, xattrKey, err)
	}

	// Ensure Security Descriptor returned by the cifs client is fine.
	if err := sdRelativeIsValid(sd, flags); err != nil {
		// panic because we expect cifs client to return a valid Security Descriptor.
		panic(fmt.Errorf("QuerySecurityObject: %v", err))
	}

	return sd, nil
}
