xref: /haiku/src/system/libroot/posix/user_group_common.cpp (revision 89d327d66e23408f92fd9bc5cce5d6bdb7d2b5d8)
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 <user_group.h>
7 
8 #include <ctype.h>
9 #include <errno.h>
10 #include <limits.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <new>
16 
17 #include <libroot_lock.h>
18 #include <libroot_private.h>
19 #include <RegistrarDefs.h>
20 
21 #include <util/KMessage.h>
22 
23 
24 using BPrivate::Tokenizer;
25 
26 
27 const char* BPrivate::kPasswdFile = "/etc/passwd";
28 const char* BPrivate::kGroupFile = "/etc/group";
29 const char* BPrivate::kShadowPwdFile = "/etc/shadow";
30 
31 static benaphore sUserGroupLock;
32 static port_id sRegistrarPort = -1;
33 
34 
35 status_t
36 BPrivate::user_group_lock()
37 {
38 	return benaphore_lock(&sUserGroupLock);
39 }
40 
41 
42 status_t
43 BPrivate::user_group_unlock()
44 {
45 	return benaphore_unlock(&sUserGroupLock);
46 }
47 
48 
49 port_id
50 BPrivate::get_registrar_authentication_port()
51 {
52 	if (sRegistrarPort < 0)
53 		sRegistrarPort = find_port(REGISTRAR_AUTHENTICATION_PORT_NAME);
54 
55 	return sRegistrarPort;
56 }
57 
58 
59 status_t
60 BPrivate::send_authentication_request_to_registrar(KMessage& request,
61 	KMessage& reply)
62 {
63 	status_t error = request.SendTo(get_registrar_authentication_port(), 0,
64 		&reply);
65 	if (error != B_OK)
66 		return error;
67 
68 	return (status_t)reply.What();
69 }
70 
71 
72 class Tokenizer {
73 public:
74 	Tokenizer(char* string)
75 		: fString(string)
76 	{
77 	}
78 
79 	char* NextToken(char separator)
80 	{
81 		if (fString == NULL)
82 			return NULL;
83 
84 		char* token = fString;
85 		fString = strchr(fString, separator);
86 		if (fString != NULL) {
87 			*fString = '\0';
88 			fString++;
89 		}
90 
91 		return token;
92 	}
93 
94 	char* NextTrimmedToken(char separator)
95 	{
96 		char* token = NextToken(separator);
97 		if (token == NULL)
98 			return NULL;
99 
100 		// skip spaces at the beginning
101 		while (*token != '\0' && isspace(*token))
102 			token++;
103 
104 		// cut off spaces at the end
105 		char* end = token + strlen(token);
106 		while (end != token && isspace(end[-1]))
107 			end--;
108 		*end = '\0';
109 
110 		return token;
111 	}
112 
113 private:
114 	char*		fString;
115 };
116 
117 
118 static char*
119 buffer_dup_string(const char* string, char*& buffer, size_t& bufferLen)
120 {
121 	if (string == NULL)
122 		return NULL;
123 
124 	size_t size = strlen(string) + 1;
125 	if (size > bufferLen)
126 		return NULL;
127 
128 	strcpy(buffer, string);
129 	char* result = buffer;
130 	buffer += size;
131 	bufferLen -= size;
132 
133 	return result;
134 }
135 
136 
137 static void*
138 buffer_allocate(size_t size, size_t align, char*& buffer, size_t& bufferSize)
139 {
140 	// align padding
141 	addr_t pad = align - (((addr_t)buffer - 1)  & (align - 1)) - 1;
142 	if (pad + size > bufferSize)
143 		return NULL;
144 
145 	char* result = buffer + pad;
146 	buffer = result + size;
147 	bufferSize -= pad + size;
148 
149 	return result;
150 }
151 
152 
153 // #pragma mark - passwd support
154 
155 
156 status_t
157 BPrivate::copy_passwd_to_buffer(const char* name, const char* password,
158 	uid_t uid, gid_t gid, const char* home, const char* shell,
159 	const char* realName, passwd* entry, char* buffer, size_t bufferSize)
160 {
161 	entry->pw_uid = uid;
162 	entry->pw_gid = gid;
163 
164 	entry->pw_name = buffer_dup_string(name, buffer, bufferSize);
165 	entry->pw_passwd = buffer_dup_string(password, buffer, bufferSize);
166 	entry->pw_dir = buffer_dup_string(home, buffer, bufferSize);
167 	entry->pw_shell = buffer_dup_string(shell, buffer, bufferSize);
168 	entry->pw_gecos = buffer_dup_string(realName, buffer, bufferSize);
169 
170 	if (entry->pw_name && entry->pw_passwd && entry->pw_dir
171 			&& entry->pw_shell && entry->pw_gecos) {
172 		return 0;
173 	}
174 
175 	return ERANGE;
176 }
177 
178 
179 status_t
180 BPrivate::copy_passwd_to_buffer(const passwd* from, passwd* entry, char* buffer,
181 	size_t bufferSize)
182 {
183 	return copy_passwd_to_buffer(from->pw_name, from->pw_passwd, from->pw_uid,
184 		from->pw_gid, from->pw_dir, from->pw_shell, from->pw_gecos, entry,
185 		buffer, bufferSize);
186 }
187 
188 
189 status_t
190 BPrivate::parse_passwd_line(char* line, char*& name, char*& password,
191 	uid_t& uid, gid_t& gid, char*& home, char*& shell, char*& realName)
192 {
193 	Tokenizer tokenizer(line);
194 
195 	name = tokenizer.NextTrimmedToken(':');
196 	password = tokenizer.NextTrimmedToken(':');
197 	char* userID = tokenizer.NextTrimmedToken(':');
198 	char* groupID = tokenizer.NextTrimmedToken(':');
199 	realName = tokenizer.NextTrimmedToken(':');
200 	home = tokenizer.NextTrimmedToken(':');
201 	shell = tokenizer.NextTrimmedToken(':');
202 
203 	// skip if invalid
204 	size_t nameLen;
205 	if (shell == NULL || (nameLen = strlen(name)) == 0
206 			|| !isdigit(*userID) || !isdigit(*groupID)
207 		|| nameLen >= MAX_PASSWD_NAME_LEN
208 		|| strlen(password) >= MAX_PASSWD_PASSWORD_LEN
209 		|| strlen(realName) >= MAX_PASSWD_REAL_NAME_LEN
210 		|| strlen(home) >= MAX_PASSWD_HOME_DIR_LEN
211 		|| strlen(shell) >= MAX_PASSWD_SHELL_LEN) {
212 		return B_BAD_VALUE;
213 	}
214 
215 	uid = atoi(userID);
216 	gid = atoi(groupID);
217 
218 	return B_OK;
219 }
220 
221 
222 // #pragma mark - group support
223 
224 
225 status_t
226 BPrivate::copy_group_to_buffer(const char* name, const char* password,
227 	gid_t gid, const char* const* members, int memberCount, group* entry,
228 	char* buffer, size_t bufferSize)
229 {
230 	entry->gr_gid = gid;
231 
232 	// allocate member array (do that first for alignment reasons)
233 	entry->gr_mem = (char**)buffer_allocate(sizeof(char*) * (memberCount + 1),
234 		sizeof(char*), buffer, bufferSize);
235 	if (entry->gr_mem == NULL)
236 		return ERANGE;
237 
238 	// copy name and password
239 	entry->gr_name = buffer_dup_string(name, buffer, bufferSize);
240 	entry->gr_passwd = buffer_dup_string(password, buffer, bufferSize);
241 	if (entry->gr_name == NULL || entry->gr_passwd == NULL)
242 		return ERANGE;
243 
244 	// copy member array
245 	for (int i = 0; i < memberCount; i++) {
246 		entry->gr_mem[i] = buffer_dup_string(members[i], buffer, bufferSize);
247 		if (entry->gr_mem[i] == NULL)
248 			return ERANGE;
249 	}
250 	entry->gr_mem[memberCount] = NULL;
251 
252 	return 0;
253 }
254 
255 
256 status_t
257 BPrivate::copy_group_to_buffer(const group* from, group* entry, char* buffer,
258 	size_t bufferSize)
259 {
260 	int memberCount = 0;
261 	while (from->gr_mem[memberCount] != NULL)
262 		memberCount++;
263 
264 	return copy_group_to_buffer(from->gr_name, from->gr_passwd,
265 		from->gr_gid, from->gr_mem, memberCount, entry, buffer, bufferSize);
266 }
267 
268 
269 status_t
270 BPrivate::parse_group_line(char* line, char*& name, char*& password, gid_t& gid,
271 	char** members, int& memberCount)
272 {
273 	Tokenizer tokenizer(line);
274 
275 	name = tokenizer.NextTrimmedToken(':');
276 	password = tokenizer.NextTrimmedToken(':');
277 	char* groupID = tokenizer.NextTrimmedToken(':');
278 
279 	// skip if invalid
280 	size_t nameLen;
281 	if (groupID == NULL || (nameLen = strlen(name)) == 0 || !isdigit(*groupID)
282 		|| nameLen >= MAX_GROUP_NAME_LEN
283 		|| strlen(password) >= MAX_GROUP_PASSWORD_LEN) {
284 		return B_BAD_VALUE;
285 	}
286 
287 	gid = atol(groupID);
288 
289 	memberCount = 0;
290 
291 	while (char* groupUser = tokenizer.NextTrimmedToken(',')) {
292 		// ignore invalid members
293 		if (*groupUser == '\0' || strlen(groupUser) >= MAX_PASSWD_NAME_LEN)
294 			continue;
295 
296 		members[memberCount++] = groupUser;
297 
298 		// ignore excess members
299 		if (memberCount == MAX_GROUP_MEMBER_COUNT)
300 			break;
301 	}
302 
303 	return B_OK;
304 }
305 
306 
307 // #pragma mark - shadow password support
308 
309 
310 status_t
311 BPrivate::copy_shadow_pwd_to_buffer(const char* name, const char* password,
312 	int lastChanged, int min, int max, int warn, int inactive, int expiration,
313 	int flags, spwd* entry, char* buffer, size_t bufferSize)
314 {
315 	entry->sp_lstchg = lastChanged;
316 	entry->sp_min = min;
317 	entry->sp_max = max;
318 	entry->sp_warn = warn;
319 	entry->sp_inact = inactive;
320 	entry->sp_expire = expiration;
321 	entry->sp_flag = flags;
322 
323 	entry->sp_namp = buffer_dup_string(name, buffer, bufferSize);
324 	entry->sp_pwdp = buffer_dup_string(password, buffer, bufferSize);
325 
326 	if (entry->sp_namp && entry->sp_pwdp)
327 		return 0;
328 
329 	return ERANGE;
330 }
331 
332 
333 status_t
334 BPrivate::copy_shadow_pwd_to_buffer(const spwd* from, spwd* entry,
335 	char* buffer, size_t bufferSize)
336 {
337 	return copy_shadow_pwd_to_buffer(from->sp_namp, from->sp_pwdp,
338 		from->sp_lstchg, from->sp_min, from->sp_max, from->sp_warn,
339 		from->sp_inact, from->sp_expire, from->sp_flag, entry, buffer,
340 		bufferSize);
341 }
342 
343 
344 status_t
345 BPrivate::parse_shadow_pwd_line(char* line, char*& name, char*& password,
346 	int& lastChanged, int& min, int& max, int& warn, int& inactive,
347 	int& expiration, int& flags)
348 {
349 	Tokenizer tokenizer(line);
350 
351 	name = tokenizer.NextTrimmedToken(':');
352 	password = tokenizer.NextTrimmedToken(':');
353 	char* lastChangedString = tokenizer.NextTrimmedToken(':');
354 	char* minString = tokenizer.NextTrimmedToken(':');
355 	char* maxString = tokenizer.NextTrimmedToken(':');
356 	char* warnString = tokenizer.NextTrimmedToken(':');
357 	char* inactiveString = tokenizer.NextTrimmedToken(':');
358 	char* expirationString = tokenizer.NextTrimmedToken(':');
359 	char* flagsString = tokenizer.NextTrimmedToken(':');
360 
361 	// skip if invalid
362 	size_t nameLen;
363 	if (flagsString == NULL || (nameLen = strlen(name)) == 0
364 		|| nameLen >= MAX_SHADOW_PWD_NAME_LEN
365 		|| strlen(password) >= MAX_SHADOW_PWD_PASSWORD_LEN) {
366 		return B_BAD_VALUE;
367 	}
368 
369 	lastChanged = atoi(lastChangedString);
370 	min = minString[0] != '\0' ? atoi(minString) : -1;
371 	max = maxString[0] != '\0' ? atoi(maxString) : -1;
372 	warn = warnString[0] != '\0' ? atoi(warnString) : -1;
373 	inactive = inactiveString[0] != '\0' ? atoi(inactiveString) : -1;
374 	expiration = expirationString[0] != '\0' ? atoi(expirationString) : -1;
375 	flags = atoi(flagsString);
376 
377 	return B_OK;
378 }
379 
380 
381 // #pragma mark -
382 
383 
384 void
385 __init_pwd_backend(void)
386 {
387 	benaphore_init(&sUserGroupLock, "user group");
388 }
389 
390 
391 void
392 __reinit_pwd_backend_after_fork(void)
393 {
394 	benaphore_init(&sUserGroupLock, "user group");
395 }
396