Resetting file security to inherit AFTER a MoveFile() operation - c++

Windows/C++
Looking for some recommendations on how to reset the security attributes on a file after it's been moved to a new folder.
Our standard way of creating files (and download from the server) is to create the file in a temporary folder, then as the file streams down, the file is appended. Once the download is completed, we move the file to it's final destination.
MoveFile() will transfer the security on the file when the file is moved. In certain configuration this causes a problem -- where the security defaults of final folder don't match the original folder. We cannot mess with folder security....
So, ultimately, I would like to perform an operation on the file after I move it. My current thinking is that I should fetch the security attributes of the folder it goes into, and then apply to the file after the move is completed.

To expand on Harry's answer, here is the full code:
// blank acl used to restore permissions after a file move
ACL g_null_acl = { 0 };
InitializeAcl(&g_null_acl, sizeof(g_null_acl), ACL_REVISION);
DWORD error = SetNamedSecurityInfo(file_path, SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION,
NULL, NULL, (PACL)&g_null_acl, NULL);
Keep in mind that calling SetNamedSecurityInfo (in this instance) requires SE_RESTORE_NAME privileges, so it cannot be called from a service running as Network Service (or Local Service), as they have limited permissions.

Use SetNamedSecurityInfo with the UNPROTECTED_DACL_SECURITY_INFORMATION flag. Just pass an empty ACL to remove the entries the file got from its previous parent. This would look something like this:
error = SetNamedSecurityInfo(
path_to_file,
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION,
NULL,
NULL,
empty_acl,
NULL);

Related

Create accessible file from root privilege process

I'm having a bunch of processes from different privileges, all running a shared code that open (and create if needed) a file for write using fopen_s with "a+" flag.
However, since no permissions that supplied to this command, and a root process create the file first, than other non-root processes couldn't access this file.
I could use int open(const char *pathname, int flags, mode_t mode); and thus control the file permissions (represented by mode_t) to allow access for everyone, but I need the file descriptor (FILE *) and not fileID. so I can use FILE *fdopen(int fd, const char *mode); in order to make the conversion.
Perhaps there's a more straight forward way to do it ?
No. The technique you described (open followed by fdopen) is the correct way to achieve what you want to do. As Some programmer dude pointed out, you could call chmod from your program to change the file permissions after it's created, but that's a more roundabout way to do it.
I could use int open(const char *pathname, int flags, mode_t mode); and thus control the file permissions (represented by mode_t)
Not really. Unless you set your process's umask setting. Because the permissions passed to open() are not the permissions the created file is necessarily created with.
Per POSIX open() (bolding mine):
the access permission bits (see <sys/stat.h>) of the file mode shall be set to the value of the argument following the oflag argument taken as type mode_t modified as follows: a bitwise AND is performed on the file-mode bits and the corresponding bits in the complement of the process' file mode creation mask. Thus, all bits in the file mode whose corresponding bit in the file mode creation mask is set are cleared.
So
int fd = open( someFileName, O_CREAT | O_RDWR, 0644 );
is NOT guaranteed to set the file permissions to 0644.
If your file creation mask is set to 0077, then the file will actually be created with permissions set to 0600.
Note that the umask() setting is a process-wide property, and it's not really a good idea to change it much. And if you're trying to write general-purpose code that has no side effects, it's a bad idea to change it at all. For example, changing the umask() setting in a multithreaded process in order to allow wider access to files being created can cause
security problems if another thread creates a file at the same time.
The best way to set file permissions to be exactly what you want is to set file permissions to be exactly what you want with fchmod():
FILE *f = fopen(...);
fchmod( fileno( f ), 0644 );
In fact, since the umask() setting is a process-wide property, it's always possible in general that it can be changed by another thread at any time, so setting the permissions explicitly via chmod() or fchmod() is the only guaranteed way to get exactly the permissions specified in all circumstances.

Information on new Windows 10 error: ERROR_CLOUD_FILE_ACCESS_DENIED

Running into a new Windows 10 error code opening a file for reading with CreateFile(). We get error 395, but there is scant information available about what it means or how to resolve. The details of the error from the Windows 10 SDK are as follows
Error number 395
Error constant
ERROR_CLOUD_FILE_ACCESS_DENIED
OS error message "Access to the
cloud file is denied."
The machine in question is Windows 10 Professional. It is running OneDrive, but the file is not located under the OneDrive folder. We suspect OneDrive may be using it's Known Folder Move feature
The code used to open the file is:
HANDLE hnd = ::CreateFile(fname,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (hnd == INVALID_HANDLE_VALUE) {
DWORD exitcode = ::GetLastError();
printf("%d\n", exitcode);
}
If anyone has encountered this issue, we'd appreciate any insight you can share.
After careful research, we discovered this was caused by a simple permission issue. The user process executing the CreateFile() call did not have permissions to access the file which was being stored in the cloud. OneDrive's Known File Move had without our realizing it caused the folder to be stored in the cloud.
Once we realized it was a cloud-permission-issue, it was a simple matter to fix the cloud permission to allow the user process to open the file.
In our particular case, we arranged to run our process as Administrator, which allowed our call to CreateFile() to succeed. If you are trying to access a file stored in a OneDrive share that is owned by another user, then you will not be able to use this solution. You will need to ask the file owner to grant you the access you are requesting.

Cygwin: Deleting file when handle is opened

I have a file which I want to delete, it's handle is held by system process so everytime I try to delete it it gives Access denied but for some reason cygwin is able to delete it.
I've downloaded the coreutils and investigated the source code of rm executable and found that it uses unlink function to achieve it. I've created a little test program which uses same function but it gives me Access denied anyway.
Then I found this article and guy describes how cygwin is able to delete a file which is the following:
Cygwin opens files always with all sharing flags
set, so a file opened by a Cygwin process should not result in a sharing
violation in another open call. The exception is the first NtOpenFile
in unlink_nt, which opens the file with FILE_SHARE_DELETE only to find
out if the file has an open handle somewhere else. In that case it gets
a STATUS_SHARING_VIOLATION, the next NtOpenFile will open the file with
all sharing flags set, and unlink_nt will try to delete the file or to
rename it, or to move it to the recycle bin, dependent on its path.
Which makes sense, So I started to implement same thing. Here is my code:
HANDLE file;
PIO_STATUS_BLOCK stat;
UNICODE_STRING myUnicodeStr;
RtlInitUnicodeString(&myUnicodeStr, L"C:\\Program Files (x86)\\TSU\\bin\\TSU.sys");
POBJECT_ATTRIBUTES attr;
InitializeObjectAttributes (attr, &myUnicodeStr, OBJ_OPENIF, NULL, NULL);
NtOpenFile(&file, MAXIMUM_ALLOWED, attr, NULL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_DELETE_ON_CLOSE);
NtClose(file);
As you can see I'm trying to open file with sharing flags set and also used FILE_DELETE_ON_CLOSE since I close the handle and I want it to be deleted afterwards.
The problem I have is Segmentation Fault(I'm using cygwin win10) for some reason. Little debugging showed that problem is in InitializeObjectAttributes function for some reason.
P.S
I know it's not the best solution to delete file when it's handle is held by some other process but the main goal is to mimic the rm.exe's behaviour in this way. Hope you can help. Thanks.
under windows not always possible delete file. for example when some process mapping this file as an image
if try delete running EXE file with rm.exe - it first call ZwOpenFile with DesiredAccess = DELETE, ShareAccess = FILE_SHARE_DELETE and OpenOptions = FILE_OPEN_FOR_BACKUP_INTENT. this is ok. than called ZwSetInformationFile with FileDispositionInformation - the DeleteFile from FILE_DISPOSITION_INFORMATION set to TRUE. this call failed with status STATUS_CANNOT_DELETE.
filesystem return STATUS_CANNOT_DELETE exactly from this place:
// Make sure there is no process mapping this file as an image.
if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers,
MmFlushForDelete )) {
DebugTrace(-1, Dbg, "Cannot delete user mapped image\n", 0);
return STATUS_CANNOT_DELETE;
}
than rm.exe again try open file, already with OpenOptions = FILE_OPEN_FOR_BACKUP_INTENT | FILE_DELETE_ON_CLOSE options. but this call of course fail with the same STATUS_CANNOT_DELETE. now error from this point:
// If the user wants to delete on close, we must check at this
// point though.
//
if (FlagOn(*DesiredAccess, FILE_WRITE_DATA) || DeleteOnClose) {
Fcb->OpenCount += 1;
DecrementFcbOpenCount = TRUE;
if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers,
MmFlushForWrite )) {
Iosb.Status = DeleteOnClose ? STATUS_CANNOT_DELETE :
STATUS_SHARING_VIOLATION;
try_return( Iosb );
}
}
after this rm.exe again call ZwSetInformationFile already with FileRenameInformation - where RootDirectory from FILE_RENAME_INFORMATION point to volume (on which file located) root (so like \Device\HarddiskVolume<N>\ and FileName point to some path in recycle bin. as result file actually moved but not deleted. rm.exe deceives you

Many ways to create a Temporary file: what is the situation behind each method?

My C++ winAPI application has a need to create a temporary file prior to uploading the file to a server. So I have searched ways to create a temporary file & found there are many ways to do this.
Can you tell me: For each of the following methods below, in which scenario am I supposed to use that method? And which method would best suite my needs?
Method 1:
// Using CreateFile()
CreateFile( "myfile.txt", GENERIC_ALL, ..., FILE_ATTRIBUTE_TEMPORARY, 0); // removed unecessary parameters
Method 2:
// I think that GetTempFileName also creates the file doesn't it? Not just generates a unique fileName?
// Gets the temp path env string (no guarantee it's a valid path).
dwRetVal = GetTempPath(MAX_PATH, // length of the buffer
lpTempPathBuffer); // buffer for path
// Generates a temporary file name.
uRetVal = GetTempFileName(lpTempPathBuffer, // directory for tmp files
TEXT("DEMO"), // temp file name prefix
0, // create unique name
szTempFileName); // buffer for name
Method 3:
// Create a file & use the flag DELETE_ON_CLOSE. So its a temporary file that will delete when the last HANDLE to it closes
HANDLE h_file = CreateFile( tmpfilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL );
Why are there more than 1 way to create a temp file. And, for example, what is the situation where I would want to use, say, method 2 over method 1?
FILE_ATTRIBUTE_TEMPORARY simply tells Windows not to bother writing the file contents to disk if there's enough cache, because the file is temporary and no other process will be using it.
FILE_FLAG_DELETE_ON_CLOSE means just what it says - when you close the file it will be deleted automatically. This guarantees that it will be temporary.
GetTempFilename creates a name for a temporary file, and guarantees that the filename hasn't been used previously.
You should use all 3 methods when creating a temporary file. None of them interferes with the others.
For method #2 if you use 0 for the "unique ID" you actually need to call SetFileAttributes with FILE_ATTRIBUTE_TEMPORARY to make the generated file temporary in the same sense as method #1 (otherwise it will be a normal ARCHIVE/NOT_CONTENT_INDEXED file.)
Use GetFileAttributes or GetFileInformationByHandle to see what attributes the file actually possesses.

How to give 'Everybody' full rights to a file (programmatically)

I'm modifying an old C++ program to run on Vista. It does not require Admin privileges.
I've changed the code to put logfiles in \ProgramData\MyApp\. These logfiles are written with the stdio functions (fopen, fprintf, fclose).
Here's the problem:
UserA runs the program first, it creates \ProgramData\MyApp\MyLogFile.txt using CreateFile()
UserB runs the program next, it tries to append to MyLogFile.txt and gets access denied.
I tried creating a null SECURITY_DESCRIPTOR and passing that to CreateFile(). That does create a file with "no permissions assigned", but it seems as if the first user to write to the file takes ownership and afterwards all the other non-admin users are out of luck.
It's important that all users share the same logfiles, but it's also important that I change as little code as possible.
Edited to add:
\ProgramData\MyApp is created by a standard Visual Studio installer. (I don't see any place to set directory security.) When it creates \MyApp it grants Users these permissions:
Read & execute
List folder contents
Read
Special permissions
Under Advanced I see that Special permissions includes:
Create files / write data
Create folders / append data
Write attributes
Write extended attributes
+1 to everyone for trying, but eventually I found the answer here:
how to change the ACLs from c++?
I did have to change one line of that solution, from this:
ea[0].grfAccessMode = DENY_ACCESS;
to this:
ea[0].grfAccessMode = GRANT_ACCESS;
Probably that your application uses an installer. When the installer creates your folder "MyApp", assign read/write rights for everyone. This will probably fix your problem. There are different ways of doing this, but it depends on the type of the setup that you use.
Added custom action info.
If after install the folder does not have the required permissions you could add for example a custom action as a visual basic script on the install sequence, that will set the required permissions.
VBS Examble:
Function SetPermissions()
Dim strHomeFolder, strHome, strUser
Dim intRunError, objShell, objFSO
strHomeFolder = "C:\Test"
Set objShell = CreateObject("Wscript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FolderExists(strHomeFolder) Then
intRunError = objShell.Run("%COMSPEC% /c Echo Y| cacls " _
& strHomeFolder & " /t /c /g everyone:F ", 2, True)
If intRunError <> 0 Then
Wscript.Echo "Error assigning permissions for user " _
& strUser & " to home folder " & strHomeFolder
End If
End If
End Function
You need to add an allow rule for the user "Everyone" if that is what you truly want.
A null descriptor will defer to the directory's security if memory serves...
You must definetely use CreateFile. See more about security and access rights. I am sure that functions from the standard C library use CreateFile (it can't use anything else on Windows) but with default security parameters which are not helpful in your case.
I tried also to look inside SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR structures but it's not very easy to understand how to do it, though it may be a chance.