// SecurityContext.cpp #include #include #include #include #include #include #include #include #include #include "Compatibility.h" #include "Node.h" #include "SecurityContext.h" #include "UserSecurityContext.h" typedef AutoLocker ContextLocker; // get_node_ref_for_path 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; } // #pragma mark - // #pragma mark ----- User ----- // constructor User::User() : BReferenceable(), BArchivable(), fName(), fPassword() { } // constructor User::User(BMessage* archive) : BReferenceable(), BArchivable(archive), fName(), fPassword() { Unarchive(archive); } // destructor User::~User() { } // Archive status_t User::Archive(BMessage* archive, bool deep) const { if (!archive) return B_BAD_VALUE; // name status_t error = B_OK; if (error == B_OK && fName.GetLength() > 0) error = archive->AddString("name", fName.GetString()); // password if (error == B_OK && fPassword.GetLength() > 0) error = archive->AddString("password", fPassword.GetString()); return error; } // Instantiate BArchivable* User::Instantiate(BMessage* archive) { if (!validate_instantiation(archive, "User")) return NULL; return new(std::nothrow) User(archive); } // Init 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; } // InitCheck status_t User::InitCheck() const { if (fName.GetLength() == 0) return B_NO_INIT; return B_OK; } // Unarchive status_t User::Unarchive(const BMessage* archive) { // name const char* name; if (archive->FindString("name", &name) != B_OK) return B_BAD_DATA; fName.SetTo(name); // password const char* password; if (archive->FindString("password", &password) == B_OK) fPassword.SetTo(password); else fPassword.Unset(); return B_OK; } // GetName const char* User::GetName() const { return fName.GetString(); } // GetPassword const char* User::GetPassword() const { return fPassword.GetString(); } // #pragma mark - // #pragma mark ----- Share ----- // constructor Share::Share() : BReferenceable(), BArchivable(), fName(), fNodeRef(), fPath() { } // constructor Share::Share(BMessage* archive) : BReferenceable(), BArchivable(archive), fName(), fNodeRef(), fPath() { Unarchive(archive); } // destructor Share::~Share() { } // Archive status_t Share::Archive(BMessage* archive, bool deep) const { if (!archive) return B_BAD_VALUE; // name status_t error = B_OK; if (error == B_OK && fName.GetLength() > 0) error = archive->AddString("name", fName.GetString()); // path if (error == B_OK && fPath.GetLength() > 0) error = archive->AddString("path", fPath.GetString()); return error; } // Instantiate BArchivable* Share::Instantiate(BMessage* archive) { if (!validate_instantiation(archive, "Share")) return NULL; return new(std::nothrow) Share(archive); } // Init status_t Share::Init(const char* name, const node_ref& ref, const char* path) { // check params if (!name) return B_BAD_VALUE; // if a path is not given, retrieve it 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(); } // set the attributes if (!fName.SetTo(name)) return B_NO_MEMORY; if (!fPath.SetTo(path)) return B_NO_MEMORY; fNodeRef = ref; return B_OK; } // Init 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); } // InitCheck status_t Share::InitCheck() const { if (fName.GetLength() == 0 || fPath.GetLength() == 0) return B_NO_INIT; return B_OK; } // Unarchive status_t Share::Unarchive(const BMessage* archive) { // name const char* name = NULL; if (archive->FindString("name", &name) != B_OK) return B_BAD_DATA; // path const char* path = NULL; if (archive->FindString("path", &path) != B_OK) return B_BAD_DATA; return Init(name, path); } // GetName const char* Share::GetName() const { return fName.GetString(); } // DoesExist bool Share::DoesExist() const { return (fNodeRef.device >= 0); } // GetNodeRef const node_ref& Share::GetNodeRef() const { return fNodeRef; } // GetVolumeID dev_t Share::GetVolumeID() const { return fNodeRef.device; } // GetNodeID ino_t Share::GetNodeID() const { return fNodeRef.node; } // GetPath const char* Share::GetPath() const { return fPath.GetString(); } // #pragma mark - // #pragma mark ----- SecurityContext ----- // UserMap struct SecurityContext::UserMap : HashMap { }; // ShareMap struct SecurityContext::ShareMap : HashMap { }; // UserPath 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; }; // PermissionMap struct SecurityContext::PermissionMap : HashMap { }; // NodePathMap struct SecurityContext::NodePathMap : HashMap { }; // PathNodeMap struct SecurityContext::PathNodeMap : HashMap { }; // constructor 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) { } // constructor 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; // users 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; } // shares 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; } // permissions 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 userReference(user, true); error = permissionsArchive.FindMessage(userName, &userArchive); if (error != B_OK) return; // got a user: iterate through its permissions #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); } } } // destructor SecurityContext::~SecurityContext() { // remove all user references for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();) { User* user = it.Next().value; user->ReleaseReference(); } // remove all share references 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; } // Archive status_t SecurityContext::Archive(BMessage* archive, bool deep) const { if (!archive) return B_BAD_VALUE; status_t error = B_OK; ContextLocker _(const_cast(this)); // users 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; } // shares 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; } // permissions // we slice them per user BMessage* tmpUserArchives = new(std::nothrow) BMessage[userCount]; if (!tmpUserArchives) return B_NO_MEMORY; ArrayDeleter deleter(tmpUserArchives); HashMap, 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++; } // fill the per user archives 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; } // put the user permissions together 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; } // Instantiate BArchivable* SecurityContext::Instantiate(BMessage* archive) { if (!validate_instantiation(archive, "SecurityContext")) return NULL; return new(std::nothrow) SecurityContext(archive); } // InitCheck 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; } // AddUser // // The caller gets a reference, if _user is not NULL. status_t SecurityContext::AddUser(const char* name, const char* password, User** _user) { if (!name) return B_BAD_VALUE; // check, if the user does already exist ContextLocker _(this); if (fUsers->Get(name)) return B_BAD_VALUE; // create a the user User* user = new(std::nothrow) User; if (!user) return B_NO_MEMORY; BReference userReference(user, true); status_t error = user->Init(name, password); if (error != B_OK) return error; // add the user error = fUsers->Put(name, user); if (error != B_OK) return error; userReference.Detach(); if (_user) { *_user = user; user->AcquireReference(); } return B_OK; } // RemoveUser // // The caller gets a reference, if _user is not NULL. status_t SecurityContext::RemoveUser(const char* name, User** _user) { if (!name) return B_BAD_VALUE; ContextLocker _(this); // get the user User* user = FindUser(name); if (!user) return B_ENTRY_NOT_FOUND; BReference userReference(user, true); // remove it status_t error = RemoveUser(user); if (error == B_OK && _user) { *_user = user; user->AcquireReference(); } return error; } // RemoveUser status_t SecurityContext::RemoveUser(User* user) { if (!user) return B_BAD_VALUE; ContextLocker _(this); // find and remove it if (fUsers->Get(user->GetName()) != user) return B_BAD_VALUE; fUsers->Remove(user->GetName()); // remove all permission entries for this user for (PermissionMap::Iterator it = fPermissions->GetIterator(); it.HasNext();) { PermissionMap::Entry entry = it.Next(); if (entry.key.user == user) fPermissions->Remove(it); } // surrender our user reference user->ReleaseReference(); return B_OK; } // FindUser // // The caller gets a reference. User* SecurityContext::FindUser(const char* name) { if (!name) return NULL; ContextLocker _(this); User* user = fUsers->Get(name); if (user) user->AcquireReference(); return user; } // AuthenticateUser // // The caller gets a reference. status_t SecurityContext::AuthenticateUser(const char* name, const char* password, User** _user) { if (!_user) return B_BAD_VALUE; // find user ContextLocker _(this); User* user = FindUser(name); if (!user) return B_PERMISSION_DENIED; BReference userReference(user, true); // check password 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; } // CountUsers int32 SecurityContext::CountUsers() { ContextLocker _(this); return fUsers->Size(); } // GetUsers status_t SecurityContext::GetUsers(BMessage* users) { if (!users) return B_BAD_VALUE; ContextLocker _(this); // iterate through all users and add their names to the message 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; } // AddShare // // The caller gets a reference, if _share is not NULL. status_t SecurityContext::AddShare(const char* name, const node_ref& ref, Share** _share) { if (!name) return B_BAD_VALUE; // check, if the share does already exist ContextLocker _(this); if (fShares->Get(name)) return B_BAD_VALUE; // create a the share Share* share = new(std::nothrow) Share; if (!share) return B_NO_MEMORY; BReference shareReference(share, true); status_t error = share->Init(name, ref); if (error != B_OK) return error; // add the share error = fShares->Put(name, share); if (error != B_OK) return error; shareReference.Detach(); if (_share) { *_share = share; share->AcquireReference(); } return B_OK; } // AddShare // // The caller gets a reference, if _share is not NULL. status_t SecurityContext::AddShare(const char* name, const char* path, Share** _share) { if (!name) return B_BAD_VALUE; // check, if the share does already exist ContextLocker _(this); if (fShares->Get(name)) return B_BAD_VALUE; // create a the share Share* share = new(std::nothrow) Share; if (!share) return B_NO_MEMORY; BReference shareReference(share, true); status_t error = share->Init(name, path); if (error != B_OK) return error; // add the share error = fShares->Put(name, share); if (error != B_OK) return error; shareReference.Detach(); if (_share) { *_share = share; share->AcquireReference(); } return B_OK; } // RemoveShare // // The caller gets a reference, if _share is not NULL. status_t SecurityContext::RemoveShare(const char* name, Share** _share) { if (!name) return B_BAD_VALUE; ContextLocker _(this); // get the share Share* share = FindShare(name); if (!share) return B_ENTRY_NOT_FOUND; BReference shareReference(share, true); // remove it status_t error = RemoveShare(share); if (error == B_OK && _share) { *_share = share; share->AcquireReference(); } return error; } // RemoveShare status_t SecurityContext::RemoveShare(Share* share) { if (!share) return B_BAD_VALUE; ContextLocker _(this); // find and remove it if (fShares->Get(share->GetName()) != share) return B_BAD_VALUE; fShares->Remove(share->GetName()); // surrender our share reference share->ReleaseReference(); return B_OK; } // FindShare // // The caller gets a reference. Share* SecurityContext::FindShare(const char* name) { if (!name) return NULL; ContextLocker _(this); Share* share = fShares->Get(name); if (share) share->AcquireReference(); return share; } // CountShares int32 SecurityContext::CountShares() { ContextLocker _(this); return fShares->Size(); } // GetShares status_t SecurityContext::GetShares(BMessage* shares) { if (!shares) return B_BAD_VALUE; ContextLocker _(this); // iterate through all shares and add their names to the message for (ShareMap::Iterator it = fShares->GetIterator(); it.HasNext();) { Share* share = it.Next().value; // add name status_t error = shares->AddString("shares", share->GetName()); if (error != B_OK) return error; // add path error = shares->AddString("paths", share->GetPath()); if (error != B_OK) return error; } return B_OK; } // SetNodePermissions status_t SecurityContext::SetNodePermissions(const node_ref& ref, User* user, Permissions permissions) { if (!user) return B_BAD_VALUE; ContextLocker _(this); // check, whether we know the user 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); } // SetNodePermissions status_t SecurityContext::SetNodePermissions(const char* path, User* user, Permissions permissions) { if (!user || !path) return B_BAD_VALUE; ContextLocker _(this); // check, whether we know the user if (fUsers->Get(user->GetName()) != user) return B_BAD_VALUE; _AddNodePath(path); return fPermissions->Put(UserPath(path, user), permissions); } // ClearNodePermissions 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)); } } // ClearNodePermissions 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)); } } // GetNodePermissions 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)); } // GetNodePermissions Permissions SecurityContext::GetNodePermissions(const char* path, User* user) { if (!user || !path) return Permissions(); ContextLocker _(this); _AddNodePath(path); return fPermissions->Get(UserPath(path, user)); } // GetUserSecurityContext 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; // iterate through all permission entries and add the ones whose user // matches 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; } // _AddNodePath 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; } // _AddNodePath 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; } // _EnterNodePath 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; } // _GetNodeForPath bool SecurityContext::_GetNodeForPath(const char* path, node_ref* ref) { if (path && fPath2Node->ContainsKey(path)) { if (ref) *ref = fPath2Node->Get(path); return true; } return false; }