Back to index

nordugrid-arc-nox  1.1.0~rc6
MCCTLS.cpp
Go to the documentation of this file.
00001 #ifdef HAVE_CONFIG_H
00002 #include <config.h>
00003 #endif
00004 
00005 #include <sys/types.h>
00006 #ifndef WIN32
00007 #include <sys/socket.h>
00008 #include <arpa/inet.h>
00009 #else
00010 #define NOGDI
00011 #endif
00012 
00013 #include <iostream>
00014 #include <fstream>
00015 #include <vector>
00016 
00017 #include <arc/credential/VOMSUtil.h>
00018 
00019 #include <openssl/err.h>
00020 #include <openssl/rand.h>
00021 #include <openssl/ssl.h>
00022 
00023 #include <arc/message/PayloadStream.h>
00024 #include <arc/message/PayloadRaw.h>
00025 #include <arc/loader/Plugin.h>
00026 #include <arc/message/MCCLoader.h>
00027 #include <arc/XMLNode.h>
00028 #include <arc/message/SecAttr.h>
00029 #include <arc/crypto/OpenSSL.h>
00030 
00031 #include "GlobusSigningPolicy.h"
00032 
00033 #include "PayloadTLSStream.h"
00034 #include "PayloadTLSMCC.h"
00035 
00036 #include "DelegationCollector.h"
00037 
00038 #include "MCCTLS.h"
00039 
00040 namespace Arc {
00041 
00042 bool x509_to_string(X509* cert,std::string& str) {
00043   BIO *out = BIO_new(BIO_s_mem());
00044   if(!out) return false;
00045   if(!PEM_write_bio_X509(out,cert)) { BIO_free_all(out); return false; };
00046   for(;;) {
00047     char s[256];
00048     int l = BIO_read(out,s,sizeof(s));
00049     if(l <= 0) break;
00050     str.append(s,l);;
00051   };
00052   BIO_free_all(out);
00053   return true;
00054 }
00055 
00056 class TLSSecAttr: public SecAttr {
00057  friend class MCC_TLS_Service;
00058  friend class MCC_TLS_Client;
00059  public:
00060   TLSSecAttr(PayloadTLSStream&, ConfigTLSMCC& config, Logger& logger);
00061   virtual ~TLSSecAttr(void);
00062   virtual operator bool(void) const;
00063   virtual bool Export(SecAttrFormat format,XMLNode &val) const;
00064   std::string Identity(void) { return identity_; };
00065   std::string Subject(void) {
00066     if(subjects_.size() <= 0) return "";
00067     return *(--(subjects_.end()));
00068   };
00069   std::string CA(void) {
00070     if(subjects_.size() <= 0) return "";
00071     return *(subjects_.begin());
00072   };
00073   std::string X509Str(void) {
00074     return x509str_;
00075   };
00076  protected:
00077   std::string identity_; // Subject of last non-proxy certificate
00078   std::list<std::string> subjects_; // Subjects of all certificates in chain
00079   std::vector<std::string> voms_attributes_; // VOMS attributes from the VOMS extension of proxy
00080   std::string target_; // Subject of host certificate
00081   std::string x509str_; // The last certificate (in string format)
00082   virtual bool equal(const SecAttr &b) const;
00083 };
00084 }
00085 
00086 //Glib::Mutex Arc::MCC_TLS::lock_;
00087 Arc::Logger Arc::MCC_TLS::logger(Arc::MCC::logger,"TLS");
00088 
00089 Arc::MCC_TLS::MCC_TLS(Arc::Config& cfg,bool client) : Arc::MCC(&cfg), config_(cfg,logger,client) {
00090 }
00091 
00092 static Arc::Plugin* get_mcc_service(Arc::PluginArgument* arg) {
00093     Arc::MCCPluginArgument* mccarg =
00094             arg?dynamic_cast<Arc::MCCPluginArgument*>(arg):NULL;
00095     if(!mccarg) return NULL;
00096     return new Arc::MCC_TLS_Service(*(Arc::Config*)(*mccarg));
00097 }
00098 
00099 static Arc::Plugin* get_mcc_client(Arc::PluginArgument* arg) {
00100     Arc::MCCPluginArgument* mccarg =
00101             arg?dynamic_cast<Arc::MCCPluginArgument*>(arg):NULL;
00102     if(!mccarg) return NULL;
00103     return new Arc::MCC_TLS_Client(*(Arc::Config*)(*mccarg));
00104 }
00105 
00106 Arc::PluginDescriptor PLUGINS_TABLE_NAME[] = {
00107     { "tls.service", "HED:MCC", 0, &get_mcc_service },
00108     { "tls.client",  "HED:MCC", 0, &get_mcc_client  },
00109     { "delegation.collector", "HED:SHC", 0, &ArcSec::DelegationCollector::get_sechandler},
00110     { NULL, NULL, 0, NULL }
00111 };
00112 
00113 using namespace Arc;
00114 
00115 
00116 #define SELFSIGNED(cert) (X509_NAME_cmp(X509_get_issuer_name(cert),X509_get_subject_name(cert)) == 0)
00117  
00118 
00119 TLSSecAttr::TLSSecAttr(PayloadTLSStream& payload, ConfigTLSMCC& config, Logger& logger) {
00120    char buf[100];
00121    std::string subject;
00122    STACK_OF(X509)* peerchain = payload.GetPeerChain();
00123    voms_attributes_.clear();
00124    if(peerchain != NULL) {
00125       for(int idx = 0;;++idx) {
00126          if(idx >= sk_X509_num(peerchain)) break;
00127          X509* cert = sk_X509_value(peerchain,sk_X509_num(peerchain)-idx-1);
00128          if(idx == 0) { // Obtain CA subject
00129            // Sometimes certificates chain contains CA certificate.
00130            if(!SELFSIGNED(cert)) {           
00131              buf[0]=0;
00132              X509_NAME_oneline(X509_get_issuer_name(cert),buf,sizeof(buf));
00133              subject=buf;
00134              subjects_.push_back(subject);
00135            };
00136          };
00137          buf[0]=0;
00138          X509_NAME_oneline(X509_get_subject_name(cert),buf,sizeof(buf));
00139          subject=buf;
00140          subjects_.push_back(subject);
00141 #ifdef HAVE_OPENSSL_PROXY
00142          if(X509_get_ext_by_NID(cert,NID_proxyCertInfo,-1) < 0) {
00143             identity_=subject;
00144          };
00145 #endif
00146         // Parse VOMS attributes from each certificate of the peer chain.
00147         bool res = parseVOMSAC(cert, config.CADir(), config.CAFile(), config.VOMSCertTrustDN(), voms_attributes_);
00148         if(!res) {
00149           logger.msg(ERROR,"VOMS attribute parsing failed");
00150         };
00151       };
00152    };
00153    X509* peercert = payload.GetPeerCert();
00154    if (peercert != NULL) {
00155       if(subjects_.size() <= 0) { // Obtain CA subject if not obtained yet
00156         // Check for CA certificate used for connection - overprotection
00157         if(!SELFSIGNED(peercert)) {           
00158           buf[0]=0;
00159           X509_NAME_oneline(X509_get_issuer_name(peercert),buf,sizeof buf);
00160           subject=buf;
00161           subjects_.push_back(subject);
00162         };
00163       };
00164       buf[0]=0;
00165       X509_NAME_oneline(X509_get_subject_name(peercert),buf,sizeof buf);
00166       subject=buf;
00167       //logger.msg(VERBOSE, "Peer name: %s", peer_dn);
00168       subjects_.push_back(subject);
00169 #ifdef HAVE_OPENSSL_PROXY
00170       if(X509_get_ext_by_NID(peercert,NID_proxyCertInfo,-1) < 0) {
00171          identity_=subject;
00172       };
00173 #endif
00174       // Parse VOMS attributes from peer certificate
00175       bool res = parseVOMSAC(peercert, config.CADir(), config.CAFile(), config.VOMSCertTrustDN(), voms_attributes_);
00176       if(!res) {
00177         logger.msg(ERROR,"VOMS attribute parsing failed");
00178       };
00179       // Convert the x509 cert into string format
00180       x509_to_string(peercert, x509str_);
00181       X509_free(peercert);
00182    };
00183    if(identity_.empty()) identity_=subject;
00184    X509* hostcert = payload.GetCert();
00185    if (hostcert != NULL) {
00186       buf[0]=0;
00187       X509_NAME_oneline(X509_get_subject_name(hostcert),buf,sizeof buf);
00188       target_=buf;
00189       //logger.msg(VERBOSE, "Host name: %s", peer_dn);
00190    };
00191 }
00192 
00193 TLSSecAttr::~TLSSecAttr(void) {
00194 }
00195 
00196 TLSSecAttr::operator bool(void) const {
00197   return true;
00198 }
00199 
00200 bool TLSSecAttr::equal(const SecAttr &b) const {
00201   try {
00202     const TLSSecAttr& a = dynamic_cast<const TLSSecAttr&>(b);
00203     if (!a) return false;
00204     // ...
00205     return false;
00206   } catch(std::exception&) { };
00207   return false;
00208 }
00209 
00210 static void add_arc_subject_attribute(XMLNode item,const std::string& subject,const char* id) {
00211    XMLNode attr = item.NewChild("ra:SubjectAttribute");
00212    attr=subject; attr.NewAttribute("Type")="string";
00213    attr.NewAttribute("AttributeId")=id;
00214 }
00215 
00216 static void add_xacml_subject_attribute(XMLNode item,const std::string& subject,const char* id) {
00217    XMLNode attr = item.NewChild("ra:Attribute");
00218    attr.NewAttribute("DataType")="xs:string";
00219    attr.NewAttribute("AttributeId")=id;
00220    attr.NewChild("ra:AttributeValue") = subject;
00221 }
00222 
00223 bool TLSSecAttr::Export(SecAttrFormat format,XMLNode &val) const {
00224   if(format == UNDEFINED) {
00225   } else if(format == ARCAuth) {
00226     NS ns;
00227     ns["ra"]="http://www.nordugrid.org/schemas/request-arc";
00228     val.Namespaces(ns); val.Name("ra:Request");
00229     XMLNode item = val.NewChild("ra:RequestItem");
00230     XMLNode subj = item.NewChild("ra:Subject");
00231     std::list<std::string>::const_iterator s = subjects_.begin();
00232     std::string subject;
00233     if(s != subjects_.end()) {
00234       subject=*s;
00235       add_arc_subject_attribute(subj,subject,"http://www.nordugrid.org/schemas/policy-arc/types/tls/ca");
00236       for(;s != subjects_.end();++s) {
00237         subject=*s;
00238         add_arc_subject_attribute(subj,subject,"http://www.nordugrid.org/schemas/policy-arc/types/tls/chain");
00239       };
00240       add_arc_subject_attribute(subj,subject,"http://www.nordugrid.org/schemas/policy-arc/types/tls/subject");
00241     };
00242     if(!identity_.empty()) {
00243        add_arc_subject_attribute(subj,identity_,"http://www.nordugrid.org/schemas/policy-arc/types/tls/identity");
00244     };
00245     if(!voms_attributes_.empty()) {
00246       for(int k=0; k < voms_attributes_.size(); k++) {
00247         add_arc_subject_attribute(subj, voms_attributes_[k],"http://www.nordugrid.org/schemas/policy-arc/types/tls/vomsattribute");
00248       };
00249     };
00250     if(!target_.empty()) {
00251       XMLNode resource = item.NewChild("ra:Resource");
00252       resource=target_; resource.NewAttribute("Type")="string";
00253       resource.NewAttribute("AttributeId")="http://www.nordugrid.org/schemas/policy-arc/types/tls/hostidentity";
00254       // Following is agreed to not be use till all use cases are clarified (Bern agreement)
00255       //hostidentity should be SubjectAttribute, because hostidentity is be constrained to access
00256       //the peer delegation identity, or some resource which is attached to the peer delegation identity.
00257       //The constrant is defined in delegation policy.
00258       //add_arc_subject_attribute(subj,target_,"http://www.nordugrid.org/schemas/policy-arc/types/tls/hostidentity");
00259     };
00260     return true;
00261   } else if(format == XACML) {
00262     NS ns;
00263     ns["ra"]="urn:oasis:names:tc:xacml:2.0:context:schema:os";
00264     val.Namespaces(ns); val.Name("ra:Request");
00265     XMLNode subj = val.NewChild("ra:Subject");
00266     std::list<std::string>::const_iterator s = subjects_.begin();
00267     std::string subject;
00268     if(s != subjects_.end()) {
00269       subject=*s;
00270       add_xacml_subject_attribute(subj,subject,"http://www.nordugrid.org/schemas/policy-arc/types/tls/ca");
00271       for(;s != subjects_.end();++s) {
00272         subject=*s;
00273         add_xacml_subject_attribute(subj,subject,"http://www.nordugrid.org/schemas/policy-arc/types/tls/chain");
00274       };
00275       add_xacml_subject_attribute(subj,subject,"http://www.nordugrid.org/schemas/policy-arc/types/tls/subject");
00276     };
00277     if(!identity_.empty()) {
00278        add_xacml_subject_attribute(subj,identity_,"http://www.nordugrid.org/schemas/policy-arc/types/tls/identity");
00279     };
00280     if(!voms_attributes_.empty()) {
00281       for(int k=0; k < voms_attributes_.size(); k++) {
00282         add_xacml_subject_attribute(subj, voms_attributes_[k],"http://www.nordugrid.org/schemas/policy-arc/types/tls/vomsattribute");
00283       };
00284     };
00285     if(!target_.empty()) {
00286       XMLNode resource = val.NewChild("ra:Resource");
00287       XMLNode attr = resource.NewChild("ra:Attribute");
00288       attr.NewChild("ra:AttributeValue") = target_; 
00289       attr.NewAttribute("DataType")="xs:string";
00290       attr.NewAttribute("AttributeId")="http://www.nordugrid.org/schemas/policy-arc/types/tls/hostidentity";
00291       // Following is agreed to not be use till all use cases are clarified (Bern agreement)
00292       //hostidentity should be SubjectAttribute, because hostidentity is be constrained to access
00293       //the peer delegation identity, or some resource which is attached to the peer delegation identity.
00294       //The constrant is defined in delegation policy.
00295       //add_xacml_subject_attribute(subj,target_,"http://www.nordugrid.org/schemas/policy-arc/types/tls/hostidentity");
00296     };
00297     return true;
00298   } else {
00299   };
00300   return false;
00301 }
00302 
00303 class MCC_TLS_Context:public MessageContextElement {
00304  public:
00305   PayloadTLSMCC* stream;
00306   MCC_TLS_Context(PayloadTLSMCC* s = NULL):stream(s) { };
00307   virtual ~MCC_TLS_Context(void) { if(stream) delete stream; };
00308 };
00309 
00310 /* The main functionality of the constructor method is to 
00311    initialize SSL layer. */
00312 MCC_TLS_Service::MCC_TLS_Service(Config& cfg):MCC_TLS(cfg,false) {
00313    if(!OpenSSLInit()) return;
00314 }
00315 
00316 MCC_TLS_Service::~MCC_TLS_Service(void) {
00317    // SSL deinit not needed
00318 }
00319 
00320 MCC_Status MCC_TLS_Service::process(Message& inmsg,Message& outmsg) {
00321    // Accepted payload is StreamInterface 
00322    // Returned payload is undefined - currently holds no information
00323 
00324    // TODO: probably some other credentials check is needed
00325    //if(!sslctx_) return MCC_Status();
00326 
00327    // Obtaining underlying stream
00328    if(!inmsg.Payload()) return MCC_Status();
00329    PayloadStreamInterface* inpayload = NULL;
00330    try {
00331       inpayload = dynamic_cast<PayloadStreamInterface*>(inmsg.Payload());
00332    } catch(std::exception& e) { };
00333    if(!inpayload) return MCC_Status();
00334 
00335    // Obtaining previously created stream context or creating a new one
00336    MCC_TLS_Context* context = NULL;
00337    {   
00338       MessageContextElement* mcontext = (*inmsg.Context())["tls.service"];
00339       if(mcontext) {
00340          try {
00341             context = dynamic_cast<MCC_TLS_Context*>(mcontext);
00342          } catch(std::exception& e) { };
00343       };
00344    };
00345    PayloadTLSMCC *stream = NULL;
00346    if(context) {
00347       // Old connection - using available SSL stream
00348       stream=context->stream;
00349    } else {
00350       // Creating new SSL object bound to stream of previous MCC
00351       // TODO: renew stream because it may be recreated by TCP MCC
00352       stream = new PayloadTLSMCC(inpayload,config_,logger);
00353       context=new MCC_TLS_Context(stream);
00354       inmsg.Context()->Add("tls.service",context);
00355    };
00356  
00357    // Creating message to pass to next MCC
00358    Message nextinmsg = inmsg;
00359    nextinmsg.Payload(stream);
00360    Message nextoutmsg = outmsg; nextoutmsg.Payload(NULL);
00361 
00362    PayloadTLSStream* tstream = dynamic_cast<PayloadTLSStream*>(stream);
00363    // Filling security attributes
00364    if(tstream && (config_.IfClientAuthn())) {
00365       TLSSecAttr* sattr = new TLSSecAttr(*tstream, config_, logger);
00366       nextinmsg.Auth()->set("TLS",sattr);
00367       //Getting the subject name of peer(client) certificate
00368       logger.msg(VERBOSE, "Peer name: %s", sattr->Subject());
00369       nextinmsg.Attributes()->set("TLS:PEERDN",sattr->Subject());
00370       logger.msg(VERBOSE, "Identity name: %s", sattr->Identity());
00371       nextinmsg.Attributes()->set("TLS:IDENTITYDN",sattr->Identity());
00372       logger.msg(VERBOSE, "CA name: %s", sattr->CA());
00373       nextinmsg.Attributes()->set("TLS:CADN",sattr->CA());
00374 
00375       nextinmsg.Attributes()->set("TLS:PEERCERT",sattr->X509Str());
00376       if(!((sattr->target_).empty()))
00377         nextinmsg.Attributes()->set("TLS:LOCALDN",sattr->target_);
00378    }
00379 
00380    // Checking authentication and authorization;
00381    if(!ProcessSecHandlers(nextinmsg,"incoming")) {
00382       logger.msg(ERROR, "Security check failed in TLS MCC for incoming message");
00383       return MCC_Status();
00384    };
00385    
00386    // Call next MCC 
00387    MCCInterface* next = Next();
00388    if(!next)  return MCC_Status();
00389    MCC_Status ret = next->process(nextinmsg,nextoutmsg);
00390    // TODO: If next MCC returns anything redirect it to stream
00391    if(nextoutmsg.Payload()) {
00392       delete nextoutmsg.Payload();
00393       nextoutmsg.Payload(NULL);
00394    };
00395    if(!ret) return MCC_Status();
00396    // For nextoutmsg, nothing to do for payload of msg, but 
00397    // transfer some attributes of msg
00398    outmsg = nextoutmsg;
00399    return MCC_Status(STATUS_OK);
00400 }
00401 
00402 MCC_TLS_Client::MCC_TLS_Client(Config& cfg):MCC_TLS(cfg,true){
00403    stream_=NULL;
00404    if(!OpenSSLInit()) return;
00405    /* Get DN from certificate, and put it into message's attribute */
00406 }
00407 
00408 MCC_TLS_Client::~MCC_TLS_Client(void) {
00409    if(stream_) delete stream_;
00410    // SSL deinit not needed
00411 }
00412 
00413 MCC_Status MCC_TLS_Client::process(Message& inmsg,Message& outmsg) {
00414    // Accepted payload is Raw
00415    // Returned payload is Stream
00416    // Extracting payload
00417    if(!inmsg.Payload()) return MCC_Status();
00418    if(!stream_) return MCC_Status();
00419    PayloadRawInterface* inpayload = NULL;
00420    try {
00421       inpayload = dynamic_cast<PayloadRawInterface*>(inmsg.Payload());
00422    } catch(std::exception& e) { };
00423    if(!inpayload) return MCC_Status();
00424    // Collecting security attributes
00425    // TODO: keep them or redo same for incoming message
00426    PayloadTLSStream* tstream = dynamic_cast<PayloadTLSStream*>(stream_);
00427    if(tstream) {
00428       TLSSecAttr* sattr = new TLSSecAttr(*tstream, config_, logger);
00429       inmsg.Auth()->set("TLS",sattr);
00430       //Getting the subject name of peer(client) certificate
00431       logger.msg(VERBOSE, "Peer name: %s", sattr->Subject());
00432       inmsg.Attributes()->set("TLS:PEERDN",sattr->Subject());
00433       logger.msg(VERBOSE, "Identity name: %s", sattr->Identity());
00434       inmsg.Attributes()->set("TLS:IDENTITYDN",sattr->Identity());
00435       logger.msg(VERBOSE, "CA name: %s", sattr->CA());
00436       inmsg.Attributes()->set("TLS:CADN",sattr->CA());
00437    }
00438 
00439    //Checking authentication and authorization;
00440    if(!ProcessSecHandlers(inmsg,"outgoing")) {
00441       logger.msg(ERROR, "Security check failed in TLS MCC for outgoing message");
00442       return MCC_Status();
00443    };
00444    // Sending payload
00445    for(int n=0;;++n) {
00446       char* buf = inpayload->Buffer(n);
00447       if(!buf) break;
00448       int bufsize = inpayload->BufferSize(n);
00449       if(!(stream_->Put(buf,bufsize))) {
00450          logger.msg(ERROR, "Failed to send content of buffer");
00451          return MCC_Status();
00452       };
00453    };
00454    outmsg.Payload(new PayloadTLSMCC(*stream_));
00455    //outmsg.Attributes(inmsg.Attributes());
00456    //outmsg.Context(inmsg.Context());
00457    if(!ProcessSecHandlers(outmsg,"incoming")) {
00458       logger.msg(ERROR, "Security check failed in TLS MCC for incoming message");
00459       delete outmsg.Payload(NULL); return MCC_Status();
00460    };
00461    return MCC_Status(STATUS_OK);
00462 }
00463 
00464 
00465 void MCC_TLS_Client::Next(MCCInterface* next,const std::string& label) {
00466    if(label.empty()) {
00467       if(stream_) delete stream_;
00468       stream_=NULL;
00469       stream_=new PayloadTLSMCC(next,config_,logger);
00470    };
00471    MCC::Next(next,label);
00472 }