Back to index

nordugrid-arc-nox  1.1.0~rc6
SAMLToken.cpp
Go to the documentation of this file.
00001 #ifdef HAVE_CONFIG_H
00002 #include <config.h>
00003 #endif
00004 
00005 #ifdef WIN32
00006 #include <arc/win32.h>
00007 #endif
00008 #include <stdlib.h>
00009 #include <sys/time.h>
00010 
00011 #include <string>
00012 #include <sstream>
00013 #include <fstream>
00014 #include <iostream>
00015 //#include <iomanip>
00016 
00017 #include <glibmm.h>
00018 
00019 #include <xmlsec/base64.h>
00020 #include <xmlsec/errors.h>
00021 #include <xmlsec/xmltree.h>
00022 #include <xmlsec/xmldsig.h>
00023 #include <xmlsec/xmlenc.h>
00024 #include <xmlsec/templates.h>
00025 #include <xmlsec/crypto.h>
00026 
00027 #include <xmlsec/openssl/app.h>
00028 #include <openssl/bio.h>
00029 
00030 #include <openssl/evp.h>
00031 #include <openssl/sha.h>
00032 #include <openssl/rand.h>
00033 #ifdef CHARSET_EBCDIC
00034 #include <openssl/ebcdic.h>
00035 #endif 
00036 
00037 #include <arc/DateTime.h>
00038 #include <arc/Base64.h>
00039 #include <arc/StringConv.h>
00040 #include <arc/GUID.h>
00041 
00042 #include <arc/xmlsec/XmlSecUtils.h>
00043 #include <arc/xmlsec/XMLSecNode.h>
00044 #include <arc/credential/Credential.h>
00045 #include "SAMLToken.h"
00046 
00047 namespace Arc {
00048 
00049 #define WSSE_NAMESPACE   "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
00050 #define WSSE11_NAMESPACE "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"
00051 #define WSU_NAMESPACE    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
00052 #define XENC_NAMESPACE   "http://www.w3.org/2001/04/xmlenc#"
00053 #define DSIG_NAMESPACE   "http://www.w3.org/2000/09/xmldsig#"
00054 
00055 #define SAMLTOKEN_BASE_URL "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-saml-token-profile-1.0"
00056 #define SAML_NAMESPACE "urn:oasis:names:tc:SAML:1.0:assertion"
00057 #define SAML2_NAMESPACE "urn:oasis:names:tc:SAML:2.0:assertion"
00058 #define SAMLP_NAMESPACE "urn:oasis:names:tc:SAML:1.0:protocol"
00059 
00060 bool SAMLToken::Check(SOAPEnvelope& soap) {
00061   XMLNode header = soap.Header();
00062   if(header.NamespacePrefix(WSSE_NAMESPACE).empty()){
00063     std::cerr<<"No wsse namespace in SOAP Header"<<std::endl;
00064     return false;
00065   }
00066   XMLNode wsse;
00067   if(!(wsse=header["wsse:Security"])) {
00068     std::cerr<<"No Security element in SOAP Header"<<std::endl;
00069     return false;
00070   };
00071   if(!(wsse["Assertion"])) {
00072     std::cerr<<"No SAMLToken element in SOAP Header"<<std::endl;
00073     return false;
00074   };
00075   if((bool)(wsse["Assertion"]["AssertionID"])) samlversion = SAML1;
00076   else samlversion = SAML2;
00077   return true;
00078 }
00079 
00080 SAMLToken::operator bool(void) {
00081   return (bool)header;
00082 }
00083 
00084 SAMLToken::SAMLToken(SOAPEnvelope& soap) : SOAPEnvelope(soap){
00085   if(!Check(soap)){
00086     return;
00087   }
00088 
00089   //if(!init_xmlsec()) return;
00090   assertion_signature_nd = NULL;
00091   wsse_signature_nd = NULL; 
00092 
00093   // Apply predefined namespace prefix
00094   NS ns;
00095   ns["wsse"]=WSSE_NAMESPACE;
00096   ns["wsse11"]=WSSE11_NAMESPACE;
00097   ns["wsu"]=WSU_NAMESPACE;
00098   header.Namespaces(ns);
00099 
00100   XMLNode st = header["wsse:Security"];   
00101   XMLNode wsse_signature = st["Signature"];
00102   XMLNode assertion;
00103   assertion = st["Assertion"];
00104   XMLNode assertion_signature = assertion["Signature"];
00105   xmlNodePtr bodyPtr = ((SAMLToken*)(&body))->node_;
00106   xmlDocPtr docPtr = bodyPtr->doc;
00107   xmlNodePtr assertionPtr = ((SAMLToken*)(&assertion))->node_;
00108 
00109   xmlChar* id;
00110   xmlAttrPtr id_attr;
00111   //Assertion reference
00112   if(samlversion == SAML1) {
00113     id = xmlGetProp(assertionPtr, (xmlChar *)"AssertionID");
00114     id_attr = NULL; id_attr = xmlHasProp(assertionPtr, (xmlChar *)"AssertionID");
00115     if(id_attr == NULL) std::cerr<<"Can not find AssertionID attribute from saml:Assertion"<<std::endl;
00116     xmlAddID(NULL, docPtr, (xmlChar *)id, id_attr);
00117     xmlFree(id);
00118   }
00119   else {
00120     id = xmlGetProp(assertionPtr, (xmlChar *)"ID");
00121     id_attr = NULL; id_attr = xmlHasProp(assertionPtr, (xmlChar *)"ID");
00122     if(id_attr == NULL) std::cerr<<"Can not find ID attribute from saml:Assertion"<<std::endl;
00123     xmlAddID(NULL, docPtr, (xmlChar *)id, id_attr);
00124     xmlFree(id);
00125   }
00126 
00127   //Signature under saml:Assertion
00128   assertion_signature_nd = ((SAMLToken*)(&assertion_signature))->node_;
00129   if(!assertion_signature_nd) { std::cerr<<"No Signature node in saml:Assertion"<<std::endl; return; }
00130 
00131   //Body reference
00132   id = xmlGetProp(bodyPtr, (xmlChar *)"Id");
00133   id_attr = xmlHasProp(bodyPtr, (xmlChar *)"Id");
00134   xmlAddID(NULL, docPtr, (xmlChar *)id, id_attr);
00135   xmlFree(id);
00136   //Signature under wsse:Security
00137   wsse_signature_nd = ((SAMLToken*)(&wsse_signature))->node_; 
00138   if(!wsse_signature_nd) { std::cerr<<"No Signature node in wsse:Security"<<std::endl; return; }
00139 
00140   //Get the public key from the assertion, the key has been used to sign soap body msg by the attesting entity
00141   //saml1
00142   if(samlversion == SAML1) {
00143     pubkey_str = (std::string)(assertion["AttributeStatement"]["Subject"]["SubjectConfirmation"]["KeyInfo"]["KeyValue"]);
00144   }
00145   //saml2
00146   else {
00147     pubkey_str = (std::string)(assertion["Subject"]["SubjectConfirmation"]["SubjectConfirmationData"]["KeyInfo"]["KeyValue"]);
00148     if(pubkey_str.empty())
00149       x509cert_str = (std::string)(assertion["Subject"]["SubjectConfirmation"]["SubjectConfirmationData"]["KeyInfo"]["X509Data"]["X509Certificate"]);
00150   }
00151   x509data = assertion_signature["KeyInfo"]["X509Data"];
00152 } 
00153 
00154 bool SAMLToken::Authenticate(void) {
00155   //TODO: not sure this situation (no trusted certificate to verify the saml assertion) is needed
00156   return true;
00157 }
00158 
00159 bool SAMLToken::Authenticate(const std::string& cafile, const std::string& capath) {
00160   xmlSecKeysMngr* keys_manager = NULL;
00161   xmlSecDSigCtx *dsigCtx;
00162 
00163   /*****************************************/
00164   //Verify the signature under saml:assertion
00165   if((bool)x509data && (!cafile.empty() || !capath.empty())) {
00166     keys_manager = load_trusted_certs(&keys_manager, cafile.c_str(), capath.c_str());
00167     //keys_manager = load_trusted_cert_file(&keys_manager, cafile.c_str());
00168     if(keys_manager == NULL) { std::cerr<<"Can not load trusted certificates"<<std::endl; return false; } 
00169   }
00170   else if((bool)x509data)
00171     { std::cerr<<"No trusted certificates exists"<<std::endl; return false;}
00172   if(keys_manager == NULL){ std::cerr<<"No <X509Data/> exists, or no trusted certificates configured"<<std::endl; return false;}
00173 
00174   dsigCtx = xmlSecDSigCtxCreate(keys_manager);
00175   if (xmlSecDSigCtxVerify(dsigCtx, assertion_signature_nd) < 0) {
00176     xmlSecDSigCtxDestroy(dsigCtx);
00177     if (keys_manager) xmlSecKeysMngrDestroy(keys_manager);
00178     std::cerr<<"Signature verification failed for saml:assertion"<<std::endl;
00179     return false;
00180   }
00181   if(keys_manager != NULL)xmlSecKeysMngrDestroy(keys_manager);
00182   if(dsigCtx->status == xmlSecDSigStatusSucceeded) {
00183     std::cout<<"Succeed to verify the signature in saml:assertion"<<std::endl;
00184     xmlSecDSigCtxDestroy(dsigCtx);
00185   }
00186   else { std::cerr<<"Invalid signature in saml:assertion"<<std::endl; xmlSecDSigCtxDestroy(dsigCtx); return false; }
00187 
00188 
00189   /*****************************************/
00190   //Verify the signature under wsse:Security
00191   dsigCtx = xmlSecDSigCtxCreate(NULL);
00192   //Load public key from incoming soap's security token
00193   xmlSecKey* pubkey = NULL;
00194   if(!pubkey_str.empty())
00195     pubkey = get_key_from_keystr(pubkey_str);
00196   else
00197     pubkey = get_key_from_certstr(x509cert_str);
00198   if (pubkey == NULL){
00199     xmlSecDSigCtxDestroy(dsigCtx);
00200     std::cerr<<"Can not load public key"<<std::endl; return false;
00201   }
00202   dsigCtx->signKey = pubkey;
00203   if (xmlSecDSigCtxVerify(dsigCtx, wsse_signature_nd) < 0) {
00204     xmlSecDSigCtxDestroy(dsigCtx);
00205     std::cerr<<"Signature verification failed for wsse:security"<<std::endl;
00206     return false;
00207   }
00208   if(dsigCtx->status == xmlSecDSigStatusSucceeded) {
00209     std::cout<<"Succeed to verify the signature in wsse:security"<<std::endl;
00210     xmlSecDSigCtxDestroy(dsigCtx); return true;
00211   }
00212   else { std::cerr<<"Invalid signature in wsse:security"<<std::endl; xmlSecDSigCtxDestroy(dsigCtx); return false; }
00213 }
00214 
00215 SAMLToken::SAMLToken(SOAPEnvelope& soap, const std::string& certfile, const std::string& keyfile, 
00216   SAMLVersion saml_version, XMLNode saml_assertion) : SOAPEnvelope (soap), samlversion(saml_version) {
00217   //if(!init_xmlsec()) return;
00218   if(samlversion == SAML2) {
00219     // Apply predefined namespace prefix
00220     NS ns, header_ns, assertion_ns;
00221     ns = envelope.Namespaces();
00222     ns["wsu"]=WSU_NAMESPACE;
00223     envelope.Namespaces(ns);
00224     header_ns["wsse"]=WSSE_NAMESPACE;
00225     header_ns["wsse11"]=WSSE11_NAMESPACE;
00226     header_ns["ds"]=DSIG_NAMESPACE;
00227     header.Namespaces(header_ns);
00228     assertion_ns["saml2"] = SAML2_NAMESPACE;
00229     // Insert the wsse:Security element
00230     XMLNode wsse = get_node(header,"wsse:Security");
00231     XMLNode assertion;
00232 
00233     if(!saml_assertion) { //If the SAML Assertion has not been provided, the self-signed assertion
00234                           //will be generated based on the keyfile
00235       /*****************************/
00236       // Generate the saml assertion
00237       // Currently only saml2 is created
00238       assertion = get_node(wsse, "saml2:Assertion");
00239       assertion.Namespaces(assertion_ns);
00240       assertion.Name("saml2:Assertion");
00241 
00242       std::string assertion_id = UUID();
00243       assertion.NewAttribute("ID") = assertion_id;
00244 
00245       Arc::Time t;
00246       std::string current_time = t.str(Arc::UTCTime);
00247       assertion.NewAttribute("IssueInstant") = current_time;
00248 
00249       Arc::Credential cred(certfile, keyfile, "", "");
00250       std::string dn = cred.GetDN();
00251       std::string rdn = Arc::convert_to_rdn(dn);
00252       assertion.NewAttribute("Issuer") = rdn;
00253 
00254       assertion.NewAttribute("Version") = std::string("2.0");
00255     
00256       XMLNode condition = get_node(assertion, "saml2:Conditions");
00257       Arc::Time t_start;
00258       std::string time_start = t_start.str(Arc::UTCTime);
00259       Arc::Time t_end = t_start + Arc::Period(43200);
00260       std::string time_end = t_end.str(Arc::UTCTime);
00261       condition.NewAttribute("NotBefore") = time_start;
00262       condition.NewAttribute("NotOnOrAfter") = time_end;
00263     
00264       XMLNode subject = get_node(assertion, "saml2:Subject");
00265       XMLNode nameid = get_node(subject, "saml2:NameID");
00266       nameid.NewAttribute("NameQualifier") = "knowarc.eu"; //
00267       nameid.NewAttribute("Format") = "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName";
00268       nameid = rdn;
00269   
00270       XMLNode subjectconfirmation = get_node(subject, "saml2:SubjectConfirmation");
00271       subjectconfirmation.NewAttribute("Method") = "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key";
00272       XMLNode subjectconfirmationdata = get_node(subjectconfirmation, "saml2:SubjectConfirmationData");
00273       XMLNode keyinfo = get_node(subjectconfirmationdata, "ds:KeyInfo");
00274       XMLNode keyvalue = get_node(keyinfo, "ds:KeyValue");
00275       //Put the pubkey as the keyvalue
00276       keyvalue = get_key_from_certfile(certfile.c_str()); 
00277   
00278       //Add some attribute here
00279       XMLNode statement = get_node(assertion, "saml2:AttributeStatement");
00280       XMLNode attribute = get_node(statement, "saml2:Attribute");
00281       attribute.NewAttribute("Name") = "email";
00282       attribute.NewAttribute("NameFormat") = "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified";
00283       
00284       //Generate the signature to the assertion, it should be the attribute authority to sign the assertion
00285       //Add signature template 
00286       xmlNodePtr assertion_signature = NULL;
00287       xmlNodePtr assertion_reference = NULL;
00288       assertion_signature = xmlSecTmplSignatureCreate(NULL,
00289                             xmlSecTransformExclC14NId,
00290                             xmlSecTransformRsaSha1Id, NULL);
00291       //Add signature into assertion
00292       xmlNodePtr assertion_nd = ((SAMLToken*)(&assertion))->node_;
00293       xmlAddChild(assertion_nd, assertion_signature);
00294   
00295       //Add reference for signature
00296       xmlDocPtr docPtr = assertion_nd->doc;
00297       xmlChar* id = NULL;
00298       id =  xmlGetProp(assertion_nd, (xmlChar *)"ID");
00299       if(!id) { std::cerr<<"There is not Assertion ID attribute in assertion"<<std::endl; return; }
00300 
00301       std::string assertion_uri; assertion_uri.append("#"); assertion_uri.append((char*)id);
00302 
00303       assertion_reference = xmlSecTmplSignatureAddReference(assertion_signature, xmlSecTransformSha1Id,
00304                                               NULL, (xmlChar *)(assertion_uri.c_str()), NULL);
00305       xmlSecTmplReferenceAddTransform(assertion_reference, xmlSecTransformEnvelopedId);
00306       xmlSecTmplReferenceAddTransform(assertion_reference, xmlSecTransformExclC14NId);
00307   
00308       xmlAttrPtr id_attr = xmlHasProp(assertion_nd, (xmlChar *)"ID");
00309       xmlAddID(NULL, docPtr, (xmlChar *)id, id_attr);
00310       xmlFree(id);
00311 
00312       xmlNodePtr key_info = xmlSecTmplSignatureEnsureKeyInfo(assertion_signature, NULL);
00313       xmlSecTmplKeyInfoAddX509Data(key_info);
00314 
00315       //Sign the assertion
00316       xmlSecDSigCtx *dsigCtx = xmlSecDSigCtxCreate(NULL);
00317       //load private key, assuming there is no need for passphrase
00318       dsigCtx->signKey = xmlSecCryptoAppKeyLoad(keyfile.c_str(), xmlSecKeyDataFormatPem, NULL, NULL, NULL);
00319       if(dsigCtx->signKey == NULL) {
00320         xmlSecDSigCtxDestroy(dsigCtx);
00321         std::cerr<<"Can not load key"<<std::endl; return;
00322       }
00323       if(xmlSecCryptoAppKeyCertLoad(dsigCtx->signKey, certfile.c_str(), xmlSecKeyDataFormatPem) < 0) {
00324         xmlSecDSigCtxDestroy(dsigCtx);
00325         std::cerr<<"Can not load certificate"<<std::endl; return;     
00326       }
00327       if (xmlSecDSigCtxSign(dsigCtx, assertion_signature) < 0) {
00328         xmlSecDSigCtxDestroy(dsigCtx);
00329         std::cerr<<"Can not sign assertion"<<std::endl; return;
00330       }
00331       if(dsigCtx != NULL)xmlSecDSigCtxDestroy(dsigCtx);
00332 
00333       std::string str;
00334       assertion.GetXML(str);
00335       std::cout<<"Assertion: "<<str<<std::endl;
00336     }
00337     else {
00338       assertion = wsse.NewChild(saml_assertion);
00339     }
00340 
00341 
00342     /*****************************/
00343     //Generate the signature of message body based on the KeyInfo inside saml assertion
00344     xmlNodePtr wsse_signature = NULL;
00345     xmlNodePtr wsse_reference = NULL;
00346     wsse_signature = xmlSecTmplSignatureCreate(NULL,
00347                                 xmlSecTransformExclC14NId,
00348                                 xmlSecTransformRsaSha1Id, NULL);
00349     //Add signature into wsse
00350     xmlNodePtr wsse_nd = ((SAMLToken*)(&wsse))->node_;
00351     xmlAddChild(wsse_nd, wsse_signature);
00352 
00353     //Add reference for signature
00354     xmlNodePtr bodyPtr = ((SAMLToken*)(&body))->node_;
00355     //docPtr = wsse_nd->doc;
00356     xmlChar* id = NULL;
00357     id =  xmlGetProp(bodyPtr, (xmlChar *)"Id");
00358     if(!id) {
00359       std::cout<<"There is not wsu:Id attribute in soap body, add a new one"<<std::endl;
00360       body.NewAttribute("wsu:Id") = "MsgBody";
00361     }
00362     id =  xmlGetProp(bodyPtr, (xmlChar *)"Id");
00363     std::string body_uri; body_uri.append("#"); body_uri.append((char*)id);
00364 
00365     wsse_reference = xmlSecTmplSignatureAddReference(wsse_signature, xmlSecTransformSha1Id,
00366                                                     NULL, (xmlChar *)(body_uri.c_str()), NULL);
00367     xmlSecTmplReferenceAddTransform(wsse_reference, xmlSecTransformEnvelopedId);
00368     xmlSecTmplReferenceAddTransform(wsse_reference, xmlSecTransformExclC14NId);
00369 
00370     xmlAttrPtr id_attr = xmlHasProp(bodyPtr, (xmlChar *)"Id");
00371     xmlDocPtr docPtr = bodyPtr->doc;
00372     xmlAddID(NULL, docPtr, (xmlChar *)id, id_attr);
00373     xmlFree(id);
00374 
00375     xmlNodePtr key_info = xmlSecTmplSignatureEnsureKeyInfo(wsse_signature, NULL);
00376     XMLNode keyinfo_nd = wsse["Signature"]["KeyInfo"];
00377     XMLNode st_ref_nd = keyinfo_nd.NewChild("wsse:SecurityTokenReference");
00378     st_ref_nd.NewAttribute("wsu:Id") = "STR1";
00379     st_ref_nd.NewAttribute("wsse11:TokenType")="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0";
00380     XMLNode keyid_nd = st_ref_nd.NewChild("wsse:KeyIdentifier");
00381     keyid_nd.NewAttribute("wsu:Id") = "abcde"; //not specified in the specification
00382     keyid_nd.NewAttribute("ValueType")="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLID"; 
00383     keyid_nd = (std::string)(assertion.Attribute("ID"));
00384 
00385     xmlSecDSigCtx *dsigCtx = xmlSecDSigCtxCreate(NULL);
00386     //Sign the assertion
00387     dsigCtx = xmlSecDSigCtxCreate(NULL);
00388     //load private key, assuming there is no need for passphrase
00389     dsigCtx->signKey = xmlSecCryptoAppKeyLoad(keyfile.c_str(), xmlSecKeyDataFormatPem, NULL, NULL, NULL);
00390     if(dsigCtx->signKey == NULL) {
00391       xmlSecDSigCtxDestroy(dsigCtx);
00392       std::cerr<<"Can not load key"<<std::endl; return;
00393     }
00394     //if(xmlSecCryptoAppKeyCertLoad(dsigCtx->signKey, certfile.c_str(), xmlSecKeyDataFormatPem) < 0) {
00395     //  xmlSecDSigCtxDestroy(dsigCtx);
00396     //  std::cerr<<"Can not load certificate"<<std::endl; return;
00397     //}
00398     if (xmlSecDSigCtxSign(dsigCtx, wsse_signature) < 0) {
00399       xmlSecDSigCtxDestroy(dsigCtx);
00400       std::cerr<<"Can not sign wsse"<<std::endl; return;
00401     }
00402     if(dsigCtx != NULL)xmlSecDSigCtxDestroy(dsigCtx);
00403 
00404     //fix namespaces
00405     //NS wsse_ns;
00406     //wsse_ns = wsse.Namespaces();
00407     //wsse.Namespaces(wsse_ns);
00408 
00409     std::string str;
00410     wsse.GetXML(str);
00411     std::cout<<"WSSE: "<<str<<std::endl;
00412   }
00413 }
00414 
00415 SAMLToken::~SAMLToken(void) {
00416   //final_xmlsec();
00417 }
00418 
00419 } // namespace Arc
00420