dir.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /* Copyright (c) 2003, Roger Dingledine
  2. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
  3. * Copyright (c) 2007-2018, The Tor Project, Inc. */
  4. /* See LICENSE for licensing information */
  5. /**
  6. * \file dir.c
  7. *
  8. * \brief Read directories, and create directories with restrictive
  9. * permissions.
  10. **/
  11. #include "lib/fs/dir.h"
  12. #include "lib/fs/path.h"
  13. #include "lib/fs/userdb.h"
  14. #include "lib/log/log.h"
  15. #include "lib/log/util_bug.h"
  16. #include "lib/log/win32err.h"
  17. #include "lib/container/smartlist.h"
  18. #include "lib/sandbox/sandbox.h"
  19. #include "lib/malloc/malloc.h"
  20. #include "lib/string/printf.h"
  21. #include "lib/string/compat_string.h"
  22. #ifdef HAVE_SYS_TYPES_H
  23. #include <sys/types.h>
  24. #endif
  25. #ifdef HAVE_SYS_STAT_H
  26. #include <sys/stat.h>
  27. #endif
  28. #ifdef HAVE_UNISTD_H
  29. #include <unistd.h>
  30. #endif
  31. #ifdef HAVE_FCNTL_H
  32. #include <fcntl.h>
  33. #endif
  34. #ifdef _WIN32
  35. #include <io.h>
  36. #include <direct.h>
  37. #include <windows.h>
  38. #else /* !(defined(_WIN32)) */
  39. #include <dirent.h>
  40. #include <pwd.h>
  41. #include <grp.h>
  42. #endif /* defined(_WIN32) */
  43. #include <errno.h>
  44. #include <string.h>
  45. /** Check whether <b>dirname</b> exists and is private. If yes return 0.
  46. * If <b>dirname</b> does not exist:
  47. * - if <b>check</b>&CPD_CREATE, try to create it and return 0 on success.
  48. * - if <b>check</b>&CPD_CHECK, and we think we can create it, return 0.
  49. * - if <b>check</b>&CPD_CHECK is false, and the directory exists, return 0.
  50. * - otherwise, return -1.
  51. * If CPD_GROUP_OK is set, then it's okay if the directory
  52. * is group-readable, but in all cases we create the directory mode 0700.
  53. * If CPD_GROUP_READ is set, existing directory behaves as CPD_GROUP_OK and
  54. * if the directory is created it will use mode 0750 with group read
  55. * permission. Group read privileges also assume execute permission
  56. * as norm for directories. If CPD_CHECK_MODE_ONLY is set, then we don't
  57. * alter the directory permissions if they are too permissive:
  58. * we just return -1.
  59. * When effective_user is not NULL, check permissions against the given user
  60. * and its primary group.
  61. */
  62. MOCK_IMPL(int,
  63. check_private_dir,(const char *dirname, cpd_check_t check,
  64. const char *effective_user))
  65. {
  66. int r;
  67. struct stat st;
  68. tor_assert(dirname);
  69. #ifndef _WIN32
  70. int fd;
  71. const struct passwd *pw = NULL;
  72. uid_t running_uid;
  73. gid_t running_gid;
  74. /*
  75. * Goal is to harden the implementation by removing any
  76. * potential for race between stat() and chmod().
  77. * chmod() accepts filename as argument. If an attacker can move
  78. * the file between stat() and chmod(), a potential race exists.
  79. *
  80. * Several suggestions taken from:
  81. * https://developer.apple.com/library/mac/documentation/
  82. * Security/Conceptual/SecureCodingGuide/Articles/RaceConditions.html
  83. */
  84. /* Open directory.
  85. * O_NOFOLLOW to ensure that it does not follow symbolic links */
  86. fd = open(sandbox_intern_string(dirname), O_NOFOLLOW);
  87. /* Was there an error? Maybe the directory does not exist? */
  88. if (fd == -1) {
  89. if (errno != ENOENT) {
  90. /* Other directory error */
  91. log_warn(LD_FS, "Directory %s cannot be read: %s", dirname,
  92. strerror(errno));
  93. return -1;
  94. }
  95. /* Received ENOENT: Directory does not exist */
  96. /* Should we create the directory? */
  97. if (check & CPD_CREATE) {
  98. log_info(LD_GENERAL, "Creating directory %s", dirname);
  99. if (check & CPD_GROUP_READ) {
  100. r = mkdir(dirname, 0750);
  101. } else {
  102. r = mkdir(dirname, 0700);
  103. }
  104. /* check for mkdir() error */
  105. if (r) {
  106. log_warn(LD_FS, "Error creating directory %s: %s", dirname,
  107. strerror(errno));
  108. return -1;
  109. }
  110. /* we just created the directory. try to open it again.
  111. * permissions on the directory will be checked again below.*/
  112. fd = open(sandbox_intern_string(dirname), O_NOFOLLOW);
  113. if (fd == -1) {
  114. log_warn(LD_FS, "Could not reopen recently created directory %s: %s",
  115. dirname,
  116. strerror(errno));
  117. return -1;
  118. } else {
  119. close(fd);
  120. }
  121. } else if (!(check & CPD_CHECK)) {
  122. log_warn(LD_FS, "Directory %s does not exist.", dirname);
  123. return -1;
  124. }
  125. /* XXXX In the case where check==CPD_CHECK, we should look at the
  126. * parent directory a little harder. */
  127. return 0;
  128. }
  129. tor_assert(fd >= 0);
  130. //f = tor_strdup(dirname);
  131. //clean_name_for_stat(f);
  132. log_debug(LD_FS, "stat()ing %s", dirname);
  133. //r = stat(sandbox_intern_string(f), &st);
  134. r = fstat(fd, &st);
  135. if (r == -1) {
  136. log_warn(LD_FS, "fstat() on directory %s failed.", dirname);
  137. close(fd);
  138. return -1;
  139. }
  140. //tor_free(f);
  141. /* check that dirname is a directory */
  142. if (!(st.st_mode & S_IFDIR)) {
  143. log_warn(LD_FS, "%s is not a directory", dirname);
  144. close(fd);
  145. return -1;
  146. }
  147. if (effective_user) {
  148. /* Look up the user and group information.
  149. * If we have a problem, bail out. */
  150. pw = tor_getpwnam(effective_user);
  151. if (pw == NULL) {
  152. log_warn(LD_CONFIG, "Error setting configured user: %s not found",
  153. effective_user);
  154. close(fd);
  155. return -1;
  156. }
  157. running_uid = pw->pw_uid;
  158. running_gid = pw->pw_gid;
  159. } else {
  160. running_uid = getuid();
  161. running_gid = getgid();
  162. }
  163. if (st.st_uid != running_uid) {
  164. char *process_ownername = NULL, *file_ownername = NULL;
  165. {
  166. const struct passwd *pw_running = tor_getpwuid(running_uid);
  167. process_ownername = pw_running ? tor_strdup(pw_running->pw_name) :
  168. tor_strdup("<unknown>");
  169. }
  170. {
  171. const struct passwd *pw_stat = tor_getpwuid(st.st_uid);
  172. file_ownername = pw_stat ? tor_strdup(pw_stat->pw_name) :
  173. tor_strdup("<unknown>");
  174. }
  175. log_warn(LD_FS, "%s is not owned by this user (%s, %d) but by "
  176. "%s (%d). Perhaps you are running Tor as the wrong user?",
  177. dirname, process_ownername, (int)running_uid,
  178. file_ownername, (int)st.st_uid);
  179. tor_free(process_ownername);
  180. tor_free(file_ownername);
  181. close(fd);
  182. return -1;
  183. }
  184. if ( (check & (CPD_GROUP_OK|CPD_GROUP_READ))
  185. && (st.st_gid != running_gid) && (st.st_gid != 0)) {
  186. struct group *gr;
  187. char *process_groupname = NULL;
  188. gr = getgrgid(running_gid);
  189. process_groupname = gr ? tor_strdup(gr->gr_name) : tor_strdup("<unknown>");
  190. gr = getgrgid(st.st_gid);
  191. log_warn(LD_FS, "%s is not owned by this group (%s, %d) but by group "
  192. "%s (%d). Are you running Tor as the wrong user?",
  193. dirname, process_groupname, (int)running_gid,
  194. gr ? gr->gr_name : "<unknown>", (int)st.st_gid);
  195. tor_free(process_groupname);
  196. close(fd);
  197. return -1;
  198. }
  199. unsigned unwanted_bits = 0;
  200. if (check & (CPD_GROUP_OK|CPD_GROUP_READ)) {
  201. unwanted_bits = 0027;
  202. } else {
  203. unwanted_bits = 0077;
  204. }
  205. unsigned check_bits_filter = ~0;
  206. if (check & CPD_RELAX_DIRMODE_CHECK) {
  207. check_bits_filter = 0022;
  208. }
  209. if ((st.st_mode & unwanted_bits & check_bits_filter) != 0) {
  210. unsigned new_mode;
  211. if (check & CPD_CHECK_MODE_ONLY) {
  212. log_warn(LD_FS, "Permissions on directory %s are too permissive.",
  213. dirname);
  214. close(fd);
  215. return -1;
  216. }
  217. log_warn(LD_FS, "Fixing permissions on directory %s", dirname);
  218. new_mode = st.st_mode;
  219. new_mode |= 0700; /* Owner should have rwx */
  220. if (check & CPD_GROUP_READ) {
  221. new_mode |= 0050; /* Group should have rx */
  222. }
  223. new_mode &= ~unwanted_bits; /* Clear the bits that we didn't want set...*/
  224. if (fchmod(fd, new_mode)) {
  225. log_warn(LD_FS, "Could not chmod directory %s: %s", dirname,
  226. strerror(errno));
  227. close(fd);
  228. return -1;
  229. } else {
  230. close(fd);
  231. return 0;
  232. }
  233. }
  234. close(fd);
  235. #else /* !(!defined(_WIN32)) */
  236. /* Win32 case: we can't open() a directory. */
  237. (void)effective_user;
  238. char *f = tor_strdup(dirname);
  239. clean_fname_for_stat(f);
  240. log_debug(LD_FS, "stat()ing %s", f);
  241. r = stat(sandbox_intern_string(f), &st);
  242. tor_free(f);
  243. if (r) {
  244. if (errno != ENOENT) {
  245. log_warn(LD_FS, "Directory %s cannot be read: %s", dirname,
  246. strerror(errno));
  247. return -1;
  248. }
  249. if (check & CPD_CREATE) {
  250. log_info(LD_GENERAL, "Creating directory %s", dirname);
  251. r = mkdir(dirname);
  252. if (r) {
  253. log_warn(LD_FS, "Error creating directory %s: %s", dirname,
  254. strerror(errno));
  255. return -1;
  256. }
  257. } else if (!(check & CPD_CHECK)) {
  258. log_warn(LD_FS, "Directory %s does not exist.", dirname);
  259. return -1;
  260. }
  261. return 0;
  262. }
  263. if (!(st.st_mode & S_IFDIR)) {
  264. log_warn(LD_FS, "%s is not a directory", dirname);
  265. return -1;
  266. }
  267. #endif /* !defined(_WIN32) */
  268. return 0;
  269. }
  270. /** Return a new list containing the filenames in the directory <b>dirname</b>.
  271. * Return NULL on error or if <b>dirname</b> is not a directory.
  272. */
  273. MOCK_IMPL(smartlist_t *,
  274. tor_listdir, (const char *dirname))
  275. {
  276. smartlist_t *result;
  277. #ifdef _WIN32
  278. char *pattern=NULL;
  279. TCHAR tpattern[MAX_PATH] = {0};
  280. char name[MAX_PATH*2+1] = {0};
  281. HANDLE handle;
  282. WIN32_FIND_DATA findData;
  283. tor_asprintf(&pattern, "%s\\*", dirname);
  284. #ifdef UNICODE
  285. mbstowcs(tpattern,pattern,MAX_PATH);
  286. #else
  287. strlcpy(tpattern, pattern, MAX_PATH);
  288. #endif
  289. if (INVALID_HANDLE_VALUE == (handle = FindFirstFile(tpattern, &findData))) {
  290. tor_free(pattern);
  291. return NULL;
  292. }
  293. result = smartlist_new();
  294. while (1) {
  295. #ifdef UNICODE
  296. wcstombs(name,findData.cFileName,MAX_PATH);
  297. name[sizeof(name)-1] = '\0';
  298. #else
  299. strlcpy(name,findData.cFileName,sizeof(name));
  300. #endif /* defined(UNICODE) */
  301. if (strcmp(name, ".") &&
  302. strcmp(name, "..")) {
  303. smartlist_add_strdup(result, name);
  304. }
  305. if (!FindNextFile(handle, &findData)) {
  306. DWORD err;
  307. if ((err = GetLastError()) != ERROR_NO_MORE_FILES) {
  308. char *errstr = format_win32_error(err);
  309. log_warn(LD_FS, "Error reading directory '%s': %s", dirname, errstr);
  310. tor_free(errstr);
  311. }
  312. break;
  313. }
  314. }
  315. FindClose(handle);
  316. tor_free(pattern);
  317. #else /* !(defined(_WIN32)) */
  318. const char *prot_dname = sandbox_intern_string(dirname);
  319. DIR *d;
  320. struct dirent *de;
  321. if (!(d = opendir(prot_dname)))
  322. return NULL;
  323. result = smartlist_new();
  324. while ((de = readdir(d))) {
  325. if (!strcmp(de->d_name, ".") ||
  326. !strcmp(de->d_name, ".."))
  327. continue;
  328. smartlist_add_strdup(result, de->d_name);
  329. }
  330. closedir(d);
  331. #endif /* defined(_WIN32) */
  332. return result;
  333. }