vfs: check userland buffers before reading them.
[haiku.git] / src / system / libroot / posix / user_group_common.cpp
blobeb5911b1e16442c07f09b974b98c3901a29dbe00
1 /*
2 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <user_group.h>
9 #include <ctype.h>
10 #include <errno.h>
11 #include <limits.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
16 #include <new>
18 #include <errno_private.h>
19 #include <launch.h>
20 #include <libroot_private.h>
21 #include <locks.h>
22 #include <RegistrarDefs.h>
24 #include <util/KMessage.h>
27 using BPrivate::Tokenizer;
30 static const char* const kUserGroupLockName = "user group";
32 const char* BPrivate::kPasswdFile = "/etc/passwd";
33 const char* BPrivate::kGroupFile = "/etc/group";
34 const char* BPrivate::kShadowPwdFile = "/etc/shadow";
36 static mutex sUserGroupLock = MUTEX_INITIALIZER(kUserGroupLockName);
37 static port_id sRegistrarPort = -1;
40 status_t
41 BPrivate::user_group_lock()
43 return mutex_lock(&sUserGroupLock);
47 status_t
48 BPrivate::user_group_unlock()
50 mutex_unlock(&sUserGroupLock);
51 return B_OK;
55 port_id
56 BPrivate::get_registrar_authentication_port()
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);
66 return sRegistrarPort;
70 void
71 BPrivate::set_registrar_authentication_port(port_id port)
73 sRegistrarPort = port;
77 status_t
78 BPrivate::send_authentication_request_to_registrar(KMessage& request,
79 KMessage& reply)
81 status_t error = request.SendTo(get_registrar_authentication_port(), 0,
82 &reply);
83 if (error != B_OK)
84 return error;
86 return (status_t)reply.What();
90 class BPrivate::Tokenizer {
91 public:
92 Tokenizer(char* string)
93 : fString(string)
97 char* NextToken(char separator)
99 if (fString == NULL)
100 return NULL;
102 char* token = fString;
103 fString = strchr(fString, separator);
104 if (fString != NULL) {
105 *fString = '\0';
106 fString++;
109 return token;
112 char* NextTrimmedToken(char separator)
114 char* token = NextToken(separator);
115 if (token == NULL)
116 return NULL;
118 // skip spaces at the beginning
119 while (*token != '\0' && isspace(*token))
120 token++;
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';
128 return token;
131 private:
132 char* fString;
136 static char*
137 buffer_dup_string(const char* string, char*& buffer, size_t& bufferLen)
139 if (string == NULL)
140 return NULL;
142 size_t size = strlen(string) + 1;
143 if (size > bufferLen)
144 return NULL;
146 strcpy(buffer, string);
147 char* result = buffer;
148 buffer += size;
149 bufferLen -= size;
151 return result;
155 static void*
156 buffer_allocate(size_t size, size_t align, char*& buffer, size_t& bufferSize)
158 // align padding
159 addr_t pad = align - (((addr_t)buffer - 1) & (align - 1)) - 1;
160 if (pad + size > bufferSize)
161 return NULL;
163 char* result = buffer + pad;
164 buffer = result + size;
165 bufferSize -= pad + size;
167 return result;
171 // #pragma mark - passwd support
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)
179 entry->pw_uid = uid;
180 entry->pw_gid = gid;
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);
188 if (entry->pw_name && entry->pw_passwd && entry->pw_dir
189 && entry->pw_shell && entry->pw_gecos) {
190 return 0;
193 return ERANGE;
197 status_t
198 BPrivate::copy_passwd_to_buffer(const passwd* from, passwd* entry, char* buffer,
199 size_t bufferSize)
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);
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)
211 Tokenizer tokenizer(line);
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(':');
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;
233 uid = atoi(userID);
234 gid = atoi(groupID);
236 return B_OK;
240 // #pragma mark - group support
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)
248 entry->gr_gid = gid;
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;
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;
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;
268 entry->gr_mem[memberCount] = NULL;
270 return 0;
274 status_t
275 BPrivate::copy_group_to_buffer(const group* from, group* entry, char* buffer,
276 size_t bufferSize)
278 int memberCount = 0;
279 while (from->gr_mem[memberCount] != NULL)
280 memberCount++;
282 return copy_group_to_buffer(from->gr_name, from->gr_passwd,
283 from->gr_gid, from->gr_mem, memberCount, entry, buffer, bufferSize);
287 status_t
288 BPrivate::parse_group_line(char* line, char*& name, char*& password, gid_t& gid,
289 char** members, int& memberCount)
291 Tokenizer tokenizer(line);
293 name = tokenizer.NextTrimmedToken(':');
294 password = tokenizer.NextTrimmedToken(':');
295 char* groupID = tokenizer.NextTrimmedToken(':');
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;
305 gid = atol(groupID);
307 memberCount = 0;
309 while (char* groupUser = tokenizer.NextTrimmedToken(',')) {
310 // ignore invalid members
311 if (*groupUser == '\0' || strlen(groupUser) >= MAX_PASSWD_NAME_LEN)
312 continue;
314 members[memberCount++] = groupUser;
316 // ignore excess members
317 if (memberCount == MAX_GROUP_MEMBER_COUNT)
318 break;
321 return B_OK;
325 // #pragma mark - shadow password support
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)
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;
341 entry->sp_namp = buffer_dup_string(name, buffer, bufferSize);
342 entry->sp_pwdp = buffer_dup_string(password, buffer, bufferSize);
344 if (entry->sp_namp && entry->sp_pwdp)
345 return 0;
347 return ERANGE;
351 status_t
352 BPrivate::copy_shadow_pwd_to_buffer(const spwd* from, spwd* entry,
353 char* buffer, size_t bufferSize)
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);
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)
367 Tokenizer tokenizer(line);
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(':');
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;
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);
395 return B_OK;
399 // #pragma mark -
402 void
403 __init_pwd_backend(void)
408 void
409 __reinit_pwd_backend_after_fork(void)
411 mutex_init(&sUserGroupLock, kUserGroupLockName);