views:

112

answers:

4

Changing a Linux C++ program which gives the user limited file access. Thus the program chroots itself to a sandbox with the files the user can get at. All worked well.

Now, however, the program needs to access some files for its own needs (not the user's) but they are outside the sandbox. I know chroot allows access to files opened before the chroot but in this case the needed files could a few among many hundreds so it is obviously impractical to open them all just for the couple that might be required.

Is there any way to get at the files?

+5  A: 

Copy them into the sandbox or open them all before chrooting. Seriously. If there was a way to do this, there would be a way to suborn it to allow other access and make your protection useless.

The whole point of the sandbox is to prevent exactly what you're trying to achieve.

paxdiablo
A: 

If the files are all in 1 directory, you could use mount to bind them to a directory inside the sandbox.

mount --bind /path/to/files /sandbox/files

The you can access the files through /sandbox/files/. If you don't want the user to see them, do mount --bind /path/to/files /sandbox/.files so the .files directory is hidden

m42a
Doesn't the user then also have access to /sandbox/files?
unknown google user
If that program is the only way they'll be interacting with the sandbox, then you could just disallow access through the program's interface.
m42a
+2  A: 

I guess that you ought to be able to split your program into two parts, one which is chroot'ed and one which isn't, and have the chroot'ed portion request files' contents from the non-chroot'ed portion via the IPC mechanism of your choice.

This is a hack, and it may be easy to get wrong, negating any benefit of a chroot. Like paxdiablo says, you're trying to get around the whole purpose of a chroot sandbox, so your options are very, very limited.

Maybe if you explained a bit more what you're trying to accomplish, we might be able to offer some other ideas. For example, SELinux and AppArmor are more flexible than chroot and may be able to give you the security you seek.

Josh Kelley
Those are both valid options and I'll use them if that's where things lead but I was hoping for something requiring less surgery. One can hope, right?
unknown google user
+3  A: 

If the files you need to access are within a few directories you could open those directories before you chroot and save the file descriptors. You can then use the so-called *at functions (e.g. openat(), renameat(), etc.) to get at the individual files. Basically you are opening the files relative to the already open directory file descriptors rather than the chrooted directory.

Whether this is a safe thing to do is open to question but it should work in Linux.

EDIT: This is on the ugly side but it seems to work. You should poke around a lot more for vulnerabilities than I have. I haven't tested how dropping privileges and so forth will effect things.

#include <iostream>
#include <string>

using namespace std;

#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <cstring>

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


int main(int argc, char *argv[])
{
    if (argc < 4)
    {
        cerr << "USAGE: " << argv[0] << " <jail directory> <freeworld directory> <filename>\n";
        exit(EXIT_FAILURE);
    }

    const string JAILDIR(argv[1]);
    const string FREEDIR(argv[2]);
    string freefilename(argv[3]);

    while (freefilename[0] == '/')
        freefilename.erase(0, 1);

    DIR *pDir;

    if ((pDir = opendir(FREEDIR.c_str())) == NULL)
    {
        perror("Could not open outside dir");
        exit(EXIT_FAILURE);
    } 

    int freeFD = dirfd(pDir);

    //cd to jail dir
    if (chdir(JAILDIR.c_str()) == -1)
    {
        perror("cd before chroot");
        exit(EXIT_FAILURE);
    }

    //lock in jail
    if (chroot(JAILDIR.c_str()) < 0)
    {
        cerr << "Failed to chroot to " << JAILDIR << " - " << strerror(errno) << endl;
        exit(EXIT_FAILURE);
    }

    //
    //in jail, won't work
    //

    string JailFile(FREEDIR);
    JailFile += "/";
    JailFile += freefilename;

    int jailFD;

    if ((jailFD = open(JailFile.c_str(), O_RDONLY)) == -1)
    {
        cout << "as expected, could not open " << JailFile << endl;
        perror("exected open fail");
    }
    else
    {
        cout << "defying all logic, opened " << JailFile << endl;
        exit(EXIT_FAILURE);
    }

    //
    //using this works
    //

    if ((jailFD = openat(freeFD, freefilename.c_str(), O_RDONLY)) == -1)
    {
        cout << "example did not work. Could not open " << freefilename << " Sorry!" << endl;
        exit(EXIT_FAILURE);
    }
    else
        cout << "opened " << freefilename << " from inside jail" << endl;

    char     buff[255];
    ssize_t  numread;

    while (1)
    {
        if ((numread = read(jailFD, buff, sizeof(buff) - 1)) == -1)
        {
            perror("read");
            exit(EXIT_FAILURE);
        }

        if (numread == 0)
            break;

        buff[numread] = '\0';
        cout << buff << endl;
    }

    return 0;
}

To test:

echo "Hello World" >/tmp/mystuff.dat

mkdir /tmp/jail

sudo ./myprog /tmp/jail /tmp mystuff.dat

Duck
The files are in 3 directories.
unknown google user
This seems to work in principle. I'll test it more and run it by some people but this might be a good short term solution for us. Thanks!
unknown google user
Unless there's something preventing openat() from accepting a relative pathname that includes "../", and I don't see anything to that effect in my man pages, this completely breaks your jail.
pra
@pra I'm not sure I understand your point. That was the goal. Are you agreeing? As long as you have a directory open before the chroot you should pretty much have the whole file system available, permissions and such aside.
Duck
I'd initially read your answer to suggest that access would be limited to files under the directory whose fd you'd saved.Given that we agree that the whole filesystem is available, calling the safety of this approach "open to question" is being overly charitable. There is no safety in this approach, and whatever requirement specified a jail in the first place is no longer being met.
pra
@pra Well that's not entirely true. The process is still in jail and whatever mechanism it is providing to the user, be it a prompt, a fork/exec, a shell, or whatever, I believe it should retain the chrooted root path. So as long as the program is careful to segregate things and not expose the open dir fd it shouldn't be any less secure. That said, chroot itself isn't exploit free and this isn't making it any *more* secure, for sure. I would put this more in the if you play with fire expect to get burned once in awhile category.
Duck