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