diff --git a/accounts/apps.py b/accounts/apps.py index 1993f7c6..45b2613d 100644 --- a/accounts/apps.py +++ b/accounts/apps.py @@ -40,10 +40,14 @@ def _store_generated_password(password): path = os.path.join(str(settings.BASE_DIR), "data", "admin_password") try: os.makedirs(os.path.dirname(path), exist_ok=True) - fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) + # Never write through a pre-existing file or symlink (this runs as + # root in Docker, while data/ belongs to www-data). + if os.path.lexists(path): + os.unlink(path) + fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) + os.fchmod(fd, 0o600) with os.fdopen(fd, "w") as f: f.write(password + "\n") - os.chmod(path, 0o600) print(f"\033[1m* \033[93mGenerated admin password written to {path}\033[0m") except OSError: print(f"\033[1m* \033[93mGenerated random admin password: {password}\033[0m") diff --git a/accounts/test_admin_password.py b/accounts/test_admin_password.py index 95789060..eb9e7acc 100644 --- a/accounts/test_admin_password.py +++ b/accounts/test_admin_password.py @@ -38,3 +38,17 @@ class GeneratedAdminPasswordTestCase(TestCase): self.assertNotIn(password, output) self.assertIn(str(password_file), output) self.assertIsNotNone(authenticate(username="admin", password=password)) + + def test_does_not_follow_a_planted_symlink(self): + with tempfile.TemporaryDirectory() as tmp: + (Path(tmp) / "data").mkdir() + victim = Path(tmp) / "victim" + victim.write_text("original\n") + (Path(tmp) / "data" / "admin_password").symlink_to(victim) + + self._run_create_admin(tmp) + + password_file = Path(tmp) / "data" / "admin_password" + self.assertEqual(victim.read_text(), "original\n") + self.assertFalse(password_file.is_symlink()) + self.assertEqual(stat.S_IMODE(password_file.stat().st_mode), 0o600) diff --git a/install.sh b/install.sh index 964fc34c..34a78054 100755 --- a/install.sh +++ b/install.sh @@ -83,8 +83,7 @@ chmod 744 "$INSTALLER" LOG_FILE="/var/log/webvirtcloud-install.log" mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null || true if ! touch "$LOG_FILE" 2>/dev/null; then - LOG_FILE="/tmp/webvirtcloud-install.log" - touch "$LOG_FILE" + LOG_FILE="$(mktemp /tmp/webvirtcloud-install.XXXXXX)" fi chmod 600 "$LOG_FILE" 2>/dev/null || true