* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
**/
#define SC_VOID_DIR "/var/lib/snapd/void"
+static int must_mkdir_and_open_with_perms(const char *dir, uid_t uid, gid_t gid,
+ mode_t mode)
+{
+ int retries = 10;
+ int fd;
+
+ mkdir:
+ if (--retries == 0) {
+ die("lost race to create dir %s too many times", dir);
+ }
+ // Ignore EEXIST since we want to reuse and we will open with
+ // O_NOFOLLOW, below.
+ if (mkdir(dir, 0700) < 0 && errno != EEXIST) {
+ die("cannot create directory %s", dir);
+ }
+ fd = open(dir, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
+ if (fd < 0) {
+ // if is not a directory then remove it and try again
+ if (errno == ENOTDIR && unlink(dir) == 0) {
+ goto mkdir;
+ }
+ die("cannot open directory %s", dir);
+ }
+ // ensure base_dir has the expected permissions since it may have
+ // already existed
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ die("cannot stat base directory %s", dir);
+ }
+ if (st.st_uid != uid || st.st_gid != gid
+ || st.st_mode != (S_IFDIR | mode)) {
+ unsigned char random[10] = { 0 };
+ char random_dir[MAX_BUF] = { 0 };
+ int offset;
+ size_t i;
+
+ // base_dir isn't what we expect - create a random
+ // directory name and rename the existing erroneous
+ // base_dir to this then try recreating it again - NOTE we
+ // don't use mkdtemp() here since we don't want to actually
+ // create the directory yet as we want rename() to do that
+ // for us
+#ifdef SYS_getrandom
+ // use syscall(SYS_getrandom) since getrandom() is
+ // not available on older glibc
+ if (syscall(SYS_getrandom, random, sizeof(random), 0) !=
+ sizeof(random)) {
+ die("cannot get random bytes");
+ }
+#else
+ // use /dev/urandom on older systems which don't support
+ // SYS_getrandom
+ int rfd = open("/dev/urandom", O_RDONLY);
+ if (rfd < 0) {
+ die("cannot open /dev/urandom");
+ }
+ if (read(rfd, random, sizeof(random)) != sizeof(random)) {
+ die("cannot get random bytes");
+ }
+ close(rfd);
+#endif
+ offset =
+ sc_must_snprintf(random_dir, sizeof(random_dir), "%s.",
+ dir);
+ for (i = 0; i < sizeof(random); i++) {
+ offset +=
+ sc_must_snprintf(random_dir + offset,
+ sizeof(random_dir) - offset,
+ "%02x", (unsigned int)random[i]);
+ }
+ // try and get dir which we own by renaming it to something
+ // else then creating it again
+
+ // TODO - change this to use renameat2(RENAME_EXCHANGE)
+ // once we can use a newer version of glibc for snapd
+ if (rename(dir, random_dir) < 0) {
+ die("cannot rename base_dir to random_dir '%s'",
+ random_dir);
+ }
+ close(fd);
+ goto mkdir;
+ }
+ return fd;
+}
+
// TODO: simplify this, after all it is just a tmpfs
// TODO: fold this into bootstrap
static void setup_private_mount(const char *snap_name)
uid_t uid = getuid();
gid_t gid = getgid();
char tmpdir[MAX_BUF] = { 0 };
+ int base_dir_fd SC_CLEANUP(sc_cleanup_close) = -1;
// Create a 0700 base directory, this is the base dir that is
// protected from other users.
// mounted for the applications to use
sc_must_snprintf(tmpdir, sizeof(tmpdir), "/tmp/snap.%d_%s_XXXXXX", uid,
snap_name);
- if (mkdtemp(tmpdir) == NULL) {
- die("cannot create temporary directory essential for private /tmp");
- }
+ base_dir_fd = must_mkdir_and_open_with_perms(tmpdir, 0, 0, 0700);
// now we create a 1777 /tmp inside our private dir
mode_t old_mask = umask(0);
char *d = sc_strdup(tmpdir);
sc_must_snprintf(tmpdir, sizeof(tmpdir), "%s/tmp", d);
free(d);
- if (mkdir(tmpdir, 01777) != 0) {
- die("cannot create temporary directory for private /tmp");
+ // Create /tmp/snap.$SNAP_NAME/tmp 01777 root.root Ignore EEXIST since we
+ // want to reuse and we will open with O_NOFOLLOW, below.
+ if (mkdirat(base_dir_fd, "tmp", 01777) < 0 && errno != EEXIST) {
+ die("cannot create private tmp directory %s/tmp", tmpdir);
}
umask(old_mask);