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