Back to index

nordugrid-arc-nox  1.1.0~rc6
charon.cpp
Go to the documentation of this file.
00001 #ifdef HAVE_CONFIG_H
00002 #include <config.h>
00003 #endif
00004 
00005 #include <iostream>
00006 
00007 #include <sys/types.h>
00008 #include <sys/stat.h>
00009 #include <unistd.h>
00010 
00011 #ifndef WIN32
00012 #include <pwd.h>
00013 #endif
00014 
00015 #include <arc/message/PayloadSOAP.h>
00016 #include <arc/message/PayloadRaw.h>
00017 #include <arc/message/PayloadStream.h>
00018 #include <arc/ws-addressing/WSA.h>
00019 #include <arc/security/ArcPDP/EvaluatorLoader.h>
00020 #include <arc/security/ArcPDP/Source.h>
00021 #include <arc/Thread.h>
00022 #include <arc/StringConv.h>
00023 #include <arc/GUID.h>
00024 
00025 #include "charon.h"
00026 
00027 namespace ArcSec {
00028 
00029 #define SAML_NAMESPACE "urn:oasis:names:tc:SAML:2.0:assertion"
00030 #define SAMLP_NAMESPACE "urn:oasis:names:tc:SAML:2.0:protocol"
00031 #define XACML_SAMLP_NAMESPACE "urn:oasis:names:tc:xacml:2.0:saml:protocol:schema:os"
00032 #define XACML_SAML_NAMESPACE "urn:oasis:names:tc:xacml:2.0:saml:assertion:schema:os"
00033 #define XACML_CONTEXT_NAMESPACE "urn:oasis:names:tc:xacml:2.0:context:schema:os"
00034 
00035 static Arc::LogStream logcerr(std::cerr);
00036 
00037 static Arc::Plugin* get_service(Arc::PluginArgument* arg) {
00038     Arc::ServicePluginArgument* srvarg =
00039             arg?dynamic_cast<Arc::ServicePluginArgument*>(arg):NULL;
00040     if(!srvarg) return NULL;
00041     return new Charon((Arc::Config*)(*srvarg));
00042 }
00043 
00044 Arc::MCC_Status Charon::make_soap_fault(Arc::Message& outmsg, const std::string& msg) {
00045   Arc::PayloadSOAP* outpayload = new Arc::PayloadSOAP(ns_,true);
00046   Arc::SOAPFault* fault = outpayload?outpayload->Fault():NULL;
00047   if(fault) {
00048     fault->Code(Arc::SOAPFault::Sender);
00049     if(msg.empty()) {
00050       fault->Reason("Failed processing request");
00051     } else {
00052       fault->Reason(msg);
00053     };
00054   };
00055   outmsg.Payload(outpayload);
00056   return Arc::MCC_Status(Arc::STATUS_OK);
00057 }
00058 
00059 Arc::MCC_Status Charon::process(Arc::Message& inmsg,Arc::Message& outmsg) {
00060 
00061   std::string method = inmsg.Attributes()->get("HTTP:METHOD");
00062 
00063   // Identify which of served endpoints request is for.
00064   // Charon can only accept POST method
00065   if(method == "POST") {
00066     logger.msg(Arc::VERBOSE, "process: POST");
00067     // Both input and output are supposed to be SOAP
00068     // Extracting payload
00069     Arc::PayloadSOAP* inpayload = NULL;
00070     try {
00071       inpayload = dynamic_cast<Arc::PayloadSOAP*>(inmsg.Payload());
00072     } catch(std::exception& e) { };
00073     if(!inpayload) {
00074       logger.msg(Arc::ERROR, "input is not SOAP");
00075       return make_soap_fault(outmsg);
00076     };
00077     // Analyzing request
00078     Arc::XMLNode request = (*inpayload)["GetPolicyDecisionRequest"];
00079     if(!request) {
00080       request = (*inpayload)["XACMLAuthzDecisionQuery"];
00081       if(!request) {
00082         logger.msg(Arc::ERROR, "soap body does not include any request node");
00083         return make_soap_fault(outmsg);
00084       };
00085     };
00086     {
00087       std::string req_xml;
00088       request.GetXML(req_xml);
00089       logger.msg(Arc::VERBOSE, "Request: %s",req_xml);
00090     };    
00091 
00092     Arc::XMLNode arc_requestnd;
00093     bool use_saml = false;
00094     std::string request_name = request.Name();
00095     if(request_name.find("GetPolicyDecisionRequest") != std::string::npos)
00096       arc_requestnd = request["Request"];
00097     else if(request_name.find("XACMLAuthzDecisionQuery") != std::string::npos) {
00098       arc_requestnd = request["Request"];
00099       use_saml = true;
00100     }
00101     if(!arc_requestnd) {
00102       logger.msg(Arc::ERROR, "request node is empty");
00103       return make_soap_fault(outmsg);
00104     };
00105 
00106     if(!eval) {
00107       logger.msg(Arc::ERROR, "Evaluator is not initialized");
00108       return make_soap_fault(outmsg,"Internal policy processing error");
00109     };
00110 
00111     //Call the functionality of policy engine
00112     //Here the decision algorithm is the same as that in ArcPDP.cpp,
00113     //so this algorithm implicitly applies to policy with Arc format;
00114     //for xacml policy (the "rlist" will only contains one item), this 
00115     //decision algorithm also applies.
00116     Response *resp = NULL;
00117     // Evaluator stores results inside, hence can't be used multi-threaded
00118     // TODO: fix multi-threading for Evaluator
00119     Glib::Mutex::Lock eval_lock(lock_);
00120     if(policies_modified()) {
00121       logger.msg(Arc::INFO, "Policy(ies) modified - reloading evaluator");
00122       load_policies();
00123     }
00124     resp = eval->evaluate(Source(arc_requestnd));
00125     ResponseList rlist = resp->getResponseItems();
00126     int size = rlist.size();
00127     int i;
00128 
00129     bool atleast_onedeny = false;
00130     bool atleast_onepermit = false;
00131 
00132     for(i = 0; i < size; i++){
00133       ResponseItem* item = rlist[i];
00134       RequestTuple* tp = item->reqtp;
00135 
00136       if(item->res == DECISION_DENY)
00137         atleast_onedeny = true;
00138       if(item->res == DECISION_PERMIT)
00139         atleast_onepermit = true;
00140      
00141       if(tp) {
00142         Subject::iterator it;
00143         Subject subject = tp->sub;
00144         for (it = subject.begin(); it!= subject.end(); it++){
00145           AttributeValue *attrval;
00146           RequestAttribute *attr;
00147           attr = dynamic_cast<RequestAttribute*>(*it);
00148           if(attr){
00149             attrval = (*it)->getAttributeValue();
00150             if(attrval) logger.msg(Arc::INFO, "%s", (attrval->encode()));
00151           }
00152         }
00153       }
00154     }
00155 
00156     bool result = false;
00157     if(atleast_onedeny) result = false;
00158     else if(!atleast_onedeny && atleast_onepermit) result = true;
00159     else if(!atleast_onedeny && !atleast_onepermit) result = false;
00160 
00161     if(result) logger.msg(Arc::INFO, "Authorized from Charon service");
00162     else logger.msg(Arc::ERROR, "Not authorized from Charon service; Some of the RequestItem does not satisfy Policy");
00163 
00164     // Put those request items which satisfy the policies into the 
00165     // response SOAP message (implicitly means the decision result: 
00166     // <ItemA, permit>, <ItemB, permit>, <ItemC, deny> (ItemC will 
00167     // not be in the response SOAP message).
00168     // The client of the pdpservice (normally a policy enforcement 
00169     // point, like job executor) is supposed to compose the policy
00170     // decision request to pdpservice by parsing the information 
00171     // from the request (aiming to the client itself), and permit 
00172     // or deny the request by using the information responded from 
00173     // pdpservice; Here pdpservice only gives some coarse decision 
00174     // to pdpservice invoker.
00175     if(!use_saml) {
00176       Arc::PayloadSOAP* outpayload = new Arc::PayloadSOAP(ns_);
00177       Arc::XMLNode response = outpayload->NewChild("pdp:GetPolicyDecisionResponse");
00178       Arc::XMLNode arc_responsend = response.NewChild("response:Response");
00179       Arc::XMLNode authz_res = arc_responsend.NewChild("response:AuthZResult");
00180       if(result) authz_res = "PERMIT";
00181       else authz_res = "DENY";
00182 
00183       for(i = 0; i<size; i++){
00184         ResponseItem* item = rlist[i];
00185         if(item->res == DECISION_PERMIT)
00186           arc_responsend.NewChild(Arc::XMLNode(item->reqxml));
00187       }
00188       outmsg.Payload(outpayload);
00189 
00190     } else {
00191       Arc::NS samlp_ns, saml_ns;
00192       samlp_ns["samlp"] = SAMLP_NAMESPACE;
00193       saml_ns["saml"] = SAML_NAMESPACE;
00194       saml_ns["xsi"] = "http://www.w3.org/2001/XMLSchema-instance";
00195 
00196       //<saml:Response/>
00197       Arc::XMLNode authz_response(samlp_ns, "samlp:Response");
00198       std::string local_dn = inmsg.Attributes()->get("TLS:LOCALDN");
00199       std::string issuer_name = Arc::convert_to_rdn(local_dn);
00200       authz_response.NewChild("samlp:Issuer") = issuer_name;
00201 
00202       std::string response_id = Arc::UUID();
00203       authz_response.NewAttribute("ID") = response_id;
00204       std::string responseto_id = (std::string)(request.Attribute("ID"));
00205       authz_response.NewAttribute("InResponseTo") = responseto_id;
00206       Arc::Time t;
00207       std::string current_time = t.str(Arc::UTCTime);
00208       authz_response.NewAttribute("IssueInstant") = current_time;
00209       authz_response.NewAttribute("Version") = std::string("2.0");
00210 
00211       Arc::XMLNode status = authz_response.NewChild("samlp:Status");
00212       Arc::XMLNode statuscode = status.NewChild("samlp:StatusCode");
00213       std::string statuscode_value = "urn:oasis:names:tc:SAML:2.0:status:Success";
00214       statuscode.NewAttribute("Value") = statuscode_value;
00215 
00216       //<saml:Assertion/>
00217       Arc::XMLNode assertion = authz_response.NewChild("saml:Assertion", saml_ns);
00218       assertion.NewAttribute("Version") = std::string("2.0");
00219       std::string assertion_id = Arc::UUID();
00220       assertion.NewAttribute("ID") = assertion_id;
00221       Arc::Time t1;
00222       std::string current_time1 = t1.str(Arc::UTCTime);
00223       assertion.NewAttribute("IssueInstant") = current_time1;
00224       assertion.NewChild("saml:Issuer") = issuer_name;
00225 
00226       //<saml:XACMLAuthzDecisionStatement/>
00227       Arc::NS xacml_saml_ns; xacml_saml_ns["xacml-saml"] = XACML_SAML_NAMESPACE;
00228       Arc::XMLNode authz_statement = assertion.NewChild("xacml-saml:XACMLAuthzDecisionStatement", xacml_saml_ns);
00229       //See "xacml-context" specification
00230       Arc::NS xacml_ns; xacml_ns["xacml-context"] = XACML_CONTEXT_NAMESPACE;
00231       Arc::XMLNode xacml_response = authz_statement.NewChild("xacml-context:Response", xacml_ns);
00232       Arc::XMLNode xacml_result = xacml_response.NewChild("xacml-context:Result");
00233       xacml_result.NewAttribute("ResourceId") = "resource-id"; //TODO
00234       if(result) xacml_result.NewChild("xacml-context:Decision") = "Permit";
00235       else xacml_result.NewChild("xacml-context:Decision") = "Deny"; //TODO: Indeterminate, NotApplicable
00236       //TODO: "xacml-context:Status"  "xacml:Obligations"
00237 
00238 
00239       Arc::NS soap_ns;
00240       Arc::SOAPEnvelope envelope(soap_ns);
00241       envelope.NewChild(authz_response);
00242       Arc::PayloadSOAP *outpayload = new Arc::PayloadSOAP(envelope);
00243 
00244       std::string tmp;
00245       outpayload->GetXML(tmp);
00246       std::cout<<"Output payload: "<<tmp<<std::endl;
00247 
00248       outmsg.Payload(outpayload);
00249     }
00250 
00251     if(resp) delete resp;
00252     
00253     return Arc::MCC_Status(Arc::STATUS_OK);
00254   } 
00255   else {
00256     delete inmsg.Payload();
00257     logger.msg(Arc::VERBOSE, "process: %s: not supported",method);
00258     return Arc::MCC_Status(); 
00259   }
00260   return Arc::MCC_Status();
00261 }
00262 
00263 Charon::Charon(Arc::Config *cfg):RegisteredService(cfg), logger_(Arc::Logger::rootLogger, "Charon"), eval(NULL) {
00264   logger_.addDestination(logcerr);
00265   // Define supported namespaces
00266   ns_["ra"]="http://www.nordugrid.org/schemas/request-arc";
00267   ns_["response"]="http://www.nordugrid.org/schemas/response-arc";
00268   ns_["pdp"]="http://www.nordugrid.org/schemas/pdp";
00269 
00270   //Load the Evaluator object
00271   evaluator_name_ = (std::string)((*cfg)["Evaluator"].Attribute("name"));
00272   logger.msg(Arc::INFO, "Evaluator: %s", evaluator_name_);
00273 
00274   //Get the policy location, and put it into evaluator
00275   Arc::XMLNode policystore = (*cfg)["PolicyStore"];
00276   Arc::XMLNode location = policystore["Location"];
00277   for(;(bool)location;++location) {
00278     std::string policyfile = location;
00279     std::string autoreload = location.Attribute("AutoReload");
00280     bool autoreload_b = true;
00281     if((autoreload == "false") || (autoreload == "0")) autoreload_b = false;
00282     if(policyfile.empty()) continue;
00283     locations_.push_back(PolicyLocation(policyfile,autoreload_b));
00284     logger.msg(Arc::INFO, "Policy location: %s", policyfile);
00285   }
00286   load_policies();
00287 }
00288 
00289 bool Charon::policies_modified(void) {
00290   for(std::list<PolicyLocation>::iterator p = locations_.begin();
00291                    p != locations_.end(); ++p) {
00292     if(p->IsModified()) return true;
00293   }
00294   return false;
00295 }
00296 
00297 bool Charon::load_policies(void) {
00298   ArcSec::EvaluatorLoader eval_loader;
00299   if(eval) delete eval;
00300   eval = eval_loader.getEvaluator(evaluator_name_);
00301   if(eval == NULL) {
00302     logger.msg(Arc::ERROR, "Can not dynamically produce Evaluator");
00303     return false;
00304   }
00305   else logger.msg(Arc::INFO, "Succeeded to produce Evaluator");
00306   for(std::list<PolicyLocation>::iterator p = locations_.begin();
00307                    p != locations_.end(); ++p) {
00308     logger.msg(Arc::VERBOSE, "Loading policy from %s",p->path);
00309     SourceFile source(p->path);
00310     if(!source) {
00311       logger.msg(Arc::ERROR, "Failed loading policy from %s",p->path);
00312       delete eval;
00313       eval = NULL;
00314       return false;
00315     }
00316     eval->addPolicy(SourceFile(p->path));
00317   }
00318   return true;
00319 }
00320 
00321 
00322 Charon::~Charon(void) {
00323   if(eval)
00324     delete eval;
00325   eval = NULL;
00326 }
00327 
00328 bool Charon::RegistrationCollector(Arc::XMLNode &doc) {
00329   Arc::NS isis_ns; isis_ns["isis"] = "http://www.nordugrid.org/schemas/isis/2008/08";
00330   Arc::XMLNode regentry(isis_ns, "RegEntry");
00331   regentry.NewChild("SrcAdv").NewChild("Type") = "org.nordugrid.security.charon";
00332   regentry.New(doc);
00333   return true;
00334 }
00335 
00336 Charon::PolicyLocation::PolicyLocation(const std::string& location,bool autoreload) {
00337   reload = autoreload;
00338   struct stat st;
00339   if(stat(location.c_str(), &st) != 0) return;
00340   if(!S_ISREG(st.st_mode)) return;
00341   mtime = st.st_mtime;
00342   ctime = st.st_ctime;
00343   path = location;
00344 }
00345 
00346 bool Charon::PolicyLocation::IsModified(void) {
00347   if(!reload) return false;
00348   logger.msg(Arc::DEBUG, "Checking policy modification: %s",path);
00349   if(path.empty()) return false;
00350   struct stat st;
00351   if((stat(path.c_str(), &st) != 0) || (!S_ISREG(st.st_mode))) {
00352     logger.msg(Arc::INFO, "Policy removed: %s", path);
00353     return true;
00354   }
00355   logger.msg(Arc::DEBUG, "Old policy times: %u/%u",(unsigned int)mtime,(unsigned int)ctime);
00356   logger.msg(Arc::DEBUG, "New policy times: %u/%u",(unsigned int)st.st_mtime,(unsigned int)st.st_ctime);
00357   if((mtime == st.st_mtime) && (ctime == st.st_ctime)) return false;
00358   mtime = st.st_mtime;
00359   ctime = st.st_ctime;
00360   logger.msg(Arc::INFO, "Policy modified: %s", path);
00361   return true;
00362 }
00363 
00364 
00365 } // namespace ArcSec
00366 
00367 Arc::PluginDescriptor PLUGINS_TABLE_NAME[] = {
00368     { "charon", "HED:SERVICE", 0, &ArcSec::get_service },
00369     { NULL, NULL, 0, NULL }
00370 };
00371