become_user.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. #include "become_user.h"
  2. #ifdef _linux_
  3. #include <yql/essentials/utils/sys/linux_version.h>
  4. #include <util/generic/yexception.h>
  5. #include <util/system/user.h>
  6. #include <memory>
  7. #include <vector>
  8. #include <errno.h>
  9. #include <grp.h>
  10. #include <pwd.h>
  11. #include <unistd.h>
  12. #include <sys/prctl.h>
  13. #include <contrib/libs/libcap/include/sys/capability.h>
  14. #include <contrib/libs/libcap/include/sys/securebits.h>
  15. // strange, but sometimes we have to specify values manually
  16. #define PR_CAP_AMBIENT 47
  17. #define PR_CAP_AMBIENT_IS_SET 1
  18. #define PR_CAP_AMBIENT_RAISE 2
  19. #define PR_CAP_AMBIENT_LOWER 3
  20. #define PR_CAP_AMBIENT_CLEAR_ALL 4
  21. namespace NYql {
  22. namespace {
  23. void SetCapFlag(cap_t caps, cap_flag_t flag, cap_value_t value) {
  24. if (cap_set_flag(caps, flag, 1, &value, CAP_SET) < 0) {
  25. throw TSystemError() << "cap_set_flag() failed, flag = " << static_cast<int>(flag) << ", value = " << value;
  26. }
  27. }
  28. void SetCapFlags(cap_t caps, cap_value_t value) {
  29. SetCapFlag(caps, CAP_EFFECTIVE, value);
  30. SetCapFlag(caps, CAP_PERMITTED, value);
  31. SetCapFlag(caps, CAP_INHERITABLE, value);
  32. }
  33. void ClearAmbientCapFlags() {
  34. // from man: PR_CAP_AMBIENT (since Linux 4.3)
  35. if (IsLinuxKernelBelow4_3()) {
  36. return;
  37. }
  38. if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) < 0) {
  39. throw TSystemError() << "prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, ....) failed";
  40. }
  41. }
  42. void SetAmbientCapFlag(cap_value_t value) {
  43. if (IsLinuxKernelBelow4_3()) {
  44. return;
  45. }
  46. if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, value, 0, 0) < 0) {
  47. throw TSystemError() << "prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, ....) failed, value = " << value;
  48. }
  49. }
  50. void SetCapFlagsVector(const std::vector<cap_value_t>& flags) {
  51. cap_t caps = cap_init();
  52. std::unique_ptr<std::remove_reference_t<decltype(*caps)>, decltype(&cap_free)> capsHolder(caps, &cap_free);
  53. if (!caps) {
  54. throw TSystemError() << "cap_init() failed";
  55. }
  56. cap_clear(caps);
  57. for (auto f : flags) {
  58. SetCapFlags(caps, f);
  59. }
  60. if (cap_set_proc(caps) < 0) {
  61. throw TSystemError() << "cap_set_proc() failed";
  62. }
  63. ClearAmbientCapFlags();
  64. for (auto f : flags) {
  65. SetAmbientCapFlag(f);
  66. }
  67. }
  68. void EnsureCapFlagsVectorCannotBeRaised(const std::vector<cap_value_t>& flags) {
  69. for (auto f : flags) {
  70. try {
  71. // one-by-one
  72. SetCapFlagsVector({ f });
  73. } catch (const TSystemError&) {
  74. continue;
  75. }
  76. throw yexception() << "Cap flag " << f << " raised unexpectedly";
  77. }
  78. }
  79. void DoBecomeUser(const char* username, const char* groupname) {
  80. errno = 0;
  81. passwd* pw = getpwnam(username);
  82. if (pw == nullptr) {
  83. if (errno == 0) {
  84. ythrow yexception() << "unknown user: " << username;
  85. } else {
  86. ythrow TSystemError() << "can't get user info";
  87. }
  88. }
  89. if (groupname == nullptr || strlen(groupname) == 0) {
  90. groupname = username;
  91. }
  92. errno = 0;
  93. group* gr = getgrnam(groupname);
  94. if (gr == nullptr) {
  95. if (errno == 0) {
  96. ythrow yexception() << "unknown group: " << groupname;
  97. } else {
  98. ythrow TSystemError() << "can't get group info";
  99. }
  100. }
  101. if (setgid(gr->gr_gid) == -1) {
  102. ythrow TSystemError() << "can't change process group";
  103. }
  104. if (initgroups(username, gr->gr_gid) == -1) {
  105. ythrow TSystemError() << "can't initgroups";
  106. }
  107. if (setuid(pw->pw_uid) == -1) {
  108. ythrow TSystemError() << "can't change process user";
  109. }
  110. if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
  111. ythrow TSystemError() << "can't set dumpable flag for a process";
  112. }
  113. }
  114. }
  115. void BecomeUser(const TString& username, const TString& groupname) {
  116. DoBecomeUser(username.data(), groupname.data());
  117. }
  118. void TurnOnBecomeUserAmbientCaps() {
  119. SetCapFlagsVector({ CAP_SETUID, CAP_SETGID, CAP_SETPCAP, CAP_KILL });
  120. if (prctl(PR_SET_SECUREBITS, SECBIT_NO_SETUID_FIXUP | SECBIT_NO_SETUID_FIXUP_LOCKED, 0, 0, 0) == -1) {
  121. ythrow TSystemError() << "can't set secure bits for a process";
  122. }
  123. }
  124. void TurnOffBecomeUserAbility() {
  125. ClearAmbientCapFlags();
  126. SetCapFlagsVector({});
  127. EnsureCapFlagsVectorCannotBeRaised({ CAP_SETUID, CAP_SETGID, CAP_SETPCAP, CAP_KILL });
  128. // ensure we cannot get root access back
  129. if (setuid(0) != -1) {
  130. ythrow TSystemError() << "unexpected switch to root in TurnOffBecomeUserAbility";
  131. }
  132. }
  133. void DumpCaps(const TString& title) {
  134. cap_t caps = cap_get_proc();
  135. std::unique_ptr<std::remove_reference_t<decltype(*caps)>, decltype(&cap_free)> capsHolder(caps, &cap_free);
  136. ssize_t size;
  137. char* capsText = cap_to_text(caps, &size);
  138. Cerr << title << ": current user: " << GetUsername() << ", proc caps: " << capsText << Endl;
  139. cap_free(capsText);
  140. }
  141. void SendSignalOnParentThreadExit(int signo)
  142. {
  143. if (::prctl(PR_SET_PDEATHSIG, signo) == -1) {
  144. ythrow TSystemError() << "Cannot set signal " << strsignal(signo) << " for parent death using prctl";
  145. }
  146. }
  147. }
  148. #endif