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