@@ -86,16 +86,73 @@ func getProfilesDirectory() (string, error) {
86
86
}
87
87
}
88
88
89
+ func isServiceAccount (sid * syscall.SID ) bool {
90
+ if ! windows .IsValidSid (sid ) {
91
+ // We don't accept SIDs from the public API, so this should never happen.
92
+ // Better be on the safe side and validate anyway.
93
+ return false
94
+ }
95
+ // The following RIDs are considered service user accounts as per
96
+ // https://learn.microsoft.com/en-us/windows/win32/secauthz/well-known-sids and
97
+ // https://learn.microsoft.com/en-us/windows/win32/services/service-user-accounts:
98
+ // - "S-1-5-18": LocalSystem
99
+ // - "S-1-5-19": LocalService
100
+ // - "S-1-5-20": NetworkService
101
+ if * windows .GetSidSubAuthorityCount (sid ) != windows .SID_REVISION ||
102
+ * windows .GetSidIdentifierAuthority (sid ) != windows .SECURITY_NT_AUTHORITY {
103
+ return false
104
+ }
105
+ switch * windows .GetSidSubAuthority (sid , 0 ) {
106
+ case windows .SECURITY_LOCAL_SYSTEM_RID ,
107
+ windows .SECURITY_LOCAL_SERVICE_RID ,
108
+ windows .SECURITY_NETWORK_SERVICE_RID :
109
+ return true
110
+ }
111
+ return false
112
+ }
113
+
114
+ func isValidUserAccountType (sid * syscall.SID , sidType uint32 ) bool {
115
+ switch sidType {
116
+ case syscall .SidTypeUser :
117
+ return true
118
+ case syscall .SidTypeWellKnownGroup :
119
+ return isServiceAccount (sid )
120
+ }
121
+ return false
122
+ }
123
+
124
+ func isValidGroupAccountType (sidType uint32 ) bool {
125
+ switch sidType {
126
+ case syscall .SidTypeGroup :
127
+ return true
128
+ case syscall .SidTypeWellKnownGroup :
129
+ // Some well-known groups are also considered service accounts,
130
+ // so isValidUserAccountType would return true for them.
131
+ // We have historically allowed them in LookupGroup and LookupGroupId,
132
+ // so don't treat them as invalid here.
133
+ return true
134
+ case syscall .SidTypeAlias :
135
+ // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/7b2aeb27-92fc-41f6-8437-deb65d950921#gt_0387e636-5654-4910-9519-1f8326cf5ec0
136
+ // SidTypeAlias should also be treated as a group type next to SidTypeGroup
137
+ // and SidTypeWellKnownGroup:
138
+ // "alias object -> resource group: A group object..."
139
+ //
140
+ // Tests show that "Administrators" can be considered of type SidTypeAlias.
141
+ return true
142
+ }
143
+ return false
144
+ }
145
+
89
146
// lookupUsernameAndDomain obtains the username and domain for usid.
90
- func lookupUsernameAndDomain (usid * syscall.SID ) (username , domain string , e error ) {
91
- username , domain , t , e : = usid .LookupAccount ("" )
147
+ func lookupUsernameAndDomain (usid * syscall.SID ) (username , domain string , sidType uint32 , e error ) {
148
+ username , domain , sidType , e = usid .LookupAccount ("" )
92
149
if e != nil {
93
- return "" , "" , e
150
+ return "" , "" , 0 , e
94
151
}
95
- if t != syscall . SidTypeUser {
96
- return "" , "" , fmt .Errorf ("user: should be user account type, not %d" , t )
152
+ if ! isValidUserAccountType ( usid , sidType ) {
153
+ return "" , "" , 0 , fmt .Errorf ("user: should be user account type, not %d" , sidType )
97
154
}
98
- return username , domain , nil
155
+ return username , domain , sidType , nil
99
156
}
100
157
101
158
// findHomeDirInRegistry finds the user home path based on the uid.
@@ -118,13 +175,7 @@ func lookupGroupName(groupname string) (string, error) {
118
175
if e != nil {
119
176
return "" , e
120
177
}
121
- // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/7b2aeb27-92fc-41f6-8437-deb65d950921#gt_0387e636-5654-4910-9519-1f8326cf5ec0
122
- // SidTypeAlias should also be treated as a group type next to SidTypeGroup
123
- // and SidTypeWellKnownGroup:
124
- // "alias object -> resource group: A group object..."
125
- //
126
- // Tests show that "Administrators" can be considered of type SidTypeAlias.
127
- if t != syscall .SidTypeGroup && t != syscall .SidTypeWellKnownGroup && t != syscall .SidTypeAlias {
178
+ if ! isValidGroupAccountType (t ) {
128
179
return "" , fmt .Errorf ("lookupGroupName: should be group account type, not %d" , t )
129
180
}
130
181
return sid .String ()
@@ -355,18 +406,27 @@ func lookupUserPrimaryGroup(username, domain string) (string, error) {
355
406
}
356
407
357
408
func newUserFromSid (usid * syscall.SID ) (* User , error ) {
358
- username , domain , e := lookupUsernameAndDomain (usid )
359
- if e != nil {
360
- return nil , e
361
- }
362
- gid , e := lookupUserPrimaryGroup (username , domain )
409
+ username , domain , sidType , e := lookupUsernameAndDomain (usid )
363
410
if e != nil {
364
411
return nil , e
365
412
}
366
413
uid , e := usid .String ()
367
414
if e != nil {
368
415
return nil , e
369
416
}
417
+ var gid string
418
+ if sidType == syscall .SidTypeWellKnownGroup {
419
+ // The SID does not contain a domain; this function's domain variable has
420
+ // been populated with the SID's identifier authority. This happens with
421
+ // special service user accounts such as "NT AUTHORITY\LocalSystem".
422
+ // In this case, gid is the same as the user SID.
423
+ gid = uid
424
+ } else {
425
+ gid , e = lookupUserPrimaryGroup (username , domain )
426
+ if e != nil {
427
+ return nil , e
428
+ }
429
+ }
370
430
// If this user has logged in at least once their home path should be stored
371
431
// in the registry under the specified SID. References:
372
432
// https://social.technet.microsoft.com/wiki/contents/articles/13895.how-to-remove-a-corrupted-user-profile-from-the-registry.aspx
@@ -396,7 +456,7 @@ func lookupUser(username string) (*User, error) {
396
456
if e != nil {
397
457
return nil , e
398
458
}
399
- if t != syscall . SidTypeUser {
459
+ if ! isValidUserAccountType ( sid , t ) {
400
460
return nil , fmt .Errorf ("user: should be user account type, not %d" , t )
401
461
}
402
462
return newUserFromSid (sid )
@@ -427,7 +487,7 @@ func lookupGroupId(gid string) (*Group, error) {
427
487
if err != nil {
428
488
return nil , err
429
489
}
430
- if t != syscall . SidTypeGroup && t != syscall . SidTypeWellKnownGroup && t != syscall . SidTypeAlias {
490
+ if ! isValidGroupAccountType ( t ) {
431
491
return nil , fmt .Errorf ("lookupGroupId: should be group account type, not %d" , t )
432
492
}
433
493
return & Group {Name : groupname , Gid : gid }, nil
@@ -465,7 +525,7 @@ func listGroups(user *User) ([]string, error) {
465
525
if err != nil {
466
526
return nil , err
467
527
}
468
- username , domain , err := lookupUsernameAndDomain (sid )
528
+ username , domain , _ , err := lookupUsernameAndDomain (sid )
469
529
if err != nil {
470
530
return nil , err
471
531
}
0 commit comments