RetroShare/libretroshare/src/pqi/xpgpcert.cc
drbob 6435357c07 Commit of the new UDP Connection methods and
the rewrite of the retroshare core networking stack.

This check-in commits the changes to the pqi code, 
and provides the majority of the improvements.

(1) Introduced new interfaces for DHT and UPnP abstraction: p3dhtmgr.h, p3upnpmgr.h
(2) Introduces abstraction for Authentication: p3authmgr.h
(3) New Connection Manager to coordinate networking code: p3connmgr.h
(4) New Configuration Manager: p3cfgmgr.h

This library has been significantly modified to support DHT synced connections
to enable connections between Firewalled Friends. The connection code has
been rewritten within a new framework, which should make extensions easier to code.

Also removed significant amount of old code relating to Tunnels, Channels etc.
This will be recycled later as higher level services that should not be
part of the core networking library.



git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@306 b45a01b8-16f6-495d-af2f-9b41ad6348cc
2008-01-25 06:36:40 +00:00

2570 lines
56 KiB
C++

/*
* "$Id: xpgpcert.cc,v 1.18 2007-04-15 18:45:18 rmf24 Exp $"
*
* 3P/PQI network interface for RetroShare.
*
* Copyright 2004-2006 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 "xpgpcert.h"
#include "pqinetwork.h"
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <sstream>
#include <iomanip>
#include "pqidebug.h"
const int pqisslrootzone = 1211;
/** XPGP keyring interface ************
int XPGP_add_certificate(XPGP_KEYRING *kr, XPGP *x);
int XPGP_auth_certficate(XPGP_KEYRING *kr, XPGP *x);
int XPGP_sign_certificate(XPGP_KEYRING *kr, XPGP *subj, XPGP *issuer);
int XPGP_check_valid_certificate(XPGP *x);
int XPGP_signer_trusted(XPGP_KEYRING *kr, XPGP *trusted);
int XPGP_signer_untrusted(XPGP_KEYRING *kr, XPGP *untrusted);
int XPGP_copy_known_signatures(XPGP_KEYRING *kr, XPGP *dest, XPGP *src);
*
*
*/
unsigned char convertHexToChar(unsigned char a, unsigned char b);
// other fns
std::string getCertName(cert *c)
{
std::string name = c -> certificate -> name;
// 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;
}
int pem_passwd_cb(char *buf, int size, int rwflag, void *password)
{
strncpy(buf, (char *)(password), size);
buf[size - 1] = '\0';
return(strlen(buf));
}
/* This class handles openssl library init/destruct.
* only one of these... handles
* the CTX and setup?
*
* it will also handle the certificates.....
* mantaining a library of recieved certs,
* and ip addresses that the connections come from...
*
*/
// the single instance of this.
static sslroot instance_sslroot;
sslroot *getSSLRoot()
{
return &instance_sslroot;
}
sslroot::sslroot()
:sslctx(NULL), init(0), certsChanged(1),
certsMajorChanged(1), pkey(NULL)
{
}
int sslroot::active()
{
return init;
}
// args: server cert, server private key, trusted certificates.
int sslroot::initssl(const char *cert_file, const char *priv_key_file,
const char *passwd)
{
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;
closessl();
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;
closessl();
return -1;
}
// make keyring.
pgp_keyring = createPGPContext(xpgp, pkey);
SSL_CTX_set_XPGP_KEYRING(sslctx, pgp_keyring);
// Setup the certificate. (after keyring is made!).
own_cert = makeCertificateXPGP(xpgp);
if (own_cert == NULL)
{
std::cerr << "Failed to Make Own Cert!" << std::endl;
return -1;
}
addCertificate(own_cert);
// 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;
/* configure basics on the certificate. */
own_cert -> Name(getX509CNString(own_cert -> certificate -> subject -> subject));
init = 1;
return 1;
}
int sslroot::closessl()
{
SSL_CTX_free(sslctx);
// clean up private key....
// remove certificates etc -> opposite of initssl.
init = 0;
return 1;
}
/* Context handling */
SSL_CTX *sslroot::getCTX()
{
return sslctx;
}
int sslroot::setConfigDirs(const char *cdir, const char *ndir)
{
certdir = cdir;
neighbourdir = ndir;
return 1;
}
static const unsigned int OPT_LEN = 16;
static const unsigned int VAL_LEN = 1000;
int sslroot::saveCertificates()
{
if (certfile.length() > 1)
return saveCertificates(certfile.c_str());
return -1;
}
int sslroot::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 add the options.
for(mit = settings.begin(); mit != settings.end(); mit++)
{
// only save the nonempty settings.
if (mit -> second != empty) {
conftxt += "OPT ";
for(i = 0; (i < OPT_LEN) && (i < mit -> first.length()); i++)
{
conftxt += mit -> first[i];
}
conftxt += "\n";
for(i = 0; i < VAL_LEN; i++)
{
if (i < mit -> second.length())
{
conftxt += mit -> second[i];
}
else
{
conftxt += '\0';
}
}
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;
}
const int PQI_SSLROOT_CERT_CONFIG_SIZE = 1024;
int sslroot::savecertificate(cert *c, const char *fname)
{
// load certificates from file.
FILE *setfp = fopen(fname, "wb");
if (setfp == NULL)
{
std::cerr << "sslroot::savecertificate() Bad File: " << fname;
std::cerr << " Cannot be Written!" << std::endl;
return -1;
}
std::cerr << "Writing out Cert...:" << c -> Name() << std::endl;
PEM_write_XPGP(setfp, c -> certificate);
// writing out details....
// read in a line.....
int size = PQI_SSLROOT_CERT_CONFIG_SIZE;
char line[size];
std::list<cert *>::iterator it;
int i;
// This will need to be made portable.....
struct sockaddr_in *addr_inet;
struct sockaddr_in *addr_inet2;
struct sockaddr_in *addr_inet3;
int pos_status = 0;
int pos_addr = sizeof(int);
int pos_addr2 = pos_addr + sizeof(*addr_inet);
int pos_addr3 = pos_addr2 + sizeof(*addr_inet2);
int pos_lcts = pos_addr3 + sizeof(*addr_inet3);
int pos_lrts = pos_lcts + sizeof(int);
int pos_ncts = pos_lrts + sizeof(int);
int pos_ncvl = pos_ncts + sizeof(int);
int pos_name = pos_ncvl + sizeof(int);
int pos_end = pos_name + 20; // \n. for readability.
int *status = (int *) &(line[pos_status]);
addr_inet = (struct sockaddr_in *) &(line[pos_addr]);
addr_inet2 = (struct sockaddr_in *) &(line[pos_addr2]);
addr_inet3 = (struct sockaddr_in *) &(line[pos_addr3]);
int *lcts = (int *) &(line[pos_lcts]);
int *lrts = (int *) &(line[pos_lrts]);
int *ncts = (int *) &(line[pos_ncts]);
int *ncvl = (int *) &(line[pos_ncvl]);
char *name = &(line[pos_name]);
char *end = &(line[pos_end]);
for(i = 0; i < 1024; i++)
line[i] = 0;
*status = c -> Status();
*addr_inet = c -> lastaddr;
*addr_inet2 = c -> localaddr;
*addr_inet3 = c -> serveraddr;
*lcts = c -> lc_timestamp;
*lrts = c -> lr_timestamp;
*ncts = c -> nc_timestamp;
*ncvl = c -> nc_timeintvl;
std::string tmpname = c -> Name();
for(i = 0; (i < (signed) tmpname.length()) && (i < 20 - 1); i++)
{
name[i] = tmpname[i];
}
name[20 - 1] = '\0';
end[0] = '\n';
/* now convert it to hex */
char config_hex[2 * size];
for(i = 0; i < size; i++)
{
sprintf(&(config_hex[i * 2]), "%02x",
(unsigned int) ((unsigned char *) line)[i]);
}
if (1 != fwrite(config_hex, size * 2,1, setfp))
{
std::cerr << "Error Writing Peer Record!" << std::endl;
return -1;
}
fclose(setfp);
// then reopen to generate hash.
setfp = fopen(fname, "rb");
if (setfp == NULL)
{
std::cerr << "sslroot::savecertificate() Bad File: " << fname;
std::cerr << " Opened for ReHash!" << std::endl;
return -1;
}
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;
// so we read rbytes.
// hash.
//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, 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: " << c -> Name() << " Status: ";
std::cerr << std::hex << (unsigned int) c->Status() << std::dec << std::endl;
std::cerr << "Cert + Setting Signature is(" << signlen << "): ";
std::string signstr;
for(i = 0; i < (signed) signlen; i++)
{
fprintf(stderr, "%02x", signature[i]);
signstr += signature[i];
}
std::cerr << std::endl;
c -> Hash(signstr);
std::cerr << "Stored Hash Length: " << (c -> Hash()).length() << std::endl;
std::cerr << "Real Hash Length: " << signlen << std::endl;
fclose(setfp);
EVP_MD_CTX_destroy(mdctx);
return 1;
}
cert *sslroot::loadcertificate(const char *fname, std::string hash)
{
// if there is a hash - check that the file matches it before loading.
FILE *pcertfp;
/* 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)
{
pcertfp = fopen(fname, "rb");
// load certificates from file.
if (pcertfp == NULL)
{
std::cerr << "sslroot::loadcertificate() Bad File: " << fname;
std::cerr << " Cannot be Hashed!" << std::endl;
return NULL;
}
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;
}
fclose(pcertfp);
EVP_MD_CTX_destroy(mdctx);
bool same = true;
if (signlen != hash.length())
{
std::cerr << "Different Length Signatures... ";
std::cerr << "Cannot Load Certificate!" << std::endl;
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;
return NULL;
}
}
std::cerr << "Verified Signature for: " << fname;
std::cerr << std::endl;
}
else
{
std::cerr << "Not checking cert signature" << std::endl;
}
pcertfp = fopen(fname, "rb");
// load certificates from file.
if (pcertfp == NULL)
{
std::cerr << "sslroot::loadcertificate() Bad File: " << fname;
std::cerr << " Cannot be Read!" << std::endl;
return NULL;
}
XPGP *pc;
cert *npc = NULL;
if ((pc = PEM_read_XPGP(pcertfp, NULL, NULL, NULL)) != NULL)
{
// read a certificate.
std::cerr << "Loaded Certificate: ";
std::cerr << pc -> name << std::endl;
npc = makeCertificateXPGP(pc);
if (npc == NULL)
{
std::cerr << "Failed to Create Cert!" << std::endl;
return NULL;
}
}
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;
}
// Now we try to read in 1024 bytes.....
// if successful, then have settings!
// read in a line.....
int size = PQI_SSLROOT_CERT_CONFIG_SIZE;
char config_hex[PQI_SSLROOT_CERT_CONFIG_SIZE * 4]; /* double for extra space */
char line[PQI_SSLROOT_CERT_CONFIG_SIZE];
/* load as much as possible into the config_hex.
*/
int rbytes = fread(config_hex, 1, size * 4, pcertfp);
bool configLoaded = false;
int i, j;
if (rbytes < size * 2)
{
if ((hash.size() > 1) && (rbytes >= size))
{
/* old format certificate (already verified) */
std::cerr << "Loading Old Style Cert Config" << std::endl;
memcpy(line, config_hex, size);
configLoaded = true;
}
else
{
std::cerr << "Error Reading Setting: Only Cert Retrieved" << std::endl;
return npc;
}
}
/* if there was no hash then we need to check it */
if (hash.size() <= 1)
{
std::cerr << "Checking Cert Configuration for spam char" << std::endl;
for(i = 0, j = 0; i < rbytes; i++)
{
if (isxdigit(config_hex[i]))
{
config_hex[j++] = config_hex[i];
}
else
{
std::cerr << "Stripped out:" << config_hex[i] << " or "
<< (int) config_hex[i] << "@" << i
<< " j:" << j << std::endl;
}
}
if (j < size * 2)
{
std::cerr << "Error Cert Config wrong size" << std::endl;
return npc;
}
std::cerr << "Stripped out " << i - j << " spam chars" << std::endl;
}
/* now convert the hex into binary */
if (!configLoaded)
{
for(i = 0; i < size; i++)
{
((unsigned char *) line)[i] = convertHexToChar(
config_hex[2 * i], config_hex[2 * i + 1]);
}
configLoaded = true;
}
// Data arrangment.
// so far
// ------------
// 4 - (int) status
// 8 - sockaddr
// 8 - sockaddr
// 8 - sockaddr
// 4 - lc_timestamp
// 4 - lr_timestamp
// 4 - nc_timestamp
// 4 - nc_timeintvl
// 20 - name.
// 1 - end
// This will need to be made portable.....
struct sockaddr_in *addr_inet;
struct sockaddr_in *addr_inet2;
struct sockaddr_in *addr_inet3;
//int pos_status = 0;
int pos_addr = sizeof(int);
int pos_addr2 = pos_addr + sizeof(*addr_inet);
int pos_addr3 = pos_addr2 + sizeof(*addr_inet2);
int pos_lcts = pos_addr3 + sizeof(*addr_inet3);
int pos_lrts = pos_lcts + sizeof(int);
int pos_ncts = pos_lrts + sizeof(int);
int pos_ncvl = pos_ncts + sizeof(int);
int pos_name = pos_ncvl + sizeof(int);
//int pos_end = pos_name + 20; // \n. for readability.
int *status = (int *) line;
addr_inet = (struct sockaddr_in *) &(line[pos_addr]);
addr_inet2 = (struct sockaddr_in *) &(line[pos_addr2]);
addr_inet3 = (struct sockaddr_in *) &(line[pos_addr3]);
int *lcts = (int *) &(line[pos_lcts]);
int *lrts = (int *) &(line[pos_lrts]);
int *ncts = (int *) &(line[pos_ncts]);
int *ncvl = (int *) &(line[pos_ncvl]);
char *name = &(line[pos_name]);
//char *end = &(line[pos_end]);
// end of data structures....
// fill in the data.
cert *c = npc;
c -> Status(*status);
std::cerr << "Loaded Cert: " << c -> Name() << " Prev Status: ";
std::cerr << std::hex << (unsigned int) c->Status() << std::dec << std::endl;
// but ensure that inUse is not set.
c -> InUse(false);
c -> lastaddr = *addr_inet;
c -> localaddr = *addr_inet2;
c -> serveraddr = *addr_inet3;
c -> lc_timestamp = *lcts;
c -> lr_timestamp = *lrts;
c -> nc_timestamp = *ncts;
c -> nc_timeintvl = *ncvl;
name[20 - 1] = '\0';
c -> Name(std::string(name));
// save the hash.
c -> Hash(hash);
fclose(pcertfp);
// small hack - as the timestamps seem wrong.....
// could be a saving thing - or a bug....
c -> lc_timestamp = 0;
c -> lr_timestamp = 0;
// reset these. as well.
c -> nc_timestamp = 0;
c -> nc_timeintvl = 5;
return c;
}
// for sending stuff as text
// cert * loadCertFromString(std::string pem);
// std::string saveCertAsString(cert *c);
//
std::string sslroot::saveCertAsString(cert *c)
{
// save certificate to a string,
// must use a BIO.
std::string certstr;
BIO *bp = BIO_new(BIO_s_mem());
std::cerr << "saveCertAsString:" << c -> Name() << std::endl;
PEM_write_bio_XPGP(bp, c -> 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;
}
cert *sslroot::loadCertFromString(std::string pem)
{
/* Put the data into a mem BIO */
char *certstr = strdup(pem.c_str());
BIO *bp = BIO_new_mem_buf(certstr, -1);
XPGP *pc;
cert *npc = NULL;
pc = PEM_read_bio_XPGP(bp, NULL, NULL, NULL);
BIO_free(bp);
free(certstr);
if (pc != NULL)
{
// read a certificate.
std::cerr << "loadCertFromString: ";
std::cerr << pc -> name << std::endl;
npc = makeCertificateXPGP(pc);
if (npc == NULL)
{
std::cerr << "Failed to Create Cert!" << std::endl;
return NULL;
}
}
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;
}
// small hack - as the timestamps seem wrong.....
// could be a saving thing - or a bug....
npc -> lc_timestamp = 0;
npc -> lr_timestamp = 0;
// reset these. as well.
npc -> nc_timestamp = 0;
npc -> nc_timeintvl = 5;
return npc;
}
unsigned char convertHexToChar(unsigned char a, unsigned char b)
{
int num1 = 0;
int num2 = 0;
if (('0' <= a) && ('9' >= a))
{
num1 = a - '0';
}
else if (('a' <= a) && ('f' >= a))
{
num1 = 10 + a - 'a';
}
else if (('A' <= a) && ('F' >= a))
{
num1 = 10 + a - 'A';
}
if (('0' <= b) && ('9' >= b))
{
num2 = b - '0';
}
else if (('a' <= b) && ('f' >= b))
{
num2 = 10 + b - 'a';
}
else if (('A' <= b) && ('F' >= b))
{
num2 = 10 + b - 'A';
}
num1 *= 16;
num1 += num2;
return (unsigned char) num1;
}
int sslroot::printCertificate(cert *c, std::ostream &out)
{
out << "Cert Name:" << (c -> certificate) -> name << std::endl;
//X509_print_fp(stderr, c -> certificate);
return 1;
}
// This function will clean up X509 *c if necessary.
// This fn will also collate the signatures....
// that are received via p3disc.
// (connections -> registerCertificate, which does similar sign merging)
//
cert *sslroot::makeCertificateXPGP(XPGP *c)
{
if (c == NULL)
{
return NULL;
}
// At this point we check to see if there is a duplicate.
cert *dup = checkDuplicateXPGP(c);
cert *npc = NULL;
if (dup == NULL)
{
npc = new cert();
npc -> certificate = c;
if (!addtosignmap(npc)) // only allow the cert if no dup
{
std::cerr << "sslroot::makeCertificate()";
std::cerr << "Failed to Get Signature - Not Allowed!";
std::cerr << std::endl;
// failed!... cannot add it!.
delete npc;
return NULL;
}
/* setup the defaults */
npc -> Status(PERSON_STATUS_MANUAL);
npc -> trustLvl = -1;
// set Tag to be their X509CN.
npc -> Name(getX509CNString(npc->certificate->
subject->subject));
allcerts.push_back(npc);
std::cerr << "sslroot::makeCertificate() For " << c -> name;
std::cerr << " A-Okay!" << std::endl;
// at this point we need to add to the signaturelist....
}
else if (c == dup -> certificate)
{
// identical - so okay.
npc = dup;
std::cerr << "sslroot::makeCertificate() For " << c -> name;
std::cerr << " Found Identical - A-Okay!" << std::endl;
}
else
{
std::cerr << "sslroot::makeCertificate() For " << c -> name;
std::cerr << " Cleaning up other XPGP!" << std::endl;
std::cerr << " Also moving new signatures ... " << std::endl;
// clean up c.
XPGP_copy_known_signatures(pgp_keyring, dup -> certificate, c);
XPGP_free(c);
npc = dup;
}
return npc;
}
cert *sslroot::checkDuplicateXPGP(XPGP *x)
{
if (x == NULL)
return NULL;
// loop through and print - then check.
std::list<cert *>::iterator it;
for(it = allcerts.begin(); it != allcerts.end(); it++)
{
if (0 == XPGP_cmp((*it) -> certificate, x))
{
return (*it);
}
}
return NULL;
}
cert *sslroot::checkPeerXPGP(XPGP *x)
{
if (x == NULL)
return NULL;
// loop through and print - then check.
std::list<cert *>::iterator it;
for(it = peercerts.begin(); it != peercerts.end(); it++)
{
if (0 == XPGP_cmp((*it) -> certificate, x))
{
return (*it);
}
}
return NULL;
}
cert *sslroot::findpeercert(const char *name)
{
// loop through and print - then check.
//std::cerr << "Checking Certs for: " << name << std::endl;
std::list<cert *>::iterator it;
for(it = peercerts.begin(); it != peercerts.end(); it++)
{
char *certname = ((*it) -> certificate) -> name;
//std::cerr << "Cert Name:" << certname << std::endl;
if (strstr(certname, name) != NULL)
{
//std::cerr << "Matches!" << std::endl;
return (*it);
}
}
std::cerr << "sslroot::findpeercert() Failed!" << std::endl;
return NULL;
}
// returns zero for the same.
int sslroot::compareCerts(cert *a, cert *b)
{
// std::cerr << "Comparing Certificates:" << std::endl;
//printCertificate(a);
//printCertificate(b);
//X509_print_fp(stderr, a -> certificate);
//X509_print_fp(stderr, b -> certificate);
int val = XPGP_cmp(a -> certificate, b -> certificate);
std::cerr << "Certificate Comparison Returned: " << val << std::endl;
return val;
}
cert * sslroot::registerCertificateXPGP(XPGP *nc, struct sockaddr_in raddr, bool in)
{
if (nc == NULL)
return NULL;
// shoud check all certs.
cert *c = checkDuplicateXPGP(nc);
if (c != NULL)
{
if (c -> certificate == nc)
{
std::cerr << "sslroot::registerCertificate()";
std::cerr << " Found Identical XPGP cert";
std::cerr << std::endl;
}
else
{
std::cerr << "sslroot::registerCertificate()";
std::cerr << " Found Same XPGP cert/diff mem - Clean";
std::cerr << std::endl;
std::cerr << "sslroot::registerCertificate()";
std::cerr << " Copying New Signatures before deleting";
std::cerr << std::endl;
/* copy across the signatures -> if necessary */
XPGP_copy_known_signatures(pgp_keyring, c->certificate, nc);
XPGP_free(nc);
}
if (!c -> Connected())
{
c -> lastaddr = raddr;
if (in == true)
{
c -> lr_timestamp = time(NULL);
// likely to be server address
// (with default port)
// if null!
if (!isValidNet(&(c -> serveraddr.sin_addr)))
{
std::cerr << "Guessing Default Server Addr!";
std::cerr << std::endl;
c -> serveraddr = raddr;
c -> serveraddr.sin_port =
htons(PQI_DEFAULT_PORT);
}
}
else
{
c -> lc_timestamp = time(NULL);
// also likely to be servera address,
// but we can check and see if its local.
// can flag local
if (0 == inaddr_cmp(c -> localaddr, raddr))
{
c -> Local(true);
// don't set serveraddr -> just ignore
}
else
{
c -> serveraddr = raddr;
c -> Firewalled(false);
}
}
}
else
{
std::cerr << "WARNING: attempt to reg CONNECTED Cert!";
std::cerr << std::endl;
}
return c;
}
std::cerr << "sslroot::registerCertificate() Certificate Not Found!" << std::endl;
std::cerr << "Saving :" << nc -> name << std::endl;
std::cerr << std::endl;
cert *npc = makeCertificateXPGP(nc);
if (npc == NULL)
{
std::cerr << "Failed to Make Certificate";
std::cerr << std::endl;
return NULL;
}
npc -> Name(nc -> name);
npc -> lastaddr = raddr;
if (in == true)
{
npc -> lr_timestamp = time(NULL);
// likely to be server address (with default port)
std::cerr << "Guessing Default Server Addr!";
std::cerr << std::endl;
npc -> serveraddr = raddr;
npc -> serveraddr.sin_port = htons(PQI_DEFAULT_PORT);
}
else
{
npc -> lc_timestamp = time(NULL);
// as it is a new cert... all fields are
// null and the earlier tests must be
// delayed until the discovery packets.
// also likely to be server.
npc -> serveraddr = raddr;
}
// push back onto collected.
npc -> nc_timestamp = 0;
collectedcerts.push_back(npc);
// return NULL to indicate that it dosen't yet exist in dbase.
return NULL;
}
cert * sslroot::getCollectedCert()
{
if (collectedcerts.size() < 1)
return NULL;
cert *c = collectedcerts.front();
collectedcerts.pop_front();
return c;
}
bool sslroot::collectedCerts()
{
return (collectedcerts.size() > 0);
}
int sslroot::removeCertificate(cert *c)
{
if (c -> InUse())
{
std::cerr << "sslroot::removeCertificate() Failed" << std::endl;
std::cerr << "\t a cert is in use." << std::endl;
return -1;
}
std::list<cert *>::iterator it;
for(it = peercerts.begin(); it != peercerts.end(); it++)
{
if (c == (*it))
{
peercerts.erase(it);
c -> InUse(false);
c -> Accepted(false);
std::cerr << "sslroot::removeCertificate() ";
std::cerr << "Success!" << std::endl;
std::cerr << "\tMoved to Collected Certs" << std::endl;
/* remove from the keyring */
XPGP_remove_certificate(pgp_keyring, c->certificate);
collectedcerts.push_back(c);
certsChanged.IndicateChanged();
certsMajorChanged.IndicateChanged();
return 1;
}
}
std::cerr << "sslroot::removeCertificate() ";
std::cerr << "Failed to Match Cert!" << std::endl;
return 0;
}
int sslroot::addCertificate(cert *c)
{
std::cerr << "sslroot::addCertificate()" << std::endl;
c -> InUse(false);
// let most flags through.
//c -> Accepted(false);
//c -> WillConnect(false);
if (c -> certificate == NULL)
{
std::cerr << "sslroot::addCertificate() certificate==NULL" << std::endl;
std::cerr << "\tNot Adding Certificate!" << std::endl;
return 0;
}
cert *dup = checkPeerXPGP(c -> certificate);
if (dup != NULL)
{
std::cerr << "sslroot::addCertificate() Not Adding";
std::cerr << "Certificate with duplicate...." << std::endl;
std::cerr << "\t\tTry RegisterCertificate() " << std::endl;
return -1;
}
// else put in in the list.
peercerts.push_back(c);
/* add to keyring */
XPGP_add_certificate(pgp_keyring, c->certificate);
/* if this should be a trusted cert... setup */
if (c-> Trusted())
{
if (XPGP_signer_trusted(pgp_keyring, c -> certificate))
{
c -> Trusted(true);
}
else
{
c -> Trusted(false);
}
}
c -> trustLvl = XPGP_auth_certificate(pgp_keyring, c->certificate);
certsChanged.IndicateChanged();
certsMajorChanged.IndicateChanged();
return 1;
}
int sslroot::addUntrustedCertificate(cert *c)
{
// blank it all.
c -> Status(PERSON_STATUS_MANUAL);
// set Tag to be their X509CN.
c -> Name(getX509CNString(c -> certificate -> subject -> subject));
return addCertificate(c);
}
int sslroot::addCollectedCertificate(cert *c)
{
// blank it all.
c -> Status(PERSON_STATUS_MANUAL);
// set Tag to be their X509CN.
c -> Name(getX509CNString(c -> certificate -> subject -> subject));
// put in the collected certs ...
collectedcerts.push_back(c);
return 1;
}
int sslroot::validateCertificateXPGP(cert *c)
{
std::cerr << "sslroot::validateCertificate() Why Not!" << std::endl;
if (XPGP_check_valid_certificate(c->certificate))
{
c -> Valid(true);
}
else
{
c -> Valid(false);
}
std::cerr << "Cert Status: " << c -> Status() << std::endl;
return 1;
}
/* this redoes the trust calculations */
int sslroot::checkAuthCertificate(cert *xpgp)
{
std::cerr << "sslroot::checkAuthCertificate()" << std::endl;
if ((xpgp == NULL) || (xpgp -> certificate == NULL))
{
return -1;
}
/* reevaluate the auth of the xpgp */
xpgp -> trustLvl = XPGP_auth_certificate(pgp_keyring, xpgp->certificate);
/* this also merges the signature into the keyring */
certsChanged.IndicateChanged();
certsMajorChanged.IndicateChanged();
return 1;
}
int sslroot::signCertificate(cert *xpgp)
{
std::cerr << "sslroot::signCertificate()" << std::endl;
cert *own = getOwnCert();
pqioutput(PQL_DEBUG_BASIC, pqisslrootzone,
"sslroot::signCertificate()");
/* check that cert is suitable */
/* sign it */
XPGP_sign_certificate(pgp_keyring, xpgp -> certificate, own -> certificate);
/* reevaluate the auth of the xpgp */
xpgp -> trustLvl = XPGP_auth_certificate(pgp_keyring, xpgp->certificate);
/* this also merges the signature into the keyring */
certsChanged.IndicateChanged();
certsMajorChanged.IndicateChanged();
return 1;
}
int sslroot::trustCertificate(cert *c, bool totrust)
{
std::cerr << "sslroot::trustCertificate()" << std::endl;
/* check auth status of certificate */
pqioutput(PQL_DEBUG_BASIC, pqisslrootzone,
"sslroot::trustCertificate()");
/* if trusted -> untrust */
if (!totrust)
{
XPGP_signer_untrusted(pgp_keyring, c -> certificate);
c -> Trusted(false);
}
else
{
/* if auth then we can trust them */
if (XPGP_signer_trusted(pgp_keyring, c -> certificate))
{
c -> Trusted(true);
}
}
/* reevaluate the auth of the xpgp */
c -> trustLvl = XPGP_auth_certificate(pgp_keyring, c->certificate);
certsChanged.IndicateChanged();
certsMajorChanged.IndicateChanged();
return 1;
}
int sslroot::superNodeMode()
{
#
/********************************** WINDOWS/UNIX SPECIFIC PART ******************/
#ifndef WINDOWS_SYS // UNIX only.
XPGP_supernode(pgp_keyring);
#endif
/********************************** WINDOWS/UNIX SPECIFIC PART ******************/
return 1;
}
/***** REMOVED!!!
*
*
std::list<std::string> sslroot::listCertificates()
{
std::list<std::string> names;
std::list<cert *>::iterator it;
for(it = peercerts.begin(); it != peercerts.end(); it++)
{
names.push_back(((*it) -> certificate) -> name);
}
return names;
}
*
*
*
*/
bool sslroot::CertsChanged()
{
return certsChanged.Changed(0);
}
bool sslroot::CertsMajorChanged()
{
return certsMajorChanged.Changed(0);
}
void sslroot::IndicateCertsChanged()
{
certsChanged.IndicateChanged();
}
std::list<cert *> &sslroot::getCertList()
{
return peercerts;
}
std::string sslroot::getSetting(std::string opt)
{
std::map<std::string, std::string>::iterator it;
if (settings.end() != (it = settings.find(opt)))
{
// found setting.
std::cerr << "sslroot::getSetting(" << opt << ") = ";
std::cerr << it -> second << std::endl;
return it -> second;
}
// else return empty string.
std::cerr << "sslroot::getSetting(" << opt;
std::cerr << ") Not There!" << std::endl;
std::string empty("");
return empty;
}
void sslroot::setSetting(std::string opt, std::string val)
{
// check settings..
std::cerr << "sslroot::saveSetting(" << opt << ", ";
std::cerr << val << ")" << std::endl;
settings[opt] = val;
return;
}
cert *sslroot::getOwnCert()
{
return own_cert;
}
int sslroot::checkNetAddress()
{
std::list<std::string> addrs = getLocalInterfaces();
std::list<std::string>::iterator it;
bool found = false;
for(it = addrs.begin(); (!found) && (it != addrs.end()); it++)
{
if ((*it) == inet_ntoa(own_cert -> localaddr.sin_addr))
{
found = true;
}
}
/* check that we didn't catch 0.0.0.0 - if so go for prefered */
if ((found) && (own_cert -> localaddr.sin_addr.s_addr == 0))
{
found = false;
}
if (!found)
{
own_cert -> localaddr.sin_addr = getPreferredInterface();
}
if ((isPrivateNet(&(own_cert -> localaddr.sin_addr))) ||
(isLoopbackNet(&(own_cert -> localaddr.sin_addr))))
{
own_cert -> Firewalled(true);
}
else
{
//own_cert -> Firewalled(false);
}
int port = ntohs(own_cert -> localaddr.sin_port);
if ((port < PQI_MIN_PORT) || (port > PQI_MAX_PORT))
{
own_cert -> localaddr.sin_port = htons(PQI_DEFAULT_PORT);
}
/* if localaddr = serveraddr, then ensure that the ports
* are the same (modify server)... this mismatch can
* occur when the local port is changed....
*/
if (own_cert -> localaddr.sin_addr.s_addr ==
own_cert -> serveraddr.sin_addr.s_addr)
{
own_cert -> serveraddr.sin_port =
own_cert -> localaddr.sin_port;
}
// ensure that address family is set, otherwise windows Barfs.
own_cert -> localaddr.sin_family = AF_INET;
own_cert -> serveraddr.sin_family = AF_INET;
own_cert -> lastaddr.sin_family = AF_INET;
return 1;
}
/********** 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;
}
cert::cert()
:certificate(NULL), hash("")
{
return;
}
cert::~cert()
{
return;
}
std::string cert::Signature()
{
if (certificate == NULL)
{
return Name();
}
else
{
std::ostringstream out;
certsign cs;
getSSLRoot() -> getcertsign(this, cs);
out << std::hex;
for(int i = 0; i < CERTSIGNLEN; i++)
{
unsigned char n = cs.data[i];
out << std::hex << std::setw(2) << std::setfill('0')
<< std::setprecision(2) << (unsigned int) n;
}
return out.str();
}
}
std::string cert::Hash()
{
return hash;
}
void cert::Hash(std::string h)
{
hash = h;
return;
}
/********************* signature stuff *********************/
bool certsign::operator<(const certsign &ref) const
{
//compare the signature.
if (0 > memcmp(data, ref.data, CERTSIGNLEN))
return true;
return false;
}
bool certsign::operator==(const certsign &ref) const
{
//compare the signature.
return (0 == memcmp(data, ref.data, CERTSIGNLEN));
}
/* Fns for relating cert signatures to structures */
cert *sslroot::findPeerId(std::string id)
{
certsign sign;
if (!convert_to_certsign(id, sign))
{
pqioutput(PQL_WARNING, pqisslrootzone,
"sslroot::findPeerId() ERROR: Failed to Convert to certsign");
return NULL;
}
return findcertsign(sign);
}
cert *sslroot::findcertsign(certsign &sign)
{
std::map<certsign, cert *>::iterator it;
std::ostringstream out;
out << "sslroot::findcertsign()" << std::endl;
for (it = signmap.begin(); it != signmap.end(); it++)
{
out << "Checking Vs " << it -> second -> Name();
if (sign == it -> first)
{
out << "Match!";
}
out << std::endl;
}
pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str());
if (signmap.end() != (it = signmap.find(sign)))
{
return it -> second;
}
pqioutput(PQL_WARNING, pqisslrootzone,
"sslroot::findcertsign() ERROR: No Matching Entry");
return NULL;
}
int sslroot::getcertsign(cert *c, certsign &sign)
{
// bug ... segv a couple of times here!
if ((c == NULL) || (c->certificate == NULL))
{
pqioutput(PQL_ALERT, pqisslrootzone,
"sslroot::getcertsign() ERROR: NULL c || c->certificate");
return 0;
}
// a Bit of a hack here.....
// get the first signature....
if (sk_XPGP_SIGNATURE_num(c->certificate->signs) < 1)
{
pqioutput(PQL_ALERT, pqisslrootzone,
"sslroot::getcertsign() ERROR: No Signatures");
return 0;
}
XPGP_SIGNATURE *xpgpsign = sk_XPGP_SIGNATURE_value(c->certificate->signs, 0);
// 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)
{
pqioutput(PQL_ALERT, pqisslrootzone,
"sslroot::getcertsign() ERROR: short Signature");
return 0;
}
// else copy in the first CERTSIGNLEN.
unsigned char *signdata = ASN1_STRING_data(signature);
memcpy(sign.data, signdata, CERTSIGNLEN);
return 1;
}
int sslroot::addtosignmap(cert *c)
{
certsign cs;
if (!getcertsign(c, cs))
{
// error.
pqioutput(PQL_ALERT, pqisslrootzone,
"sslroot::addsigntomap() ERROR: Fail to getcertsign()");
return 0;
}
cert *c2 = findcertsign(cs);
if (c2 == NULL)
{
// add, and return okay.
signmap[cs] = c;
return 1;
}
if (c2 != c)
{
// error.
pqioutput(PQL_ALERT, pqisslrootzone,
"sslroot::addsigntomap() ERROR: Duplicate Entry()");
return 0;
}
// else already exists.
return 1;
}
int sslroot::hashFile(std::string fname, unsigned char *hash, unsigned int hlen)
{
// open the file.
// setup the hash.
// pipe the file through.
return 1;
}
int sslroot::hashDigest(char *data, unsigned int dlen,
unsigned char *hash, unsigned int hlen)
{
EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
if (0 == EVP_DigestInit_ex(mdctx, EVP_sha1(), NULL))
{
std::cerr << "EVP_DigestInit Failure!" << std::endl;
return -1;
}
if (0 == EVP_DigestUpdate(mdctx, data, dlen))
{
std::cerr << "EVP_DigestUpdate Failure!" << std::endl;
return -1;
}
unsigned int signlen = hlen;
if (0 == EVP_DigestFinal_ex(mdctx, hash, &signlen))
{
std::cerr << "EVP_DigestFinal Failure!" << std::endl;
return -1;
}
EVP_MD_CTX_destroy(mdctx);
return signlen;
}
int sslroot::signDigest(EVP_PKEY *key, char *data, unsigned int dlen,
unsigned char *sign, unsigned int slen)
{
unsigned int signlen = EVP_PKEY_size(key);
{
std::ostringstream out;
out << "sslroot::signDigest(" << (void *) key;
out << ", " << (void *) data << ", " << dlen << ", ";
out << (void *) sign << ", " << slen << ")" << std::endl;
pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str());
}
if (signlen > slen)
{
pqioutput(PQL_ALERT, pqisslrootzone,
"sslroot::signDigest() Sign Length too short");
return -1;
}
EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
if (0 == EVP_SignInit_ex(mdctx, EVP_sha1(), NULL))
{
pqioutput(PQL_ALERT, pqisslrootzone,
"sslroot::signDigest() EVP_SignInit Failure!");
return -1;
}
if (0 == EVP_SignUpdate(mdctx, data, dlen))
{
pqioutput(PQL_ALERT, pqisslrootzone,
"sslroot::signDigest() EVP_SignUpdate Failure!");
return -1;
}
signlen = slen;
if (0 == EVP_SignFinal(mdctx, sign, &signlen, key))
{
pqioutput(PQL_ALERT, pqisslrootzone,
"sslroot::signDigest() EVP_SignFinal Failure!");
return -1;
}
{
// display signed data
std::ostringstream out;
out << "sslroot::signDigest() Data Display" << std::endl;
out << "Data To Sign (" << dlen << "):::::::::::::" << std::hex;
for(unsigned int i = 0; i < dlen; i++)
{
if (i % 16 == 0)
{
out << std::endl;
out << std::setw(4) << i << " : ";
}
out << std::setw(2) << (unsigned int) ((unsigned char *) data)[i] << " ";
}
out << std::endl;
out << "Signature (" << std::dec << slen << "):::::::::::::" << std::hex;
for(unsigned int i = 0; i < slen; i++)
{
if (i % 16 == 0)
{
out << std::endl;
out << std::setw(4) << i << " : ";
}
out << std::setw(2) << (unsigned int) ((unsigned char *) sign)[i] << " ";
}
out << std::endl;
out << "::::::::::::::::::::::::::::::::::::::::::::::" << std::endl;
pqioutput(PQL_DEBUG_BASIC, pqisslrootzone, out.str());
}
EVP_MD_CTX_destroy(mdctx);
return signlen;
}
int sslroot::verifyDigest(EVP_PKEY *key, char *data, unsigned int dlen,
unsigned char *sign, unsigned int slen)
{
EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
if (0 == EVP_VerifyInit_ex(mdctx, EVP_sha1(), NULL))
{
pqioutput(PQL_ALERT, pqisslrootzone,
"sslroot::verifyDigest() EVP_VerifyInit Failure!");
return -1;
}
if (0 == EVP_VerifyUpdate(mdctx, data, dlen))
{
pqioutput(PQL_ALERT, pqisslrootzone,
"sslroot::verifyDigest() EVP_VerifyUpdate Failure!");
return -1;
}
int vv;
if (0 > (vv = EVP_VerifyFinal(mdctx, sign, slen, key)))
{
pqioutput(PQL_ALERT, pqisslrootzone,
"sslroot::verifyDigest() EVP_VerifyFinale Failure!");
return -1;
}
if (vv == 1)
{
pqioutput(PQL_DEBUG_BASIC, pqisslrootzone,
"sslroot::verifyDigest() Verified Signature OKAY");
}
else
{
std::ostringstream out;
out << "sslroot::verifyDigest() Failed Verification!" << std::endl;
out << "Data To Verify (" << dlen << "):::::::::::::" << std::hex;
for(unsigned int i = 0; i < dlen; i++)
{
if (i % 16 == 0)
{
out << std::endl;
out << std::setw(4) << i << " : ";
}
out << std::setw(2) << (unsigned int) ((unsigned char *) data)[i] << " ";
}
out << std::endl;
out << "Signature (" << std::dec << slen << "):::::::::::::" << std::hex;
for(unsigned int i = 0; i < slen; i++)
{
if (i % 16 == 0)
{
out << std::endl;
out << std::setw(4) << i << " : ";
}
out << std::setw(2) << (unsigned int) ((unsigned char *) sign)[i] << " ";
}
out << std::endl;
out << "::::::::::::::::::::::::::::::::::::::::::::::" << std::endl;
out << "sslroot::verifyDigest() Should Clear SSL Error!";
pqioutput(PQL_ALERT, pqisslrootzone, out.str());
}
EVP_MD_CTX_destroy(mdctx);
return vv;
}
// Think both will fit in the one Structure.
int sslroot::generateKeyPair(EVP_PKEY *keypair, unsigned int keylen)
{
RSA *rsa = RSA_generate_key(2048, 65537, NULL, NULL);
EVP_PKEY_assign_RSA(keypair, rsa);
std::cerr << "sslroot::generateKeyPair()" << std::endl;
return 1;
}
// Extra features for XPGP..... (for login window)
// This fn installs and signs a trusted peer.
// It is limited to only working just after certificate creation.
// this is done by checking the timestamps.
//
// It should be called before the pqi handler is initiated,
// otherwise the connection will not be automatically started.
int sslroot::loadInitialTrustedPeer(std::string tp_file)
{
/* we will only do this if various conditions are met.
* (1) check validity of certificate
* (2) check that we don't have any other certificates loaded.
* (3) check that our certificate has just been created (timestamp) and only has one signature.
*/
bool canLoad = true;
std::string userName;
/* check (1) valid cert */
if (!LoadCheckXPGPandGetName(tp_file.c_str(),userName))
{
std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(1)";
std::cerr << std::endl;
canLoad = false;
return 0;
}
/* check (2) no other certificates loaded */
if (peercerts.size() != 1)
{
/* too many certs loaded! */
std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(2a)";
std::cerr << std::endl;
canLoad = false;
return 0;
}
/* that one must be our own */
cert *ourcert = getOwnCert();
if ((!ourcert) || (ourcert != *(peercerts.begin())) || (!ourcert->certificate))
{
/* too many certs loaded! */
std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(2b)";
std::cerr << std::endl;
canLoad = false;
return 0;
}
XPGP *xpgp = ourcert->certificate;
if (sk_XPGP_SIGNATURE_num(xpgp->signs) != 1)
{
/* too many certs loaded! */
std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(3a)";
std::cerr << std::endl;
canLoad = false;
return 0;
}
/* check own certificate timestamps */
time_t cts = time(NULL);
X509_VAL *certv = xpgp->key->validity;
XPGP_SIGNATURE *ownsign = sk_XPGP_SIGNATURE_value(xpgp->signs, 0);
ASN1_TIME *signts = ownsign->timestamp;
ASN1_TIME *createts = certv->notBefore;
/* compare timestamps
* Certificate timestamp should have been generated
* within the last 5 seconds, */
time_t max_initts = cts - 5;
if ((0 > X509_cmp_time(createts, &max_initts)) || (0 > X509_cmp_time(signts, &max_initts)))
{
std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(3b)";
std::cerr << std::endl;
canLoad = false;
return 0;
}
/* or if in the future! */
if ((0 < X509_cmp_current_time(createts)) || (0 < X509_cmp_current_time(signts)))
{
std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(3c)";
std::cerr << std::endl;
canLoad = false;
return 0;
}
/* if we get here - it has passed the tests, and we can sign it, and install it */
cert *trusted_cert = loadcertificate(tp_file.c_str(), ""); /* no Hash! */
if (!trusted_cert)
{
std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(4a)";
std::cerr << std::endl;
canLoad = false;
return 0;
}
/* now add it */
if (1 != addCertificate(trusted_cert))
{
std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(4b)";
std::cerr << std::endl;
canLoad = false;
return 0;
}
/* must set these flags completely - as they aren't changed */
trusted_cert->Accepted(true);
trusted_cert->Manual(false);
trusted_cert->WillConnect(true);
trusted_cert->WillListen(true);
/* use existing firewall/forwarded flags */
/* sign it! (must be after add) */
if (!signCertificate(trusted_cert))
{
std::cerr << "sslroot::loadInitialTrustedPeer() Failed TrustedPeer Checks!(4c)";
std::cerr << std::endl;
canLoad = false;
return 0;
}
if (canLoad)
{
std::cerr << "sslroot::loadInitialTrustedPeer() Loaded: " << userName;
std::cerr << std::endl;
return 1;
}
return 0;
}
// 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, 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 convert_to_str(certsign &sign)
{
std::ostringstream id;
for(int i = 0; i < CERTSIGNLEN; i++)
{
id << std::hex << std::setw(2) << std::setfill('0') << (uint16_t) (((uint8_t *) (sign.data))[i]);
}
return id.str();
}
bool convert_to_certsign(std::string id, certsign &sign)
{
char num[3];
if (id.length() < CERTSIGNLEN * 2)
{
return false;
}
for(int i = 0; i < CERTSIGNLEN; i++)
{
num[0] = id[i * 2];
num[1] = id[i * 2 + 1];
num[2] = '\0';
int32_t val;
if (1 != sscanf(num, "%x", &val))
{
return false;
}
sign.data[i] = (uint8_t) val;
}
return true;
}