// ---------------------------------------------------------------------- // File: XrdMqMessage.cc // Author: Andreas-Joachim Peters - CERN // ---------------------------------------------------------------------- /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2011 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #include "mq/XrdMqMessage.hh" #include "common/SymKeys.hh" #include "XrdOuc/XrdOucEnv.hh" #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Add compatibility methods present in OpenSSL >= 1.1.0 if we use an older // version of OpenSSL #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined (LIBRESSL_VERSION_NUMBER) void* EVP_MD_CTX_md_data(const EVP_MD_CTX* ctx) { return ctx->md_data; } #endif EVP_PKEY* XrdMqMessage::PrivateKey = 0; XrdOucString XrdMqMessage::PublicKeyDirectory = ""; XrdOucString XrdMqMessage::PrivateKeyFile = ""; XrdOucString XrdMqMessage::PublicKeyFileHash = ""; XrdOucHash XrdMqMessage::PublicKeyHash; bool XrdMqMessage::kCanSign = false; bool XrdMqMessage::kCanVerify = false; XrdSysLogger* XrdMqMessage::Logger = 0; XrdSysError XrdMqMessage::Eroute(0); /******************************************************************************/ /* X r d M q M e s s a g e H e a d e r */ /******************************************************************************/ //------------------------------------------------------------------------------ // Constructor //------------------------------------------------------------------------------ XrdMqMessageHeader::XrdMqMessageHeader(): kMessageId(""), kReplyId(""), kSenderId(""), kBrokerId(""), kReceiverId(""), kSenderTime_sec(0), kSenderTime_nsec(0), kBrokerTime_sec(0), kBrokerTime_nsec(0), kReceiverTime_sec(0), kReceiverTime_nsec(0), kMessageSignature(""), kMessageDigest(""), kEncrypted(false), kType(0), mMsgHdrBuffer(""), kCertificateHash("") { } //------------------------------------------------------------------------------ // Get header buffer //------------------------------------------------------------------------------ const char* XrdMqMessageHeader::GetHeaderBuffer() const { return mMsgHdrBuffer.c_str(); } //------------------------------------------------------------------------------ // GetTime //------------------------------------------------------------------------------ void XrdMqMessageHeader::GetTime(time_t& sec, long& nsec) { struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); sec = tv.tv_sec; nsec = tv.tv_usec * 1000; } //------------------------------------------------------------------------------ // Encode //------------------------------------------------------------------------------ void XrdMqMessageHeader::Encode() { char buff[1024]; char sep = '^'; std::ostringstream oss; // TODO: check that none of this strings contains a : ! oss << XMQHEADER << "=" << kMessageId << sep << kReplyId << sep << kSenderId << sep << kBrokerId << sep << kReceiverId << sep << kReceiverQueue << sep << kDescription << sep; sprintf(buff, "%ld", kSenderTime_sec); oss << buff << sep; sprintf(buff, "%ld", kSenderTime_nsec); oss << buff << sep; sprintf(buff, "%ld", kBrokerTime_sec); oss << buff << sep; sprintf(buff, "%ld", kBrokerTime_nsec); oss << buff << sep; sprintf(buff, "%ld", kReceiverTime_sec); oss << buff << sep; sprintf(buff, "%ld", kReceiverTime_nsec); oss << buff << sep; oss << kCertificateHash << sep << kMessageSignature << sep << kMessageDigest << sep << kEncrypted << sep << kType << sep; mMsgHdrBuffer = oss.str().c_str(); } //------------------------------------------------------------------------------ // Decode //------------------------------------------------------------------------------ bool XrdMqMessageHeader::Decode(const char* str_header) { XrdOucEnv decenv(str_header); const char* hp = 0; hp = decenv.Get(XMQHEADER); mMsgHdrBuffer = XMQHEADER; mMsgHdrBuffer += "="; if (hp) { mMsgHdrBuffer += hp; } else { mMsgHdrBuffer += str_header; } if (!mMsgHdrBuffer.length()) { return false; } char sep = '^'; int pos = strlen(XMQHEADER) + 1; int ppos = STR_NPOS; if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { kMessageId.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { kReplyId.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { kSenderId.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { kBrokerId.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { kReceiverId.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { kReceiverQueue.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { kDescription.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; XrdOucString tmpstring; if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { tmpstring.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; kSenderTime_sec = (time_t)strtol(tmpstring.c_str(), 0, 10); if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { tmpstring.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; kSenderTime_nsec = (long)strtol(tmpstring.c_str(), 0, 10); if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { tmpstring.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; kBrokerTime_sec = (time_t)strtol(tmpstring.c_str(), 0, 10); if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { tmpstring.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; kBrokerTime_nsec = (long)strtol(tmpstring.c_str(), 0, 10); if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { tmpstring.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; kReceiverTime_sec = (time_t)strtol(tmpstring.c_str(), 0, 10); if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { tmpstring.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; kReceiverTime_nsec = (long)strtol(tmpstring.c_str(), 0, 10); if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { kCertificateHash.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { kMessageSignature.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { kMessageDigest.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { tmpstring.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; kEncrypted = atoi(tmpstring.c_str()); if ((ppos = mMsgHdrBuffer.find(sep, pos)) != STR_NPOS) { tmpstring.assign(mMsgHdrBuffer, pos, ppos - 1); pos = ppos + 1; kType = atoi(tmpstring.c_str()); return true; } } } } } } } } } } } } } } } } } } return false; } //------------------------------------------------------------------------------ // Print //------------------------------------------------------------------------------ void XrdMqMessageHeader::Print() { std::cerr << "-------------------------------------------------------------"; std::cerr << std::endl; std::cerr << "kMessageId : " << kMessageId << std::endl; std::cerr << "kReplyId : " << kReplyId << std::endl; std::cerr << "kSenderId : " << kSenderId << std::endl; std::cerr << "kBrokerId : " << kBrokerId << std::endl; std::cerr << "kReceiverId : " << kReceiverId << std::endl; std::cerr << "kReceiverQueue : " << kReceiverQueue << std::endl; std::cerr << "kDescription : " << kDescription << std::endl; std::cerr << "kSenderTime_sec : " << kSenderTime_sec << std::endl; std::cerr << "kSenderTime_nsec : " << kSenderTime_nsec << std::endl; std::cerr << "kBrokerTime_sec : " << kBrokerTime_sec << std::endl; std::cerr << "kBrokerTime_nsec : " << kBrokerTime_nsec << std::endl; std::cerr << "kReceiverTime_sec : " << kReceiverTime_sec << std::endl; std::cerr << "kReceiverTime_nsec : " << kReceiverTime_nsec << std::endl; std::cerr << "kCertificateHash : " << kCertificateHash << std::endl; std::cerr << "kMessageSignature : " << kMessageSignature << std::endl; std::cerr << "kMessageDigest : " << kMessageDigest << std::endl; std::cerr << "kEncrypted : " << kEncrypted << std::endl; std::cerr << "kType : " << kType << std::endl; std::cerr << "mMsgHdrBuffer : " << mMsgHdrBuffer << std::endl; std::cerr << "---------------------------------------------------------------"; std::cerr << std::endl; } /******************************************************************************/ /* X r d M q M e s s a g e */ /******************************************************************************/ //------------------------------------------------------------------------------ // Constructor //------------------------------------------------------------------------------ XrdMqMessage::XrdMqMessage(const char* description, int type): kMonitor(false), errc(0) { kMessageHeader.kDescription = description; NewId(); kMessageHeader.kType = type; } //------------------------------------------------------------------------------ // Constructor - just fills the raw buffer, the Decode method has to be called // to unpack it. //------------------------------------------------------------------------------ XrdMqMessage::XrdMqMessage(XrdOucString& rawmessage): kMonitor(false), errc(0) { kMessageBuffer = rawmessage; } //------------------------------------------------------------------------------ // Factory method //------------------------------------------------------------------------------ XrdMqMessage* XrdMqMessage::Create(const char* messagebuffer) { XrdOucString mbuf = messagebuffer; XrdMqMessage* msg = new XrdMqMessage(mbuf); if (!msg->Decode()) { delete msg; return 0; } else { return msg; } } //------------------------------------------------------------------------------ // Generate new id //------------------------------------------------------------------------------ void XrdMqMessage::NewId() { char uuidstring[40]; uuid_t uuid; uuid_generate_time(uuid); uuid_unparse(uuid, uuidstring); kMessageHeader.kMessageId = uuidstring; } //------------------------------------------------------------------------------ // Configure //------------------------------------------------------------------------------ bool XrdMqMessage::Configure(const char* ConfigFN) { char* var; const char* val; int cfgFD; ERR_load_crypto_strings(); if (!Logger) { // Create a logger, if there was none set before Logger = new XrdSysLogger(); } Eroute.logger(Logger); XrdOucStream Config(&Eroute, "xmessage"); if ((! ConfigFN) || (!strlen(ConfigFN))) { return false; } if ((cfgFD = open(ConfigFN, O_RDONLY, 0)) < 0) { return Eroute.Emsg("Config", errno, "open config file fn=", ConfigFN); } Config.Attach(cfgFD); while ((var = Config.GetMyFirstWord())) { if (!strncmp(var, "mq.", 3)) { var += 3; if (!strcmp("privatekeyfile", var)) { if ((val = Config.GetWord())) { PrivateKeyFile = val; } } if (!strcmp("publickeydirectory", var)) { if ((val = Config.GetWord())) { PublicKeyDirectory = val; } } if (!strcmp("publickeyfilehash", var)) { if ((val = Config.GetWord())) { PublicKeyFileHash = val; } } } } Config.Close(); close(cfgFD); if (PrivateKeyFile.length()) { // Load the private key FILE* fp = fopen(PrivateKeyFile.c_str(), "r"); if (fp == 0) { return Eroute.Emsg("Config", errno, "open private key file fn=", PrivateKeyFile.c_str()); } PrivateKey = PEM_read_PrivateKey(fp, 0, 0, 0); fclose(fp); if (!PrivateKey) { return Eroute.Emsg("Config", EINVAL, "load private key from file fn=", PrivateKeyFile.c_str()); } if (!PublicKeyFileHash.length()) { return Eroute.Emsg("Config", EINVAL, "continue - you have to provide the " "hash value of the corresponding public key for your " "private key [ use: openssl x509 -in -hash ]"); } kCanSign = true; } if (PublicKeyDirectory.length()) { // Read all public keys into the public key hash DIR* dp; struct dirent* ep; dp = opendir(PublicKeyDirectory.c_str()); if (dp != 0) { while ((ep = readdir(dp))) { if (!strncmp(ep->d_name, ".", 1)) { continue; } XrdOucString fullcertpath = PublicKeyDirectory; fullcertpath += "/"; fullcertpath += (char*)ep->d_name; FILE* fp = fopen(fullcertpath.c_str(), "r"); if (!fp) { closedir(dp); return Eroute.Emsg("Config", errno, "open public key file fn=", fullcertpath.c_str()); } X509* x509 = PEM_read_X509(fp, 0, 0, 0); fclose(fp); if (x509 == 0) { ERR_print_errors_fp(stderr); if (dp) { closedir(dp); } return Eroute.Emsg("Config", EINVAL, "load public key file fn=", fullcertpath.c_str()); } EVP_PKEY* pkey = X509_extract_key(x509); if (pkey == 0) { ERR_print_errors_fp(stderr); if (dp) { closedir(dp); } return Eroute.Emsg("Config", EINVAL, "extract public key from file fn=", fullcertpath.c_str()); } // add to the public key hash try { PublicKeyHash.Add(ep->d_name, new KeyWrapper(pkey)); } catch (int& excp) { return Eroute.Emsg("Config", EINVAL, "insert public key in map"); } X509_free(x509); x509 = 0; } (void) closedir(dp); } else { return Eroute.Emsg("Config", errno, "open public key directory dn=", PublicKeyDirectory.c_str()); } kCanVerify = true; } if (kCanSign) { Eroute.Say("*****> mq-client can sign messages"); Eroute.Say("=====> mq.privatekeyfile : ", PrivateKeyFile.c_str(), ""); Eroute.Say("=====> mq.publickeyhash : ", PublicKeyFileHash.c_str(), ""); } if (kCanVerify) { Eroute.Say("*****> mq-client can verify messages"); Eroute.Say("=====> mq.publickeydirectory : ", PublicKeyDirectory.c_str(), ""); XrdOucString nh = ""; nh += PublicKeyHash.Num(); Eroute.Say("=====> public keys <#> : : ", nh.c_str(), ""); } return 0; } //------------------------------------------------------------------------------ // Encode the header and the body //------------------------------------------------------------------------------ void XrdMqMessage::Encode() { kMessageHeader.Encode(); kMessageBuffer = kMessageHeader.GetHeaderBuffer(); kMessageBuffer += "&"; kMessageBuffer += XMQBODY; kMessageBuffer += "="; kMessageBuffer += kMessageBody; if (kMonitor) { kMessageBuffer += "&"; kMessageBuffer += XMQMONITOR; kMessageBuffer += "=1"; } } //------------------------------------------------------------------------------ // Decode //------------------------------------------------------------------------------ bool XrdMqMessage::Decode() { bool decode_hdr = kMessageHeader.Decode(kMessageBuffer.c_str()); XrdOucEnv decenv(kMessageBuffer.c_str()); const char* hp = decenv.Get(XMQBODY); kMessageBody = (hp ? hp : ""); kMonitor = (decenv.Get(XMQMONITOR) ? true : false); return decode_hdr; } //------------------------------------------------------------------------------ // RSA encrypt //------------------------------------------------------------------------------ bool XrdMqMessage::RSAEncrypt(char* data, ssize_t data_length, char*& encrypted_data, ssize_t& encrypted_length) { encrypted_data = (char*)malloc(RSA_size(EVP_PKEY_get1_RSA(PrivateKey))); if (!encrypted_data) { return false; } encrypted_length = RSA_private_encrypt(data_length, (uint_fast8_t*)data, (uint_fast8_t*)encrypted_data, EVP_PKEY_get1_RSA(PrivateKey), RSA_PKCS1_PADDING); if (encrypted_length < 0) { free(encrypted_data); encrypted_data = 0; Eroute.Emsg(__FUNCTION__, EINVAL, "encrypt with private key", ERR_error_string(ERR_get_error(), 0)); return false; } return true; } //------------------------------------------------------------------------------ // RSA key wrapper //------------------------------------------------------------------------------ class RSAWrapper { public: RSAWrapper(RSA* r) : rsa(r) {} ~RSAWrapper() { RSA_free(rsa); } RSA* get() { return rsa; } private: RSA* rsa = nullptr; }; //------------------------------------------------------------------------------ // RSA Decrypt //------------------------------------------------------------------------------ bool XrdMqMessage::RSADecrypt(char* encrypted_data, ssize_t encrypted_length, char*& data, ssize_t& data_length, XrdOucString& key_hash) { KeyWrapper* wrapper = PublicKeyHash.Find(key_hash.c_str()); EVP_PKEY* pkey = nullptr; if (wrapper) { pkey = wrapper->get(); } if (!pkey) { Eroute.Emsg(__FUNCTION__, EINVAL, "load requested public key:", key_hash.c_str()); return false; } RSAWrapper rsaKey(EVP_PKEY_get1_RSA(pkey)); if ((encrypted_length != (unsigned int)RSA_size(rsaKey.get()))) { Eroute.Emsg(__FUNCTION__, EINVAL, "decrypt - keylength/encryption buffer" " mismatch"); return false; } data = (char*)malloc(RSA_size(rsaKey.get())); if (!data) { return false; } data_length = RSA_public_decrypt(encrypted_length, (uint_fast8_t*)encrypted_data, (uint_fast8_t*)data, rsaKey.get(), RSA_PKCS1_PADDING); if (data_length < 0) { free(data); data = 0; Eroute.Emsg(__FUNCTION__, EINVAL, "decrypt with public key", ERR_error_string(ERR_get_error(), 0)); return false; } return true; } //------------------------------------------------------------------------------ // Sign //------------------------------------------------------------------------------ bool XrdMqMessage::Sign(bool encrypt) { ssize_t sig_len; unsigned char sig_buf[16384]; EVP_MD_CTX* md_ctx = EVP_MD_CTX_create(); std::string b64out; EVP_MD_CTX_init(md_ctx); EVP_SignInit(md_ctx, EVP_sha1()); EVP_SignUpdate(md_ctx, kMessageBody.c_str(), kMessageBody.length()); sig_len = sizeof(sig_buf); if (!EVP_SignFinal(md_ctx, sig_buf, (unsigned int*)&sig_len, PrivateKey)) { EVP_MD_CTX_destroy(md_ctx); return false; } std::string signature; if (!eos::common::SymKey::Base64Encode((char*)sig_buf, sig_len, signature)) { EVP_MD_CTX_destroy(md_ctx); return false; } kMessageHeader.kMessageSignature = "rsa:"; kMessageHeader.kMessageSignature += PublicKeyFileHash; kMessageHeader.kMessageSignature += ":"; kMessageHeader.kMessageSignature += signature.c_str(); if (!encrypt) { // Base64 encode the message digest if (!eos::common::SymKey::Base64Encode((char*)EVP_MD_CTX_md_data(md_ctx), (ssize_t)SHA_DIGEST_LENGTH, b64out)) { EVP_MD_CTX_destroy(md_ctx); return false; } kMessageHeader.kMessageDigest = b64out.c_str(); EVP_MD_CTX_destroy(md_ctx); Encode(); return true; } // RSA encode the message digest char* rsadigest = 0; ssize_t rsalen; if (!RSAEncrypt((char*)EVP_MD_CTX_md_data(md_ctx), SHA_DIGEST_LENGTH, rsadigest, rsalen)) { EVP_MD_CTX_destroy(md_ctx); free(rsadigest); return false; } // Base64 encode the rsa encoded digest if (!eos::common::SymKey::Base64Encode(rsadigest, rsalen, b64out)) { EVP_MD_CTX_destroy(md_ctx); free(rsadigest); return false; } kMessageHeader.kMessageDigest = b64out.c_str(); free(rsadigest); // Add a prefix with the public key rsa::digest XrdOucString sdigest = "rsa:"; sdigest += PublicKeyFileHash; sdigest += ":"; sdigest += kMessageHeader.kMessageDigest; kMessageHeader.kMessageDigest = sdigest; // Encrypt the message with the plain digest char* encryptptr = 0; ssize_t encryptlen = 0; if ((!eos::common::SymKey::CipherEncrypt(kMessageBody.c_str(), kMessageBody.length(), encryptptr, encryptlen, (char*)EVP_MD_CTX_md_data(md_ctx)))) { Eroute.Emsg(__FUNCTION__, EINVAL, "encrypt message"); EVP_MD_CTX_destroy(md_ctx); return false; } if ((!eos::common::SymKey::Base64Encode(encryptptr, encryptlen, b64out))) { Eroute.Emsg(__FUNCTION__, EINVAL, "base64 encode message"); EVP_MD_CTX_destroy(md_ctx); free(encryptptr); return false; } kMessageBody = b64out.c_str(); kMessageHeader.kEncrypted = true; free(encryptptr); EVP_MD_CTX_destroy(md_ctx); Encode(); return true; } //------------------------------------------------------------------------------ // Verify //------------------------------------------------------------------------------ bool XrdMqMessage::Verify() { using eos::common::SymKey; if (!Decode()) { Eroute.Emsg(__FUNCTION__, EINVAL, "decode message"); return false; } if (kMessageHeader.kEncrypted) { // Decode the digest if (!kMessageHeader.kMessageDigest.beginswith("rsa:")) { Eroute.Emsg(__FUNCTION__, EINVAL, "decode message digest - is not rsa encrypted"); return false; } // Get public key XrdOucString PublicKeyName; int dpos = kMessageHeader.kMessageDigest.find(":", 4); if (dpos != STR_NPOS) { PublicKeyName.assign(kMessageHeader.kMessageDigest, 4, dpos - 1); } else { Eroute.Emsg(__FUNCTION__, EINVAL, "find public key reference in message digest"); return false; } // Truncate the key rsa: from the digest string kMessageHeader.kMessageDigest.erase(0, dpos + 1); // Base64 decode the digest string char* encrypteddigest = 0; ssize_t encrypteddigestlen = 0; char* decrypteddigest = 0; ssize_t decrypteddigestlen = 0; if (!eos::common::SymKey::Base64Decode((char*) kMessageHeader.kMessageDigest.c_str(), encrypteddigest, encrypteddigestlen)) { Eroute.Emsg(__FUNCTION__, EINVAL, "base64 decode encrypted message digest"); free(encrypteddigest); return false; } if (!RSADecrypt(encrypteddigest, (unsigned int) encrypteddigestlen, decrypteddigest, decrypteddigestlen, PublicKeyName)) { Eroute.Emsg(__FUNCTION__, EINVAL, "RSA decrypt encrypted message digest"); free(encrypteddigest); free(decrypteddigest); return false; } if (decrypteddigestlen != SHA_DIGEST_LENGTH) { Eroute.Emsg(__FUNCTION__, EINVAL, "RSA decrypted message digest has illegal " "length"); free(encrypteddigest); free(decrypteddigest); return false; } // Base64 decode message body char* encryptedbody = 0; ssize_t encryptedbodylen = 0; if (!eos::common::SymKey::Base64Decode((char*)kMessageBody.c_str(), encryptedbody, encryptedbodylen)) { Eroute.Emsg(__FUNCTION__, EINVAL, "base64 decode encrypted message body"); free(encryptedbody); free(encrypteddigest); free(decrypteddigest); return false; } // CIPHER decrypt message body char* data; ssize_t data_len; if (!eos::common::SymKey::CipherDecrypt(encryptedbody, encryptedbodylen, data, data_len, decrypteddigest)) { Eroute.Emsg(__FUNCTION__, EINVAL, "base64 decode encrypted message body"); free(encryptedbody); free(encrypteddigest); free(decrypteddigest); return false; } kMessageBody = data; kMessageHeader.kEncrypted = false; free(encryptedbody); free(encrypteddigest); free(decrypteddigest); } // Decompose the signature if (!kMessageHeader.kMessageSignature.beginswith("rsa:")) { Eroute.Emsg(__FUNCTION__, EINVAL, "decode message signature - misses rsa: tag"); return false; } // Get public key XrdOucString PublicKeyName = ""; int dpos = kMessageHeader.kMessageSignature.find(":", 4); if (dpos != STR_NPOS) { PublicKeyName.assign(kMessageHeader.kMessageSignature, 4, dpos - 1); } else { Eroute.Emsg(__FUNCTION__, EINVAL, "find public key reference in signature"); return false; } // Truncate the key rsa: from the digest string kMessageHeader.kMessageSignature.erase(0, dpos + 1); // Base64 decode signature char* sig = 0; ssize_t siglen = 0; if (!eos::common::SymKey::Base64Decode((char*) kMessageHeader.kMessageSignature.c_str(), sig, siglen)) { Eroute.Emsg(__FUNCTION__, EINVAL, "base64 decode message signature"); free(sig); return false; } KeyWrapper* wrapper = PublicKeyHash.Find(PublicKeyName.c_str()); EVP_PKEY* PublicKey = nullptr; if (wrapper) { PublicKey = wrapper->get(); } if (!PublicKey) { Eroute.Emsg(__FUNCTION__, EINVAL, "load requested public key:", PublicKeyName.c_str()); free(sig); return false; } // Verify the signature of the body EVP_MD_CTX* md_ctx = EVP_MD_CTX_create(); EVP_VerifyInit(md_ctx, EVP_sha1()); EVP_VerifyUpdate(md_ctx, kMessageBody.c_str(), kMessageBody.length()); int retc = EVP_VerifyFinal(md_ctx, (unsigned char*) sig, siglen, PublicKey); EVP_MD_CTX_destroy(md_ctx); if (!retc) { Eroute.Emsg(__FUNCTION__, EPERM, "verify signature of message body", ERR_error_string(ERR_get_error(), 0)); free(sig); return false; } free(sig); kMessageBuffer = ""; kMessageHeader.kMessageSignature = ""; kMessageHeader.kMessageDigest = ""; kMessageHeader.kEncrypted = false; kMessageHeader.Encode(); return true; } //------------------------------------------------------------------------------ // SetReply //------------------------------------------------------------------------------ void XrdMqMessage::SetReply(XrdMqMessage& message) { kMessageHeader.kReplyId = message.kMessageHeader.kMessageId; } //------------------------------------------------------------------------------ // Print //------------------------------------------------------------------------------ void XrdMqMessage::Print() { kMessageHeader.Print(); if (kMessageBody.length() > 256) { std::cerr << "kMessageBody : (...) too long" << std::endl; } else { std::cerr << "kMessageBody : " << kMessageBody << std::endl; } std::cerr << "--------------------------------------------------" << std::endl; if (kMessageBuffer.length() > 256) { std::cerr << "kMessageBuffer : (...) too long" << std::endl; std::cerr << "Length : " << kMessageBuffer.length() << std::endl; } else { std::cerr << "kMessageBuffer : " << kMessageBuffer << std::endl; } std::cerr << "--------------------------------------------------" << std::endl; } /******************************************************************************/ /* X r d A d v i s o r y M q M e s s a g e */ /******************************************************************************/ //------------------------------------------------------------------------------ // Encode method //------------------------------------------------------------------------------ void XrdAdvisoryMqMessage::Encode() { kMessageHeader.Encode(); std::ostringstream oss; oss << kMessageHeader.GetHeaderBuffer() << "&" << XMQADVISORYHOST << "=" << kQueue << "&" << XMQADVISORYSTATE << "=" << kOnline; kMessageBuffer = oss.str().c_str(); } //------------------------------------------------------------------------------ // Decode method //------------------------------------------------------------------------------ bool XrdAdvisoryMqMessage::Decode() { if (!kMessageHeader.Decode(kMessageBuffer.c_str())) { fprintf(stderr, "Failed to decode message header\n"); return false; } XrdOucEnv mq(kMessageBuffer.c_str()); const char* q = mq.Get(XMQADVISORYHOST); const char* p = mq.Get(XMQADVISORYSTATE); if ((!q) || (!p)) { return false; } // Extract the queue which changed nad the online state kQueue = q; kOnline = atoi(p); return true; } //------------------------------------------------------------------------------ // Print //------------------------------------------------------------------------------ void XrdAdvisoryMqMessage::Print() { XrdMqMessage::Print(); std::cerr << "--------------------------------------------------" << std::endl; std::cerr << "kQueue : " << kQueue << std::endl; std::cerr << "kOnline : " << kOnline << std::endl; } //---------------------------------------------------------------------------- // Construction factory //---------------------------------------------------------------------------- XrdAdvisoryMqMessage* XrdAdvisoryMqMessage::Create(const char* messagebuffer) { XrdAdvisoryMqMessage* msg = new XrdAdvisoryMqMessage(); msg->kMessageBuffer = messagebuffer; if (!msg->Decode()) { delete msg; return 0; } else { return msg; } }