简体   繁体   中英

Get permission to a file for another user by login name c#

How can I find out if another user (by login name) has access to read a file?

Does this require me to have to use DirectoryInfo.GetAccessControl().GetAccessRules(...) to get the Access Control List for a specific file, and then query AD for the target user's entire list of AD group memberships (direct or indirect) and then compare if there is any matching group associated with the target file?

Is there any other way to do this? Some of these AD queries are taking 30 seconds due to the sheer number of group memberships a user may have.

Windows can resolve very quickly if the logged in user has access to files. If anyone can shed some light on how it is doing this so I can make the same method calls to check if another user has access, then please let me know.

The reason I need to do this is because I have links on a web page to return files from a network drive. Some browsers (eg chrome) cannot be redirected to UNC paths, so they are redirected to a .NET handler/page which uses impersonation. Due to double-hop, the handler can not access the UNC path to as the .NET impersonated user, so therefore I need to use a service account, but it needs to determine whether a specific user by login is allowed to read a file before returning it.

Any alternatives are welcome.

Windows provides the AccessCheck function, note that the user being checked is represented by a token. As NetMage commented, the group membership is fetched from AD only during token creation and cached, so it doesn't have to be repeated for each security check.

A function with similar capability but a simpler interface is GetEffectiveRightsFromAcl .

I don't know whether there is a managed API for doing this with a .NET SecurityPrincipal. It may be that you have to p/invoke AccessCheck . Trying to duplicate the logic for checking ACLs is definitely not recommended.

For anyone else who needs to do this. This is what i did.

    [DllImport("advapi32.dll")]
    private static extern uint GetEffectiveRightsFromAcl(byte[] pacl, ref TRUSTEE pTrustee, ref uint pAccessRights);

    private enum MULTIPLE_TRUSTEE_OPERATION
    {
        NO_MULTIPLE_TRUSTEE,
        TRUSTEE_IS_IMPERSONATE
    }

    private enum TRUSTEE_FORM
    {
        TRUSTEE_IS_SID,
        TRUSTEE_IS_NAME,
        TRUSTEE_BAD_FORM,
        TRUSTEE_IS_OBJECTS_AND_SID,
        TRUSTEE_IS_OBJECTS_AND_NAME
    }

    private enum TRUSTEE_TYPE
    {
        TRUSTEE_IS_UNKNOWN,
        TRUSTEE_IS_USER,
        TRUSTEE_IS_GROUP,
        TRUSTEE_IS_DOMAIN,
        TRUSTEE_IS_ALIAS,
        TRUSTEE_IS_WELL_KNOWN_GROUP,
        TRUSTEE_IS_DELETED,
        TRUSTEE_IS_INVALID,
        TRUSTEE_IS_COMPUTER
    }

    private struct TRUSTEE
    {
        public IntPtr pMultipleTrustee;
        public MULTIPLE_TRUSTEE_OPERATION MultipleTrusteeOperation;
        public TRUSTEE_FORM TrusteeForm;
        public TRUSTEE_TYPE TrusteeType;
        public IntPtr ptstrName;
    }

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern void BuildTrusteeWithSid(
        ref TRUSTEE pTrustee,
        byte[] sid
    );

    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool QueryServiceObjectSecurity(SafeHandle serviceHandle, System.Security.AccessControl.SecurityInfos secInfo, byte[] lpSecDesrBuf, uint bufSize, out uint bufSizeNeeded);

    // Reference for these flags: https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectoryrights(v=vs.110).aspx
    [System.FlagsAttribute]
    public enum ServiceAccessFlags : uint
    {
        QueryConfig = 1,
        ChangeConfig = 2,
        QueryStatus = 4,
        EnumerateDependents = 8,
        Start = 16,
        Stop = 32,
        PauseContinue = 64,
        Interrogate = 128,
        UserDefinedControl = 256,
        Delete = 65536,
        ReadControl = 131072,
        WriteDac = 262144,
        WriteOwner = 524288,
        Synchronize = 1048576,
        AccessSystemSecurity = 16777216,
        GenericAll = 268435456,
        GenericExecute = 536870912,
        GenericWrite = 1073741824,
        GenericRead = 2147483648
    }

    public enum EXTENDED_NAME_FORMAT
    {
        NameUnknown = 0,
        NameFullyQualifiedDN = 1,
        NameSamCompatible = 2,
        NameDisplay = 3,
        NameUniqueId = 6,
        NameCanonical = 7,
        NameUserPrincipal = 8,
        NameCanonicalEx = 9,
        NameServicePrincipal = 10,
        NameDnsDomain = 12
    }

    [DllImport("secur32.dll", CharSet = CharSet.Auto)]
    public static extern int GetUserNameEx(int nameFormat, StringBuilder userName, ref int userNameSize);

    public static string GetUserUpn()
    {
        string upn = null;
        StringBuilder userName = new StringBuilder(1024);
        int userNameSize = userName.Capacity;
        if (GetUserNameEx((int)EXTENDED_NAME_FORMAT.NameUserPrincipal, userName, ref userNameSize) != 0)
        {
            upn = userName.ToString();
        }
        return upn;
    }

    /// <summary>
    /// Returns the user access flags by path and UPN. This can be used to determine the level of access another user has to a file.
    /// </summary>
    public static ServiceAccessFlags? GetUserPermission(string path, string upn)
    {
        WindowsIdentity windowsIdentity = new WindowsIdentity(upn);

        DirectoryInfo di = new DirectoryInfo(path);
        DirectorySecurity ds = di.GetAccessControl();

        RawSecurityDescriptor rsd = new RawSecurityDescriptor(ds.GetSecurityDescriptorBinaryForm(), 0);
        RawAcl racl = rsd.DiscretionaryAcl;
        DiscretionaryAcl dacl = new DiscretionaryAcl(false, false, racl);

        byte[] daclBuffer = new byte[dacl.BinaryLength];
        dacl.GetBinaryForm(daclBuffer, 0);

        SecurityIdentifier sid = windowsIdentity.User;
        byte[] sidBuffer = new byte[sid.BinaryLength];
        sid.GetBinaryForm(sidBuffer, 0);

        TRUSTEE t = new TRUSTEE();
        BuildTrusteeWithSid(ref t, sidBuffer);

        uint access = 0;
        uint hr = GetEffectiveRightsFromAcl(daclBuffer, ref t, ref access);

        ServiceAccessFlags serviceAccess = (ServiceAccessFlags)access;

        int i = Marshal.Release(t.ptstrName);

        return serviceAccess;
    }

Usage example:

FileService.ServiceAccessFlags? flags = null;
try { flags = FileService.GetUserPermission(uncFilePath, upn); } 
catch (DirectoryNotFoundException ex)
{
    LogWarning("Could not resolve permission because the file was not found as the service account (" + FileService.GetUserUpn() + ")");
}
catch (Exception ex)
{
    LogWarning("Could not resolve permission on this file (" + FileService.GetUserUpn() + "): " + ex.Message);
}
if (flags.HasValue)
{
    // Check for Read access
    if ((flags.Value & FileService.ServiceAccessFlags.ReadControl) == FileService.ServiceAccessFlags.ReadControl)
    {
        hasPermission = true;
        LogMessage("User has access (upn: " + upn + "): " + flags.ToString());
    }
    else
    {
        hasPermission = false;
        LogError("User does not have read access to file (upn: " + upn + "): " + flags.ToString());
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM