uidswap.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /* $OpenBSD: uidswap.c,v 1.42 2019/06/28 13:35:04 deraadt Exp $ */
  2. /*
  3. * Author: Tatu Ylonen <ylo@cs.hut.fi>
  4. * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  5. * All rights reserved
  6. * Code for uid-swapping.
  7. *
  8. * As far as I am concerned, the code I have written for this software
  9. * can be used freely for any purpose. Any derived versions of this
  10. * software must be clearly marked as such, and if the derived work is
  11. * incompatible with the protocol description in the RFC file, it must be
  12. * called by a name other than "ssh" or "Secure Shell".
  13. */
  14. #include "includes.h"
  15. #include <errno.h>
  16. #include <pwd.h>
  17. #include <string.h>
  18. #include <unistd.h>
  19. #include <limits.h>
  20. #include <stdarg.h>
  21. #include <stdlib.h>
  22. #include <grp.h>
  23. #include "log.h"
  24. #include "uidswap.h"
  25. #include "xmalloc.h"
  26. /*
  27. * Note: all these functions must work in all of the following cases:
  28. * 1. euid=0, ruid=0
  29. * 2. euid=0, ruid!=0
  30. * 3. euid!=0, ruid!=0
  31. * Additionally, they must work regardless of whether the system has
  32. * POSIX saved uids or not.
  33. */
  34. #if defined(_POSIX_SAVED_IDS) && !defined(BROKEN_SAVED_UIDS)
  35. /* Lets assume that posix saved ids also work with seteuid, even though that
  36. is not part of the posix specification. */
  37. #define SAVED_IDS_WORK_WITH_SETEUID
  38. /* Saved effective uid. */
  39. static uid_t saved_euid = 0;
  40. static gid_t saved_egid = 0;
  41. #endif
  42. /* Saved effective uid. */
  43. static int privileged = 0;
  44. static int temporarily_use_uid_effective = 0;
  45. static uid_t user_groups_uid;
  46. static gid_t *saved_egroups = NULL, *user_groups = NULL;
  47. static int saved_egroupslen = -1, user_groupslen = -1;
  48. /*
  49. * Temporarily changes to the given uid. If the effective user
  50. * id is not root, this does nothing. This call cannot be nested.
  51. */
  52. void
  53. temporarily_use_uid(struct passwd *pw)
  54. {
  55. /* Save the current euid, and egroups. */
  56. #ifdef SAVED_IDS_WORK_WITH_SETEUID
  57. saved_euid = geteuid();
  58. saved_egid = getegid();
  59. debug("temporarily_use_uid: %u/%u (e=%u/%u)",
  60. (u_int)pw->pw_uid, (u_int)pw->pw_gid,
  61. (u_int)saved_euid, (u_int)saved_egid);
  62. #ifndef HAVE_CYGWIN
  63. if (saved_euid != 0) {
  64. privileged = 0;
  65. return;
  66. }
  67. #endif
  68. #else
  69. if (geteuid() != 0) {
  70. privileged = 0;
  71. return;
  72. }
  73. #endif /* SAVED_IDS_WORK_WITH_SETEUID */
  74. privileged = 1;
  75. temporarily_use_uid_effective = 1;
  76. saved_egroupslen = getgroups(0, NULL);
  77. if (saved_egroupslen == -1)
  78. fatal("getgroups: %.100s", strerror(errno));
  79. if (saved_egroupslen > 0) {
  80. saved_egroups = xreallocarray(saved_egroups,
  81. saved_egroupslen, sizeof(gid_t));
  82. if (getgroups(saved_egroupslen, saved_egroups) == -1)
  83. fatal("getgroups: %.100s", strerror(errno));
  84. } else { /* saved_egroupslen == 0 */
  85. free(saved_egroups);
  86. saved_egroups = NULL;
  87. }
  88. /* set and save the user's groups */
  89. if (user_groupslen == -1 || user_groups_uid != pw->pw_uid) {
  90. if (initgroups(pw->pw_name, pw->pw_gid) == -1)
  91. fatal("initgroups: %s: %.100s", pw->pw_name,
  92. strerror(errno));
  93. user_groupslen = getgroups(0, NULL);
  94. if (user_groupslen == -1)
  95. fatal("getgroups: %.100s", strerror(errno));
  96. if (user_groupslen > 0) {
  97. user_groups = xreallocarray(user_groups,
  98. user_groupslen, sizeof(gid_t));
  99. if (getgroups(user_groupslen, user_groups) == -1)
  100. fatal("getgroups: %.100s", strerror(errno));
  101. } else { /* user_groupslen == 0 */
  102. free(user_groups);
  103. user_groups = NULL;
  104. }
  105. user_groups_uid = pw->pw_uid;
  106. }
  107. /* Set the effective uid to the given (unprivileged) uid. */
  108. if (setgroups(user_groupslen, user_groups) == -1)
  109. fatal("setgroups: %.100s", strerror(errno));
  110. #ifndef SAVED_IDS_WORK_WITH_SETEUID
  111. /* Propagate the privileged gid to all of our gids. */
  112. if (setgid(getegid()) == -1)
  113. debug("setgid %u: %.100s", (u_int) getegid(), strerror(errno));
  114. /* Propagate the privileged uid to all of our uids. */
  115. if (setuid(geteuid()) == -1)
  116. debug("setuid %u: %.100s", (u_int) geteuid(), strerror(errno));
  117. #endif /* SAVED_IDS_WORK_WITH_SETEUID */
  118. if (setegid(pw->pw_gid) == -1)
  119. fatal("setegid %u: %.100s", (u_int)pw->pw_gid,
  120. strerror(errno));
  121. if (seteuid(pw->pw_uid) == -1)
  122. fatal("seteuid %u: %.100s", (u_int)pw->pw_uid,
  123. strerror(errno));
  124. }
  125. /*
  126. * Restores to the original (privileged) uid.
  127. */
  128. void
  129. restore_uid(void)
  130. {
  131. /* it's a no-op unless privileged */
  132. if (!privileged) {
  133. debug("restore_uid: (unprivileged)");
  134. return;
  135. }
  136. if (!temporarily_use_uid_effective)
  137. fatal("restore_uid: temporarily_use_uid not effective");
  138. #ifdef SAVED_IDS_WORK_WITH_SETEUID
  139. debug("restore_uid: %u/%u", (u_int)saved_euid, (u_int)saved_egid);
  140. /* Set the effective uid back to the saved privileged uid. */
  141. if (seteuid(saved_euid) == -1)
  142. fatal("seteuid %u: %.100s", (u_int)saved_euid, strerror(errno));
  143. if (setegid(saved_egid) == -1)
  144. fatal("setegid %u: %.100s", (u_int)saved_egid, strerror(errno));
  145. #else /* SAVED_IDS_WORK_WITH_SETEUID */
  146. /*
  147. * We are unable to restore the real uid to its unprivileged value.
  148. * Propagate the real uid (usually more privileged) to effective uid
  149. * as well.
  150. */
  151. if (setuid(getuid()) == -1)
  152. fatal("%s: setuid failed: %s", __func__, strerror(errno));
  153. if (setgid(getgid()) == -1)
  154. fatal("%s: setgid failed: %s", __func__, strerror(errno));
  155. #endif /* SAVED_IDS_WORK_WITH_SETEUID */
  156. if (setgroups(saved_egroupslen, saved_egroups) == -1)
  157. fatal("setgroups: %.100s", strerror(errno));
  158. temporarily_use_uid_effective = 0;
  159. }
  160. /*
  161. * Permanently sets all uids to the given uid. This cannot be
  162. * called while temporarily_use_uid is effective.
  163. */
  164. void
  165. permanently_set_uid(struct passwd *pw)
  166. {
  167. #ifndef NO_UID_RESTORATION_TEST
  168. uid_t old_uid = getuid();
  169. gid_t old_gid = getgid();
  170. #endif
  171. if (pw == NULL)
  172. fatal("permanently_set_uid: no user given");
  173. if (temporarily_use_uid_effective)
  174. fatal("permanently_set_uid: temporarily_use_uid effective");
  175. debug("permanently_set_uid: %u/%u", (u_int)pw->pw_uid,
  176. (u_int)pw->pw_gid);
  177. if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
  178. fatal("setresgid %u: %.100s", (u_int)pw->pw_gid, strerror(errno));
  179. #ifdef __APPLE__
  180. /*
  181. * OS X requires initgroups after setgid to opt back into
  182. * memberd support for >16 supplemental groups.
  183. */
  184. if (initgroups(pw->pw_name, pw->pw_gid) == -1)
  185. fatal("initgroups %.100s %u: %.100s",
  186. pw->pw_name, (u_int)pw->pw_gid, strerror(errno));
  187. #endif
  188. if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
  189. fatal("setresuid %u: %.100s", (u_int)pw->pw_uid, strerror(errno));
  190. #ifndef NO_UID_RESTORATION_TEST
  191. /* Try restoration of GID if changed (test clearing of saved gid) */
  192. if (old_gid != pw->pw_gid && pw->pw_uid != 0 &&
  193. (setgid(old_gid) != -1 || setegid(old_gid) != -1))
  194. fatal("%s: was able to restore old [e]gid", __func__);
  195. #endif
  196. /* Verify GID drop was successful */
  197. if (getgid() != pw->pw_gid || getegid() != pw->pw_gid) {
  198. fatal("%s: egid incorrect gid:%u egid:%u (should be %u)",
  199. __func__, (u_int)getgid(), (u_int)getegid(),
  200. (u_int)pw->pw_gid);
  201. }
  202. #ifndef NO_UID_RESTORATION_TEST
  203. /* Try restoration of UID if changed (test clearing of saved uid) */
  204. if (old_uid != pw->pw_uid &&
  205. (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
  206. fatal("%s: was able to restore old [e]uid", __func__);
  207. #endif
  208. /* Verify UID drop was successful */
  209. if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) {
  210. fatal("%s: euid incorrect uid:%u euid:%u (should be %u)",
  211. __func__, (u_int)getuid(), (u_int)geteuid(),
  212. (u_int)pw->pw_uid);
  213. }
  214. }