Create accessible file from root privilege process - c++

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.

Related

Setting file permissions when opening a file with ofstream

Is there a way in C++'s standard libraries (or linux sys/stat.h, sys/types.h, sys/.... libraries) to set the file permissions of a file when creating it with ofstream (or using something else from those libraries)?
When I create a file it just gets created with some default set of file permissions (I assume whatever the current umask is), but I want to explicitly set the permissions to something other than the default (ex. 600), and I can't just set the umask before starting the program (b/c others will run it).
// Example of creating a file by writing to it
ofstream fp(filename.c_str())
/* write something to it */
Is there a way to do this in C++ or if not, a way to set the umask within the C++ program?
For example, in C's standard library you can just do:
open(filename, O_RDWR|O_CREAT, 0666)
but I don't want to resort to using the C function, as it'd be nice to be able to use the functions associated with fstream objects.
(Side Note: there was a question whose title was exactly what I was looking for, but it turned out to be unrelated.)
You cannot. The standard library must be platform agnostic, and permissions like 0600 are meaningless on Windows for example. If you want to use platform-specific features, you'll need to use platform-specific APIs. Of course, you can always call umask() before you open the file, but that's not part of the C++ standard library, it's part of the platform API.
Note: open() isn't part of the C standard library either. It's a platform API. The C standard library function to open a file is fopen().
In C++17, std::filesystem::permissions was introduced. It will enable you to get and set permissions in a platform-agnostic manner.
Get permissions:
using fs = std::filesystem;
bool owner_can_read =
fs::status("my_file.txt").permissions() & fs::perms::owner_read != fs::perms::none;
Set permissions (add all permissions for owner and group, that is, add modes 0x770 on unix):
using fs = std::filesystem;
fs::permissions("my_file.txt",
fs::perms::owner_all | fs::perms::group_all,
fs::perm_options::add);
Example based on an example from cppreference.

Getting the file-mode from the FILE struct?

I have a piece of C code, a function to be specific, which operates on a FILE*.
Depending on which mode the FILE* was opened with there are certain things I can and cannot do.
Is there any way I can obtain the mode the FILE* was opened with?
That FILE* is all the info I can rely on, because it is created somewhere else in the program and the actual file-name is long lost before it reaches my function, and this I cannot influence.
I would prefer a portable solution.
Edit: I'm not interested in file-restrictions specifying which users can do what with the file. That is mostly irrelevant as it is dealt with upon file-opening. For this bit of code I only care about the open-mode.
On POSIX (and sufficiently similar) systems, fcntl(fileno(f), F_GETFL) will return the mode/flags for the open file in the form that would be passed to open (not fopen). To check whether it was opened read-only, read-write, or write-only, you can do something like:
int mode = fcntl(fileno(f), F_GETFL);
switch (mode & O_ACCMODE) {
case O_RDONLY: ...
case O_WRONLY: ...
case O_RDWR: ...
}
You can also check for flags like O_APPEND, etc.
Assuming Linux/Unix:
See fstat(), to get the details of file permissions.
To get the file descriptor us fileno() for that function

Linux: check if process has read access to file in C/C++

Assuming we have some PID and absolute file path [not a symlink, just a regular file] - what is the most efficient way to determine that PID has read access to this file?
I'm only aware of one way to do this. First, find the UID and GID of the process by constructing the path /proc/ + the PID. For example /proc/4261. You then stat() that path and get its UID and GID. Then, you stat() the file you want to check for read access and check whether the UID/GID of the process has read permissions:
(It is assumed you already constructed the "/proc/[PID]" path in path_to_proc.)
struct stat buf;
// Get UID and GID of the process.
stat(path_to_proc, &buf);
uid_t proc_uid = buf.st_uid;
gid_t proc_gid = buf.st_gid;
// Get UID and GID of the file.
stat(path_to_file_you_want_to_check, &buf);
// If the process owns the file, check if it has read access.
if (proc_uid == buf.st_uid && buf.st_mode & S_IRUSR) {
// Yes, the process has read access.
}
// Check if the group of the process's UID matches the file's group
// and if so, check for read/write access.
else if (proc_gid == buf.st_gid && buf.st_mode & S_IRGRP) {
// Yes, the process has read access.
}
// The process's UID is neither the owner of the file nor does its GID
// match the file's. Check whether the file is world readable.
else if (buf.st_mode & S_IROTH) {
// Yes, the process has read access.
}
Note that the code is not perfect. It does not handle the possibility that the user of the process actually belongs to the file's group without it being the user's primary group. To deal with that, you will need to make use of getgrouplist() (which means you will need to convert the process UID to a string containing the actual username first, and then compare all returned groups to the file's group and if one matches, check for group read access (S_IRGRP).)
Open the file. That's really the only way to know. The answers involving stat(2) require that you write code to interpret the permissions bits and compare them to your active uid/gid and supplemental groups. And in any case it is incomplete in the general case: LSM hooks like selinux or apparmor can also implement permissions models on files that are not captured by the traditional Unix permissions model.

What does fd represent when typing: int fd = open("file");?

I am looking at I/O operations in C++ and I have a question.
When opening a file like:
#include <fcntl.h>
int main() {
unsigned char buffer[16];
int fd = open (argv[1], O_RDONLY);
read(fd, buffer, sizeof(buffer));
return 0;
}
How can the variable fd represent a file as an integer when passing it to the open method? Is it repesenting a file in current folder? If I print the ´fd´variable, it prints 3. What does that mean?
Ps. I know there are several other ways to handle files, like stdio.h, fstream etc but that is out of the scope of this question. Ds.
How can the variable fd represent a file as an integer when passing it to the open method?
It's a handle that identifies the open file; it's generally called a file descriptor, hence the name fd.
When you open the file, the operating system creates some resources that are needed to access it. These are stored in some kind of data structure (perhaps a simple array) that uses an integer as a key; the call to open returns that integer so that when you pass it read, the operating system can use it to find the resources it needs.
Is it repesenting a file in current folder?
It's representing the file that you opened; its filename was argv[1], the first of the arguments that was passed to the program when it was launched. If that file doesn't exist, or open failed for some reason, then it has the value -1 and doesn't represent any file; you really should check for that before you try to do anything with it.
If I print the fd variable, it prints 3. What does that mean?
It doesn't have any particular meaning; but it has that value because it was the fourth file (or file-like thing) that was opened, after the input (0), output (1) and error (2) streams that are used by cin, cout and cerr in C++.
Because that is the index of the table of resources stored for your current process.
Each process has it own resources table, so you just need to pass the index to read/write/etc function
Generally, a file descriptor is an index for an entry in a kernel-resident data structure containing the details of all open files. In POSIX this data structure is called a file descriptor table, and each process has its own file descriptor table. The user application passes the abstract key to the kernel through a system call, and the kernel will access the file on behalf of the application, based on the key. The application itself cannot read or write the file descriptor table directly.
from: http://en.wikipedia.org/wiki/File_descriptor
open() returns the file descriptor of the file which is the C type int. To know more about File Descriptor refer http://en.wikipedia.org/wiki/File_descriptor.
"fd" stands for file descriptor. It is a value identifying a file. It is often an index (in the global table), an offset, or a pointer. Different APIs use different types. WinAPI, for example, uses different types of handles (HANDLE, HGDI, etc.), which are essentially typedefs for int/void*/long, and so on.
Using naked types like "int" is usually not a good idea, but if the implementation tells you to do so (like POSIX in this case), you should keep it.
The simplified answer is that fd is just an index into some array of file descriptors.
When most processes are started, they are given three open file descriptors to begin with: stdin (0), stdout (1), and stderr (2). So when you open your first file, the next available array entry is 3.

How to set file permissions (cross platform) in C++?

I am using C++ ofstream to write out a file. I want to set the permissions to be only accessible by the user: 700. In unix; I suppose I can just issue a system("chmod 700 file.txt"); but I need this code to work on Windows as well. I can use some Windows api; but what is the best c++ cross platform way to do this?
Ironically, I have just run into this very same need earlier today.
In my case, the answer came down to what level of permission granularity I need on Windows, versus Linux. In my case, I only care about User, Group, and Other permission on Linux. On Windows, the basic Read/Write All permission leftover from DOS is good enough for me, i.e. I don't need to deal with ACL on Windows.
Generally speaking, Windows has two privilege models: the basic DOS model and the newer access control model. Under the DOS model there is one type of privilege: write privilege. All files can be read, so there is no way to turn off read permission (because it doesn't exist). There is also no concept of execute permission. If a file can be read (answer is yes) and it is binary, then it can be executed; otherwise it can't.
The basic DOS model is sufficient for most Windows environments, i.e. environments where the system is used by a single user in a physical location that can be considered relatively secure. The access control model is more complex by several orders of magnitude.
The access control model uses access control lists (ACL) to grant privileges. Privileges can only be granted by a process with the necessary privileges. This model not only allows the control of User, Group, and Other with Read, Write, and Execute permission, but it also allows control of files over the network and between Windows domains. (You can also get this level of insanity on Unix systems with PAM.)
Note: The Access Control model is only available on NTFS partitions, if you are using FAT partitions you are SOL.
Using ACL is a big pain in the ass. It is not a trivial undertaking and it will require you to learn not just ACL but also all about Security Descriptors, Access Tokens, and a whole lot of other advanced Windows security concepts.
Fortunately for me, for my current needs, I don't need the true security that the access control model provides. I can get by with basically pretending to set permissions on Windows, as long as I really set permissions on Linux.
Windows supports what they call an "ISO C++ conformant" version of chmod(2). This API is called _chmod, and it is similar to chmod(2), but more limited and not type or name compatible (of course). Windows also has a deprecated chmod, so you can't simply add chmod to Windows and use the straight chmod(2) on Linux.
I wrote the following:
#include <sys/stat.h>
#include <sys/types.h>
#ifdef _WIN32
# include <io.h>
typedef int mode_t;
/// #Note If STRICT_UGO_PERMISSIONS is not defined, then setting Read for any
/// of User, Group, or Other will set Read for User and setting Write
/// will set Write for User. Otherwise, Read and Write for Group and
/// Other are ignored.
///
/// #Note For the POSIX modes that do not have a Windows equivalent, the modes
/// defined here use the POSIX values left shifted 16 bits.
static const mode_t S_ISUID = 0x08000000; ///< does nothing
static const mode_t S_ISGID = 0x04000000; ///< does nothing
static const mode_t S_ISVTX = 0x02000000; ///< does nothing
static const mode_t S_IRUSR = mode_t(_S_IREAD); ///< read by user
static const mode_t S_IWUSR = mode_t(_S_IWRITE); ///< write by user
static const mode_t S_IXUSR = 0x00400000; ///< does nothing
# ifndef STRICT_UGO_PERMISSIONS
static const mode_t S_IRGRP = mode_t(_S_IREAD); ///< read by *USER*
static const mode_t S_IWGRP = mode_t(_S_IWRITE); ///< write by *USER*
static const mode_t S_IXGRP = 0x00080000; ///< does nothing
static const mode_t S_IROTH = mode_t(_S_IREAD); ///< read by *USER*
static const mode_t S_IWOTH = mode_t(_S_IWRITE); ///< write by *USER*
static const mode_t S_IXOTH = 0x00010000; ///< does nothing
# else
static const mode_t S_IRGRP = 0x00200000; ///< does nothing
static const mode_t S_IWGRP = 0x00100000; ///< does nothing
static const mode_t S_IXGRP = 0x00080000; ///< does nothing
static const mode_t S_IROTH = 0x00040000; ///< does nothing
static const mode_t S_IWOTH = 0x00020000; ///< does nothing
static const mode_t S_IXOTH = 0x00010000; ///< does nothing
# endif
static const mode_t MS_MODE_MASK = 0x0000ffff; ///< low word
static inline int my_chmod(const char * path, mode_t mode)
{
int result = _chmod(path, (mode & MS_MODE_MASK));
if (result != 0)
{
result = errno;
}
return (result);
}
#else
static inline int my_chmod(const char * path, mode_t mode)
{
int result = chmod(path, mode);
if (result != 0)
{
result = errno;
}
return (result);
}
#endif
It's important to remember that my solution only provides DOS type security. This is also known as no security, but it is the amount of security that most apps give you on Windows.
Also, under my solution, if you don't define STRICT_UGO_PERMISSIONS, when you give a permission to group or other (or remove it for that matter), you are really changing the owner. If you didn't want to do that, but you still didn't need full Windows ACL permissions, just define STRICT_UGO_PERMISSIONS.
There is no cross-platform way to do this. Windows does not support Unix-style file permissions. In order to do what you want, you'll have to look into creating an access control list for that file, which will let you explicitly define access permissions for users and groups.
An alternative might be to create the file in a directory whose security settings have already been set to exclude everyone but the user.
Cross-platform example to set 0700 for a file with C++17 and its std::filesystem.
#include <exception>
//#include <filesystem>
#include <experimental/filesystem> // Use this for most compilers as of yet.
//namespace fs = std::filesystem;
namespace fs = std::experimental::filesystem; // Use this for most compilers as of yet.
int main()
{
fs::path myFile = "path/to/file.ext";
try {
fs::permissions(myFile, fs::perms::owner_all); // Uses fs::perm_options::replace.
}
catch (std::exception& e) {
// Handle exception or use another overload of fs::permissions()
// with std::error_code.
}
}
See std::filesystem::permissions, std::filesystem::perms and std::filesystem::perm_options.
The system() call is a strange beast. I have been bitten by a NOP system() implementation on a Mac many moons ago. It's implementation defined meaning the standard doesn't define what an implementation (platform/compiler) is supposed to do. Unfortunately, this is also about the only standard way of doing something outside the scope of your function (in your case -- changing the permissions).
Update: A proposed hack:
Create a non-empty file with appropriate permissions on your system.
Use Boost Filesystem's copy_file to copy this file out to your desired output.
void copy_file(const path& frompath, const path& topath): The contents and attributes of the file referred to by frompath is copied to the file referred to by topath. This routine expects a destination file to be absent; if the destination file is present, it throws an exception. This, therefore, is not equivalent to the system specified cp command in UNIX. It is also expected that the frompath variable would refer to a proper regular file. Consider this example: frompath refers to a symbolic link /tmp/file1, which in turn refers to a file /tmp/file2; topath is, say, /tmp/file3. In this situation, copy_file will fail. This is yet another difference that this API sports compared to the cp command.
Now, overwrite the output with actual contents.
But, this is only a hack I thought of long after midnight. Take it with a pinch of salt and try this out :)
No idea if it would work, but you could look into using the chmod.exe executable that comes with Cygwin.
There's no standard way to do this in C++, but for this special requirement you should probably just write a custom wrapper, with #ifdef _WIN32. Qt has a permission wrapper in it's QFile class, but this would of course mean depending on Qt ...
I just found a couple of ways to do chmod 700 easily from the Windows command line. I'm going to post another question asking for help coming up with an equivalent win32 security descriptor structure to use (if I can't figure it out in the next few hours).
Windows 2000 & XP (messy- it always seems to prompt):
echo Y|cacls *onlyme.txt* /g %username%:F
Windows 2003+:
icacls *onlyme.txt* /inheritance:r /grant %username%:r
EDIT:
If you had the ability to use the ATL, this article covers it (I don't have Visual Studio available):
http://www.codeproject.com/KB/winsdk/accessctrl2.aspx
Actually, it says the sample code includes non-ATL sample code as well- it should have something that works for you (and me!)
The important thing to remember is to get r/w/x for owner only on win32, you just have to wipe all of the security descriptors from the file and add one for yourself with full control.
You can't do it in a cross-platform manner. In Linux, you should use the function chmod(2) instead of using system(2) to spawn a new shell. On Windows, you'll have to use the various authorization functions to make an ACL (access-control list) with the proper permissions.