sddl/sddlHelper_linux.go (1,168 lines of code) (raw):
//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"
"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,
}
/****************************************************************************/
// 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])
// 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 && offsetDacl+uint32(unsafe.Sizeof(ACL{})) > uint32(len(sd)) {
return fmt.Errorf("DACL (offsetDacl=%d) must lie within sd (length=%d)", offsetDacl, 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 offsetSacl+uint32(unsafe.Sizeof(ACL{})) > uint32(len(sd)) {
return fmt.Errorf("SACL (offsetSacl=%d) must lie within sd (length=%d)", offsetSacl, 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 offsetOwner+uint32(unsafe.Sizeof(SID{})) > uint32(len(sd)) {
return fmt.Errorf("OwnerSID (offsetOwner=%d) must lie within sd (length=%d)",
offsetOwner, 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 offsetGroup+uint32(unsafe.Sizeof(SID{})) > uint32(len(sd)) {
return fmt.Errorf("GroupSID (offsetGroup=%d) must lie within sd (length=%d)",
offsetGroup, 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])
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])
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 (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 (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 (offset + ace_size) > uint32(len(sd)) {
return "", fmt.Errorf("ACE (offset=%d, ace_size=%d) lies outside Security Descriptor of size %d bytes!", 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
}