vfs: check userland buffers before reading them.
[haiku.git] / src / servers / registrar / AuthenticationManager.cpp
blobf8fa1072c3c8b0e09dd8c9d42cab2436098e6b72
1 /*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "AuthenticationManager.h"
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <sys/param.h>
14 #include <map>
15 #include <new>
16 #include <set>
17 #include <string>
19 #include <DataIO.h>
20 #include <StringList.h>
22 #include <AutoDeleter.h>
23 #include <LaunchRoster.h>
24 #include <RegistrarDefs.h>
26 #include <libroot_private.h>
27 #include <user_group.h>
28 #include <util/KMessage.h>
31 using std::map;
32 using std::string;
34 using namespace BPrivate;
37 typedef std::set<std::string> StringSet;
40 class AuthenticationManager::FlatStore {
41 public:
42 FlatStore()
43 : fSize(0)
45 fBuffer.SetBlockSize(1024);
48 void WriteData(size_t offset, const void* data, size_t length)
50 ssize_t result = fBuffer.WriteAt(offset, data, length);
51 if (result < 0)
52 throw status_t(result);
55 template<typename Type>
56 void WriteData(size_t offset, const Type& data)
58 WriteData(&data, sizeof(Type));
61 size_t ReserveSpace(size_t length, bool align)
63 if (align)
64 fSize = _ALIGN(fSize);
66 size_t pos = fSize;
67 fSize += length;
69 return pos;
72 void* AppendData(const void* data, size_t length, bool align)
74 size_t pos = ReserveSpace(length, align);
75 WriteData(pos, data, length);
76 return (void*)(addr_t)pos;
79 template<typename Type>
80 Type* AppendData(const Type& data)
82 return (Type*)AppendData(&data, sizeof(Type), true);
85 char* AppendString(const char* string)
87 return (char*)AppendData(string, strlen(string) + 1, false);
90 char* AppendString(const string& str)
92 return (char*)AppendData(str.c_str(), str.length() + 1, false);
95 const void* Buffer() const
97 return fBuffer.Buffer();
100 size_t BufferLength() const
102 return fSize;
105 private:
106 BMallocIO fBuffer;
107 size_t fSize;
111 class AuthenticationManager::User {
112 public:
113 User()
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)
127 User(const char* name, const char* password, uid_t uid, gid_t gid,
128 const char* home, const char* shell, const char* realName)
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)
147 User(const User& other)
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)
167 const string& Name() const { return fName; }
168 const uid_t UID() const { return fUID; }
170 void SetShadowInfo(const char* password, int lastChanged, int min, int max,
171 int warn, int inactive, int expiration, int flags)
173 fShadowPassword = password;
174 fLastChanged = lastChanged;
175 fMin = min;
176 fMax = max;
177 fWarn = warn;
178 fInactive = inactive;
179 fExpiration = expiration;
180 fFlags = flags;
183 void UpdateFromMessage(const KMessage& message)
185 int32 intValue;
186 const char* stringValue;
188 if (message.FindInt32("uid", &intValue) == B_OK)
189 fUID = intValue;
191 if (message.FindInt32("gid", &intValue) == B_OK)
192 fGID = intValue;
194 if (message.FindString("name", &stringValue) == B_OK)
195 fName = stringValue;
197 if (message.FindString("password", &stringValue) == B_OK)
198 fPassword = stringValue;
200 if (message.FindString("home", &stringValue) == B_OK)
201 fHome = stringValue;
203 if (message.FindString("shell", &stringValue) == B_OK)
204 fShell = stringValue;
206 if (message.FindString("real name", &stringValue) == B_OK)
207 fRealName = stringValue;
209 if (message.FindString("shadow password", &stringValue) == B_OK) {
210 fShadowPassword = stringValue;
211 // TODO:
212 // fLastChanged = now;
215 if (message.FindInt32("last changed", &intValue) == B_OK)
216 fLastChanged = intValue;
218 if (message.FindInt32("min", &intValue) == B_OK)
219 fMin = intValue;
221 if (message.FindInt32("max", &intValue) == B_OK)
222 fMax = intValue;
224 if (message.FindInt32("warn", &intValue) == B_OK)
225 fWarn = intValue;
227 if (message.FindInt32("inactive", &intValue) == B_OK)
228 fInactive = intValue;
230 if (message.FindInt32("expiration", &intValue) == B_OK)
231 fExpiration = intValue;
233 if (message.FindInt32("flags", &intValue) == B_OK)
234 fFlags = intValue;
237 passwd* WriteFlatPasswd(FlatStore& store) const
239 struct passwd passwd;
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);
249 return store.AppendData(passwd);
252 spwd* WriteFlatShadowPwd(FlatStore& store) const
254 struct spwd spwd;
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;
266 return store.AppendData(spwd);
269 status_t WriteToMessage(KMessage& message, bool addShadowPwd)
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;
284 if (!addShadowPwd)
285 return B_OK;
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;
299 return B_OK;
302 void WritePasswdLine(FILE* file)
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());
309 void WriteShadowPwdLine(FILE* file)
311 fprintf(file, "%s:%s:%d:", fName.c_str(), fShadowPassword.c_str(),
312 fLastChanged);
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, ":");
323 fprintf(file, "%d\n", fFlags);
326 private:
327 uid_t fUID;
328 gid_t fGID;
329 string fName;
330 string fPassword;
331 string fHome;
332 string fShell;
333 string fRealName;
334 string fShadowPassword;
335 int fLastChanged;
336 int fMin;
337 int fMax;
338 int fWarn;
339 int fInactive;
340 int fExpiration;
341 int fFlags;
345 class AuthenticationManager::Group {
346 public:
347 Group()
349 fGID(0),
350 fName(),
351 fPassword(),
352 fMembers()
356 Group(const char* name, const char* password, gid_t gid,
357 const char* const* members, int memberCount)
359 fGID(gid),
360 fName(name),
361 fPassword(password),
362 fMembers()
364 for (int i = 0; i < memberCount; i++)
365 fMembers.insert(members[i]);
368 ~Group()
372 const string& Name() const { return fName; }
373 const gid_t GID() const { return fGID; }
375 bool HasMember(const char* name)
377 try {
378 return fMembers.find(name) != fMembers.end();
379 } catch (...) {
380 return false;
384 bool MemberRemoved(const std::string& name)
386 return fMembers.erase(name) > 0;
389 void UpdateFromMessage(const KMessage& message)
391 int32 intValue;
392 if (message.FindInt32("gid", &intValue) == B_OK)
393 fGID = intValue;
395 const char* stringValue;
396 if (message.FindString("name", &stringValue) == B_OK)
397 fName = stringValue;
399 if (message.FindString("password", &stringValue) == B_OK)
400 fPassword = stringValue;
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);
413 group* WriteFlatGroup(FlatStore& store) const
415 struct group group;
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());
423 members[count] = (char*)-1;
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);
431 return store.AppendData(group);
434 status_t WriteToMessage(KMessage& message)
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;
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;
450 return B_OK;
453 void WriteGroupLine(FILE* file)
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());
464 fputs("\n", file);
467 private:
468 gid_t fGID;
469 string fName;
470 string fPassword;
471 StringSet fMembers;
475 class AuthenticationManager::UserDB {
476 public:
477 status_t AddUser(User* user)
479 try {
480 fUsersByID[user->UID()] = user;
481 } catch (...) {
482 return B_NO_MEMORY;
485 try {
486 fUsersByName[user->Name()] = user;
487 } catch (...) {
488 fUsersByID.erase(fUsersByID.find(user->UID()));
489 return B_NO_MEMORY;
492 return B_OK;
495 void RemoveUser(User* user)
497 fUsersByID.erase(fUsersByID.find(user->UID()));
498 fUsersByName.erase(fUsersByName.find(user->Name()));
501 User* UserByID(uid_t uid) const
503 map<uid_t, User*>::const_iterator it = fUsersByID.find(uid);
504 return (it == fUsersByID.end() ? NULL : it->second);
507 User* UserByName(const char* name) const
509 map<string, User*>::const_iterator it = fUsersByName.find(name);
510 return (it == fUsersByName.end() ? NULL : it->second);
513 int32 WriteFlatPasswdDB(FlatStore& store) const
515 int32 count = fUsersByID.size();
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);
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);
528 store.WriteData(offset, entries, entriesSpace);
530 return count;
533 int32 WriteFlatShadowDB(FlatStore& store) const
535 int32 count = fUsersByID.size();
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);
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);
548 store.WriteData(offset, entries, entriesSpace);
550 return count;
553 void WriteToDisk()
555 // rename the old files
556 string passwdBackup(kPasswdFile);
557 string shadowBackup(kShadowPwdFile);
558 passwdBackup += ".old";
559 shadowBackup += ".old";
561 rename(kPasswdFile, passwdBackup.c_str());
562 rename(kShadowPwdFile, shadowBackup.c_str());
563 // Don't check errors. We can't do anything anyway.
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));
571 CObjectDeleter<FILE, int> _1(passwdFile, fclose);
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));
578 CObjectDeleter<FILE, int> _2(shadowFile, fclose);
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);
589 private:
590 map<uid_t, User*> fUsersByID;
591 map<string, User*> fUsersByName;
595 class AuthenticationManager::GroupDB {
596 public:
597 status_t AddGroup(Group* group)
599 try {
600 fGroupsByID[group->GID()] = group;
601 } catch (...) {
602 return B_NO_MEMORY;
605 try {
606 fGroupsByName[group->Name()] = group;
607 } catch (...) {
608 fGroupsByID.erase(fGroupsByID.find(group->GID()));
609 return B_NO_MEMORY;
612 return B_OK;
615 void RemoveGroup(Group* group)
617 fGroupsByID.erase(fGroupsByID.find(group->GID()));
618 fGroupsByName.erase(fGroupsByName.find(group->Name()));
621 bool UserRemoved(const std::string& user)
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);
629 return changed;
632 Group* GroupByID(gid_t gid) const
634 map<gid_t, Group*>::const_iterator it = fGroupsByID.find(gid);
635 return (it == fGroupsByID.end() ? NULL : it->second);
638 Group* GroupByName(const char* name) const
640 map<string, Group*>::const_iterator it = fGroupsByName.find(name);
641 return (it == fGroupsByName.end() ? NULL : it->second);
644 int32 GetUserGroups(const char* name, gid_t* groups, int maxCount)
646 int count = 0;
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++;
658 return count;
662 int32 WriteFlatGroupDB(FlatStore& store) const
664 int32 count = fGroupsByID.size();
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);
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);
677 store.WriteData(offset, entries, entriesSpace);
679 return count;
682 void WriteToDisk()
684 // rename the old files
685 string groupBackup(kGroupFile);
686 groupBackup += ".old";
688 rename(kGroupFile, groupBackup.c_str());
689 // Don't check errors. We can't do anything anyway.
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));
697 CObjectDeleter<FILE, int> _1(groupFile, fclose);
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);
707 private:
708 map<uid_t, Group*> fGroupsByID;
709 map<string, Group*> fGroupsByName;
713 AuthenticationManager::AuthenticationManager()
715 fRequestPort(-1),
716 fRequestThread(-1),
717 fUserDB(NULL),
718 fGroupDB(NULL),
719 fPasswdDBReply(NULL),
720 fGroupDBReply(NULL),
721 fShadowPwdDBReply(NULL)
726 AuthenticationManager::~AuthenticationManager()
728 // Quit the request thread and wait for it to finish
729 write_port(fRequestPort, 'quit', NULL, 0);
730 wait_for_thread(fRequestThread, NULL);
732 delete fUserDB;
733 delete fGroupDB;
734 delete fPasswdDBReply;
735 delete fGroupDBReply;
736 delete fShadowPwdDBReply;
740 status_t
741 AuthenticationManager::Init()
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);
749 if (fUserDB == NULL || fGroupDB == NULL || fPasswdDBReply == NULL
750 || fGroupDBReply == NULL || fShadowPwdDBReply == NULL) {
751 return B_NO_MEMORY;
754 fRequestPort = BLaunchRoster().GetPort(
755 B_REGISTRAR_AUTHENTICATION_PORT_NAME);
756 if (fRequestPort < 0)
757 return fRequestPort;
759 fRequestThread = spawn_thread(&_RequestThreadEntry,
760 "authentication manager", B_NORMAL_PRIORITY + 1, this);
761 if (fRequestThread < 0)
762 return fRequestThread;
764 resume_thread(fRequestThread);
766 return B_OK;
770 status_t
771 AuthenticationManager::_RequestThreadEntry(void* data)
773 return ((AuthenticationManager*)data)->_RequestThread();
777 status_t
778 AuthenticationManager::_RequestThread()
780 // read the DB files
781 _InitPasswdDB();
782 _InitGroupDB();
783 _InitShadowPwdDB();
785 // get our team ID
786 team_id registrarTeam = -1;
788 thread_info info;
789 if (get_thread_info(find_thread(NULL), &info) == B_OK)
790 registrarTeam = info.team;
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;
801 bool isRoot = (messageInfo.sender == 0);
803 switch (message.What()) {
804 case B_REG_GET_PASSWD_DB:
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;
818 fPasswdDBReply->SetWhat(0);
820 } catch (...) {
821 error = B_NO_MEMORY;
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);
832 break;
835 case B_REG_GET_GROUP_DB:
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;
849 fGroupDBReply->SetWhat(0);
851 } catch (...) {
852 error = B_NO_MEMORY;
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);
863 break;
867 case B_REG_GET_SHADOW_PASSWD_DB:
869 // only root may see the shadow passwd
870 if (!isRoot)
871 error = EPERM;
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;
885 fShadowPwdDBReply->SetWhat(0);
887 } catch (...) {
888 error = B_NO_MEMORY;
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);
900 break;
903 case B_REG_GET_USER:
905 User* user = NULL;
906 int32 uid;
907 const char* name;
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;
918 if (error == B_OK && user == NULL)
919 error = ENOENT;
921 bool getShadowPwd = message.GetBool("shadow", false);
923 // only root may see the shadow passwd
924 if (error == B_OK && getShadowPwd && !isRoot)
925 error = EPERM;
927 // add user to message
928 KMessage reply;
929 if (error == B_OK)
930 error = user->WriteToMessage(reply, getShadowPwd);
932 // send reply
933 reply.SetWhat(error);
934 message.SendReply(&reply, -1, -1, 0, registrarTeam);
936 break;
939 case B_REG_GET_GROUP:
941 Group* group = NULL;
942 int32 gid;
943 const char* name;
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;
954 if (error == B_OK && group == NULL)
955 error = ENOENT;
957 // add group to message
958 KMessage reply;
959 if (error == B_OK)
960 error = group->WriteToMessage(reply);
962 // send reply
963 reply.SetWhat(error);
964 message.SendReply(&reply, -1, -1, 0, registrarTeam);
966 break;
969 case B_REG_GET_USER_GROUPS:
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;
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);
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;
999 // send reply
1000 reply.SetWhat(error);
1001 message.SendReply(&reply, -1, -1, 0, registrarTeam);
1003 break;
1006 case B_REG_UPDATE_USER:
1008 // find user
1009 User* user = NULL;
1010 int32 uid;
1011 const char* name;
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;
1021 // only root can change anything
1022 if (error == B_OK && !isRoot)
1023 error = EPERM;
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;
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);
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;
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();
1060 } catch (...) {
1061 error = B_NO_MEMORY;
1064 if (error == B_OK)
1065 delete oldUser;
1066 else
1067 delete user;
1070 // send reply
1071 KMessage reply;
1072 reply.SetWhat(error);
1073 message.SendReply(&reply, -1, -1, 0, registrarTeam);
1075 break;
1078 case B_REG_DELETE_USER:
1080 // find user
1081 User* user = NULL;
1082 int32 uid;
1083 const char* name;
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;
1093 if (error == B_OK && user == NULL)
1094 error = ENOENT;
1096 // only root can change anything
1097 if (error == B_OK && !isRoot)
1098 error = EPERM;
1100 // apply the change
1101 if (error == B_OK) {
1102 std::string userName = user->Name();
1104 fUserDB->RemoveUser(user);
1105 fUserDB->WriteToDisk();
1106 _InvalidatePasswdDBReply();
1107 _InvalidateShadowPwdDBReply();
1109 if (fGroupDB->UserRemoved(userName)) {
1110 fGroupDB->WriteToDisk();
1111 _InvalidateGroupDBReply();
1115 // send reply
1116 KMessage reply;
1117 reply.SetWhat(error);
1118 message.SendReply(&reply, -1, -1, 0, registrarTeam);
1120 break;
1123 case B_REG_UPDATE_GROUP:
1125 // find group
1126 Group* group = NULL;
1127 int32 gid;
1128 const char* name;
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;
1138 // only root can change anything
1139 if (error == B_OK && !isRoot)
1140 error = EPERM;
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;
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);
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;
1170 // replace the old group and write DBs to disk
1171 if (error == B_OK) {
1172 fGroupDB->AddGroup(group);
1173 fGroupDB->WriteToDisk();
1174 _InvalidateGroupDBReply();
1176 } catch (...) {
1177 error = B_NO_MEMORY;
1180 if (error == B_OK)
1181 delete oldGroup;
1182 else
1183 delete group;
1186 // send reply
1187 KMessage reply;
1188 reply.SetWhat(error);
1189 message.SendReply(&reply, -1, -1, 0, registrarTeam);
1191 break;
1194 case B_REG_DELETE_GROUP:
1196 // find group
1197 Group* group = NULL;
1198 int32 gid;
1199 const char* name;
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;
1209 if (error == B_OK && group == NULL)
1210 error = ENOENT;
1212 // only root can change anything
1213 if (error == B_OK && !isRoot)
1214 error = EPERM;
1216 // apply the change
1217 if (error == B_OK) {
1218 fGroupDB->RemoveGroup(group);
1219 fGroupDB->WriteToDisk();
1220 _InvalidateGroupDBReply();
1223 // send reply
1224 KMessage reply;
1225 reply.SetWhat(error);
1226 message.SendReply(&reply, -1, -1, 0, registrarTeam);
1228 break;
1231 default:
1232 debug_printf("REG: invalid message: %" B_PRIu32 "\n",
1233 message.What());
1239 status_t
1240 AuthenticationManager::_InitPasswdDB()
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;
1248 CObjectDeleter<FILE, int> _(file, fclose);
1250 char lineBuffer[LINE_MAX];
1251 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) {
1252 if (strlen(line) == 0)
1253 continue;
1255 char* name;
1256 char* password;
1257 uid_t uid;
1258 gid_t gid;
1259 char* home;
1260 char* shell;
1261 char* realName;
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;
1271 User* user = NULL;
1272 try {
1273 user = new User(name, password, uid, gid, home, shell, realName);
1274 } catch (...) {
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;
1284 return B_OK;
1288 status_t
1289 AuthenticationManager::_InitGroupDB()
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;
1297 CObjectDeleter<FILE, int> _(file, fclose);
1299 char lineBuffer[LINE_MAX];
1300 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) {
1301 if (strlen(line) == 0)
1302 continue;
1304 char* name;
1305 char* password;
1306 gid_t gid;
1307 char* members[MAX_GROUP_MEMBER_COUNT];
1308 int memberCount;
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;
1319 Group* group = NULL;
1320 try {
1321 group = new Group(name, password, gid, members, memberCount);
1322 } catch (...) {
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;
1332 return B_OK;
1336 status_t
1337 AuthenticationManager::_InitShadowPwdDB()
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;
1345 CObjectDeleter<FILE, int> _(file, fclose);
1347 char lineBuffer[LINE_MAX];
1348 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) {
1349 if (strlen(line) == 0)
1350 continue;
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;
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;
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;
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;
1386 return B_OK;
1390 void
1391 AuthenticationManager::_InvalidatePasswdDBReply()
1393 fPasswdDBReply->SetTo(1);
1397 void
1398 AuthenticationManager::_InvalidateGroupDBReply()
1400 fGroupDBReply->SetTo(1);
1404 void
1405 AuthenticationManager::_InvalidateShadowPwdDBReply()
1407 fShadowPwdDBReply->SetTo(1);