grp.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Maxime Friess <M4x1me@pm.me>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/ScopeGuard.h>
  8. #include <AK/String.h>
  9. #include <AK/Vector.h>
  10. #include <errno.h>
  11. #include <errno_codes.h>
  12. #include <grp.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <unistd.h>
  17. extern "C" {
  18. static FILE* s_stream = nullptr;
  19. static unsigned s_line_number = 0;
  20. static struct group s_group;
  21. static String s_name;
  22. static String s_passwd;
  23. static Vector<String> s_members;
  24. static Vector<char const*> s_members_ptrs;
  25. void setgrent()
  26. {
  27. s_line_number = 0;
  28. if (s_stream) {
  29. rewind(s_stream);
  30. } else {
  31. s_stream = fopen("/etc/group", "r");
  32. if (!s_stream) {
  33. perror("open /etc/group");
  34. }
  35. }
  36. }
  37. void endgrent()
  38. {
  39. s_line_number = 0;
  40. if (s_stream) {
  41. fclose(s_stream);
  42. s_stream = nullptr;
  43. }
  44. }
  45. struct group* getgrgid(gid_t gid)
  46. {
  47. setgrent();
  48. ScopeGuard guard = [] { endgrent(); };
  49. while (auto* gr = getgrent()) {
  50. if (gr->gr_gid == gid)
  51. return gr;
  52. }
  53. return nullptr;
  54. }
  55. struct group* getgrnam(char const* name)
  56. {
  57. setgrent();
  58. ScopeGuard guard = [] { endgrent(); };
  59. while (auto* gr = getgrent()) {
  60. if (!strcmp(gr->gr_name, name))
  61. return gr;
  62. }
  63. return nullptr;
  64. }
  65. static bool parse_grpdb_entry(String const& line)
  66. {
  67. auto parts = line.split_view(':', SplitBehavior::KeepEmpty);
  68. if (parts.size() != 4) {
  69. warnln("getgrent(): Malformed entry on line {}: '{}' has {} parts", s_line_number, line, parts.size());
  70. return false;
  71. }
  72. s_name = parts[0];
  73. s_passwd = parts[1];
  74. auto& gid_string = parts[2];
  75. String members_string = parts[3];
  76. auto gid = gid_string.to_uint();
  77. if (!gid.has_value()) {
  78. warnln("getgrent(): Malformed GID on line {}", s_line_number);
  79. return false;
  80. }
  81. s_members = members_string.split(',');
  82. s_members_ptrs.clear_with_capacity();
  83. s_members_ptrs.ensure_capacity(s_members.size() + 1);
  84. for (auto& member : s_members) {
  85. s_members_ptrs.append(member.characters());
  86. }
  87. s_members_ptrs.append(nullptr);
  88. s_group.gr_gid = gid.value();
  89. s_group.gr_name = const_cast<char*>(s_name.characters());
  90. s_group.gr_passwd = const_cast<char*>(s_passwd.characters());
  91. s_group.gr_mem = const_cast<char**>(s_members_ptrs.data());
  92. return true;
  93. }
  94. struct group* getgrent()
  95. {
  96. if (!s_stream)
  97. setgrent();
  98. while (true) {
  99. if (!s_stream || feof(s_stream))
  100. return nullptr;
  101. if (ferror(s_stream)) {
  102. warnln("getgrent(): Read error: {}", strerror(ferror(s_stream)));
  103. return nullptr;
  104. }
  105. char buffer[1024];
  106. ++s_line_number;
  107. char* s = fgets(buffer, sizeof(buffer), s_stream);
  108. // Silently tolerate an empty line at the end.
  109. if ((!s || !s[0]) && feof(s_stream))
  110. return nullptr;
  111. String line(s, Chomp);
  112. if (parse_grpdb_entry(line))
  113. return &s_group;
  114. // Otherwise, proceed to the next line.
  115. }
  116. }
  117. int initgroups(char const* user, gid_t extra_gid)
  118. {
  119. size_t count = 0;
  120. gid_t gids[32];
  121. bool extra_gid_added = false;
  122. setgrent();
  123. while (auto* gr = getgrent()) {
  124. for (auto* mem = gr->gr_mem; *mem; ++mem) {
  125. if (!strcmp(*mem, user)) {
  126. gids[count++] = gr->gr_gid;
  127. if (gr->gr_gid == extra_gid)
  128. extra_gid_added = true;
  129. break;
  130. }
  131. }
  132. }
  133. endgrent();
  134. if (!extra_gid_added)
  135. gids[count++] = extra_gid;
  136. return setgroups(count, gids);
  137. }
  138. int putgrent(const struct group* group, FILE* stream)
  139. {
  140. if (!group || !stream || !group->gr_name || !group->gr_passwd) {
  141. errno = EINVAL;
  142. return -1;
  143. }
  144. auto is_valid_field = [](char const* str) {
  145. return str && !strpbrk(str, ":\n");
  146. };
  147. if (!is_valid_field(group->gr_name) || !is_valid_field(group->gr_passwd)) {
  148. errno = EINVAL;
  149. return -1;
  150. }
  151. int nwritten = fprintf(stream, "%s:%s:%u:", group->gr_name, group->gr_passwd, group->gr_gid);
  152. if (!nwritten || nwritten < 0) {
  153. errno = ferror(stream);
  154. return -1;
  155. }
  156. if (group->gr_mem) {
  157. for (size_t i = 0; group->gr_mem[i] != nullptr; i++) {
  158. nwritten = fprintf(stream, i == 0 ? "%s" : ",%s", group->gr_mem[i]);
  159. if (!nwritten || nwritten < 0) {
  160. errno = ferror(stream);
  161. return -1;
  162. }
  163. }
  164. }
  165. nwritten = fprintf(stream, "\n");
  166. if (!nwritten || nwritten < 0) {
  167. errno = ferror(stream);
  168. return -1;
  169. }
  170. return 0;
  171. }
  172. }