From d7314eaef4fb3b9cb2d7ceab04b4fa174faac041 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 23 Apr 2019 17:53:16 -0700 Subject: [PATCH] asLib check against PeerInfo Check Put permissions --- pdbApp/pdbgroup.cpp | 20 +++++- pdbApp/pdbgroup.h | 3 + pdbApp/pdbsingle.cpp | 22 ++++++- pdbApp/pdbsingle.h | 4 ++ pdbApp/pvif.cpp | 150 +++++++++++++++++++++++++++++++++++++------ pdbApp/pvif.h | 28 +++++++- 6 files changed, 201 insertions(+), 26 deletions(-) diff --git a/pdbApp/pdbgroup.cpp b/pdbApp/pdbgroup.cpp index f98a061..fbf1ab3 100644 --- a/pdbApp/pdbgroup.cpp +++ b/pdbApp/pdbgroup.cpp @@ -139,6 +139,15 @@ PDBGroupPV::connect(const std::tr1::shared_ptr& prov, const pva::ChannelRequester::shared_pointer& req) { PDBGroupChannel::shared_pointer ret(new PDBGroupChannel(shared_from_this(), prov, req)); + + ret->cred.update(req); + + ret->aspvt.resize(members.size()); + for(size_t i=0, N=members.size(); iaspvt[i].add(members[i].chan, ret->cred); + } + return ret; } @@ -341,6 +350,7 @@ void PDBGroupPut::put(pvd::PVStructure::shared_pointer const & value, // assume value may be a different struct each time... lot of wasted prep work const size_t npvs = channel->pv->members.size(); std::vector > putpvif(npvs); + pvd::Status ret; for(size_t i=0; iattach(info.chan, value, info.attachment)); } - pvd::Status ret; - if(atomic) { + if(!ret.isOK()) { + // no access + + } else if(atomic) { DBManyLocker L(channel->pv->locker); for(size_t i=0; ret && iget(*changed, info.allowProc ? doProc : PVIF::ProcInhibit); + ret |= putpvif[i]->get(*changed, + info.allowProc ? doProc : PVIF::ProcInhibit, + channel->aspvt[i].canWrite()); } } diff --git a/pdbApp/pdbgroup.h b/pdbApp/pdbgroup.h index 2f360c9..73e53b9 100644 --- a/pdbApp/pdbgroup.h +++ b/pdbApp/pdbgroup.h @@ -139,6 +139,9 @@ struct epicsShareClass PDBGroupChannel : public BaseChannel, POINTER_DEFINITIONS(PDBGroupChannel); PDBGroupPV::shared_pointer pv; + std::vector aspvt; + // storage referenced from aspvt + ASCred cred; static size_t num_instances; diff --git a/pdbApp/pdbsingle.cpp b/pdbApp/pdbsingle.cpp index ff7d41c..9ccbfcf 100644 --- a/pdbApp/pdbsingle.cpp +++ b/pdbApp/pdbsingle.cpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -11,6 +12,7 @@ #include #include +#include #include #define epicsExportSharedSymbols @@ -128,6 +130,11 @@ PDBSinglePV::connect(const std::tr1::shared_ptr& prov, const pva::ChannelRequester::shared_pointer& req) { PDBSingleChannel::shared_pointer ret(new PDBSingleChannel(shared_from_this(), req)); + + ret->cred.update(req); + + ret->aspvt.add(chan, ret->cred); + return ret; } @@ -200,7 +207,15 @@ PDBSingleChannel::~PDBSingleChannel() void PDBSingleChannel::printInfo(std::ostream& out) { - out<<"PDBSingleChannel"; + if(aspvt.canWrite()) + out << "RW "; + else + out << "RO "; + out<<(&cred.user[0])<<'@'<<(&cred.host[0]); + for(size_t i=0, N=cred.groups.size(); i=DBF_INLINK && dbChannelFieldType(chan)<=DBF_FWDLINK) { + if(!channel->aspvt.canWrite()) { + ret = pvd::Status::error("Put not permitted"); + + } else if(dbChannelFieldType(chan)>=DBF_INLINK && dbChannelFieldType(chan)<=DBF_FWDLINK) { try{ std::string lval(value->getSubFieldT("value")->getAs()); long status = dbChannelPutField(chan, DBF_STRING, lval.c_str(), 1); diff --git a/pdbApp/pdbsingle.h b/pdbApp/pdbsingle.h index 4f15eb9..a8d70a3 100644 --- a/pdbApp/pdbsingle.h +++ b/pdbApp/pdbsingle.h @@ -5,6 +5,7 @@ #include #include +#include #include @@ -78,6 +79,9 @@ struct PDBSingleChannel : public BaseChannel, POINTER_DEFINITIONS(PDBSingleChannel); PDBSinglePV::shared_pointer pv; + // storage referenced from aspvt + ASCred cred; + ASCLIENT aspvt; static size_t num_instances; diff --git a/pdbApp/pvif.cpp b/pdbApp/pvif.cpp index 2a681a5..437c617 100644 --- a/pdbApp/pvif.cpp +++ b/pdbApp/pvif.cpp @@ -3,6 +3,7 @@ #include /* for pvdVersion.h */ #include +#include #include #include #include @@ -18,6 +19,8 @@ #include #include #include +#include +#include #define epicsExportSharedSymbols #include "sb.h" @@ -34,6 +37,7 @@ #endif namespace pvd = epics::pvData; +namespace pva = epics::pvAccess; DBCH::DBCH(dbChannel *ch) :chan(ch) { @@ -66,6 +70,97 @@ void DBCH::swap(DBCH& o) std::swap(chan, o.chan); } +void ASCred::update(const pva::ChannelRequester::shared_pointer& req) +{ + pva::PeerInfo::const_shared_pointer info(req->getPeerInfo()); + std::string usertemp, hosttemp; + + if(info && info->identified) { + hosttemp = info->peer; + if(info->authority=="ca") { + usertemp = info->account; + size_t sep = usertemp.find_last_of('/'); + if(sep != std::string::npos) { + // prevent CA auth from claiming to be eg. "krb/someone.special" + usertemp = usertemp.substr(sep+1); + } + + } else { + usertemp = info->authority + "/" + info->account; + } + + static const char role[] = "role/"; + + groups.resize(info->roles.size()); + size_t idx = 0u; + for(pva::PeerInfo::roles_t::iterator it(info->roles.begin()), end(info->roles.end()); it!=end; ++it, idx++) { + groups[idx].resize((*it).size()+sizeof(role)); // sizeof(role) includes trailing nil + std::copy(role, + role+sizeof(role)-1, + groups[idx].begin()); + std::copy(it->begin(), + it->end(), + groups[idx].begin()+sizeof(role)-1); + groups[idx][groups[idx].size()-1] = '\0'; + } + + } else { + // legacy and anonymous + hosttemp = req->getRequesterName(); + } + + // remote names have the form "IP:port" + size_t sep = hosttemp.find_first_of(":"); + if(sep == std::string::npos) { + sep = hosttemp.size(); + } + hosttemp.resize(sep); + + host.resize(hosttemp.size()+1); + std::copy(hosttemp.begin(), + hosttemp.end(), + host.begin()); + host[hosttemp.size()] = '\0'; + + user.resize(usertemp.size()+1); + std::copy(usertemp.begin(), + usertemp.end(), + user.begin()); + user[usertemp.size()] = '\0'; +} + +ASCLIENT::~ASCLIENT() +{ + asRemoveClient(&aspvt); + for(size_t i=0, N=grppvt.size(); iasp, dbChannelFldDes(chan)->as_level, &cred.user[0], &cred.host[0]); + + grppvt.resize(cred.groups.size(), 0); + + for(size_t i=0, N=grppvt.size(); iasp, dbChannelFldDes(chan)->as_level, &cred.groups[i][0], &cred.host[0]); + } +} + +bool ASCLIENT::canWrite() { + if(!asActive || (aspvt && asCheckPut(aspvt))) + return true; + for(size_t i=0, N=grppvt.size(); ipact) { + if (!permit) { + return pvd::Status::error("Process not permitted"); + + } else if (precord->pact) { if (precord->tpro) printf("%s: Active %s\n", epicsThreadGetNameSelf(), precord->name); diff --git a/pdbApp/pvif.h b/pdbApp/pvif.h index 34b94f8..03173f1 100644 --- a/pdbApp/pvif.h +++ b/pdbApp/pvif.h @@ -3,6 +3,7 @@ #include +#include #include #include #include @@ -29,6 +30,12 @@ # define USE_MULTILOCK #endif +namespace epics { +namespace pvAccess { +class ChannelRequester; +} +} + short PVD2DBR(epics::pvData::ScalarType pvt); // copy from PVField (.value sub-field) to DBF buffer @@ -81,6 +88,23 @@ private: void prepare(); }; +struct ASCred { + // string storage must be safely mutable. cf. asAddClient() + std::vector user, host; + std::vector > groups; + void update(const std::tr1::shared_ptr& request); +}; + +struct ASCLIENT { + ASCLIENTPVT aspvt; + std::vector grppvt; + ASCLIENT() :aspvt(0) {} + ~ASCLIENT(); + // ASCred storage must remain valid + void add(dbChannel* chan, ASCred& cred); + bool canWrite(); +}; + struct pdbRecordInfo { DBENTRY ent; pdbRecordInfo(const char *name) @@ -343,9 +367,9 @@ struct epicsShareClass PVIF { //! Copy from PDB record to pvalue (call dbChannelGet()) //! caller must lock record virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) =0; - //! Copy from pvalue to PDB record (call dbChannelPut()) + //! May copy from pvalue to PDB record (call dbChannelPut()) //! caller must lock record - virtual epics::pvData::Status get(const epics::pvData::BitSet& mask, proc_t proc=ProcInhibit) =0; + virtual epics::pvData::Status get(const epics::pvData::BitSet& mask, proc_t proc=ProcInhibit, bool permit=true) =0; //! Calculate DBE mask from changed bitset virtual unsigned dbe(const epics::pvData::BitSet& mask) =0;