How to Read Linux File Permission Programmatically in C/C++

How to read linux file permission programmatically in C/C++

The stat(2) system call returns a struct stat that contains a st_mode member. This is the mode bits that ls -l displays.

On my system, the man 2 stat page says:

   The following flags are defined for the st_mode field:

S_IFMT 0170000 bitmask for the file type bitfields
S_IFSOCK 0140000 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 FIFO
S_ISUID 0004000 set UID bit
S_ISGID 0002000 set-group-ID bit (see below)
S_ISVTX 0001000 sticky bit (see below)
S_IRWXU 00700 mask for file owner permissions
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070 mask for group permissions
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 mask for permissions for others (not in group)
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission

How to read linux file permission programmatically in linux kernel

For check whether a user has specific permissions for given inode, use inode_permissions function. It is declared in linux/fs.h and has following definition (in fs/namei.c):

/**
* inode_permission - Check for access rights to a given inode
* @inode: Inode to check permission on
* @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
*
* Check for read/write/execute permissions on an inode. We use fs[ug]id for
* this, letting us set arbitrary permissions for filesystem access without
* changing the "normal" UIDs which are used for other things.
*
* When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
*/
int inode_permission(struct inode *inode, int mask)

Usage example:

if(inode_permission(inode, MAY_WRITE | MAY_READ) == 0) {
// Current user has permissions to read and write the file.
}

Evaluating permission bit of a file

stat is a Linux system call, not just a shell command.

man 2 stat

That means you can write code like this:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main() {

struct stat buf;

if (0 == stat("file.txt", &buf)) {
if (buf.st_mode & S_IXOTH) {
/* do what you wanted */
}
} else {
/* handle error accessing the file here **/
}

return 0;
}

How to change permissions of a file in Linux with C++


#include <sys/types.h>
#include <sys/stat.h>

int main() {
chmod("./myfile", S_IRWXU); // enables owner to rwx file
}

See man 2 chmod for more details.

If the question is how to parse a 9 character string of the form "rwx-w-r--", realize that the permissions are encoded in an int as bits. If a bit is on, that permission is on. The following code will take your string, turn it into bits, in the obvious way...no validation etc. is done. It's a proof-of-concept.

#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>

int parse(char* perms) {
int bits = 0;
for(int i=0; i<9; i++){
if (perms[i] != '-') {
bits |= 1<<(8-i);
}
}
return bits;
}
int main() {
char perms[]="rwx-w-r--";
int exmp = S_IRWXU | S_IWGRP | S_IROTH;
printf("%d %d\n", parse(perms), exmp);
// outputs 468 468
}

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

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.

File permissions on Linux/Unix with .NET Core

At the moment, there is no built in API in .NET Core for this. However, the .NET Core team is working to make Mono.Posix available on .NET Core. This exposes API to do this kind of operation in managed code. See https://github.com/dotnet/corefx/issues/15289 and https://github.com/dotnet/corefx/issues/3186. You can try an early version of this API here: https://www.nuget.org/packages/Mono.Posix.NETStandard/1.0.0-beta1

    var unixFileInfo = new Mono.Unix.UnixFileInfo("test.txt");
// set file permission to 644
unixFileInfo.FileAccessPermissions =
FileAccessPermissions.UserRead | FileAccessPermissions.UserWrite
| FileAccessPermissions.GroupRead
| FileAccessPermissions.OtherRead;

If you don't want to use Mono.Posix, you can implement this same functionality by invoking native code. Using P/Invoke, you can call the chmod function from libc. See man 2 chmod for more details on the native API.

using System;
using System.IO;
using System.Runtime.InteropServices;
using static System.Console;

class Program
{
[DllImport("libc", SetLastError = true)]
private static extern int chmod(string pathname, int mode);

// user permissions
const int S_IRUSR = 0x100;
const int S_IWUSR = 0x80;
const int S_IXUSR = 0x40;

// group permission
const int S_IRGRP = 0x20;
const int S_IWGRP = 0x10;
const int S_IXGRP = 0x8;

// other permissions
const int S_IROTH = 0x4;
const int S_IWOTH = 0x2;
const int S_IXOTH = 0x1;

static void Main(string[] args)
{
WriteLine("Setting permissions to 0755 on test.sh");
const int _0755 =
S_IRUSR | S_IXUSR | S_IWUSR
| S_IRGRP | S_IXGRP
| S_IROTH | S_IXOTH;
WriteLine("Result = " + chmod(Path.GetFullPath("test.sh"), (int)_0755));

WriteLine("Setting permissions to 0644 on sample.txt");
const int _0644 =
S_IRUSR | S_IWUSR
| S_IRGRP
| S_IROTH;
WriteLine("Result = " + chmod(Path.GetFullPath("sample.txt"), _0644));

WriteLine("Setting permissions to 0600 on secret.txt");
const int _0600 = S_IRUSR | S_IWUSR;
WriteLine("Result = " + chmod(Path.GetFullPath("secret.txt"), _0600));
}
}


Related Topics



Leave a reply



Submit