asLib check against PeerInfo
Check Put permissions
This commit is contained in:
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
150
pdbApp/pvif.cpp
150
pdbApp/pvif.cpp
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user