I have an ASP.NET app which needs to save files to a network share (Samba).
The share requires a username and password to connect.
I have mapped a persistent drive to the share and provided the login credentials whilst logged in to the server as DOMAIN\WEBUSER.
I have changed the virtual directory which hosts my app to use the DOMAIN\WEBUSER account instead of the IWAM account.
However the user still cannot see the mapped drive.
What am I missing out?
Did you try mapping the drive in code? Here is a class for doing just that...
public static class NetworkDrives
{
public static bool MapDrive(string DriveLetter, string Path, string Username, string Password)
{
bool ReturnValue = false;
if(System.IO.Directory.Exists(DriveLetter + ":\\"))
{
DisconnectDrive(DriveLetter);
}
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "net.exe";
p.StartInfo.Arguments = " use " + DriveLetter + ": " + Path + " " + Password + " /user:" + Username;
p.Start();
p.WaitForExit();
string ErrorMessage = p.StandardError.ReadToEnd();
string OuputMessage = p.StandardOutput.ReadToEnd();
if (ErrorMessage.Length > 0)
{
throw new Exception("Error:" + ErrorMessage);
}
else
{
ReturnValue = true;
}
return ReturnValue;
}
public static bool DisconnectDrive(string DriveLetter)
{
bool ReturnValue = false;
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "net.exe";
p.StartInfo.Arguments = " use " + DriveLetter + ": /DELETE";
p.Start();
p.WaitForExit();
string ErrorMessage = p.StandardError.ReadToEnd();
string OuputMessage = p.StandardOutput.ReadToEnd();
if (ErrorMessage.Length > 0)
{
throw new Exception("Error:" + ErrorMessage);
}
else
{
ReturnValue = true;
}
return ReturnValue;
}
}
It is best to use the UNC if you can as the mapped drives generally are linked to the interactive user and the virtual directory is probably connecting with a service or netwrok login type.
Another possible fix is described in this KB articleError occurs when you configure IIS to use a Samba network share as its root. Excerpted below.
Important These steps may increase
your security risk. These steps may
also make the computer or the network
more vulnerable to attack by malicious
users or by malicious software such as
viruses. We recommend the process that
this article describes to enable
programs to operate as they are
designed to or to implement specific
program capabilities. Before you make
these changes, we recommend that you
evaluate the risks that are associated
with implementing this process in your
particular environment. If you decide
to implement this process, take any
appropriate additional steps to help
protect the system. We recommend that
you use this process only if you
really require this process.
Warning This method involves a
security risk because the user who
created the mapping must remain logged
on to the local console. Therefore,
the only security is by locking the
computer. To work around this problem,
do the following:
Map a drive letter to \servername\iisroot using "root" and
"password."
In the Samba virtual directory, change the home directory from Share
on Another Computer to Local
Directory, and then specify the drive
letter that you mapped in step 1.
Restart the Web site, and then test it by browsing.
It's been a while (6 or 8 years) since I've done something like this, but as I recall, I also had to run the APP Pool as the domain user that had access to the network share in order for IIS to be able to serve up files from it.
Related
I'm trying the change some configurations of the network adapters locally. I have to use a non-administrative user, but I just get the return value "91", which stands for "Access Denied", when I try to invoke methods. It was possible to set the rights for the "EnableStatic" method, but "SetDNSServerSearchOrder" doesn't work at all. It always returns "Access Denied". I cannot even disable the network adapter (in Win32_NetworkAdapter). Everything works great with an admin user. Since I had the same problem with "EnableStatic", I'm pretty sure, that there must be a way to get this working with the other methods. I set the security options in wmimgmt.msc for all namespaces, I set the limits in dcomcnfg, I granted permissions in the registry for tcpip, added the user in distributed com-users, performance groups, network configuration operators, but there is no way to get this working. I recognized, that there is no dns ip, when my tool was running. So something changes although the method is returning "Access Denied". Any ideas? Even Microsoft couldn't help me yet, since three weeks now. The tool is running without any problems, when I'm logged in as admin. Here is my C# Code: (and sorry for some mistakes in my written English ;) )
private static ManagementScope CreateScope(string strScope)
{
ManagementScope scope = new ManagementScope(strScope);
scope.Options.Impersonation = ImpersonationLevel.Impersonate;
scope.Options.Authentication = AuthenticationLevel.Packet;
return scope;
}
public static void SetIP(String strScope, String strQuery, string IPAddress, string SubnetMask, string Gateway, string DNSServer1, string DNSServer2)
{
ManagementScope scope = CreateScope(strScope);
SelectQuery query = new SelectQuery(strQuery);
Collection<object> objCol = new Collection<object>();
scope.Connect();
ManagementObjectCollection mobjCol = null;
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
{
mobjCol = searcher.Get();
}
foreach (ManagementObject objMO in mobjCol)
{
try
{
ManagementBaseObject objNewIP = null;
ManagementBaseObject objSetIP = null;
ManagementBaseObject objNewGate = null;
ManagementBaseObject objNewDNS = null;
objNewIP = objMO.GetMethodParameters("EnableStatic");
objNewGate = objMO.GetMethodParameters("SetGateways");
objNewDNS = objMO.GetMethodParameters("SetDNSServerSearchOrder");
//Set DefaultGateway
objNewGate["DefaultIPGateway"] = new string[] { Gateway };
objNewGate["GatewayCostMetric"] = new int[] { 1 };
//Set IPAddress and Subnet Mask
objNewIP["IPAddress"] = new string[] { IPAddress };
objNewIP["SubnetMask"] = new string[] { SubnetMask };
//Set DNS servers
objNewDNS["DNSServerSearchOrder"] = new string[] { DNSServer1, DNSServer2 };
//Invoke all changes
objSetIP = objMO.InvokeMethod("EnableStatic", objNewIP, null);
MessageBox.Show("EnableStatic: " + objSetIP["ReturnValue"].ToString());
objSetIP = objMO.InvokeMethod("SetGateways", objNewGate, null);
MessageBox.Show("SetGateways: " + objSetIP["ReturnValue"].ToString());
objSetIP = objMO.InvokeMethod("SetDNSServerSearchOrder", objNewDNS,null);
MessageBox.Show("SetDNSServerSearchOrder: " + objSetIP["ReturnValue"].ToString());
}
catch (ManagementException ex)
{
MessageBox.Show("Unable to Set IP : " + ex.Message);
}
}
}
non-administrative users need permissions to run WMI queries. You can set the relevant permissions using WMI Control [under ServerManager / Configuration or Computer Management / Services & Applications] ref: http://technet.microsoft.com/en-us/library/cc775497(v=ws.10).aspx
Try providing Execute Methods, Enable Account & Provider Write permissions to your non-admin user in the CIMV2 namespace.
I'm trying to authenticate against AD using application mode (ADAM), but keep getting unknown username or bad password. If I test the login in LDP.exe it logs in no problem, on simple bind. I've trawled through all similar posts with the same issue, but have not resolved it, any suggestions what I should be checking for?
private bool ValidateActiveDirectoryLogin(string Username, string Password)
{
bool Success = false;
System.DirectoryServices.DirectoryEntry Entry = new System.DirectoryServices.DirectoryEntry("LDAP://localhost:389/OU=Users,O=TestDirectory", Username, Password);
System.DirectoryServices.DirectorySearcher Searcher = new System.DirectoryServices.DirectorySearcher(Entry);
Searcher.SearchScope = System.DirectoryServices.SearchScope.Subtree;
try
{
System.DirectoryServices.SearchResult Results = Searcher.FindOne();
Success = (Results != null);
}
catch (Exception ex)
{
Success = false;
throw;
}
return Success;
}
Determine what context your application is hitting AD with. If your ASP.NET application pool identity is one that is low privileged, it won't have enough permissions to query active directory. If you don't want to create a custom user to run the app pool as with appropriate permissions - you could use the LogonUser API to make your ValidateActiveDirectoryLogin call under the security context of that account.
Finally, you should consider using System.DirectoryServices.AccountManagement if you are using .NET 3.5 or above.
You can use code like
bool validCreds = false;
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
validCreds = context.ValidateCredentials( username, password );
}
I get an error by a website, on which I use Windows Authentication.
Strange things:
Only occurs if user is not yet saved into database (new unknown user)
Appears only on live system, everything fine on local development environment
This is what I get in a logging mail:
Source : System.DirectoryServices
Message: The server is not operational.
Trace:
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.DirectorySearcher.FindAll(Boolean findMoreThanOne)
at System.DirectoryServices.DirectorySearcher.FindOne()
at Smarthouse.Labs.DataAccess.UserListManager.SaveUser(String windowsUserName)
This is how I implement DirectorySearch:
private void SaveUser(string windowsUserName)
{
string[] domainAndUser = windowsUserName.Split('\\');
string domain = domainAndUser[0];
string username = domainAndUser[1];
DirectoryEntry entry = new DirectoryEntry("LDAP://" + domain);
DirectorySearcher search = new DirectorySearcher(entry);
try
{
// Bind to the native AdsObject to force authentication.
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
search.PropertiesToLoad.Add("sn");
search.PropertiesToLoad.Add("givenName");
search.PropertiesToLoad.Add("mail");
SearchResult result = search.FindOne();
if (result == null)
{
throw new Exception("No results found in Windows authentication.");
}
User userToSave = new User();
userToSave.FirstName = (String) result.Properties["givenName"][0];
userToSave.LastName = (String) result.Properties["sn"][0];
userToSave.Email = (String) result.Properties["mail"][0];
userToSave.Username = windowsUserName;
userToSave.Guid = Guid.NewGuid();
SaveUser(userToSave);
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message, ex);
}
finally
{
//Dispose service and search to prevent leek in memory
entry.Dispose();
search.Dispose();
}
}
If more code examples are needed just tell me.
Your problem is that you're using a "plain" domain name to bind - this won't work in LDAP. Actually, if you try to bind to LDAP://MyDomain, what you're really doing is trying to bind to the server called MyDomain.
You need a valid LDAP bind string - something like LDAP://dc=yourdomain,dc=local or something.
To find out what your default LDAP binding context is, use this code snippet:
DirectoryEntry deRoot = new DirectoryEntry("LDAP://RootDSE");
if (deRoot != null)
{
string defaultNamingContext = deRoot.Properties["defaultNamingContext"].Value.ToString();
}
Once you have that string - use that as your bind string to your LDAP server.
And if you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context -- no domain name needed, uses default domain
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username);
if(user != null)
{
// do something here....
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!
You can use bind strings in the format LDAP://mydomain.com:389. I kept getting "Access is Denied" when trying to use the format LDAP://DC=mydomain,DC=com. Once I switched to the LDAP://mydomain.com:389 format, and bound using the AuthenticationTypes.ServerBind flag when constructing my DirectoryEntry, it worked great. This was in Azure App Service.
To add to marc_s's answer above, I needed to search multiple domains.
So for each Domain I did the following:
DirectoryEntry deRoot = new DirectoryEntry("LDAP://" +"DomainName"+ "/RootDSE");
string defaultNamingContext = "LDAP://" + deRoot.Properties["defaultNamingContext"].Value.ToString();
DirectoryEntry mySearchRoot = new DirectoryEntry(defaultNamingContext);
DirectorySearcher myDirectorySearcher = new DirectorySearcher(mySearchRoot);
Similar Error Happened to me (though it happened all the time and not in specific cases like pointed out here) because of a wrong Active Directory connection string. i used the corp instead the prod one .
Use something that works for another app in your organization if exists.
I have an ajax uploader to allow a user to upload a picture to the program that will display back to the user afterwards. I would like to check this file for viruses before allowing the file to stay in the server. Below is the code where it sends the file to the virus scanner. I would like to be able to see if the scan results yield a virus and then delete it from the server if it does have a virus.
try
{
Process myProcess = new Process();
myProcess.StartInfo.FileName = #"C:\Program Files\AVG\AVG8\avgscanx.exe";
//myProcess.StartInfo.FileName=#"C:\Program Files\ClamWin\bin\clamscan.exe";
string myprocarg = #"""C:\Documents and Settings\Administrator.USER20501\My Documents\Luke's Projects\DADS\212\Images\FinalLogo" + file.ClientFileName + #"""";
myProcess.StartInfo.Arguments = myprocarg;
myProcess.OutputDataReceived += new DataReceivedEventHandler(myProcess_OutputDataReceived);
myProcess.ErrorDataReceived += new DataReceivedEventHandler(myProcess_ErrorDataReceived);
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.StartInfo.RedirectStandardError = true;
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.CreateNoWindow = true;
myProcess.Start();
myProcess.BeginOutputReadLine();
myProcess.WaitForExit();
}
catch (Exception)
{
}
void myProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
// this value will always be null.......
string s = e.Data.ToString();
}
void myProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
string s = e.Data.ToString();
}
It's a rare coincidence that the user uploads a special hand-crafted jpg/png/whatever image file exploiting an unknown (antivirus useless in this case) vulnerability of the reading components/libraries.
Still more rare if the vulnerability is known and your AV knows the exploit signature (or intercepts the exploit before running it) blocking it and safe-guarding your system (with libraries not up-to-date).
Even more rare if the abovementioned operating system has DEP and ASLR aware (fully enforced) components.
Basically I'm running the same problem as this post Accessing mapped drives when impersonating in ASP.NET
I'm working on a legacy website and I need to allow the admins to change the site's logo, banners, etc, from an image file on their desktops to a mapped drive on the server.
So, their website is using impersonation whenever it needs to save on the drive, and it's working just fine; however I can't manage to make it work on their test environment nor in my test environment.
¿Any ideas? I've double checked user and password (the code doesn't specify domain) and that's not the issue.
Here's an excerpt from the code that handles impersonation:
public bool ImpersonateUser(String user, String password, String domain)
{
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUserA(user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
//... rest of the code
And a -sanitized- test:
if (impUtility.ImpersonateUser("user", "password", string.Empty))
{
fu.SaveAs(#"C:\Images\" + imgName);
}
I couldn't get that to work either.
Then I realized that even if I could implement it, there is an easier way.
What I did was share the folder on the target machine, and give only read/write permissions to the users that would be using my application.
//Impersonate user to save file on server
WindowsIdentity wi = (WindowsIdentity)User.Identity;
WindowsImpersonationContext wic = null;
try
{
wic = wi.Impersonate();
if (wi.IsAuthenticated)
asyncFileUpload.SaveAs(location);
}
catch (Exception ex)
{
//Log Error or notify here
success = false;
}
finally
{
if (wic != null)
wic.Undo();
}
I created an AD group for the users, and give read/write permissions for those users on the hidden shared drive. This makes it easier to maintain, since I don't have to create mapped drives for each user.