Skip to content

Commit a822796

Browse files
committed
Fix NtSecurityDescriptor.Marshal emitting offsets for an unset Owner/Group (#82)
1 parent f78e516 commit a822796

2 files changed

Lines changed: 47 additions & 4 deletions

File tree

securitydescriptor/NtSecurityDescriptor.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,26 +157,33 @@ func (ntsd *NtSecurityDescriptor) Marshal() ([]byte, error) {
157157
ntsd.Header.OffsetDacl = 0
158158
}
159159

160-
// Marshal Owner
160+
// Marshal Owner. A zero-value Identity (as produced by NewSecurityDescriptor)
161+
// has an unset SID whose RevisionLevel is 0, which is not a valid SID. Treat
162+
// it as "no owner" and write OffsetOwner = 0, mirroring the presence checks
163+
// used for the SACL and DACL, instead of emitting an invalid S-0-0-0 SID.
161164
dataOwner := []byte{}
162-
if ntsd.Owner != nil {
165+
if ntsd.Owner != nil && ntsd.Owner.SID.RevisionLevel != 0 {
163166
dataOwner, err = ntsd.Owner.SID.Marshal()
164167
if err != nil {
165168
return nil, fmt.Errorf("failed to marshal Owner: %w", err)
166169
}
167170
ntsd.Header.OffsetOwner = uint32(offset)
168171
offset += len(dataOwner)
172+
} else {
173+
ntsd.Header.OffsetOwner = 0
169174
}
170175

171-
// Marshal Group
176+
// Marshal Group (see the Owner note above).
172177
dataGroup := []byte{}
173-
if ntsd.Group != nil {
178+
if ntsd.Group != nil && ntsd.Group.SID.RevisionLevel != 0 {
174179
dataGroup, err = ntsd.Group.SID.Marshal()
175180
if err != nil {
176181
return nil, fmt.Errorf("failed to marshal Group: %w", err)
177182
}
178183
ntsd.Header.OffsetGroup = uint32(offset)
179184
offset += len(dataGroup)
185+
} else {
186+
ntsd.Header.OffsetGroup = 0
180187
}
181188

182189
// Update the header and append the header bytes

securitydescriptor/NtSecurityDescriptor_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,42 @@ func TestNtSecurityDescriptor_Involution(t *testing.T) {
147147
}
148148
}
149149

150+
// TestNtSecurityDescriptor_Marshal_UnsetOwnerGroup verifies that marshalling a
151+
// descriptor whose Owner/Group were never populated (as produced by
152+
// NewSecurityDescriptor) writes OffsetOwner/OffsetGroup = 0 and emits no SID
153+
// bytes, rather than an invalid S-0-0-0 SID.
154+
func TestNtSecurityDescriptor_Marshal_UnsetOwnerGroup(t *testing.T) {
155+
ntsd := securitydescriptor.NewSecurityDescriptor()
156+
157+
data, err := ntsd.Marshal()
158+
if err != nil {
159+
t.Fatalf("Marshal() error = %v", err)
160+
}
161+
162+
if ntsd.Header.OffsetOwner != 0 {
163+
t.Errorf("OffsetOwner = %d, want 0 for an unset owner", ntsd.Header.OffsetOwner)
164+
}
165+
if ntsd.Header.OffsetGroup != 0 {
166+
t.Errorf("OffsetGroup = %d, want 0 for an unset group", ntsd.Header.OffsetGroup)
167+
}
168+
// With no owner, group, DACL, or SACL set, only the 20-byte header is emitted.
169+
if len(data) != 20 {
170+
t.Errorf("Marshal() length = %d, want 20 (header only)", len(data))
171+
}
172+
173+
// The descriptor must round-trip with no fabricated owner/group.
174+
parsed := &securitydescriptor.NtSecurityDescriptor{}
175+
if _, err := parsed.Unmarshal(data); err != nil {
176+
t.Fatalf("Unmarshal() error = %v", err)
177+
}
178+
if parsed.Owner != nil {
179+
t.Errorf("parsed Owner = %q, want nil", parsed.Owner.SID.ToString())
180+
}
181+
if parsed.Group != nil {
182+
t.Errorf("parsed Group = %q, want nil", parsed.Group.SID.ToString())
183+
}
184+
}
185+
150186
func TestNtSecurityDescriptor_Unmarshal(t *testing.T) {
151187
ntsd := securitydescriptor.NewSecurityDescriptor()
152188

0 commit comments

Comments
 (0)