RetroShare/libretroshare/src/pqi/authssl.cc
chrisparker126 00c1bfcf54 fixed bug related to opening cache hist file in text mode for windows
added cleaning of cipher context to authssl encrypt decrypt functions 

added header to trustview which caused compilation error


git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@4199 b45a01b8-16f6-495d-af2f-9b41ad6348cc
2011-05-15 12:01:23 +00:00

1345 lines
39 KiB
C++
Raw Blame History

/*
* 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 "sslfns.h"
#include "cleanupxpgp.h"
#include "pqinetwork.h"
#include "authgpg.h"
#include "pqi/p3connmgr.h"
#include "serialiser/rsconfigitems.h"
#include "util/rsdir.h"
/******************** notify of new Cert **************************/
#include "pqinotify.h"
#include <openssl/err.h>
//#include <openssl/evp.h>
//#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include <sstream>
#include <iomanip>
/****
* #define AUTHSSL_DEBUG 1
***/
// initialisation du pointeur de singleton <20> z<>ro
static AuthSSL *instance_ssl = NULL;
/* hidden function - for testing purposes() */
void setAuthSSL(AuthSSL *newssl)
{
instance_ssl = newssl;
}
void AuthSSLInit()
{
instance_ssl = new AuthSSLimpl();
}
AuthSSL *AuthSSL::getAuthSSL()
{
return instance_ssl;
}
AuthSSL::AuthSSL()
{
return;
}
/********************************************************************************/
/********************************************************************************/
/********************* Cert Search / Add / Remove **************************/
/********************************************************************************/
/********************************************************************************/
static int verify_x509_callback(int preverify_ok, X509_STORE_CTX *ctx);
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;
}
/************************************************************************
*
*
* CODE IS DIVIDED INTO
*
* 1) SSL Setup.
* 3) Cert Access.
* 4) Cert Sign / Verify.
* 5) Cert Authentication
* 2) Cert Add / Remove
* 6) Cert Storage
*/
/********************************************************************************/
/********************************************************************************/
/********************* Cert Search / Add / Remove **************************/
/********************************************************************************/
/********************************************************************************/
AuthSSLimpl::AuthSSLimpl()
: p3Config(CONFIG_TYPE_AUTHSSL), sslctx(NULL),
mOwnCert(NULL), mOwnPrivateKey(NULL), mOwnPublicKey(NULL), init(0)
{
}
bool AuthSSLimpl::active()
{
return init;
}
int AuthSSLimpl::InitAuth(const char *cert_file, const char *priv_key_file,
const char *passwd)
{
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::InitAuth()";
std::cerr << std::endl;
#endif
/* single call here si don't need to invoke mutex yet */
static int initLib = 0;
if (!initLib)
{
initLib = 1;
SSL_load_error_strings();
SSL_library_init();
}
if (init == 1)
{
std::cerr << "AuthSSLimpl::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 = RsDirUtil::rs_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 << "AuthSSLimpl::InitAuth() PEM_read_X509() Failed";
std::cerr << std::endl;
return -1;
}
SSL_CTX_use_certificate(sslctx, x509);
mOwnPublicKey = X509_get_pubkey(x509);
// get private key
FILE *pkfp = RsDirUtil::rs_fopen(priv_key_file, "rb");
if (pkfp == NULL)
{
std::cerr << "Couldn't Open PrivKey File!" << std::endl;
CloseAuth();
return -1;
}
mOwnPrivateKey = PEM_read_PrivateKey(pkfp, NULL, NULL, (void *) passwd);
fclose(pkfp);
if (mOwnPrivateKey == NULL)
{
std::cerr << "AuthSSLimpl::InitAuth() PEM_read_PrivateKey() Failed";
std::cerr << std::endl;
return -1;
}
SSL_CTX_use_PrivateKey(sslctx, mOwnPrivateKey);
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 << "AuthSSLimpl::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, mOwnPrivateKey))
{
std::cerr << "AuthSSLimpl::InitAuth() validateOwnCertificate() Failed";
std::cerr << std::endl;
/* bad certificate */
CloseAuth();
exit(1);
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 AuthSSLimpl::validateOwnCertificate(X509 *x509, EVP_PKEY *pkey)
{
(void) pkey; /* remove unused parameter warning */
/* standard authentication */
if (!AuthX509WithGPG(x509))
{
return false;
}
return true;
}
bool AuthSSLimpl::CloseAuth()
{
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::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 *AuthSSLimpl::getCTX()
{
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::getCTX()";
std::cerr << std::endl;
#endif
return sslctx;
}
std::string AuthSSLimpl::OwnId()
{
#ifdef AUTHSSL_DEBUG
// std::cerr << "AuthSSLimpl::OwnId()" << std::endl;
#endif
return mOwnId;
}
std::string AuthSSLimpl::getOwnLocation()
{
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::OwnId()" << std::endl;
#endif
return mOwnCert->location;
}
std::string AuthSSLimpl::SaveOwnCertificateToString()
{
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::SaveOwnCertificateToString() " << std::endl;
#endif
return saveX509ToPEM(mOwnCert->certificate);
}
/********************************************************************************/
/********************************************************************************/
/********************* Cert Search / Add / Remove **************************/
/********************************************************************************/
/********************************************************************************/
bool AuthSSLimpl::SignData(std::string input, std::string &sign)
{
return SignData(input.c_str(), input.length(), sign);
}
bool AuthSSLimpl::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(mOwnPrivateKey);
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, mOwnPrivateKey))
{
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 AuthSSLimpl::SignDataBin(std::string input, unsigned char *sign, unsigned int *signlen)
{
return SignDataBin(input.c_str(), input.length(), sign, signlen);
}
bool AuthSSLimpl::SignDataBin(const void *data, const uint32_t len,
unsigned char *sign, unsigned int *signlen)
{
RsStackMutex stack(sslMtx); /***** STACK LOCK MUTEX *****/
return SSL_SignDataBin(data, len, sign, signlen, mOwnPrivateKey);
}
bool AuthSSLimpl::VerifySignBin(const void *data, const uint32_t len,
unsigned char *sign, unsigned int signlen, SSL_id sslId)
{
/* find certificate.
* if we don't have - fail.
*/
RsStackMutex stack(sslMtx); /***** STACK LOCK MUTEX *****/
/* find the peer */
sslcert *peer;
if (sslId == mOwnId)
{
peer = mOwnCert;
}
else if (!locked_FindCert(sslId, &peer))
{
std::cerr << "VerifySignBin() no peer" << std::endl;
return false;
}
return SSL_VerifySignBin(data, len, sign, signlen, peer->certificate);
}
bool AuthSSLimpl::VerifyOwnSignBin(const void *data, const uint32_t len,
unsigned char *sign, unsigned int signlen)
{
return SSL_VerifySignBin(data, len, sign, signlen, mOwnCert->certificate);
}
/********************************************************************************/
/********************************************************************************/
/********************* Sign and Auth with GPG **************************/
/********************************************************************************/
/********************************************************************************/
/* Note these functions don't need Mutexes -
* only using GPG functions - which lock themselves
*/
X509 *AuthSSLimpl::SignX509ReqWithGPG(X509_REQ *req, long days)
{
/* 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 << "AuthSSLimpl::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 << "AuthSSLimpl::SignX509Req() Issuer name: " << AuthGPG::getAuthGPG()->getGPGOwnId() << std::endl;
BIGNUM *btmp = BN_new();
if (!BN_pseudo_rand(btmp, SERIAL_RAND_BITS, 0, 0))
{
std::cerr << "AuthSSLimpl::SignX509Req() rand FAIL" << std::endl;
return NULL;
}
if (!BN_to_ASN1_INTEGER(btmp, serial))
{
std::cerr << "AuthSSLimpl::SignX509Req() asn1 FAIL" << std::endl;
return NULL;
}
BN_free(btmp);
if (!X509_set_serialNumber(x509, serial))
{
std::cerr << "AuthSSLimpl::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 << "AuthSSLimpl::SignX509Req() issue FAIL" << std::endl;
return NULL;
}
X509_NAME_free(issuer_name);
if (!X509_gmtime_adj(X509_get_notBefore(x509),0))
{
std::cerr << "AuthSSLimpl::SignX509Req() notbefore FAIL" << std::endl;
return NULL;
}
if (!X509_gmtime_adj(X509_get_notAfter(x509), (long)60*60*24*days))
{
std::cerr << "AuthSSLimpl::SignX509Req() notafter FAIL" << std::endl;
return NULL;
}
if (!X509_set_subject_name(x509, X509_REQ_get_subject_name(req)))
{
std::cerr << "AuthSSLimpl::SignX509Req() sub FAIL" << std::endl;
return NULL;
}
tmppkey = X509_REQ_get_pubkey(req);
if (!tmppkey || !X509_set_pubkey(x509,tmppkey))
{
std::cerr << "AuthSSLimpl::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 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, "AuthSSLimpl::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, "AuthSSLimpl::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;
/* XXX CLEANUP */
err:
/* cleanup */
std::cerr << "GPGAuthMgr::SignX509Req() err: FAIL" << std::endl;
return NULL;
}
/* This function, checks that the X509 is signed by a known GPG key,
* NB: we do not have to have approved this person as a friend.
* this is important - as it allows non-friends messages to be validated.
*/
bool AuthSSLimpl::AuthX509WithGPG(X509 *x509)
{
#ifdef AUTHSSL_DEBUG
fprintf(stderr, "AuthSSLimpl::AuthX509WithGPG() called\n");
#endif
if (!CheckX509Certificate(x509))
{
std::cerr << "AuthSSLimpl::AuthX509() X509 NOT authenticated : Certificate failed basic checks" << std::endl;
return false;
}
/* 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 << "AuthSSLimpl::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 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, "AuthSSLimpl::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, "AuthSSLimpl::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 << "AuthSSLimpl::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 << "AuthSSLimpl::AuthX509() X509 authenticated" << std::endl;
#endif
OPENSSL_free(buf_in) ;
OPENSSL_free(buf_hashout) ;
return true;
err:
std::cerr << "AuthSSLimpl::AuthX509() X509 NOT authenticated" << std::endl;
if(buf_in != NULL)
OPENSSL_free(buf_in) ;
if(buf_hashout != NULL)
OPENSSL_free(buf_hashout) ;
return false;
}
/* validate + get id */
bool AuthSSLimpl::ValidateCertificate(X509 *x509, std::string &peerId)
{
/* check self signed */
if (!AuthX509WithGPG(x509))
{
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::ValidateCertificate() bad certificate.";
std::cerr << std::endl;
#endif
return false;
}
if(!getX509id(x509, peerId))
{
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::ValidateCertificate() Cannot retrieve peer id from certificate..";
std::cerr << std::endl;
#endif
return false;
}
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::ValidateCertificate() good certificate.";
std::cerr << std::endl;
#endif
return true;
}
/********************************************************************************/
/********************************************************************************/
/**************************** encrypt / decrypt fns ****************************/
/********************************************************************************/
/********************************************************************************/
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 AuthSSLimpl::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 << "AuthSSLimpl::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 << "AuthSSLimpl::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 (!AuthX509WithGPG(X509_STORE_CTX_get_current_cert(ctx)))
{
#ifdef AUTHSSL_DEBUG
fprintf(stderr, "AuthSSLimpl::VerifyX509Callback() X509 not authenticated.\n");
#endif
return false;
}
std::string pgpid = getX509CNString(X509_STORE_CTX_get_current_cert(ctx)->cert_info->issuer);
if (pgpid != AuthGPG::getAuthGPG()->getGPGOwnId() && !AuthGPG::getAuthGPG()->isGPGAccepted(pgpid))
{
#ifdef AUTHSSL_DEBUG
fprintf(stderr, "AuthSSLimpl::VerifyX509Callback() pgp key not accepted : \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, "A normal certificate is probably a security breach attempt. We sould fail it !!!\n");
#endif
preverify_ok = false;
}
if (preverify_ok) {
//sslcert *cert = NULL;
std::string certId;
getX509id(X509_STORE_CTX_get_current_cert(ctx), certId);
}
#ifdef AUTHSSL_DEBUG
if (preverify_ok) {
fprintf(stderr, "AuthSSLimpl::VerifyX509Callback returned true.\n");
} else {
fprintf(stderr, "AuthSSLimpl::VerifyX509Callback returned false.\n");
}
#endif
return preverify_ok;
}
/********************************************************************************/
/********************************************************************************/
/**************************** encrypt / decrypt fns ****************************/
/********************************************************************************/
/********************************************************************************/
bool AuthSSLimpl::encrypt(void *&out, int &outlen, const void *in, int inlen, std::string peerId)
{
RsStackMutex stack(sslMtx); /******* LOCKED ******/
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::encrypt() called for peerId : " << peerId << " with inlen : " << inlen << std::endl;
#endif
//TODO : use ssl to crypt the binary input buffer
// out = malloc(inlen);
// memcpy(out, in, inlen);
// outlen = inlen;
EVP_PKEY *public_key;
if (peerId == mOwnId) {
public_key = mOwnPublicKey;
} else {
if (!mCerts[peerId]) {
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::encrypt() public key not found." << std::endl;
#endif
return false;
} else {
public_key = mCerts[peerId]->certificate->cert_info->key->pkey;
}
}
EVP_CIPHER_CTX ctx;
int eklen, net_ekl;
unsigned char *ek;
unsigned char iv[EVP_MAX_IV_LENGTH];
EVP_CIPHER_CTX_init(&ctx);
int out_currOffset = 0;
int out_offset = 0;
int max_evp_key_size = EVP_PKEY_size(public_key);
ek = (unsigned char*)malloc(max_evp_key_size);
const EVP_CIPHER *cipher = EVP_aes_128_cbc();
int cipher_block_size = EVP_CIPHER_block_size(cipher);
int size_net_ekl = sizeof(net_ekl);
int max_outlen = inlen + cipher_block_size + EVP_MAX_IV_LENGTH + max_evp_key_size + size_net_ekl;
// intialize context and send store encrypted cipher in ek
if(!EVP_SealInit(&ctx, EVP_aes_128_cbc(), &ek, &eklen, iv, &public_key, 1)) {
free(ek);
return false;
}
// now assign memory to out accounting for data, and cipher block size, key length, and key length val
out = new unsigned char[inlen + cipher_block_size + size_net_ekl + eklen + EVP_MAX_IV_LENGTH];
net_ekl = htonl(eklen);
memcpy((unsigned char*)out + out_offset, &net_ekl, size_net_ekl);
out_offset += size_net_ekl;
memcpy((unsigned char*)out + out_offset, ek, eklen);
out_offset += eklen;
memcpy((unsigned char*)out + out_offset, iv, EVP_MAX_IV_LENGTH);
out_offset += EVP_MAX_IV_LENGTH;
// now encrypt actual data
if(!EVP_SealUpdate(&ctx, (unsigned char*) out + out_offset, &out_currOffset, (unsigned char*) in, inlen)) {
free(ek);
delete[] (unsigned char*) out;
out = NULL;
return false;
}
// move along to partial block space
out_offset += out_currOffset;
// add padding
if(!EVP_SealFinal(&ctx, (unsigned char*) out + out_offset, &out_currOffset)) {
free(ek);
delete[] (unsigned char*) out;
out = NULL;
return false;
}
// move to end
out_offset += out_currOffset;
// make sure offset has not gone passed valid memory bounds
if(out_offset > max_outlen) return false;
// free encrypted key data
free(ek);
EVP_CIPHER_CTX_cleanup(&ctx);
outlen = out_offset;
#ifdef DISTRIB_DEBUG
std::cerr << "Authssl::encrypt() finished with outlen : " << outlen << std::endl;
#endif
return true;
}
bool AuthSSLimpl::decrypt(void *&out, int &outlen, const void *in, int inlen)
{
RsStackMutex stack(sslMtx); /******* LOCKED ******/
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::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;
EVP_CIPHER_CTX ctx;
int eklen = 0, net_ekl = 0;
unsigned char *ek = NULL;
unsigned char iv[EVP_MAX_IV_LENGTH];
int ek_mkl = EVP_PKEY_size(mOwnPrivateKey);
ek = (unsigned char*)malloc(ek_mkl);
EVP_CIPHER_CTX_init(&ctx);
int in_offset = 0, out_currOffset = 0;
int size_net_ekl = sizeof(net_ekl);
if(size_net_ekl > inlen) {
free(ek);
return false;
}
memcpy(&net_ekl, (unsigned char*)in, size_net_ekl);
eklen = ntohl(net_ekl);
in_offset += size_net_ekl;
if(eklen > (inlen-in_offset)) {
free(ek);
return false;
}
memcpy(ek, (unsigned char*)in + in_offset, eklen);
in_offset += eklen;
if(EVP_MAX_IV_LENGTH > (inlen-in_offset)) {
free(ek);
return false;
}
memcpy(iv, (unsigned char*)in + in_offset, EVP_MAX_IV_LENGTH);
in_offset += EVP_MAX_IV_LENGTH;
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
if(0 == EVP_OpenInit(&ctx, cipher, ek, eklen, iv, mOwnPrivateKey)) {
free(ek);
return false;
}
out = new unsigned char[inlen - in_offset];
if(!EVP_OpenUpdate(&ctx, (unsigned char*) out, &out_currOffset, (unsigned char*)in + in_offset, inlen - in_offset)) {
free(ek);
delete[] (unsigned char*) out;
out = NULL;
return false;
}
in_offset += out_currOffset;
outlen += out_currOffset;
if(!EVP_OpenFinal(&ctx, (unsigned char*)out + out_currOffset, &out_currOffset)) {
free(ek);
delete[] (unsigned char*) out;
out = NULL;
return false;
}
outlen += out_currOffset;
if(ek != NULL)
free(ek);
EVP_CIPHER_CTX_cleanup(&ctx);
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::decrypt() finished with outlen : " << outlen << std::endl;
#endif
return true;
}
/********************************************************************************/
/********************************************************************************/
/********************* Cert Search / Add / Remove **************************/
/********************************************************************************/
/********************************************************************************/
/* store for discovery */
bool AuthSSLimpl::FailedCertificate(X509 *x509, bool incoming)
{
(void) incoming; /* remove unused parameter warning */
/* if auths -> store */
if (AuthX509WithGPG(x509))
{
LocalStoreCert(x509);
return true;
}
return false;
}
bool AuthSSLimpl::CheckCertificate(std::string id, X509 *x509)
{
(void) id; /* remove unused parameter warning */
/* if auths -> store */
if (AuthX509WithGPG(x509))
{
LocalStoreCert(x509);
return true;
}
return false;
}
/* Locked search -> internal help function */
bool AuthSSLimpl::locked_FindCert(std::string id, sslcert **cert)
{
std::map<std::string, sslcert *>::iterator it;
if (mCerts.end() != (it = mCerts.find(id)))
{
*cert = it->second;
return true;
}
return false;
}
/* Remove Certificate */
bool AuthSSLimpl::RemoveX509(std::string id)
{
std::map<std::string, sslcert *>::iterator it;
RsStackMutex stack(sslMtx); /******* LOCKED ******/
if (mCerts.end() != (it = mCerts.find(id)))
{
sslcert *cert = it->second;
/* clean up */
X509_free(cert->certificate);
cert->certificate = NULL;
delete cert;
mCerts.erase(it);
return true;
}
return false;
}
bool AuthSSLimpl::LocalStoreCert(X509* x509)
{
//store the certificate in the local cert list
std::string peerId;
if(!getX509id(x509, peerId))
{
std::cerr << "AuthSSLimpl::LocalStoreCert() Cannot retrieve peer id from certificate." << std::endl;
#ifdef AUTHSSL_DEBUG
#endif
return false;
}
RsStackMutex stack(sslMtx); /******* LOCKED ******/
if (peerId == mOwnId)
{
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::LocalStoreCert() not storing own certificate" << std::endl;
#endif
return false;
}
/* do a search */
std::map<std::string, sslcert *>::iterator it;
if (mCerts.end() != (it = mCerts.find(peerId)))
{
sslcert *cert = it->second;
/* found something */
/* check that they are exact */
if (0 != X509_cmp(cert->certificate, x509))
{
/* MAJOR ERROR */
std::cerr << "ERROR : AuthSSLimpl::LocalStoreCert() got two ssl certificates with identical ids -> dropping second";
std::cerr << std::endl;
return false;
}
/* otherwise - we have it already */
return false;
}
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::LocalStoreCert() storing certificate for " << peerId << std::endl;
#endif
mCerts[peerId] = new sslcert(X509_dup(x509), peerId);
/* flag for saving config */
IndicateConfigChanged();
return true ;
}
/********************************************************************************/
/********************************************************************************/
/************************ Config Functions **********************************/
/********************************************************************************/
/********************************************************************************/
RsSerialiser *AuthSSLimpl::setupSerialiser()
{
RsSerialiser *rss = new RsSerialiser();
rss->addSerialType(new RsGeneralConfigSerialiser());
return rss ;
}
bool AuthSSLimpl::saveList(bool& cleanup, std::list<RsItem*>& lst)
{
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::saveList() called" << std::endl ;
#endif
RsStackMutex stack(sslMtx); /******* LOCKED ******/
cleanup = true ;
// Now save config for network digging strategies
RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet ;
std::map<std::string, sslcert*>::iterator mapIt;
for (mapIt = mCerts.begin(); mapIt != mCerts.end(); mapIt++) {
if (mapIt->first == mOwnId) {
continue;
}
RsTlvKeyValue kv;
kv.key = mapIt->first;
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::saveList() called (mapIt->first) : " << (mapIt->first) << std::endl ;
#endif
kv.value = saveX509ToPEM(mapIt->second->certificate);
vitem->tlvkvs.pairs.push_back(kv) ;
}
lst.push_back(vitem);
return true ;
}
bool AuthSSLimpl::loadList(std::list<RsItem*>& load)
{
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::loadList() Item Count: " << load.size() << std::endl;
#endif
/* load the list of accepted gpg keys */
std::list<RsItem *>::iterator it;
for(it = load.begin(); it != load.end(); it++) {
RsConfigKeyValueSet *vitem = dynamic_cast<RsConfigKeyValueSet *>(*it);
if(vitem) {
#ifdef AUTHSSL_DEBUG
std::cerr << "AuthSSLimpl::loadList() General Variable Config Item:" << std::endl;
vitem->print(std::cerr, 10);
std::cerr << std::endl;
#endif
std::list<RsTlvKeyValue>::iterator kit;
for(kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); kit++) {
if (kit->key == mOwnId) {
continue;
}
X509 *peer = loadX509FromPEM(kit->value);
/* authenticate it */
if (AuthX509WithGPG(peer))
{
LocalStoreCert(peer);
}
}
}
delete (*it);
}
return true;
}
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/