func SecurityDescriptorFromString()

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
}