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