CoImpersonateClient causes CreateFileW to fail - c++

I am working in a client/server scenario. I have a COM-based server which is running on the same machine as the client (the server is running as admin).
Anyways, there is a specific method in the server application which opens a file with CreateFileW. This is some pseudocode that makes up the call.
HRESULT __fastcall CxChmCallback::ParseQueryFile(CxChmCallback* this, wchar_t const* lpFileName, ...)
{
if (FAILED(CoImpersonateClient())
return E_FAIL;
HANDLE hFile = CreateFileW(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
CoRevertToSelf();
if (hFile == INVALID_HANDLE_VALUE)
return (HRESULT)-1;
...
}
Now, I know CreateFileW works because if I skip the call to CoImpersonateClient(), the function succeeds. Otherwise, the function fails and GetLastError() returns ERROR_BAD_IMPERSONATION_LEVEL (1364).
The client is running as a standard user with a medium integrity level, under a user account in the Administrators group. I have tried testing this with the client running as admin, but I get the same problem. Is there anything special about CreateFileW which causes it to fail?

Related

OpenFileById fails with ERROR_ACCESS_DENIED

I'm working on a product where OpenFileById() fails with ERROR_ACCESS_DENIED on files and folders that are otherwise accessible (meaning a CreateFile() on the same file or folder specified by path with the same access level / share mode, etc. succeeds).
I'm using backup semantics so I could also get a handle to folders; SE_BACKUP_NAME and SE_RESTORE_NAME privileges are enabled. This code works everywhere else other than this one machine (Windows 8.1).
The process is running as a service under local system, I tried having them change that to a different account with admin privileges and that didn't work either. Files / folders in question haven't been open for deletion (which is one case when this function will fail with ERROR_ACCESS_DENIED as per documentation).
I don't have physical access to this machine so can't kernel debug or anything like that. Has anyone run into this before?
Here's what I'm trying to do in a nutshell:
hRoot = ::CreateFileA(szRootPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hRoot != INVALID_HANDLE_VALUE)
{
FILE_ID_DESCRIPTOR fileId;
fileId.dwSize = sizeof(fileId);
fileId.Type = FileIdType;
fileId.FileId.QuadPart = nId;
hFile = ::OpenFileById(hRoot, &fileId, SYNCHRONIZE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, FILE_FLAG_BACKUP_SEMANTICS);
if (hFile != INVALID_HANDLE_VALUE)
{
...
::CloseHandle(hFile);
}
::CloseHandle(hRoot);
}
Apparently this is caused by a third party software product's kernel components (can't really go into specifics)

Prompting for Credentials for CreateFile

I would like to design a function that checks a file for some text. It takes a file path, and this path can be a UNC path such as \path\to\file\share.
It will use the current users credentials as well. What I want to do is have the ability to prompt the user for new credentials to use; In pseudocode
hCreds = NULL;
if (!fAutoLogon) { hCreds = PromptForCredentials(); }
hFile = CreateFile( pszFile, GENERIC_READ, FILE_SHARE_READ ,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL , hCreds);
Use CredUIPromptForCredentials() or CredUIPromptForWindowsCredentials() to get the user's credentials, then use LogonUser() and ImpersonateLoggedOnUser() to impersonate that user, then call CreateFile() with your UNC path, and then finally call RevertToSelf() to stop impersonating after you close the file.
You can connect to a network share using WNetAddConnection3():
NETRESOURCE nr;
nr.dwType = RESOURCETYPE_ANY;
nr.lpLocalName = NULL;
nr.lpRemoteName = dcpath;
nr.lpProvider = NULL;
dw = WNetAddConnection3(NULL, &nr, NULL, NULL, CONNECT_INTERACTIVE | CONNECT_PROMPT);
This will prompt for credentials. I believe the path string must point to a directory rather than to a file; usually it will just be \\server\share.
If the connection is successful, you can then use CreateFile() to open the file. The MPR will automatically use the existing connection.
You should use WNetCancelConnection() to disconnect from the server once you are finished using the file.
One potential advantage of this approach over Remy's is that it works even if your credentials on the server are different to those on the local machine.

Calling ::CreateFile for a modem in C++ always returning ERROR_SHARING_VIOLATION

I have a modem on COM44 and when I try to access it via C++ I end up receiving ERROR_SHARING_VIOLATION. The code I am using is and m_hFile ends up as -1:
void* m_hFile;
m_hFile = ::CreateFile( "\\\\.\\COM44",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
NULL,
NULL );
I have tested access to the modem via Putty and was able to open its comport and send AT commands to it without any problem so I know it works.
The problem ends up not being with the CreateFile code but with the registry code I have above it which determines the com port number to use which I hadn't added because I didn't think it was relevant.

Getting Access Denied While Accessing the Named Pipe from Another System

I have a Named Pipe and It Works Fine While I access it using a Client which runs on My System
The Client tries to open the File using following code:
LPTSTR lpszPipename = TEXT("\\\\smyServerName\\pipe\\iPipe01");
hPipe = CreateFile(
lpszPipename, // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL);
if (hPipe != INVALID_HANDLE_VALUE)
break;
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (GetLastError() != ERROR_PIPE_BUSY)
{
_tprintf( TEXT("Could not open pipe. GLE=%d\n"), GetLastError() );
return -1;
}
While Creating the Named Pipe I have used
lpszPipename = TEXT("\\\\.\\pipe\\iPipe01");
Instead of myServerName I have used .(Dot). I get GLE 5 (Access denied) while I run the client from another system.
First things first - check your permissions and firewall. Almost always, when something works locally but not on the network, it's permissions.
(Had this problem more times than I can count!)
AFAIR there was a change of security of anonymous access to named pipes in Windows Vista.
When you want to open it (with write access) from the anonymous account, you may have to change the pipe's security attributes as described here.

CreateFile Win32 API Call with OPEN_ALWAYS failed in an Odd Way

We had a line of code
if( !CreateFile( m_hFile, szFile, GENERIC_READ|GENERIC_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL ) )
{
DWORD dwErr = GetLastError();
CString czInfo;
czInfo.Format ("CMemoryMapFile::OpenAppend SetFilePointer call failed - GetLastError returned %d", dwErr);
LOG(czInfo);
return false;
}
This code worked great for many years. A few weeks ago, we had a customer with a problem. Turns out, the problem could be traced to this line of code, where the function would return a INVALID_HANDLE_VALUE handle and GetLastError() returned ERROR_FILE_NOT_FOUND(2).
Now, this is very confusing to us. OPEN_ALWAYS should direct the file to be created if it does not exist. So, why are we getting a ERROR_FILE_NOT_FOUND?
More confusion: For this customer, this only happened on one network share point (we were using a UNC path). Other UNC paths to other machines for this customer worked. Local paths worked. All our other customers (10000+ installs) have no problem at all.
The customer was using XP as the client OS, and the servers were running what appeared to be standard Windows Server 2003 (I think the Small Business Server version). We could not replicate their errors in our test lab using the same OS's. They could repeat the problem with several XP clients, but the problem was only on one server (other Server 2003 servers did not exhibit the problem).
We fixed the problem by nesting two CreateFile calls, the first with OPEN_EXISTING, and the second with CREATE_ALWAYS if OPEN_EXISTING failed. So, we have no immediate need for a fix.
My question: Does anyone have any idea why this API call would fail in this particular way? We are puzzled.
Addendum:
The CreateFile function above is a wrapper on the Windows API function. Here's the code:
bool CMemoryMapFile::CreateFile( HANDLE & hFile, LPCSTR szFile, DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes )
{
hFile = ::CreateFile (szFile, dwDesiredAccess, dwShareMode, NULL,
dwCreationDisposition, dwFlagsAndAttributes, NULL);
return (hFile != INVALID_HANDLE_VALUE)
}
First, you should always check for success of the CreateFile() API like this:
if (CreateFile(...) == INVALID_HANDLE_VALUE)
{
// handle the error
}
because CreateFile() doesn't return !=0 in case of success, but anything other than INVALID_HANDLE_VALUE (which is -1).
Then, the CreateFile() can fail in the situation you described if either the directory doesn't exist where you want to create/open the file in, or it can fail if the user has the rights to open and write files in that directory, but no rights to create new files.
Maybe the directory you wanted to create the file in did not exist?
Are you sure you fixed it by using 2 CreateFile calls? Or did you just not reproduce it?
We have also seen this problem when a file is accessed by a UNC share. None of the ideas and suggestions applied in our case - the directory always exists, the parameters to CreateFile are correct, we are checking the return correctly.
We believe this to be a shares issue. We solved the problem with a simple retry loop:
If a CreateFile with OPEN_ALWAYS fails with ERROR_FILE_NOT_FOUND, we simply sleep for a few ms and try again. It always works second time (if it failed first time).
If CreateFile fails, it will return INVALID_HANDLE_VALUE which is non-zero (I think it's negative 1). So CreateFile might be succeeding and returning zero as the handle.
It's only safe to check GetLastError() after a function fails but it looks like you might be checking the last error when CreateFile has succeeded (returned zero).
According to this article, some functions set the "last error" if they succeed:
My guesses would be something server related like the server not implementing support correctly for that filesystem operation. Maybe a linux server compared to a windows server?
Your arguments to create file are incorrect, so I assume that this is some sort of CreateFile helper function.
A call to CreateFile should look like:
m_hFile = CreateFile( szFile, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
if(m_hFile == INVALID_HANDLE_VALUE)