45#if defined(__APPLE__) || defined(__NetBSD__)
46#define st_ctim st_ctimespec
49bool XrdVomsMapfile::tried_configure =
false;
50std::unique_ptr<XrdVomsMapfile> XrdVomsMapfile::mapper;
55PathToString(
const std::vector<std::string> &path)
57 if (path.empty()) {
return "/";}
59 for (
const auto &entry : path) {
66uint64_t monotonic_time_s() {
68 clock_gettime(CLOCK_MONOTONIC, &tp);
69 return tp.tv_sec + (tp.tv_nsec >= 500000000);
76 const std::string &mapfile)
77 : m_mapfile(mapfile), m_edest(erp)
80 if (-1 ==
stat(m_mapfile.c_str(), &statbuf)) {
81 m_edest->Emsg(
"XrdVomsMapfile", errno,
"Error checking the mapfile", m_mapfile.c_str());
84 memcpy(&m_mapfile_ctime, &statbuf.st_ctim,
sizeof(
decltype(m_mapfile_ctime)));
86 if (!ParseMapfile(m_mapfile)) {
return;}
90 static_cast<void*
>(
this), 0,
"VOMS Mapfile refresh");
92 m_edest->Emsg(
"XrdVomsMapfile",
"Failed to launch VOMS mapfile monitoring thread");
104XrdVomsMapfile::ParseMapfile(
const std::string &mapfile)
106 std::ifstream fstr(mapfile);
107 if (!fstr.is_open()) {
108 m_edest->
Emsg(
"ParseMapfile",
"Failed to open file", mapfile.c_str(), strerror(errno));
111 std::shared_ptr<std::vector<MapfileEntry>> entries(
new std::vector<MapfileEntry>());
112 for (std::string line; std::getline(fstr, line); ) {
114 if (ParseLine(line, entry.m_path, entry.m_target) && !entry.m_path.empty()) {
115 if (m_edest->getMsgMask() & LogMask::Debug) {
116 m_edest->Log(LogMask::Debug,
"ParseMapfile", PathToString(entry.m_path).c_str(),
"->", entry.m_target.c_str());
118 entries->emplace_back(entry);
127XrdVomsMapfile::ParseLine(
const std::string &line, std::vector<std::string> &entry, std::string &target)
129 bool began_entry =
false;
130 bool finish_entry =
false;
131 bool began_target =
false;
134 for (
size_t idx=0; idx<line.size(); idx++) {
135 auto txt = line[idx];
136 if (!began_entry && !finish_entry) {
137 if (txt ==
'#') {
return false;}
138 else if (txt ==
'"') {began_entry =
true;}
139 else if (!isspace(txt)) {
return false;}
141 }
else if (began_entry && !finish_entry) {
143 if (idx + 1 == line.size()) {
return false;}
145 auto escaped_char = line[idx];
146 switch (escaped_char) {
171 }
else if (txt ==
'"') {
172 if (!element.empty()) entry.push_back(element);
174 }
else if (txt ==
'/') {
175 if (!element.empty()) entry.push_back(element);
177 }
else if (isprint(txt)) {
182 }
else if (!began_target) {
183 if (isspace(txt)) {
continue;}
189 }
else if (isspace(txt)) {
201XrdVomsMapfile::Map(
const std::vector<std::string> &fqan)
203 decltype(m_entries) entries = m_entries;
204 if (!entries) {
return "";}
206 if (m_edest && (m_edest->getMsgMask() & LogMask::Debug)) {
207 m_edest->Log(LogMask::Debug,
"VOMSMapfile",
"Mapping VOMS FQAN", PathToString(fqan).c_str());
210 for (
const auto &entry : *entries) {
211 if (Compare(entry, fqan)) {
212 if (m_edest && (m_edest->getMsgMask() & LogMask::Debug)) {
213 m_edest->Log(LogMask::Debug,
"VOMSMapfile",
"Mapped FQAN to target", entry.m_target.c_str());
215 return entry.m_target;
223XrdVomsMapfile::Compare(
const MapfileEntry &entry,
const std::vector<std::string> &fqan)
225 if (entry.m_path.empty()) {
return false;}
228 if (fqan.size() < entry.m_path.size()) {
return false;}
230 XrdOucString fqan_element;
231 for (
size_t idx=0; idx<entry.m_path.size(); idx++) {
232 fqan_element.
assign(fqan[idx].c_str(), 0);
233 const auto &path_element = entry.m_path[idx];
234 if (!fqan_element.
matches(path_element.c_str())) {
return false;}
236 if (fqan.size() == entry.m_path.size()) {
return true;}
237 if (entry.m_path.back() ==
"*") {
return true;}
242std::vector<std::string>
247 std::vector<std::string> path;
252 while ((from =
const_cast<XrdOucString&
>(group).tokenize(entry, from,
'/')) != -1) {
253 if (entry.
length() == 0)
continue;
254 path.emplace_back(entry.
c_str());
268 std::string gridmap_name;
269 auto gridmap_success = entity.
eaAPI->
Get(
"gridmap.name", gridmap_name);
270 if (gridmap_success && gridmap_name ==
"1") {
274 int from_vorg = 0, from_role = 0, from_grps = 0;
278 if (m_edest) m_edest->Log(LogMask::Debug,
"VOMSMapfile",
"Applying VOMS mapfile to incoming credential");
279 while (((from_vorg = vorg.
tokenize(entry_vorg, from_vorg,
' ')) != -1) &&
280 ((role ==
"") || (from_role = role.
tokenize(entry_role, from_role,
' ')) != -1) &&
281 ((from_grps = grps.
tokenize(entry_grps, from_grps,
' ')) != -1))
283 auto fqan = MakePath(entry_grps);
284 if (fqan.empty()) {
continue;}
288 if (strcmp(fqan[0].c_str(), entry_vorg.c_str())) {
continue;}
290 fqan.emplace_back(std::string(
"Role=") + entry_role.c_str());
291 fqan.emplace_back(
"Capability=NULL");
292 std::string username;
293 if (!(username = Map(fqan)).empty()) {
294 if (entity.
name) {free(entity.
name);}
295 entity.
name = strdup(username.c_str());
314 if (tried_configure) {
315 auto result = mapper.get();
317 result->SetErrorStream(erp);
322 tried_configure =
true;
325 if (erp) erp->
setMsgMask(LogMask::Error | LogMask::Warning);
327 char *config_filename =
nullptr;
332 XrdOucStream stream(erp, getenv(
"XRDINSTANCE"), &myEnv,
"=====> ");
335 if ((cfg_fd =
open(config_filename, O_RDONLY, 0)) < 0) {
336 if (erp) erp->
Emsg(
"Config", errno,
"open config file", config_filename);
341 std::string map_filename;
343 if (!strcmp(var,
"voms.mapfile")) {
345 if (!val || !val[0]) {
346 if (erp) erp->
Emsg(
"Config",
"VOMS mapfile not specified");
350 }
else if (!strcmp(var,
"voms.trace")) {
352 if (!val || !val[0]) {
353 if (erp) erp->
Emsg(
"Config",
"VOMS logging level not specified");
363 else if (!strcmp(val,
"none")) {erp->
setMsgMask(0);}
364 else {erp->
Emsg(
"Config",
"voms.trace encountered an unknown directive:", val);}
370 if (!map_filename.empty()) {
371 if (erp) erp->
Emsg(
"Config",
"Will initialize VOMS mapfile", map_filename.c_str());
372 mapper.reset(
new XrdVomsMapfile(erp, map_filename));
373 if (!mapper->IsValid()) {
374 mapper.reset(
nullptr);
384XrdVomsMapfile::MaintenanceThread(
void *myself_raw)
388 auto now = monotonic_time_s();
389 auto next_update = now + m_update_interval;
391 now = monotonic_time_s();
392 auto remaining = next_update - now;
393 auto rval = sleep(remaining);
398 next_update = monotonic_time_s() + m_update_interval;
400 if (-1 ==
stat(myself->m_mapfile.c_str(), &statbuf)) {
401 myself->m_edest->Emsg(
"XrdVomsMapfile", errno,
"Error checking the mapfile",
402 myself->m_mapfile.c_str());
403 myself->m_mapfile_ctime.tv_sec = 0;
404 myself->m_mapfile_ctime.tv_nsec = 0;
405 myself->m_is_valid =
false;
414 if ((myself->m_mapfile_ctime.tv_sec == statbuf.st_ctim.tv_sec) &&
415 (myself->m_mapfile_ctime.tv_nsec == statbuf.st_ctim.tv_nsec))
417 myself->m_edest->Log(LogMask::Debug,
"Maintenance",
"Not reloading VOMS mapfile; "
418 "no changes detected.");
421 memcpy(&myself->m_mapfile_ctime, &statbuf.st_ctim,
sizeof(
decltype(statbuf.st_ctim)));
423 myself->m_edest->Log(LogMask::Debug,
"Maintenance",
"Reloading VOMS mapfile now");
424 if ( !(myself->m_is_valid = myself->ParseMapfile(myself->m_mapfile)) ) {
425 myself->m_edest->Log(LogMask::Error,
"Maintenance",
"Failed to reload VOMS mapfile");
static bool Import(const char *var, char *&val)
char * GetMyFirstWord(int lowcase=0)
char * GetWord(int lowcase=0)
int Attach(int FileDescriptor, int bsz=2047)
void assign(const char *s, int j, int k=-1)
int matches(const char *s, char wch=' *')
int tokenize(XrdOucString &tok, int from, char del=':')
const char * c_str() const
XrdSecAttr * Get(const void *sigkey)
char * vorg
Entity's virtual organization(s)
XrdSecEntityAttr * eaAPI
non-const API to attributes
char * grps
Entity's group name(s)
char * name
Entity's name.
char * role
Entity's role(s)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
void setMsgMask(int mask)
static int Run(pthread_t *, void *(*proc)(void *), void *arg, int opts=0, const char *desc=0)
static XrdVomsMapfile * Get()
static XrdVomsMapfile * Configure(XrdSysError *)
virtual ~XrdVomsMapfile()
int Apply(XrdSecEntity &)