Prompting for Credentials for CreateFile - c++

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.

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)

SetNamedSecurityInfo: Setting DACL for Object works, but does not persist after system restart

In Windows 7, I'm trying to give a user group the ability read/write access to a specific disk connected through SCSI so they can run a utility that reads/writes to that disk. I'm trying to do this through modifying the DACL of the disk object using SetNamedSecurityInfo using C++.
string devicePath = "\\?\scsi#disk&ven_wsi&prod_drs1100p#6&383ae3b6&0&000300#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}";
PSID ppsidOwner, ppsidGroup;
PACL ppDacl = NULL, ppSacl = NULL;
PSECURITY_DESCRIPTOR ppSecurityDescriptor = NULL;
char objName[200];
strcpy(objName, devicePath.c_str());
// Get SecurityInfo
GetNamedSecurityInfoA(objName, 1, DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, &ppsidOwner, &ppsidGroup, &ppDacl, NULL, &ppSecurityDescriptor);
// Build ACE
EXPLICIT_ACCESS str_ACE;
DWORD rightsMask = STANDARD_RIGHTS_ALL | GENERIC_ALL | GENERIC_WRITE | GENERIC_READ;
BuildExplicitAccessWithNameA(&str_ACE, "DRS Operators", rightsMask, GRANT_ACCESS, NO_INHERITANCE);
PACL newPACL;
// Set ACE then Set SecurityInfo
SetEntriesInAclA(1, &str_ACE, ppDacl, &newPACL);
SetNamedSecurityInfoA(objName, 1, DACL_SECURITY_INFORMATION, NULL, NULL, newPACL, NULL);
The code I wrote seems to work, which I have confirmed by using GetNamedSecurityInfoA on the object before and after I change the DACL, then passing the received SecurityDescriptors to ConvertSecurityDescriptorToStringSecurityDescriptorA, and making sure that they have changed. After running through this code, I'm able to log in as any user in the "DRS Operators" user group and am able to read/write to the disk.
The only problem I'm having is that the changes to the DACL do not persist after the system shuts down. After a restart, the DACL is set back to what it was before I ran through the code above (which I checked by using GetNamedSecurityInfoA). Does anyone here have an idea of why the changes aren't persisting?

How can I capture File directory from edit control(textbox)

so I am new to the whole c++ windows API. I'm creating a simple dialogbox in which the user types in a directory into a textbox for a time file which has already been created. the program will then read the file and display the time in another edit control. Im having a few problems making the directory entered the parameter for CreateFile(). If I hard code the directory in, the program will work correctly. But I cant figure out how to take the textbox data and plug it into the CreateFile() function. if this doesn't make sense i can try an explain differently. Ive searched an can't seem to find anything.
Thanks
for example:
if the user types c:\test\time.txt into the text box I want "c:\test\time.txt" to be the put into CreateFile();
CHAR temp[20] = "";
HANDLE hFile;
GetDlgItemText(hDlg, IDC_TEXTIN, temp, 20);//IDC_TEXTIN is name of edit control
//open file
hFile = CreateFile(
temp,
GENERIC_READ | GENERIC_WRITE,
0, // no sharing
NULL, // no security
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL // no template
);

Why is CreateFile failing to open a file across a network share?

I wrote a small program which frequently opens small, user text files and until now haven't encountered any problems with read/write access or any sorts of conflict. The files are selected in another piece of software which I have no control over, and are passed to me as a string.
When attempting to open a file from a mapped network drive I am getting a "The system cannot find the path specified" error (GetLastError() = 3).
The call is shown below, *iNCfileName = "z:\\Validation\\Sample Files\\1_1-4 120MM.CC", where Z: is a mapped folder on our domain.
iNCfile = CreateFile( iNCfileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if ( iNCfile == INVALID_HANDLE_VALUE )
{
string msg; // lots of better ways to get this printed ... but ...
dw = GetLastError();
msg = iNCfileName;
msg += ": ";
msg += _com_error(dw).ErrorMessage();
print_error(dw , (char*)msg.c_str() );
return 102;
}
The file opens from my program if I copy it to the local hard drive. It also opens in notepad from the mapped drive.
Could this be a problem between the "Z:\whatever.txt" mapped representation and the true file name (\mydomain\Validation\S....??)?
If so, how can I convert from one to the other in a programmatic way (assume I won't know the domain/share names ahead of time)?
If it makes any difference I use VS2010 and the application executes on a Win XP machine.
Related: my follow up question
I've encountered this before. When using a path like \\DOMAIN\PATH\FILE.TXT I had to first call WNetAddConnection2().
Here is my code (of course you can exclude the NULL members):
NETRESOURCE nr = {0}; //new structure for network resource
nr.dwType = RESOURCETYPE_ANY; //generic resource (any type allowed)
nr.lpLocalName = NULL; //does not use a device
nr.lpRemoteName = "\\\\DOMAIN\\PATH\\FOLDER"; //For me, this pointed to an account's documents folder, and from there I could use a subfolder
nr.lpProvider = NULL; //no provider
DWORD ret = WNetAddConnection2 (&nr, NULL, NULL, CONNECT_TEMPORARY); //add connection
Don't forget the header and library.
I just had the same issue; trying to create a file using API CreateFileW under a mapped drive ( Z:\folder ) did not worked; howerver, after researching this subject i tried to create the file using the real path ( \\Shared_computer_name\folder\ ) immediately worked successfully.
Now I have to work a function to retrieve the real name of a mapped drive, to use it when necessary... just found WNetGetUniversalName, have to make it to work.

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.