/* * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All Rights Reserved. * Distributed under the terms of the MIT License. */ #include "AuthenticationManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using std::map; using std::string; using namespace BPrivate; typedef std::set StringSet; class AuthenticationManager::FlatStore { public: FlatStore() : fSize(0) { fBuffer.SetBlockSize(1024); } void WriteData(size_t offset, const void* data, size_t length) { ssize_t result = fBuffer.WriteAt(offset, data, length); if (result < 0) throw status_t(result); } template void WriteData(size_t offset, const Type& data) { WriteData(&data, sizeof(Type)); } size_t ReserveSpace(size_t length, bool align) { if (align) fSize = _ALIGN(fSize); size_t pos = fSize; fSize += length; return pos; } void* AppendData(const void* data, size_t length, bool align) { size_t pos = ReserveSpace(length, align); WriteData(pos, data, length); return (void*)(addr_t)pos; } template Type* AppendData(const Type& data) { return (Type*)AppendData(&data, sizeof(Type), true); } char* AppendString(const char* string) { return (char*)AppendData(string, strlen(string) + 1, false); } char* AppendString(const string& str) { return (char*)AppendData(str.c_str(), str.length() + 1, false); } const void* Buffer() const { return fBuffer.Buffer(); } size_t BufferLength() const { return fSize; } private: BMallocIO fBuffer; size_t fSize; }; class AuthenticationManager::User { public: User() : fUID(0), fGID(0), fLastChanged(0), fMin(-1), fMax(-1), fWarn(-1), fInactive(-1), fExpiration(-1), fFlags(0) { } User(const char* name, const char* password, uid_t uid, gid_t gid, const char* home, const char* shell, const char* realName) : fUID(uid), fGID(gid), fName(name), fPassword(password), fHome(home), fShell(shell), fRealName(realName), fLastChanged(0), fMin(-1), fMax(-1), fWarn(-1), fInactive(-1), fExpiration(-1), fFlags(0) { } User(const User& other) : fUID(other.fUID), fGID(other.fGID), fName(other.fName), fPassword(other.fPassword), fHome(other.fHome), fShell(other.fShell), fRealName(other.fRealName), fShadowPassword(other.fShadowPassword), fLastChanged(other.fLastChanged), fMin(other.fMin), fMax(other.fMax), fWarn(other.fWarn), fInactive(other.fInactive), fExpiration(other.fExpiration), fFlags(other.fFlags) { } const string& Name() const { return fName; } const uid_t UID() const { return fUID; } void SetShadowInfo(const char* password, int lastChanged, int min, int max, int warn, int inactive, int expiration, int flags) { fShadowPassword = password; fLastChanged = lastChanged; fMin = min; fMax = max; fWarn = warn; fInactive = inactive; fExpiration = expiration; fFlags = flags; } void UpdateFromMessage(const KMessage& message) { int32 intValue; const char* stringValue; if (message.FindInt32("uid", &intValue) == B_OK) fUID = intValue; if (message.FindInt32("gid", &intValue) == B_OK) fGID = intValue; if (message.FindString("name", &stringValue) == B_OK) fName = stringValue; if (message.FindString("password", &stringValue) == B_OK) fPassword = stringValue; if (message.FindString("home", &stringValue) == B_OK) fHome = stringValue; if (message.FindString("shell", &stringValue) == B_OK) fShell = stringValue; if (message.FindString("real name", &stringValue) == B_OK) fRealName = stringValue; if (message.FindString("shadow password", &stringValue) == B_OK) { fShadowPassword = stringValue; // TODO: // fLastChanged = now; } if (message.FindInt32("last changed", &intValue) == B_OK) fLastChanged = intValue; if (message.FindInt32("min", &intValue) == B_OK) fMin = intValue; if (message.FindInt32("max", &intValue) == B_OK) fMax = intValue; if (message.FindInt32("warn", &intValue) == B_OK) fWarn = intValue; if (message.FindInt32("inactive", &intValue) == B_OK) fInactive = intValue; if (message.FindInt32("expiration", &intValue) == B_OK) fExpiration = intValue; if (message.FindInt32("flags", &intValue) == B_OK) fFlags = intValue; } passwd* WriteFlatPasswd(FlatStore& store) const { struct passwd passwd; passwd.pw_uid = fUID; passwd.pw_gid = fGID; passwd.pw_name = store.AppendString(fName); passwd.pw_passwd = store.AppendString(fPassword); passwd.pw_dir = store.AppendString(fHome); passwd.pw_shell = store.AppendString(fShell); passwd.pw_gecos = store.AppendString(fRealName); return store.AppendData(passwd); } spwd* WriteFlatShadowPwd(FlatStore& store) const { struct spwd spwd; spwd.sp_namp = store.AppendString(fName); spwd.sp_pwdp = store.AppendString(fShadowPassword); spwd.sp_lstchg = fLastChanged; spwd.sp_min = fMin; spwd.sp_max = fMax; spwd.sp_warn = fWarn; spwd.sp_inact = fInactive; spwd.sp_expire = fExpiration; spwd.sp_flag = fFlags; return store.AppendData(spwd); } status_t WriteToMessage(KMessage& message, bool addShadowPwd) { status_t error; if ((error = message.AddInt32("uid", fUID)) != B_OK || (error = message.AddInt32("gid", fGID)) != B_OK || (error = message.AddString("name", fName.c_str())) != B_OK || (error = message.AddString("password", fPassword.c_str())) != B_OK || (error = message.AddString("home", fHome.c_str())) != B_OK || (error = message.AddString("shell", fShell.c_str())) != B_OK || (error = message.AddString("real name", fRealName.c_str())) != B_OK) { return error; } if (!addShadowPwd) return B_OK; if ((error = message.AddString("shadow password", fShadowPassword.c_str())) != B_OK || (error = message.AddInt32("last changed", fLastChanged)) != B_OK || (error = message.AddInt32("min", fMin)) != B_OK || (error = message.AddInt32("max", fMax)) != B_OK || (error = message.AddInt32("warn", fWarn)) != B_OK || (error = message.AddInt32("inactive", fInactive)) != B_OK || (error = message.AddInt32("expiration", fExpiration)) != B_OK || (error = message.AddInt32("flags", fFlags)) != B_OK) { return error; } return B_OK; } void WritePasswdLine(FILE* file) { fprintf(file, "%s:%s:%d:%d:%s:%s:%s\n", fName.c_str(), fPassword.c_str(), (int)fUID, (int)fGID, fRealName.c_str(), fHome.c_str(), fShell.c_str()); } void WriteShadowPwdLine(FILE* file) { fprintf(file, "%s:%s:%d:", fName.c_str(), fShadowPassword.c_str(), fLastChanged); // The following values are supposed to be printed as empty strings, // if negative. int values[5] = { fMin, fMax, fWarn, fInactive, fExpiration }; for (int i = 0; i < 5; i++) { if (values[i] >= 0) fprintf(file, "%d", values[i]); fprintf(file, ":"); } fprintf(file, "%d\n", fFlags); } private: uid_t fUID; gid_t fGID; string fName; string fPassword; string fHome; string fShell; string fRealName; string fShadowPassword; int fLastChanged; int fMin; int fMax; int fWarn; int fInactive; int fExpiration; int fFlags; }; class AuthenticationManager::Group { public: Group() : fGID(0), fName(), fPassword(), fMembers() { } Group(const char* name, const char* password, gid_t gid, const char* const* members, int memberCount) : fGID(gid), fName(name), fPassword(password), fMembers() { for (int i = 0; i < memberCount; i++) fMembers.insert(members[i]); } ~Group() { } const string& Name() const { return fName; } const gid_t GID() const { return fGID; } bool HasMember(const char* name) { try { return fMembers.find(name) != fMembers.end(); } catch (...) { return false; } } bool MemberRemoved(const std::string& name) { return fMembers.erase(name) > 0; } void UpdateFromMessage(const KMessage& message) { int32 intValue; if (message.FindInt32("gid", &intValue) == B_OK) fGID = intValue; const char* stringValue; if (message.FindString("name", &stringValue) == B_OK) fName = stringValue; if (message.FindString("password", &stringValue) == B_OK) fPassword = stringValue; if (message.FindString("members", &stringValue) == B_OK) { fMembers.clear(); for (int32 i = 0; (stringValue = message.GetString("members", i, NULL)) != NULL; i++) { if (stringValue != NULL && *stringValue != '\0') fMembers.insert(stringValue); } } } group* WriteFlatGroup(FlatStore& store) const { struct group group; char* members[MAX_GROUP_MEMBER_COUNT + 1]; int32 count = 0; for (StringSet::const_iterator it = fMembers.begin(); it != fMembers.end(); ++it) { members[count++] = store.AppendString(it->c_str()); } members[count] = (char*)-1; group.gr_gid = fGID; group.gr_name = store.AppendString(fName); group.gr_passwd = store.AppendString(fPassword); group.gr_mem = (char**)store.AppendData(members, sizeof(char*) * (count + 1), true); return store.AppendData(group); } status_t WriteToMessage(KMessage& message) { status_t error; if ((error = message.AddInt32("gid", fGID)) != B_OK || (error = message.AddString("name", fName.c_str())) != B_OK || (error = message.AddString("password", fPassword.c_str())) != B_OK) { return error; } for (StringSet::const_iterator it = fMembers.begin(); it != fMembers.end(); ++it) { if ((error = message.AddString("members", it->c_str())) != B_OK) return error; } return B_OK; } void WriteGroupLine(FILE* file) { fprintf(file, "%s:%s:%d:", fName.c_str(), fPassword.c_str(), (int)fGID); for (StringSet::const_iterator it = fMembers.begin(); it != fMembers.end(); ++it) { if (it == fMembers.begin()) fprintf(file, "%s", it->c_str()); else fprintf(file, ",%s", it->c_str()); } fputs("\n", file); } private: gid_t fGID; string fName; string fPassword; StringSet fMembers; }; class AuthenticationManager::UserDB { public: status_t AddUser(User* user) { try { fUsersByID[user->UID()] = user; } catch (...) { return B_NO_MEMORY; } try { fUsersByName[user->Name()] = user; } catch (...) { fUsersByID.erase(fUsersByID.find(user->UID())); return B_NO_MEMORY; } return B_OK; } void RemoveUser(User* user) { fUsersByID.erase(fUsersByID.find(user->UID())); fUsersByName.erase(fUsersByName.find(user->Name())); } User* UserByID(uid_t uid) const { map::const_iterator it = fUsersByID.find(uid); return (it == fUsersByID.end() ? NULL : it->second); } User* UserByName(const char* name) const { map::const_iterator it = fUsersByName.find(name); return (it == fUsersByName.end() ? NULL : it->second); } int32 WriteFlatPasswdDB(FlatStore& store) const { int32 count = fUsersByID.size(); size_t entriesSpace = sizeof(passwd*) * count; size_t offset = store.ReserveSpace(entriesSpace, true); passwd** entries = new passwd*[count]; ArrayDeleter _(entries); int32 index = 0; for (map::const_iterator it = fUsersByID.begin(); it != fUsersByID.end(); ++it) { entries[index++] = it->second->WriteFlatPasswd(store); } store.WriteData(offset, entries, entriesSpace); return count; } int32 WriteFlatShadowDB(FlatStore& store) const { int32 count = fUsersByID.size(); size_t entriesSpace = sizeof(spwd*) * count; size_t offset = store.ReserveSpace(entriesSpace, true); spwd** entries = new spwd*[count]; ArrayDeleter _(entries); int32 index = 0; for (map::const_iterator it = fUsersByID.begin(); it != fUsersByID.end(); ++it) { entries[index++] = it->second->WriteFlatShadowPwd(store); } store.WriteData(offset, entries, entriesSpace); return count; } void WriteToDisk() { // rename the old files string passwdBackup(kPasswdFile); string shadowBackup(kShadowPwdFile); passwdBackup += ".old"; shadowBackup += ".old"; rename(kPasswdFile, passwdBackup.c_str()); rename(kShadowPwdFile, shadowBackup.c_str()); // Don't check errors. We can't do anything anyway. // open files FileCloser passwdFile(fopen(kPasswdFile, "w")); if (!passwdFile.IsSet()) { debug_printf("REG: Failed to open passwd file \"%s\" for " "writing: %s\n", kPasswdFile, strerror(errno)); } FileCloser shadowFile(fopen(kShadowPwdFile, "w")); if (!shadowFile.IsSet()) { debug_printf("REG: Failed to open shadow passwd file \"%s\" for " "writing: %s\n", kShadowPwdFile, strerror(errno)); } // write users for (map::const_iterator it = fUsersByID.begin(); it != fUsersByID.end(); ++it) { User* user = it->second; user->WritePasswdLine(passwdFile.Get()); user->WriteShadowPwdLine(shadowFile.Get()); } } private: map fUsersByID; map fUsersByName; }; class AuthenticationManager::GroupDB { public: status_t AddGroup(Group* group) { try { fGroupsByID[group->GID()] = group; } catch (...) { return B_NO_MEMORY; } try { fGroupsByName[group->Name()] = group; } catch (...) { fGroupsByID.erase(fGroupsByID.find(group->GID())); return B_NO_MEMORY; } return B_OK; } void RemoveGroup(Group* group) { fGroupsByID.erase(fGroupsByID.find(group->GID())); fGroupsByName.erase(fGroupsByName.find(group->Name())); } bool UserRemoved(const std::string& user) { bool changed = false; for (map::const_iterator it = fGroupsByID.begin(); it != fGroupsByID.end(); ++it) { Group* group = it->second; changed |= group->MemberRemoved(user); } return changed; } Group* GroupByID(gid_t gid) const { map::const_iterator it = fGroupsByID.find(gid); return (it == fGroupsByID.end() ? NULL : it->second); } Group* GroupByName(const char* name) const { map::const_iterator it = fGroupsByName.find(name); return (it == fGroupsByName.end() ? NULL : it->second); } int32 GetUserGroups(const char* name, gid_t* groups, int maxCount) { int count = 0; for (map::const_iterator it = fGroupsByID.begin(); it != fGroupsByID.end(); ++it) { Group* group = it->second; if (group->HasMember(name)) { if (count < maxCount) groups[count] = group->GID(); count++; } } return count; } int32 WriteFlatGroupDB(FlatStore& store) const { int32 count = fGroupsByID.size(); size_t entriesSpace = sizeof(group*) * count; size_t offset = store.ReserveSpace(entriesSpace, true); group** entries = new group*[count]; ArrayDeleter _(entries); int32 index = 0; for (map::const_iterator it = fGroupsByID.begin(); it != fGroupsByID.end(); ++it) { entries[index++] = it->second->WriteFlatGroup(store); } store.WriteData(offset, entries, entriesSpace); return count; } void WriteToDisk() { // rename the old files string groupBackup(kGroupFile); groupBackup += ".old"; rename(kGroupFile, groupBackup.c_str()); // Don't check errors. We can't do anything anyway. // open file FileCloser groupFile(fopen(kGroupFile, "w")); if (!groupFile.IsSet()) { debug_printf("REG: Failed to open group file \"%s\" for " "writing: %s\n", kGroupFile, strerror(errno)); } // write groups for (map::const_iterator it = fGroupsByID.begin(); it != fGroupsByID.end(); ++it) { Group* group = it->second; group->WriteGroupLine(groupFile.Get()); } } private: map fGroupsByID; map fGroupsByName; }; AuthenticationManager::AuthenticationManager() : fRequestPort(-1), fRequestThread(-1), fUserDB(NULL), fGroupDB(NULL), fPasswdDBReply(NULL), fGroupDBReply(NULL), fShadowPwdDBReply(NULL) { } AuthenticationManager::~AuthenticationManager() { // Quit the request thread and wait for it to finish write_port(fRequestPort, 'quit', NULL, 0); wait_for_thread(fRequestThread, NULL); delete fUserDB; delete fGroupDB; delete fPasswdDBReply; delete fGroupDBReply; delete fShadowPwdDBReply; } status_t AuthenticationManager::Init() { fUserDB = new(std::nothrow) UserDB; fGroupDB = new(std::nothrow) GroupDB; fPasswdDBReply = new(std::nothrow) KMessage(1); fGroupDBReply = new(std::nothrow) KMessage(1); fShadowPwdDBReply = new(std::nothrow) KMessage(1); if (fUserDB == NULL || fGroupDB == NULL || fPasswdDBReply == NULL || fGroupDBReply == NULL || fShadowPwdDBReply == NULL) { return B_NO_MEMORY; } fRequestPort = BLaunchRoster().GetPort( B_REGISTRAR_AUTHENTICATION_PORT_NAME); if (fRequestPort < 0) return fRequestPort; fRequestThread = spawn_thread(&_RequestThreadEntry, "authentication manager", B_NORMAL_PRIORITY + 1, this); if (fRequestThread < 0) return fRequestThread; resume_thread(fRequestThread); return B_OK; } status_t AuthenticationManager::_RequestThreadEntry(void* data) { return ((AuthenticationManager*)data)->_RequestThread(); } status_t AuthenticationManager::_RequestThread() { // read the DB files _InitPasswdDB(); _InitGroupDB(); _InitShadowPwdDB(); // get our team ID team_id registrarTeam = -1; { thread_info info; if (get_thread_info(find_thread(NULL), &info) == B_OK) registrarTeam = info.team; } // request loop while (true) { KMessage message; port_message_info messageInfo; status_t error = message.ReceiveFrom(fRequestPort, -1, &messageInfo); if (error != B_OK) return B_OK; bool isRoot = (messageInfo.sender == 0); switch (message.What()) { case B_REG_GET_PASSWD_DB: { // lazily build the reply try { if (fPasswdDBReply->What() == 1) { FlatStore store; int32 count = fUserDB->WriteFlatPasswdDB(store); if (fPasswdDBReply->AddInt32("count", count) != B_OK || fPasswdDBReply->AddData("entries", B_RAW_TYPE, store.Buffer(), store.BufferLength(), false) != B_OK) { error = B_NO_MEMORY; } fPasswdDBReply->SetWhat(0); } } catch (...) { error = B_NO_MEMORY; } if (error == B_OK) { message.SendReply(fPasswdDBReply, -1, -1, 0, registrarTeam); } else { _InvalidatePasswdDBReply(); KMessage reply(error); message.SendReply(&reply, -1, -1, 0, registrarTeam); } break; } case B_REG_GET_GROUP_DB: { // lazily build the reply try { if (fGroupDBReply->What() == 1) { FlatStore store; int32 count = fGroupDB->WriteFlatGroupDB(store); if (fGroupDBReply->AddInt32("count", count) != B_OK || fGroupDBReply->AddData("entries", B_RAW_TYPE, store.Buffer(), store.BufferLength(), false) != B_OK) { error = B_NO_MEMORY; } fGroupDBReply->SetWhat(0); } } catch (...) { error = B_NO_MEMORY; } if (error == B_OK) { message.SendReply(fGroupDBReply, -1, -1, 0, registrarTeam); } else { _InvalidateGroupDBReply(); KMessage reply(error); message.SendReply(&reply, -1, -1, 0, registrarTeam); } break; } case B_REG_GET_SHADOW_PASSWD_DB: { // only root may see the shadow passwd if (!isRoot) error = EPERM; // lazily build the reply try { if (error == B_OK && fShadowPwdDBReply->What() == 1) { FlatStore store; int32 count = fUserDB->WriteFlatShadowDB(store); if (fShadowPwdDBReply->AddInt32("count", count) != B_OK || fShadowPwdDBReply->AddData("entries", B_RAW_TYPE, store.Buffer(), store.BufferLength(), false) != B_OK) { error = B_NO_MEMORY; } fShadowPwdDBReply->SetWhat(0); } } catch (...) { error = B_NO_MEMORY; } if (error == B_OK) { message.SendReply(fShadowPwdDBReply, -1, -1, 0, registrarTeam); } else { _InvalidateShadowPwdDBReply(); KMessage reply(error); message.SendReply(&reply, -1, -1, 0, registrarTeam); } break; } case B_REG_GET_USER: { User* user = NULL; int32 uid; const char* name; // find user if (message.FindInt32("uid", &uid) == B_OK) { user = fUserDB->UserByID(uid); } else if (message.FindString("name", &name) == B_OK) { user = fUserDB->UserByName(name); } else { error = B_BAD_VALUE; } if (error == B_OK && user == NULL) error = ENOENT; bool getShadowPwd = message.GetBool("shadow", false); // only root may see the shadow passwd if (error == B_OK && getShadowPwd && !isRoot) error = EPERM; // add user to message KMessage reply; if (error == B_OK) error = user->WriteToMessage(reply, getShadowPwd); // send reply reply.SetWhat(error); message.SendReply(&reply, -1, -1, 0, registrarTeam); break; } case B_REG_GET_GROUP: { Group* group = NULL; int32 gid; const char* name; // find group if (message.FindInt32("gid", &gid) == B_OK) { group = fGroupDB->GroupByID(gid); } else if (message.FindString("name", &name) == B_OK) { group = fGroupDB->GroupByName(name); } else { error = B_BAD_VALUE; } if (error == B_OK && group == NULL) error = ENOENT; // add group to message KMessage reply; if (error == B_OK) error = group->WriteToMessage(reply); // send reply reply.SetWhat(error); message.SendReply(&reply, -1, -1, 0, registrarTeam); break; } case B_REG_GET_USER_GROUPS: { // get user name const char* name; int32 maxCount; if (message.FindString("name", &name) != B_OK || message.FindInt32("max count", &maxCount) != B_OK || maxCount <= 0) { error = B_BAD_VALUE; } // get groups gid_t groups[NGROUPS_MAX + 1]; int32 count = 0; if (error == B_OK) { maxCount = min_c(maxCount, NGROUPS_MAX + 1); count = fGroupDB->GetUserGroups(name, groups, maxCount); } // add groups to message KMessage reply; if (error == B_OK) { if (reply.AddInt32("count", count) != B_OK || reply.AddData("groups", B_INT32_TYPE, groups, min_c(maxCount, count) * sizeof(gid_t), false) != B_OK) { error = B_NO_MEMORY; } } // send reply reply.SetWhat(error); message.SendReply(&reply, -1, -1, 0, registrarTeam); break; } case B_REG_UPDATE_USER: { // find user User* user = NULL; int32 uid; const char* name; if (message.FindInt32("uid", &uid) == B_OK) { user = fUserDB->UserByID(uid); } else if (message.FindString("name", &name) == B_OK) { user = fUserDB->UserByName(name); } else { error = B_BAD_VALUE; } // only root can change anything if (error == B_OK && !isRoot) error = EPERM; // check addUser vs. existing user bool addUser = message.GetBool("add user", false); if (error == B_OK) { if (addUser) { if (user != NULL) error = EEXIST; } else if (user == NULL) error = ENOENT; } // apply all changes if (error == B_OK) { // clone the user object and update it from the message User* oldUser = user; user = NULL; try { user = (oldUser != NULL ? new User(*oldUser) : new User); user->UpdateFromMessage(message); // uid and name should remain the same if (oldUser != NULL) { if (oldUser->UID() != user->UID() || oldUser->Name() != user->Name()) { error = B_BAD_VALUE; } } // replace the old user and write DBs to disk if (error == B_OK) { fUserDB->AddUser(user); fUserDB->WriteToDisk(); _InvalidatePasswdDBReply(); _InvalidateShadowPwdDBReply(); } } catch (...) { error = B_NO_MEMORY; } if (error == B_OK) delete oldUser; else delete user; } // send reply KMessage reply; reply.SetWhat(error); message.SendReply(&reply, -1, -1, 0, registrarTeam); break; } case B_REG_DELETE_USER: { // find user User* user = NULL; int32 uid; const char* name; if (message.FindInt32("uid", &uid) == B_OK) { user = fUserDB->UserByID(uid); } else if (message.FindString("name", &name) == B_OK) { user = fUserDB->UserByName(name); } else { error = B_BAD_VALUE; } if (error == B_OK && user == NULL) error = ENOENT; // only root can change anything if (error == B_OK && !isRoot) error = EPERM; // apply the change if (error == B_OK) { std::string userName = user->Name(); fUserDB->RemoveUser(user); fUserDB->WriteToDisk(); _InvalidatePasswdDBReply(); _InvalidateShadowPwdDBReply(); if (fGroupDB->UserRemoved(userName)) { fGroupDB->WriteToDisk(); _InvalidateGroupDBReply(); } } // send reply KMessage reply; reply.SetWhat(error); message.SendReply(&reply, -1, -1, 0, registrarTeam); break; } case B_REG_UPDATE_GROUP: { // find group Group* group = NULL; int32 gid; const char* name; if (message.FindInt32("gid", &gid) == B_OK) { group = fGroupDB->GroupByID(gid); } else if (message.FindString("name", &name) == B_OK) { group = fGroupDB->GroupByName(name); } else { error = B_BAD_VALUE; } // only root can change anything if (error == B_OK && !isRoot) error = EPERM; // check addGroup vs. existing group bool addGroup = message.GetBool("add group", false); if (error == B_OK) { if (addGroup) { if (group != NULL) error = EEXIST; } else if (group == NULL) error = ENOENT; } // apply all changes if (error == B_OK) { // clone the group object and update it from the message Group* oldGroup = group; group = NULL; try { group = (oldGroup != NULL ? new Group(*oldGroup) : new Group); group->UpdateFromMessage(message); // gid and name should remain the same if (oldGroup != NULL) { if (oldGroup->GID() != group->GID() || oldGroup->Name() != group->Name()) { error = B_BAD_VALUE; } } // replace the old group and write DBs to disk if (error == B_OK) { fGroupDB->AddGroup(group); fGroupDB->WriteToDisk(); _InvalidateGroupDBReply(); } } catch (...) { error = B_NO_MEMORY; } if (error == B_OK) delete oldGroup; else delete group; } // send reply KMessage reply; reply.SetWhat(error); message.SendReply(&reply, -1, -1, 0, registrarTeam); break; } case B_REG_DELETE_GROUP: { // find group Group* group = NULL; int32 gid; const char* name; if (message.FindInt32("gid", &gid) == B_OK) { group = fGroupDB->GroupByID(gid); } else if (message.FindString("name", &name) == B_OK) { group = fGroupDB->GroupByName(name); } else { error = B_BAD_VALUE; } if (error == B_OK && group == NULL) error = ENOENT; // only root can change anything if (error == B_OK && !isRoot) error = EPERM; // apply the change if (error == B_OK) { fGroupDB->RemoveGroup(group); fGroupDB->WriteToDisk(); _InvalidateGroupDBReply(); } // send reply KMessage reply; reply.SetWhat(error); message.SendReply(&reply, -1, -1, 0, registrarTeam); break; } default: debug_printf("REG: invalid message: %" B_PRIu32 "\n", message.What()); } } } status_t AuthenticationManager::_InitPasswdDB() { FileCloser file(fopen(kPasswdFile, "r")); if (!file.IsSet()) { debug_printf("REG: Failed to open passwd DB file \"%s\": %s\n", kPasswdFile, strerror(errno)); return errno; } char lineBuffer[LINE_MAX]; while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file.Get())) { if (strlen(line) == 0) continue; char* name; char* password; uid_t uid; gid_t gid; char* home; char* shell; char* realName; status_t error = parse_passwd_line(line, name, password, uid, gid, home, shell, realName); if (error != B_OK) { debug_printf("REG: Unparsable line in passwd DB file: \"%s\"\n", strerror(errno)); continue; } User* user = NULL; try { user = new User(name, password, uid, gid, home, shell, realName); } catch (...) { } if (user == NULL || fUserDB->AddUser(user) != B_OK) { delete user; debug_printf("REG: Out of memory\n"); return B_NO_MEMORY; } } return B_OK; } status_t AuthenticationManager::_InitGroupDB() { FileCloser file(fopen(kGroupFile, "r")); if (!file.IsSet()) { debug_printf("REG: Failed to open group DB file \"%s\": %s\n", kGroupFile, strerror(errno)); return errno; } char lineBuffer[LINE_MAX]; while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file.Get())) { if (strlen(line) == 0) continue; char* name; char* password; gid_t gid; char* members[MAX_GROUP_MEMBER_COUNT]; int memberCount; status_t error = parse_group_line(line, name, password, gid, members, memberCount); if (error != B_OK) { debug_printf("REG: Unparsable line in group DB file: \"%s\"\n", strerror(errno)); continue; } Group* group = NULL; try { group = new Group(name, password, gid, members, memberCount); } catch (...) { } if (group == NULL || fGroupDB->AddGroup(group) != B_OK) { delete group; debug_printf("REG: Out of memory\n"); return B_NO_MEMORY; } } return B_OK; } status_t AuthenticationManager::_InitShadowPwdDB() { FileCloser file(fopen(kShadowPwdFile, "r")); if (!file.IsSet()) { debug_printf("REG: Failed to open shadow passwd DB file \"%s\": %s\n", kShadowPwdFile, strerror(errno)); return errno; } char lineBuffer[LINE_MAX]; while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file.Get())) { if (strlen(line) == 0) continue; char* name; char* password; int lastChanged; int min; int max; int warn; int inactive; int expiration; int flags; status_t error = parse_shadow_pwd_line(line, name, password, lastChanged, min, max, warn, inactive, expiration, flags); if (error != B_OK) { debug_printf("REG: Unparsable line in shadow passwd DB file: " "\"%s\"\n", strerror(errno)); continue; } User* user = fUserDB->UserByName(name); if (user == NULL) { debug_printf("REG: shadow pwd entry for unknown user \"%s\"\n", name); continue; } try { user->SetShadowInfo(password, lastChanged, min, max, warn, inactive, expiration, flags); } catch (...) { debug_printf("REG: Out of memory\n"); return B_NO_MEMORY; } } return B_OK; } void AuthenticationManager::_InvalidatePasswdDBReply() { fPasswdDBReply->SetTo(1); } void AuthenticationManager::_InvalidateGroupDBReply() { fGroupDBReply->SetTo(1); } void AuthenticationManager::_InvalidateShadowPwdDBReply() { fShadowPwdDBReply->SetTo(1); }