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