xref: /haiku/src/servers/registrar/AuthenticationManager.cpp (revision c90684742e7361651849be4116d0e5de3a817194)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "AuthenticationManager.h"
7 
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <sys/param.h>
12 
13 #include <map>
14 #include <new>
15 #include <string>
16 
17 #include <DataIO.h>
18 
19 #include <AutoDeleter.h>
20 #include <RegistrarDefs.h>
21 
22 #include <libroot_private.h>
23 #include <user_group.h>
24 #include <util/KMessage.h>
25 
26 
27 using std::map;
28 using std::string;
29 
30 using namespace BPrivate;
31 
32 
33 class AuthenticationManager::FlatStore {
34 public:
35 	FlatStore()
36 		: fSize(0)
37 	{
38 		fBuffer.SetBlockSize(1024);
39 	}
40 
41 	void WriteData(size_t offset, const void* data, size_t length)
42 	{
43 		ssize_t result = fBuffer.WriteAt(offset, data, length);
44 		if (result < 0)
45 			throw status_t(result);
46 	}
47 
48 	template<typename Type>
49 	void WriteData(size_t offset, const Type& data)
50 	{
51 		WriteData(&data, sizeof(Type));
52 	}
53 
54 	size_t ReserveSpace(size_t length, bool align)
55 	{
56 		if (align)
57 			fSize = _ALIGN(fSize);
58 
59 		size_t pos = fSize;
60 		fSize += length;
61 
62 		return pos;
63 	}
64 
65 	void* AppendData(const void* data, size_t length, bool align)
66 	{
67 		size_t pos = ReserveSpace(length, align);
68 		WriteData(pos, data, length);
69 		return (void*)(addr_t)pos;
70 	}
71 
72 	template<typename Type>
73 	Type* AppendData(const Type& data)
74 	{
75 		return (Type*)AppendData(&data, sizeof(Type), true);
76 	}
77 
78 	char* AppendString(const char* string)
79 	{
80 		return (char*)AppendData(string, strlen(string) + 1, false);
81 	}
82 
83 	char* AppendString(const string& str)
84 	{
85 		return (char*)AppendData(str.c_str(), str.length() + 1, false);
86 	}
87 
88 	const void* Buffer() const
89 	{
90 		return fBuffer.Buffer();
91 	}
92 
93 	size_t BufferLength() const
94 	{
95 		return fSize;
96 	}
97 
98 private:
99 	BMallocIO	fBuffer;
100 	size_t		fSize;
101 };
102 
103 
104 class AuthenticationManager::User {
105 public:
106 	User()
107 		:
108 		fUID(0),
109 		fGID(0),
110 		fLastChanged(0),
111 		fMin(-1),
112 		fMax(-1),
113 		fWarn(-1),
114 		fInactive(-1),
115 		fExpiration(-1),
116 		fFlags(0)
117 	{
118 	}
119 
120 	User(const char* name, const char* password, uid_t uid, gid_t gid,
121 		const char* home, const char* shell, const char* realName)
122 		:
123 		fUID(uid),
124 		fGID(gid),
125 		fName(name),
126 		fPassword(password),
127 		fHome(home),
128 		fShell(shell),
129 		fRealName(realName),
130 		fLastChanged(0),
131 		fMin(-1),
132 		fMax(-1),
133 		fWarn(-1),
134 		fInactive(-1),
135 		fExpiration(-1),
136 		fFlags(0)
137 	{
138 	}
139 
140 	User(const User& other)
141 		:
142 		fUID(other.fUID),
143 		fGID(other.fGID),
144 		fName(other.fName),
145 		fPassword(other.fPassword),
146 		fHome(other.fHome),
147 		fShell(other.fShell),
148 		fRealName(other.fRealName),
149 		fShadowPassword(other.fShadowPassword),
150 		fLastChanged(other.fLastChanged),
151 		fMin(other.fMin),
152 		fMax(other.fMax),
153 		fWarn(other.fWarn),
154 		fInactive(other.fInactive),
155 		fExpiration(other.fExpiration),
156 		fFlags(other.fFlags)
157 	{
158 	}
159 
160 	const string& Name() const	{ return fName; }
161 	const uid_t UID() const		{ return fUID; }
162 
163 	void SetShadowInfo(const char* password, int lastChanged, int min, int max,
164 		int warn, int inactive, int expiration, int flags)
165 	{
166 		fShadowPassword = password;
167 		fLastChanged = lastChanged;
168 		fMin = min;
169 		fMax = max;
170 		fWarn = warn;
171 		fInactive = inactive;
172 		fExpiration = expiration;
173 		fFlags = flags;
174 	}
175 
176 	void UpdateFromMessage(const KMessage& message)
177 	{
178 		int32 intValue;
179 		const char* stringValue;
180 
181 		if (message.FindInt32("uid", &intValue) == B_OK)
182 			fUID = intValue;
183 
184 		if (message.FindInt32("gid", &intValue) == B_OK)
185 			fGID = intValue;
186 
187 		if (message.FindString("name", &stringValue) == B_OK)
188 			fName = stringValue;
189 
190 		if (message.FindString("password", &stringValue) == B_OK)
191 			fPassword = stringValue;
192 
193 		if (message.FindString("home", &stringValue) == B_OK)
194 			fHome = stringValue;
195 
196 		if (message.FindString("shell", &stringValue) == B_OK)
197 			fShell = stringValue;
198 
199 		if (message.FindString("real name", &stringValue) == B_OK)
200 			fRealName = stringValue;
201 
202 		if (message.FindString("shadow password", &stringValue) == B_OK) {
203 			fShadowPassword = stringValue;
204 			// TODO:
205 			// fLastChanged = now;
206 		}
207 
208 		if (message.FindInt32("last changed", &intValue) == B_OK)
209 			fLastChanged = intValue;
210 
211 		if (message.FindInt32("min", &intValue) == B_OK)
212 			fMin = intValue;
213 
214 		if (message.FindInt32("max", &intValue) == B_OK)
215 			fMax = intValue;
216 
217 		if (message.FindInt32("warn", &intValue) == B_OK)
218 			fWarn = intValue;
219 
220 		if (message.FindInt32("inactive", &intValue) == B_OK)
221 			fInactive = intValue;
222 
223 		if (message.FindInt32("expiration", &intValue) == B_OK)
224 			fExpiration = intValue;
225 
226 		if (message.FindInt32("flags", &intValue) == B_OK)
227 			fFlags = intValue;
228 	}
229 
230 	passwd* WriteFlatPasswd(FlatStore& store) const
231 	{
232 		struct passwd passwd;
233 
234 		passwd.pw_uid = fUID;
235 		passwd.pw_gid = fGID;
236 		passwd.pw_name = store.AppendString(fName);
237 		passwd.pw_passwd = store.AppendString(fPassword);
238 		passwd.pw_dir = store.AppendString(fHome);
239 		passwd.pw_shell = store.AppendString(fShell);
240 		passwd.pw_gecos = store.AppendString(fRealName);
241 
242 		return store.AppendData(passwd);
243 	}
244 
245 	spwd* WriteFlatShadowPwd(FlatStore& store) const
246 	{
247 		struct spwd spwd;
248 
249 		spwd.sp_namp = store.AppendString(fName);
250 		spwd.sp_pwdp = store.AppendString(fShadowPassword);
251 		spwd.sp_lstchg = fLastChanged;
252 		spwd.sp_min = fMin;
253 		spwd.sp_max = fMax;
254 		spwd.sp_warn = fWarn;
255 		spwd.sp_inact = fInactive;
256 		spwd.sp_expire = fExpiration;
257 		spwd.sp_flag = fFlags;
258 
259 		return store.AppendData(spwd);
260 	}
261 
262 	status_t WriteToMessage(KMessage& message, bool addShadowPwd)
263 	{
264 		status_t error;
265 		if ((error = message.AddInt32("uid", fUID)) != B_OK
266 			|| (error = message.AddInt32("gid", fGID)) != B_OK
267 			|| (error = message.AddString("name", fName.c_str())) != B_OK
268 			|| (error = message.AddString("password", fPassword.c_str()))
269 					!= B_OK
270 			|| (error = message.AddString("home", fHome.c_str())) != B_OK
271 			|| (error = message.AddString("shell", fShell.c_str())) != B_OK
272 			|| (error = message.AddString("real name", fRealName.c_str()))
273 					!= B_OK) {
274 			return error;
275 		}
276 
277 		if (!addShadowPwd)
278 			return B_OK;
279 
280 		if ((error = message.AddString("shadow password",
281 					fShadowPassword.c_str())) != B_OK
282 			|| (error = message.AddInt32("last changed", fLastChanged)) != B_OK
283 			|| (error = message.AddInt32("min", fMin)) != B_OK
284 			|| (error = message.AddInt32("max", fMax)) != B_OK
285 			|| (error = message.AddInt32("warn", fWarn)) != B_OK
286 			|| (error = message.AddInt32("inactive", fInactive)) != B_OK
287 			|| (error = message.AddInt32("expiration", fExpiration)) != B_OK
288 			|| (error = message.AddInt32("flags", fFlags)) != B_OK) {
289 			return error;
290 		}
291 
292 		return B_OK;
293 	}
294 
295 	void WritePasswdLine(FILE* file)
296 	{
297 		fprintf(file, "%s:%s:%d:%d:%s:%s:%s\n",
298 			fName.c_str(), fPassword.c_str(), (int)fUID, (int)fGID,
299 			fRealName.c_str(), fHome.c_str(), fShell.c_str());
300 	}
301 
302 	void WriteShadowPwdLine(FILE* file)
303 	{
304 		fprintf(file, "%s:%s:%d:", fName.c_str(), fShadowPassword.c_str(),
305 			fLastChanged);
306 
307 		// The following values are supposed to be printed as empty strings,
308 		// if negative.
309 		int values[5] = { fMin, fMax, fWarn, fInactive, fExpiration };
310 		for (int i = 0; i < 5; i++) {
311 			if (values[i] >= 0)
312 				fprintf(file, "%d", values[i]);
313 			fprintf(file, ":");
314 		}
315 
316 		fprintf(file, "%d\n", fFlags);
317 	}
318 
319 private:
320 	uid_t	fUID;
321 	gid_t	fGID;
322 	string	fName;
323 	string	fPassword;
324 	string	fHome;
325 	string	fShell;
326 	string	fRealName;
327 	string	fShadowPassword;
328 	int		fLastChanged;
329 	int		fMin;
330 	int		fMax;
331 	int		fWarn;
332 	int		fInactive;
333 	int		fExpiration;
334 	int		fFlags;
335 };
336 
337 
338 class AuthenticationManager::Group {
339 public:
340 	Group(const char* name, const char* password, gid_t gid,
341 		const char* const* members, int memberCount)
342 		:
343 		fGID(gid),
344 		fName(name),
345 		fPassword(password),
346 		fMembers(new string[memberCount]),
347 		fMemberCount(memberCount)
348 	{
349 		for (int i = 0; i < memberCount; i++)
350 			fMembers[i] = members[i];
351 	}
352 
353 	~Group()
354 	{
355 		delete[] fMembers;
356 	}
357 
358 	const string& Name() const	{ return fName; }
359 	const gid_t GID() const		{ return fGID; }
360 
361 	bool HasMember(const char* name)
362 	{
363 		for (int i = 0; i < fMemberCount; i++) {
364 			if (fMembers[i] == name)
365 				return true;
366 		}
367 
368 		return false;
369 	}
370 
371 	group* WriteFlatGroup(FlatStore& store) const
372 	{
373 		struct group group;
374 
375 		char* members[MAX_GROUP_MEMBER_COUNT + 1];
376 		for (int i = 0; i < fMemberCount; i++)
377 			members[i] = store.AppendString(fMembers[i].c_str());
378 		members[fMemberCount] = (char*)-1;
379 
380 		group.gr_gid = fGID;
381 		group.gr_name = store.AppendString(fName);
382 		group.gr_passwd = store.AppendString(fPassword);
383 		group.gr_mem = (char**)store.AppendData(members,
384 			sizeof(char*) * (fMemberCount + 1), true);
385 
386 		return store.AppendData(group);
387 	}
388 
389 	status_t WriteToMessage(KMessage& message)
390 	{
391 		status_t error;
392 		if ((error = message.AddInt32("gid", fGID)) != B_OK
393 			|| (error = message.AddString("name", fName.c_str())) != B_OK
394 			|| (error = message.AddString("password", fPassword.c_str()))
395 					!= B_OK) {
396 			return error;
397 		}
398 
399 		for (int i = 0; i < fMemberCount; i++) {
400 			if ((error = message.AddString("members", fMembers[i].c_str()))
401 					!= B_OK) {
402 				return error;
403 			}
404 		}
405 
406 		return B_OK;
407 	}
408 
409 private:
410 	gid_t	fGID;
411 	string	fName;
412 	string	fPassword;
413 	string*	fMembers;
414 	int		fMemberCount;
415 };
416 
417 
418 class AuthenticationManager::UserDB {
419 public:
420 	status_t AddUser(User* user)
421 	{
422 		try {
423 			fUsersByID[user->UID()] = user;
424 		} catch (...) {
425 			return B_NO_MEMORY;
426 		}
427 
428 		try {
429 			fUsersByName[user->Name()] = user;
430 		} catch (...) {
431 			fUsersByID.erase(fUsersByID.find(user->UID()));
432 			return B_NO_MEMORY;
433 		}
434 
435 		return B_OK;
436 	}
437 
438 	User* UserByID(uid_t uid) const
439 	{
440 		map<uid_t, User*>::const_iterator it = fUsersByID.find(uid);
441 		return (it == fUsersByID.end() ? NULL : it->second);
442 	}
443 
444 	User* UserByName(const char* name) const
445 	{
446 		map<string, User*>::const_iterator it = fUsersByName.find(name);
447 		return (it == fUsersByName.end() ? NULL : it->second);
448 	}
449 
450 	int32 WriteFlatPasswdDB(FlatStore& store) const
451 	{
452 		int32 count = fUsersByID.size();
453 
454 		size_t entriesSpace = sizeof(passwd*) * count;
455 		size_t offset = store.ReserveSpace(entriesSpace, true);
456 		passwd** entries = new passwd*[count];
457 		ArrayDeleter<passwd*> _(entries);
458 
459 		int32 index = 0;
460 		for (map<uid_t, User*>::const_iterator it = fUsersByID.begin();
461 			 it != fUsersByID.end(); ++it) {
462 			entries[index++] = it->second->WriteFlatPasswd(store);
463 		}
464 
465 		store.WriteData(offset, entries, entriesSpace);
466 
467 		return count;
468 	}
469 
470 	int32 WriteFlatShadowDB(FlatStore& store) const
471 	{
472 		int32 count = fUsersByID.size();
473 
474 		size_t entriesSpace = sizeof(spwd*) * count;
475 		size_t offset = store.ReserveSpace(entriesSpace, true);
476 		spwd** entries = new spwd*[count];
477 		ArrayDeleter<spwd*> _(entries);
478 
479 		int32 index = 0;
480 		for (map<uid_t, User*>::const_iterator it = fUsersByID.begin();
481 			 it != fUsersByID.end(); ++it) {
482 			entries[index++] = it->second->WriteFlatShadowPwd(store);
483 		}
484 
485 		store.WriteData(offset, entries, entriesSpace);
486 
487 		return count;
488 	}
489 
490 	void WriteToDisk()
491 	{
492 		// rename the old files
493 		string passwdBackup(kPasswdFile);
494 		string shadowBackup(kShadowPwdFile);
495 		passwdBackup += ".old";
496 		shadowBackup += ".old";
497 
498 		rename(kPasswdFile, passwdBackup.c_str());
499 		rename(kShadowPwdFile, shadowBackup.c_str());
500 			// Don't check errors. We can't do anything anyway.
501 
502 		// open files
503 		FILE* passwdFile = fopen(kPasswdFile, "w");
504 		if (passwdFile == NULL) {
505 			debug_printf("REG: Failed to open passwd file \"%s\" for "
506 				"writing: %s\n", kPasswdFile, strerror(errno));
507 		}
508 		CObjectDeleter<FILE, int> _1(passwdFile, fclose);
509 
510 		FILE* shadowFile = fopen(kShadowPwdFile, "w");
511 		if (shadowFile == NULL) {
512 			debug_printf("REG: Failed to open shadow passwd file \"%s\" for "
513 				"writing: %s\n", kShadowPwdFile, strerror(errno));
514 		}
515 		CObjectDeleter<FILE, int> _2(shadowFile, fclose);
516 
517 		// write users
518 		for (map<uid_t, User*>::const_iterator it = fUsersByID.begin();
519 			 it != fUsersByID.end(); ++it) {
520 			User* user = it->second;
521 			user->WritePasswdLine(passwdFile);
522 			user->WriteShadowPwdLine(shadowFile);
523 		}
524 	}
525 
526 private:
527 	map<uid_t, User*>	fUsersByID;
528 	map<string, User*>	fUsersByName;
529 };
530 
531 
532 class AuthenticationManager::GroupDB {
533 public:
534 	status_t AddGroup(Group* group)
535 	{
536 		try {
537 			fGroupsByID[group->GID()] = group;
538 		} catch (...) {
539 			return B_NO_MEMORY;
540 		}
541 
542 		try {
543 			fGroupsByName[group->Name()] = group;
544 		} catch (...) {
545 			fGroupsByID.erase(fGroupsByID.find(group->GID()));
546 			return B_NO_MEMORY;
547 		}
548 
549 		return B_OK;
550 	}
551 
552 	Group* GroupByID(gid_t gid) const
553 	{
554 		map<gid_t, Group*>::const_iterator it = fGroupsByID.find(gid);
555 		return (it == fGroupsByID.end() ? NULL : it->second);
556 	}
557 
558 	Group* GroupByName(const char* name) const
559 	{
560 		map<string, Group*>::const_iterator it = fGroupsByName.find(name);
561 		return (it == fGroupsByName.end() ? NULL : it->second);
562 	}
563 
564 	int32 GetUserGroups(const char* name, gid_t* groups, int maxCount)
565 	{
566 		int count = 0;
567 
568 		for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin();
569 			 it != fGroupsByID.end(); ++it) {
570 			Group* group = it->second;
571 			if (group->HasMember(name)) {
572 				if (count < maxCount)
573 					groups[count] = group->GID();
574 				count++;
575 			}
576 		}
577 
578 		return count;
579 	}
580 
581 
582 	int32 WriteFlatGroupDB(FlatStore& store) const
583 	{
584 		int32 count = fGroupsByID.size();
585 
586 		size_t entriesSpace = sizeof(group*) * count;
587 		size_t offset = store.ReserveSpace(entriesSpace, true);
588 		group** entries = new group*[count];
589 		ArrayDeleter<group*> _(entries);
590 
591 		int32 index = 0;
592 		for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin();
593 			 it != fGroupsByID.end(); ++it) {
594 			entries[index++] = it->second->WriteFlatGroup(store);
595 		}
596 
597 		store.WriteData(offset, entries, entriesSpace);
598 
599 		return count;
600 	}
601 
602 private:
603 	map<uid_t, Group*>	fGroupsByID;
604 	map<string, Group*>	fGroupsByName;
605 };
606 
607 
608 AuthenticationManager::AuthenticationManager()
609 	:
610 	fRequestPort(-1),
611 	fRequestThread(-1),
612 	fUserDB(NULL),
613 	fGroupDB(NULL),
614 	fPasswdDBReply(NULL),
615 	fGroupDBReply(NULL),
616 	fShadowPwdDBReply(NULL)
617 {
618 }
619 
620 
621 AuthenticationManager::~AuthenticationManager()
622 {
623 	// delete port and wait for the request thread to finish
624 	if (fRequestPort >= 0)
625 		delete_port(fRequestPort);
626 
627 	status_t dummy;
628 	wait_for_thread(fRequestThread, &dummy);
629 
630 	delete fUserDB;
631 	delete fGroupDB;
632 	delete fPasswdDBReply;
633 	delete fGroupDBReply;
634 	delete fShadowPwdDBReply;
635 }
636 
637 
638 status_t
639 AuthenticationManager::Init()
640 {
641 	fUserDB = new(std::nothrow) UserDB;
642 	fGroupDB = new(std::nothrow) GroupDB;
643 	fPasswdDBReply = new(std::nothrow) KMessage(1);
644 	fGroupDBReply = new(std::nothrow) KMessage(1);
645 	fShadowPwdDBReply = new(std::nothrow) KMessage(1);
646 
647 	if (fUserDB == NULL || fGroupDB == NULL || fPasswdDBReply == NULL
648 			|| fGroupDBReply == NULL || fShadowPwdDBReply == NULL) {
649 		return B_NO_MEMORY;
650 	}
651 
652 	fRequestPort = create_port(100, REGISTRAR_AUTHENTICATION_PORT_NAME);
653 	if (fRequestPort < 0)
654 		return fRequestPort;
655 
656 	fRequestThread = spawn_thread(&_RequestThreadEntry,
657 		"authentication manager", B_NORMAL_PRIORITY + 1, this);
658 	if (fRequestThread < 0)
659 		return fRequestThread;
660 
661 	resume_thread(fRequestThread);
662 
663 	return B_OK;
664 }
665 
666 
667 status_t
668 AuthenticationManager::_RequestThreadEntry(void* data)
669 {
670 	return ((AuthenticationManager*)data)->_RequestThread();
671 }
672 
673 
674 status_t
675 AuthenticationManager::_RequestThread()
676 {
677 	// read the DB files
678 	_InitPasswdDB();
679 	_InitGroupDB();
680 	_InitShadowPwdDB();
681 
682     // get our team ID
683 	team_id registrarTeam = -1;
684 	{
685 		thread_info info;
686 		if (get_thread_info(find_thread(NULL), &info) == B_OK)
687 			registrarTeam = info.team;
688 	}
689 
690 	// request loop
691 	while (true) {
692 		KMessage message;
693 		port_message_info messageInfo;
694 		status_t error = message.ReceiveFrom(fRequestPort, -1, &messageInfo);
695 		if (error != B_OK)
696 			return B_OK;
697 
698 		bool isRoot = (messageInfo.sender == 0);
699 
700 		switch (message.What()) {
701 			case B_REG_GET_PASSWD_DB:
702 			{
703 				// lazily build the reply
704 				try {
705 					if (fPasswdDBReply->What() == 1) {
706 						FlatStore store;
707 						int32 count = fUserDB->WriteFlatPasswdDB(store);
708 						if (fPasswdDBReply->AddInt32("count", count) != B_OK
709 							|| fPasswdDBReply->AddData("entries", B_RAW_TYPE,
710 									store.Buffer(), store.BufferLength(),
711 									false) != B_OK) {
712 							error = B_NO_MEMORY;
713 						}
714 
715 						fPasswdDBReply->SetWhat(0);
716 					}
717 				} catch (...) {
718 					error = B_NO_MEMORY;
719 				}
720 
721 				if (error == B_OK) {
722 					message.SendReply(fPasswdDBReply, -1, -1, 0, registrarTeam);
723 				} else {
724 					fPasswdDBReply->SetTo(1);
725 					KMessage reply(error);
726 					message.SendReply(&reply, -1, -1, 0, registrarTeam);
727 				}
728 
729 				break;
730 			}
731 
732 			case B_REG_GET_GROUP_DB:
733 			{
734 				// lazily build the reply
735 				try {
736 					if (fGroupDBReply->What() == 1) {
737 						FlatStore store;
738 						int32 count = fGroupDB->WriteFlatGroupDB(store);
739 						if (fGroupDBReply->AddInt32("count", count) != B_OK
740 							|| fGroupDBReply->AddData("entries", B_RAW_TYPE,
741 									store.Buffer(), store.BufferLength(),
742 									false) != B_OK) {
743 							error = B_NO_MEMORY;
744 						}
745 
746 						fGroupDBReply->SetWhat(0);
747 					}
748 				} catch (...) {
749 					error = B_NO_MEMORY;
750 				}
751 
752 				if (error == B_OK) {
753 					message.SendReply(fGroupDBReply, -1, -1, 0, registrarTeam);
754 				} else {
755 					fGroupDBReply->SetTo(1);
756 					KMessage reply(error);
757 					message.SendReply(&reply, -1, -1, 0, registrarTeam);
758 				}
759 
760 				break;
761 			}
762 
763 
764 			case B_REG_GET_SHADOW_PASSWD_DB:
765 			{
766 				// only root may see the shadow passwd
767 				if (!isRoot)
768 					error = EPERM;
769 
770 				// lazily build the reply
771 				try {
772 					if (error == B_OK && fShadowPwdDBReply->What() == 1) {
773 						FlatStore store;
774 						int32 count = fUserDB->WriteFlatShadowDB(store);
775 						if (fShadowPwdDBReply->AddInt32("count", count) != B_OK
776 							|| fShadowPwdDBReply->AddData("entries", B_RAW_TYPE,
777 									store.Buffer(), store.BufferLength(),
778 									false) != B_OK) {
779 							error = B_NO_MEMORY;
780 						}
781 
782 						fShadowPwdDBReply->SetWhat(0);
783 					}
784 				} catch (...) {
785 					error = B_NO_MEMORY;
786 				}
787 
788 				if (error == B_OK) {
789 					message.SendReply(fShadowPwdDBReply, -1, -1, 0,
790 						registrarTeam);
791 				} else {
792 					fShadowPwdDBReply->SetTo(1);
793 					KMessage reply(error);
794 					message.SendReply(&reply, -1, -1, 0, registrarTeam);
795 				}
796 
797 				break;
798 			}
799 
800 			case B_REG_GET_USER:
801 			{
802 				User* user = NULL;
803 				int32 uid;
804 				const char* name;
805 
806 				// find user
807 				if (message.FindInt32("uid", &uid) == B_OK) {
808 					user = fUserDB->UserByID(uid);
809 				} else if (message.FindString("name", &name) == B_OK) {
810 					user = fUserDB->UserByName(name);
811 				} else {
812 					error = B_BAD_VALUE;
813 				}
814 
815 				if (error == B_OK && user == NULL)
816 					error = ENOENT;
817 
818 				bool getShadowPwd = message.GetBool("shadow", false);
819 
820 				// only root may see the shadow passwd
821 				if (error == B_OK && getShadowPwd && !isRoot)
822 					error = EPERM;
823 
824 				// add user to message
825 				KMessage reply;
826 				if (error == B_OK)
827 					error = user->WriteToMessage(reply, getShadowPwd);
828 
829 				// send reply
830 				reply.SetWhat(error);
831 				message.SendReply(&reply, -1, -1, 0, registrarTeam);
832 
833 				break;
834 			}
835 
836 			case B_REG_GET_GROUP:
837 			{
838 				Group* group = NULL;
839 				int32 gid;
840 				const char* name;
841 
842 				// find group
843 				if (message.FindInt32("gid", &gid) == B_OK) {
844 					group = fGroupDB->GroupByID(gid);
845 				} else if (message.FindString("name", &name) == B_OK) {
846 					group = fGroupDB->GroupByName(name);
847 				} else {
848 					error = B_BAD_VALUE;
849 				}
850 
851 				if (error == B_OK && group == NULL)
852 					error = ENOENT;
853 
854 				// add group to message
855 				KMessage reply;
856 				if (error == B_OK)
857 					error = group->WriteToMessage(reply);
858 
859 				// send reply
860 				reply.SetWhat(error);
861 				message.SendReply(&reply, -1, -1, 0, registrarTeam);
862 
863 				break;
864 			}
865 
866 			case B_REG_GET_USER_GROUPS:
867 			{
868 				// get user name
869 				const char* name;
870 				int32 maxCount;
871 				if (message.FindString("name", &name) != B_OK
872 					|| message.FindInt32("max count", &maxCount) != B_OK
873 					|| maxCount <= 0) {
874 					error = B_BAD_VALUE;
875 				}
876 
877 				// get groups
878 				gid_t groups[NGROUPS_MAX + 1];
879 				int32 count = 0;
880 				if (error == B_OK) {
881 					maxCount = min_c(maxCount, NGROUPS_MAX + 1);
882 					count = fGroupDB->GetUserGroups(name, groups, maxCount);
883 				}
884 
885 				// add groups to message
886 				KMessage reply;
887 				if (error == B_OK) {
888 					if (reply.AddInt32("count", count) != B_OK
889 						|| reply.AddData("groups", B_INT32_TYPE,
890 								groups, min_c(maxCount, count) * sizeof(gid_t),
891 								false) != B_OK) {
892 						error = B_NO_MEMORY;
893 					}
894 				}
895 
896 				// send reply
897 				reply.SetWhat(error);
898 				message.SendReply(&reply, -1, -1, 0, registrarTeam);
899 
900 				break;
901 			}
902 
903 			case B_REG_UPDATE_USER:
904 			{
905 				// find user
906 				User* user = NULL;
907 				int32 uid;
908 				const char* name;
909 
910 				if (message.FindInt32("uid", &uid) == B_OK) {
911 					user = fUserDB->UserByID(uid);
912 				} else if (message.FindString("name", &name) == B_OK) {
913 					user = fUserDB->UserByName(name);
914 				} else {
915 					error = B_BAD_VALUE;
916 				}
917 
918 				// only can change anything
919 				if (error == B_OK && !isRoot)
920 					error = EPERM;
921 
922 				// check addUser vs. existing user
923 				bool addUser = message.GetBool("add user", false);
924 				if (error == B_OK) {
925 					if (addUser) {
926 						if (user != NULL)
927 							error = EEXIST;
928 					} else if (user == NULL)
929 						error = ENOENT;
930 				}
931 
932 				// apply all changes
933 				if (error == B_OK) {
934 					// clone the user object and update it from the message
935 					User* oldUser = user;
936 					user = NULL;
937 					try {
938 						user = (oldUser != NULL ? new User(*oldUser)
939 							: new User);
940 						user->UpdateFromMessage(message);
941 
942 						// uid and name should remain the same
943 						if (oldUser != NULL) {
944 							if (oldUser->UID() != user->UID()
945 								|| oldUser->Name() != user->Name()) {
946 								error = B_BAD_VALUE;
947 							}
948 						}
949 
950 						// replace the old user and write DBs to disk
951 						if (error == B_OK) {
952 							fUserDB->AddUser(user);
953 							fUserDB->WriteToDisk();
954 							fPasswdDBReply->SetTo(1);
955 							fShadowPwdDBReply->SetTo(1);
956 						}
957 					} catch (...) {
958 						error = B_NO_MEMORY;
959 					}
960 
961 					if (error == B_OK)
962 						delete oldUser;
963 					else
964 						delete user;
965 				}
966 
967 				// send reply
968 				KMessage reply;
969 				reply.SetWhat(error);
970 				message.SendReply(&reply, -1, -1, 0, registrarTeam);
971 
972 				break;
973 			}
974 			case B_REG_UPDATE_GROUP:
975 				debug_printf("B_REG_UPDATE_GROUP done: currently unsupported!\n");
976 				break;
977 			default:
978 				debug_printf("REG: invalid message: %lu\n", message.What());
979 
980 		}
981 	}
982 }
983 
984 
985 status_t
986 AuthenticationManager::_InitPasswdDB()
987 {
988 	FILE* file = fopen(kPasswdFile, "r");
989 	if (file == NULL) {
990 		debug_printf("REG: Failed to open passwd DB file \"%s\": %s\n",
991 			kPasswdFile, strerror(errno));
992 		return errno;
993 	}
994 	CObjectDeleter<FILE, int> _(file, fclose);
995 
996 	char lineBuffer[LINE_MAX];
997 	while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) {
998 		if (strlen(line) == 0)
999 			continue;
1000 
1001 		char* name;
1002 		char* password;
1003 		uid_t uid;
1004 		gid_t gid;
1005 		char* home;
1006 		char* shell;
1007 		char* realName;
1008 
1009 		status_t error = parse_passwd_line(line, name, password, uid, gid,
1010 			home, shell, realName);
1011 		if (error != B_OK) {
1012 			debug_printf("REG: Unparsable line in passwd DB file: \"%s\"\n",
1013 				strerror(errno));
1014 			continue;
1015 		}
1016 
1017 		User* user = NULL;
1018 		try {
1019 			user = new User(name, password, uid, gid, home, shell, realName);
1020 		} catch (...) {
1021 		}
1022 
1023 		if (user == NULL || fUserDB->AddUser(user) != B_OK) {
1024 			delete user;
1025 			debug_printf("REG: Out of memory\n");
1026 			return B_NO_MEMORY;
1027 		}
1028 	}
1029 
1030 	return B_OK;
1031 }
1032 
1033 
1034 status_t
1035 AuthenticationManager::_InitGroupDB()
1036 {
1037 	FILE* file = fopen(kGroupFile, "r");
1038 	if (file == NULL) {
1039 		debug_printf("REG: Failed to open group DB file \"%s\": %s\n",
1040 			kGroupFile, strerror(errno));
1041 		return errno;
1042 	}
1043 	CObjectDeleter<FILE, int> _(file, fclose);
1044 
1045 	char lineBuffer[LINE_MAX];
1046 	while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) {
1047 		if (strlen(line) == 0)
1048 			continue;
1049 
1050 		char* name;
1051 		char* password;
1052 		gid_t gid;
1053 		char* members[MAX_GROUP_MEMBER_COUNT];
1054 		int memberCount;
1055 
1056 
1057 		status_t error = parse_group_line(line, name, password, gid, members,
1058 			memberCount);
1059 		if (error != B_OK) {
1060 			debug_printf("REG: Unparsable line in group DB file: \"%s\"\n",
1061 				strerror(errno));
1062 			continue;
1063 		}
1064 
1065 		Group* group = NULL;
1066 		try {
1067 			group = new Group(name, password, gid, members, memberCount);
1068 		} catch (...) {
1069 		}
1070 
1071 		if (group == NULL || fGroupDB->AddGroup(group) != B_OK) {
1072 			delete group;
1073 			debug_printf("REG: Out of memory\n");
1074 			return B_NO_MEMORY;
1075 		}
1076 	}
1077 
1078 	return B_OK;
1079 }
1080 
1081 
1082 status_t
1083 AuthenticationManager::_InitShadowPwdDB()
1084 {
1085 	FILE* file = fopen(kShadowPwdFile, "r");
1086 	if (file == NULL) {
1087 		debug_printf("REG: Failed to open shadow passwd DB file \"%s\": %s\n",
1088 			kShadowPwdFile, strerror(errno));
1089 		return errno;
1090 	}
1091 	CObjectDeleter<FILE, int> _(file, fclose);
1092 
1093 	char lineBuffer[LINE_MAX];
1094 	while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) {
1095 		if (strlen(line) == 0)
1096 			continue;
1097 
1098 		char* name;
1099 		char* password;
1100 		int lastChanged;
1101 		int min;
1102 		int max;
1103 		int warn;
1104 		int inactive;
1105 		int expiration;
1106 		int flags;
1107 
1108 		status_t error = parse_shadow_pwd_line(line, name, password,
1109 			lastChanged, min, max, warn, inactive, expiration, flags);
1110 		if (error != B_OK) {
1111 			debug_printf("REG: Unparsable line in shadow passwd DB file: "
1112 				"\"%s\"\n", strerror(errno));
1113 			continue;
1114 		}
1115 
1116 		User* user = fUserDB->UserByName(name);
1117 		if (user == NULL) {
1118 			debug_printf("REG: shadow pwd entry for unknown user \"%s\"\n",
1119 				name);
1120 			continue;
1121 		}
1122 
1123 		try {
1124 			user->SetShadowInfo(password, lastChanged, min, max, warn, inactive,
1125 				expiration, flags);
1126 		} catch (...) {
1127 			debug_printf("REG: Out of memory\n");
1128 			return B_NO_MEMORY;
1129 		}
1130 	}
1131 
1132 	return B_OK;
1133 }
1134