in sddl/sddlHelper_linux.go [1272:1623]
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
}