haiku/src/servers/registrar/AuthenticationManager.cpp

1410 lines
31 KiB
C++

/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#include "AuthenticationManager.h"
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/param.h>
#include <map>
#include <new>
#include <set>
#include <string>
#include <DataIO.h>
#include <StringList.h>
#include <AutoDeleter.h>
#include <AutoDeleterPosix.h>
#include <LaunchRoster.h>
#include <RegistrarDefs.h>
#include <libroot_private.h>
#include <user_group.h>
#include <util/KMessage.h>
using std::map;
using std::string;
using namespace BPrivate;
typedef std::set<std::string> 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<typename Type>
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<typename Type>
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<uid_t, User*>::const_iterator it = fUsersByID.find(uid);
return (it == fUsersByID.end() ? NULL : it->second);
}
User* UserByName(const char* name) const
{
map<string, User*>::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<passwd*> _(entries);
int32 index = 0;
for (map<uid_t, User*>::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<spwd*> _(entries);
int32 index = 0;
for (map<uid_t, User*>::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
FILE* passwdFile = fopen(kPasswdFile, "w");
if (passwdFile == NULL) {
debug_printf("REG: Failed to open passwd file \"%s\" for "
"writing: %s\n", kPasswdFile, strerror(errno));
}
FileCloser _1(passwdFile);
FILE* shadowFile = fopen(kShadowPwdFile, "w");
if (shadowFile == NULL) {
debug_printf("REG: Failed to open shadow passwd file \"%s\" for "
"writing: %s\n", kShadowPwdFile, strerror(errno));
}
FileCloser _2(shadowFile);
// write users
for (map<uid_t, User*>::const_iterator it = fUsersByID.begin();
it != fUsersByID.end(); ++it) {
User* user = it->second;
user->WritePasswdLine(passwdFile);
user->WriteShadowPwdLine(shadowFile);
}
}
private:
map<uid_t, User*> fUsersByID;
map<string, User*> 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<gid_t, Group*>::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<gid_t, Group*>::const_iterator it = fGroupsByID.find(gid);
return (it == fGroupsByID.end() ? NULL : it->second);
}
Group* GroupByName(const char* name) const
{
map<string, Group*>::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<gid_t, Group*>::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<group*> _(entries);
int32 index = 0;
for (map<gid_t, Group*>::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
FILE* groupFile = fopen(kGroupFile, "w");
if (groupFile == NULL) {
debug_printf("REG: Failed to open group file \"%s\" for "
"writing: %s\n", kGroupFile, strerror(errno));
}
FileCloser _1(groupFile);
// write groups
for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin();
it != fGroupsByID.end(); ++it) {
Group* group = it->second;
group->WriteGroupLine(groupFile);
}
}
private:
map<uid_t, Group*> fGroupsByID;
map<string, Group*> 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()
{
FILE* file = fopen(kPasswdFile, "r");
if (file == NULL) {
debug_printf("REG: Failed to open passwd DB file \"%s\": %s\n",
kPasswdFile, strerror(errno));
return errno;
}
FileCloser _(file);
char lineBuffer[LINE_MAX];
while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) {
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()
{
FILE* file = fopen(kGroupFile, "r");
if (file == NULL) {
debug_printf("REG: Failed to open group DB file \"%s\": %s\n",
kGroupFile, strerror(errno));
return errno;
}
FileCloser _(file);
char lineBuffer[LINE_MAX];
while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) {
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()
{
FILE* file = fopen(kShadowPwdFile, "r");
if (file == NULL) {
debug_printf("REG: Failed to open shadow passwd DB file \"%s\": %s\n",
kShadowPwdFile, strerror(errno));
return errno;
}
FileCloser _(file);
char lineBuffer[LINE_MAX];
while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) {
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);
}