xref: /haiku/src/servers/registrar/AuthenticationManager.cpp (revision 46b7da1f4f40f7157d74fc7fb26ff9ec7f2416f2)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "AuthenticationManager.h"
8 
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <sys/param.h>
13 
14 #include <map>
15 #include <new>
16 #include <set>
17 #include <string>
18 
19 #include <DataIO.h>
20 #include <StringList.h>
21 
22 #include <AutoDeleter.h>
23 #include <AutoDeleterPosix.h>
24 #include <LaunchRoster.h>
25 #include <RegistrarDefs.h>
26 
27 #include <libroot_private.h>
28 #include <user_group.h>
29 #include <util/KMessage.h>
30 
31 
32 using std::map;
33 using std::string;
34 
35 using namespace BPrivate;
36 
37 
38 typedef std::set<std::string> StringSet;
39 
40 
41 class AuthenticationManager::FlatStore {
42 public:
FlatStore()43 	FlatStore()
44 		: fSize(0)
45 	{
46 		fBuffer.SetBlockSize(1024);
47 	}
48 
WriteData(size_t offset,const void * data,size_t length)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>
WriteData(size_t offset,const Type & data)57 	void WriteData(size_t offset, const Type& data)
58 	{
59 		WriteData(&data, sizeof(Type));
60 	}
61 
ReserveSpace(size_t length,bool align)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 
AppendData(const void * data,size_t length,bool align)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>
AppendData(const Type & data)81 	Type* AppendData(const Type& data)
82 	{
83 		return (Type*)AppendData(&data, sizeof(Type), true);
84 	}
85 
AppendString(const char * string)86 	char* AppendString(const char* string)
87 	{
88 		return (char*)AppendData(string, strlen(string) + 1, false);
89 	}
90 
AppendString(const string & str)91 	char* AppendString(const string& str)
92 	{
93 		return (char*)AppendData(str.c_str(), str.length() + 1, false);
94 	}
95 
Buffer() const96 	const void* Buffer() const
97 	{
98 		return fBuffer.Buffer();
99 	}
100 
BufferLength() const101 	size_t BufferLength() const
102 	{
103 		return fSize;
104 	}
105 
106 private:
107 	BMallocIO	fBuffer;
108 	size_t		fSize;
109 };
110 
111 
112 class AuthenticationManager::User {
113 public:
User()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 
User(const char * name,const char * password,uid_t uid,gid_t gid,const char * home,const char * shell,const char * realName)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 
User(const User & other)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 
Name() const168 	const string& Name() const	{ return fName; }
UID() const169 	const uid_t UID() const		{ return fUID; }
170 
SetShadowInfo(const char * password,int lastChanged,int min,int max,int warn,int inactive,int expiration,int flags)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 
UpdateFromMessage(const KMessage & message)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 
WriteFlatPasswd(FlatStore & store) const238 	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 
WriteFlatShadowPwd(FlatStore & store) const253 	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 
WriteToMessage(KMessage & message,bool addShadowPwd)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 
WritePasswdLine(FILE * file)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 
WriteShadowPwdLine(FILE * file)310 	void WriteShadowPwdLine(FILE* file)
311 	{
312 		fprintf(file, "%s:%s:%d:", fName.c_str(), fShadowPassword.c_str(),
313 			fLastChanged);
314 
315 		// The following values are supposed to be printed as empty strings,
316 		// if negative.
317 		int values[5] = { fMin, fMax, fWarn, fInactive, fExpiration };
318 		for (int i = 0; i < 5; i++) {
319 			if (values[i] >= 0)
320 				fprintf(file, "%d", values[i]);
321 			fprintf(file, ":");
322 		}
323 
324 		fprintf(file, "%d\n", fFlags);
325 	}
326 
327 private:
328 	uid_t	fUID;
329 	gid_t	fGID;
330 	string	fName;
331 	string	fPassword;
332 	string	fHome;
333 	string	fShell;
334 	string	fRealName;
335 	string	fShadowPassword;
336 	int		fLastChanged;
337 	int		fMin;
338 	int		fMax;
339 	int		fWarn;
340 	int		fInactive;
341 	int		fExpiration;
342 	int		fFlags;
343 };
344 
345 
346 class AuthenticationManager::Group {
347 public:
Group()348 	Group()
349 		:
350 		fGID(0),
351 		fName(),
352 		fPassword(),
353 		fMembers()
354 	{
355 	}
356 
Group(const char * name,const char * password,gid_t gid,const char * const * members,int memberCount)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 
~Group()369 	~Group()
370 	{
371 	}
372 
Name() const373 	const string& Name() const	{ return fName; }
GID() const374 	const gid_t GID() const		{ return fGID; }
375 
HasMember(const char * name)376 	bool HasMember(const char* name)
377 	{
378 		try {
379 			return fMembers.find(name) != fMembers.end();
380 		} catch (...) {
381 			return false;
382 		}
383 	}
384 
MemberRemoved(const std::string & name)385 	bool MemberRemoved(const std::string& name)
386 	{
387 		return fMembers.erase(name) > 0;
388 	}
389 
UpdateFromMessage(const KMessage & message)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 
WriteFlatGroup(FlatStore & store) const414 	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 
WriteToMessage(KMessage & message)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 
WriteGroupLine(FILE * file)454 	void WriteGroupLine(FILE* file)
455 	{
456 		fprintf(file, "%s:%s:%d:",
457 			fName.c_str(), fPassword.c_str(), (int)fGID);
458 		for (StringSet::const_iterator it = fMembers.begin();
459 			it != fMembers.end(); ++it) {
460 			if (it == fMembers.begin())
461 				fprintf(file, "%s", it->c_str());
462 			else
463 				fprintf(file, ",%s", it->c_str());
464 		}
465 		fputs("\n", file);
466 	}
467 
468 private:
469 	gid_t		fGID;
470 	string		fName;
471 	string		fPassword;
472 	StringSet	fMembers;
473 };
474 
475 
476 class AuthenticationManager::UserDB {
477 public:
AddUser(User * user)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 
RemoveUser(User * user)496 	void RemoveUser(User* user)
497 	{
498 		fUsersByID.erase(fUsersByID.find(user->UID()));
499 		fUsersByName.erase(fUsersByName.find(user->Name()));
500 	}
501 
UserByID(uid_t uid) const502 	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 
UserByName(const char * name) const508 	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 
WriteFlatPasswdDB(FlatStore & store) const514 	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 
WriteFlatShadowDB(FlatStore & store) const534 	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 
WriteToDisk()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 
588 private:
589 	map<uid_t, User*>	fUsersByID;
590 	map<string, User*>	fUsersByName;
591 };
592 
593 
594 class AuthenticationManager::GroupDB {
595 public:
AddGroup(Group * group)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 
RemoveGroup(Group * group)614 	void RemoveGroup(Group* group)
615 	{
616 		fGroupsByID.erase(fGroupsByID.find(group->GID()));
617 		fGroupsByName.erase(fGroupsByName.find(group->Name()));
618 	}
619 
UserRemoved(const std::string & user)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 
GroupByID(gid_t gid) const631 	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 
GroupByName(const char * name) const637 	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 
GetUserGroups(const char * name,gid_t * groups,int maxCount)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 
WriteFlatGroupDB(FlatStore & store) const661 	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 
WriteToDisk()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 
705 private:
706 	map<uid_t, Group*>	fGroupsByID;
707 	map<string, Group*>	fGroupsByName;
708 };
709 
710 
AuthenticationManager()711 AuthenticationManager::AuthenticationManager()
712 	:
713 	fRequestPort(-1),
714 	fRequestThread(-1),
715 	fUserDB(NULL),
716 	fGroupDB(NULL),
717 	fPasswdDBReply(NULL),
718 	fGroupDBReply(NULL),
719 	fShadowPwdDBReply(NULL)
720 {
721 }
722 
723 
~AuthenticationManager()724 AuthenticationManager::~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 
738 status_t
Init()739 AuthenticationManager::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 
768 status_t
_RequestThreadEntry(void * data)769 AuthenticationManager::_RequestThreadEntry(void* data)
770 {
771 	return ((AuthenticationManager*)data)->_RequestThread();
772 }
773 
774 
775 status_t
_RequestThread()776 AuthenticationManager::_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 
1237 status_t
_InitPasswdDB()1238 AuthenticationManager::_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 
1285 status_t
_InitGroupDB()1286 AuthenticationManager::_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 
1332 status_t
_InitShadowPwdDB()1333 AuthenticationManager::_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 
1385 void
_InvalidatePasswdDBReply()1386 AuthenticationManager::_InvalidatePasswdDBReply()
1387 {
1388 	fPasswdDBReply->SetTo(1);
1389 }
1390 
1391 
1392 void
_InvalidateGroupDBReply()1393 AuthenticationManager::_InvalidateGroupDBReply()
1394 {
1395 	fGroupDBReply->SetTo(1);
1396 }
1397 
1398 
1399 void
_InvalidateShadowPwdDBReply()1400 AuthenticationManager::_InvalidateShadowPwdDBReply()
1401 {
1402 	fShadowPwdDBReply->SetTo(1);
1403 }
1404