When a Windows service running under the NT AUTHORITY\NETWORK SERVICE
account attempts to access UNC paths (\\server\share), it presents the machine's credentials rather than proper user credentials. This becomes problematic when accessing shares on non-domain workgroup machines.
File.Exists()
returns false despite file presence- Access denied errors when attempting file operations
- Event Viewer shows authentication failures (Event ID 4625)
Option 1: Use Explicit Credentials
Create a dedicated local account on both machines with identical credentials:
// In your service code:
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;
string path = @"\\server\share\file.txt";
// Approach 1: Impersonation
WindowsIdentity.RunImpersonated(GetToken(), () => {
bool exists = File.Exists(path);
});
// Helper method to get token
SafeAccessTokenHandle GetToken() {
string user = "machinename\\username";
string password = "password";
string domain = "machinename";
bool success = LogonUser(
user,
domain,
password,
LOGON32_LOGON_NEW_CREDENTIALS,
LOGON32_PROVIDER_DEFAULT,
out SafeAccessTokenHandle token);
if (!success) throw new System.ComponentModel.Win32Exception();
return token;
}
Option 2: Configure Share Permissions Properly
Even with "Everyone" permissions, the Windows security model requires both share and NTFS permissions:
- Share permissions: Grant "Change" to "Everyone"
- NTFS permissions: Add "COMPUTERNAME$" (the machine account) with Modify rights
Option 3: Use Service Account with Credential Manager
Store credentials securely using cmdkey:
cmdkey /add:targetname /user:username /pass:password
Enable auditing on the file server:
auditpol /set /subcategory:"File Share" /success:enable /failure:enable
Check effective permissions using PowerShell:
Test-Path "\\server\share" -PathType Any
Get-SmbConnection | Where-Object {$_.Status -eq "Failed"}
For services that must access multiple network resources, consider:
- Running the service under a domain account (if available)
- Using a Group Managed Service Account (gMSA)
- Configuring constrained delegation in domain environments
- Verify both share and NTFS permissions
- Check firewall settings (ports 445, 139)
- Confirm NetBIOS over TCP/IP is enabled
- Test with both FQDN and NetBIOS names
I recently encountered this frustrating scenario where my Windows service running under NT AUTHORITY\NETWORK SERVICE
couldn't access files on a UNC share, even though "Everyone" had Full Control permissions. The File.Exists()
call kept returning false for files that definitely existed.
The root cause stems from how Windows handles authentication for network resources. When accessing a UNC path:
- The
NETWORK SERVICE
account authenticates as the computer account (DOMAIN\COMPUTERNAME$ in domains) - For workgroup computers, it uses anonymous authentication by default
- The "Everyone" group doesn't include anonymous users by default
Option 1: Modify Local Security Policy
On the machine hosting the share:
1. Open Local Security Policy (secpol.msc)
2. Navigate to: Local Policies → Security Options
3. Find "Network access: Let Everyone permissions apply to anonymous users"
4. Enable this policy
Option 2: Use Credential Management
For more secure access, you can impersonate a specific user:
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken);
// Usage example:
IntPtr userToken;
if (LogonUser("username", "domain", "password",
2 /*LOGON32_LOGON_INTERACTIVE*/,
0 /*LOGON32_PROVIDER_DEFAULT*/,
out userToken))
{
using (WindowsIdentity.Impersonate(userToken))
{
bool exists = File.Exists(@"\\server\share\file.txt");
}
}
If you can't modify security policies, consider:
- Running the service under a domain account with explicit share permissions
- Mapping the UNC path as a network drive (requires different approach for services)
- Using explicit credentials in your connection string or API calls
Always verify with a simple test harness:
try
{
bool exists = File.Exists(uncPath);
Console.WriteLine($"File exists: {exists}");
using (var stream = File.OpenRead(uncPath))
{
Console.WriteLine("Successfully opened file");
}
}
catch (Exception ex)
{
Console.WriteLine($"Access failed: {ex.Message}");
}
Remember that File.Exists()
might return false positives due to caching - always follow up with actual file operations.