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