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
32using std::map;
33using std::string;
34
35using namespace BPrivate;
36
37
38typedef std::set<std::string> StringSet;
39
40
41class AuthenticationManager::FlatStore {
42public:
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
106private:
107	BMallocIO	fBuffer;
108	size_t		fSize;
109};
110
111
112class AuthenticationManager::User {
113public:
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
327private:
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
346class AuthenticationManager::Group {
347public:
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
468private:
469	gid_t		fGID;
470	string		fName;
471	string		fPassword;
472	StringSet	fMembers;
473};
474
475
476class AuthenticationManager::UserDB {
477public:
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		FileCloser passwdFile(fopen(kPasswdFile, "w"));
568		if (!passwdFile.IsSet()) {
569			debug_printf("REG: Failed to open passwd file \"%s\" for "
570				"writing: %s\n", kPasswdFile, strerror(errno));
571		}
572
573		FileCloser shadowFile(fopen(kShadowPwdFile, "w"));
574		if (!shadowFile.IsSet()) {
575			debug_printf("REG: Failed to open shadow passwd file \"%s\" for "
576				"writing: %s\n", kShadowPwdFile, strerror(errno));
577		}
578
579		// write users
580		for (map<uid_t, User*>::const_iterator it = fUsersByID.begin();
581			 it != fUsersByID.end(); ++it) {
582			User* user = it->second;
583			user->WritePasswdLine(passwdFile.Get());
584			user->WriteShadowPwdLine(shadowFile.Get());
585		}
586	}
587
588private:
589	map<uid_t, User*>	fUsersByID;
590	map<string, User*>	fUsersByName;
591};
592
593
594class AuthenticationManager::GroupDB {
595public:
596	status_t AddGroup(Group* group)
597	{
598		try {
599			fGroupsByID[group->GID()] = group;
600		} catch (...) {
601			return B_NO_MEMORY;
602		}
603
604		try {
605			fGroupsByName[group->Name()] = group;
606		} catch (...) {
607			fGroupsByID.erase(fGroupsByID.find(group->GID()));
608			return B_NO_MEMORY;
609		}
610
611		return B_OK;
612	}
613
614	void RemoveGroup(Group* group)
615	{
616		fGroupsByID.erase(fGroupsByID.find(group->GID()));
617		fGroupsByName.erase(fGroupsByName.find(group->Name()));
618	}
619
620	bool UserRemoved(const std::string& user)
621	{
622		bool changed = false;
623		for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin();
624			 it != fGroupsByID.end(); ++it) {
625			Group* group = it->second;
626			changed |= group->MemberRemoved(user);
627		}
628		return changed;
629	}
630
631	Group* GroupByID(gid_t gid) const
632	{
633		map<gid_t, Group*>::const_iterator it = fGroupsByID.find(gid);
634		return (it == fGroupsByID.end() ? NULL : it->second);
635	}
636
637	Group* GroupByName(const char* name) const
638	{
639		map<string, Group*>::const_iterator it = fGroupsByName.find(name);
640		return (it == fGroupsByName.end() ? NULL : it->second);
641	}
642
643	int32 GetUserGroups(const char* name, gid_t* groups, int maxCount)
644	{
645		int count = 0;
646
647		for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin();
648			 it != fGroupsByID.end(); ++it) {
649			Group* group = it->second;
650			if (group->HasMember(name)) {
651				if (count < maxCount)
652					groups[count] = group->GID();
653				count++;
654			}
655		}
656
657		return count;
658	}
659
660
661	int32 WriteFlatGroupDB(FlatStore& store) const
662	{
663		int32 count = fGroupsByID.size();
664
665		size_t entriesSpace = sizeof(group*) * count;
666		size_t offset = store.ReserveSpace(entriesSpace, true);
667		group** entries = new group*[count];
668		ArrayDeleter<group*> _(entries);
669
670		int32 index = 0;
671		for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin();
672			 it != fGroupsByID.end(); ++it) {
673			entries[index++] = it->second->WriteFlatGroup(store);
674		}
675
676		store.WriteData(offset, entries, entriesSpace);
677
678		return count;
679	}
680
681	void WriteToDisk()
682	{
683		// rename the old files
684		string groupBackup(kGroupFile);
685		groupBackup += ".old";
686
687		rename(kGroupFile, groupBackup.c_str());
688			// Don't check errors. We can't do anything anyway.
689
690		// open file
691		FileCloser groupFile(fopen(kGroupFile, "w"));
692		if (!groupFile.IsSet()) {
693			debug_printf("REG: Failed to open group file \"%s\" for "
694				"writing: %s\n", kGroupFile, strerror(errno));
695		}
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.Get());
702		}
703	}
704
705private:
706	map<uid_t, Group*>	fGroupsByID;
707	map<string, Group*>	fGroupsByName;
708};
709
710
711AuthenticationManager::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
724AuthenticationManager::~AuthenticationManager()
725{
726	// Quit the request thread and wait for it to finish
727	write_port(fRequestPort, 'quit', NULL, 0);
728	wait_for_thread(fRequestThread, NULL);
729
730	delete fUserDB;
731	delete fGroupDB;
732	delete fPasswdDBReply;
733	delete fGroupDBReply;
734	delete fShadowPwdDBReply;
735}
736
737
738status_t
739AuthenticationManager::Init()
740{
741	fUserDB = new(std::nothrow) UserDB;
742	fGroupDB = new(std::nothrow) GroupDB;
743	fPasswdDBReply = new(std::nothrow) KMessage(1);
744	fGroupDBReply = new(std::nothrow) KMessage(1);
745	fShadowPwdDBReply = new(std::nothrow) KMessage(1);
746
747	if (fUserDB == NULL || fGroupDB == NULL || fPasswdDBReply == NULL
748			|| fGroupDBReply == NULL || fShadowPwdDBReply == NULL) {
749		return B_NO_MEMORY;
750	}
751
752	fRequestPort = BLaunchRoster().GetPort(
753		B_REGISTRAR_AUTHENTICATION_PORT_NAME);
754	if (fRequestPort < 0)
755		return fRequestPort;
756
757	fRequestThread = spawn_thread(&_RequestThreadEntry,
758		"authentication manager", B_NORMAL_PRIORITY + 1, this);
759	if (fRequestThread < 0)
760		return fRequestThread;
761
762	resume_thread(fRequestThread);
763
764	return B_OK;
765}
766
767
768status_t
769AuthenticationManager::_RequestThreadEntry(void* data)
770{
771	return ((AuthenticationManager*)data)->_RequestThread();
772}
773
774
775status_t
776AuthenticationManager::_RequestThread()
777{
778	// read the DB files
779	_InitPasswdDB();
780	_InitGroupDB();
781	_InitShadowPwdDB();
782
783    // get our team ID
784	team_id registrarTeam = -1;
785	{
786		thread_info info;
787		if (get_thread_info(find_thread(NULL), &info) == B_OK)
788			registrarTeam = info.team;
789	}
790
791	// request loop
792	while (true) {
793		KMessage message;
794		port_message_info messageInfo;
795		status_t error = message.ReceiveFrom(fRequestPort, -1, &messageInfo);
796		if (error != B_OK)
797			return B_OK;
798
799		bool isRoot = (messageInfo.sender == 0);
800
801		switch (message.What()) {
802			case B_REG_GET_PASSWD_DB:
803			{
804				// lazily build the reply
805				try {
806					if (fPasswdDBReply->What() == 1) {
807						FlatStore store;
808						int32 count = fUserDB->WriteFlatPasswdDB(store);
809						if (fPasswdDBReply->AddInt32("count", count) != B_OK
810							|| fPasswdDBReply->AddData("entries", B_RAW_TYPE,
811									store.Buffer(), store.BufferLength(),
812									false) != B_OK) {
813							error = B_NO_MEMORY;
814						}
815
816						fPasswdDBReply->SetWhat(0);
817					}
818				} catch (...) {
819					error = B_NO_MEMORY;
820				}
821
822				if (error == B_OK) {
823					message.SendReply(fPasswdDBReply, -1, -1, 0, registrarTeam);
824				} else {
825					_InvalidatePasswdDBReply();
826					KMessage reply(error);
827					message.SendReply(&reply, -1, -1, 0, registrarTeam);
828				}
829
830				break;
831			}
832
833			case B_REG_GET_GROUP_DB:
834			{
835				// lazily build the reply
836				try {
837					if (fGroupDBReply->What() == 1) {
838						FlatStore store;
839						int32 count = fGroupDB->WriteFlatGroupDB(store);
840						if (fGroupDBReply->AddInt32("count", count) != B_OK
841							|| fGroupDBReply->AddData("entries", B_RAW_TYPE,
842									store.Buffer(), store.BufferLength(),
843									false) != B_OK) {
844							error = B_NO_MEMORY;
845						}
846
847						fGroupDBReply->SetWhat(0);
848					}
849				} catch (...) {
850					error = B_NO_MEMORY;
851				}
852
853				if (error == B_OK) {
854					message.SendReply(fGroupDBReply, -1, -1, 0, registrarTeam);
855				} else {
856					_InvalidateGroupDBReply();
857					KMessage reply(error);
858					message.SendReply(&reply, -1, -1, 0, registrarTeam);
859				}
860
861				break;
862			}
863
864
865			case B_REG_GET_SHADOW_PASSWD_DB:
866			{
867				// only root may see the shadow passwd
868				if (!isRoot)
869					error = EPERM;
870
871				// lazily build the reply
872				try {
873					if (error == B_OK && fShadowPwdDBReply->What() == 1) {
874						FlatStore store;
875						int32 count = fUserDB->WriteFlatShadowDB(store);
876						if (fShadowPwdDBReply->AddInt32("count", count) != B_OK
877							|| fShadowPwdDBReply->AddData("entries", B_RAW_TYPE,
878									store.Buffer(), store.BufferLength(),
879									false) != B_OK) {
880							error = B_NO_MEMORY;
881						}
882
883						fShadowPwdDBReply->SetWhat(0);
884					}
885				} catch (...) {
886					error = B_NO_MEMORY;
887				}
888
889				if (error == B_OK) {
890					message.SendReply(fShadowPwdDBReply, -1, -1, 0,
891						registrarTeam);
892				} else {
893					_InvalidateShadowPwdDBReply();
894					KMessage reply(error);
895					message.SendReply(&reply, -1, -1, 0, registrarTeam);
896				}
897
898				break;
899			}
900
901			case B_REG_GET_USER:
902			{
903				User* user = NULL;
904				int32 uid;
905				const char* name;
906
907				// find user
908				if (message.FindInt32("uid", &uid) == B_OK) {
909					user = fUserDB->UserByID(uid);
910				} else if (message.FindString("name", &name) == B_OK) {
911					user = fUserDB->UserByName(name);
912				} else {
913					error = B_BAD_VALUE;
914				}
915
916				if (error == B_OK && user == NULL)
917					error = ENOENT;
918
919				bool getShadowPwd = message.GetBool("shadow", false);
920
921				// only root may see the shadow passwd
922				if (error == B_OK && getShadowPwd && !isRoot)
923					error = EPERM;
924
925				// add user to message
926				KMessage reply;
927				if (error == B_OK)
928					error = user->WriteToMessage(reply, getShadowPwd);
929
930				// send reply
931				reply.SetWhat(error);
932				message.SendReply(&reply, -1, -1, 0, registrarTeam);
933
934				break;
935			}
936
937			case B_REG_GET_GROUP:
938			{
939				Group* group = NULL;
940				int32 gid;
941				const char* name;
942
943				// find group
944				if (message.FindInt32("gid", &gid) == B_OK) {
945					group = fGroupDB->GroupByID(gid);
946				} else if (message.FindString("name", &name) == B_OK) {
947					group = fGroupDB->GroupByName(name);
948				} else {
949					error = B_BAD_VALUE;
950				}
951
952				if (error == B_OK && group == NULL)
953					error = ENOENT;
954
955				// add group to message
956				KMessage reply;
957				if (error == B_OK)
958					error = group->WriteToMessage(reply);
959
960				// send reply
961				reply.SetWhat(error);
962				message.SendReply(&reply, -1, -1, 0, registrarTeam);
963
964				break;
965			}
966
967			case B_REG_GET_USER_GROUPS:
968			{
969				// get user name
970				const char* name;
971				int32 maxCount;
972				if (message.FindString("name", &name) != B_OK
973					|| message.FindInt32("max count", &maxCount) != B_OK
974					|| maxCount <= 0) {
975					error = B_BAD_VALUE;
976				}
977
978				// get groups
979				gid_t groups[NGROUPS_MAX + 1];
980				int32 count = 0;
981				if (error == B_OK) {
982					maxCount = min_c(maxCount, NGROUPS_MAX + 1);
983					count = fGroupDB->GetUserGroups(name, groups, maxCount);
984				}
985
986				// add groups to message
987				KMessage reply;
988				if (error == B_OK) {
989					if (reply.AddInt32("count", count) != B_OK
990						|| reply.AddData("groups", B_INT32_TYPE,
991								groups, min_c(maxCount, count) * sizeof(gid_t),
992								false) != B_OK) {
993						error = B_NO_MEMORY;
994					}
995				}
996
997				// send reply
998				reply.SetWhat(error);
999				message.SendReply(&reply, -1, -1, 0, registrarTeam);
1000
1001				break;
1002			}
1003
1004			case B_REG_UPDATE_USER:
1005			{
1006				// find user
1007				User* user = NULL;
1008				int32 uid;
1009				const char* name;
1010
1011				if (message.FindInt32("uid", &uid) == B_OK) {
1012					user = fUserDB->UserByID(uid);
1013				} else if (message.FindString("name", &name) == B_OK) {
1014					user = fUserDB->UserByName(name);
1015				} else {
1016					error = B_BAD_VALUE;
1017				}
1018
1019				// only root can change anything
1020				if (error == B_OK && !isRoot)
1021					error = EPERM;
1022
1023				// check addUser vs. existing user
1024				bool addUser = message.GetBool("add user", false);
1025				if (error == B_OK) {
1026					if (addUser) {
1027						if (user != NULL)
1028							error = EEXIST;
1029					} else if (user == NULL)
1030						error = ENOENT;
1031				}
1032
1033				// apply all changes
1034				if (error == B_OK) {
1035					// clone the user object and update it from the message
1036					User* oldUser = user;
1037					user = NULL;
1038					try {
1039						user = (oldUser != NULL ? new User(*oldUser)
1040							: new User);
1041						user->UpdateFromMessage(message);
1042
1043						// uid and name should remain the same
1044						if (oldUser != NULL) {
1045							if (oldUser->UID() != user->UID()
1046								|| oldUser->Name() != user->Name()) {
1047								error = B_BAD_VALUE;
1048							}
1049						}
1050
1051						// replace the old user and write DBs to disk
1052						if (error == B_OK) {
1053							fUserDB->AddUser(user);
1054							fUserDB->WriteToDisk();
1055							_InvalidatePasswdDBReply();
1056							_InvalidateShadowPwdDBReply();
1057						}
1058					} catch (...) {
1059						error = B_NO_MEMORY;
1060					}
1061
1062					if (error == B_OK)
1063						delete oldUser;
1064					else
1065						delete user;
1066				}
1067
1068				// send reply
1069				KMessage reply;
1070				reply.SetWhat(error);
1071				message.SendReply(&reply, -1, -1, 0, registrarTeam);
1072
1073				break;
1074			}
1075
1076			case B_REG_DELETE_USER:
1077			{
1078				// find user
1079				User* user = NULL;
1080				int32 uid;
1081				const char* name;
1082
1083				if (message.FindInt32("uid", &uid) == B_OK) {
1084					user = fUserDB->UserByID(uid);
1085				} else if (message.FindString("name", &name) == B_OK) {
1086					user = fUserDB->UserByName(name);
1087				} else {
1088					error = B_BAD_VALUE;
1089				}
1090
1091				if (error == B_OK && user == NULL)
1092					error = ENOENT;
1093
1094				// only root can change anything
1095				if (error == B_OK && !isRoot)
1096					error = EPERM;
1097
1098				// apply the change
1099				if (error == B_OK) {
1100					std::string userName = user->Name();
1101
1102					fUserDB->RemoveUser(user);
1103					fUserDB->WriteToDisk();
1104					_InvalidatePasswdDBReply();
1105					_InvalidateShadowPwdDBReply();
1106
1107					if (fGroupDB->UserRemoved(userName)) {
1108						fGroupDB->WriteToDisk();
1109						_InvalidateGroupDBReply();
1110					}
1111				}
1112
1113				// send reply
1114				KMessage reply;
1115				reply.SetWhat(error);
1116				message.SendReply(&reply, -1, -1, 0, registrarTeam);
1117
1118				break;
1119			}
1120
1121			case B_REG_UPDATE_GROUP:
1122			{
1123				// find group
1124				Group* group = NULL;
1125				int32 gid;
1126				const char* name;
1127
1128				if (message.FindInt32("gid", &gid) == B_OK) {
1129					group = fGroupDB->GroupByID(gid);
1130				} else if (message.FindString("name", &name) == B_OK) {
1131					group = fGroupDB->GroupByName(name);
1132				} else {
1133					error = B_BAD_VALUE;
1134				}
1135
1136				// only root can change anything
1137				if (error == B_OK && !isRoot)
1138					error = EPERM;
1139
1140				// check addGroup vs. existing group
1141				bool addGroup = message.GetBool("add group", false);
1142				if (error == B_OK) {
1143					if (addGroup) {
1144						if (group != NULL)
1145							error = EEXIST;
1146					} else if (group == NULL)
1147						error = ENOENT;
1148				}
1149
1150				// apply all changes
1151				if (error == B_OK) {
1152					// clone the group object and update it from the message
1153					Group* oldGroup = group;
1154					group = NULL;
1155					try {
1156						group = (oldGroup != NULL ? new Group(*oldGroup)
1157							: new Group);
1158						group->UpdateFromMessage(message);
1159
1160						// gid and name should remain the same
1161						if (oldGroup != NULL) {
1162							if (oldGroup->GID() != group->GID()
1163								|| oldGroup->Name() != group->Name()) {
1164								error = B_BAD_VALUE;
1165							}
1166						}
1167
1168						// replace the old group and write DBs to disk
1169						if (error == B_OK) {
1170							fGroupDB->AddGroup(group);
1171							fGroupDB->WriteToDisk();
1172							_InvalidateGroupDBReply();
1173						}
1174					} catch (...) {
1175						error = B_NO_MEMORY;
1176					}
1177
1178					if (error == B_OK)
1179						delete oldGroup;
1180					else
1181						delete group;
1182				}
1183
1184				// send reply
1185				KMessage reply;
1186				reply.SetWhat(error);
1187				message.SendReply(&reply, -1, -1, 0, registrarTeam);
1188
1189				break;
1190			}
1191
1192			case B_REG_DELETE_GROUP:
1193			{
1194				// find group
1195				Group* group = NULL;
1196				int32 gid;
1197				const char* name;
1198
1199				if (message.FindInt32("gid", &gid) == B_OK) {
1200					group = fGroupDB->GroupByID(gid);
1201				} else if (message.FindString("name", &name) == B_OK) {
1202					group = fGroupDB->GroupByName(name);
1203				} else {
1204					error = B_BAD_VALUE;
1205				}
1206
1207				if (error == B_OK && group == NULL)
1208					error = ENOENT;
1209
1210				// only root can change anything
1211				if (error == B_OK && !isRoot)
1212					error = EPERM;
1213
1214				// apply the change
1215				if (error == B_OK) {
1216					fGroupDB->RemoveGroup(group);
1217					fGroupDB->WriteToDisk();
1218					_InvalidateGroupDBReply();
1219				}
1220
1221				// send reply
1222				KMessage reply;
1223				reply.SetWhat(error);
1224				message.SendReply(&reply, -1, -1, 0, registrarTeam);
1225
1226				break;
1227			}
1228
1229			default:
1230				debug_printf("REG: invalid message: %" B_PRIu32 "\n",
1231					message.What());
1232		}
1233	}
1234}
1235
1236
1237status_t
1238AuthenticationManager::_InitPasswdDB()
1239{
1240	FileCloser file(fopen(kPasswdFile, "r"));
1241	if (!file.IsSet()) {
1242		debug_printf("REG: Failed to open passwd DB file \"%s\": %s\n",
1243			kPasswdFile, strerror(errno));
1244		return errno;
1245	}
1246
1247	char lineBuffer[LINE_MAX];
1248	while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file.Get())) {
1249		if (strlen(line) == 0)
1250			continue;
1251
1252		char* name;
1253		char* password;
1254		uid_t uid;
1255		gid_t gid;
1256		char* home;
1257		char* shell;
1258		char* realName;
1259
1260		status_t error = parse_passwd_line(line, name, password, uid, gid,
1261			home, shell, realName);
1262		if (error != B_OK) {
1263			debug_printf("REG: Unparsable line in passwd DB file: \"%s\"\n",
1264				strerror(errno));
1265			continue;
1266		}
1267
1268		User* user = NULL;
1269		try {
1270			user = new User(name, password, uid, gid, home, shell, realName);
1271		} catch (...) {
1272		}
1273
1274		if (user == NULL || fUserDB->AddUser(user) != B_OK) {
1275			delete user;
1276			debug_printf("REG: Out of memory\n");
1277			return B_NO_MEMORY;
1278		}
1279	}
1280
1281	return B_OK;
1282}
1283
1284
1285status_t
1286AuthenticationManager::_InitGroupDB()
1287{
1288	FileCloser file(fopen(kGroupFile, "r"));
1289	if (!file.IsSet()) {
1290		debug_printf("REG: Failed to open group DB file \"%s\": %s\n",
1291			kGroupFile, strerror(errno));
1292		return errno;
1293	}
1294
1295	char lineBuffer[LINE_MAX];
1296	while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file.Get())) {
1297		if (strlen(line) == 0)
1298			continue;
1299
1300		char* name;
1301		char* password;
1302		gid_t gid;
1303		char* members[MAX_GROUP_MEMBER_COUNT];
1304		int memberCount;
1305
1306
1307		status_t error = parse_group_line(line, name, password, gid, members,
1308			memberCount);
1309		if (error != B_OK) {
1310			debug_printf("REG: Unparsable line in group DB file: \"%s\"\n",
1311				strerror(errno));
1312			continue;
1313		}
1314
1315		Group* group = NULL;
1316		try {
1317			group = new Group(name, password, gid, members, memberCount);
1318		} catch (...) {
1319		}
1320
1321		if (group == NULL || fGroupDB->AddGroup(group) != B_OK) {
1322			delete group;
1323			debug_printf("REG: Out of memory\n");
1324			return B_NO_MEMORY;
1325		}
1326	}
1327
1328	return B_OK;
1329}
1330
1331
1332status_t
1333AuthenticationManager::_InitShadowPwdDB()
1334{
1335	FileCloser file(fopen(kShadowPwdFile, "r"));
1336	if (!file.IsSet()) {
1337		debug_printf("REG: Failed to open shadow passwd DB file \"%s\": %s\n",
1338			kShadowPwdFile, strerror(errno));
1339		return errno;
1340	}
1341
1342	char lineBuffer[LINE_MAX];
1343	while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file.Get())) {
1344		if (strlen(line) == 0)
1345			continue;
1346
1347		char* name;
1348		char* password;
1349		int lastChanged;
1350		int min;
1351		int max;
1352		int warn;
1353		int inactive;
1354		int expiration;
1355		int flags;
1356
1357		status_t error = parse_shadow_pwd_line(line, name, password,
1358			lastChanged, min, max, warn, inactive, expiration, flags);
1359		if (error != B_OK) {
1360			debug_printf("REG: Unparsable line in shadow passwd DB file: "
1361				"\"%s\"\n", strerror(errno));
1362			continue;
1363		}
1364
1365		User* user = fUserDB->UserByName(name);
1366		if (user == NULL) {
1367			debug_printf("REG: shadow pwd entry for unknown user \"%s\"\n",
1368				name);
1369			continue;
1370		}
1371
1372		try {
1373			user->SetShadowInfo(password, lastChanged, min, max, warn, inactive,
1374				expiration, flags);
1375		} catch (...) {
1376			debug_printf("REG: Out of memory\n");
1377			return B_NO_MEMORY;
1378		}
1379	}
1380
1381	return B_OK;
1382}
1383
1384
1385void
1386AuthenticationManager::_InvalidatePasswdDBReply()
1387{
1388	fPasswdDBReply->SetTo(1);
1389}
1390
1391
1392void
1393AuthenticationManager::_InvalidateGroupDBReply()
1394{
1395	fGroupDBReply->SetTo(1);
1396}
1397
1398
1399void
1400AuthenticationManager::_InvalidateShadowPwdDBReply()
1401{
1402	fShadowPwdDBReply->SetTo(1);
1403}
1404