|
54 | 54 | #include <syslog.h> |
55 | 55 | #include <algorithm> |
56 | 56 | #include <atomic> |
| 57 | +#include <filesystem> |
57 | 58 | #include <list> |
58 | 59 | #include <string> |
59 | 60 |
|
@@ -1757,6 +1758,83 @@ change_uid_gid(const char *user) |
1757 | 1758 | #endif |
1758 | 1759 | } |
1759 | 1760 |
|
| 1761 | +#if !TS_USE_POSIX_CAP |
| 1762 | +/** |
| 1763 | + * Recursively chown a directory and all its contents to the given uid/gid. |
| 1764 | + * Uses lchown() to avoid following symlinks. |
| 1765 | + */ |
| 1766 | +static void |
| 1767 | +chown_dir_recursive(const char *dir, uid_t uid, gid_t gid) |
| 1768 | +{ |
| 1769 | + if (lchown(dir, uid, gid) != 0) { |
| 1770 | + Warning("chown_dir_recursive: failed to chown '%s': %s", dir, strerror(errno)); |
| 1771 | + } |
| 1772 | + |
| 1773 | + std::error_code ec; |
| 1774 | + |
| 1775 | + for (const auto &entry : std::filesystem::recursive_directory_iterator(dir, ec)) { |
| 1776 | + if (lchown(entry.path().c_str(), uid, gid) != 0) { |
| 1777 | + Warning("chown_dir_recursive: failed to chown '%s': %s", entry.path().c_str(), strerror(errno)); |
| 1778 | + } |
| 1779 | + } |
| 1780 | + |
| 1781 | + if (ec) { |
| 1782 | + Warning("chown_dir_recursive: error iterating '%s': %s", dir, ec.message().c_str()); |
| 1783 | + } |
| 1784 | +} |
| 1785 | + |
| 1786 | +/** |
| 1787 | + * On systems without POSIX capabilities, privilege is dropped late — after |
| 1788 | + * initialization has already created files and directories as root. This |
| 1789 | + * causes runtime operations (plugin reloads, log rotation) to fail because |
| 1790 | + * the now-unprivileged process can't write to root-owned paths. Fix this by |
| 1791 | + * chowning affected directories to the target user before dropping privileges. |
| 1792 | + */ |
| 1793 | +static void |
| 1794 | +chown_owned_dirs(const char *user) |
| 1795 | +{ |
| 1796 | + if (getuid() != 0 && geteuid() != 0) { |
| 1797 | + return; |
| 1798 | + } |
| 1799 | + |
| 1800 | + struct passwd *pwd; |
| 1801 | + struct passwd pbuf; |
| 1802 | + unsigned pw_bufsize = 4096; |
| 1803 | +#if defined(_SC_GETPW_R_SIZE_MAX) |
| 1804 | + long pw_size = sysconf(_SC_GETPW_R_SIZE_MAX); |
| 1805 | + |
| 1806 | + if (pw_size > 0) { |
| 1807 | + pw_bufsize = static_cast<unsigned>(pw_size); |
| 1808 | + } |
| 1809 | +#endif |
| 1810 | + char buf[pw_bufsize]; |
| 1811 | + |
| 1812 | + if (*user == '#') { |
| 1813 | + uid_t uid = static_cast<uid_t>(atoi(&user[1])); |
| 1814 | + if (getpwuid_r(uid, &pbuf, buf, sizeof(buf), &pwd) != 0 || pwd == nullptr) { |
| 1815 | + Warning("chown_owned_dirs: cannot resolve uid %ld", static_cast<long>(uid)); |
| 1816 | + return; |
| 1817 | + } |
| 1818 | + } else { |
| 1819 | + if (getpwnam_r(user, &pbuf, buf, sizeof(buf), &pwd) != 0 || pwd == nullptr) { |
| 1820 | + Warning("chown_owned_dirs: cannot resolve user '%s'", user); |
| 1821 | + return; |
| 1822 | + } |
| 1823 | + } |
| 1824 | + |
| 1825 | + std::string rundir(RecConfigReadRuntimeDir()); |
| 1826 | + std::string logdir(RecConfigReadLogDir()); |
| 1827 | + |
| 1828 | + if (!rundir.empty()) { |
| 1829 | + chown_dir_recursive(rundir.c_str(), pwd->pw_uid, pwd->pw_gid); |
| 1830 | + } |
| 1831 | + |
| 1832 | + if (!logdir.empty()) { |
| 1833 | + chown_dir_recursive(logdir.c_str(), pwd->pw_uid, pwd->pw_gid); |
| 1834 | + } |
| 1835 | +} |
| 1836 | +#endif // !TS_USE_POSIX_CAP |
| 1837 | + |
1760 | 1838 | /* |
1761 | 1839 | * Binds stdout and stderr to files specified by the parameters |
1762 | 1840 | * |
@@ -2400,6 +2478,7 @@ main(int /* argc ATS_UNUSED */, const char **argv) |
2400 | 2478 |
|
2401 | 2479 | #if !TS_USE_POSIX_CAP |
2402 | 2480 | if (admin_user_p) { |
| 2481 | + chown_owned_dirs(user); |
2403 | 2482 | change_uid_gid(user); |
2404 | 2483 | } |
2405 | 2484 | #endif |
|
0 commit comments