views:

4150

answers:

7

Hi guys. 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?

+5  A: 

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.

Ferruccio
+1  A: 

No idea if it would work, but you could look into using the chmod.exe executable that comes with Cygwin.

codelogic
+1 because it's an interesting idea. I know that chmod under cygwin does The Right Thing every time I've ever tried it.
rmeador
What? It changes NTFS ACLs?
aib
+1  A: 

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 ...

VolkA
The warning on the link you provided implies that this might not work as expected on Windows.
Ferruccio
A: 

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.

Adam Rosenfield
A: 

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 :)

dirkgently
+5  A: 

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 of 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 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.

Mike
A: 

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.

nstenz