/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2010-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include "privchanger.h" namespace Winix { void PrivChanger::SetCur(Cur * pcur) { cur = pcur; } void PrivChanger::SetSystem(System * psystem) { system = psystem; } void PrivChanger::SetDb(Db * pdb) { db = pdb; } bool PrivChanger::CheckAccess() { // we do not check permissions here // permissions depends on the user, group, and privileges // but we cannot use parameter 'r' on files // and only logged users can change permissions if( !cur->session->puser || (cur->request->is_item && cur->request->IsParam(L"r")) ) { cur->request->status = WINIX_ERR_PERMISSION_DENIED; return false; } return true; } bool PrivChanger::ChangeOwner(Item & item, long user_id, long group_id) { if( user_id!=item.item_content.user_id || group_id!=item.item_content.group_id ) { if( !system->CanChangeUser(item, user_id) ) { log << log3 << "Priv: can't change the user" << logend; return false; } if( !system->CanChangeGroup(item, group_id) ) { log << log3 << "Priv: can't change the group" << logend; return false; } } item.item_content.user_id = user_id; item.item_content.group_id = group_id; return true; } bool PrivChanger::ChangePrivileges(Item & item, int privileges) { if( privileges != item.item_content.privileges ) { if( !system->CanChangePrivileges(item, privileges) ) { log << log3 << "Priv: can't change privileges" << logend; return false; } } item.item_content.privileges = privileges; return true; } void PrivChanger::ChangePriv(Item & item, long user_id, long group_id, int privileges) { if( change_owner ) { if( !ChangeOwner(item, user_id, group_id) ) return; } if( change_priv ) { if( !ChangePrivileges(item, privileges) ) return; } item.propagate_connector(); //cur->request->status = db->EditPrivById(item, item.id); if( !item.item_content.update() ) { // IMPROVEME put some log here, and slog too } } void PrivChanger::PrivLogStart(const wchar_t * what, long user, long group, int priv) { log << log2 << what; if( change_owner ) { User * puser = system->users.GetUser(user); Group * pgroup = system->groups.GetGroup(group); log << "new user: "; if( puser ) log << puser->login; else log << "id: " << user; log << ", new group: "; if( pgroup ) log << pgroup->name; else log << "id: " << group; if( change_priv ) log << ", "; } if( change_priv ) { char buf[30]; sprintf(buf, "0%o", priv); log << "privileges: " << buf; } log << logend; } void PrivChanger::PrivLog(const wchar_t * what, long id, const std::wstring & url) { Item * root = 0; if( id != -1 ) root = system->dirs.GetRootDir(); log << log3 << "Priv: " << what; if( root && root->id == id ) log << "(root)"; else log << url; log << logend; } void PrivChanger::PrivFilesInDir(long parent_id) { // DbItemQuery iq; // // iq.SetAll(false, false); // iq.sel_user_id = iq.sel_group_id = iq.sel_guest_name = iq.sel_privileges = iq.sel_url = true; // // iq.WhereParentId(parent_id); // iq.WhereType(Item::dir, false); morm::Finder finder(model_connector); finder. select(). where(). eq(L"parent_id", parent_id). neq(L"type", static_cast(Item::dir)). get_vector(cur->request->item_tab); //db->GetItems(cur->request->item_tab, iq); std::vector::iterator i = cur->request->item_tab.begin(); for( ; i != cur->request->item_tab.end() ; ++i) { PrivLog(L"changed file: ", -1, i->url); ChangePriv(*i, user_id_file, group_id_file, priv_file); } } // recurrence void PrivChanger::PrivDir(long parent_id) { PrivFilesInDir(parent_id); DirContainer::ParentIterator i = system->dirs.FindFirstChild(parent_id); for( ; i != system->dirs.ParentEnd() ; i = system->dirs.NextChild(i) ) { PrivLog(L"changed dir: ", -1, i->second->url); ChangePriv(*(i->second), user_id_dir, group_id_dir, priv_dir); if( subdirectories ) PrivDir(i->second->id); } } void PrivChanger::ReadPriv(const wchar_t * user_in, const wchar_t * group_in, const wchar_t * priv_in, long & user_id, long & group_id, int & priv) { const std::wstring & user_str = cur->request->PostVar(user_in); const std::wstring & group_str = cur->request->PostVar(group_in); const std::wstring & priv_str = cur->request->PostVar(priv_in); if( change_owner ) { user_id = system->users.GetUserId( user_str ); group_id = system->groups.GetGroupId( group_str ); } if( change_priv ) priv = wcstol(priv_str.c_str(), 0, 8); } void PrivChanger::PrivDir() { ReadPriv(L"userfile", L"groupfile", L"privilegesfile", user_id_file, group_id_file, priv_file); ReadPriv(L"userdir", L"groupdir", L"privilegesdir", user_id_dir, group_id_dir, priv_dir); PrivLogStart(L"Priv: changes for files: ", user_id_file, group_id_file, priv_file); PrivLogStart(L"Priv: changes for dirs: ", user_id_dir, group_id_dir, priv_dir); if( cur->request->IsPostVar(L"changecurrentdir") ) { Item & last_dir = *cur->request->dir_tab.back(); PrivLog(L"changed dir: ", last_dir.id, last_dir.url); ChangePriv(*cur->request->dir_tab.back(), user_id_dir, group_id_dir, priv_dir); } subdirectories = cur->request->IsPostVar(L"changesubdirs"); // go through all directories PrivDir(cur->request->dir_tab.back()->id); system->RedirectToLastDir(); } // changing only one item (either a dir or file) void PrivChanger::PrivOneItem() { ReadPriv(L"user", L"group", L"privileges", user_id_file, group_id_file, priv_file); PrivLogStart(L"Priv: changes: ", user_id_file, group_id_file, priv_file); if( cur->request->is_item ) { ChangePriv(cur->request->item, user_id_file, group_id_file, priv_file); system->RedirectTo(cur->request->item); } else { ChangePriv(*cur->request->dir_tab.back(), user_id_file, group_id_file, priv_file); system->RedirectToLastDir(); } } /* !! IMPROVE ME we can add a counter to measure how many there are access denieds for files/directories and when changing only one file/directory we can show access denied message */ void PrivChanger::Change(bool change_owner_, bool change_priv_) { if( !CheckAccess() ) return; change_owner = change_owner_; change_priv = change_priv_; if( cur->request->IsParam(L"r") ) { PrivDir(); } else { PrivOneItem(); } system->dirs.CheckRootDir(); } } // namespace Winix