9
#include <arc/Logger.h>
10
#include <arc/IString.h>
11
#include <arc/StringConv.h>
12
#include <arc/IniConfig.h>
13
#include <arc/ArcRegex.h>
14
#include <arc/credential/Credential.h>
15
#include <arc/credential/VOMSUtil.h>
16
#include <arc/credential/VOMSAttribute.h>
18
#define AC_POLICY_PARAM_NAME "ac_policy"
20
static Arc::Logger logger(Arc::Logger::rootLogger, "arc-vomsac-check");
22
static void usage(char *pname) {
23
logger.msg(Arc::ERROR,"Usage: %s [-N] -P <user proxy> -L <job status file> [-c <configfile>] [-d <loglevel>]",pname);
26
/* ARC classes use '/VO=voname/Group=groupname/subgroupname/Role=role' notation
27
* VOMS classes use '/voname/groupname/subgroupname/Role=role/Capability=NULL' notation
28
* For configuration compatibility reasons normalization to common format used
29
* either for values provided in config or retreived from proxy certificate
31
std::string normalize_fqan(std::string fqan) {
32
// first trim possible spaces and quotes
33
std::string nfqan = Arc::trim(fqan," \"");
34
// remove 'VO=' and 'Group=' if any
35
std::size_t pos = nfqan.find("VO=");
36
if(pos != std::string::npos) nfqan.erase(pos, 3);
37
pos = nfqan.find("Group=");
38
if(pos != std::string::npos) nfqan.erase(pos, 6);
40
pos = nfqan.find("/Role=NULL");
41
if(pos != std::string::npos) nfqan.erase(pos, 10);
42
pos = nfqan.find("/Capability=NULL");
43
if(pos != std::string::npos) nfqan.erase(pos, 16);
44
// return normalized fqan
48
int main(int argc, char *argv[]) {
50
int no_ac_success = 0;
51
const char *user_proxy_f = NULL;
52
const char *job_local_f = NULL;
53
const char *config_f = NULL;
56
Arc::LogStream logcerr(std::cerr);
57
Arc::Logger::getRootLogger().addDestination(logcerr);
58
Arc::Logger::getRootLogger().setThreshold(Arc::ERROR);
61
while ((opt = getopt(argc, argv, "NP:L:c:d:")) != -1) {
67
user_proxy_f = optarg;
76
Arc::Logger::getRootLogger().setThreshold(
77
Arc::old_level_to_level(atoi(optarg))
88
if ( !user_proxy_f ) {
89
logger.msg(Arc::ERROR,"User proxy file is required but is not specified");
94
logger.msg(Arc::ERROR,"Local job status file is required");
99
config_f = "/etc/arc.conf";
102
// read information about the job used by the A-REX
103
// and determine selected queue
105
std::ifstream job_local;
106
job_local.open(job_local_f, std::ios::in);
107
if ( job_local.is_open() ) {
109
while ( ! job_local.eof() ){
110
getline(job_local,line);
111
if ( ! line.compare(0,6,"queue=") ) {
112
queue = line.substr(6);
113
logger.msg(Arc::INFO,"Making the decision for the queue %s",queue);
118
logger.msg(Arc::ERROR,"Can not read information from the local job status file");
123
// Parse INI configuraion file
124
Arc::IniConfig cfg(config_f);
126
logger.msg(Arc::ERROR,"Can not parse the configuration file %s",config_f);
130
// get queue ac_policy
131
// first try [queue/name] block directly, then search for 'id' or 'name' field in [queue] blocks
132
std::string qqueue = '"' + queue + '"';
133
Arc::XMLNode qparams = cfg["queue/" + queue];
134
if ( ! (bool)qparams ) {
135
for(Arc::XMLNode qnode = cfg["queue"];(bool)qnode;++qnode) {
136
if ( (std::string)qnode["id"] == qqueue || (std::string)qnode["name"] == qqueue ) {
142
if ( ! (bool)qparams) {
143
logger.msg(Arc::ERROR,"Can not find queue '%s' in the configuration file",queue);
147
// create match regexes from ac_policy provided
148
std::vector< std::pair<Arc::RegularExpression,bool> > access_policies;
149
for ( Arc::XMLNode pnode = qparams[AC_POLICY_PARAM_NAME];(bool)pnode;++pnode) {
150
std::string acp = (std::string)pnode;
151
std::size_t pos = acp.find("VOMS:");
152
if ( pos != std::string::npos ) {
153
// determine positive/negative match
156
char pnflag = acp[pos-1];
157
if ( pnflag == '-' || pnflag == '!' ) pmatch = false;
159
// normalize rest part of the string
160
std::string regex = ".*" + normalize_fqan(acp.substr(pos + 5)) + ".*";
161
// save (regex,pmatch) pairs to access_policies vector
162
std::pair <Arc::RegularExpression,bool> match_regex(Arc::RegularExpression(regex),pmatch);
163
access_policies.push_back(match_regex);
166
if ( access_policies.empty() ) {
167
logger.msg(Arc::INFO,"No access policy to check, returning success");
171
struct stat statFInfo;
173
// CA Cert directory required to work with proxy
174
std::string ca_dir = (std::string)cfg["common"]["x509_cert_dir"];
175
if (ca_dir.empty()) {
176
ca_dir = "/etc/grid-security/certificates";
178
ca_dir = Arc::trim(ca_dir,"\"");
180
if ( stat(ca_dir.c_str(),&statFInfo) ) {
181
logger.msg(Arc::ERROR,"CA certificates directory %s does not exist", ca_dir);
185
// VOMS directory required to verify VOMS ACs
186
std::string voms_dir = (std::string)cfg["common"]["x509_voms_dir"];
187
if (voms_dir.empty()) {
188
voms_dir = "/etc/grid-security/vomsdir";
190
voms_dir = Arc::trim(voms_dir,"\"");
192
// Or maybe not _required_
193
//if ( stat(voms_dir.c_str(),&statFInfo) ) {
194
// logger.msg(Arc::ERROR,"VOMS directory %s does not exist", voms_dir);
195
// return EXIT_FAILURE;
198
// construct ARC credentials object
199
Arc::Credential holder(user_proxy_f, "", ca_dir, "");
200
if (! holder.GetVerification()) {
201
logger.msg(Arc::ERROR,"User proxy certificate is not valid");
205
// get VOMS AC from proxy certificate
206
logger.msg(Arc::DEBUG,"Getting VOMS AC for: %s", holder.GetDN());
207
std::vector<Arc::VOMSACInfo> voms_attributes;
208
Arc::VOMSTrustList vomscert_trust_dn;
209
vomscert_trust_dn.AddRegex(".*");
211
if ( ! Arc::parseVOMSAC(holder, ca_dir, "", voms_dir, vomscert_trust_dn, voms_attributes, true, true) ) {
212
// logger.msg(Arc::WARNING,"Error parsing VOMS AC");
213
if ( no_ac_success ) return EXIT_SUCCESS;
217
// loop over access_policies
218
for (std::vector<std::pair<Arc::RegularExpression,bool> >::iterator iP = access_policies.begin();
219
iP != access_policies.end(); iP++) {
220
logger.msg(Arc::VERBOSE,"Checking a match for '%s'",(iP->first).getPattern());
221
// for every VOMS AC provided
222
for (std::vector<Arc::VOMSACInfo>::iterator iAC = voms_attributes.begin(); iAC != voms_attributes.end(); iAC++) {
223
// check every attribure specified to match specified policy
224
for (int acnt = 0; acnt < iAC->attributes.size(); acnt++ ) {
225
std::string fqan = normalize_fqan(iAC->attributes[acnt]);
226
if ( (iP->first).match(fqan) ) {
227
logger.msg(Arc::DEBUG,"FQAN '%s' IS a match to '%s'",fqan,(iP->first).getPattern());
228
// if positive match return success
229
if ( iP->second ) return EXIT_SUCCESS;
230
// prohibit execution on negative match
231
logger.msg(Arc::ERROR,"Queue '%s' usage is prohibited to FQAN '%s' by the site access policy",
235
logger.msg(Arc::DEBUG,"FQAN '%s' IS NOT a match to '%s'",fqan,(iP->first).getPattern());
241
logger.msg(Arc::ERROR,"Queue '%s' usage with provided FQANs is prohibited by the site access policy",queue);