mirror of
https://github.com/mandiant/gopacket
synced 2026-06-21 13:57:02 +00:00
6657f2dd4a
When AceSize is less than 8, the fixed 4-byte header plus 4-byte Mask are not contained in the ACE. The existing len(data) < aceSize check passes (len(data) may be >= 8) but later slices like data[4:8] and data[offset:aceSize] panic with 'slice bounds out of range'. Fixes #23.
154 lines
4.1 KiB
Go
154 lines
4.1 KiB
Go
// Copyright 2026 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package security
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
)
|
|
|
|
// ACE represents an Access Control Entry.
|
|
type ACE struct {
|
|
Type uint8
|
|
Flags uint8
|
|
Mask uint32
|
|
ObjectFlags uint32
|
|
ObjectType GUID
|
|
InheritedObjectType GUID
|
|
SID *SID
|
|
}
|
|
|
|
// IsObjectACE returns true if this is an object-type ACE.
|
|
func (a *ACE) IsObjectACE() bool {
|
|
return a.Type == ACCESS_ALLOWED_OBJECT_ACE_TYPE ||
|
|
a.Type == ACCESS_DENIED_OBJECT_ACE_TYPE ||
|
|
a.Type == SYSTEM_AUDIT_OBJECT_ACE_TYPE
|
|
}
|
|
|
|
// ParseACE parses a single ACE from binary data, returning the ACE and bytes consumed.
|
|
func ParseACE(data []byte) (*ACE, int, error) {
|
|
if len(data) < 8 {
|
|
return nil, 0, fmt.Errorf("ACE too short: need at least 8 bytes, got %d", len(data))
|
|
}
|
|
|
|
ace := &ACE{
|
|
Type: data[0],
|
|
Flags: data[1],
|
|
}
|
|
aceSize := int(binary.LittleEndian.Uint16(data[2:4]))
|
|
if aceSize < 8 {
|
|
return nil, 0, fmt.Errorf("ACE size %d is below minimum header size of 8", aceSize)
|
|
}
|
|
if len(data) < aceSize {
|
|
return nil, 0, fmt.Errorf("ACE size %d exceeds available data %d", aceSize, len(data))
|
|
}
|
|
|
|
ace.Mask = binary.LittleEndian.Uint32(data[4:8])
|
|
|
|
offset := 8
|
|
if ace.IsObjectACE() {
|
|
if len(data) < offset+4 {
|
|
return nil, 0, fmt.Errorf("object ACE too short for ObjectFlags")
|
|
}
|
|
ace.ObjectFlags = binary.LittleEndian.Uint32(data[offset : offset+4])
|
|
offset += 4
|
|
|
|
if ace.ObjectFlags&ACE_OBJECT_TYPE_PRESENT != 0 {
|
|
if len(data) < offset+16 {
|
|
return nil, 0, fmt.Errorf("object ACE too short for ObjectType")
|
|
}
|
|
copy(ace.ObjectType[:], data[offset:offset+16])
|
|
offset += 16
|
|
}
|
|
|
|
if ace.ObjectFlags&ACE_INHERITED_OBJECT_TYPE_PRESENT != 0 {
|
|
if len(data) < offset+16 {
|
|
return nil, 0, fmt.Errorf("object ACE too short for InheritedObjectType")
|
|
}
|
|
copy(ace.InheritedObjectType[:], data[offset:offset+16])
|
|
offset += 16
|
|
}
|
|
}
|
|
|
|
// Parse SID from remaining data
|
|
sid, _, err := ParseSIDBytes(data[offset:aceSize])
|
|
if err != nil {
|
|
return nil, 0, fmt.Errorf("failed to parse ACE SID: %v", err)
|
|
}
|
|
ace.SID = sid
|
|
|
|
return ace, aceSize, nil
|
|
}
|
|
|
|
// Marshal serializes the ACE to binary format.
|
|
func (a *ACE) Marshal() []byte {
|
|
var body []byte
|
|
|
|
// Mask (4 bytes)
|
|
maskBuf := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(maskBuf, a.Mask)
|
|
body = append(body, maskBuf...)
|
|
|
|
if a.IsObjectACE() {
|
|
// Determine object flags based on content
|
|
flags := uint32(0)
|
|
if !a.ObjectType.IsZero() {
|
|
flags |= ACE_OBJECT_TYPE_PRESENT
|
|
}
|
|
if !a.InheritedObjectType.IsZero() {
|
|
flags |= ACE_INHERITED_OBJECT_TYPE_PRESENT
|
|
}
|
|
|
|
flagsBuf := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(flagsBuf, flags)
|
|
body = append(body, flagsBuf...)
|
|
|
|
if flags&ACE_OBJECT_TYPE_PRESENT != 0 {
|
|
body = append(body, a.ObjectType[:]...)
|
|
}
|
|
if flags&ACE_INHERITED_OBJECT_TYPE_PRESENT != 0 {
|
|
body = append(body, a.InheritedObjectType[:]...)
|
|
}
|
|
}
|
|
|
|
// SID
|
|
body = append(body, a.SID.Marshal()...)
|
|
|
|
// Build header: Type(1) + Flags(1) + Size(2)
|
|
aceSize := 4 + len(body) // 4 bytes header + body
|
|
header := make([]byte, 4)
|
|
header[0] = a.Type
|
|
header[1] = a.Flags
|
|
binary.LittleEndian.PutUint16(header[2:4], uint16(aceSize))
|
|
|
|
return append(header, body...)
|
|
}
|
|
|
|
// Matches checks if this ACE matches the given criteria.
|
|
func (a *ACE) Matches(sid *SID, objectType *GUID, mask uint32) bool {
|
|
if sid != nil && !a.SID.Equal(sid) {
|
|
return false
|
|
}
|
|
if mask != 0 && (a.Mask&mask) != mask {
|
|
return false
|
|
}
|
|
if objectType != nil && !objectType.IsZero() {
|
|
if a.ObjectType.IsZero() || a.ObjectType != *objectType {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|