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