WNetAddConnection2 from a Windows Service - windows

I'm trying to connect to a remote password protected shared folder from a Windows service, which runs as LocalSystem account. It seems that the LocalSystem account is unable to directly access password-protected network shares using WNetAddConnection2() or similar calls.
Can anyone confirm this?
I've read that impersonating an administrator user might be the way to go.
I've tried using LogonUser() and ImpersonateLoggedOnUser() before WNetAddConnection2(), it appears that the mount of the network path succeeds, but then actual accesses (e.g. enumerating of files in remote folder) fail.
Any ideas?
Thanks.

I just encountered this problem as well, and found that if I put the remote computer name into the user name, it worked. (I didn't actually figure this out, we had another place in the code already doing this that worked, so I knew it was possible, and finally figured out the difference.)
So for example:
WNetAddConnection2(&nr, "password", "SomeComputer\\Username", 0);
I'm not doing any other special calls like LogonUser or ImpersonateLoggedOnUser.
This is in a service running under the SYSTEM account.
I haven't tried using the SomeComputer\Administrator account, but that's not exactly a good practice anyway. I'm using a normal user account on SomeComputer.

To tell the trust I worked all time only in a domain environment and without password-protected network shares, but I know that there are two main ways to make a connection: WNetAddConnection2 API and NetUseAdd API. I recommend you to try NetUseAdd function with Level equal to 1 (USE_INFO_1). I used only USE_INFO_2 which has ui2_username, ui2_domainname and ui2_password, but USE_INFO_1 has only ui1_password, so it looks like a function made for connection to a password-protected share.
By the way, LogonUser() has really no sense, because it makes local login on the local computer and you need to establish a session to the remote computer. This do WNetAddConnection2 and NetUseAdd functions.

The way you can access network share from a local system account(which is "NT AUTHORITY\SYSTEM"):
You need to log on using some local account that has access to netowork even in non-domain net. It's enough to use "NT AUTHORITY\NETWORK SERVICE" account to gain this
Add network share connection with specifying it's access credentials:
The main point here is to use LOGON32_LOGON_NEW_CREDENTIALS logon type during LogonUser() call (see MSDN for details/restrictions). Otherwise you'l get ERROR_NO_SUCH_LOGON_SESSION when executing WNetAddConnection2(), even if LogonUser and impersonation succeded.
LogonUser("NETWORK SERVICE", "NT AUTHORITY", NULL, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, &hToken );
ImpersonateLoggedOnUser(hToken);
NETRESOURCE nr;
nr.dwScope = RESOURCE_GLOBALNET;
nr.dwType = RESOURCETYPE_DISK;
nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;
nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
nr.lpRemoteName = "\\\\SomeCopmuter\\C$";
nr.lpLocalName = "Z:";
WNetAddConnection2(&nr, "password", "Administrator", 0);
Notes
Impersonation woks only for current thread.
with local resources it will work as LocalSystem, with the added share it will work as user on remote computer specified in WNetAddConenction2 (in this case - Administrator on SomeComputer).
You can omit using drive letter in NETRESOURCE and access files via "\server\share\filename.ext" notation
This may not work on some old systems (NT/2000, don't know exact list)

I'm actually grappling with the same problem right now, Flavio, and my current suspicion is that it works if someone is interactively logged on to the machine, and will return ERROR_NO_SUCH_LOGON_SESSION if no one is logged on. I may be wrong, though. More to come. I've starred this question and will check back :)

import win32wnet from win32netcon import RESOURCETYPE_DISK as DISK path="\192.168.1.11\Student" win32wnet.WNetAddConnection2(DISK,"R:","\192.168.1.11\Student",None,"Student","pass",0)

Related

Workstation has lost the trust relationship with the domain

Ok here is a tuffy.
I know that to rebuild the trust relationship you have to logon to the workstation as a local administrator and disjoin, then rejoin the domain.
My question is, is there a way to rebuild the trust relationship remotely? Does anyone have a script, utility, application or command that will rebuild a lost trust relationship?
Frequently (for us) the source of the problem behind "trust relationship" issues is that the machine account AD password is out of sync (the machine believes it to be one thing, the domain controller(s) believe it to be something else) and the machine is unable to authenticate to AD.
There's no need to disjoin first if the only thing that needs to change is the password. This can be fixed relatively easily by invoking the JoinDomainOrWorkGroup method of the Win32_ComputerSystem class. The catch is that the process must be run from the machine you wish to (re)join to the domain (otherwise, the machine account password won't get updated).
It can be done easily enough in PowerShell. Something like this (adapted from this TechNet contribution) would suffice:
$CS = Get-WmiObject Win32_ComputerSystem
$CS.JoinDomainOrWorkgroup($Domain,'','',$null,33)
That will join the machine to the domain using an existing machine account. You may have to specify a user and password, depending on how you get shell access to the remote machine.
Running it remotely does not have a one-size-fits-all solution. I usually use PSExec to get remote shell access and invoke the PowerShell command (using -EncodedCommand to negate the need for quoting).

Device Driver access permissions for domain users in Windows 7

I'm writing a Windows device driver for a custom USB device, but am having trouble opening the device from my user program (using CreateFile) when the user program is run as a domain user. If I run as a local user, or as an administrator (or 'Run As' administrator) I can open the driver fine, but as a domain user GetLastError returns 5 (access denied).
I originally had this problem with local users too, and found I had to add the following SDDL entry to the .inf file, which solved the problem for local users:
HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;BU)
From this reference:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff563667(v=vs.85).aspx
When I discovered that domain users did not have access I thought that simply adding them to this SDDL entry would give them access, but it doesn't seem to work: I still get access denied. I've even tried extreme solutions such as giving all users (everyone (WD), unauthenticated users etc.) full access, but this doesn't work either, which makes me think the problem lies elsewhere; i.e. something else is denying domain users access which takes precedent over the permit in the SDDL entry in the driver inf.
So my question is, what am I missing that is required to give domain users (or all users) access to connect to the driver? Or are there any other solutions to this problem (such as connecting to the driver as a service and then accessing this service from the user program)?
HKR,,Security,,"D:P(A;;GA;;;WD)"
set everyone can access, try it!

LogonUser using LOGON32_LOGON_NEW_CREDENTIALS works against remote untrusted domain machine

So between the two machines, there is no trust - they are in different domains.
I've successfully connected to the remote machine using LogonUser API using logon type, LOGON32_LOGON_NEW_CREDENTIALS. I am able to retrieve the content of a directory using the UNC share, and create a file stream to "download" the file. So far so good.
The only issue is that it seems, LogonUser fails unless there is an already open session. Let me clarify that.
I found that the ASP.NET MVC page was not working this morning, specifically the page that retrieves the file list from this remote machine using LogonUser. I look at the log and I see in the stacktrace, System.IO.__Error.WinIOError above Directory.GetFiles call. I then remoted into the web server and tried to open the remote folder in the explorer using the same login/password used by the web site. It went through and I could see the files. I opened up the command prompt, type in net use, and I see that there is an open connection to the remote machine. Then I went back to the page and suddenly the page is working again.
So, at this point, I am not exactly sure if the LogonUser is working as expected or not. If the call requires that a network connection opened first by other means, then this is certainly not satisfactory.
Does anyone know what may be happening or suggest a workaround?
I am not sure that I understand why you use LogonUser. This function help you if you want to do some job on the local machine with another user credentials, but it helps not to establish a remote connection to another computer.
If you want to get some information from the remote computer independent on existing trust between to the computer you should use WNet or Net (Network Management) functions to establish a new connection to the remote computer. So you should use WNetAddConnection2 (see http://msdn.microsoft.com/en-us/library/aa385413%28VS.85%29.aspx) or NetUseAdd (http://msdn.microsoft.com/en-us/library/aa370645%28VS.85%29.aspx) functions. This function will makes remote login on the destination computer and establish a new session (exact what net use \\computer\share /u:domain\user password do). You can don't map a new connection to a local drive. To do so you should fill lpLocalName with NULL in the struct NETRESOURCE. As a lpUsername and lpPassword you should give any values which understand the destination computer. You can also use ipc$ as a destination share name, then you just establish a session to the computer and nothing more. After that you can use any other functions to access the remote share, directory or files. To close the session you should use WNetCancelConnection2 or NetUseDel.
This is how you LogonUser to a remote computer. Make sure that you're using LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50. Then impersonate the token. You should be able to do a lot of remote things this way without WNetAddConnection2. WNetAddConnection2 is not very good because the connection can be destroyed by a lot of things. LogonUser will also make the appropriate connection when needed by several api calls.
PXERR impersonate_user(LPCWSTR lpszUserName, LPCWSTR lpszDomain, LPCWSTR lpszPassword)
{
HANDLE token;
if(!LogonUserW(lpszUserName, lpszDomain, lpszPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, &token))
{
return PXERR_IMPERSONATION_FAILURE;
}
if(!ImpersonateLoggedOnUser(token))
{
CloseHandle(token);
RevertToSelf();
return PXERR_IMPERSONATION_FAILURE;
}
CloseHandle(token);
return PXERR_SUCCESS;
}

Sending a password to a Windows Service

What is the best way to send a password to a Windows Service? Our application needs a password in order to start. I don't care that services are "normally" supposed to run without user interaction. Its good enough for us that an operator can start the application and then log off.
On a unix system, I would just echo the password over stdin but the service has no stdin.
Currently, we use the DPAPI to just store the password using CryptProtectData. While this, works, it presents other problems that are beginning to become troublesome.
I'm guessing that I'll need to use some form of IPC between the service and the application that is sending the password but I'm not sure which method is appropriate, if any.
Thanks
Two main options:
You could listen on a socket on startup and wait for the required password to be supplied (maybe embed an SSH server in there, so that the password cannot be snooped over the wire)
My preferred option would be to read the password from a configuration file (that can be secured to the minimum readership) or registry setting (again, sufficiently secure such that only your service and administrators can read/change it)
Thanks for responding Rowland.
You could listen on a socket on
startup and wait for the required
password to be supplied (maybe embed
an SSH server in there, so that the
password cannot be snooped over the
wire)
I considered that but without certificate verification, wouldn't that leave us open to a man in the middle attack?
My preferred option would be to read
the password from a configuration file
(that can be secured to the minimum
readership) or registry setting
(again, sufficiently secure such that
only your service and administrators
can read/change it)
We're trying to follow "defense in depth" as much as possible such that if an attacker compromised the machine, he would not able to access our application.
You can use kerberos mutual authentication. There are few options and examples there.
But just wondering. On a compromised machine, There may be a key logger. So typing the password is never secure if you want to maintain security in this environment. The same problem exist afaik for unix terminals.
DPAPI in UserMode is really the best option, and storing the encrypted data in a protected location, e.g. registry key with limited ACL.
What exactly were the problems that are beginning to be troublesome? Maybe we can just solve those...
What exactly were the problems that
are beginning to be troublesome? Maybe
we can just solve those...
Currently, the application runs as the Local System account.
Our application stores a number of credentials in an encrypted file and uses the DPAPI (in UserMode) for the encryption.
Thus, when the application is installed, the installer is run as the Local System account. We also have a set of tools that ship with the application, some of which need access to this encrypted file and thus, they too need to run as the Local System account.
By the time the application is installed and started, we're heavily dependent on that account.
We're running into problems because one of our users wants to use the application to access a shared network drive. The Local System account has no such privileges and we can't simply run our service as a different user because our encrypted information is protected under the Local System Account.
We've tried to avoid the process of setting up a user account just for our application because it is installed across many different customers and environments, all of whom have wildly different security policies.
You can access a remote drive from a service running under system account. However, you will need to have credentials & share information to connect to the remote machine. You can use the API wnetaddconnection to gain access. Probably your encrypted file can store this credential as well.

Accessing network shares

I am currently trying to access a network share from my machine programaticaly using vc++ 6.0. I am using WNetAddConnection2 to connect to a share using my credentials.
I am facing the following problem:
Consider the case when there are 2 users trying to access the same remote share from same machine one after the other. Now once a user logins, the other user is not able to login into the share using his credentials until the 1st user logouts.
Also if the 1st user logs onto the remote-machine's root share, then the other user is not able to access anyother share on that machine.
Please let me know if there is any workaround for the same.
Thanks in advance
The answer depends on if the 1st user still needs access. If they don't, then call WNetCancelConnection2(). If they do, Keith Brown in his "Programming Windows Security" book has a trick of calling LogonUser() to establish a new logon session, which creates a new SMB "port" to avoid the conflict. After impersonation, your thread will be able to connect using different credentials.

Resources