LogonUser using LOGON32_LOGON_NEW_CREDENTIALS works against remote untrusted domain machine - winapi

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;
}

Related

How do I get a windows service to have access to a shared network folder?

I have a windows service that will be running on a client server. This service needs to access 2 folders that are located in different shared folders (Y:\ and Z:\ ) which are both on separate computers (3 computers total).
Currently the service is only able to see files located on the same machine. When I do a IO.Directory.Exists() on the shared folders, it returns false.
Here are things I have tried:
Running as a Network Service
Running as a Local System
Running as a Local Service
testing existence with \\192.168.1.xx\path\to\folder
testing existence with Y:\ and Z:\
Creating a user on the other computers with the same Username and Password
Verifying the folders gave full access to everyone
Nothing yet has worked, any help is much appreciated.
In an Active Directory environment, Network Service and Local System both have network access to other machines in the domain; the server just needs to be configured to grant access to the client's computer account in the domain, i.e., the COMPUTERNAME$ account. If you use a UNC path the connection will be established automatically. You still can't use drive letters established in another logon session, but in most circumstances the UNC path will do.
In a stand-alone environment it's a bit trickier. The only reliable approach is to establish the network connection explicitly, which you can do using WNetAddConnection2 or any of the various alternatives. (Or if you can't call the Win32 API, you can shell out to the net use command.) In either case, once the connection is established you can use a UNC path. There is typically no need to map a drive letter.
If you must have a drive letter for some reason, it is usually best to use the WNetUseConnection function instead of WNetAddConnection2. That can be configured to select a drive letter automatically, so you don't have to try to figure out which letters are already in use.
Note that depending on the circumstances, it may be necessary to use the long form of the username for the account on the server, i.e., SERVERNAME\USERNAME or DOMAINNAME\USERNAME if it is a domain account. Windows 10 clients seem particularly fussy about this for some reason.

Working around the Windows OS limitation of not allowing multiple connections to a server/shared resource

We are building a system on windows where we centrally (server) need to do fopen to either local files or remote smb resources. The idea is to authenticate in the case of remote resources before doing fopen (with unc paths).
We need to authenticate with the credentials the user (client application) supplied for this resource on that remote share. We don't want to copy any resources.
Using the Win Net Api this works smoothly since it stores the given credentials so that subsequent fopens in the same or in different processes succeed.
But there is a problem:
Many of you probably know the following message from windows when trying to connect to a smb share with different credentials then the ones used for a previous connection:
"Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again."
See http://support.microsoft.com/kb/938120 for the defined limitation and possible "work arounds".
Since we have a central server application running as a service ('Local System' account) we hit this limitation with having already two different users :).
Closing the previously established connection to allow for the 2nd one is not an option (ongoing processing).
On the one hand it's great that windows caches authentication information on the other hand it's too limited.
Modifying the hosts file for each user does not look very nice.
Using smb client libraries (like libsmb++, impacket) doesn't seem to be the solution since we need "over process" authentication.
Configuring a "master" smb share user is also not wanted.
Maybe passing windows user auth tokens around is a way?
This problem is of general nature (i.e. independent of language) and I'm convinced that there are people out there who solved it (in a more or less elegant way ;))
I hope my explanation is understandable.
Thanks in advance for any hint.
felix

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!

How does impersonation in DCOM work?

I have a DCOM client and server applications which use OLE automation marshaller. They work fine when run on the same PC but when the server is on a different PC not in the same domain I get E_ACCESSDENIED (0x80070005).
Server PC is configured with dcomcnfg to give all access to any DCOM object to the user whose login and password I specify on the client. ServerApp and its type library are registered on the server pc.
Type library is also registered on the client PC. I specify server name directly in the ClientApp so no dcomcnfg configuration is needed on the Client PC as far as I understand.
CreateInstanceEx() with server name, login, domain and password works fine. It returns IUnknown and at the same time starts ServerApp on server PC.
But when I try to QueryInterface() for the interface which server supports, I get E_ACCESSDENIED.
Analyzing the Security Event Log, I have two records there:
First, a successful network login by the user whose credentials I specify in ClientApp. This happens when I call CreateInstanceEx().
Next, a failed login attempt by the user under which I'm logged in on a client PC. Since two PCs are not in a domain, this user is unknown to server PC.
Now, why the heck would THIS user be logging into server, especially when I call QueryInterface of all things?
Studying CreateInterfaceEx params, it appears there's some kind of impersonation mechanism going on. But it's unclear who impersonates who. There are THREE user credentials involved:
User under which ServerApp runs on the server PC (as configured in dcomcnfg).
User whose credentials ClientApp specifies when connecting.
User under whose credentials ClientApp runs on client PC.
No matter how you look at it, if #3 is involved it's one user too much. If DCOM is going to identify/impersonate #3 on server PC anyway, why do I need to specify #2's credentials? To what point?
It would have seem logical for DCOM to impersonate #2 because this is what I have explicitly specified as my credentials. But why the second login attempt then?
Can someone please explain how exactly the impersonation works, and also if there's a way to just ignore it and run as user which is specified in dcomcnfg?
Answering my own question. After much exploration it became apparent that DCOM has TWO different identification cases:
Authorization for object creation (CoCreateInstanceEx)
Authorization for method calls.
For reasons unknown, #2 doesn't inherit #1 settings. By default it uses the credentials of the client process, hence strange logins.
There are two ways to specify credentials for #2. First one is CoSetProxyBlanket. It sets credentials for a specified proxy (marshaller-unmarshaller) only:
CoCreateInstanceEx(IID_IObject1, /*login, pass*/, obj1); //Success!
//Logged in and recevied IObject1 proxy in obj1
obj1->DoSomething();
//IObject1 proxy in obj1 now tries to login under process credentials.
//Failure! E_ACCESSDENIED
CoSetProxyBlanket(obj1, /*login, pass*/); //Success!
//IObject1 proxy is now authorized.
obj1->DoSomething(); //Success!
obj1->QueryInterface(IID_IObject2, obj2); //Success!
obj2->DoSomethingElse(); //Failure!
//This different proxy for IObject2 have not yet been authorized.
CoSetProxyBlanket(obj2, /*login, pass*/);
//etc.
It's important to note that while CoCreateInstanceEx requires impersonation level to be at least IMPERSONATE, CoSetProxyBlanket doesn't seem to work on anything except IDENTIFY.
Another option is to use CoInitializeSecurity to set default credentials for the whole process. Then you don't have to call CoSetProxyBlanket on every proxy:
CoInitializeSecurity(/* login, pass */);
CoCreateInstanceEx(IID_IUnknown, /*login, pass*/, obj); //Success!
obj->DoSomething(); //Success!
When using CoInitializeSecurity on the client you have to specify asAuthSvc too, even though MSDN says you don't.
The drawback of this method is obviously that if you have several DCOM objects from different PCs you're going to have to specify all the credentials in this call and those are probably going to be tried against every computer every time you open a different proxy.
It also is not reliable when you're running from a DLL (what if a process has different default security?). So, it's probably better to implement a QueryInterface wrapper which CoSetsProxyBlanket before returning from every call.
For those who are working in Delphi there is one little note that can save a lot of your time. After you did obj as ISomeInterface operation, you have to call CoSetProxyBlanket for the new instance. This could be not very obvious, but all we know that as operator calls QueryInterface method, and it can return new instance.

WNetAddConnection2 from a Windows Service

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)

Resources