xref: /haiku/src/system/libroot/posix/grp.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include <grp.h>
7 
8 #include <errno.h>
9 #include <string.h>
10 #include <unistd.h>
11 
12 #include <new>
13 
14 #include <OS.h>
15 
16 #include <libroot_private.h>
17 #include <RegistrarDefs.h>
18 #include <user_group.h>
19 
20 #include <util/KMessage.h>
21 
22 
23 using BPrivate::UserGroupLocker;
24 using BPrivate::relocate_pointer;
25 
26 
27 static KMessage sGroupDBReply;
28 static group** sGroupEntries = NULL;
29 static size_t sGroupEntryCount = 0;
30 static size_t sIterationIndex = 0;
31 
32 static struct group sGroupBuffer;
33 static char sGroupStringBuffer[MAX_GROUP_BUFFER_SIZE];
34 
35 
36 static status_t
37 query_group_entry(const char* name, gid_t _gid, struct group *group,
38 	char *buffer, size_t bufferSize, struct group **_result)
39 {
40 	*_result = NULL;
41 
42 	KMessage message(BPrivate::B_REG_GET_GROUP);
43 	if (name)
44 		message.AddString("name", name);
45 	else
46 		message.AddInt32("gid", _gid);
47 
48 	KMessage reply;
49 	status_t error = BPrivate::send_authentication_request_to_registrar(message,
50 		reply);
51 	if (error != B_OK)
52 		return error;
53 
54 	int32 gid;
55 	const char* password;
56 
57 	if ((error = reply.FindInt32("gid", &gid)) != B_OK
58 		|| (error = reply.FindString("name", &name)) != B_OK
59 		|| (error = reply.FindString("password", &password)) != B_OK) {
60 		return error;
61 	}
62 
63 	const char* members[MAX_GROUP_MEMBER_COUNT];
64 	int memberCount = 0;
65 	for (int memberCount = 0; memberCount < MAX_GROUP_MEMBER_COUNT;) {
66 		if (reply.FindString("members", members + memberCount) != B_OK)
67 			break;
68 		memberCount++;
69 	}
70 
71 	error = BPrivate::copy_group_to_buffer(name, password, gid, members,
72 		memberCount, group, buffer, bufferSize);
73 	if (error == B_OK)
74 		*_result = group;
75 
76 	return error;
77 }
78 
79 
80 static status_t
81 init_group_db()
82 {
83 	if (sGroupEntries != NULL)
84 		return B_OK;
85 
86 	// ask the registrar
87 	KMessage message(BPrivate::B_REG_GET_GROUP_DB);
88 	status_t error = BPrivate::send_authentication_request_to_registrar(message,
89 		sGroupDBReply);
90 	if (error != B_OK)
91 		return error;
92 
93 	// unpack the reply
94 	int32 count;
95 	group** entries;
96 	int32 numBytes;
97 	if ((error = sGroupDBReply.FindInt32("count", &count)) != B_OK
98 		|| (error = sGroupDBReply.FindData("entries", B_RAW_TYPE,
99 				(const void**)&entries, &numBytes)) != B_OK) {
100 		return error;
101 	}
102 
103 	// relocate the entries
104 	addr_t baseAddress = (addr_t)entries;
105 	for (int32 i = 0; i < count; i++) {
106 		group* entry = relocate_pointer(baseAddress, entries[i]);
107 		relocate_pointer(baseAddress, entry->gr_name);
108 		relocate_pointer(baseAddress, entry->gr_passwd);
109 		relocate_pointer(baseAddress, entry->gr_mem);
110 		int32 k = 0;
111 		for (; entry->gr_mem[k] != (void*)-1; k++)
112 			relocate_pointer(baseAddress, entry->gr_mem[k]);
113 		entry->gr_mem[k] = NULL;
114 	}
115 
116 	sGroupEntries = entries;
117 	sGroupEntryCount = count;
118 
119 	return B_OK;
120 }
121 
122 
123 // #pragma mark -
124 
125 
126 struct group*
127 getgrent(void)
128 {
129 	struct group* result = NULL;
130 	int status = getgrent_r(&sGroupBuffer, sGroupStringBuffer,
131 		sizeof(sGroupStringBuffer), &result);
132 	if (status != 0)
133 		errno = status;
134 	return result;
135 }
136 
137 
138 int
139 getgrent_r(struct group* group, char* buffer, size_t bufferSize,
140 	struct group** _result)
141 {
142 	UserGroupLocker _;
143 
144 	int status = B_NO_MEMORY;
145 
146 	*_result = NULL;
147 
148 	if ((status = init_group_db()) == B_OK) {
149 		if (sIterationIndex >= sGroupEntryCount)
150 			return ENOENT;
151 
152 		status = BPrivate::copy_group_to_buffer(
153 			sGroupEntries[sIterationIndex], group, buffer, bufferSize);
154 
155 		if (status == B_OK) {
156 			sIterationIndex++;
157 			*_result = group;
158 		}
159 	}
160 
161 	return status;
162 }
163 
164 
165 void
166 setgrent(void)
167 {
168 	UserGroupLocker _;
169 
170 	sIterationIndex = 0;
171 }
172 
173 
174 void
175 endgrent(void)
176 {
177 	UserGroupLocker locker;
178 
179 	sGroupDBReply.Unset();
180 	sGroupEntries = NULL;
181 	sGroupEntryCount = 0;
182 	sIterationIndex = 0;
183 }
184 
185 
186 struct group *
187 getgrnam(const char *name)
188 {
189 	struct group* result = NULL;
190 	int status = getgrnam_r(name, &sGroupBuffer, sGroupStringBuffer,
191 		sizeof(sGroupStringBuffer), &result);
192 	if (status != 0)
193 		errno = status;
194 	return result;
195 }
196 
197 
198 int
199 getgrnam_r(const char *name, struct group *group, char *buffer,
200 	size_t bufferSize, struct group **_result)
201 {
202 	return query_group_entry(name, 0, group, buffer, bufferSize, _result);
203 }
204 
205 
206 struct group *
207 getgrgid(gid_t gid)
208 {
209 	struct group* result = NULL;
210 	int status = getgrgid_r(gid, &sGroupBuffer, sGroupStringBuffer,
211 		sizeof(sGroupStringBuffer), &result);
212 	if (status != 0)
213 		errno = status;
214 	return result;
215 }
216 
217 
218 int
219 getgrgid_r(gid_t gid, struct group *group, char *buffer,
220 	size_t bufferSize, struct group **_result)
221 {
222 	return query_group_entry(NULL, gid, group, buffer, bufferSize, _result);
223 }
224 
225 
226 int
227 getgrouplist(const char* user, gid_t baseGroup, gid_t* groupList,
228 	int* groupCount)
229 {
230 	int maxGroupCount = *groupCount;
231 	*groupCount = 0;
232 
233 	status_t error = B_OK;
234 
235 	// prepare request
236 	KMessage message(BPrivate::B_REG_GET_USER_GROUPS);
237 	if (message.AddString("name", user) != B_OK
238 		|| message.AddInt32("max count", maxGroupCount) != B_OK) {
239 		return -1;
240 	}
241 
242 	// send request
243 	KMessage reply;
244 	error = BPrivate::send_authentication_request_to_registrar(message, reply);
245 	if (error != B_OK)
246 		return -1;
247 
248 	// unpack reply
249 	int32 count;
250 	const int32* groups;
251 	int32 groupsSize;
252 	if (reply.FindInt32("count", &count) != B_OK
253 		|| reply.FindData("groups", B_INT32_TYPE, (const void**)&groups,
254 				&groupsSize) != B_OK) {
255 		return -1;
256 	}
257 
258 	memcpy(groupList, groups, groupsSize);
259 	*groupCount = count;
260 
261 	// add the base group
262 	if (*groupCount < maxGroupCount)
263 		groupList[*groupCount] = baseGroup;
264 	++*groupCount;
265 
266 	return *groupCount <= maxGroupCount ? *groupCount : -1;
267 }
268 
269 
270 int
271 initgroups(const char* user, gid_t baseGroup)
272 {
273 	gid_t groups[NGROUPS_MAX + 1];
274 	int groupCount = NGROUPS_MAX + 1;
275 	if (getgrouplist(user, baseGroup, groups, &groupCount) < 0)
276 		return -1;
277 
278 	return setgroups(groupCount, groups);
279 }
280