Why doesn't StreamWriter work in a Windows Service? - c#

I have this simple code that records appends a log to a text file:
public static void RecordToFile(string filename, Log log)
{
TextWriter textWriter = new StreamWriter(Constants.APP_PATH +
"\\" + filename, true);
textWriter.WriteLine(log.ToString());
textWriter.Close();
}
This works perfectly in a Windows Forms application. However, using the instsrv and srvany trick, I made this a Windows Service. The service runs fine, accesses the database, performs queries and all... Except for this StreamWriter. The log just doesn't get updated as it should. Any ideas why?

Most likely the service is running under user credentials that does not have access rights to that directory.
So check the properties dialog for the service, and check the Log On tab to see what it logs on as.

Possible Reasons:
Constants.APP_PATH is pointing to a mapped drive - services don't run in the same environment as a logged-in user, so the path may not be valid
Permissions - depending on what user the service is running as, it may not have access to the same set of directories that the WinForms app did

Without more information there isn't much that anyone can help with. In what way, exactly, does it not function? Do you get an exception? Does it just fail silently?
Just a couple of tips...
1) Don't use string concatenation to create file paths. Use System.IO.Path.Combine instead. Like this:
TextWriter textWriter = new StreamWriter(
System.IO.Path.Combine(Constants.APP_PATH, filename), true);
2) Enclose your writer in a using() block. Like so:
using(TextWriter textWriter = new StreamWriter(
System.IO.Path.Combine(Constants.APP_PATH, filename), true))
{
textWriter.WriteLine(log.ToString());
}
3) Verify that the account the service is using has access to create/overwrite files in that directory. Often service accounts like LOCAL_SYSTEM or NETWORK_SERVICE aren't going to have the same permissions as user accounts would. This could explain why it works as a user but not as a service. It could also be that your APP_PATH constant points to something user specific, like a drive mapping to a network share. Drive mappings don't span users, so this could also be an issue.

Without any more information I'd guess that the account your service is running under doesn't have rights to open the file for writing.

Related

StreamWriter throws IOException Logon failure: unknown user name or bad password

I'm trying to have my web service write to a shared drive on our intranet. I'm getting an IOException on my StreamWriter
string fullpath = path + "\\" + fileName;
using (System.IO.StreamWriter file = new System.IO.StreamWriter(fullpath, append))
{
file.WriteLine(contents);
file.Close();
}
And the exception I'm seeing,
IOException - Logon failure: unknown user name or bad password
If I'm understanding correctly, the user of the IIS web server needs to have access to write to the shared drive. I'm wondering if there's a way I can hard-code credentials for now, to write to the shared drive for debugging purposes. How can I provide credentials to a StreamWriter, or is there another mechanism I should be using?
I looked into Impersonation (using StreamWriter on server in C#) and would like to use that as a last resort if possible.
Whenever you write to a network drive programmatically, you use the username and password that you used to start the program with. Unless you are using a domain, this will generate an error because the network drive does not recognize your username and password.
So to fix this, all you need to do is run the same code you have as a different user. This is basically the impersonation stuff you wanted to avoid, but I found a useful Stack Overflow Thread that explains this and has links to wrapper classes you can use in your code to make things simpler.
As Simple as this (from thread):
using (Impersonation.LogonUser(domain, username, password, logonType))
{
// do whatever you want as this user.
}
This should resolve your issue

Application Cannot Acces File even though I can copy it using Explorer

An internal application in our company requires files aa, bb ... zz in folder X to be read upon startup. When I (as someone with FullControl access to folder X) launch the app, all goes fine. When any of my colleagues (who only have Read access to folder X) launch the app, they get an "Access denied to file aa ... " exception.
The files are being read by the following routine
public static void readFromBinaryFile(this QIHasFileIo xThis, string xFilePath)
{
if (!System.IO.File.Exists(xFilePath))
throw new System.Exception("File to read " + xFilePath + " does not exist ... ");
if (xThis == null)
throw new NullReferenceException("xThis cannot be null, as it is a readonly reference ... ");
using (BinaryReader xReader = new BinaryReader(new FileStream(xFilePath, FileMode.Open, FileAccess.Read)))
xThis.readObject(xReader);
}
i.e. I am specifying the Read mode, which should in turn require only Read access to the folder. When my colleagues go to folder X in Explorer, then can copy the aa, bb, ... files to their Desktops, which means they DO have Read access to the files.
So I am intrigued. This weird behaviour started with changes to the data server a couple days ago. The most notable changes were that 1/ my colleagues stopped having admin rights on the data server 2/ some GPO's might have been messed up (it happenned before in the company). The IT department is baffled as well, so I have no clue how to proceed.
Any hint is much appreciated,
Daniel
Edit: An already deleted post proposed using FileShare.ReadWrite. I am grateful to the author for the comment, however the file is guaranteed not to have a write-lock on it. Hence, the why File.copy works but File.OpenRead prompts access denied? thread is not relevant here.
You need to add FileShare.ReadWrite to the parameters passed to the FileStream constructor.
This prevents the application trying to get an exclusive read lock, which might not be possible under some conditions where a shared read-write lock is possible (such as the file being left open for writing by another process).
I had similar problem while reading a file. The issue was with the level of access for the ActiveDirectory group for the particular group (readers group) of users was not setup correctly.
I am not sure if you are using AD group authentication on the server. I would recommend you to check the type of access and groups your colleagues have. Also, you need to check how your application is currently authenticating the users to access the directory.

How to deny access to directory contents (IIS served site) via browser

I have some files kept in SiteFolder/ which I need to access through the C# project but I do not want them to be listed via browser at those path. How can I accomplish this? Is there any setting in IIS that could be changed?
Also, as I gave full control to IIS user to access/read/write this directory files, what is the solution then?
Basically I do not want anyone to access this directory's content via browser but should be accessible by IIS.
Thanks in advance.
Edit 1: This is the file write functionality which should remain unaffected -
FileStream file = new FileStream(Server.MapPath("~/SiteFolder/example.txt"), FileMode.Append, FileAccess.Write, FileShare.Write);
using (StreamWriter fileWriter = new StreamWriter(file))
{
fileWriter.WriteLine("Something");
}
Additionally, as this code is running on Amazon EC2, I had to provide full control of the folder and file to these users -
IUSR
MyProjectName
I am going to assume that you're impersonating the caller when trying to write to the file in question. Otherwise, if you were running as the system account, it doesn't seem like write permission should be affected.
Therefore, what I suggest in addition to the configuration mentioned above (https://serverfault.com/questions/37762/block-access-to-subdirectory-using-web-config) is to run the code block that writes the file as the system account by temporarily undoing impersonation like this:
using (System.Security.Principal.WindowsImpersonationContext wic =
System.Security.Principal.WindowsIdentity.Impersonate(IntPtr.Zero))
{
FileStream file = new FileStream(Server.MapPath("~/SiteFolder/example.txt"), FileMode.Append, FileAccess.Write, FileShare.Write);
using (StreamWriter fileWriter = new StreamWriter(file))
{
fileWriter.WriteLine("Something");
}
}
If your utilizing a default Internet Information System, the default folder wwwroot is locked directly to the designated user through your Application Pool. Which in most instances, will be a Network Service Account.
Your specific application will have a degree of permission, but your web-application by default shouldn't have access to the other contents of wwwroot.
The directory would be:
inetpub - wwwroot - Example
inetpub - wwwroot - Demo
So Example and Demo are both two separate web-sites that are hosted. So if you attempt to navigate to another directory, from one application, it should protect you automatically.
However, if you need to block a web-site from particular users or an application it is indeed possible. In the current state, the question is too broad.
Request Filtering
URL Rewrite
Those are two approaches, however if your utilizing Virtual Directories you should take a look at the following here.
Without a Virtual Directory, my above protection stands true:
A site is a container for applications and virtual directories, and
you can access it through one or more unique bindings.
The binding includes two attributes important for communication: the
binding protocol and the binding information. The binding protocol
defines the protocol over which communication between the server and
client occurs. The binding information defines the information that is
used to access the site. For example, the binding protocol of a Web
site can be either HTTP or HTTPS, and the binding information is the
combination of IP address, port, and optional host header.

Denied acces to a file

I have a code which is similar this:
string file;
using (StreamReader r = new StreamReader("xml.xml"))
{
file = r.ReadToEnd();
}
XElement xml = XElement.Parse(file);
using (XmlWriter w = XmlWriter.Create("xml.xml")) //The point of problem!
{
w.WriteStartDocument();
...;
w.WriteEndDocument();
}
When I try run it like a console application is everything all right. But problems start when I want to use it in an ASP.NET application. At the using line it throws UnauthorizedAccessException exception with a description "access to the path is denied". Why?
You need to check which account your application Pool is using to access your server files/folders, for example, make one code to copy one file to application folder, check all security info, copy and paste on this problem folder, normally use this account "IIS_IURRS" give full control to test only...
If IIS/the web server is configured correctly, an account with a very limited set of permissions is used. As your path points to the application directory, it is very likely that the application pool account is not allowed to write to this location.
If you run the code in a console application, your user's permissions are applied and it is more than likely that you are allowed to write to the output folder of the project as Visual Studio writes the build output there under your account.
I would not recommend to change the application pool account or the permissions of the application folder in the file system - it is a very sensible limitation that limits the amount of trouble an attacker can possibly make.
Therefore I'd recommend to either move the file to a folder that the account can write to without changing permissions or define a special one outside of the application folder hierarchy that the account is given permissions to.
Also keep in mind that multiple users might access the file at the same time, so a database might be a better choice to store the data.

Logging to TextFile from SharePoint

I'm trying to debug a webpart installed on a client's SharePoint instance. I wanted a quick and easy logging feature, so I thought of writing messages to a text file in the temp directory. SharePoint doesn't seem to like it, so what are my options?
IF you are writing to the temp directory, you will need to give the file (if it exists) or the directory rights for the IIS Application pool that the SharePoint IIS application is running under.
There are few ways of custom logging in sharepoint -
Use SPDiagnosticsService - You may write to the ULS via SPDiagnosticsService class.
Utilize diagnostics.asmx web service -
SharePointDiagnostics SharePointDiagnosticsObject = new SharePointDiagnostics();
SharePointDiagnosticsObject.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
string Response = SharePointDiagnosticsObject.SendClientScriptErrorReport(message, file, line, client, stack, team, originalFile);
For more details on usage of diagnostics.asmx refer the following link -
https://vivekkumar11432.wordpress.com/2016/09/23/how-to-do-logging-in-uls-from-csom-in-c/
For more details on logging refer the following link -
http://www.codeproject.com/Articles/620996/Five-suggestions-to-implement-a-better-logging-in
Don't use
Microsoft.Office.Server.Diagnostics.PortalLog.LogString("Message");
According to Microsoft documentation - LogString is reserved for internal use and is not intended to be used directly from your code.
I would guess that this is a permissions issue that SharePoint is blocking you on (and probably not telling you that it is). When you try to write to a text file on the server, you need to have elevated permissions in order to do it. You can accomplish this using SPSecurity.RunWithElevatedPrivileges. Something like the following, if you want just a simple, small-code solution.
SPSecurity.RunWithElevatedPrivileges(delegate() {
using (StreamWriter sw = new StreamWriter(#"C:\log.txt"))
{
//log information here
}
});
Try a logging framework like log4net, or write a small logging framework writing into an external database, you could also use lists to log if you want to stay inside sharepoint

Categories

Resources