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
31using std::map;
32using std::string;
33
34using namespace BPrivate;
35
36
37typedef std::set<std::string> StringSet;
38
39
40class AuthenticationManager::FlatStore {
41public:
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
105private:
106	BMallocIO	fBuffer;
107	size_t		fSize;
108};
109
110
111class AuthenticationManager::User {
112public:
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
326private:
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
345class AuthenticationManager::Group {
346public:
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
467private:
468	gid_t		fGID;
469	string		fName;
470	string		fPassword;
471	StringSet	fMembers;
472};
473
474
475class AuthenticationManager::UserDB {
476public:
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
589private:
590	map<uid_t, User*>	fUsersByID;
591	map<string, User*>	fUsersByName;
592};
593
594
595class AuthenticationManager::GroupDB {
596public:
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
707private:
708	map<uid_t, Group*>	fGroupsByID;
709	map<string, Group*>	fGroupsByName;
710};
711
712
713AuthenticationManager::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
726AuthenticationManager::~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
740status_t
741AuthenticationManager::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
770status_t
771AuthenticationManager::_RequestThreadEntry(void* data)
772{
773	return ((AuthenticationManager*)data)->_RequestThread();
774}
775
776
777status_t
778AuthenticationManager::_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
1239status_t
1240AuthenticationManager::_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
1288status_t
1289AuthenticationManager::_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
1336status_t
1337AuthenticationManager::_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
1390void
1391AuthenticationManager::_InvalidatePasswdDBReply()
1392{
1393	fPasswdDBReply->SetTo(1);
1394}
1395
1396
1397void
1398AuthenticationManager::_InvalidateGroupDBReply()
1399{
1400	fGroupDBReply->SetTo(1);
1401}
1402
1403
1404void
1405AuthenticationManager::_InvalidateShadowPwdDBReply()
1406{
1407	fShadowPwdDBReply->SetTo(1);
1408}
1409