check-perm.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /*
  2. * Placed in the public domain
  3. */
  4. /* $OpenBSD: modpipe.c,v 1.6 2013/11/21 03:16:47 djm Exp $ */
  5. #include "includes.h"
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <unistd.h>
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <stdarg.h>
  12. #include <stdlib.h>
  13. #include <errno.h>
  14. #include <pwd.h>
  15. #ifdef HAVE_LIBGEN_H
  16. #include <libgen.h>
  17. #endif
  18. static void
  19. fatal(const char *fmt, ...)
  20. {
  21. va_list args;
  22. va_start(args, fmt);
  23. vfprintf(stderr, fmt, args);
  24. fputc('\n', stderr);
  25. va_end(args);
  26. exit(1);
  27. }
  28. /* Based on session.c. NB. keep tests in sync */
  29. static void
  30. safely_chroot(const char *path, uid_t uid)
  31. {
  32. const char *cp;
  33. char component[PATH_MAX];
  34. struct stat st;
  35. if (*path != '/')
  36. fatal("chroot path does not begin at root");
  37. if (strlen(path) >= sizeof(component))
  38. fatal("chroot path too long");
  39. /*
  40. * Descend the path, checking that each component is a
  41. * root-owned directory with strict permissions.
  42. */
  43. for (cp = path; cp != NULL;) {
  44. if ((cp = strchr(cp, '/')) == NULL)
  45. strlcpy(component, path, sizeof(component));
  46. else {
  47. cp++;
  48. memcpy(component, path, cp - path);
  49. component[cp - path] = '\0';
  50. }
  51. /* debug3("%s: checking '%s'", __func__, component); */
  52. if (stat(component, &st) != 0)
  53. fatal("%s: stat(\"%s\"): %s", __func__,
  54. component, strerror(errno));
  55. if (st.st_uid != 0 || (st.st_mode & 022) != 0)
  56. fatal("bad ownership or modes for chroot "
  57. "directory %s\"%s\"",
  58. cp == NULL ? "" : "component ", component);
  59. if (!S_ISDIR(st.st_mode))
  60. fatal("chroot path %s\"%s\" is not a directory",
  61. cp == NULL ? "" : "component ", component);
  62. }
  63. if (chdir(path) == -1)
  64. fatal("Unable to chdir to chroot path \"%s\": "
  65. "%s", path, strerror(errno));
  66. }
  67. /* from platform.c */
  68. int
  69. platform_sys_dir_uid(uid_t uid)
  70. {
  71. if (uid == 0)
  72. return 1;
  73. #ifdef PLATFORM_SYS_DIR_UID
  74. if (uid == PLATFORM_SYS_DIR_UID)
  75. return 1;
  76. #endif
  77. return 0;
  78. }
  79. /* from auth.c */
  80. int
  81. auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
  82. uid_t uid, char *err, size_t errlen)
  83. {
  84. char buf[PATH_MAX], homedir[PATH_MAX];
  85. char *cp;
  86. int comparehome = 0;
  87. struct stat st;
  88. if (realpath(name, buf) == NULL) {
  89. snprintf(err, errlen, "realpath %s failed: %s", name,
  90. strerror(errno));
  91. return -1;
  92. }
  93. if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL)
  94. comparehome = 1;
  95. if (!S_ISREG(stp->st_mode)) {
  96. snprintf(err, errlen, "%s is not a regular file", buf);
  97. return -1;
  98. }
  99. if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) ||
  100. (stp->st_mode & 022) != 0) {
  101. snprintf(err, errlen, "bad ownership or modes for file %s",
  102. buf);
  103. return -1;
  104. }
  105. /* for each component of the canonical path, walking upwards */
  106. for (;;) {
  107. if ((cp = dirname(buf)) == NULL) {
  108. snprintf(err, errlen, "dirname() failed");
  109. return -1;
  110. }
  111. strlcpy(buf, cp, sizeof(buf));
  112. if (stat(buf, &st) < 0 ||
  113. (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) ||
  114. (st.st_mode & 022) != 0) {
  115. snprintf(err, errlen,
  116. "bad ownership or modes for directory %s", buf);
  117. return -1;
  118. }
  119. /* If are past the homedir then we can stop */
  120. if (comparehome && strcmp(homedir, buf) == 0)
  121. break;
  122. /*
  123. * dirname should always complete with a "/" path,
  124. * but we can be paranoid and check for "." too
  125. */
  126. if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
  127. break;
  128. }
  129. return 0;
  130. }
  131. static void
  132. usage(void)
  133. {
  134. fprintf(stderr, "check-perm -m [chroot | keys-command] [path]\n");
  135. exit(1);
  136. }
  137. int
  138. main(int argc, char **argv)
  139. {
  140. const char *path = ".";
  141. char errmsg[256];
  142. int ch, mode = -1;
  143. extern char *optarg;
  144. extern int optind;
  145. struct stat st;
  146. while ((ch = getopt(argc, argv, "hm:")) != -1) {
  147. switch (ch) {
  148. case 'm':
  149. if (strcasecmp(optarg, "chroot") == 0)
  150. mode = 1;
  151. else if (strcasecmp(optarg, "keys-command") == 0)
  152. mode = 2;
  153. else {
  154. fprintf(stderr, "Invalid -m option\n"),
  155. usage();
  156. }
  157. break;
  158. default:
  159. usage();
  160. }
  161. }
  162. argc -= optind;
  163. argv += optind;
  164. if (argc > 1)
  165. usage();
  166. else if (argc == 1)
  167. path = argv[0];
  168. if (mode == 1)
  169. safely_chroot(path, getuid());
  170. else if (mode == 2) {
  171. if (stat(path, &st) < 0)
  172. fatal("Could not stat %s: %s", path, strerror(errno));
  173. if (auth_secure_path(path, &st, NULL, 0,
  174. errmsg, sizeof(errmsg)) != 0)
  175. fatal("Unsafe %s: %s", path, errmsg);
  176. } else {
  177. fprintf(stderr, "Invalid mode\n");
  178. usage();
  179. }
  180. return 0;
  181. }