RetroShare/libretroshare/src/pqi/authxpgp.cc

1913 lines
40 KiB
C++
Raw Normal View History

/*
* libretroshare/src/pqi: authxpgp.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".
*
*/
#include "authxpgp.h"
#include "pqinetwork.h"
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <sstream>
#include <iomanip>
/******************************** TRUST LVLS
xPGP_vfy.h:#define TRUST_SIGN_OWN 6
xPGP_vfy.h:#define TRUST_SIGN_TRSTED 5
xPGP_vfy.h:#define TRUST_SIGN_AUTHEN 4
xPGP_vfy.h:#define TRUST_SIGN_BASIC 3
xPGP_vfy.h:#define TRUST_SIGN_UNTRUSTED 2
xPGP_vfy.h:#define TRUST_SIGN_UNKNOWN 1
xPGP_vfy.h:#define TRUST_SIGN_NONE 0
xPGP_vfy.h:#define TRUST_SIGN_BAD -1
******************************************************/
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/
/***********
** #define AUTHXPGP_DEBUG 1
**********/
// the single instance of this.
static AuthXPGP instance_sslroot;
p3AuthMgr *getAuthMgr()
{
return &instance_sslroot;
}
xpgpcert::xpgpcert(XPGP *xpgp, std::string pid)
{
certificate = xpgp;
id = pid;
name = getX509CNString(xpgp->subject -> subject);
org = getX509OrgString(xpgp->subject -> subject);
location = getX509LocString(xpgp->subject -> subject);
email = "";
/* These should be filled in afterwards */
fpr = pid;
trustLvl = 0;
ownsign = false;
trusted = false;
}
AuthXPGP::AuthXPGP()
:init(0), sslctx(NULL), pkey(NULL)
{
}
bool AuthXPGP::active()
{
return init;
}
// args: server cert, server private key, trusted certificates.
int AuthXPGP::InitAuth(const char *cert_file, const char *priv_key_file,
const char *passwd)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::InitAuth()";
std::cerr << std::endl;
#endif
static int initLib = 0;
if (!initLib)
{
initLib = 1;
SSL_load_error_strings();
SSL_library_init();
}
if (init == 1)
{
return 1;
}
if ((cert_file == NULL) ||
(priv_key_file == NULL) ||
(passwd == NULL))
{
fprintf(stderr, "sslroot::initssl() missing parameters!\n");
return 0;
}
// XXX TODO
// actions_to_seed_PRNG();
std::cerr << "SSL Library Init!" << std::endl;
// setup connection method
sslctx = SSL_CTX_new(PGPv1_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.
XPGP *xpgp = PEM_read_XPGP(ownfp, NULL, NULL, NULL);
fclose(ownfp);
if (xpgp == NULL)
{
return -1;
}
SSL_CTX_use_pgp_certificate(sslctx, xpgp);
// 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)
{
return -1;
}
SSL_CTX_use_pgp_PrivateKey(sslctx, pkey);
if (1 != SSL_CTX_check_pgp_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;
}
// make keyring.
pgp_keyring = createPGPContext(xpgp, pkey);
SSL_CTX_set_XPGP_KEYRING(sslctx, pgp_keyring);
// Setup the certificate. (after keyring is made!).
if (!XPGP_check_valid_certificate(xpgp))
{
/* bad certificate */
CloseAuth();
return -1;
}
if (!getXPGPid(xpgp, mOwnId))
{
/* bad certificate */
CloseAuth();
return -1;
}
// enable verification of certificates (PEER)
SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
std::cerr << "SSL Verification Set" << std::endl;
mOwnCert = new xpgpcert(xpgp, mOwnId);
/* add to keyring */
XPGP_add_certificate(pgp_keyring, mOwnCert->certificate);
mOwnCert->trustLvl = XPGP_auth_certificate(pgp_keyring, mOwnCert->certificate);
mOwnCert->trusted = true;
mOwnCert->ownsign = true;
init = 1;
return 1;
}
bool AuthXPGP::CloseAuth()
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::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 *AuthXPGP::getCTX()
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::getCTX()";
std::cerr << std::endl;
#endif
return sslctx;
}
int AuthXPGP::setConfigDirectories(const char *cdir, const char *ndir)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::setConfigDirectories()";
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
mCertDir = cdir;
mNeighDir = ndir;
xpgpMtx.unlock(); /**** UNLOCK ****/
return 1;
}
std::string AuthXPGP::OwnId()
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::OwnId()";
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
std::string id = mOwnId;
xpgpMtx.unlock(); /**** UNLOCK ****/
return id;
}
bool AuthXPGP::getAllList(std::list<std::string> &ids)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::getAllList()";
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
/* iterate through both lists */
std::map<std::string, xpgpcert *>::iterator it;
for(it = mCerts.begin(); it != mCerts.end(); it++)
{
ids.push_back(it->first);
}
xpgpMtx.unlock(); /**** UNLOCK ****/
return true;
}
bool AuthXPGP::getAuthenticatedList(std::list<std::string> &ids)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::getAuthenticatedList()";
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
/* iterate through both lists */
std::map<std::string, xpgpcert *>::iterator it;
for(it = mCerts.begin(); it != mCerts.end(); it++)
{
if (it->second->trustLvl > TRUST_SIGN_BASIC)
{
ids.push_back(it->first);
}
}
xpgpMtx.unlock(); /**** UNLOCK ****/
return true;
}
bool AuthXPGP::getUnknownList(std::list<std::string> &ids)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::getUnknownList()";
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
/* iterate through both lists */
std::map<std::string, xpgpcert *>::iterator it;
for(it = mCerts.begin(); it != mCerts.end(); it++)
{
if (it->second->trustLvl <= TRUST_SIGN_BASIC)
{
ids.push_back(it->first);
}
}
xpgpMtx.unlock(); /**** UNLOCK ****/
return true;
}
/* silly question really - only valid certs get saved to map
* so if in map its okay
*/
bool AuthXPGP::isValid(std::string id)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::isValid() " << id;
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
bool valid = (mCerts.end() != mCerts.find(id));
xpgpMtx.unlock(); /**** UNLOCK ****/
return valid;
}
bool AuthXPGP::isAuthenticated(std::string id)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::isAuthenticated() " << id;
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
xpgpcert *cert = NULL;
bool auth = false;
if (locked_FindCert(id, &cert))
{
auth = (cert->trustLvl > TRUST_SIGN_BASIC);
}
xpgpMtx.unlock(); /**** UNLOCK ****/
return auth;
}
std::string AuthXPGP::getName(std::string id)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::getName() " << id;
std::cerr << std::endl;
#endif
std::string name;
xpgpMtx.lock(); /***** LOCK *****/
xpgpcert *cert = NULL;
if (id == mOwnId)
{
name = mOwnCert->name;
}
else if (locked_FindCert(id, &cert))
{
name = cert->name;
}
xpgpMtx.unlock(); /**** UNLOCK ****/
return name;
}
bool AuthXPGP::getDetails(std::string id, pqiAuthDetails &details)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::getDetails() " << id;
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
bool valid = false;
xpgpcert *cert = NULL;
if (id == mOwnId)
{
cert = mOwnCert;
valid = true;
}
else if (locked_FindCert(id, &cert))
{
valid = true;
}
if (valid)
{
/* fill details */
details.id = cert->id;
details.name = cert->name;
details.email = cert->email;
details.location= cert->location;
details.org = cert->org;
details.fpr = cert->fpr;
details.signers = cert->signers;
details.trustLvl= cert->trustLvl;
details.ownsign = cert->ownsign;
details.trusted = cert->trusted;
}
xpgpMtx.unlock(); /**** UNLOCK ****/
return valid;
}
/* Load/Save certificates */
bool AuthXPGP::LoadCertificateFromString(std::string pem, std::string &id)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::LoadCertificateFromString() " << id;
std::cerr << std::endl;
#endif
XPGP *xpgp = loadXPGPFromPEM(pem);
if (!xpgp)
return false;
return ProcessXPGP(xpgp, id);
}
std::string AuthXPGP::SaveCertificateToString(std::string id)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::SaveCertificateToString() " << id;
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
/* get the cert first */
std::string certstr;
xpgpcert *cert = NULL;
bool valid = false;
if (id == mOwnId)
{
cert = mOwnCert;
valid = true;
}
else if (locked_FindCert(id, &cert))
{
valid = true;
}
if (valid)
{
BIO *bp = BIO_new(BIO_s_mem());
PEM_write_bio_XPGP(bp, cert->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);
}
xpgpMtx.unlock(); /**** UNLOCK ****/
return certstr;
}
bool AuthXPGP::LoadCertificateFromFile(std::string filename, std::string &id)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::LoadCertificateFromFile() " << id;
std::cerr << std::endl;
#endif
std::string nullhash;
XPGP *xpgp = loadXPGPFromFile(filename.c_str(), nullhash);
if (!xpgp)
return false;
return ProcessXPGP(xpgp, id);
}
bool AuthXPGP::SaveCertificateToFile(std::string id, std::string filename)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::SaveCertificateToFile() " << id;
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
/* get the cert first */
xpgpcert *cert = NULL;
bool valid = false;
std::string hash;
if (id == mOwnId)
{
cert = mOwnCert;
valid = true;
}
else if (locked_FindCert(id, &cert))
{
valid = true;
}
if (valid)
{
valid = saveXPGPToFile(cert->certificate, filename, hash);
}
xpgpMtx.unlock(); /**** UNLOCK ****/
return valid;
}
/**** To/From DER format ***/
bool AuthXPGP::LoadCertificateFromBinary(const uint8_t *ptr, uint32_t len, std::string &id)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::LoadCertificateFromFile() " << id;
std::cerr << std::endl;
#endif
XPGP *xpgp = loadXPGPFromDER(ptr, len);
if (!xpgp)
return false;
return ProcessXPGP(xpgp, id);
}
bool AuthXPGP::SaveCertificateToBinary(std::string id, uint8_t **ptr, uint32_t *len)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::SaveCertificateToBinary() " << id;
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
/* get the cert first */
xpgpcert *cert = NULL;
bool valid = false;
std::string hash;
if (id == mOwnId)
{
cert = mOwnCert;
valid = true;
}
else if (locked_FindCert(id, &cert))
{
valid = true;
}
if (valid)
{
valid = saveXPGPToDER(cert->certificate, ptr, len);
}
xpgpMtx.unlock(); /**** UNLOCK ****/
return valid;
}
/* Signatures */
bool AuthXPGP::SignCertificate(std::string id)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::SignCertificate() " << id;
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
/* get the cert first */
xpgpcert *cert = NULL;
xpgpcert *own = mOwnCert;
bool valid = false;
if (locked_FindCert(id, &cert))
{
XPGP_sign_certificate(pgp_keyring, cert->certificate, own->certificate);
/* reevaluate the auth of the xpgp */
cert->trustLvl = XPGP_auth_certificate(pgp_keyring, cert->certificate);
cert->ownsign = true;
valid = true;
}
xpgpMtx.unlock(); /**** UNLOCK ****/
return valid;
}
bool AuthXPGP::TrustCertificate(std::string id, bool totrust)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::TrustCertificate() " << id;
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
/* get the cert first */
xpgpcert *cert = NULL;
bool valid = false;
if (locked_FindCert(id, &cert))
{
/* if trusted -> untrust */
if (!totrust)
{
XPGP_signer_untrusted(pgp_keyring, cert->certificate);
cert->trusted = false;
}
else
{
/* if auth then we can trust them */
if (XPGP_signer_trusted(pgp_keyring, cert->certificate))
{
cert->trusted = true;
}
}
/* reevaluate the auth of the xpgp */
cert->trustLvl = XPGP_auth_certificate(pgp_keyring, cert->certificate);
valid = true;
}
xpgpMtx.unlock(); /**** UNLOCK ****/
return valid;
}
bool AuthXPGP::RevokeCertificate(std::string id)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::RevokeCertificate() " << id;
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
xpgpMtx.unlock(); /**** UNLOCK ****/
return false;
}
bool AuthXPGP::AuthCertificate(std::string id)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::AuthCertificate() " << id;
std::cerr << std::endl;
#endif
xpgpMtx.lock(); /***** LOCK *****/
/* get the cert first */
xpgpcert *cert = NULL;
xpgpcert *own = mOwnCert;
bool valid = false;
if (locked_FindCert(id, &cert))
{
/* ADD IN LATER */
//if (cert->trustLvl > TRUST_SIGN_BASIC)
//{
#ifdef AUTHXPGP_DEBUG
// std::cerr << "AuthXPGP::AuthCertificate() Already Authed: " << id;
// std::cerr << std::endl;
#endif
//}
if (0 < validateCertificateIsSignedByKey(
cert->certificate, own->certificate))
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::AuthCertificate() Signed Already: " << id;
std::cerr << std::endl;
#endif
cert->ownsign=true;
}
else
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::AuthCertificate() Signing Cert: " << id;
std::cerr << std::endl;
#endif
/* sign certificate */
XPGP_sign_certificate(pgp_keyring, cert->certificate, own->certificate);
/* reevaluate the auth of the xpgp */
cert->trustLvl = XPGP_auth_certificate(pgp_keyring, cert->certificate);
cert->ownsign = true;
}
valid = true;
}
xpgpMtx.unlock(); /**** UNLOCK ****/
return valid;
}
/* Sign / Encrypt / Verify Data (TODO) */
/**** NEW functions we've added ****/
/**** AUX Functions ****/
bool AuthXPGP::locked_FindCert(std::string id, xpgpcert **cert)
{
std::map<std::string, xpgpcert *>::iterator it;
if (mCerts.end() != (it = mCerts.find(id)))
{
*cert = it->second;
return true;
}
return false;
}
XPGP *AuthXPGP::loadXPGPFromFile(std::string fname, std::string hash)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::LoadXPGPFromFile()";
std::cerr << std::endl;
#endif
// if there is a hash - check that the file matches it before loading.
XPGP *pc = NULL;
FILE *pcertfp = fopen(fname.c_str(), "rb");
// load certificates from file.
if (pcertfp == NULL)
{
std::cerr << "sslroot::loadcertificate() Bad File: " << fname;
std::cerr << " Cannot be Hashed!" << std::endl;
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. (XPGP 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)))
{
std::cerr << "Error Reading Peer Record!" << std::endl;
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())
{
std::cerr << "Different Length Signatures... ";
std::cerr << "Cannot Load Certificate!" << std::endl;
fclose(pcertfp);
return NULL;
}
for(int i = 0; i < (signed) signlen; i++)
{
if (signature[i] != (unsigned char) hash[i])
{
same = false;
std::cerr << "Invalid Signature... ";
std::cerr << "Cannot Load Certificate!" << std::endl;
fclose(pcertfp);
return NULL;
}
}
std::cerr << "Verified Signature for: " << fname;
std::cerr << std::endl;
}
else
{
std::cerr << "Not checking cert signature" << std::endl;
}
fseek(pcertfp, 0, SEEK_SET); /* rewind */
pc = PEM_read_XPGP(pcertfp, NULL, NULL, NULL);
fclose(pcertfp);
if (pc != NULL)
{
// read a certificate.
std::cerr << "Loaded Certificate: " << pc -> name << std::endl;
}
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 AuthXPGP::saveXPGPToFile(XPGP *xpgp, std::string fname, std::string &hash)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::saveXPGPToFile()";
std::cerr << std::endl;
#endif
// load certificates from file.
FILE *setfp = fopen(fname.c_str(), "wb");
if (setfp == NULL)
{
std::cerr << "sslroot::savecertificate() Bad File: " << fname;
std::cerr << " Cannot be Written!" << std::endl;
return false;
}
std::cerr << "Writing out Cert...:" << xpgp->name << std::endl;
PEM_write_XPGP(setfp, xpgp);
fclose(setfp);
// then reopen to generate hash.
setfp = fopen(fname.c_str(), "rb");
if (setfp == NULL)
{
std::cerr << "sslroot::savecertificate() Bad File: " << fname;
std::cerr << " Opened for ReHash!" << std::endl;
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)))
{
std::cerr << "Error Writing Peer Record!" << std::endl;
return -1;
}
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;
}
std::cerr << "Saved Cert: " << xpgp->name;
std::cerr << std::endl;
std::cerr << "Cert + Setting Signature is(" << signlen << "): ";
std::string signstr;
for(uint32_t i = 0; i < signlen; i++)
{
fprintf(stderr, "%02x", signature[i]);
signstr += signature[i];
}
std::cerr << std::endl;
hash = signstr;
fclose(setfp);
EVP_MD_CTX_destroy(mdctx);
return true;
}
XPGP *AuthXPGP::loadXPGPFromPEM(std::string pem)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::LoadXPGPFromPEM()";
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);
XPGP *pc = PEM_read_bio_XPGP(bp, NULL, NULL, NULL);
BIO_free(bp);
free(certstr);
return pc;
}
XPGP *AuthXPGP::loadXPGPFromDER(const uint8_t *ptr, uint32_t len)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::LoadXPGPFromDER()";
std::cerr << std::endl;
#endif
XPGP *tmp = NULL;
unsigned char **certptr = (unsigned char **) &ptr;
XPGP *xpgp = d2i_XPGP(&tmp, certptr, len);
return xpgp;
}
bool AuthXPGP::saveXPGPToDER(XPGP *xpgp, uint8_t **ptr, uint32_t *len)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::saveXPGPToDER()";
std::cerr << std::endl;
#endif
int certlen = i2d_XPGP(xpgp, (unsigned char **) ptr);
if (certlen > 0)
{
*len = certlen;
return true;
}
else
{
*len = 0;
return false;
}
return false;
}
bool AuthXPGP::ProcessXPGP(XPGP *xpgp, std::string &id)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::ProcessXPGP()";
std::cerr << std::endl;
#endif
/* extract id */
std::string xpgpid;
if (!XPGP_check_valid_certificate(xpgp))
{
/* bad certificate */
XPGP_free(xpgp);
return false;
}
if (!getXPGPid(xpgp, xpgpid))
{
/* bad certificate */
XPGP_free(xpgp);
return false;
}
xpgpcert *cert = NULL;
bool duplicate = false;
xpgpMtx.lock(); /***** LOCK *****/
if (xpgpid == mOwnId)
{
cert = mOwnCert;
duplicate = true;
}
else if (locked_FindCert(xpgpid, &cert))
{
duplicate = true;
}
if (duplicate)
{
/* have a duplicate */
/* check that they are exact */
if (0 != XPGP_cmp(cert->certificate, xpgp))
{
/* MAJOR ERROR */
XPGP_free(xpgp);
xpgpMtx.unlock(); /**** UNLOCK ****/
return false;
}
/* transfer new signatures */
XPGP_copy_known_signatures(pgp_keyring, cert->certificate, xpgp);
XPGP_free(xpgp);
/* we accepted it! */
id = xpgpid;
/* update signers */
cert->signers = getXPGPsigners(cert->certificate);
xpgpMtx.unlock(); /**** UNLOCK ****/
return true;
}
xpgpMtx.unlock(); /**** UNLOCK ****/
/* if we get here -> its a new certificate */
cert = new xpgpcert(xpgp, xpgpid);
xpgpMtx.lock(); /***** LOCK *****/
/* add to keyring */
XPGP_add_certificate(pgp_keyring, cert->certificate);
mCerts[xpgpid] = cert;
cert -> trustLvl = XPGP_auth_certificate(pgp_keyring, cert->certificate);
if (cert -> trustLvl == TRUST_SIGN_TRSTED)
{
cert->trusted = true;
cert->ownsign = true;
}
else if (cert->trustLvl == TRUST_SIGN_OWN)
{
cert->ownsign = true;
}
cert->signers = getXPGPsigners(xpgp);
xpgpMtx.unlock(); /**** UNLOCK ****/
id = xpgpid;
return true;
}
bool AuthXPGP::getXPGPid(XPGP *xpgp, std::string &xpgpid)
{
#ifdef AUTHXPGP_DEBUG
std::cerr << "AuthXPGP::getXPGPid()";
std::cerr << std::endl;
#endif
xpgpid = "";
if (xpgp == NULL)
{
#ifdef XPGP_DEBUG
std::cerr << "AuthXPGP::getXPGPid() NULL pointer";
std::cerr << std::endl;
#endif
return false;
}
// get the first signature....
if (sk_XPGP_SIGNATURE_num(xpgp->signs) < 1)
{
#ifdef XPGP_DEBUG
std::cerr << "AuthXPGP::getXPGPid() ERROR: No Signature";
std::cerr << std::endl;
#endif
return false;
}
XPGP_SIGNATURE *xpgpsign = sk_XPGP_SIGNATURE_value(xpgp->signs, 0);
// Validate that it is a self signature.
// (Already Done - but not in this function)
// get the signature from the cert, and copy to the array.
ASN1_BIT_STRING *signature = xpgpsign->signature;
int signlen = ASN1_STRING_length(signature);
if (signlen < CERTSIGNLEN)
{
#ifdef XPGP_DEBUG
std::cerr << "AuthXPGP::getXPGPid() 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;
for(uint32_t i = 0; i < CERTSIGNLEN; i++)
{
id << std::hex << std::setw(2) << std::setfill('0')
<< (uint16_t) (((uint8_t *) (signdata))[i]);
}
xpgpid = id.str();
return true;
}
/* validate + get id */
bool AuthXPGP::ValidateCertificateXPGP(XPGP *xpgp, std::string &peerId)
{
/* check self signed */
if (!XPGP_check_valid_certificate(xpgp))
{
/* bad certificate */
return false;
}
return getXPGPid(xpgp, peerId);
}
/* store for discovery */
bool AuthXPGP::FailedCertificateXPGP(XPGP *xpgp, bool incoming)
{
std::string id;
return ProcessXPGP(xpgp, id);
}
/* check that they are exact match */
bool AuthXPGP::CheckCertificateXPGP(std::string xpgpId, XPGP *xpgp)
{
xpgpMtx.lock(); /***** LOCK *****/
xpgpcert *cert = NULL;
if (!locked_FindCert(xpgpId, &cert))
{
/* not there -> error */
XPGP_free(xpgp);
xpgpMtx.unlock(); /**** UNLOCK ****/
return false;
}
else
{
/* have a duplicate */
/* check that they are exact */
if (0 != XPGP_cmp(cert->certificate, xpgp))
{
/* MAJOR ERROR */
XPGP_free(xpgp);
xpgpMtx.unlock(); /**** UNLOCK ****/
return false;
}
/* transfer new signatures */
XPGP_copy_known_signatures(pgp_keyring, cert->certificate, xpgp);
XPGP_free(xpgp);
/* update signers */
cert->signers = getXPGPsigners(cert->certificate);
xpgpMtx.unlock(); /**** UNLOCK ****/
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));
}
// Not dependent on sslroot. load, and detroys the XPGP memory.
int LoadCheckXPGPandGetName(const char *cert_file, std::string &userName)
{
/* This function loads the XPGP certificate from the file,
* and checks the certificate
*/
FILE *tmpfp = fopen(cert_file, "r");
if (tmpfp == NULL)
{
std::cerr << "sslroot::LoadCheckAndGetXPGPName()";
std::cerr << " Failed to open Certificate File:" << cert_file;
std::cerr << std::endl;
return 0;
}
// get xPGP certificate.
XPGP *xpgp = PEM_read_XPGP(tmpfp, NULL, NULL, NULL);
fclose(tmpfp);
// check the certificate.
bool valid = false;
if (xpgp)
{
valid = XPGP_check_valid_certificate(xpgp);
}
if (valid)
{
// extract the name.
userName = getX509CNString(xpgp->subject->subject);
}
// clean up.
XPGP_free(xpgp);
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 getXPGPInfo(XPGP *cert)
{
std::stringstream out;
long l;
int i,j;
out << "XPGP Certificate:" << std::endl;
l=XPGP_get_version(cert);
out << " Version: " << l+1 << "(0x" << l << ")" << std::endl;
out << " Subject: " << std::endl;
out << " " << getX509NameString(cert -> subject -> subject);
out << std::endl;
out << std::endl;
out << " Signatures:" << std::endl;
for(i = 0; i < sk_XPGP_SIGNATURE_num(cert->signs); i++)
{
out << "Sign[" << i << "] -> [";
XPGP_SIGNATURE *sig = sk_XPGP_SIGNATURE_value(cert->signs,i);
ASN1_BIT_STRING *signature = sig->signature;
int signlen = ASN1_STRING_length(signature);
unsigned char *signdata = ASN1_STRING_data(signature);
/* only show the first 8 bytes */
if (signlen > 8)
signlen = 8;
for(j=0;j<signlen;j++)
{
out << std::hex << std::setw(2) << (int) (signdata[j]);
if ((j+1)%16==0)
{
out << std::endl;
}
else
{
out << ":";
}
}
out << "] by:";
out << std::endl;
out << getX509NameString(sig->issuer);
out << std::endl;
out << std::endl;
}
return out.str();
}
std::string getXPGPAuthCode(XPGP *xpgp)
{
/* get the self signature -> the first signature */
std::stringstream out;
if (1 > sk_XPGP_SIGNATURE_num(xpgp->signs))
{
out.str();
}
XPGP_SIGNATURE *sig = sk_XPGP_SIGNATURE_value(xpgp->signs,0);
ASN1_BIT_STRING *signature = sig->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;j<signlen;j++)
{
out << std::hex << std::setprecision(2) << std::setw(2)
<< std::setfill('0') << (unsigned int) (signdata[j]);
}
return out.str();
}
std::list<std::string> getXPGPsigners(XPGP *cert)
{
std::list<std::string> signers;
int i;
for(i = 0; i < sk_XPGP_SIGNATURE_num(cert->signs); i++)
{
XPGP_SIGNATURE *sig = sk_XPGP_SIGNATURE_value(cert->signs,i);
std::string str = getX509CNString(sig->issuer);
signers.push_back(str);
std::cerr << "XPGPsigners(" << i << ")" << str << std::endl;
}
return signers;
}
/********** 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;
}
#if 0
int AuthXPGP::saveCertificates(const char *fname)
{
// construct file name.
//
// create the file in memory - hash + sign.
// write out data to a file.
std::string neighdir = certdir + "/" + neighbourdir + "/";
std::string configname = certdir + "/";
configname += fname;
std::map<std::string, std::string>::iterator mit;
std::string conftxt;
std::string empty("");
unsigned int i;
std::list<cert *>::iterator it;
for(it = peercerts.begin(); it != peercerts.end(); it++)
{
std::string neighfile = neighdir + getCertName(*it) + ".pqi";
savecertificate((*it), neighfile.c_str());
conftxt += "CERT ";
conftxt += getCertName(*it);
conftxt += "\n";
conftxt += (*it) -> Hash();
conftxt += "\n";
std::cerr << std::endl;
}
// now work out signature of it all. This relies on the
// EVP library of openSSL..... We are going to use signing
// for the moment.
unsigned int signlen = EVP_PKEY_size(pkey);
unsigned char signature[signlen];
//OpenSSL_add_all_digests();
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, conftxt.c_str(), conftxt.length()))
{
std::cerr << "EVP_SignUpdate Failure!" << std::endl;
}
if (0 == EVP_SignFinal(mdctx, signature, &signlen, pkey))
{
std::cerr << "EVP_SignFinal Failure!" << std::endl;
}
std::cerr << "Conf Signature is(" << signlen << "): ";
for(i = 0; i < signlen; i++)
{
fprintf(stderr, "%02x", signature[i]);
conftxt += signature[i];
}
std::cerr << std::endl;
FILE *cfd = fopen(configname.c_str(), "wb");
int wrec;
if (1 != (wrec = fwrite(conftxt.c_str(), conftxt.length(), 1, cfd)))
{
std::cerr << "Error writing: " << configname << std::endl;
std::cerr << "Wrote: " << wrec << "/" << 1 << " Records" << std::endl;
}
EVP_MD_CTX_destroy(mdctx);
fclose(cfd);
return 1;
}
int sslroot::loadCertificates(const char *conf_fname)
{
// open the configuration file.
//
// read in CERT + Hash.
// construct file name.
//
// create the file in memory - hash + sign.
// write out data to a file.
std::string neighdir = certdir + "/" + neighbourdir + "/";
std::string configname = certdir + "/";
configname += conf_fname;
// save name for later save attempts.
certfile = conf_fname;
std::string conftxt;
unsigned int maxnamesize = 1024;
char name[maxnamesize];
int c;
unsigned int i;
FILE *cfd = fopen(configname.c_str(), "rb");
if (cfd == NULL)
{
std::cerr << "Unable to Load Configuration File!" << std::endl;
std::cerr << "File: " << configname << std::endl;
return -1;
}
std::list<std::string> fnames;
std::list<std::string> hashes;
std::map<std::string, std::string>::iterator mit;
std::map<std::string, std::string> tmpsettings;
unsigned int signlen = EVP_PKEY_size(pkey);
unsigned char conf_signature[signlen];
char *ret = NULL;
for(ret = fgets(name, maxnamesize, cfd);
((ret != NULL) && (!strncmp(name, "CERT ", 5)));
ret = fgets(name, maxnamesize, cfd))
{
for(i = 5; (name[i] != '\n') && (i < (unsigned) maxnamesize); i++);
if (name[i] == '\n')
{
name[i] = '\0';
}
// so the name is first....
std::string fname = &(name[5]);
// now read the
std::string hash;
std::string signature;
for(i = 0; i < signlen; i++)
{
if (EOF == (c = fgetc(cfd)))
{
std::cerr << "Error Reading Signature of: ";
std::cerr << fname;
std::cerr << std::endl;
std::cerr << "ABorting Load!";
std::cerr << std::endl;
return -1;
}
unsigned char uc = (unsigned char) c;
signature += (unsigned char) uc;
}
if ('\n' != (c = fgetc(cfd)))
{
std::cerr << "Warning Mising seperator" << std::endl;
}
std::cerr << "Read fname:" << fname << std::endl;
std::cerr << "Signature:" << std::endl;
for(i = 0; i < signlen; i++)
{
fprintf(stderr, "%02x", (unsigned char) signature[i]);
}
std::cerr << std::endl;
std::cerr << std::endl;
// push back.....
fnames.push_back(fname);
hashes.push_back(signature);
conftxt += "CERT ";
conftxt += fname;
conftxt += "\n";
conftxt += signature;
conftxt += "\n";
// be sure to write over a bit...
name[0] = 'N';
name[1] = 'O';
}
// string already waiting!
for(; ((ret != NULL) && (!strncmp(name, "OPT ", 4)));
ret = fgets(name, maxnamesize, cfd))
{
for(i = 4; (name[i] != '\n') && (i < OPT_LEN); i++);
// terminate the string.
name[i] = '\0';
// so the name is first....
std::string opt = &(name[4]);
// now read the
std::string val; // cleaned up value.
std::string valsign; // value in the file.
for(i = 0; i < VAL_LEN; i++)
{
if (EOF == (c = fgetc(cfd)))
{
std::cerr << "Error Reading Value of: ";
std::cerr << opt;
std::cerr << std::endl;
std::cerr << "ABorting Load!";
std::cerr << std::endl;
return -1;
}
// remove zeros on strings...
if (c != '\0')
{
val += (unsigned char) c;
}
valsign += (unsigned char) c;
}
if ('\n' != (c = fgetc(cfd)))
{
std::cerr << "Warning Mising seperator" << std::endl;
}
std::cerr << "Read OPT:" << opt;
std::cerr << " Val:" << val << std::endl;
// push back.....
tmpsettings[opt] = val;
conftxt += "OPT ";
conftxt += opt;
conftxt += "\n";
conftxt += valsign;
conftxt += "\n";
// be sure to write over a bit...
name[0] = 'N';
name[1] = 'O';
}
// only read up to the first newline symbol....
// continue...
for(i = 0; (name[i] != '\n') && (i < signlen); i++);
//printf("Stepping over [%d] %0x\n", i, name[i]);
if (i != signlen)
{
for(i++; i < signlen; i++)
{
c = fgetc(cfd);
if (c == EOF)
{
std::cerr << "Error Reading Conf Signature:";
std::cerr << std::endl;
return 1;
}
unsigned char uc = (unsigned char) c;
name[i] = uc;
}
}
std::cerr << "Configuration File Signature: " << std::endl;
for(i = 0; i < signlen; i++)
{
fprintf(stderr, "%02x", (unsigned char) name[i]);
}
std::cerr << std::endl;
// when we get here - should have the final signature in the buffer.
// check.
//
// compare signatures.
// instead of verifying with the public key....
// we'll sign it again - and compare .... FIX LATER...
EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
if (0 == EVP_SignInit(mdctx, EVP_sha1()))
{
std::cerr << "EVP_SignInit Failure!" << std::endl;
}
if (0 == EVP_SignUpdate(mdctx, conftxt.c_str(), conftxt.length()))
{
std::cerr << "EVP_SignUpdate Failure!" << std::endl;
}
if (0 == EVP_SignFinal(mdctx, conf_signature, &signlen, pkey))
{
std::cerr << "EVP_SignFinal Failure!" << std::endl;
}
EVP_MD_CTX_destroy(mdctx);
fclose(cfd);
std::cerr << "Recalced File Signature: " << std::endl;
for(i = 0; i < signlen; i++)
{
fprintf(stderr, "%02x", conf_signature[i]);
}
std::cerr << std::endl;
bool same = true;
for(i = 0; i < signlen; i++)
{
if ((unsigned char) name[i] != conf_signature[i])
{
same = false;
}
}
if (same == false)
{
std::cerr << "ERROR VALIDATING CONFIGURATION!" << std::endl;
std::cerr << "PLEASE FIX!" << std::endl;
return -1;
}
std::list<std::string>::iterator it;
std::list<std::string>::iterator it2;
for(it = fnames.begin(), it2 = hashes.begin(); it != fnames.end(); it++, it2++)
{
std::string neighfile = neighdir + (*it) + ".pqi";
cert *nc = loadcertificate(neighfile.c_str(), (*it2));
if (nc != NULL)
{
if (0 > addCertificate(nc))
{
// cleanup.
std::cerr << "Updated Certificate....but no";
std::cerr << " need for addition";
std::cerr << std::endl;
// X509_free(nc -> certificate);
//delete nc;
}
}
}
for(mit = tmpsettings.begin(); mit != tmpsettings.end(); mit++)
{
settings[mit -> first] = mit -> second;
}
return 1;
}
#endif