/* * libretroshare/src/pqi: authssl.cc * * 3P/PQI network interface for RetroShare. * * Copyright 2004-2008 by Robert Fernie. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License Version 2 as published by the Free Software Foundation. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. * * Please report all bugs and problems to "retroshare@lunamutt.com". * * * This class is designed to provide authentication using ssl certificates * only. It is intended to be wrapped by an gpgauthmgr to provide * pgp + ssl web-of-trust authentication. * */ #include "authssl.h" #include "cleanupxpgp.h" #include "pqinetwork.h" #include "authgpg.h" #include "pqi/p3connmgr.h" /******************** notify of new Cert **************************/ #include "pqinotify.h" #include #include #include #include #include #include /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ // initialisation du pointeur de singleton à zéro AuthSSL *AuthSSL::instance_ssl = new AuthSSL(); // initialisation du pointeur de ex data du ssl context int AuthSSL::ex_data_ctx_index = 0; sslcert::sslcert(X509 *x509, std::string pid) { certificate = x509; id = pid; name = getX509CNString(x509->cert_info->subject); org = getX509OrgString(x509->cert_info->subject); location = getX509LocString(x509->cert_info->subject); email = ""; issuer = getX509CNString(x509->cert_info->issuer); authed = false; } sslcert::sslcert() { email = ""; authed = false; } X509_REQ *GenerateX509Req( std::string pkey_file, std::string passwd, std::string name, std::string email, std::string org, std::string loc, std::string state, std::string country, int nbits_in, std::string &errString) { /* generate request */ X509_REQ *req=X509_REQ_new(); // setup output. BIO *bio_out = NULL; bio_out = BIO_new(BIO_s_file()); BIO_set_fp(bio_out,stdout,BIO_NOCLOSE); EVP_PKEY *pkey = NULL; // first generate a key.... if ((pkey=EVP_PKEY_new()) == NULL) { fprintf(stderr,"GenerateX509Req: Couldn't Create Key\n"); return 0; } int nbits = 2048; unsigned long e = 0x10001; if ((nbits_in >= 512) && (nbits_in <= 4096)) { nbits = nbits_in; } else { fprintf(stderr,"GenerateX509Req: strange num of nbits: %d\n", nbits_in); fprintf(stderr,"GenerateX509Req: reverting to %d\n", nbits); } RSA *rsa = RSA_generate_key(nbits, e, NULL, NULL); if ((rsa == NULL) || !EVP_PKEY_assign_RSA(pkey, rsa)) { if(rsa) RSA_free(rsa); fprintf(stderr,"GenerateX509Req: Couldn't Generate RSA Key!\n"); return 0; } // open the file. FILE *out; if (NULL == (out = fopen(pkey_file.c_str(), "w"))) { fprintf(stderr,"GenerateX509Req: Couldn't Create Key File!"); fprintf(stderr," : %s\n", pkey_file.c_str()); return 0; } const EVP_CIPHER *cipher = EVP_des_ede3_cbc(); if (!PEM_write_PrivateKey(out,pkey,cipher, NULL,0,NULL,(void *) passwd.c_str())) { fprintf(stderr,"GenerateX509Req() Couldn't Save Private Key"); fprintf(stderr," : %s\n", pkey_file.c_str()); return 0; } fclose(out); // We have now created a private key.... fprintf(stderr,"GenerateX509Req() Saved Private Key"); fprintf(stderr," : %s\n", pkey_file.c_str()); /********** Test Loading the private Key.... ************/ FILE *tst_in = NULL; EVP_PKEY *tst_pkey = NULL; if (NULL == (tst_in = fopen(pkey_file.c_str(), "rb"))) { fprintf(stderr,"GenerateX509Req() Couldn't Open Private Key"); fprintf(stderr," : %s\n", pkey_file.c_str()); return 0; } if (NULL == (tst_pkey = PEM_read_PrivateKey(tst_in,NULL,NULL,(void *) passwd.c_str()))) { fprintf(stderr,"GenerateX509Req() Couldn't Read Private Key"); fprintf(stderr," : %s\n", pkey_file.c_str()); return 0; } fclose(tst_in); EVP_PKEY_free(tst_pkey); /********** Test Loading the private Key.... ************/ /* Fill in details: fields. req->req_info; req->req_info->enc; req->req_info->version; req->req_info->subject; req->req_info->pubkey; ****************************/ long version = 0x00; unsigned long chtype = MBSTRING_ASC; X509_NAME *x509_name = X509_NAME_new(); // fill in the request. /**** X509_REQ -> Version ********************************/ if (!X509_REQ_set_version(req,version)) /* version 1 */ { fprintf(stderr,"GenerateX509Req(): Couldn't Set Version!\n"); return 0; } /**** X509_REQ -> Version ********************************/ /**** X509_REQ -> Key ********************************/ if (!X509_REQ_set_pubkey(req,pkey)) { fprintf(stderr,"GenerateX509Req() Couldn't Set PUBKEY !\n"); return 0; } /**** SUBJECT ********************************/ // create the name. // fields to add. // commonName CN // emailAddress (none) // organizationName O // localityName L // stateOrProvinceName ST // countryName C if (0 < strlen(name.c_str())) { X509_NAME_add_entry_by_txt(x509_name, "CN", chtype, (unsigned char *) name.c_str(), -1, -1, 0); } else { fprintf(stderr,"GenerateX509Req(): No Name -> Not creating X509 Cert Req\n"); return 0; } if (0 < strlen(email.c_str())) { //X509_NAME_add_entry_by_txt(x509_name, "Email", 0, // (unsigned char *) ui -> gen_email -> value(), -1, -1, 0); X509_NAME_add_entry_by_NID(x509_name, 48, 0, (unsigned char *) email.c_str(), -1, -1, 0); } if (0 < strlen(org.c_str())) { X509_NAME_add_entry_by_txt(x509_name, "O", chtype, (unsigned char *) org.c_str(), -1, -1, 0); } if (0 < strlen(loc.c_str())) { X509_NAME_add_entry_by_txt(x509_name, "L", chtype, (unsigned char *) loc.c_str(), -1, -1, 0); } if (0 < strlen(state.c_str())) { X509_NAME_add_entry_by_txt(x509_name, "ST", chtype, (unsigned char *) state.c_str(), -1, -1, 0); } if (0 < strlen(country.c_str())) { X509_NAME_add_entry_by_txt(x509_name, "C", chtype, (unsigned char *) country.c_str(), -1, -1, 0); } if (!X509_REQ_set_subject_name(req,x509_name)) { fprintf(stderr,"GenerateX509Req() Couldn't Set Name to Request!\n"); X509_NAME_free(x509_name); return 0; } X509_NAME_free(x509_name); /**** SUBJECT ********************************/ if (!X509_REQ_sign(req,pkey,EVP_sha1())) { fprintf(stderr,"GenerateX509Req() Failed to Sign REQ\n"); return 0; } return req; } #define SERIAL_RAND_BITS 64 X509 *SignX509Certificate(X509_NAME *issuer, EVP_PKEY *privkey, X509_REQ *req, long days) { const EVP_MD *digest = EVP_sha1(); ASN1_INTEGER *serial = ASN1_INTEGER_new(); EVP_PKEY *tmppkey; X509 *x509 = X509_new(); if (x509 == NULL) return NULL; BIGNUM *btmp = BN_new(); if (!BN_pseudo_rand(btmp, SERIAL_RAND_BITS, 0, 0)) { fprintf(stderr,"SignX509Certificate() Failed: "); fprintf(stderr," pseudo_rand\n"); return NULL; } if (!BN_to_ASN1_INTEGER(btmp, serial)) { fprintf(stderr,"SignX509Certificate() Failed: "); fprintf(stderr," int\n"); return NULL; } BN_free(btmp); if (!X509_set_serialNumber(x509, serial)) { fprintf(stderr,"SignX509Certificate() Failed: "); fprintf(stderr," serialNumber\n"); return NULL; } ASN1_INTEGER_free(serial); if (!X509_set_issuer_name(x509, issuer)) { fprintf(stderr,"SignX509Certificate() Failed: "); fprintf(stderr," issuer\n"); return NULL; } if (!X509_gmtime_adj(x509->cert_info->validity->notBefore, 0)) { fprintf(stderr,"SignX509Certificate() Failed: "); fprintf(stderr," notBefore\n"); return NULL; } //x509->cert_info->validity->notAfter //if (!X509_gmtime_adj(X509_get_notAfter(x509), (long)60*60*24*days)) if (!X509_gmtime_adj(x509->cert_info->validity->notAfter, (long)60*60*24*days)) { fprintf(stderr,"SignX509Certificate() Failed: "); fprintf(stderr," notAfter\n"); return NULL; } if (!X509_set_subject_name(x509, X509_REQ_get_subject_name(req))) { fprintf(stderr,"SignX509Certificate() Failed: "); fprintf(stderr," subject_name\n"); return NULL; } tmppkey = X509_REQ_get_pubkey(req); if (!tmppkey || !X509_set_pubkey(x509,tmppkey)) { fprintf(stderr,"SignX509Certificate() Failed: "); fprintf(stderr," pubkey\n"); return NULL; } /* Cleanup Algorithm part */ X509_ALGOR *algor1 = x509->cert_info->signature; X509_ALGOR *algor2 = x509->sig_alg; X509_ALGOR *a; a = algor1; ASN1_TYPE_free(a->parameter); a->parameter=ASN1_TYPE_new(); a->parameter->type=V_ASN1_NULL; ASN1_OBJECT_free(a->algorithm); a->algorithm=OBJ_nid2obj(digest->pkey_type); a = algor2; ASN1_TYPE_free(a->parameter); a->parameter=ASN1_TYPE_new(); a->parameter->type=V_ASN1_NULL; ASN1_OBJECT_free(a->algorithm); a->algorithm=OBJ_nid2obj(digest->pkey_type); if (!X509_sign(x509,privkey,digest)) { long e = ERR_get_error(); fprintf(stderr,"SignX509Certificate() Failed: "); fprintf(stderr," signing Error: %ld\n", e); fprintf(stderr,"ERR: %s, %s, %s\n", ERR_lib_error_string(e), ERR_func_error_string(e), ERR_reason_error_string(e)); int inl=i2d_X509(x509,NULL); int outl=EVP_PKEY_size(privkey); fprintf(stderr,"Size Check: inl: %d, outl: %d\n", inl, outl); return NULL; } fprintf(stderr,"SignX509Certificate() Success\n"); return x509; } AuthSSL::AuthSSL() :init(0), sslctx(NULL), pkey(NULL), mToSaveCerts(false), mConfigSaveActive(true) { } bool AuthSSL::active() { return init; } // args: server cert, server private key, trusted certificates. static int verify_x509_callback(int preverify_ok, X509_STORE_CTX *ctx) { #ifdef AUTHSSL_DEBUG std::cerr << "static verify_x509_callback called."; std::cerr << std::endl; #endif return AuthSSL::getAuthSSL()->VerifyX509Callback(preverify_ok, ctx); } int AuthSSL::InitAuth(const char *cert_file, const char *priv_key_file, const char *passwd) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::InitAuth()"; std::cerr << std::endl; #endif static int initLib = 0; if (!initLib) { initLib = 1; SSL_load_error_strings(); SSL_library_init(); } if (init == 1) { std::cerr << "AuthSSL::InitAuth already initialized." << std::endl; return 1; } if ((cert_file == NULL) || (priv_key_file == NULL) || (passwd == NULL)) { //fprintf(stderr, "sslroot::initssl() missing parameters!\n"); return 0; } // actions_to_seed_PRNG(); RAND_seed(passwd, strlen(passwd)); std::cerr << "SSL Library Init!" << std::endl; // setup connection method sslctx = SSL_CTX_new(TLSv1_method()); // setup cipher lists. SSL_CTX_set_cipher_list(sslctx, "DEFAULT"); // certificates (Set Local Server Certificate). FILE *ownfp = fopen(cert_file, "r"); if (ownfp == NULL) { std::cerr << "Couldn't open Own Certificate!" << std::endl; return -1; } // get xPGP certificate. X509 *x509 = PEM_read_X509(ownfp, NULL, NULL, NULL); fclose(ownfp); if (x509 == NULL) { std::cerr << "AuthSSL::InitAuth() PEM_read_X509() Failed"; std::cerr << std::endl; return -1; } SSL_CTX_use_certificate(sslctx, x509); // get private key FILE *pkfp = fopen(priv_key_file, "rb"); if (pkfp == NULL) { std::cerr << "Couldn't Open PrivKey File!" << std::endl; CloseAuth(); return -1; } pkey = PEM_read_PrivateKey(pkfp, NULL, NULL, (void *) passwd); fclose(pkfp); if (pkey == NULL) { std::cerr << "AuthSSL::InitAuth() PEM_read_PrivateKey() Failed"; std::cerr << std::endl; return -1; } SSL_CTX_use_PrivateKey(sslctx, pkey); if (1 != SSL_CTX_check_private_key(sslctx)) { std::cerr << "Issues With Private Key! - Doesn't match your Cert" << std::endl; std::cerr << "Check your input key/certificate:" << std::endl; std::cerr << priv_key_file << " & " << cert_file; std::cerr << std::endl; CloseAuth(); return -1; } if (!getX509id(x509, mOwnId)) { std::cerr << "AuthSSL::InitAuth() getX509id() Failed"; std::cerr << std::endl; /* bad certificate */ CloseAuth(); return -1; } /* Check that Certificate is Ok ( virtual function ) * for gpg/pgp or CA verification */ if (!validateOwnCertificate(x509, pkey)) { std::cerr << "AuthSSL::InitAuth() validateOwnCertificate() Failed"; std::cerr << std::endl; /* bad certificate */ CloseAuth(); return -1; } // enable verification of certificates (PEER) // and install verify callback. SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_x509_callback); std::cerr << "SSL Verification Set" << std::endl; mOwnCert = new sslcert(x509, mOwnId); init = 1; return 1; } /* Dummy function to be overloaded by real implementation */ bool AuthSSL::validateOwnCertificate(X509 *x509, EVP_PKEY *pkey) { return true; //return false; } bool AuthSSL::CloseAuth() { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::CloseAuth()"; std::cerr << std::endl; #endif SSL_CTX_free(sslctx); // clean up private key.... // remove certificates etc -> opposite of initssl. init = 0; return 1; } /* Context handling */ SSL_CTX *AuthSSL::getCTX() { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::getCTX()"; std::cerr << std::endl; #endif return sslctx; } /* Context handling */ SSL_CTX *AuthSSL::getNewSslCtx() { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::getNewSslCtx()" << std::endl; #endif // setup connection method SSL_CTX *newSslctx = SSL_CTX_new(TLSv1_method()); // setup cipher lists. SSL_CTX_set_cipher_list(newSslctx, "DEFAULT"); // certificates (Set Local Server Certificate). SSL_CTX_use_certificate(newSslctx, mOwnCert->certificate); // get private key SSL_CTX_use_PrivateKey(newSslctx, pkey); // enable verification of certificates (PEER) // and install verify callback. SSL_CTX_set_verify(newSslctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_x509_callback); std::cerr << "getNewSslCtx() finished" << std::endl; return newSslctx; } int AuthSSL::setConfigDirectories(std::string configfile, std::string neighdir) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::setConfigDirectories() "; std::cerr << " configfile: " << configfile; std::cerr << " neighdir: " << neighdir; std::cerr << std::endl; #endif RsStackMutex stack(sslMtx); /***** LOCK *****/ mCertConfigFile = configfile; mNeighDir = neighdir; return 1; } std::string AuthSSL::OwnId() { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::OwnId()"; std::cerr << std::endl; #endif return mOwnId; } std::string AuthSSL::getOwnLocation() { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::OwnId()"; std::cerr << std::endl; #endif return mOwnCert->location; } /* Load/Save certificates */ //don't save the ssl certs anymore, just return the id bool AuthSSL::LoadDetailsFromStringCert(std::string pem, RsPeerDetails &pd) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::LoadIdsFromStringCert() " << std::endl; #endif X509 *x509 = loadX509FromPEM(pem); if (!x509) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::LoadIdsFromStringCert() certificate not loadable (maybe malformed)" << std::endl; #endif return false; } if (!ValidateCertificate(x509, pd.id)) { return false; } else { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::LoadIdsFromStringCert() certificate validated." << std::endl; #endif pd.gpg_id = getX509CNString(x509->cert_info->issuer); pd.location = getX509LocString(x509->cert_info->subject); return true; } } std::string AuthSSL::SaveOwnCertificateToString() { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::SaveOwnCertificateToString() " << std::endl; #endif /* get the cert first */ std::string certstr; BIO *bp = BIO_new(BIO_s_mem()); PEM_write_bio_X509(bp, mOwnCert->certificate); /* translate the bp data to a string */ char *data; int len = BIO_get_mem_data(bp, &data); for(int i = 0; i < len; i++) { certstr += data[i]; } BIO_free(bp); return certstr; } /* Sign / Encrypt / Verify Data (TODO) */ bool AuthSSL::SignData(std::string input, std::string &sign) { return SignData(input.c_str(), input.length(), sign); } bool AuthSSL::SignData(const void *data, const uint32_t len, std::string &sign) { RsStackMutex stack(sslMtx); /***** STACK LOCK MUTEX *****/ EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); unsigned int signlen = EVP_PKEY_size(pkey); unsigned char signature[signlen]; if (0 == EVP_SignInit(mdctx, EVP_sha1())) { std::cerr << "EVP_SignInit Failure!" << std::endl; EVP_MD_CTX_destroy(mdctx); return false; } if (0 == EVP_SignUpdate(mdctx, data, len)) { std::cerr << "EVP_SignUpdate Failure!" << std::endl; EVP_MD_CTX_destroy(mdctx); return false; } if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) { std::cerr << "EVP_SignFinal Failure!" << std::endl; EVP_MD_CTX_destroy(mdctx); return false; } EVP_MD_CTX_destroy(mdctx); sign.clear(); std::ostringstream out; out << std::hex; for(uint32_t i = 0; i < signlen; i++) { out << std::setw(2) << std::setfill('0'); out << (uint32_t) (signature[i]); } sign = out.str(); return true; } bool AuthSSL::SignDataBin(std::string input, unsigned char *sign, unsigned int *signlen) { return SignDataBin(input.c_str(), input.length(), sign, signlen); } bool AuthSSL::SignDataBin(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen) { RsStackMutex stack(sslMtx); /***** STACK LOCK MUTEX *****/ EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); unsigned int req_signlen = EVP_PKEY_size(pkey); if (req_signlen > *signlen) { /* not enough space */ std::cerr << "SignDataBin() Not Enough Sign SpacegnInit Failure!" << std::endl; return false; } if (0 == EVP_SignInit(mdctx, EVP_sha1())) { std::cerr << "EVP_SignInit Failure!" << std::endl; EVP_MD_CTX_destroy(mdctx); return false; } if (0 == EVP_SignUpdate(mdctx, data, len)) { std::cerr << "EVP_SignUpdate Failure!" << std::endl; EVP_MD_CTX_destroy(mdctx); return false; } if (0 == EVP_SignFinal(mdctx, sign, signlen, pkey)) { std::cerr << "EVP_SignFinal Failure!" << std::endl; EVP_MD_CTX_destroy(mdctx); return false; } EVP_MD_CTX_destroy(mdctx); return true; } //#define AUTHSSL_DEBUG2 bool AuthSSL::VerifyOtherSignBin(const void *data, const uint32_t len, unsigned char *sign, unsigned int signlen, std::string sslCert) { X509 *x509 = loadX509FromPEM(sslCert); std::string sslId; if (!ValidateCertificate(x509, sslId)) { std::cerr << "AuthSSL::VerifyOtherSignBin() failed to validate certificate." << std::endl; return false; } else { sslcert *cert = new sslcert(x509, sslId); return VerifySignBin(data, len, sign, signlen, cert); } return false; } bool AuthSSL::VerifyOwnSignBin(const void *data, const uint32_t len, unsigned char *sign, unsigned int signlen) { return VerifySignBin(data, len, sign, signlen, mOwnCert); } bool AuthSSL::VerifySignBin(const void *data, const uint32_t len, unsigned char *sign, unsigned int signlen, sslcert* peer) { RsStackMutex stack(sslMtx); /***** STACK LOCK MUTEX *****/ /* find the peer */ #ifdef AUTHSSL_DEBUG2 std::cerr << "In AuthSSL::VerifySignBin" << std::endl; #endif //std::cerr << "Cert info : " << getX509Info(X509 *cert) << std::endl; /* cert->cert_info->key->pkey is NULL until we call SSL_CTX_use_certificate(), * so we do it here then... */ SSL_CTX *newSslctx = SSL_CTX_new(TLSv1_method()); SSL_CTX_set_cipher_list(newSslctx, "DEFAULT"); SSL_CTX_use_certificate(newSslctx, peer->certificate); EVP_PKEY *peerkey = peer->certificate->cert_info->key->pkey; if(peerkey == NULL) { #ifdef AUTHSSL_DEBUG2 std::cerr << "AuthSSL::VerifySignBin: no public key available !!" << std::endl ; #endif return false ; } EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); if (0 == EVP_VerifyInit(mdctx, EVP_sha1())) { std::cerr << "EVP_VerifyInit Failure!" << std::endl; EVP_MD_CTX_destroy(mdctx); X509_free(peer->certificate); return false; } if (0 == EVP_VerifyUpdate(mdctx, data, len)) { std::cerr << "EVP_VerifyUpdate Failure!" << std::endl; EVP_MD_CTX_destroy(mdctx); X509_free(peer->certificate); return false; } if(signlen == 0 || sign == NULL) { #ifdef AUTHSSL_DEBUG2 std::cerr << "AuthSSL::VerifySignBin: signlen=" << signlen << ", sign=" << (void*)sign << "!!" << std::endl ; #endif EVP_MD_CTX_destroy(mdctx); X509_free(peer->certificate); return false ; } if (0 == EVP_VerifyFinal(mdctx, sign, signlen, peerkey)) { std::cerr << "EVP_VerifyFinal Failure!" << std::endl; EVP_MD_CTX_destroy(mdctx); X509_free(peer->certificate); return false; } EVP_MD_CTX_destroy(mdctx); X509_free(peer->certificate); return true; } X509 *AuthSSL::loadX509FromFile(std::string fname, std::string hash) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::LoadX509FromFile()"; std::cerr << std::endl; #endif // if there is a hash - check that the file matches it before loading. X509 *pc = NULL; FILE *pcertfp = fopen(fname.c_str(), "rb"); // load certificates from file. if (pcertfp == NULL) { #ifdef AUTHSSL_DEBUG std::cerr << "sslroot::loadcertificate() Bad File: " << fname; std::cerr << " Cannot be Hashed!" << std::endl; #endif return NULL; } /* We only check a signature's hash if * we are loading from a configuration file. * Therefore we saved the file and it should be identical. * and a direct load + verify will work. * * If however it has been transported by email.... * Then we might have to correct the data (strip out crap) * from the configuration at the end. (X509 load should work!) */ if (hash.length() > 1) { unsigned int signlen = EVP_PKEY_size(pkey); unsigned char signature[signlen]; int maxsize = 20480; /* should be enough for about 50 signatures */ int rbytes; char inall[maxsize]; if (0 == (rbytes = fread(inall, 1, maxsize, pcertfp))) { #ifdef AUTHSSL_DEBUG std::cerr << "Error Reading Peer Record!" << std::endl; #endif return NULL; } //std::cerr << "Read " << rbytes << std::endl; EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) { std::cerr << "EVP_SignInit Failure!" << std::endl; } if (0 == EVP_SignUpdate(mdctx, inall, rbytes)) { std::cerr << "EVP_SignUpdate Failure!" << std::endl; } if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) { std::cerr << "EVP_SignFinal Failure!" << std::endl; } EVP_MD_CTX_destroy(mdctx); bool same = true; if (signlen != hash.length()) { #ifdef AUTHSSL_DEBUG std::cerr << "Different Length Signatures... "; std::cerr << "Cannot Load Certificate!" << std::endl; #endif fclose(pcertfp); return NULL; } for(int i = 0; i < (signed) signlen; i++) { if (signature[i] != (unsigned char) hash[i]) { same = false; #ifdef AUTHSSL_DEBUG std::cerr << "Invalid Signature... "; std::cerr << "Cannot Load Certificate!" << std::endl; #endif fclose(pcertfp); return NULL; } } #ifdef AUTHSSL_DEBUG std::cerr << "Verified Signature for: " << fname; std::cerr << std::endl; #endif } else { #ifdef AUTHSSL_DEBUG std::cerr << "Not checking cert signature" << std::endl; #endif } fseek(pcertfp, 0, SEEK_SET); /* rewind */ pc = PEM_read_X509(pcertfp, NULL, NULL, NULL); fclose(pcertfp); if (pc != NULL) { // read a certificate. #ifdef AUTHSSL_DEBUG std::cerr << "Loaded Certificate: " << pc -> name << std::endl; #endif } else // (pc == NULL) { unsigned long err = ERR_get_error(); std::cerr << "Read Failed .... CODE(" << err << ")" << std::endl; std::cerr << ERR_error_string(err, NULL) << std::endl; return NULL; } return pc; } bool AuthSSL::saveX509ToFile(X509 *x509, std::string fname, std::string &hash) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::saveX509ToFile()"; std::cerr << std::endl; #endif // load certificates from file. FILE *setfp = fopen(fname.c_str(), "wb"); if (setfp == NULL) { #ifdef AUTHSSL_DEBUG std::cerr << "sslroot::savecertificate() Bad File: " << fname; std::cerr << " Cannot be Written!" << std::endl; #endif return false; } #ifdef AUTHSSL_DEBUG std::cerr << "Writing out Cert...:" << x509->name << std::endl; #endif PEM_write_X509(setfp, x509); fclose(setfp); // then reopen to generate hash. setfp = fopen(fname.c_str(), "rb"); if (setfp == NULL) { #ifdef AUTHSSL_DEBUG std::cerr << "sslroot::savecertificate() Bad File: " << fname; std::cerr << " Opened for ReHash!" << std::endl; #endif return false; } unsigned int signlen = EVP_PKEY_size(pkey); unsigned char signature[signlen]; int maxsize = 20480; int rbytes; char inall[maxsize]; if (0 == (rbytes = fread(inall, 1, maxsize, setfp))) { #ifdef AUTHSSL_DEBUG std::cerr << "Error Writing Peer Record!" << std::endl; #endif return -1; } #ifdef AUTHSSL_DEBUG std::cerr << "Read " << rbytes << std::endl; #endif EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL)) { std::cerr << "EVP_SignInit Failure!" << std::endl; } if (0 == EVP_SignUpdate(mdctx, inall, rbytes)) { std::cerr << "EVP_SignUpdate Failure!" << std::endl; } if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey)) { std::cerr << "EVP_SignFinal Failure!" << std::endl; } #ifdef AUTHSSL_DEBUG std::cerr << "Saved Cert: " << x509->name; std::cerr << std::endl; #endif #ifdef AUTHSSL_DEBUG std::cerr << "Cert + Setting Signature is(" << signlen << "): "; #endif std::string signstr; for(uint32_t i = 0; i < signlen; i++) { #ifdef AUTHSSL_DEBUG fprintf(stderr, "%02x", signature[i]); #endif signstr += signature[i]; } #ifdef AUTHSSL_DEBUG std::cerr << std::endl; #endif hash = signstr; fclose(setfp); EVP_MD_CTX_destroy(mdctx); return true; } X509 *AuthSSL::loadX509FromPEM(std::string pem) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::LoadX509FromPEM()"; std::cerr << std::endl; #endif /* Put the data into a mem BIO */ char *certstr = strdup(pem.c_str()); BIO *bp = BIO_new_mem_buf(certstr, -1); X509 *pc = PEM_read_bio_X509(bp, NULL, NULL, NULL); BIO_free(bp); free(certstr); return pc; } X509 *AuthSSL::loadX509FromDER(const uint8_t *ptr, uint32_t len) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::LoadX509FromDER()"; std::cerr << std::endl; #endif X509 *tmp = NULL; const unsigned char **certptr = (const unsigned char **) &ptr; X509 *x509 = d2i_X509(&tmp, certptr, len); return x509; } bool AuthSSL::saveX509ToDER(X509 *x509, uint8_t **ptr, uint32_t *len) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::saveX509ToDER()"; std::cerr << std::endl; #endif int certlen = i2d_X509(x509, (unsigned char **) ptr); if (certlen > 0) { *len = certlen; return true; } else { *len = 0; return false; } return false; } bool getX509id(X509 *x509, std::string &xid) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::getX509id()"; std::cerr << std::endl; #endif xid = ""; if (x509 == NULL) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::getX509id() NULL pointer"; std::cerr << std::endl; #endif return false; } // get the signature from the cert, and copy to the array. ASN1_BIT_STRING *signature = x509->signature; int signlen = ASN1_STRING_length(signature); if (signlen < CERTSIGNLEN) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::getX509id() ERROR: Short Signature"; std::cerr << std::endl; #endif return false; } // else copy in the first CERTSIGNLEN. unsigned char *signdata = ASN1_STRING_data(signature); std::ostringstream id; /* switched to the other end of the signature. for * more randomness */ for(int i = signlen - CERTSIGNLEN; i < signlen; i++) { id << std::hex << std::setw(2) << std::setfill('0') << (uint16_t) (((uint8_t *) (signdata))[i]); } xid = id.str(); return true; } X509 *AuthSSL::SignX509Req(X509_REQ *req, long days) { RsStackMutex stack(sslMtx); /******* LOCKED ******/ /* Transform the X509_REQ into a suitable format to * generate DIGEST hash. (for SSL to do grunt work) */ #define SERIAL_RAND_BITS 64 const EVP_MD *digest = EVP_sha1(); ASN1_INTEGER *serial = ASN1_INTEGER_new(); EVP_PKEY *tmppkey; X509 *x509 = X509_new(); if (x509 == NULL) { std::cerr << "AuthSSL::SignX509Req() FAIL" << std::endl; return NULL; } long version = 0x00; unsigned long chtype = MBSTRING_ASC; X509_NAME *issuer_name = X509_NAME_new(); X509_NAME_add_entry_by_txt(issuer_name, "CN", chtype, (unsigned char *) AuthGPG::getAuthGPG()->getGPGOwnId().c_str(), -1, -1, 0); /**** X509_NAME_add_entry_by_NID(issuer_name, 48, 0, (unsigned char *) "email@email.com", -1, -1, 0); X509_NAME_add_entry_by_txt(issuer_name, "O", chtype, (unsigned char *) "org", -1, -1, 0); X509_NAME_add_entry_by_txt(x509_name, "L", chtype, (unsigned char *) "loc", -1, -1, 0); ****/ std::cerr << "AuthSSL::SignX509Req() Issuer name: " << AuthGPG::getAuthGPG()->getGPGOwnId() << std::endl; BIGNUM *btmp = BN_new(); if (!BN_pseudo_rand(btmp, SERIAL_RAND_BITS, 0, 0)) { std::cerr << "AuthSSL::SignX509Req() rand FAIL" << std::endl; return NULL; } if (!BN_to_ASN1_INTEGER(btmp, serial)) { std::cerr << "AuthSSL::SignX509Req() asn1 FAIL" << std::endl; return NULL; } BN_free(btmp); if (!X509_set_serialNumber(x509, serial)) { std::cerr << "AuthSSL::SignX509Req() serial FAIL" << std::endl; return NULL; } ASN1_INTEGER_free(serial); /* Generate SUITABLE issuer name. * Must reference OpenPGP key, that is used to verify it */ if (!X509_set_issuer_name(x509, issuer_name)) { std::cerr << "AuthSSL::SignX509Req() issue FAIL" << std::endl; return NULL; } X509_NAME_free(issuer_name); if (!X509_gmtime_adj(X509_get_notBefore(x509),0)) { std::cerr << "AuthSSL::SignX509Req() notbefore FAIL" << std::endl; return NULL; } if (!X509_gmtime_adj(X509_get_notAfter(x509), (long)60*60*24*days)) { std::cerr << "AuthSSL::SignX509Req() notafter FAIL" << std::endl; return NULL; } if (!X509_set_subject_name(x509, X509_REQ_get_subject_name(req))) { std::cerr << "AuthSSL::SignX509Req() sub FAIL" << std::endl; return NULL; } tmppkey = X509_REQ_get_pubkey(req); if (!tmppkey || !X509_set_pubkey(x509,tmppkey)) { std::cerr << "AuthSSL::SignX509Req() pub FAIL" << std::endl; return NULL; } std::cerr << "X509 Cert, prepared for signing" << std::endl; /*** NOW The Manual signing bit (HACKED FROM asn1/a_sign.c) ***/ int (*i2d)(X509_CINF*, unsigned char**) = i2d_X509_CINF; X509_ALGOR *algor1 = x509->cert_info->signature; X509_ALGOR *algor2 = x509->sig_alg; ASN1_BIT_STRING *signature = x509->signature; X509_CINF *data = x509->cert_info; EVP_PKEY *pkey = NULL; const EVP_MD *type = EVP_sha1(); EVP_MD_CTX ctx; unsigned char *p,*buf_in=NULL; unsigned char *buf_hashout=NULL,*buf_sigout=NULL; int i,inl=0,hashoutl=0,hashoutll=0; int sigoutl=0,sigoutll=0; X509_ALGOR *a; EVP_MD_CTX_init(&ctx); /* FIX ALGORITHMS */ a = algor1; ASN1_TYPE_free(a->parameter); a->parameter=ASN1_TYPE_new(); a->parameter->type=V_ASN1_NULL; ASN1_OBJECT_free(a->algorithm); a->algorithm=OBJ_nid2obj(type->pkey_type); a = algor2; ASN1_TYPE_free(a->parameter); a->parameter=ASN1_TYPE_new(); a->parameter->type=V_ASN1_NULL; ASN1_OBJECT_free(a->algorithm); a->algorithm=OBJ_nid2obj(type->pkey_type); std::cerr << "Algorithms Fixed" << std::endl; /* input buffer */ inl=i2d(data,NULL); buf_in=(unsigned char *)OPENSSL_malloc((unsigned int)inl); hashoutll=hashoutl=EVP_MD_size(type); buf_hashout=(unsigned char *)OPENSSL_malloc((unsigned int)hashoutl); sigoutll=sigoutl=2048; // hashoutl; //EVP_PKEY_size(pkey); buf_sigout=(unsigned char *)OPENSSL_malloc((unsigned int)sigoutl); if ((buf_in == NULL) || (buf_hashout == NULL) || (buf_sigout == NULL)) { hashoutl=0; sigoutl=0; fprintf(stderr, "AuthSSL::SignX509Req: ASN1err(ASN1_F_ASN1_SIGN,ERR_R_MALLOC_FAILURE)\n"); goto err; } p=buf_in; std::cerr << "Buffers Allocated" << std::endl; i2d(data,&p); /* data in buf_in, ready to be hashed */ EVP_DigestInit_ex(&ctx,type, NULL); EVP_DigestUpdate(&ctx,(unsigned char *)buf_in,inl); if (!EVP_DigestFinal(&ctx,(unsigned char *)buf_hashout, (unsigned int *)&hashoutl)) { hashoutl=0; fprintf(stderr, "AuthSSL::SignX509Req: ASN1err(ASN1_F_ASN1_SIGN,ERR_R_EVP_LIB)\n"); goto err; } std::cerr << "Digest Applied: len: " << hashoutl << std::endl; /* NOW Sign via GPG Functions */ if (!AuthGPG::getAuthGPG()->SignDataBin(buf_hashout, hashoutl, buf_sigout, (unsigned int *) &sigoutl)) { sigoutl = 0; goto err; } std::cerr << "Buffer Sizes: in: " << inl; std::cerr << " HashOut: " << hashoutl; std::cerr << " SigOut: " << sigoutl; std::cerr << std::endl; //passphrase = "NULL"; std::cerr << "Signature done: len:" << sigoutl << std::endl; /* ADD Signature back into Cert... Signed!. */ if (signature->data != NULL) OPENSSL_free(signature->data); signature->data=buf_sigout; buf_sigout=NULL; signature->length=sigoutl; /* In the interests of compatibility, I'll make sure that * the bit string has a 'not-used bits' value of 0 */ signature->flags&= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07); signature->flags|=ASN1_STRING_FLAG_BITS_LEFT; std::cerr << "Certificate Complete" << std::endl; return x509; err: /* cleanup */ std::cerr << "GPGAuthMgr::SignX509Req() err: FAIL" << std::endl; return NULL; } bool AuthSSL::AuthX509(X509 *x509) { #ifdef AUTHSSL_DEBUG fprintf(stderr, "AuthSSL::AuthX509() called\n"); #endif //RsStackMutex stack(sslMtx); /******* LOCKED ******/ /* extract CN for peer Id */ std::string issuer = getX509CNString(x509->cert_info->issuer); RsPeerDetails pd; #ifdef AUTHSSL_DEBUG std::cerr << "Checking GPG issuer : " << issuer << std::endl ; #endif if (!AuthGPG::getAuthGPG()->getGPGDetails(issuer, pd)) { std::cerr << "AuthSSL::AuthX509() X509 NOT authenticated : AuthGPG::getAuthGPG()->getGPGDetails() returned false." << std::endl; return false; } /* verify GPG signature */ /*** NOW The Manual signing bit (HACKED FROM asn1/a_sign.c) ***/ int (*i2d)(X509_CINF*, unsigned char**) = i2d_X509_CINF; ASN1_BIT_STRING *signature = x509->signature; X509_CINF *data = x509->cert_info; const EVP_MD *type = EVP_sha1(); EVP_MD_CTX ctx; unsigned char *p,*buf_in=NULL; unsigned char *buf_hashout=NULL,*buf_sigout=NULL; int i,inl=0,hashoutl=0,hashoutll=0; int sigoutl=0,sigoutll=0; X509_ALGOR *a; EVP_MD_CTX_init(&ctx); /* input buffer */ inl=i2d(data,NULL); buf_in=(unsigned char *)OPENSSL_malloc((unsigned int)inl); hashoutll=hashoutl=EVP_MD_size(type); buf_hashout=(unsigned char *)OPENSSL_malloc((unsigned int)hashoutl); sigoutll=sigoutl=2048; //hashoutl; //EVP_PKEY_size(pkey); buf_sigout=(unsigned char *)OPENSSL_malloc((unsigned int)sigoutl); #ifdef AUTHSSL_DEBUG std::cerr << "Buffer Sizes: in: " << inl; std::cerr << " HashOut: " << hashoutl; std::cerr << " SigOut: " << sigoutl; std::cerr << std::endl; #endif if ((buf_in == NULL) || (buf_hashout == NULL) || (buf_sigout == NULL)) { hashoutl=0; sigoutl=0; fprintf(stderr, "AuthSSL::AuthX509: ASN1err(ASN1_F_ASN1_SIGN,ERR_R_MALLOC_FAILURE)\n"); goto err; } p=buf_in; #ifdef AUTHSSL_DEBUG std::cerr << "Buffers Allocated" << std::endl; #endif i2d(data,&p); /* data in buf_in, ready to be hashed */ EVP_DigestInit_ex(&ctx,type, NULL); EVP_DigestUpdate(&ctx,(unsigned char *)buf_in,inl); if (!EVP_DigestFinal(&ctx,(unsigned char *)buf_hashout, (unsigned int *)&hashoutl)) { hashoutl=0; fprintf(stderr, "AuthSSL::AuthX509: ASN1err(ASN1_F_ASN1_SIGN,ERR_R_EVP_LIB)\n"); goto err; } #ifdef AUTHSSL_DEBUG std::cerr << "Digest Applied: len: " << hashoutl << std::endl; #endif /* copy data into signature */ sigoutl = signature->length; memmove(buf_sigout, signature->data, sigoutl); /* NOW check sign via GPG Functions */ //get the fingerprint of the key that is supposed to sign #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::AuthX509() verifying the gpg sig with keyprint : " << pd.fpr << std::endl; std::cerr << "Sigoutl = " << sigoutl << std::endl ; std::cerr << "pd.fpr = " << pd.fpr << std::endl ; std::cerr << "hashoutl = " << hashoutl << std::endl ; #endif if (!AuthGPG::getAuthGPG()->VerifySignBin(buf_hashout, hashoutl, buf_sigout, (unsigned int) sigoutl, pd.fpr)) { sigoutl = 0; goto err; } #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::AuthX509() X509 authenticated" << std::endl; #endif return true; err: std::cerr << "AuthSSL::AuthX509() X509 NOT authenticated" << std::endl; return false; } /* validate + get id */ bool AuthSSL::ValidateCertificate(X509 *x509, std::string &peerId) { /* check self signed */ if (!AuthX509(x509)) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::ValidateCertificate() bad certificate."; std::cerr << std::endl; #endif return false; } if(!getX509id(x509, peerId)) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::ValidateCertificate() Cannot retrieve peer id from certificate.."; std::cerr << std::endl; #endif return false; } #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::ValidateCertificate() good certificate."; std::cerr << std::endl; #endif return true; } /* store for discovery */ //bool AuthSSL::FailedCertificate(X509 *x509, bool incoming) //{ // std::string id; // return ProcessX509(x509, id); //} bool AuthSSL::encrypt(void *&out, int &outlen, const void *in, int inlen, std::string peerId) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::encrypt() called with inlen : " << inlen << std::endl; #endif //TODO : use ssl to crypt the binary input buffer out = malloc(inlen); memcpy(out, in, inlen); outlen = inlen; return true ; } bool AuthSSL::decrypt(void *&out, int &outlen, const void *in, int inlen) { #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::decrypt() called with inlen : " << inlen << std::endl; #endif //TODO : use ssl to decrypt the binary input buffer out = malloc(inlen); memcpy(out, in, inlen); outlen = inlen; return true ; } /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ /********************************************************************************/ int pem_passwd_cb(char *buf, int size, int rwflag, void *password) { strncpy(buf, (char *)(password), size); buf[size - 1] = '\0'; return(strlen(buf)); } int AuthSSL::VerifyX509Callback(int preverify_ok, X509_STORE_CTX *ctx) { char buf[256]; X509 *err_cert; int err, depth; err_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::VerifyX509Callback(preverify_ok: " << preverify_ok << " Err: " << err << " Depth: " << depth << std::endl; #endif /* * Retrieve the pointer to the SSL of the connection currently treated * and the application specific data stored into the SSL object. */ X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); #ifdef AUTHSSL_DEBUG std::cerr << "AuthSSL::VerifyX509Callback: depth: " << depth << ":" << buf << std::endl; #endif if (!preverify_ok) { #ifdef AUTHSSL_DEBUG fprintf(stderr, "Verify error:num=%d:%s:depth=%d:%s\n", err, X509_verify_cert_error_string(err), depth, buf); #endif } /* * At this point, err contains the last verification error. We can use * it for something special */ if (!preverify_ok) { X509_NAME_oneline(X509_get_issuer_name(X509_STORE_CTX_get_current_cert(ctx)), buf, 256); #ifdef AUTHSSL_DEBUG printf("issuer= %s\n", buf); #endif #ifdef AUTHSSL_DEBUG fprintf(stderr, "Doing REAL PGP Certificates\n"); #endif /* do the REAL Authentication */ if (!AuthX509(X509_STORE_CTX_get_current_cert(ctx))) { #ifdef AUTHSSL_DEBUG fprintf(stderr, "AuthSSL::VerifyX509Callback() X509 not authenticated.\n"); #endif return false; } std::string pgpid = getX509CNString(X509_STORE_CTX_get_current_cert(ctx)->cert_info->issuer); if (!AuthGPG::getAuthGPG()->isGPGAccepted(pgpid) && pgpid != AuthGPG::getAuthGPG()->getGPGOwnId()) { #ifdef AUTHSSL_DEBUG fprintf(stderr, "AuthSSL::VerifyX509Callback() pgp key not signed by ourself : \n"); fprintf(stderr, "issuer pgpid : "); fprintf(stderr, "%s\n",pgpid.c_str()); fprintf(stderr, "\n AuthGPG::getAuthGPG()->getGPGOwnId() : "); fprintf(stderr, "%s\n",AuthGPG::getAuthGPG()->getGPGOwnId().c_str()); fprintf(stderr, "\n"); #endif return false; } preverify_ok = true; } else { #ifdef AUTHSSL_DEBUG fprintf(stderr, "Failing Normal Certificate!!!\n"); #endif preverify_ok = false; } if (preverify_ok) { //sslcert *cert = NULL; std::string certId; getX509id(X509_STORE_CTX_get_current_cert(ctx), certId); if (!mConnMgr->isFriend(certId)) { //we've got a new ssl id preverify_ok = false; } //is the connection was initiated by us, then it was for a specific peer id wich is stored is in the context //check that the peerid in the context is the same as the cert one SSL *ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); if (SSL_get_ex_data(ssl, AuthSSL::ex_data_ctx_index)) { char *peer_id_in_context = (char*) SSL_get_ex_data(ssl, AuthSSL::ex_data_ctx_index); if (std::string(certId.c_str()) != std::string(peer_id_in_context)) { //the connection was asked for a given peer and get connected top another peer #ifdef AUTHSSL_DEBUG fprintf(stderr, "AuthSSL::VerifyX509Callback peer id in context not the same as cert, aborting connection."); #endif preverify_ok = false; //tranfer the ip address to the new peer peerConnectState detail; if (mConnMgr->getFriendNetStatus(peer_id_in_context, detail)) { mConnMgr->addFriend(certId, getX509CNString(X509_STORE_CTX_get_current_cert(ctx)->cert_info->issuer)); mConnMgr->setAddressList(certId, detail.getIpAddressList()); } } else { #ifdef AUTHSSL_DEBUG fprintf(stderr, "AuthSSL::VerifyX509Callback peer id in context is the same as cert, continung connection."); #endif } } //just to be sure mConnMgr->addFriend(certId, getX509CNString(X509_STORE_CTX_get_current_cert(ctx)->cert_info->issuer)); //set location mConnMgr->setLocation(certId, getX509LocString(X509_STORE_CTX_get_current_cert(ctx)->cert_info->subject)); //Check if peer isn't already connected peerConnectState detail; if (mConnMgr->getFriendNetStatus(certId, detail)) { if (detail.state & RS_PEER_CONNECTED && !(detail.connecttype & RS_NET_CONN_TUNNEL)) { #ifdef AUTHSSL_DEBUG fprintf(stderr, "AuthSSL::VerifyX509Callback this peer is already connected, refuse a new connection."); #endif preverify_ok = false; } } } #ifdef AUTHSSL_DEBUG if (preverify_ok) { fprintf(stderr, "AuthSSL::VerifyX509Callback returned true.\n"); } else { fprintf(stderr, "AuthSSL::VerifyX509Callback returned false.\n"); } #endif return preverify_ok; } // Not dependent on sslroot. load, and detroys the X509 memory. int LoadCheckX509andGetLocation(const char *cert_file, std::string &location, std::string &userId) { /* This function loads the X509 certificate from the file, * and checks the certificate */ FILE *tmpfp = fopen(cert_file, "r"); if (tmpfp == NULL) { std::cerr << "sslroot::LoadCheckAndGetX509Name()"; std::cerr << " Failed to open Certificate File:" << cert_file; std::cerr << std::endl; return 0; } // get xPGP certificate. X509 *x509 = PEM_read_X509(tmpfp, NULL, NULL, NULL); fclose(tmpfp); if(x509 == NULL) { std::cerr << "PEM_read_X509 failed !!" << std::endl ; return false ; } // check the certificate. // if (AuthSSL::getAuthSSL()->ValidateCertificate(x509, userId)) location = getX509LocString(x509->cert_info->subject); else { std::cerr << "certificate validation failed !!" << std::endl ; return false ; } #ifdef AUTHSSL_DEBUG std::cerr << "getX509LocString ok. Info:" << std::endl ; std::cout << getX509Info(x509) << std::endl ; #endif // clean up. X509_free(x509); return true ; } // Not dependent on sslroot. load, and detroys the X509 memory. int LoadCheckX509andGetIssuerName(const char *cert_file, std::string &issuerName, std::string &userId) { /* This function loads the X509 certificate from the file, * and checks the certificate */ FILE *tmpfp = fopen(cert_file, "r"); if (tmpfp == NULL) { #ifdef AUTHSSL_DEBUG std::cerr << "sslroot::LoadCheckAndGetX509Name()"; std::cerr << " Failed to open Certificate File:" << cert_file; std::cerr << std::endl; #endif return 0; } // get xPGP certificate. X509 *x509 = PEM_read_X509(tmpfp, NULL, NULL, NULL); fclose(tmpfp); // check the certificate. bool valid = false; if (x509) { valid = AuthSSL::getAuthSSL()->ValidateCertificate(x509, userId); } if (valid) { // extract the name. issuerName = getX509CNString(x509->cert_info->issuer); } #ifdef AUTHSSL_DEBUG std::cout << getX509Info(x509) << std::endl ; #endif // clean up. X509_free(x509); if (valid) { // happy! return 1; } else { // something went wrong! return 0; } } std::string getX509NameString(X509_NAME *name) { std::string namestr; for(int i = 0; i < X509_NAME_entry_count(name); i++) { X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); namestr += "\t"; namestr += OBJ_nid2ln(OBJ_obj2nid(entry_obj)); namestr += " : "; //namestr += entry_obj -> flags; //namestr += entry_data -> length; //namestr += entry_data -> type; //namestr += entry_data -> flags; //entry -> set; if (entry_data -> data != NULL) { namestr += (char *) entry_data -> data; } else { namestr += "NULL"; } if (i + 1 < X509_NAME_entry_count(name)) { namestr += "\n"; } } return namestr; } std::string getX509CNString(X509_NAME *name) { std::string namestr; for(int i = 0; i < X509_NAME_entry_count(name); i++) { X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); if (0 == strncmp("CN", OBJ_nid2sn(OBJ_obj2nid(entry_obj)), 2)) { if (entry_data -> data != NULL) { namestr += (char *) entry_data -> data; } else { namestr += "Unknown"; } return namestr; } } return namestr; } std::string getX509TypeString(X509_NAME *name, const char *type, int len) { std::string namestr; for(int i = 0; i < X509_NAME_entry_count(name); i++) { X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i); ASN1_STRING *entry_data = X509_NAME_ENTRY_get_data(entry); ASN1_OBJECT *entry_obj = X509_NAME_ENTRY_get_object(entry); if (0 == strncmp(type, OBJ_nid2sn(OBJ_obj2nid(entry_obj)), len)) { if (entry_data -> data != NULL) { namestr += (char *) entry_data -> data; } else { namestr += "Unknown"; } return namestr; } } return namestr; } std::string getX509LocString(X509_NAME *name) { return getX509TypeString(name, "L", 2); } std::string getX509OrgString(X509_NAME *name) { return getX509TypeString(name, "O", 2); } std::string getX509CountryString(X509_NAME *name) { return getX509TypeString(name, "C", 2); } std::string getX509Info(X509 *cert) { std::stringstream out; long l; out << "X509 Certificate:" << std::endl; l=X509_get_version(cert); out << " Version: " << l+1 << "(0x" << l << ")" << std::endl; out << " Subject: " << std::endl; out << " " << getX509NameString(cert->cert_info->subject); out << std::endl; out << std::endl; out << " Signatures:" << std::endl; return out.str(); } std::string getX509AuthCode(X509 *x509) { /* get the self signature -> the first signature */ std::stringstream out; ASN1_BIT_STRING *signature = x509->signature; int signlen = ASN1_STRING_length(signature); unsigned char *signdata = ASN1_STRING_data(signature); /* extract the authcode from the signature */ /* convert it to a string, inverse of 2 bytes of signdata */ if (signlen > 2) signlen = 2; int j; for(j=0;jcert_info->subject); // strip out bad chars. for(int i = 0; i < (signed) name.length(); i++) { if ((name[i] == '/') || (name[i] == ' ') || (name[i] == '=') || (name[i] == '\\') || (name[i] == '\t') || (name[i] == '\n')) { name[i] = '_'; } } return name; } /********** SSL ERROR STUFF ******************************************/ int printSSLError(SSL *ssl, int retval, int err, unsigned long err2, std::ostream &out) { std::string reason; std::string mainreason = std::string("UNKNOWN ERROR CODE"); if (err == SSL_ERROR_NONE) { mainreason = std::string("SSL_ERROR_NONE"); } else if (err == SSL_ERROR_ZERO_RETURN) { mainreason = std::string("SSL_ERROR_ZERO_RETURN"); } else if (err == SSL_ERROR_WANT_READ) { mainreason = std::string("SSL_ERROR_WANT_READ"); } else if (err == SSL_ERROR_WANT_WRITE) { mainreason = std::string("SSL_ERROR_WANT_WRITE"); } else if (err == SSL_ERROR_WANT_CONNECT) { mainreason = std::string("SSL_ERROR_WANT_CONNECT"); } else if (err == SSL_ERROR_WANT_ACCEPT) { mainreason = std::string("SSL_ERROR_WANT_ACCEPT"); } else if (err == SSL_ERROR_WANT_X509_LOOKUP) { mainreason = std::string("SSL_ERROR_WANT_X509_LOOKUP"); } else if (err == SSL_ERROR_SYSCALL) { mainreason = std::string("SSL_ERROR_SYSCALL"); } else if (err == SSL_ERROR_SSL) { mainreason = std::string("SSL_ERROR_SSL"); } out << "RetVal(" << retval; out << ") -> SSL Error: " << mainreason << std::endl; out << "\t + ERR Error: " << ERR_error_string(err2, NULL) << std::endl; return 1; }