//------------------------------------------------------------------------------
//! @file AccessChecker.cc
//! @author Fabio Luchetti, Georgios Bitzes - CERN
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2017 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#include "mgm/auth/AccessChecker.hh"
#include "mgm/Acl.hh"
#include "namespace/interface/IFileMD.hh"
//#include
#include
#include
EOSMGMNAMESPACE_BEGIN
//----------------------------------------------------------------------------
// Check access to the given container - linked attributes are necessary
// to construct the Acl object.
//
// All information required to make a decision are passed to this function.
//----------------------------------------------------------------------------
bool AccessChecker::checkContainer(IContainerMD *cont,
const eos::IContainerMD::XAttrMap &linkedAttrs, int mode,
const eos::common::VirtualIdentity &vid)
{
//----------------------------------------------------------------------------
// Construct Acl object
//----------------------------------------------------------------------------
Acl acl(linkedAttrs, vid);
//----------------------------------------------------------------------------
// Delegate to method taking receiving acl object instead of linked xattrs
//----------------------------------------------------------------------------
return checkContainer(cont, acl, mode, vid);
}
//------------------------------------------------------------------------------
// Check access to the given container - all information required to make
// a decision are passed to this function, no external information should
// be needed.
//------------------------------------------------------------------------------
bool AccessChecker::checkContainer(IContainerMD *cont, const Acl &acl,
int mode, const eos::common::VirtualIdentity &vid)
{
//----------------------------------------------------------------------------
// Allow root to do anything
//----------------------------------------------------------------------------
if(vid.uid == 0) {
return true;
}
//----------------------------------------------------------------------------
// Always allow daemon to read / browse
//----------------------------------------------------------------------------
if(vid.uid == DAEMONUID && (!(mode & W_OK)) ) {
return true;
}
//----------------------------------------------------------------------------
// A non-root attempting to write an immutable directory?
//----------------------------------------------------------------------------
if(acl.HasAcl() && (!acl.IsMutable() && (mode & W_OK))) {
return false;
}
//----------------------------------------------------------------------------
// A non-root attempting to prepare, but no explicit Acl allowing prepare?
//----------------------------------------------------------------------------
if( (mode & P_OK) && (!acl.HasAcl() || !acl.CanPrepare()) ) {
return false;
}
//----------------------------------------------------------------------------
// Basic permission check
//----------------------------------------------------------------------------
bool basicCheck = cont->access(vid.uid, vid.gid, mode);
//----------------------------------------------------------------------------
// Access granted, or we have no Acls? We're done.
//----------------------------------------------------------------------------
if(basicCheck || !acl.HasAcl()) {
return basicCheck;
}
//----------------------------------------------------------------------------
// Basic check denied us access... let's see if we can recover through Acls
//----------------------------------------------------------------------------
//if ((mode & W_OK) && (!acl.CanWrite() && !cont->access(vid.uid, vid.gid, W_OK) ))
if ( (mode & W_OK) &&
( acl.CanNotWrite() ||
( !acl.CanWrite() && !cont->access(vid.uid, vid.gid, W_OK) )
)
) {
//--------------------------------------------------------------------------
// Asking for write permission, and neither basic check, nor Acls grant us
// write. Deny.
//--------------------------------------------------------------------------
return false;
}
// if ((mode & R_OK) && (!acl.CanRead() && !cont->access(vid.uid, vid.gid, R_OK) ))
if ( (mode & R_OK) &&
( acl.CanNotRead() ||
( !acl.CanRead() && !cont->access(vid.uid, vid.gid, R_OK) )
)
) {
//--------------------------------------------------------------------------
// Asking for read permission, and neither basic check, nor Acls grant us
// read. Deny.
//--------------------------------------------------------------------------
return false;
}
// if ((mode & X_OK) && (!acl.CanBrowse() && !cont->access(vid.uid, vid.gid, X_OK) ))
if ( (mode & X_OK) &&
( acl.CanNotBrowse() ||
( !acl.CanBrowse() && !cont->access(vid.uid, vid.gid, X_OK) )
)
) {
//--------------------------------------------------------------------------
// Asking for browse permission, and neither basic check, nor Acls grant us
// browse. Deny.
//--------------------------------------------------------------------------
return false;
}
//----------------------------------------------------------------------------
// We survived Acl check, grant.
//----------------------------------------------------------------------------
return true;
}
//------------------------------------------------------------------------------
// Check access to the given file. The parent directory of the file
// needs to be checked separately!
//------------------------------------------------------------------------------
bool AccessChecker::checkFile(IFileMD *file, int mode,
const eos::common::VirtualIdentity &vid)
{
//----------------------------------------------------------------------------
// We only check browse permissions for files, for now.
//----------------------------------------------------------------------------
if( !(mode & X_OK) ) {
return true;
}
//----------------------------------------------------------------------------
// root can do anything
//----------------------------------------------------------------------------
if(vid.uid == 0) {
return true;
}
uint16_t flags = file->getFlags();
uid_t uid = file->getCUid();
gid_t gid = file->getCGid();
// both uid and gid match? return OR-match
if(vid.uid == uid && vid.gid == gid) {
return (flags & S_IXUSR) || (flags & S_IXGRP);
}
// user check
if(vid.uid == uid) {
return (flags & S_IXUSR);
}
// group check
if(vid.gid == gid) {
return (flags & S_IXGRP);
}
// other check
return (flags & S_IXOTH);
}
//---------------------------------------------------------------------------------------------------
// Test if public access is allowed for a given path
//
//---------------------------------------------------------------------------------------------------
bool
AccessChecker::checkPublicAccess(const std::string& fullpath,
const common::VirtualIdentity& vid)
{
int errc = 0;
if ((eos::common::Mapping::UserNameToUid(std::string("eosnobody"),errc) == vid.uid) && !errc && (strcmp(vid.prot.c_str(),"sss")==0)) {
// eosnobody can access all squash files
eos::common::Path cPath(fullpath);
if (!cPath.isSquashFile()) {
errno = EACCES;
return false;
}
return true;
}
/* check only for anonymous access */
if (vid.uid != 99) {
return true;
} else {
uint32_t level = eos::common::Mapping::GetPublicAccessLevel();
if (level >= 1024) {
return true;
} // short cut
eos::common::Path cPath{fullpath};
return cPath.GetSubPathSize() < level ? true : false;
}
}
EOSMGMNAMESPACE_END