xref: /haiku/src/servers/registrar/AuthenticationManager.cpp (revision d0ac609964842f8cdb6d54b3c539c6c15293e172)
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 <LaunchRoster.h>
24 #include <RegistrarDefs.h>
25 
26 #include <libroot_private.h>
27 #include <user_group.h>
28 #include <util/KMessage.h>
29 
30 
31 using std::map;
32 using std::string;
33 
34 using namespace BPrivate;
35 
36 
37 typedef std::set<std::string> StringSet;
38 
39 
40 class AuthenticationManager::FlatStore {
41 public:
42 	FlatStore()
43 		: fSize(0)
44 	{
45 		fBuffer.SetBlockSize(1024);
46 	}
47 
48 	void WriteData(size_t offset, const void* data, size_t length)
49 	{
50 		ssize_t result = fBuffer.WriteAt(offset, data, length);
51 		if (result < 0)
52 			throw status_t(result);
53 	}
54 
55 	template<typename Type>
56 	void WriteData(size_t offset, const Type& data)
57 	{
58 		WriteData(&data, sizeof(Type));
59 	}
60 
61 	size_t ReserveSpace(size_t length, bool align)
62 	{
63 		if (align)
64 			fSize = _ALIGN(fSize);
65 
66 		size_t pos = fSize;
67 		fSize += length;
68 
69 		return pos;
70 	}
71 
72 	void* AppendData(const void* data, size_t length, bool align)
73 	{
74 		size_t pos = ReserveSpace(length, align);
75 		WriteData(pos, data, length);
76 		return (void*)(addr_t)pos;
77 	}
78 
79 	template<typename Type>
80 	Type* AppendData(const Type& data)
81 	{
82 		return (Type*)AppendData(&data, sizeof(Type), true);
83 	}
84 
85 	char* AppendString(const char* string)
86 	{
87 		return (char*)AppendData(string, strlen(string) + 1, false);
88 	}
89 
90 	char* AppendString(const string& str)
91 	{
92 		return (char*)AppendData(str.c_str(), str.length() + 1, false);
93 	}
94 
95 	const void* Buffer() const
96 	{
97 		return fBuffer.Buffer();
98 	}
99 
100 	size_t BufferLength() const
101 	{
102 		return fSize;
103 	}
104 
105 private:
106 	BMallocIO	fBuffer;
107 	size_t		fSize;
108 };
109 
110 
111 class AuthenticationManager::User {
112 public:
113 	User()
114 		:
115 		fUID(0),
116 		fGID(0),
117 		fLastChanged(0),
118 		fMin(-1),
119 		fMax(-1),
120 		fWarn(-1),
121 		fInactive(-1),
122 		fExpiration(-1),
123 		fFlags(0)
124 	{
125 	}
126 
127 	User(const char* name, const char* password, uid_t uid, gid_t gid,
128 		const char* home, const char* shell, const char* realName)
129 		:
130 		fUID(uid),
131 		fGID(gid),
132 		fName(name),
133 		fPassword(password),
134 		fHome(home),
135 		fShell(shell),
136 		fRealName(realName),
137 		fLastChanged(0),
138 		fMin(-1),
139 		fMax(-1),
140 		fWarn(-1),
141 		fInactive(-1),
142 		fExpiration(-1),
143 		fFlags(0)
144 	{
145 	}
146 
147 	User(const User& other)
148 		:
149 		fUID(other.fUID),
150 		fGID(other.fGID),
151 		fName(other.fName),
152 		fPassword(other.fPassword),
153 		fHome(other.fHome),
154 		fShell(other.fShell),
155 		fRealName(other.fRealName),
156 		fShadowPassword(other.fShadowPassword),
157 		fLastChanged(other.fLastChanged),
158 		fMin(other.fMin),
159 		fMax(other.fMax),
160 		fWarn(other.fWarn),
161 		fInactive(other.fInactive),
162 		fExpiration(other.fExpiration),
163 		fFlags(other.fFlags)
164 	{
165 	}
166 
167 	const string& Name() const	{ return fName; }
168 	const uid_t UID() const		{ return fUID; }
169 
170 	void SetShadowInfo(const char* password, int lastChanged, int min, int max,
171 		int warn, int inactive, int expiration, int flags)
172 	{
173 		fShadowPassword = password;
174 		fLastChanged = lastChanged;
175 		fMin = min;
176 		fMax = max;
177 		fWarn = warn;
178 		fInactive = inactive;
179 		fExpiration = expiration;
180 		fFlags = flags;
181 	}
182 
183 	void UpdateFromMessage(const KMessage& message)
184 	{
185 		int32 intValue;
186 		const char* stringValue;
187 
188 		if (message.FindInt32("uid", &intValue) == B_OK)
189 			fUID = intValue;
190 
191 		if (message.FindInt32("gid", &intValue) == B_OK)
192 			fGID = intValue;
193 
194 		if (message.FindString("name", &stringValue) == B_OK)
195 			fName = stringValue;
196 
197 		if (message.FindString("password", &stringValue) == B_OK)
198 			fPassword = stringValue;
199 
200 		if (message.FindString("home", &stringValue) == B_OK)
201 			fHome = stringValue;
202 
203 		if (message.FindString("shell", &stringValue) == B_OK)
204 			fShell = stringValue;
205 
206 		if (message.FindString("real name", &stringValue) == B_OK)
207 			fRealName = stringValue;
208 
209 		if (message.FindString("shadow password", &stringValue) == B_OK) {
210 			fShadowPassword = stringValue;
211 			// TODO:
212 			// fLastChanged = now;
213 		}
214 
215 		if (message.FindInt32("last changed", &intValue) == B_OK)
216 			fLastChanged = intValue;
217 
218 		if (message.FindInt32("min", &intValue) == B_OK)
219 			fMin = intValue;
220 
221 		if (message.FindInt32("max", &intValue) == B_OK)
222 			fMax = intValue;
223 
224 		if (message.FindInt32("warn", &intValue) == B_OK)
225 			fWarn = intValue;
226 
227 		if (message.FindInt32("inactive", &intValue) == B_OK)
228 			fInactive = intValue;
229 
230 		if (message.FindInt32("expiration", &intValue) == B_OK)
231 			fExpiration = intValue;
232 
233 		if (message.FindInt32("flags", &intValue) == B_OK)
234 			fFlags = intValue;
235 	}
236 
237 	passwd* WriteFlatPasswd(FlatStore& store) const
238 	{
239 		struct passwd passwd;
240 
241 		passwd.pw_uid = fUID;
242 		passwd.pw_gid = fGID;
243 		passwd.pw_name = store.AppendString(fName);
244 		passwd.pw_passwd = store.AppendString(fPassword);
245 		passwd.pw_dir = store.AppendString(fHome);
246 		passwd.pw_shell = store.AppendString(fShell);
247 		passwd.pw_gecos = store.AppendString(fRealName);
248 
249 		return store.AppendData(passwd);
250 	}
251 
252 	spwd* WriteFlatShadowPwd(FlatStore& store) const
253 	{
254 		struct spwd spwd;
255 
256 		spwd.sp_namp = store.AppendString(fName);
257 		spwd.sp_pwdp = store.AppendString(fShadowPassword);
258 		spwd.sp_lstchg = fLastChanged;
259 		spwd.sp_min = fMin;
260 		spwd.sp_max = fMax;
261 		spwd.sp_warn = fWarn;
262 		spwd.sp_inact = fInactive;
263 		spwd.sp_expire = fExpiration;
264 		spwd.sp_flag = fFlags;
265 
266 		return store.AppendData(spwd);
267 	}
268 
269 	status_t WriteToMessage(KMessage& message, bool addShadowPwd)
270 	{
271 		status_t error;
272 		if ((error = message.AddInt32("uid", fUID)) != B_OK
273 			|| (error = message.AddInt32("gid", fGID)) != B_OK
274 			|| (error = message.AddString("name", fName.c_str())) != B_OK
275 			|| (error = message.AddString("password", fPassword.c_str()))
276 					!= B_OK
277 			|| (error = message.AddString("home", fHome.c_str())) != B_OK
278 			|| (error = message.AddString("shell", fShell.c_str())) != B_OK
279 			|| (error = message.AddString("real name", fRealName.c_str()))
280 					!= B_OK) {
281 			return error;
282 		}
283 
284 		if (!addShadowPwd)
285 			return B_OK;
286 
287 		if ((error = message.AddString("shadow password",
288 					fShadowPassword.c_str())) != B_OK
289 			|| (error = message.AddInt32("last changed", fLastChanged)) != B_OK
290 			|| (error = message.AddInt32("min", fMin)) != B_OK
291 			|| (error = message.AddInt32("max", fMax)) != B_OK
292 			|| (error = message.AddInt32("warn", fWarn)) != B_OK
293 			|| (error = message.AddInt32("inactive", fInactive)) != B_OK
294 			|| (error = message.AddInt32("expiration", fExpiration)) != B_OK
295 			|| (error = message.AddInt32("flags", fFlags)) != B_OK) {
296 			return error;
297 		}
298 
299 		return B_OK;
300 	}
301 
302 	void WritePasswdLine(FILE* file)
303 	{
304 		fprintf(file, "%s:%s:%d:%d:%s:%s:%s\n",
305 			fName.c_str(), fPassword.c_str(), (int)fUID, (int)fGID,
306 			fRealName.c_str(), fHome.c_str(), fShell.c_str());
307 	}
308 
309 	void WriteShadowPwdLine(FILE* file)
310 	{
311 		fprintf(file, "%s:%s:%d:", fName.c_str(), fShadowPassword.c_str(),
312 			fLastChanged);
313 
314 		// The following values are supposed to be printed as empty strings,
315 		// if negative.
316 		int values[5] = { fMin, fMax, fWarn, fInactive, fExpiration };
317 		for (int i = 0; i < 5; i++) {
318 			if (values[i] >= 0)
319 				fprintf(file, "%d", values[i]);
320 			fprintf(file, ":");
321 		}
322 
323 		fprintf(file, "%d\n", fFlags);
324 	}
325 
326 private:
327 	uid_t	fUID;
328 	gid_t	fGID;
329 	string	fName;
330 	string	fPassword;
331 	string	fHome;
332 	string	fShell;
333 	string	fRealName;
334 	string	fShadowPassword;
335 	int		fLastChanged;
336 	int		fMin;
337 	int		fMax;
338 	int		fWarn;
339 	int		fInactive;
340 	int		fExpiration;
341 	int		fFlags;
342 };
343 
344 
345 class AuthenticationManager::Group {
346 public:
347 	Group()
348 		:
349 		fGID(0),
350 		fName(),
351 		fPassword(),
352 		fMembers()
353 	{
354 	}
355 
356 	Group(const char* name, const char* password, gid_t gid,
357 		const char* const* members, int memberCount)
358 		:
359 		fGID(gid),
360 		fName(name),
361 		fPassword(password),
362 		fMembers()
363 	{
364 		for (int i = 0; i < memberCount; i++)
365 			fMembers.insert(members[i]);
366 	}
367 
368 	~Group()
369 	{
370 	}
371 
372 	const string& Name() const	{ return fName; }
373 	const gid_t GID() const		{ return fGID; }
374 
375 	bool HasMember(const char* name)
376 	{
377 		try {
378 			return fMembers.find(name) != fMembers.end();
379 		} catch (...) {
380 			return false;
381 		}
382 	}
383 
384 	bool MemberRemoved(const std::string& name)
385 	{
386 		return fMembers.erase(name) > 0;
387 	}
388 
389 	void UpdateFromMessage(const KMessage& message)
390 	{
391 		int32 intValue;
392 		if (message.FindInt32("gid", &intValue) == B_OK)
393 			fGID = intValue;
394 
395 		const char* stringValue;
396 		if (message.FindString("name", &stringValue) == B_OK)
397 			fName = stringValue;
398 
399 		if (message.FindString("password", &stringValue) == B_OK)
400 			fPassword = stringValue;
401 
402 		if (message.FindString("members", &stringValue) == B_OK) {
403 			fMembers.clear();
404 			for (int32 i = 0;
405 				(stringValue = message.GetString("members", i, NULL)) != NULL;
406 				i++) {
407 				if (stringValue != NULL && *stringValue != '\0')
408 					fMembers.insert(stringValue);
409 			}
410 		}
411 	}
412 
413 	group* WriteFlatGroup(FlatStore& store) const
414 	{
415 		struct group group;
416 
417 		char* members[MAX_GROUP_MEMBER_COUNT + 1];
418 		int32 count = 0;
419 		for (StringSet::const_iterator it = fMembers.begin();
420 			it != fMembers.end(); ++it) {
421 			members[count++] = store.AppendString(it->c_str());
422 		}
423 		members[count] = (char*)-1;
424 
425 		group.gr_gid = fGID;
426 		group.gr_name = store.AppendString(fName);
427 		group.gr_passwd = store.AppendString(fPassword);
428 		group.gr_mem = (char**)store.AppendData(members,
429 			sizeof(char*) * (count + 1), true);
430 
431 		return store.AppendData(group);
432 	}
433 
434 	status_t WriteToMessage(KMessage& message)
435 	{
436 		status_t error;
437 		if ((error = message.AddInt32("gid", fGID)) != B_OK
438 			|| (error = message.AddString("name", fName.c_str())) != B_OK
439 			|| (error = message.AddString("password", fPassword.c_str()))
440 					!= B_OK) {
441 			return error;
442 		}
443 
444 		for (StringSet::const_iterator it = fMembers.begin();
445 			it != fMembers.end(); ++it) {
446 			if ((error = message.AddString("members", it->c_str())) != B_OK)
447 				return error;
448 		}
449 
450 		return B_OK;
451 	}
452 
453 	void WriteGroupLine(FILE* file)
454 	{
455 		fprintf(file, "%s:%s:%d:",
456 			fName.c_str(), fPassword.c_str(), (int)fGID);
457 		for (StringSet::const_iterator it = fMembers.begin();
458 			it != fMembers.end(); ++it) {
459 			if (it == fMembers.begin())
460 				fprintf(file, "%s", it->c_str());
461 			else
462 				fprintf(file, ",%s", it->c_str());
463 		}
464 		fputs("\n", file);
465 	}
466 
467 private:
468 	gid_t		fGID;
469 	string		fName;
470 	string		fPassword;
471 	StringSet	fMembers;
472 };
473 
474 
475 class AuthenticationManager::UserDB {
476 public:
477 	status_t AddUser(User* user)
478 	{
479 		try {
480 			fUsersByID[user->UID()] = user;
481 		} catch (...) {
482 			return B_NO_MEMORY;
483 		}
484 
485 		try {
486 			fUsersByName[user->Name()] = user;
487 		} catch (...) {
488 			fUsersByID.erase(fUsersByID.find(user->UID()));
489 			return B_NO_MEMORY;
490 		}
491 
492 		return B_OK;
493 	}
494 
495 	void RemoveUser(User* user)
496 	{
497 		fUsersByID.erase(fUsersByID.find(user->UID()));
498 		fUsersByName.erase(fUsersByName.find(user->Name()));
499 	}
500 
501 	User* UserByID(uid_t uid) const
502 	{
503 		map<uid_t, User*>::const_iterator it = fUsersByID.find(uid);
504 		return (it == fUsersByID.end() ? NULL : it->second);
505 	}
506 
507 	User* UserByName(const char* name) const
508 	{
509 		map<string, User*>::const_iterator it = fUsersByName.find(name);
510 		return (it == fUsersByName.end() ? NULL : it->second);
511 	}
512 
513 	int32 WriteFlatPasswdDB(FlatStore& store) const
514 	{
515 		int32 count = fUsersByID.size();
516 
517 		size_t entriesSpace = sizeof(passwd*) * count;
518 		size_t offset = store.ReserveSpace(entriesSpace, true);
519 		passwd** entries = new passwd*[count];
520 		ArrayDeleter<passwd*> _(entries);
521 
522 		int32 index = 0;
523 		for (map<uid_t, User*>::const_iterator it = fUsersByID.begin();
524 			 it != fUsersByID.end(); ++it) {
525 			entries[index++] = it->second->WriteFlatPasswd(store);
526 		}
527 
528 		store.WriteData(offset, entries, entriesSpace);
529 
530 		return count;
531 	}
532 
533 	int32 WriteFlatShadowDB(FlatStore& store) const
534 	{
535 		int32 count = fUsersByID.size();
536 
537 		size_t entriesSpace = sizeof(spwd*) * count;
538 		size_t offset = store.ReserveSpace(entriesSpace, true);
539 		spwd** entries = new spwd*[count];
540 		ArrayDeleter<spwd*> _(entries);
541 
542 		int32 index = 0;
543 		for (map<uid_t, User*>::const_iterator it = fUsersByID.begin();
544 			 it != fUsersByID.end(); ++it) {
545 			entries[index++] = it->second->WriteFlatShadowPwd(store);
546 		}
547 
548 		store.WriteData(offset, entries, entriesSpace);
549 
550 		return count;
551 	}
552 
553 	void WriteToDisk()
554 	{
555 		// rename the old files
556 		string passwdBackup(kPasswdFile);
557 		string shadowBackup(kShadowPwdFile);
558 		passwdBackup += ".old";
559 		shadowBackup += ".old";
560 
561 		rename(kPasswdFile, passwdBackup.c_str());
562 		rename(kShadowPwdFile, shadowBackup.c_str());
563 			// Don't check errors. We can't do anything anyway.
564 
565 		// open files
566 		FILE* passwdFile = fopen(kPasswdFile, "w");
567 		if (passwdFile == NULL) {
568 			debug_printf("REG: Failed to open passwd file \"%s\" for "
569 				"writing: %s\n", kPasswdFile, strerror(errno));
570 		}
571 		CObjectDeleter<FILE, int> _1(passwdFile, fclose);
572 
573 		FILE* shadowFile = fopen(kShadowPwdFile, "w");
574 		if (shadowFile == NULL) {
575 			debug_printf("REG: Failed to open shadow passwd file \"%s\" for "
576 				"writing: %s\n", kShadowPwdFile, strerror(errno));
577 		}
578 		CObjectDeleter<FILE, int> _2(shadowFile, fclose);
579 
580 		// write users
581 		for (map<uid_t, User*>::const_iterator it = fUsersByID.begin();
582 			 it != fUsersByID.end(); ++it) {
583 			User* user = it->second;
584 			user->WritePasswdLine(passwdFile);
585 			user->WriteShadowPwdLine(shadowFile);
586 		}
587 	}
588 
589 private:
590 	map<uid_t, User*>	fUsersByID;
591 	map<string, User*>	fUsersByName;
592 };
593 
594 
595 class AuthenticationManager::GroupDB {
596 public:
597 	status_t AddGroup(Group* group)
598 	{
599 		try {
600 			fGroupsByID[group->GID()] = group;
601 		} catch (...) {
602 			return B_NO_MEMORY;
603 		}
604 
605 		try {
606 			fGroupsByName[group->Name()] = group;
607 		} catch (...) {
608 			fGroupsByID.erase(fGroupsByID.find(group->GID()));
609 			return B_NO_MEMORY;
610 		}
611 
612 		return B_OK;
613 	}
614 
615 	void RemoveGroup(Group* group)
616 	{
617 		fGroupsByID.erase(fGroupsByID.find(group->GID()));
618 		fGroupsByName.erase(fGroupsByName.find(group->Name()));
619 	}
620 
621 	bool UserRemoved(const std::string& user)
622 	{
623 		bool changed = false;
624 		for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin();
625 			 it != fGroupsByID.end(); ++it) {
626 			Group* group = it->second;
627 			changed |= group->MemberRemoved(user);
628 		}
629 		return changed;
630 	}
631 
632 	Group* GroupByID(gid_t gid) const
633 	{
634 		map<gid_t, Group*>::const_iterator it = fGroupsByID.find(gid);
635 		return (it == fGroupsByID.end() ? NULL : it->second);
636 	}
637 
638 	Group* GroupByName(const char* name) const
639 	{
640 		map<string, Group*>::const_iterator it = fGroupsByName.find(name);
641 		return (it == fGroupsByName.end() ? NULL : it->second);
642 	}
643 
644 	int32 GetUserGroups(const char* name, gid_t* groups, int maxCount)
645 	{
646 		int count = 0;
647 
648 		for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin();
649 			 it != fGroupsByID.end(); ++it) {
650 			Group* group = it->second;
651 			if (group->HasMember(name)) {
652 				if (count < maxCount)
653 					groups[count] = group->GID();
654 				count++;
655 			}
656 		}
657 
658 		return count;
659 	}
660 
661 
662 	int32 WriteFlatGroupDB(FlatStore& store) const
663 	{
664 		int32 count = fGroupsByID.size();
665 
666 		size_t entriesSpace = sizeof(group*) * count;
667 		size_t offset = store.ReserveSpace(entriesSpace, true);
668 		group** entries = new group*[count];
669 		ArrayDeleter<group*> _(entries);
670 
671 		int32 index = 0;
672 		for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin();
673 			 it != fGroupsByID.end(); ++it) {
674 			entries[index++] = it->second->WriteFlatGroup(store);
675 		}
676 
677 		store.WriteData(offset, entries, entriesSpace);
678 
679 		return count;
680 	}
681 
682 	void WriteToDisk()
683 	{
684 		// rename the old files
685 		string groupBackup(kGroupFile);
686 		groupBackup += ".old";
687 
688 		rename(kGroupFile, groupBackup.c_str());
689 			// Don't check errors. We can't do anything anyway.
690 
691 		// open file
692 		FILE* groupFile = fopen(kGroupFile, "w");
693 		if (groupFile == NULL) {
694 			debug_printf("REG: Failed to open group file \"%s\" for "
695 				"writing: %s\n", kGroupFile, strerror(errno));
696 		}
697 		CObjectDeleter<FILE, int> _1(groupFile, fclose);
698 
699 		// write groups
700 		for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin();
701 			it != fGroupsByID.end(); ++it) {
702 			Group* group = it->second;
703 			group->WriteGroupLine(groupFile);
704 		}
705 	}
706 
707 private:
708 	map<uid_t, Group*>	fGroupsByID;
709 	map<string, Group*>	fGroupsByName;
710 };
711 
712 
713 AuthenticationManager::AuthenticationManager()
714 	:
715 	fRequestPort(-1),
716 	fRequestThread(-1),
717 	fUserDB(NULL),
718 	fGroupDB(NULL),
719 	fPasswdDBReply(NULL),
720 	fGroupDBReply(NULL),
721 	fShadowPwdDBReply(NULL)
722 {
723 }
724 
725 
726 AuthenticationManager::~AuthenticationManager()
727 {
728 	// Quit the request thread and wait for it to finish
729 	write_port(fRequestPort, 'quit', NULL, 0);
730 	wait_for_thread(fRequestThread, NULL);
731 
732 	delete fUserDB;
733 	delete fGroupDB;
734 	delete fPasswdDBReply;
735 	delete fGroupDBReply;
736 	delete fShadowPwdDBReply;
737 }
738 
739 
740 status_t
741 AuthenticationManager::Init()
742 {
743 	fUserDB = new(std::nothrow) UserDB;
744 	fGroupDB = new(std::nothrow) GroupDB;
745 	fPasswdDBReply = new(std::nothrow) KMessage(1);
746 	fGroupDBReply = new(std::nothrow) KMessage(1);
747 	fShadowPwdDBReply = new(std::nothrow) KMessage(1);
748 
749 	if (fUserDB == NULL || fGroupDB == NULL || fPasswdDBReply == NULL
750 			|| fGroupDBReply == NULL || fShadowPwdDBReply == NULL) {
751 		return B_NO_MEMORY;
752 	}
753 
754 	fRequestPort = BLaunchRoster().GetPort(
755 		B_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