asLib check against PeerInfo

Check Put permissions
This commit is contained in:
Michael Davidsaver
2019-04-23 17:53:16 -07:00
parent 5170c2230d
commit d7314eaef4
6 changed files with 201 additions and 26 deletions

View File

@ -139,6 +139,15 @@ PDBGroupPV::connect(const std::tr1::shared_ptr<PDBProvider>& 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(); i<N; i++)
{
ret->aspvt[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<std::tr1::shared_ptr<PVIF> > putpvif(npvs);
pvd::Status ret;
for(size_t i=0; i<npvs; i++)
{
@ -350,8 +360,10 @@ void PDBGroupPut::put(pvd::PVStructure::shared_pointer const & value,
putpvif[i].reset(info.builder->attach(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 && i<npvs; i++) {
if(!putpvif[i].get()) continue;
@ -368,7 +380,9 @@ void PDBGroupPut::put(pvd::PVStructure::shared_pointer const & value,
DBScanLocker L(dbChannelRecord(info.chan));
ret |= putpvif[i]->get(*changed, info.allowProc ? doProc : PVIF::ProcInhibit);
ret |= putpvif[i]->get(*changed,
info.allowProc ? doProc : PVIF::ProcInhibit,
channel->aspvt[i].canWrite());
}
}

View File

@ -139,6 +139,9 @@ struct epicsShareClass PDBGroupChannel : public BaseChannel,
POINTER_DEFINITIONS(PDBGroupChannel);
PDBGroupPV::shared_pointer pv;
std::vector<ASCLIENT> aspvt;
// storage referenced from aspvt
ASCred cred;
static size_t num_instances;

View File

@ -2,6 +2,7 @@
#include <string.h>
#include <osiSock.h>
#include <dbAccess.h>
#include <dbChannel.h>
#include <dbStaticLib.h>
@ -11,6 +12,7 @@
#include <pv/epicsException.h>
#include <pv/pvAccess.h>
#include <pv/security.h>
#include <pv/configuration.h>
#define epicsExportSharedSymbols
@ -128,6 +130,11 @@ PDBSinglePV::connect(const std::tr1::shared_ptr<PDBProvider>& 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<N; i++) {
out<<", "<<(&cred.groups[i][0]);
}
out<<"\n";
}
pva::ChannelPut::shared_pointer
@ -337,7 +352,10 @@ void PDBSinglePut::put(pvd::PVStructure::shared_pointer const & value,
dbFldDes *fld = dbChannelFldDes(chan);
pvd::Status ret;
if(dbChannelFieldType(chan)>=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<pvd::PVScalar>("value")->getAs<std::string>());
long status = dbChannelPutField(chan, DBF_STRING, lval.c_str(), 1);

View File

@ -5,6 +5,7 @@
#include <dbAccess.h>
#include <dbNotify.h>
#include <asLib.h>
#include <dbEvent.h>
@ -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;

View File

@ -3,6 +3,7 @@
#include <pv/pvIntrospect.h> /* for pvdVersion.h */
#include <pv/standardField.h>
#include <osiSock.h>
#include <dbAccess.h>
#include <dbChannel.h>
#include <dbStaticLib.h>
@ -18,6 +19,8 @@
#include <pv/pvData.h>
#include <pv/anyscalar.h>
#include <pv/reftrack.h>
#include <pv/pvAccess.h>
#include <pv/security.h>
#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(); i<N; i++) {
asRemoveClient(&grppvt[i]);
}
}
void ASCLIENT::add(dbChannel* chan, ASCred& cred)
{
asRemoveClient(&aspvt);
/* asAddClient() fails secure to no-permission */
(void)asAddClient(&aspvt, dbChannelRecord(chan)->asp, dbChannelFldDes(chan)->as_level, &cred.user[0], &cred.host[0]);
grppvt.resize(cred.groups.size(), 0);
for(size_t i=0, N=grppvt.size(); i<N; i++) {
asRemoveClient(&grppvt[i]);
(void)asAddClient(&grppvt[i], dbChannelRecord(chan)->asp, 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(); i<N; i++) {
if(grppvt[i] && asCheckPut(grppvt[i]))
return true;
}
return false;
}
PVIF::PVIF(dbChannel *ch)
:chan(ch)
{}
@ -602,15 +697,21 @@ struct PVIFScalarNumeric : public PVIF
}
}
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc) OVERRIDE FINAL
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
{
pvd::Status ret;
bool newval = mask.logical_and(pvmeta.maskVALUEPut);
if(newval) {
getValue(pvmeta.chan, pvmeta.value.get());
if(permit)
getValue(pvmeta.chan, pvmeta.value.get());
else
ret = pvd::Status::error("Put not permitted");
}
if(newval || proc==PVIF::ProcForce) {
ret = PVIF::get(mask, proc);
if(permit)
ret = PVIF::get(mask, proc);
else
ret = pvd::Status::error("Process not permitted");
}
return ret;
}
@ -795,7 +896,7 @@ struct PVIFPlain : public PVIF
virtual ~PVIFPlain() {}
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl)
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
{
if(dbe&DBE_VALUE) {
putValue(channel, field.get(), pfl);
@ -803,20 +904,26 @@ struct PVIFPlain : public PVIF
}
}
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc)
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
{
pvd::Status ret;
bool newval = mask.get(fieldOffset);
if(newval) {
getValue(channel, field.get());
if(permit)
getValue(channel, field.get());
else
ret = pvd::Status::error("Put not permitted");
}
if(newval || proc==PVIF::ProcForce) {
ret = PVIF::get(mask, proc);
if(permit)
ret = PVIF::get(mask, proc);
else
ret = pvd::Status::error("Process not permitted");
}
return ret;
}
virtual unsigned dbe(const epics::pvData::BitSet& mask)
virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
{
// TODO: figure out how to handle various intermidiate compressed
// bitSet and enclosing.
@ -940,7 +1047,7 @@ struct PVIFMeta : public PVIF
virtual ~PVIFMeta() {}
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl)
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
{
mask |= meta.maskALWAYS;
if(dbe&DBE_ALARM)
@ -949,13 +1056,15 @@ struct PVIFMeta : public PVIF
putTime(meta, dbe, pfl);
}
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc)
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
{
// can't put time/alarm
if(mask.logical_and(meta.maskALARM))
return pvd::Status::warn("Put to meta field ignored");
return pvd::Status::Ok;
}
virtual unsigned dbe(const epics::pvData::BitSet& mask)
virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
{
if(mask.logical_and(meta.maskALARM))
return DBE_ALARM;
@ -1010,18 +1119,18 @@ struct PVIFProc : public PVIF
{
PVIFProc(dbChannel *channel) :PVIF(channel) {}
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl)
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
{
// nothing to get
}
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc)
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
{
// always process
return PVIF::get(mask, PVIF::ProcForce);
// always process (if permitted)
return PVIF::get(mask, PVIF::ProcForce, permit);
}
virtual unsigned dbe(const epics::pvData::BitSet& mask)
virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
{
return 0;
}
@ -1036,7 +1145,7 @@ struct ProcBuilder : public PVIFBuilder
virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
const std::string& fld,
dbChannel *channel)
dbChannel *channel) OVERRIDE FINAL
{
// invisible
return builder;
@ -1054,7 +1163,7 @@ struct ProcBuilder : public PVIFBuilder
}//namespace
pvd::Status PVIF::get(const epics::pvData::BitSet& mask, proc_t proc)
pvd::Status PVIF::get(const epics::pvData::BitSet& mask, proc_t proc, bool permit)
{
dbCommon *precord = dbChannelRecord(chan);
@ -1066,7 +1175,10 @@ pvd::Status PVIF::get(const epics::pvData::BitSet& mask, proc_t proc)
pvd::Status ret;
if (tryproc) {
if (precord->pact) {
if (!permit) {
return pvd::Status::error("Process not permitted");
} else if (precord->pact) {
if (precord->tpro)
printf("%s: Active %s\n",
epicsThreadGetNameSelf(), precord->name);

View File

@ -3,6 +3,7 @@
#include <map>
#include <asLib.h>
#include <dbAccess.h>
#include <dbChannel.h>
#include <dbStaticLib.h>
@ -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<char> user, host;
std::vector<std::vector<char> > groups;
void update(const std::tr1::shared_ptr<epics::pvAccess::ChannelRequester>& request);
};
struct ASCLIENT {
ASCLIENTPVT aspvt;
std::vector<ASCLIENTPVT> 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;