0

To ensure consistency on power outage, files can be updated as follows (pseudocode):

echo original_contents > file
echo new_contents > /tmp_dir_in_same_fs/file.tmp
mv /tmp_dir_in_same_fs/file.tmp file

Now the thing is, if the files are in a folder root-owned, that rename operation gets a permission denied:

mkdir the_dir
echo original_contents > the_dir/file
echo new_contents > file.tmp
sudo chown root:root the_dir
sudo chmod 755 the_dir
mv file.tmp the_dir/file # permission denied

while:

echo new_contents > the_dir/file

works (but it's not as safe when it comes to consistency on power loss).

So I'm thinking about writing a small SUID program rename_suid that will just check that the source & target files have the proper ownership, so the replacement can be performed.

Here's a proposed implementation:

/*! \file rename_suid.c
    \brief rename file with SUID, provided target file is owned
    by calling user and source file mode mode matches the target file's.

    \license https://creativecommons.org/licenses/by-sa/3.0/
    © cJ-so-rs@zougloub.eu 2018
*/

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


int main(int argc, char ** argv)
{
    int res;

    if (argc != 3) {
        fprintf(stderr, "Usage: %s <source> <target>\n", argv[0]);
        return EINVAL;
    }

    char const * src = argv[1];
    char const * dst = argv[2];
    struct stat stat_src;
    struct stat stat_dst;
    int uid = getuid();
    int euid = geteuid();

    if (euid != 0) {
        fprintf(stderr, "I am not root EUID, trouble ahead\n");
    }

    res = stat(src, &stat_src);
    if (res != 0) {
        perror("Cannot stat source file");
        return errno;
    }

    res = stat(dst, &stat_dst);
    if (res != 0) {
        perror("Cannot stat target file");
        return errno;
    }

    if (stat_src.st_uid != uid) {
        perror("Bad source uid");
        return EPERM
    }

    if (stat_dst.st_uid != uid) {
        perror("Bad target uid");
        return EPERM;
    }

    if (stat_src.st_mode != stat_dst.st_mode) {
        fprintf(stderr, "Inconsistent source-target modes\n");
        return EPERM;
    }

    res = rename(src, dst);
    if (res != 0) {
        perror("Cannot rename");
    }

    return errno;
}

The question is... am I missing something:

  • Is there another solution to the problem?
  • If not, does a similar tool exist?
  • If such this tool is the only solution, could it, by its principle of operation, cause unforeseen security issues?
  • Any (security) problem with the proposed implementation?

Thanks,

cJ Zougloub
  • 1,484
  • 10
  • 19
  • Wouldn't setfacl on the directory help you? You are anyway going to give similar privileges to the non-root user via the binary. Don't really understand the scenario but writing this seems risky+overkill for this task. I'd personally try to avoid power outage with a power source backup and on power outage I'd gracefully shut the services down if UPS can't hold it anymore. You can anyway run into issues like changes not being (yet) synced to the disk etc. and data corruption can happen. – lukash Aug 24 '18 at 07:14
  • The user shouldn't be able to create new files in this folder. The 777+sticky bit solution doesn't work either. – cJ Zougloub Aug 24 '18 at 14:31
  • A more expert friend told me that a) my SUID code has a TOCTOU problem (although it's not clear how it could be exploited) and b) he suggests to bypass the problem by having the files in the folder be symlinks to user files, so the user can do their atomic moves elsewhere. The problem can be worked around with that solution but let's assume that we don't want to do that for some reason ;) – cJ Zougloub Aug 24 '18 at 14:35

0 Answers0