Finding users' HOMEPATHs from a service - c#

In the process of writing a service I have
//# Service Account Information
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
in the installer for it.
I was having problems writing a log to
static string USERS_HOME_DIR = Environment.GetEnvironmentVariable("HOMEDRIVE") + Environment.GetEnvironmentVariable("HOMEPATH");
since when the service was running (installed "as administrator") the event logger was reporting
Service cannot be started. System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\Documents\Folder of Interest'
I need the HOMEPATHs of the users of the local_PC, any ideas how to get them?
UPDATE
Actually it would be better to just get the path for the currently logged on user, as their session and the service start. My sevice is not re-entrant but one user is better than none.

If I understand your question correctly what you're looking for are the special folder enumerations.
With something like:
String PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
If you want to run the service as the localsystem account there's a separate set of standards for storing the data. See this answer on serverfault.

Related

Programmatically grant local user rights to start a service with .Net

I want to implement an update service in my application (so users don't need admin rights to update my app once the service is installed, much like Google or Mozilla do their updates), and I think I found a good way to do this with WCF.
I have a WCFServiceLibrary-Project which contains the ServiceContract and the core functionality (download/install updates) and a Windows Service-Project which implements the WCFServiceLibrary as a Windows Service.
Additionally, there is a Visual Studio Installer-Project which installs the service and my application, which should be able to start/stop/communicate with the service using NamedPipes.
The service is configured to start manually with the LocalSystem-Account.
Now when the service is installed, I can start/stop it using services.msc (probably elevated), but not when I try it with net start Servicename (Error 5: Access denied) or with my application, which tells me that the local users probably don't have the permission to start/stop the service.
I need the service to run with higher permissions in order to perform the installation of updates, so I would like to give local users permission to start my service either during the first installation of the service or when the service starts for the first time (since I can trigger that also during installation).
However, how would I accomplish this with VB.NET (or C#)? I found some examples using API-Calls of advapi32.dll, but it didn't looks like the permission can be changed with this.
So, long story short, heres a summary of what I'm looking for:
Grant Group "Local Users" permission to Start my Service, best approach either during installation (Maybe with Custom Actions in Visual Studio Installer Project? Or in the ServiceInstaller-Class in the Windows Service-Project?) or the first time the service starts (OnStart-Event in Windows Service Project)
The service must not run with local user rights, since it would miss elevated privileges then which would be necessary to install updates.
I can't assign permissions through GPO/Local Policy since the users are not within our company, but all around the world. For the same reason I cannot assume that they can get an admin to elevate them everytime an update comes out.
I would like to avoid commandline calls if possible (as in assign permissions through command line, since those are most likely os-dependent)
Another solution would be to configure the Service as Automatic and Start it after install, but I don't like the idea that my service runs all the time since its only needed when my main application starts up.
Its most likely not a file permission issue. EVERYONE, SYSTEM and SERVICE got FULL ACCESS to the services folder and files in it.
There are already different similar questions here, but none did give a clear answer to this problem. One user probably did it using the WiX-Installer, but I would like to keep the Visual Studio Installer Project since it's pretty straight forward and easy to use.
After a bit more of googling and trying to find a "clean" solution, I've given up and using now Process.Start to execute sc.exe and set new Permissions after Installation.
Here's my ServiceInstaller-Class, for anyone curious:
[VB.NET]
Imports System.ComponentModel
Imports System.Configuration.Install
Imports System.ServiceProcess
<RunInstaller(True)>
Public Class SvcInstaller
Inherits Installer
Dim svcprocinst As ServiceProcessInstaller
Dim svcinst As ServiceInstaller
Public Sub New()
svcprocinst = New ServiceProcessInstaller
svcprocinst.Account = ServiceAccount.LocalSystem
svcinst = New ServiceInstaller
svcinst.ServiceName = "KrahMickeySvc"
svcinst.DisplayName = "Mickey-Service"
svcinst.Description = "This Service is used by KRAH Mickey for application updates and maintenance"
Installers.Add(svcprocinst)
Installers.Add(svcinst)
End Sub
Private Sub SvcInstaller_AfterInstall(sender As Object, e As InstallEventArgs) Handles Me.AfterInstall
'Set new permissions acc. to Security Descriptor Definition Language (SDDL)
'Source: https://blogs.msmvps.com/erikr/2007/09/26/set-permissions-on-a-specific-service-windows/
'Keeping the source DACL and just adding RP,WP and DT (Start/Stop/PauseContinue) to IU (Interactive User)
Dim DACLString As String = "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRCRPWPDT;;;IU)(A;;CCLCSWLOCRRC;;;SU)"
process.Start("sc.exe", $"sdset {svcinst.ServiceName} ""{DACLString}""")
End Sub
End Class

Accessing Network Path in Windows Service C#

I have developed a windows service using local system as Account. I have used
network path of file
FileInfo fi = new FileInfo(#"\\epm-server\penDocuments_LabMeds\" + Convert.ToString(dr["mrn"]) + "\\SyncedXML\\" + Convert.ToString(dr["xmlfile"]));
if (!fi.Exists)
boolFileNotFound = true;
A dynamic path of a file that is built from database.
It works fine when I run Windows Service in Debug Mode, but when I install it then fileNotExists returns TRUE always like the file doesnt exist but infact it does exist.
This is bugging me a lot now. Kindly help me why its not working. Its a server path. Its getting opened in my PC.
Thanks
Did you notice the double backslashes in front and after SyncedXML (\\SyncedXML\\)?
This is probably the cause of your error.
Additionally I'd use string.Format in such cases to reduce the inadvertently addition of unwanted characters:
var path = string.Format(#"\\epm-server\penDocuments_LabMeds\{0}\SyncedXML\{1}", dr[mrn], dr[xmlfile]);
var fi = new FileInfo(path);
Edit:
If it's permission-related, then it's very likely that your local system account (in whose context the service is running) isn't allowed to access the epm-server.
The path is accessible if you're opening it directly or if you're running the service in debug mode as this is happening in your user context (e.g. YOURDOMAIN\vickyshazad), and you're allowed to access the ressource, whereas NT AUTHORITY\SYSTEM is not.
It's usually a good practise to have a special service account for your developed windows service and grant this user only and exactly the required permissions (least privilege). Maybe ask your system administrator for a service user.
Local System (NT AUTHORITY\SYSTEM) is a highly privileged account that's not recommended to use in general (see MSDN).
Most services do not need such a high privilege level. If your service does not need these privileges, and it is not an interactive service, consider using the LocalService account or the NetworkService account.

Service writing files in a different path

I have 1 form and 1 service that should be writting to the same location.
So I added the following line in both.
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "myFolder" + Path.DirectorySeparatorChar + "log.txt");
the problem is that the form is writing the file in the right location but the service isn't. The service is writing in C:\Windows\System32\config\systemprofile\AppData\Roaming\myFolder\log.txt and the form in C:\Users\<user>\AppData\Roaming\myFolder\log.txt.
I can't use the windows event logger and i got to handle some other files too.
The user that runs the service is either NETWORK SERVICE or LOCAL SERVICE, instead of a real user account. Under those special accounts, Environment.SpecialFolder.ApplicationData comes to the systemprofile directory.
So to fix that, you'll need to change the user the service runs under, or hard code/determine the correct user directory to use.
Put the folder name in a config file. Or at least put the user's name, so you can build the folder name properly in the service.

Giving Windows Service rights to move files to a remote directory that req. user/pass

I'm using EWS to grab file attachments from emails in an inbox, and need to put those files (if they meet certain criteria) onto a network directory path that requires an active directory user/pass that is not the same as what the machine running the service is using.
There's probably multiple ways to attack this. Without having to set that directory path to allow the user/pass that is running the windows service to have rights to read/write is there a way in code that I can set the user/pass before I try and place the files in that path?
In the installer setup of the windows service I've tried the following:
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.User;
this.serviceProcessInstaller1.Password = "password";
this.serviceProcessInstaller1.Username = #"\\serverName\user";
when I try and install I get an error about mapping the user pass, so I tried this:
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.NetworkService;
this.serviceProcessInstaller1.Password = "password";
this.serviceProcessInstaller1.Username = #"\\serverName\user";
the installer works, the service shows up and I can start it, but when I debug/attach to the process it throws an exception when trying to write to the directory about access rights.
So maybe I'm not even attacking the right issue/section, as this is probably an active directory issue and something not done in code.
Any suggestions?
What you tried there is irrelevant to your problem.
If you're on windows 7, you may workaround by going to [Control Panel]->[User Accounts]->[Credential Manager] to store login information of target machines.

Reading UNC path with FileSystemWatcher [duplicate]

I am trying to run a file watcher over some server path using windows service.
I am using my windows login credential to run the service, and am able to access this "someServerPath" from my login.
But when I do that from the FileSystemWatcher it throws:
The directory name \someServerPath is invalid" exception.
var fileWatcher = new FileSystemWatcher(GetServerPath())
{
NotifyFilter=(NotifyFilters.LastWrite|NotifyFilters.FileName),
EnableRaisingEvents=true,
IncludeSubdirectories=true
};
public static string GetServerPath()
{
return string.Format(#"\\{0}", FileServer1);
}
Can anyone please help me with this?
I have projects using the FileSystemWatcher object monitoring UNC paths without any issues.
My guess from looking at your code example may be that you are pointing the watcher at the root share of the server (//servername/) which may not be a valid file system share? I know it returns things like printers, scheduled tasks, etc. in windows explorer.
Try pointing the watcher to a share beneath the root - something like //servername/c$/ would be a good test example if you have remote administrative rights on the server.
With regards to the updated question, I agree that you probably need to specify a valid share, rather than just the remote server name.
[Update] Fixed previous question about the exception with this:
specify the name as #"\\someServerPath"
The \ is being escaped as a single \
When you prefix the string with an # symbol, it doesn't process the escape sequences.
I was just asked this question in regards to FileSystemWatcher code running as a service and the issue is permissions. I searched and found this question and answer but unfortunately none of the answers here solved the problem. Anyway, I just solved it, so I thought I would throw in the solution here for next guy who searches and find this question.
The drive was mapped as a logged in user but the service was running as LocalSystem. LocalSystem is a different account and does not have access to drives mapped by a user.
The fix is to either:
Authenticate first (I use a C# Class to establish a network connection with credentials)
Run your service as a user that has access to the share.
You can test LocalSystem authentication by using a LocalSystem command prompt, see How to open a command prompt running as Local System?
Even though this is already answered I thought I would put in my two cents worth becaus eyou can see this same error even if you supply valid paths.
You will get the same error when the process running the watcher does not have access to the remote share. This will happen if the watcher is in a service running under the System account and the share is created by a user. System does not have access to that share and wont recognize it, you will need to impersonate the user to get access to it.
although you can use a FileWatcher over the network, you will have to account for other factors, like disconnection of the network share. If your connection to the share is terminated (maintenance, lag, equipment reset, etc) you will no longer have a valid handle on the share in your filewatcher
You can't use directory watches over network shares, this is a limitation of the OS, not of .NET.

Categories

Resources