#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Entry.h>
#include <HashMap.h>
#include <Message.h>
#include <Path.h>
#include "Compatibility.h"
#include "Node.h"
#include "SecurityContext.h"
#include "UserSecurityContext.h"
typedef AutoLocker<SecurityContext> ContextLocker;
static
status_t
get_node_ref_for_path(const char* path, node_ref* ref)
{
if (!path || !ref)
return B_BAD_VALUE;
struct stat st;
if (lstat(path, &st) < 0)
return errno;
ref->device = st.st_dev;
ref->node = st.st_ino;
return B_OK;
}
User::User()
: BReferenceable(),
BArchivable(),
fName(),
fPassword()
{
}
User::User(BMessage* archive)
: BReferenceable(),
BArchivable(archive),
fName(),
fPassword()
{
Unarchive(archive);
}
User::~User()
{
}
status_t
User::Archive(BMessage* archive, bool deep) const
{
if (!archive)
return B_BAD_VALUE;
status_t error = B_OK;
if (error == B_OK && fName.GetLength() > 0)
error = archive->AddString("name", fName.GetString());
if (error == B_OK && fPassword.GetLength() > 0)
error = archive->AddString("password", fPassword.GetString());
return error;
}
BArchivable*
User::Instantiate(BMessage* archive)
{
if (!validate_instantiation(archive, "User"))
return NULL;
return new(std::nothrow) User(archive);
}
status_t
User::Init(const char* name, const char* password)
{
if (!name)
return B_BAD_VALUE;
if (!fName.SetTo(name))
return B_NO_MEMORY;
if (password && !fPassword.SetTo(password))
return B_NO_MEMORY;
return B_OK;
}
status_t
User::InitCheck() const
{
if (fName.GetLength() == 0)
return B_NO_INIT;
return B_OK;
}
status_t
User::Unarchive(const BMessage* archive)
{
const char* name;
if (archive->FindString("name", &name) != B_OK)
return B_BAD_DATA;
fName.SetTo(name);
const char* password;
if (archive->FindString("password", &password) == B_OK)
fPassword.SetTo(password);
else
fPassword.Unset();
return B_OK;
}
const char*
User::GetName() const
{
return fName.GetString();
}
const char*
User::GetPassword() const
{
return fPassword.GetString();
}
Share::Share()
: BReferenceable(),
BArchivable(),
fName(),
fNodeRef(),
fPath()
{
}
Share::Share(BMessage* archive)
: BReferenceable(),
BArchivable(archive),
fName(),
fNodeRef(),
fPath()
{
Unarchive(archive);
}
Share::~Share()
{
}
status_t
Share::Archive(BMessage* archive, bool deep) const
{
if (!archive)
return B_BAD_VALUE;
status_t error = B_OK;
if (error == B_OK && fName.GetLength() > 0)
error = archive->AddString("name", fName.GetString());
if (error == B_OK && fPath.GetLength() > 0)
error = archive->AddString("path", fPath.GetString());
return error;
}
BArchivable*
Share::Instantiate(BMessage* archive)
{
if (!validate_instantiation(archive, "Share"))
return NULL;
return new(std::nothrow) Share(archive);
}
status_t
Share::Init(const char* name, const node_ref& ref, const char* path)
{
if (!name)
return B_BAD_VALUE;
BPath localPath;
if (!path) {
entry_ref entryRef(ref.device, ref.node, ".");
status_t error = localPath.SetTo(&entryRef);
if (error != B_OK)
return error;
path = localPath.Path();
}
if (!fName.SetTo(name))
return B_NO_MEMORY;
if (!fPath.SetTo(path))
return B_NO_MEMORY;
fNodeRef = ref;
return B_OK;
}
status_t
Share::Init(const char* name, const char* path)
{
if (!name || !path)
return B_BAD_VALUE;
node_ref nodeRef;
if (get_node_ref_for_path(path, &nodeRef) != B_OK) {
nodeRef.device = -1;
nodeRef.node = -1;
}
return Init(name, nodeRef, path);
}
status_t
Share::InitCheck() const
{
if (fName.GetLength() == 0 || fPath.GetLength() == 0)
return B_NO_INIT;
return B_OK;
}
status_t
Share::Unarchive(const BMessage* archive)
{
const char* name = NULL;
if (archive->FindString("name", &name) != B_OK)
return B_BAD_DATA;
const char* path = NULL;
if (archive->FindString("path", &path) != B_OK)
return B_BAD_DATA;
return Init(name, path);
}
const char*
Share::GetName() const
{
return fName.GetString();
}
bool
Share::DoesExist() const
{
return (fNodeRef.device >= 0);
}
const node_ref&
Share::GetNodeRef() const
{
return fNodeRef;
}
dev_t
Share::GetVolumeID() const
{
return fNodeRef.device;
}
ino_t
Share::GetNodeID() const
{
return fNodeRef.node;
}
const char*
Share::GetPath() const
{
return fPath.GetString();
}
struct SecurityContext::UserMap : HashMap<HashString, User*> {
};
struct SecurityContext::ShareMap : HashMap<HashString, Share*> {
};
struct SecurityContext::UserPath {
UserPath() {}
UserPath(const char* path, User* user)
: path(path),
user(user)
{
}
UserPath(const UserPath& other)
: path(other.path),
user(other.user)
{
}
uint32 GetHashCode() const
{
#ifdef B_HAIKU_64_BIT
uint64 v = (uint64)user;
return (path.GetHashCode() * 31) + ((uint32)(v >> 32) ^ (uint32)v);
#else
return path.GetHashCode() * 31 + (uint32)user;
#endif
}
UserPath& operator=(const UserPath& other)
{
path = other.path;
user = other.user;
return *this;
}
bool operator==(const UserPath& other) const
{
return (path == other.path && user == other.user);
}
bool operator!=(const UserPath& other) const
{
return !(*this == other);
}
HashString path;
User* user;
};
struct SecurityContext::PermissionMap
: HashMap<SecurityContext::UserPath, Permissions> {
};
struct SecurityContext::NodePathMap : HashMap<NodeRef, HashString> {
};
struct SecurityContext::PathNodeMap : HashMap<HashString, NodeRef> {
};
SecurityContext::SecurityContext()
: BArchivable(),
BLocker("security context"),
fUsers(new(std::nothrow) UserMap),
fShares(new(std::nothrow) ShareMap),
fPermissions(new(std::nothrow) PermissionMap),
fNode2Path(new(std::nothrow) NodePathMap),
fPath2Node(new(std::nothrow) PathNodeMap)
{
}
SecurityContext::SecurityContext(BMessage* archive)
: BArchivable(archive),
fUsers(new(std::nothrow) UserMap),
fShares(new(std::nothrow) ShareMap),
fPermissions(new(std::nothrow) PermissionMap),
fNode2Path(new(std::nothrow) NodePathMap),
fPath2Node(new(std::nothrow) PathNodeMap)
{
if (InitCheck() != B_OK)
return;
status_t error = B_OK;
BMessage userArchive;
for (int32 i = 0;
archive->FindMessage("users", i, &userArchive) == B_OK;
i++) {
User tmpUser;
error = tmpUser.Unarchive(&userArchive);
if (error != B_OK)
return;
error = AddUser(tmpUser.GetName(), tmpUser.GetPassword());
if (error != B_OK)
return;
}
BMessage shareArchive;
for (int32 i = 0;
archive->FindMessage("shares", i, &shareArchive) == B_OK;
i++) {
Share tmpShare;
error = tmpShare.Unarchive(&shareArchive);
if (error != B_OK)
return;
error = AddShare(tmpShare.GetName(), tmpShare.GetPath());
if (error != B_OK)
return;
}
BMessage permissionsArchive;
if (archive->FindMessage("permissions", &permissionsArchive) != B_OK)
return;
#ifdef HAIKU_TARGET_PLATFORM_DANO
const char* userName;
#else
char* userName;
#endif
type_code type;
for (int32 userIndex = 0;
permissionsArchive.GetInfo(B_MESSAGE_TYPE, userIndex, &userName, &type)
== B_OK;
userIndex++) {
User* user = FindUser(userName);
if (!user)
return;
BReference<User> userReference(user, true);
error = permissionsArchive.FindMessage(userName, &userArchive);
if (error != B_OK)
return;
#ifdef HAIKU_TARGET_PLATFORM_DANO
const char* path;
#else
char* path;
#endif
for (int32 i = 0;
userArchive.GetInfo(B_INT32_TYPE, i, &path, &type) == B_OK;
i++) {
uint32 permissions;
error = userArchive.FindInt32(path, (int32*)&permissions);
if (error == B_OK)
error = SetNodePermissions(path, user, permissions);
}
}
}
SecurityContext::~SecurityContext()
{
for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();) {
User* user = it.Next().value;
user->ReleaseReference();
}
for (ShareMap::Iterator it = fShares->GetIterator(); it.HasNext();) {
Share* share = it.Next().value;
share->ReleaseReference();
}
delete fUsers;
delete fShares;
delete fPermissions;
delete fNode2Path;
delete fPath2Node;
}
status_t
SecurityContext::Archive(BMessage* archive, bool deep) const
{
if (!archive)
return B_BAD_VALUE;
status_t error = B_OK;
ContextLocker _(const_cast<SecurityContext*>(this));
int32 userCount = fUsers->Size();
for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();) {
User* user = it.Next().value;
BMessage userArchive;
error = user->Archive(&userArchive, deep);
if (error != B_OK)
return error;
error = archive->AddMessage("users", &userArchive);
if (error != B_OK)
return error;
}
for (ShareMap::Iterator it = fShares->GetIterator(); it.HasNext();) {
Share* share = it.Next().value;
BMessage shareArchive;
error = share->Archive(&shareArchive, deep);
if (error != B_OK)
return error;
error = archive->AddMessage("shares", &shareArchive);
if (error != B_OK)
return error;
}
BMessage* tmpUserArchives = new(std::nothrow) BMessage[userCount];
if (!tmpUserArchives)
return B_NO_MEMORY;
ArrayDeleter<BMessage> deleter(tmpUserArchives);
HashMap<HashKeyPointer<User*>, BMessage*> userArchives;
int32 i = 0;
for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();) {
User* user = it.Next().value;
error = userArchives.Put(user, tmpUserArchives + i);
if (error != B_OK)
return error;
i++;
}
for (PermissionMap::Iterator it = fPermissions->GetIterator();
it.HasNext();) {
PermissionMap::Entry entry = it.Next();
BMessage* userArchive = userArchives.Get(entry.key.user);
error = userArchive->AddInt32(entry.key.path.GetString(),
entry.value.GetPermissions());
if (error != B_OK)
return error;
}
BMessage permissionsArchive;
for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();) {
User* user = it.Next().value;
error = permissionsArchive.AddMessage(user->GetName(),
userArchives.Get(user));
if (error != B_OK)
return error;
}
error = archive->AddMessage("permissions", &permissionsArchive);
if (error != B_OK)
return error;
return B_OK;
}
BArchivable*
SecurityContext::Instantiate(BMessage* archive)
{
if (!validate_instantiation(archive, "SecurityContext"))
return NULL;
return new(std::nothrow) SecurityContext(archive);
}
status_t
SecurityContext::InitCheck() const
{
if (!fUsers || !fShares || !fPermissions || !fNode2Path || !fPath2Node)
return B_NO_MEMORY;
if (fUsers->InitCheck() != B_OK)
return fUsers->InitCheck();
if (fShares->InitCheck() != B_OK)
return fShares->InitCheck();
if (fPermissions->InitCheck() != B_OK)
return fPermissions->InitCheck();
if (fNode2Path->InitCheck() != B_OK)
return fNode2Path->InitCheck();
if (fPath2Node->InitCheck() != B_OK)
return fPath2Node->InitCheck();
return B_OK;
}
status_t
SecurityContext::AddUser(const char* name, const char* password, User** _user)
{
if (!name)
return B_BAD_VALUE;
ContextLocker _(this);
if (fUsers->Get(name))
return B_BAD_VALUE;
User* user = new(std::nothrow) User;
if (!user)
return B_NO_MEMORY;
BReference<User> userReference(user, true);
status_t error = user->Init(name, password);
if (error != B_OK)
return error;
error = fUsers->Put(name, user);
if (error != B_OK)
return error;
userReference.Detach();
if (_user) {
*_user = user;
user->AcquireReference();
}
return B_OK;
}
status_t
SecurityContext::RemoveUser(const char* name, User** _user)
{
if (!name)
return B_BAD_VALUE;
ContextLocker _(this);
User* user = FindUser(name);
if (!user)
return B_ENTRY_NOT_FOUND;
BReference<User> userReference(user, true);
status_t error = RemoveUser(user);
if (error == B_OK && _user) {
*_user = user;
user->AcquireReference();
}
return error;
}
status_t
SecurityContext::RemoveUser(User* user)
{
if (!user)
return B_BAD_VALUE;
ContextLocker _(this);
if (fUsers->Get(user->GetName()) != user)
return B_BAD_VALUE;
fUsers->Remove(user->GetName());
for (PermissionMap::Iterator it = fPermissions->GetIterator();
it.HasNext();) {
PermissionMap::Entry entry = it.Next();
if (entry.key.user == user)
fPermissions->Remove(it);
}
user->ReleaseReference();
return B_OK;
}
User*
SecurityContext::FindUser(const char* name)
{
if (!name)
return NULL;
ContextLocker _(this);
User* user = fUsers->Get(name);
if (user)
user->AcquireReference();
return user;
}
status_t
SecurityContext::AuthenticateUser(const char* name, const char* password,
User** _user)
{
if (!_user)
return B_BAD_VALUE;
ContextLocker _(this);
User* user = FindUser(name);
if (!user)
return B_PERMISSION_DENIED;
BReference<User> userReference(user, true);
if (user->GetPassword()) {
if (!password || strcmp(user->GetPassword(), password) != 0)
return B_PERMISSION_DENIED;
} else if (password)
return B_PERMISSION_DENIED;
*_user = user;
userReference.Detach();
return B_OK;
}
int32
SecurityContext::CountUsers()
{
ContextLocker _(this);
return fUsers->Size();
}
status_t
SecurityContext::GetUsers(BMessage* users)
{
if (!users)
return B_BAD_VALUE;
ContextLocker _(this);
for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();) {
User* user = it.Next().value;
status_t error = users->AddString("users", user->GetName());
if (error != B_OK)
return error;
}
return B_OK;
}
status_t
SecurityContext::AddShare(const char* name, const node_ref& ref, Share** _share)
{
if (!name)
return B_BAD_VALUE;
ContextLocker _(this);
if (fShares->Get(name))
return B_BAD_VALUE;
Share* share = new(std::nothrow) Share;
if (!share)
return B_NO_MEMORY;
BReference<Share> shareReference(share, true);
status_t error = share->Init(name, ref);
if (error != B_OK)
return error;
error = fShares->Put(name, share);
if (error != B_OK)
return error;
shareReference.Detach();
if (_share) {
*_share = share;
share->AcquireReference();
}
return B_OK;
}
status_t
SecurityContext::AddShare(const char* name, const char* path, Share** _share)
{
if (!name)
return B_BAD_VALUE;
ContextLocker _(this);
if (fShares->Get(name))
return B_BAD_VALUE;
Share* share = new(std::nothrow) Share;
if (!share)
return B_NO_MEMORY;
BReference<Share> shareReference(share, true);
status_t error = share->Init(name, path);
if (error != B_OK)
return error;
error = fShares->Put(name, share);
if (error != B_OK)
return error;
shareReference.Detach();
if (_share) {
*_share = share;
share->AcquireReference();
}
return B_OK;
}
status_t
SecurityContext::RemoveShare(const char* name, Share** _share)
{
if (!name)
return B_BAD_VALUE;
ContextLocker _(this);
Share* share = FindShare(name);
if (!share)
return B_ENTRY_NOT_FOUND;
BReference<Share> shareReference(share, true);
status_t error = RemoveShare(share);
if (error == B_OK && _share) {
*_share = share;
share->AcquireReference();
}
return error;
}
status_t
SecurityContext::RemoveShare(Share* share)
{
if (!share)
return B_BAD_VALUE;
ContextLocker _(this);
if (fShares->Get(share->GetName()) != share)
return B_BAD_VALUE;
fShares->Remove(share->GetName());
share->ReleaseReference();
return B_OK;
}
Share*
SecurityContext::FindShare(const char* name)
{
if (!name)
return NULL;
ContextLocker _(this);
Share* share = fShares->Get(name);
if (share)
share->AcquireReference();
return share;
}
int32
SecurityContext::CountShares()
{
ContextLocker _(this);
return fShares->Size();
}
status_t
SecurityContext::GetShares(BMessage* shares)
{
if (!shares)
return B_BAD_VALUE;
ContextLocker _(this);
for (ShareMap::Iterator it = fShares->GetIterator(); it.HasNext();) {
Share* share = it.Next().value;
status_t error = shares->AddString("shares", share->GetName());
if (error != B_OK)
return error;
error = shares->AddString("paths", share->GetPath());
if (error != B_OK)
return error;
}
return B_OK;
}
status_t
SecurityContext::SetNodePermissions(const node_ref& ref, User* user,
Permissions permissions)
{
if (!user)
return B_BAD_VALUE;
ContextLocker _(this);
if (fUsers->Get(user->GetName()) != user)
return B_BAD_VALUE;
HashString path;
status_t error = _AddNodePath(ref, &path);
if (error != B_OK)
return error;
return fPermissions->Put(UserPath(path.GetString(), user), permissions);
}
status_t
SecurityContext::SetNodePermissions(const char* path, User* user,
Permissions permissions)
{
if (!user || !path)
return B_BAD_VALUE;
ContextLocker _(this);
if (fUsers->Get(user->GetName()) != user)
return B_BAD_VALUE;
_AddNodePath(path);
return fPermissions->Put(UserPath(path, user), permissions);
}
void
SecurityContext::ClearNodePermissions(const node_ref& ref, User* user)
{
ContextLocker _(this);
HashString path;
status_t error = _AddNodePath(ref, &path);
if (error != B_OK)
return;
if (user) {
fPermissions->Remove(UserPath(path.GetString(), user));
} else {
for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();)
fPermissions->Remove(UserPath(path.GetString(), it.Next().value));
}
}
void
SecurityContext::ClearNodePermissions(const char* path, User* user)
{
if (!path)
return;
ContextLocker _(this);
_AddNodePath(path);
if (user) {
fPermissions->Remove(UserPath(path, user));
} else {
for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();)
fPermissions->Remove(UserPath(path, it.Next().value));
}
}
Permissions
SecurityContext::GetNodePermissions(const node_ref& ref, User* user)
{
if (!user)
return Permissions();
ContextLocker _(this);
HashString path;
status_t error = _AddNodePath(ref, &path);
if (error != B_OK)
return Permissions();
return fPermissions->Get(UserPath(path.GetString(), user));
}
Permissions
SecurityContext::GetNodePermissions(const char* path, User* user)
{
if (!user || !path)
return Permissions();
ContextLocker _(this);
_AddNodePath(path);
return fPermissions->Get(UserPath(path, user));
}
status_t
SecurityContext::GetUserSecurityContext(User* user,
UserSecurityContext* userContext)
{
if (!userContext)
return B_BAD_VALUE;
status_t error = userContext->Init(user);
if (error != B_OK)
return error;
ContextLocker _(this);
for (PermissionMap::Iterator it = fPermissions->GetIterator();
it.HasNext();) {
PermissionMap::Entry entry = it.Next();
node_ref ref;
if (entry.key.user == user
&& _GetNodeForPath(entry.key.path.GetString(), &ref)) {
error = userContext->AddNode(ref.device, ref.node, entry.value);
if (error != B_OK)
return error;
}
}
return B_OK;
}
status_t
SecurityContext::_AddNodePath(const char* path, node_ref* _ref)
{
if (!fPath2Node->ContainsKey(path)) {
node_ref ref;
status_t error = get_node_ref_for_path(path, &ref);
if (error == B_OK)
error = _EnterNodePath(path, ref);
if (error != B_OK)
return error;
}
if (_ref)
*_ref = fPath2Node->Get(path);
return B_OK;
}
status_t
SecurityContext::_AddNodePath(const node_ref& ref, HashString* _path)
{
if (!fNode2Path->ContainsKey(ref)) {
BPath path;
entry_ref entryRef(ref.device, ref.node, ".");
status_t error = path.SetTo(&entryRef);
if (error == B_OK)
error = _EnterNodePath(path.Path(), ref);
if (error != B_OK)
return error;
}
if (_path)
*_path = fNode2Path->Get(ref);
return B_OK;
}
status_t
SecurityContext::_EnterNodePath(const char* path, const node_ref& ref)
{
status_t error = fNode2Path->Put(ref, path);
if (error == B_OK) {
error = fPath2Node->Put(path, ref);
if (error != B_OK)
fNode2Path->Remove(ref);
}
return error;
}
bool
SecurityContext::_GetNodeForPath(const char* path, node_ref* ref)
{
if (path && fPath2Node->ContainsKey(path)) {
if (ref)
*ref = fPath2Node->Get(path);
return true;
}
return false;
}