@@ -3299,6 +3299,97 @@ int uname(struct utsname *buf)
3299
3299
return 0 ;
3300
3300
}
3301
3301
3302
+ /*
3303
+ * Determines whether the SID refers to an administrator or the current user.
3304
+ *
3305
+ * For convenience, the `info` parameter allows avoiding multiple calls to
3306
+ * `OpenProcessToken()` if this function is called more than once. The initial
3307
+ * value of `*info` is expected to be `NULL`, and it needs to be released via
3308
+ * `free()` after the last call to this function.
3309
+ *
3310
+ * Returns 0 if the SID indicates a dubious owner of system files, otherwise 1.
3311
+ */
3312
+ static int is_valid_system_file_owner (PSID sid , TOKEN_USER * * info )
3313
+ {
3314
+ HANDLE token ;
3315
+ DWORD len ;
3316
+ char builtin_administrators_sid [SECURITY_MAX_SID_SIZE ];
3317
+
3318
+ if (IsWellKnownSid (sid , WinBuiltinAdministratorsSid ) ||
3319
+ IsWellKnownSid (sid , WinLocalSystemSid ))
3320
+ return 1 ;
3321
+
3322
+ /* Obtain current user's SID */
3323
+ if (!* info &&
3324
+ OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY , & token )) {
3325
+ if (!GetTokenInformation (token , TokenUser , NULL , 0 , & len )) {
3326
+ * info = xmalloc ((size_t )len );
3327
+ if (!GetTokenInformation (token , TokenUser , * info , len ,
3328
+ & len ))
3329
+ FREE_AND_NULL (* info );
3330
+ }
3331
+ CloseHandle (token );
3332
+ }
3333
+
3334
+ if (* info && EqualSid (sid , (* info )-> User .Sid ))
3335
+ return 1 ;
3336
+
3337
+ /* Is the owner at least a member of BUILTIN\Administrators? */
3338
+ len = ARRAY_SIZE (builtin_administrators_sid );
3339
+ if (CreateWellKnownSid (WinBuiltinAdministratorsSid , NULL ,
3340
+ builtin_administrators_sid , & len )) {
3341
+ wchar_t name [256 ], domain [256 ];
3342
+ DWORD name_size = ARRAY_SIZE (name );
3343
+ DWORD domain_size = ARRAY_SIZE (domain );
3344
+ SID_NAME_USE type ;
3345
+ PSID * members ;
3346
+ DWORD dummy , i ;
3347
+ /*
3348
+ * We avoid including the `lm.h` header and linking to
3349
+ * `netapi32.dll` directly, in favor of lazy-loading that DLL
3350
+ * when, and _only_ when, needed.
3351
+ */
3352
+ DECLARE_PROC_ADDR (netapi32 .dll , DWORD ,
3353
+ NetLocalGroupGetMembers , LPCWSTR ,
3354
+ LPCWSTR , DWORD , LPVOID , DWORD ,
3355
+ LPDWORD , LPDWORD , PDWORD_PTR );
3356
+ DECLARE_PROC_ADDR (netapi32 .dll , DWORD ,
3357
+ NetApiBufferFree , LPVOID );
3358
+
3359
+ if (LookupAccountSidW (NULL , builtin_administrators_sid ,
3360
+ name , & name_size , domain , & domain_size ,
3361
+ & type ) &&
3362
+ INIT_PROC_ADDR (NetLocalGroupGetMembers ) &&
3363
+ /*
3364
+ * Technically, `NetLocalGroupGetMembers()` wants to assign
3365
+ * an array of type `LOCALGROUP_MEMBERS_INFO_0`, which
3366
+ * however contains only one field of type `PSID`,
3367
+ * therefore we can pretend that it is an array over the
3368
+ * type `PSID`.
3369
+ *
3370
+ * Also, we simply ignore the condition where
3371
+ * `ERROR_MORE_DATA` is returned; This should not happen
3372
+ * anyway, as we are passing `-1` as `prefmaxlen`
3373
+ * parameter, which is equivalent to the constant
3374
+ * `MAX_PREFERRED_LENGTH`.
3375
+ */
3376
+ !NetLocalGroupGetMembers (NULL , name , 0 , & members , -1 ,
3377
+ & len , & dummy , NULL )) {
3378
+ for (i = 0 ; i < len ; i ++ )
3379
+ if (EqualSid (sid , members [i ]))
3380
+ break ;
3381
+
3382
+ if (INIT_PROC_ADDR (NetApiBufferFree ))
3383
+ NetApiBufferFree (members );
3384
+
3385
+ /* Did we find the `sid` in the members? */
3386
+ return i < len ;
3387
+ }
3388
+ }
3389
+
3390
+ return 0 ;
3391
+ }
3392
+
3302
3393
/*
3303
3394
* Verify that the file in question is owned by an administrator or system
3304
3395
* account, or at least by the current user.
@@ -3309,11 +3400,10 @@ int uname(struct utsname *buf)
3309
3400
static int validate_system_file_ownership (const char * path )
3310
3401
{
3311
3402
WCHAR wpath [MAX_LONG_PATH ];
3312
- PSID owner_sid = NULL ;
3403
+ PSID owner_sid = NULL , problem_sid = NULL ;
3313
3404
PSECURITY_DESCRIPTOR descriptor = NULL ;
3314
- HANDLE token ;
3315
3405
TOKEN_USER * info = NULL ;
3316
- DWORD err , len ;
3406
+ DWORD err ;
3317
3407
int ret ;
3318
3408
3319
3409
if (xutftowcs_long_path (wpath , path ) < 0 )
@@ -3325,63 +3415,37 @@ static int validate_system_file_ownership(const char *path)
3325
3415
& owner_sid , NULL , NULL , NULL , & descriptor );
3326
3416
3327
3417
/* if the file does not exist, it does not have a valid owner */
3328
- if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND ) {
3418
+ if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND )
3329
3419
ret = 0 ;
3330
- owner_sid = NULL ;
3331
- goto finish_validation ;
3332
- }
3333
-
3334
- if (err != ERROR_SUCCESS ) {
3420
+ else if (err != ERROR_SUCCESS )
3335
3421
ret = error (_ ("failed to validate '%s' (%ld)" ), path , err );
3336
- owner_sid = NULL ;
3337
- goto finish_validation ;
3338
- }
3339
-
3340
- if (!IsValidSid (owner_sid )) {
3422
+ else if (!IsValidSid (owner_sid ))
3341
3423
ret = error (_ ("invalid owner: '%s'" ), path );
3342
- goto finish_validation ;
3343
- }
3344
-
3345
- if (IsWellKnownSid (owner_sid , WinBuiltinAdministratorsSid ) ||
3346
- IsWellKnownSid (owner_sid , WinLocalSystemSid )) {
3424
+ else if (is_valid_system_file_owner (owner_sid , & info ))
3347
3425
ret = 1 ;
3348
- goto finish_validation ;
3349
- }
3350
-
3351
- /* Obtain current user's SID */
3352
- if (OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY , & token ) &&
3353
- !GetTokenInformation (token , TokenUser , NULL , 0 , & len )) {
3354
- info = xmalloc ((size_t )len );
3355
- if (!GetTokenInformation (token , TokenUser , info , len , & len ))
3356
- FREE_AND_NULL (info );
3357
- }
3358
-
3359
- if (!info )
3360
- ret = 0 ;
3361
3426
else {
3362
- ret = EqualSid ( owner_sid , info -> User . Sid ) ? 1 : 0 ;
3363
- free ( info ) ;
3427
+ ret = 0 ;
3428
+ problem_sid = owner_sid ;
3364
3429
}
3365
3430
3366
- finish_validation :
3367
- if (!ret && owner_sid ) {
3431
+ if (!ret && problem_sid ) {
3368
3432
#define MAX_NAME_OR_DOMAIN 256
3369
- wchar_t owner_name [MAX_NAME_OR_DOMAIN ];
3370
- wchar_t owner_domain [MAX_NAME_OR_DOMAIN ];
3433
+ wchar_t name [MAX_NAME_OR_DOMAIN ];
3434
+ wchar_t domain [MAX_NAME_OR_DOMAIN ];
3371
3435
wchar_t * p = NULL ;
3372
3436
DWORD size = MAX_NAME_OR_DOMAIN ;
3373
3437
SID_NAME_USE type ;
3374
- char name [3 * MAX_NAME_OR_DOMAIN + 1 ];
3438
+ char utf [3 * MAX_NAME_OR_DOMAIN + 1 ];
3375
3439
3376
- if (!LookupAccountSidW (NULL , owner_sid , owner_name , & size ,
3377
- owner_domain , & size , & type ) ||
3378
- xwcstoutf (name , owner_name , ARRAY_SIZE (name )) < 0 ) {
3379
- if (!ConvertSidToStringSidW (owner_sid , & p ))
3380
- strlcpy (name , "(unknown)" , ARRAY_SIZE (name ));
3440
+ if (!LookupAccountSidW (NULL , problem_sid , name , & size ,
3441
+ domain , & size , & type ) ||
3442
+ xwcstoutf (utf , name , ARRAY_SIZE (utf )) < 0 ) {
3443
+ if (!ConvertSidToStringSidW (problem_sid , & p ))
3444
+ strlcpy (utf , "(unknown)" , ARRAY_SIZE (utf ));
3381
3445
else {
3382
- if (xwcstoutf (name , p , ARRAY_SIZE (name )) < 0 )
3383
- strlcpy (name , "(some user)" ,
3384
- ARRAY_SIZE (name ));
3446
+ if (xwcstoutf (utf , p , ARRAY_SIZE (utf )) < 0 )
3447
+ strlcpy (utf , "(some user)" ,
3448
+ ARRAY_SIZE (utf ));
3385
3449
LocalFree (p );
3386
3450
}
3387
3451
}
@@ -3390,11 +3454,12 @@ static int validate_system_file_ownership(const char *path)
3390
3454
"For security reasons, it is therefore ignored.\n"
3391
3455
"To fix this, please transfer ownership to an "
3392
3456
"admininstrator." ),
3393
- path , name );
3457
+ path , utf );
3394
3458
}
3395
3459
3396
3460
if (descriptor )
3397
3461
LocalFree (descriptor );
3462
+ free (info );
3398
3463
3399
3464
return ret ;
3400
3465
}
0 commit comments