I have a web application that needs to read (and possibly write) files from a network share. I was wondering what the best way to do this would be?
I can't give the network service or aspnet accounts access to the network share. I could possibly use impersonation.
The network share and the web application are both hosted on the same domain and I can create a new user on the domain specifically for this purpose however I'm not quite sure how to join the dots between creating the filestream and specifying the credentials to use in the web application.
Unfortunately the drive isn't mapped as a network drive on the machine, it's only available to me as a network share so unfortunately I can't make a transparent call.
There is one problem I can think of with impersonation... I can only impersonate one user per application domain I think but I'm happy to be corrected. I may need to write this file to several different shares which means I may have to impersonate several users.
I like the idea of creating a token... if I can do that I'll be able to ask the use up front for their credentials and then dynamically apply the security and give them meaningful error messages if access is denied... I'm off to play but I'll be back with an update.
Given everyone already has domain accounts. Try IIS integrated authentication. You will get an ugly logon box off network but your creds should pass down to the file share.
#lomaxx
Are you saying that only you have perms to the share or that you manually mapped it to a drive letter. If the later you can use ucn \host\share the same way you would use a c:\shared_folder.
Random
Would it be a burden to mirror the share to a local folder on the host? I hear ROBOCOPY is pretty handy.
Another Idea. Run IIS on your target share you can read via http and if you need to write investigate webdav.
I've had no problems connecting to network shares transparently as if they were local drives. The only issue you may have is what you mentioned: having the aspnet account gain access to the share. Impersonation is probably the best way to do this.
You should be able to use any filestream objects to access the network share as long as it has a drive letter on the server machine.
Impersonation worked well for me in this scenario. We had a wizard that uploaded a zip file through the website, but we load balanced the site. Therefore needed to setup a way to save the file on all the machines.
There are many different ways to do it. We decided to make all requests to run under the user we setup and just added the web.config entry and setup the security permissions on the folders for the user. This kb article explains the setup very well.
You do have some options and one of of those is impersonation as you mentioned. However, another one I like to use and have used in the past is a trusted service call. Let's assume for a moment that it's always much safer to limit access through IIS to ensure there are as few holes as possible. With that let's go down this road.
Build a WCF service that has a couple of entry points and the interface might look like this.
public interface IDocumentService
{
public string BuildTrustedRelationship(string privateKey);
public byte[] ReadFile(string token, string fileName);
public void WriteFile(string token, string fileName, byte[] file);
}
Now, you can host this service via a Windows service very easily and so now all you need to do is on Application_start build the relationship with the service to get your token and you're off to the races. The other nice thing here is that this service is internal, trusted, and I've even hosted it on the file server before and so it's much easier to grant permissions to this operation.
If you can create a new AD user, I think the simplest solution is to have the Application Pool run under that AD account's authority, which would mean your application is now running as the AD user. You would need to add the AD user to the IIS Worker Process Group on the machine running your application. Then as long as your AD user has write permissions on the network share, you should be able to use the UNC path in your file operations.
Related
I am implementing an application that will create a folder on the NTFS File Server.
For that, I have set up the distributed environment. Meaning, I have machine on which I have hosted my C# Web API and I have NTFS file server on which I have to create folders.
I want my API to create a folder on NTFS machine. How can I do that?
Do I need to share NTFS Drive/Folder to create a subfolder? If so, then with whom I need to share a NTFS folder (either server user or IIS_USRS)?
Is there any other way to create a folder without sharing a drive/folder on NTFS folder.
When I have done this in the past I have done it through a temporary impersonation. For example...
using (Impersonator impersonate = new Impersonator(#"UserName", "Password", "Domain"))
{
//Create directory or copy files across the network
}
I believe I lifted my impersonator code from A small C# Class for impersonating a User
Case 1:
if web server and file server are in the same domain, you can consider to use a domain user, and created a shared folder on NTFS server and grant full access for the domain user accessing it. (depends on your requirements).
On web server, for the web application pool, set the domain user as the identity/credential to run the pool. so you can easily use IIS management tool to update it if password gets changed. for the authentication, you can use whatever you want based on requirements, but remember while you call the code to create folders on NTFS server, you need to use app pool user. (for example, if you turn on impersonation on a different user in authentication, in your code, you need to do impersonation using the pool user.)
Case 2,
web server and file server are not in the same domain, I usually will set up a ftp server on the file server to allow specific users to access it (creating folders and upload files....). Otherwise when you may need your IT administrator to make File server domain trust web server domain, then you can do the same thing as case 1.
About impersonation code, it could be something like:
//get the identity of an appPool
using(System.Security.Principal.WindowsIdentity wid = System.Security.Principal.WindowsIdentity.GetCurrent())
{
using (System.Security.Principal.WindowsImpersonationContext ImpersonationCtx = wid.Impersonate())
{
//creating folders, uploading files to UNC path...
ImpersonationCtx.Undo();
}
}
If your case is one of this, hope it helps.
I know I can use the WNetAddConnection2 and similar variants to connect to remote shares using custom credentials. I would like to do the same for mapped drives (ex:\\MyShare\MyFolder mapped to Y:) and my local disks (eg: C:,D:,E:, etc...)
If I point WNetAddConnection to something not \\ it fails...
Is there a function specific for this scenario???
At work we use a WindowsImpersonationContext to handle this situation. Originally we used it in a VB.NET ASP.NET application, but it is readily modified.
Microsoft Support has a great article on how to do it. (We originally used it for Network Drives, but it can be expanded to local disks as well.)
You can basically impersonate a valid user account, which will grant you permissions that account would have in the situation. I had to use it to get access to a network drive at work, on a computer that was not a part of the domain. (As such, we had to provide a Username, Domain, and Password for this user account that was not on our domain network and this topic served us well.)
I need to create FTP users via a C# software, which is run Client-Side, I have no idea where to start or how to do that, I am not even sure if this is possible as this may require to be logged as root?
My other option would be to call a PHP script via the C# software, which could call a system() script?
This function needs to work on Linux shared hosting (as this is shared I can't do everything I can).
The only way YOU have to manage the FTP server is through DirectAdmin. So unless there is an API for DirectAdmin and you have access to it, or your shared hosting provider offers some kind of API, you won't be able to do this.
If you can manage your users on the shared host with system(), then by all means go that route.
If you can't, then you might need to create a script that runs on the server, that accepts incoming requests and then works directly with DirectAdmin to manage the users. This would require the script know the DirectAdmin credentials, and if any updates happen to DirectAdmin it could potentially break your script. I would be very careful with this method, as anyone with access to this script could then manage ftp users. If your client can manage these FTP users, anyone can. How will users be "authenticated" with the client if there is any authentication. If so, how are they authenticated? Because your "api" script will need to work with that authentication as well.
My question is, do you really NEED to create an FTP user? If they just need to upload a file, couldn't they simply upload a file via an online form? Or through the client? Are you required to use FTP?
EDIT:
So as I said in the comments, one idea would be to have a server side script (maybe PHP) that would handle managing the files. It would accept requests to upload/delete/blah the files. When a file is uploaded to the site it would be stored, and it would return a special token along with the location of the file.
The client would then take the token, store it locally, and when the user manages the file on the client it would use that token for future modifications. I'd generate this token randomly (not based off any data of the user). How you store the token is up to you, could be on the file system with the files or in a database.
The communication between the client and the site should be done through SSL if possible, to stop MITM attacks.
As for uploading files, it's really up to how your client authenticates it's users. If it's via a central database, the shared hosting server could access that, if you don't want to do that, then you'd need to something similar to a public key system. So that the client can send a key that the shared hosting script can tell if the user is valid or not. But then you have to have the script know when the key has been revoked, etc.
So it's up to you how you want to proceed with this part.
If this were me--and this is adding one more moving part, I realize--I'd decouple the creation of users, and place that in a long-running process on the remote server, where both the server process and the PHP facade work with something like Gearman to coordinate jobs. You're not going to be able to create the processes on the remote Linux server from C# without some external facility like rsh. Something just skeeves me out about the idea of creating users via a PHP site, unless it's only accessible over a private intranet with no external access. Having a separate process would give you just one extra step in which to validate.
In my ASP.NET application, I need to be able to authenticate/authorise against local Windows users/groups (ie. not Active Directory) on a different machine, as well as be able to change the passwords of said remote local Windows accounts.
Yes, I know Active Directory is built for this sort of thing, but unfortunately the higher ups have decreed it needs to be done this way (so authentication against users in a database is out as well).
I've tried using DirectoryEntry and WinNT like so:
DirectoryEntry user = new DirectoryEntry(String.Format("WinNT://{0}/{1},User",
serverName, username), username, password, AuthenticationTypes.Secure)
but this results in an exception when you try to log in more than one user:
Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.
I've tried making sure my DirectoryEntries are used inside a using block, so they're disposed properly, but this doesn't seem to fix the issue. Plus, even if that did work it is possible that two users could hit that line of code concurrently and therefore try to create multiple connections, so it would be fragile anyway.
Is there a better way to authenticate against local Windows accounts on a remote machine, authorise against their groups, and change their passwords?
Thanks for your help in advance.
In my opinion you cannot do this from your ASP.Net script. Because from the server you need to know what all client machines will access your page and provide sufficient rights to the server to access the client to do this extra user authentication and password change. Also this is cumbersome. One solution is to use an activex control and write this logic (user authentication and password change) in that activex control and provide the activex control sufficient rights in the clients. It is a bit ugly but this is the only possible solution without ADS.
I recently built a program that parses a remote file from \some_server\c$\directory\file.xls and it works fine on my local machine as just a normal aspx page.
Then I put the program into web part
form on my VM SharePoint server and I
get this error: Access to the path
'\some_server\c$\directory\file.xls'
is denied.
The file is shared to Domain\Authenticated Users so I am not sure why it would be denied? Is it possible my SharePoint is trying to call it with a local or network service account? How can I get it to read? Thank you.
Salamander is right, SharePoint doesn't run with trust to do this.
Changing the trust level for SharePoint in it's web.config from WSS_Medium to Full is the quick solution, but there are security implications..
Just a quick note, you could be running into the classic NTLM Double-Hop issue. You can authenticate to the front end, but because the front end does not have your password, it cannot then authenticate to a resource on another server.
Running with Elevated priviliges, and setting permissions based on the Application Pool identity could be one way of moving your forward.
I think you will need RunWithElevatedPrivleges which will make SharePoint use the application pool account. Also keep in mind you will have to make sure that application pool account has access to that network share. Avoid using full trust.
Can you explain further what exactly setting the trust level does for you?
I would think that if your app pool identity is a domain account you can use SPSecurity.RunWithElevatedPrivileges to use the app pool credentials to access the file. Or, use impersonate to explicitly pass another account's credentials.
SharePoint usually runs in a separate application pool. Please check the identity of this application pool.
I think to be able to access network path, your code has to run in FULL TRUST, which I don't think SharePoint does.
Why not store the file in SharePoint so you have better access to it? Put it in a hidden library and access it using SPSecurity.RunWithElevatedPrivledges.
There are caveats to RWEP. Any reference to SPSite and SPWeb obtained from the SPContext (ie SPContext.Current.Site) will still run under the privledges of the logged on user. You must explicity create a reference inside the RWEP delegate.
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(SPContext.Current.Site.Url))
{
using (SPWeb web = site.OpenWeb())
{
//... Do something with SPWeb
}
}
});
If you need to access the file from outside sharepoint to update it with existing processes you can use the file share path which is available for all SPDocumentLibrary's but going to Actions --> Open with Windows Explorer to obtain the network path.