@@ -3299,6 +3299,97 @@ int uname(struct utsname *buf)
32993299 return 0 ;
33003300}
33013301
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+
33023393/*
33033394 * Verify that the file in question is owned by an administrator or system
33043395 * account, or at least by the current user.
@@ -3309,11 +3400,10 @@ int uname(struct utsname *buf)
33093400static int validate_system_file_ownership (const char * path )
33103401{
33113402 WCHAR wpath [MAX_LONG_PATH ];
3312- PSID owner_sid = NULL ;
3403+ PSID owner_sid = NULL , problem_sid = NULL ;
33133404 PSECURITY_DESCRIPTOR descriptor = NULL ;
3314- HANDLE token ;
33153405 TOKEN_USER * info = NULL ;
3316- DWORD err , len ;
3406+ DWORD err ;
33173407 int ret ;
33183408
33193409 if (xutftowcs_long_path (wpath , path ) < 0 )
@@ -3325,63 +3415,37 @@ static int validate_system_file_ownership(const char *path)
33253415 & owner_sid , NULL , NULL , NULL , & descriptor );
33263416
33273417 /* 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 )
33293419 ret = 0 ;
3330- owner_sid = NULL ;
3331- goto finish_validation ;
3332- }
3333-
3334- if (err != ERROR_SUCCESS ) {
3420+ else if (err != ERROR_SUCCESS )
33353421 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 ))
33413423 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 ))
33473425 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 ;
33613426 else {
3362- ret = EqualSid ( owner_sid , info -> User . Sid ) ? 1 : 0 ;
3363- free ( info ) ;
3427+ ret = 0 ;
3428+ problem_sid = owner_sid ;
33643429 }
33653430
3366- finish_validation :
3367- if (!ret && owner_sid ) {
3431+ if (!ret && problem_sid ) {
33683432#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 ];
33713435 wchar_t * p = NULL ;
33723436 DWORD size = MAX_NAME_OR_DOMAIN ;
33733437 SID_NAME_USE type ;
3374- char name [3 * MAX_NAME_OR_DOMAIN + 1 ];
3438+ char utf [3 * MAX_NAME_OR_DOMAIN + 1 ];
33753439
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 ));
33813445 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 ));
33853449 LocalFree (p );
33863450 }
33873451 }
@@ -3390,11 +3454,12 @@ static int validate_system_file_ownership(const char *path)
33903454 "For security reasons, it is therefore ignored.\n"
33913455 "To fix this, please transfer ownership to an "
33923456 "admininstrator." ),
3393- path , name );
3457+ path , utf );
33943458 }
33953459
33963460 if (descriptor )
33973461 LocalFree (descriptor );
3462+ free (info );
33983463
33993464 return ret ;
34003465}
0 commit comments