Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Downloading a file from a Remote File share throws access denied

I have the following code in ASP.Net Generic handler to download the file.

// generate you file
// set FilePath and FileName variables
string stFile = FilePath + FileName;
try {
    response.Clear();
    response.ContentType = "application/pdf";
    response.AppendHeader("Content-Disposition", "attachment; filename=" + FileName + ";");
    response.TransmitFile(stFile);
} catch (Exception ex) {
    // any error handling mechanism
} finally {
    response.End();
}

it works fine with local path - like "D:\Files\Sample.pdf" or "\localserver\Files\Sample.pdf".

but it throws and access denied error while trying to access the Network file share like "\anotherServer\Files\Sample.pdf".

Does it related to double hopping? If can I use spsecurity.runwithelevatedprivileges to fix this issue? or what are the other options?

As per this article https://weblogs.asp.net/owscott/iis-windows-authentication-and-the-double-hop-issue it seems to be a double hopping issue. How do I address this? All I want is that end user should be able to download remote resources by using asp.net generic handler.

like image 499
Karthikeyan Vijayakumar Avatar asked Mar 17 '18 12:03

Karthikeyan Vijayakumar


People also ask

How do I fix access denied downloads?

Right-click the file or folder, and then click Properties. Click the Security tab. Under Group or user names, click your name to see the permissions that you have. Click Edit, click your name, select the check boxes for the permissions that you must have, and then click OK.

What is the meaning of Access Denied?

Access denied is an error message displayed when you do not have appropriate access rights. If you are being denied access to a network share, Intranet, or the Internet, and are receiving the access denied message, you need permission to gain access.


1 Answers

I have to deal with something similar once and to get this to work by mapping network share to specific drive.

Solution to add credentials to your network share before trying to access it didn't work (but you can try it as a first attempt)

NetworkCredential nc = new NetworkCredential("<login>", "<pass>");
CredentialCache cache = new CredentialCache();
cache.Add(new Uri("<your network share>"), "Basic", nc);

If this doesn't work use the combined solution with mapping network share to drive. Use class NetworkDrive to make WinApi calls to WNetAddConnection2 and WNetCancelConnection2:

public class NetworkDrive
{
    public enum ResourceScope
    {
        RESOURCE_CONNECTED = 1,
        RESOURCE_GLOBALNET,
        RESOURCE_REMEMBERED,
        RESOURCE_RECENT,
        RESOURCE_CONTEXT
    }

    public enum ResourceType
    {
        RESOURCETYPE_ANY,
        RESOURCETYPE_DISK,
        RESOURCETYPE_PRINT,
        RESOURCETYPE_RESERVED
    }

    public enum ResourceUsage
    {
        RESOURCEUSAGE_CONNECTABLE = 0x00000001,
        RESOURCEUSAGE_CONTAINER = 0x00000002,
        RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
        RESOURCEUSAGE_SIBLING = 0x00000008,
        RESOURCEUSAGE_ATTACHED = 0x00000010,
        RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
    }

    public enum ResourceDisplayType
    {
        RESOURCEDISPLAYTYPE_GENERIC,
        RESOURCEDISPLAYTYPE_DOMAIN,
        RESOURCEDISPLAYTYPE_SERVER,
        RESOURCEDISPLAYTYPE_SHARE,
        RESOURCEDISPLAYTYPE_FILE,
        RESOURCEDISPLAYTYPE_GROUP,
        RESOURCEDISPLAYTYPE_NETWORK,
        RESOURCEDISPLAYTYPE_ROOT,
        RESOURCEDISPLAYTYPE_SHAREADMIN,
        RESOURCEDISPLAYTYPE_DIRECTORY,
        RESOURCEDISPLAYTYPE_TREE,
        RESOURCEDISPLAYTYPE_NDSCONTAINER
    }

    [StructLayout(LayoutKind.Sequential)]
    private class NETRESOURCE
    {
        public ResourceScope dwScope = 0;
        public ResourceType dwType = 0;
        public ResourceDisplayType dwDisplayType = 0;
        public ResourceUsage dwUsage = 0;
        public string lpLocalName = null;
        public string lpRemoteName = null;
        public string lpComment = null;
        public string lpProvider = null;
    }

    [DllImport("mpr.dll")]
    private static extern int WNetAddConnection2(NETRESOURCE lpNetResource, string lpPassword, string lpUsername, int dwFlags);

    [DllImport("mpr.dll")]
    static extern int WNetCancelConnection2(string lpName, Int32 dwFlags, bool bForce);

    private const int CONNECT_UPDATE_PROFILE = 0x1;
    private const int NO_ERROR = 0;

    public int MapNetworkDrive(string unc, string drive, string user, string password)
    {
        NETRESOURCE myNetResource = new NETRESOURCE();
        myNetResource.lpLocalName = drive;
        myNetResource.lpRemoteName = unc;
        myNetResource.lpProvider = null;
        int result = WNetAddConnection2(myNetResource, password, user, 0);
        if (result == 0)
            return result;
        else
            throw new Win32Exception(Marshal.GetLastWin32Error());
    }

    public int UnmapNetworkDrive(string drive)
    {
        int result = WNetCancelConnection2(drive, CONNECT_UPDATE_PROFILE, true);
        if (result != NO_ERROR)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        return result;
    }
}

And use it like this:

NetworkDrive nd = new NetworkDrive();

try
{
    if (nd.MapNetworkDrive("<your network share>", "Z:", "<login>", "<pass>") == 0)
    {
        NetworkCredential nc = new NetworkCredential("<login>", "<pass>");
        CredentialCache cache = new CredentialCache();
        cache.Add(new Uri("<your network share>"), "Basic", nc);

        // access network share using UNC path (not your drive letter)
    }
}
finally
{
    nd.UnmapNetworkDrive("Z:");
}
like image 115
tinamou Avatar answered Sep 19 '22 20:09

tinamou