You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pEpEngine/src/pgp_netpgp.c

1807 lines
43 KiB
C

/**
* @internal
* @file pgp_netpgp.c
* @brief Implementation of NetPGP
* @license This file is under GNU General Public License 3.0 see LICENSE.txt
*/
/*
* Check to see if this machine uses EBCDIC. (Yes, believe it or
* not, there are still machines out there that use EBCDIC.)
*/
#if 'A' == '\301'
# define NETPGP_EBCDIC 1
#else
# define NETPGP_ASCII 1
#endif
#include "pEp_internal.h"
#include "pgp_netpgp.h"
#include <limits.h>
#include <ctype.h>
#include "wrappers.h"
#include <netpgp.h>
#include <netpgp/config.h>
#include <netpgp/memory.h>
#include <netpgp/crypto.h>
#include <netpgp/netpgpsdk.h>
#include <netpgp/validate.h>
#include <netpgp/readerwriter.h>
#include <netpgp/netpgpdefs.h>
#include <pthread.h>
#include <regex.h>
#if defined(NETPGP_EBCDIC)
#include <unistd.h>
#endif
#if 0
#define TRACE_FUNCS() printf("Trace fun: %s:%d, %s\n",__FILE__,__LINE__,__FUNCTION__);
#else
#define TRACE_FUNCS()
#endif
#define _ENDL "\\s*(\r\n|\r|\n)"
inline char A(char c)
{
TRACE_FUNCS()
#if defined(NETPGP_EBCDIC)
__e2a_l(&c,1);
#endif
return c;
}
netpgp_t *netpgp;
static pthread_mutex_t netpgp_mutex;
static PEP_STATUS init_netpgp()
{
TRACE_FUNCS()
PEP_STATUS status = PEP_STATUS_OK;
const char *home = NULL;
pgp_io_t *io;
if(pthread_mutex_init(&netpgp_mutex, NULL)){
return PEP_OUT_OF_MEMORY;
}
if(pthread_mutex_lock(&netpgp_mutex)){
return PEP_UNKNOWN_ERROR;
}
if (strcmp(setlocale(LC_ALL, NULL), "C") == 0)
setlocale(LC_ALL, "");
netpgp=malloc(sizeof(netpgp_t));
memset(netpgp, 0x0, sizeof(netpgp_t));
//netpgp_setvar(netpgp, "max mem alloc", "4194304");
netpgp_setvar(netpgp, "need seckey", "1");
netpgp_setvar(netpgp, "need pubkey", "1");
netpgp_setvar(netpgp, "batch", "1");
//pgp_set_debug_level("keyring.c");
//netpgp_setvar(netpgp, "need userid", "1");
if (!home)
home = getenv("HOME");
if (!home)
status = PEP_INIT_CRYPTO_LIB_INIT_FAILED;
if(home){
netpgp_set_homedir(netpgp,(char*)home, "/.netpgp", 0);
}else{
status = PEP_INIT_NO_CRYPTO_HOME;
goto unlock_netpgp;
}
// pair with gpg's cert-digest-algo
netpgp_setvar(netpgp, "hash", "SHA256");
// subset of gpg's personal-cipher-preferences
// here only one cipher can be selected
netpgp_setvar(netpgp, "cipher", "CAST5");
if (!netpgp_init(netpgp)) {
status = PEP_INIT_CRYPTO_LIB_INIT_FAILED;
free(netpgp);
netpgp=NULL;
goto unlock_netpgp;
}
//pgp_set_debug_level("packet-parse.c");
//pgp_set_debug_level("openssl_crypto.c");
//pgp_set_debug_level("crypto.c");
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return status;
}
static void release_netpgp()
{
TRACE_FUNCS()
if(pthread_mutex_lock(&netpgp_mutex)){
return;
}
netpgp_end(netpgp);
memset(netpgp, 0x0, sizeof(netpgp_t));
pthread_mutex_destroy(&netpgp_mutex);
return;
}
PEP_STATUS pgp_init(PEP_SESSION session, bool in_first)
{
TRACE_FUNCS()
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
if(!session) return PEP_ILLEGAL_VALUE;
if (in_first) {
if((status = init_netpgp()) != PEP_STATUS_OK)
return status;
}
return PEP_STATUS_OK;
}
void pgp_release(PEP_SESSION session, bool out_last)
{
TRACE_FUNCS()
assert(session);
if(!session) return;
if (out_last){
release_netpgp();
}
}
// return 1 if the file contains ascii-armoured text
static unsigned
_armoured(const char *buf, size_t size, const char *pattern)
{
TRACE_FUNCS()
unsigned armoured = 0;
regex_t r;
if( regcomp(&r, pattern, REG_EXTENDED|REG_NOSUB) ) printf("_armoured error\n");
if (regexec(&r, buf, size, 0, NULL) == 0) {
armoured = 1;
}
regfree(&r);
return armoured;
}
// Iterate through netpgp' reported valid signatures
// fill a list of valid figerprints
// returns PEP_STATUS_OK if all sig reported valid
// error status otherwise.
static PEP_STATUS _validation_results(
netpgp_t *netpgp,
pgp_validation_t *vresult,
stringlist_t **keylist
)
{
TRACE_FUNCS()
time_t now;
time_t t;
*keylist = NULL;
now = time(NULL);
if (now < vresult->birthtime) {
// signature is not valid yet
return PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH;
}
if (vresult->duration != 0 && now > vresult->birthtime + vresult->duration) {
// signature has expired
t = vresult->duration + vresult->birthtime;
return PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH;
}
if (vresult->validc && vresult->valid_sigs &&
!vresult->invalidc && !vresult->unknownc ) {
stringlist_t *_keylist;
// caller responsible to free
_keylist = new_stringlist(NULL);
assert(_keylist);
if (_keylist == NULL) {
return PEP_OUT_OF_MEMORY;
}
stringlist_t *k = _keylist;
unsigned c = 0;
for (unsigned n = 0; n < vresult->validc; ++n) {
unsigned from = 0;
const pgp_key_t *signer;
char *fprstr = NULL;
const uint8_t *keyid = vresult->valid_sigs[n].signer_id;
signer = pgp_getkeybyid(netpgp->io, netpgp->pubring,
keyid, &from, NULL, NULL,
0, 0); /* check neither revocation nor expiry
as is should be checked already */
if(signer)
uint_to_string(signer->pubkeyfpr.fingerprint, &fprstr, signer->pubkeyfpr.length);
else
continue;
if (fprstr == NULL){
free_stringlist(_keylist);
return PEP_OUT_OF_MEMORY;
}
k = stringlist_add(k, fprstr);
free(fprstr);
if(!k){
free_stringlist(_keylist);
return PEP_OUT_OF_MEMORY;
}
c++;
}
if(c > 0) {
*keylist = _keylist;
return PEP_STATUS_OK;
}
free_stringlist(_keylist);
return PEP_VERIFY_NO_KEY;
}
if (vresult->validc + vresult->invalidc + vresult->unknownc == 0) {
// No signatures found - is this memory signed?
return PEP_VERIFY_NO_KEY;
}
if (vresult->invalidc) {
// some invalid signatures
return PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH;
}
// only unknown sigs
return PEP_DECRYPTED;
}
#define ARMOR_HEAD "^-----BEGIN PGP MESSAGE-----"_ENDL
PEP_STATUS pgp_decrypt_and_verify(
PEP_SESSION session, const char *ctext, size_t csize,
const char *dsigtext, size_t dsigsize,
char **ptext, size_t *psize, stringlist_t **keylist,
char** filename_ptr // will be ignored
)
{
TRACE_FUNCS()
char *_ptext = NULL;
char* passphrase = NULL;
PEP_STATUS result;
stringlist_t *_keylist = NULL;
assert(session);
assert(ctext);
assert(csize);
assert(ptext);
assert(psize);
assert(keylist);
passphrase = session->curr_passphrase;
if(passphrase && passphrase[0]) {
netpgp_set_validation_password(passphrase);
}
if(!session || !ctext || !csize || !ptext || !psize || !keylist)
return PEP_ILLEGAL_VALUE;
if(pthread_mutex_lock(&netpgp_mutex)){
return PEP_UNKNOWN_ERROR;
}
*ptext = NULL;
*psize = 0;
*keylist = NULL;
pgp_validation_t *vresult = malloc(sizeof(pgp_validation_t));
memset(vresult, 0x0, sizeof(pgp_validation_t));
key_id_t *recipients_key_ids = NULL;
unsigned recipients_count = 0;
pgp_memory_t *mem = pgp_decrypt_and_validate_buf(
netpgp,
vresult,
ctext, csize,
netpgp->secring, netpgp->pubring,
_armoured(ctext, csize, ARMOR_HEAD),
&recipients_key_ids, &recipients_count
);
if (mem == NULL) {
result = PEP_OUT_OF_MEMORY;
goto unlock_netpgp;
}
const size_t _psize = pgp_mem_len(mem);
if (_psize){
if ((_ptext = malloc(_psize + 1)) == NULL) {
result = PEP_OUT_OF_MEMORY;
goto free_pgp;
}
memcpy(_ptext, pgp_mem_data(mem), _psize);
_ptext[_psize] = '\0'; // safeguard for naive users
result = PEP_DECRYPTED;
}else{
result = PEP_DECRYPT_NO_KEY;
goto free_pgp;
}
if (result == PEP_DECRYPTED) {
result = _validation_results(netpgp, vresult, &_keylist);
printf("result=%x\n",result);
if (result == PEP_DECRYPTED || result == PEP_VERIFY_NO_KEY) {
if((_keylist = new_stringlist("")) == NULL) {
result = PEP_OUT_OF_MEMORY;
goto free_ptext;
}
result = PEP_DECRYPTED;
}else if (result != PEP_STATUS_OK) {
goto free_ptext;
}else{
result = PEP_DECRYPTED_AND_VERIFIED;
}
}
stringlist_t *k = _keylist;
for (unsigned n = 0; n < recipients_count; ++n) {
unsigned from = 0;
const pgp_key_t *rcpt;
char *fprstr = NULL;
key_id_t *keyid = &recipients_key_ids[n];
rcpt = pgp_getkeybyid(netpgp->io, netpgp->pubring,
*keyid, &from, NULL, NULL,
0, 0); /* check neither revocation nor expiry*/
if(rcpt)
uint_to_string(rcpt->pubkeyfpr.fingerprint, &fprstr, rcpt->pubkeyfpr.length);
else
// if no key found put ID instead of fpr
uint_to_string(*keyid, &fprstr, sizeof(key_id_t));
if (fprstr == NULL){
result = PEP_OUT_OF_MEMORY;
goto free_keylist;
}
k = stringlist_add_unique(k, fprstr);
free(fprstr);
if(!k){
result = PEP_OUT_OF_MEMORY;
goto free_keylist;
}
}
if (result == PEP_DECRYPTED_AND_VERIFIED || result == PEP_DECRYPTED) {
*ptext = _ptext;
*psize = _psize;
(*ptext)[*psize] = 0; // safeguard for naive users
*keylist = _keylist;
/* _ptext and _keylist ownership transfer, don't free */
goto free_pgp;
}
free_keylist:
free_stringlist(_keylist);
free_ptext:
free(_ptext);
free_pgp:
pgp_memory_free(mem);
pgp_validate_result_free(vresult);
unlock_netpgp:
free(recipients_key_ids);
pthread_mutex_unlock(&netpgp_mutex);
return result;
}
#define ARMOR_SIG_HEAD "^-----BEGIN PGP (SIGNATURE|SIGNED MESSAGE)-----"_ENDL
PEP_STATUS pgp_verify_text(
PEP_SESSION session, const char *text, size_t size,
const char *signature, size_t sig_size, stringlist_t **keylist
)
{
TRACE_FUNCS()
pgp_memory_t *signedmem;
pgp_memory_t *sig;
pgp_validation_t *vresult;
PEP_STATUS result;
stringlist_t *_keylist;
assert(session);
assert(text);
assert(size);
assert(signature);
assert(sig_size);
assert(keylist);
if(!session || !text || !size || !signature || !sig_size || !keylist)
return PEP_ILLEGAL_VALUE;
if(pthread_mutex_lock(&netpgp_mutex)){
return PEP_UNKNOWN_ERROR;
}
*keylist = NULL;
vresult = malloc(sizeof(pgp_validation_t));
if (vresult == NULL) {
result = PEP_OUT_OF_MEMORY;
goto unlock_netpgp;
}
memset(vresult, 0x0, sizeof(pgp_validation_t));
signedmem = pgp_memory_new();
if (signedmem == NULL) {
result = PEP_OUT_OF_MEMORY;
goto unlock_netpgp;
}
pgp_memory_add(signedmem, (const uint8_t*)text, size);
sig = pgp_memory_new();
if (sig == NULL) {
pgp_memory_free(signedmem);
result = PEP_OUT_OF_MEMORY;
goto unlock_netpgp;
}
pgp_memory_add(sig, (const uint8_t*)signature, sig_size);
pgp_validate_mem_detached(netpgp->io, vresult, sig,
NULL,/* output */
_armoured(signature, sig_size, ARMOR_SIG_HEAD),
netpgp->pubring,
signedmem);
result = _validation_results(netpgp, vresult, &_keylist);
if (result != PEP_STATUS_OK) {
goto free_pgp;
}else{
result = PEP_VERIFIED;
}
if (result == PEP_VERIFIED) {
/* TODO : check trust level */
result = PEP_VERIFIED_AND_TRUSTED;
}
if (result == PEP_VERIFIED || result == PEP_VERIFIED_AND_TRUSTED) {
*keylist = _keylist;
/* _keylist ownership transfer, don't free */
goto free_pgp;
}
free_stringlist(_keylist);
free_pgp:
// free done by pgp_validate_mem_detached
// pgp_memory_free(sig);
// pgp_memory_free(signedmem);
pgp_validate_result_free(vresult);
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return result;
}
PEP_STATUS pgp_sign_only(
PEP_SESSION session, const char* fpr, const char *ptext,
size_t psize, char **stext, size_t *ssize
)
{
TRACE_FUNCS()
pgp_key_t *signer = NULL;
pgp_seckey_t *seckey = NULL;
pgp_memory_t *signedmem = NULL;
pgp_memory_t *text = NULL;
pgp_output_t *output;
const char *hashalg;
pgp_keyring_t *snrs;
pgp_create_sig_t *sig;
uint8_t keyid[PGP_KEY_ID_SIZE];
PEP_STATUS result;
assert(session);
assert(fpr);
assert(ptext);
assert(psize);
assert(stext);
assert(ssize);
if(!session || !ptext || !psize || !stext || !ssize || !fpr || !fpr[0])
return PEP_ILLEGAL_VALUE;
if(pthread_mutex_lock(&netpgp_mutex)){
return PEP_UNKNOWN_ERROR;
}
*stext = NULL;
*ssize = 0;
if ((snrs = calloc(1, sizeof(*snrs))) == NULL) {
result = PEP_OUT_OF_MEMORY;
goto unlock_netpgp;
}
assert(fpr && fpr[0]);
uint8_t *uint_fpr = NULL;
size_t fprlen;
unsigned from = 0;
if (string_to_uint(fpr, &uint_fpr, &fprlen)) {
if ((signer = (pgp_key_t *)pgp_getkeybyfpr(netpgp->io, netpgp->secring, uint_fpr, fprlen, &from, NULL, 1,1)) == NULL) {
/* reject revoked and expired */
result = PEP_KEY_NOT_FOUND;
printf("%s:%d, Key not found\n",__FILE__,__LINE__);
goto free_snrs;
}
} else{
result = PEP_ILLEGAL_VALUE;
goto free_snrs;
}
// add key to signers
pgp_keyring_add(snrs, signer);
if(snrs->keys == NULL){
result = PEP_OUT_OF_MEMORY;
goto free_snrs;
}
/* Empty keylist ?*/
if(snrs->keyc == 0){
result = PEP_ILLEGAL_VALUE;
goto free_snrs;
}
seckey = pgp_key_get_certkey(signer);
/* No signing key. Revoked ? */
if(seckey == NULL){
result = PEP_GET_KEY_FAILED;
goto free_snrs;
}
hashalg = netpgp_getvar(netpgp, "hash");
const char *_stext;
size_t _ssize;
text = pgp_memory_new();
pgp_memory_add(text, (const uint8_t*)ptext, psize);
pgp_setup_memory_write(&output, &signedmem, psize);
pgp_writer_push_armor_msg(output);
pgp_hash_alg_t hash_alg = pgp_str_to_hash_alg(hashalg);
sig = pgp_create_sig_new();
pgp_start_sig(sig, seckey, hash_alg, PGP_SIG_BINARY);
pgp_sig_add_data(sig, pgp_mem_data(text), pgp_mem_len(text));
pgp_memory_free(text);
pgp_add_creation_time(sig, time(NULL));
pgp_add_sig_expiration_time(sig, 0);
pgp_keyid(keyid, sizeof(keyid), &seckey->pubkey, hash_alg);
pgp_add_issuer_keyid(sig, keyid);
pgp_end_hashed_subpkts(sig);
pgp_write_sig(output, sig, &seckey->pubkey, seckey);
pgp_writer_close(output);
pgp_create_sig_delete(sig);
if (!signedmem) {
result = PEP_UNENCRYPTED;
goto free_snrs;
}
_stext = (char*) pgp_mem_data(signedmem);
_ssize = pgp_mem_len(signedmem);
// Allocate transferable buffer
char *_buffer = malloc(_ssize + 1);
assert(_buffer);
if (_buffer == NULL) {
result = PEP_OUT_OF_MEMORY;
goto free_signedmem;
}
memcpy(_buffer, _stext, _ssize);
*stext = _buffer;
*ssize = _ssize;
(*stext)[*ssize] = 0; // safeguard for naive users
result = PEP_STATUS_OK;
free_signedmem :
pgp_memory_free(signedmem);
free_snrs :
pgp_keyring_free(snrs);
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return result;
}
PEP_STATUS pgp_encrypt_and_sign(
PEP_SESSION session, const stringlist_t *keylist, const char *ptext,
size_t psize, char **ctext, size_t *csize
)
{
TRACE_FUNCS()
PEP_STATUS result = PEP_STATUS_OK;
stringlist_t *klp;
int ret;
uint8_t *fpr = NULL;
unsigned from;
size_t len = 0;
pgp_memory_t *mem = NULL;
pgp_keyring_t *rcpts = NULL;
pgp_seckey_t *seckey = NULL;
pgp_key_t *signer = NULL;
pgp_key_t *key = NULL;
assert(netpgp->secring);
string_to_uint(keylist->value, &fpr, &len);
from = 0;
signer = pgp_getkeybyfpr(netpgp->io, netpgp->secring, fpr, len, &from, NULL, 0, 0 );
if(!signer) return PEP_KEY_NOT_FOUND;
rcpts = malloc(sizeof(pgp_keyring_t));
memset(rcpts,0,sizeof(pgp_keyring_t));
klp = keylist;
while(klp) {
string_to_uint(klp->value, &fpr, &len);
from = 0;
key = pgp_getkeybyfpr(netpgp->io, netpgp->pubring, fpr, len, &from, NULL, 0, 0 );
if(key) {
pgp_keyring_add(rcpts, key);
}
klp = klp->next;
}
mem = netpgp_encrypt_and_sign(netpgp, rcpts, seckey, ptext, psize, true, 1);
free(rcpts);
return result;
}
PEP_STATUS pgp_encrypt_only(
PEP_SESSION session, const stringlist_t *keylist, const char *ptext,
size_t psize, char **ctext, size_t *csize
)
{
TRACE_FUNCS()
PEP_STATUS result = PEP_STATUS_OK;
pgp_memory_t *mem;
pgp_keyring_t *rcpts = NULL;
unsigned from;
size_t len = 0;
uint8_t *fpr = NULL;
pgp_key_t *key = NULL;
stringlist_t *klp;
rcpts = malloc(sizeof(pgp_keyring_t));
memset(rcpts,0,sizeof(pgp_keyring_t));
klp = keylist;
while(klp) {
string_to_uint(klp->value, &fpr, &len);
from = 0;
key = pgp_getkeybyfpr(netpgp->io, netpgp->pubring, fpr, len, &from, NULL, 0, 0 );
if(key) {
pgp_keyring_add(rcpts, key);
}
klp = klp->next;
}
mem = netpgp_encrypt_and_sign(netpgp, rcpts, NULL, ptext, psize, false, 1);
free(rcpts);
return result;
}
PEP_STATUS pgp_generate_keypair(
PEP_SESSION session, pEp_identity *identity
)
{
TRACE_FUNCS()
pgp_key_t newseckey;
pgp_key_t *newpubkey;
PEP_STATUS result;
char newid[1024];
const char *hashalg;
const char *cipher;
assert(session);
assert(identity);
assert(identity->address);
assert(identity->fpr == NULL);
assert(identity->username);
if(!session || !identity ||
!identity->address || identity->fpr || !identity->username)
return PEP_ILLEGAL_VALUE;
if(pthread_mutex_lock(&netpgp_mutex)){
return PEP_UNKNOWN_ERROR;
}
if(snprintf(newid, sizeof(newid),
"%s <%s>", identity->username, identity->address) >= sizeof(newid)){
result = PEP_BUFFER_TOO_SMALL;
goto unlock_netpgp;
}
hashalg = netpgp_getvar(netpgp, "hash");
cipher = netpgp_getvar(netpgp, "cipher");
bzero(&newseckey, sizeof(newseckey));
// Generate the key
if (!pgp_rsa_generate_keypair(&newseckey, 4096, 65537UL, hashalg, cipher,
(const uint8_t *) "", (const size_t) 0))
{
result = PEP_CANNOT_CREATE_KEY;
goto free_seckey;
}
/* make a public key out of generated secret key */
if((newpubkey = pgp_ensure_pubkey(
netpgp->pubring,
&newseckey.key.seckey.pubkey,
newseckey.pubkeyid))==NULL)
{
result = PEP_OUT_OF_MEMORY;
goto free_seckey;
}
// "Expire-Date: 1y\n";
if (!pgp_add_selfsigned_userid(&newseckey, newpubkey,
(uint8_t *)newid, 365*24*3600))
{
result = PEP_CANNOT_CREATE_KEY;
goto delete_pubkey;
}
if (newpubkey == NULL)
{
result = PEP_OUT_OF_MEMORY;
goto delete_pubkey;
}
// Append key to netpgp's rings (key ownership transfered)
if (!pgp_keyring_add(netpgp->secring, &newseckey)){
result = PEP_OUT_OF_MEMORY;
goto delete_pubkey;
}
// save rings
if (netpgp_save_pubring(netpgp) && netpgp_save_secring(netpgp))
{
char *fprstr = NULL;
uint_to_string(newseckey.pubkeyfpr.fingerprint, &fprstr, newseckey.pubkeyfpr.length);
if (fprstr == NULL) {
result = PEP_OUT_OF_MEMORY;
goto pop_secring;
}
/* keys saved, pass fingerprint back */
identity->fpr = fprstr;
result = PEP_STATUS_OK;
/* free nothing, everything transfered */
goto unlock_netpgp;
} else {
/* XXX in case only pubring save succeed
* pubring file is left as-is, but backup restore
* could be attempted if such corner case matters */
result = PEP_UNKNOWN_ERROR;
}
pop_secring:
((pgp_keyring_t *)netpgp->secring)->keyc--;
delete_pubkey:
pgp_deletekeybyfpr(netpgp->io,
(pgp_keyring_t *)netpgp->pubring,
newseckey.pubkeyfpr.fingerprint,
newseckey.pubkeyfpr.length);
free_seckey:
pgp_key_free(&newseckey);
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return result;
}
PEP_STATUS pgp_delete_keypair(PEP_SESSION session, const char *fprstr)
{
TRACE_FUNCS()
uint8_t *fpr;
size_t length;
PEP_STATUS result;
assert(session);
assert(fprstr);
if (!session || !fprstr)
return PEP_ILLEGAL_VALUE;
if(pthread_mutex_lock(&netpgp_mutex)){
return PEP_UNKNOWN_ERROR;
}
if (string_to_uint(fprstr, &fpr, &length)) {
unsigned insec = pgp_deletekeybyfpr(netpgp->io,
(pgp_keyring_t *)netpgp->secring,
(const uint8_t *)fpr, length);
unsigned inpub = pgp_deletekeybyfpr(netpgp->io,
(pgp_keyring_t *)netpgp->pubring,
(const uint8_t *)fpr, length);
if(!insec && !inpub){
result = PEP_KEY_NOT_FOUND;
printf("%s:%d, Key not found\n",__FILE__,__LINE__);
goto unlock_netpgp;
} else {
result = PEP_STATUS_OK;
}
}else{
result = PEP_OUT_OF_MEMORY;
goto unlock_netpgp;
}
// save rings
if (netpgp_save_pubring(netpgp) &&
netpgp_save_secring(netpgp))
{
result = PEP_STATUS_OK;
}else{
result = PEP_UNKNOWN_ERROR;
}
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return result;
}
void stringlist_from_keyring(pgp_keyring_t* keyring, stringlist_t **list)
{
TRACE_FUNCS()
int i;
char *fpr;
pgp_key_t key;
for(i = 0; i < keyring->keyc; i++) {
key = keyring->keys[i];
if(uint_to_string(key.pubkeyfpr.fingerprint, &fpr, PGP_FINGERPRINT_SIZE)) {
if(!*list) *list = new_stringlist(fpr);
else stringlist_add(*list, fpr);
}
}
}
pEp_identity *ident_from_uid_fpr(char *uid, uint8_t *fpr)
{
char *address;
char *user_id;
char *username;
pEp_identity *ident;
ident = malloc(sizeof(pEp_identity));
ident->fpr = NULL;
uint_to_string(fpr, &ident->fpr, PGP_FINGERPRINT_SIZE);
printf("FPR: %s\n",ident->fpr);
username=strtok(uid, "<");
if(username[strlen(username)-1]==' ') username[strlen(username)-1]=0;
ident->username=malloc(strlen(username));
strcpy(ident->username,username);
printf("User name: %s\n",ident->username);
address=strtok(NULL, ">");
ident->address=malloc(strlen(address));
strcpy(ident->address,address);
printf("Address: %s\n",ident->address);
user_id=strtok(address, "@");
ident->user_id=malloc(strlen(user_id));
strcpy(ident->user_id,user_id);
printf("User ID: %s\n",ident->address);
return ident;
}
identity_list *add_idents_from_keyring(pgp_keyring_t* keyring, identity_list *list)
{
TRACE_FUNCS()
int i, j;
pgp_key_t key;
char *uid;
pEp_identity *ident;
for(i = 0; i < keyring->keyc; i++ ) {
key = keyring->keys[i];
for(j = 0; j < key.uidc; j++) {
uid = strdup(key.uids[j]);
ident = ident_from_uid_fpr(uid, key.pubkeyfpr.fingerprint);
list = identity_list_add(list, ident);
}
}
return list;
}
#define ARMOR_KEY_HEAD "^-----BEGIN PGP (PUBLIC|PRIVATE) KEY BLOCK-----"_ENDL
PEP_STATUS pgp_import_keydata(PEP_SESSION session, const char *key_data,
size_t size, identity_list **private_idents,
stringlist_t** imported_keys,
uint64_t* changed_key_index)
{
TRACE_FUNCS()
int ret;
PEP_STATUS result = PEP_STATUS_OK;
char* passphrase = NULL;
if (!imported_keys && changed_key_index)
return PEP_ILLEGAL_VALUE;
stringlist_t* key_fprs = NULL;
pgp_memory_t *mem = NULL;
assert(session);
assert(key_data);
passphrase = session->curr_passphrase;
if(passphrase && passphrase[0]) {
netpgp_set_validation_password(passphrase);
}
//pgp_set_debug_level("validate.c");
//pgp_set_debug_level("reader.c");
if(!session || !key_data)
return PEP_ILLEGAL_VALUE;
if(pthread_mutex_lock(&netpgp_mutex)){
return PEP_UNKNOWN_ERROR;
}
mem = pgp_memory_new();
if (mem == NULL) {
result = PEP_OUT_OF_MEMORY;
goto unlock_netpgp;
}
pgp_memory_add(mem, (const uint8_t*)key_data, size);
ret = pgp_keyring_read_from_mem(netpgp, netpgp->pubring, netpgp->secring, _armoured(key_data, size, ARMOR_KEY_HEAD), mem, 1);
switch(ret) {
case PGP_PASSWORD_REQUIRED:
printf("Password required\n");
result=PEP_PASSPHRASE_REQUIRED;
goto unlock_netpgp;
break;
case PGP_WRONG_PASSWORD:
printf("Wrong password\n");
result=PEP_WRONG_PASSPHRASE;
goto unlock_netpgp;
break;
default:
result=PEP_KEY_IMPORTED;
if(changed_key_index) {
(*changed_key_index)=0;
if (netpgp->pubring->keyc) {
(*changed_key_index)=pow(2,netpgp->pubring->keyc)-1;
}
//(*changed_key_index)=pow(2,netpgp->secring->keyc+netpgp->pubring->keyc)-1;
}
break;
}
pgp_memory_free(mem);
if(private_idents) {
printf("Return private indents\n");
(*private_idents) = NULL;
(*private_idents) = add_idents_from_keyring(netpgp->secring, (*private_idents));
//(*private_idents) = add_idents_from_keyring(netpgp->pubring, (*private_idents));
}
if(imported_keys) {
printf("Return list of imported keys\n");
stringlist_from_keyring(netpgp->pubring, imported_keys);
}
netpgp_save_pubring(netpgp);
netpgp_save_secring(netpgp);
// save rings
//if ( !netpgp_save_pubring(netpgp) || !netpgp_save_secring(netpgp) )
//{
// result = PEP_UNKNOWN_ERROR;
//}
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return result;
}
static PEP_STATUS _export_keydata(
pgp_key_t *key,
char **buffer,
size_t *buflen
)
{
TRACE_FUNCS()
PEP_STATUS result;
pgp_output_t *output;
pgp_memory_t *mem;
pgp_setup_memory_write(&output, &mem, 128);
if (mem == NULL || output == NULL) {
return PEP_ILLEGAL_VALUE;
}
if (!pgp_write_xfer_key(output, key, 1)) {
result = PEP_UNKNOWN_ERROR;
goto free_mem;
}
*buffer = NULL;
*buflen = pgp_mem_len(mem);
// Allocate transferable buffer
*buffer = malloc(*buflen + 1);
assert(*buffer);
if (*buffer == NULL) {
result = PEP_OUT_OF_MEMORY;
goto free_mem;
}
memcpy(*buffer, pgp_mem_data(mem), *buflen);
(*buffer)[*buflen] = 0; // safeguard for naive users
return PEP_STATUS_OK;
free_mem :
pgp_teardown_memory_write(output, mem);
return result;
}
PEP_STATUS pgp_export_keydata(
PEP_SESSION session, const char *fprstr, char **key_data, size_t *size,
bool secret
)
{
TRACE_FUNCS()
pgp_key_t *key;
uint8_t *fpr = NULL;
size_t fprlen;
PEP_STATUS result;
char *buffer;
size_t buflen;
const pgp_keyring_t *srcring;
assert(session);
assert(fprstr);
assert(key_data);
assert(size);
if (secret)
srcring = netpgp->secring;
else
srcring = netpgp->pubring;
if (!session || !fprstr || !key_data || !size)
return PEP_ILLEGAL_VALUE;
if(pthread_mutex_lock(&netpgp_mutex)) {
return PEP_UNKNOWN_ERROR;
}
if (string_to_uint(fprstr, &fpr, &fprlen)) {
unsigned from = 0;
if ((key = (pgp_key_t *)pgp_getkeybyfpr(netpgp->io, srcring, fpr, fprlen, &from, NULL,0,0)) == NULL) {
result = PEP_KEY_NOT_FOUND;
printf("%s:%d, Key not found\n",__FILE__,__LINE__);
goto unlock_netpgp;
}
}else{
result = PEP_OUT_OF_MEMORY;
goto unlock_netpgp;
}
result = _export_keydata(key, &buffer, &buflen);
if(result == PEP_STATUS_OK)
{
*key_data = buffer;
*size = buflen;
result = PEP_STATUS_OK;
}
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return result;
}
struct HKP_answer {
char *memory;
size_t size;
};
PEP_STATUS pgp_recv_key(PEP_SESSION session, const char *pattern)
{
TRACE_FUNCS()
assert(!"pgp_recv_key not implemented");
return PEP_UNKNOWN_ERROR;
}
typedef PEP_STATUS (*find_key_cb_t)(void*, pgp_key_t *);
static PEP_STATUS find_keys_do(pgp_keyring_t* keyring,
const char *pattern, find_key_cb_t cb, void* cb_arg)
{
TRACE_FUNCS()
uint8_t *fpr = NULL;
size_t length;
unsigned from;
pgp_key_t *key;
PEP_STATUS result;
// Try find a fingerprint in pattern
if (string_to_uint(pattern, &fpr, &length)) {
// Only one fingerprint can match
from = 0;
key = pgp_getkeybyfpr(netpgp->io, keyring, fpr, length, &from, NULL, 0, 0);
if ( key == NULL) {
return PEP_KEY_NOT_FOUND;
}
result = cb(cb_arg, key);
} else {
TRACE_FUNCS()
// Search by name for pattern. Can match many.
from = 0;
result = PEP_KEY_NOT_FOUND;
printf("%s:%d, Key not found\n",__FILE__,__LINE__);
while((key = (pgp_key_t *)pgp_getnextkeybyname(netpgp->io, keyring, (const char *)pattern, &from)) != NULL) {
result = cb(cb_arg, key);
if (result != PEP_STATUS_OK)
break;
from++;
}
}
return result;
}
static PEP_STATUS add_key_uint_to_stringinglist(void *arg, pgp_key_t *key)
{
TRACE_FUNCS()
stringlist_t **keylist = arg;
char *newfprstr = NULL;
uint_to_string(key->pubkeyfpr.fingerprint, &newfprstr, key->pubkeyfpr.length);
if (newfprstr == NULL) {
return PEP_OUT_OF_MEMORY;
} else {
stringlist_add(*keylist, newfprstr);
free(newfprstr);
if (*keylist == NULL) {
return PEP_OUT_OF_MEMORY;
}
}
return PEP_STATUS_OK;
}
static PEP_STATUS add_secret_key_uint_to_stringinglist(void *arg, pgp_key_t *key)
{
TRACE_FUNCS()
if (pgp_is_key_secret(key)) {
stringlist_t **keylist = arg;
char *newfprstr = NULL;
uint_to_string(key->pubkeyfpr.fingerprint, &newfprstr, key->pubkeyfpr.length);
if (newfprstr == NULL) {
return PEP_OUT_OF_MEMORY;
} else {
stringlist_add(*keylist, newfprstr);
free(newfprstr);
if (*keylist == NULL) {
return PEP_OUT_OF_MEMORY;
}
}
}
return PEP_STATUS_OK;
}
static PEP_STATUS add_keyinfo_to_stringpair_list(void* arg, pgp_key_t *key) {
TRACE_FUNCS()
stringpair_list_t** keyinfo_list = (stringpair_list_t**)arg;
stringpair_t* pair = NULL;
char* id_fpr = NULL;
char* primary_userid = (char*)pgp_key_get_primary_userid(key);
// Unused:
// bool key_revoked = false;
// PEP_STATUS key_status = pgp_key_revoked(session, id_fpr, &key_revoked);
// if (key_revoked || key_status == PEP_GET_KEY_FAILED)
// return PEP_STATUS_OK; // we just move on
uint_to_string(key->pubkeyfpr.fingerprint, &id_fpr, key->pubkeyfpr.length);
pair = new_stringpair(id_fpr, primary_userid);
if (pair == NULL)
return PEP_OUT_OF_MEMORY;
*keyinfo_list = stringpair_list_add(*keyinfo_list, pair);
free(id_fpr);
if (*keyinfo_list == NULL)
return PEP_OUT_OF_MEMORY;
return PEP_STATUS_OK;
}
PEP_STATUS pgp_find_keys(
PEP_SESSION session, const char *pattern, stringlist_t **keylist
)
{
TRACE_FUNCS()
stringlist_t *_keylist, *_k;
PEP_STATUS result;
assert(session);
assert(pattern);
assert(keylist);
if (!session || !pattern || !keylist )
{
return PEP_ILLEGAL_VALUE;
}
if (pthread_mutex_lock(&netpgp_mutex))
{
return PEP_UNKNOWN_ERROR;
}
*keylist = NULL;
_keylist = new_stringlist(NULL);
if (_keylist == NULL) {
result = PEP_OUT_OF_MEMORY;
goto unlock_netpgp;
}
_k = _keylist;
result = find_keys_do(netpgp->pubring, pattern, &add_key_uint_to_stringinglist, &_k);
if (result == PEP_STATUS_OK) {
*keylist = _keylist;
// Transfer ownership, no free
goto unlock_netpgp;
}
free_stringlist(_keylist);
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return result;
}
#define HKP_REQ_PREFIX "keytext="
#define HKP_REQ_PREFIX_LEN 8
PEP_STATUS pgp_send_key(PEP_SESSION session, const char *pattern)
{
TRACE_FUNCS()
assert(!"pgp_send_key not implemented");
return PEP_UNKNOWN_ERROR;
}
PEP_STATUS pgp_get_key_rating(
PEP_SESSION session, const char *fprstr, PEP_comm_type *comm_type)
{
TRACE_FUNCS()
pgp_key_t *key;
uint8_t *fpr = NULL;
unsigned from = 0;
size_t length;
int rating;
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
assert(fprstr);
assert(comm_type);
if (!session || !fprstr || !comm_type )
return PEP_ILLEGAL_VALUE;
*comm_type = PEP_ct_unknown;
if(pthread_mutex_lock(&netpgp_mutex)){
return PEP_UNKNOWN_ERROR;
}
if (!string_to_uint(fprstr, &fpr, &length)) {
status = PEP_ILLEGAL_VALUE;
goto unlock_netpgp;
}
key = pgp_getkeybyfpr(netpgp->io, netpgp->pubring, fpr, length, &from, NULL, 0, 0);
if(key == NULL)
{
status = PEP_KEY_NOT_FOUND;
goto unlock_netpgp;
}
rating = pgp_key_get_rating(key);
switch(rating){
case PGP_VALID:
*comm_type = PEP_ct_OpenPGP_unconfirmed;
break;
case PGP_WEAK:
*comm_type = PEP_ct_OpenPGP_weak_unconfirmed;
break;
case PGP_TOOSHORT:
*comm_type = PEP_ct_key_too_short;
break;
case PGP_INVALID:
*comm_type = PEP_ct_key_b0rken;
break;
case PGP_EXPIRED:
*comm_type = PEP_ct_key_expired;
break;
case PGP_REVOKED:
*comm_type = PEP_ct_key_revoked;
break;
default:
break;
}
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return status;
}
PEP_STATUS pgp_renew_key(
PEP_SESSION session,
const char *fprstr,
const timestamp *ts
)
{
TRACE_FUNCS()
pgp_key_t *pkey;
pgp_key_t *skey;
uint8_t fpr[PGP_FINGERPRINT_SIZE];
size_t length;
unsigned from = 0;
time_t duration;
const uint8_t *primid;
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
assert(fprstr);
if (!session || !fprstr )
return PEP_ILLEGAL_VALUE;
if(ts)
{
time_t now, when;
now = time(NULL);
when = mktime((struct tm*)ts);
if(now && when && when > now){
duration = when - now;
}else{
return PEP_ILLEGAL_VALUE;
}
}else{
/* Default 1 year from now */
duration = 365*24*3600;
}
if(pthread_mutex_lock(&netpgp_mutex)){
return PEP_UNKNOWN_ERROR;
}
if (!string_to_uint(fprstr, &fpr, &length)) {
status = PEP_ILLEGAL_VALUE;
goto unlock_netpgp;
}
pkey = pgp_getkeybyfpr(netpgp->io, netpgp->pubring, fpr, length, &from, NULL, 1, 0); /* reject revoked, accept expired */
if(pkey == NULL)
{
status = PEP_KEY_NOT_FOUND;
printf("%s:%d, Key not found\n",__FILE__,__LINE__);
goto unlock_netpgp;
}
from = 0;
skey = pgp_getkeybyfpr( netpgp->io, netpgp->secring, fpr, length, &from, NULL, 1, 0); /* reject revoked, accept expired */
if(skey == NULL)
{
status = PEP_KEY_NOT_FOUND;
printf("%s:%d, Key not found\n",__FILE__,__LINE__);
goto unlock_netpgp;
}
if((primid = pgp_key_get_primary_userid(skey)) == NULL)
{
status = PEP_KEY_HAS_AMBIG_NAME;
goto unlock_netpgp;
}
// FIXME : renew in a more gentle way
if (!pgp_add_selfsigned_userid(skey, pkey, primid, duration))
{
status = PEP_CANNOT_CREATE_KEY;
goto unlock_netpgp;
}
// save rings
if (netpgp_save_pubring(netpgp) &&
netpgp_save_secring(netpgp))
{
status = PEP_STATUS_OK;
}else{
status = PEP_UNKNOWN_ERROR;
}
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return status;
}
PEP_STATUS pgp_revoke_key(
PEP_SESSION session,
const char *fprstr,
const char *reason
)
{
TRACE_FUNCS()
uint8_t *fpr = NULL;
size_t length;
unsigned from = 0;
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
assert(fprstr);
if (!session || !fprstr)
return PEP_UNKNOWN_ERROR;
if(pthread_mutex_lock(&netpgp_mutex)){
return PEP_UNKNOWN_ERROR;
}
// FIXME : deduplicate that code w/ renew
if (!string_to_uint(fprstr, &fpr, &length)) {
status = PEP_ILLEGAL_VALUE;
goto unlock_netpgp;
}
pgp_key_t *pkey = pgp_getkeybyfpr( netpgp->io, netpgp->pubring, fpr, length, &from, NULL, 1, 0); /* reject revoked, accept expired */
if(pkey == NULL)
{
status = PEP_KEY_NOT_FOUND;
printf("%s:%d, Key not found\n",__FILE__,__LINE__);
goto unlock_netpgp;
}
from = 0;
pgp_key_t *skey = pgp_getkeybyfpr( netpgp->io, netpgp->secring, fpr, length, &from, NULL, 1, 0); /* reject revoked, accept expired */
if(skey == NULL)
{
status = PEP_KEY_NOT_FOUND;
printf("%s:%d, Key not found\n",__FILE__,__LINE__);
goto unlock_netpgp;
}
pgp_key_revoke(skey, pkey,
0, /* no reason code specified */
reason);
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return status;
}
PEP_STATUS pgp_key_expired(
PEP_SESSION session,
const char *fprstr,
const time_t when,
bool *expired
)
{
TRACE_FUNCS()
PEP_STATUS status = PEP_STATUS_OK;
PEP_comm_type comm_type;
assert(session);
assert(fprstr);
assert(expired);
if (!session || !fprstr || !expired)
return PEP_UNKNOWN_ERROR;
// TODO : take "when" in account
*expired = false;
status = pgp_get_key_rating(session, fprstr, &comm_type);
if (status != PEP_STATUS_OK)
return status;
if (comm_type == PEP_ct_key_expired){
*expired = true;
}
return PEP_STATUS_OK;
}
PEP_STATUS pgp_key_revoked(
PEP_SESSION session,
const char *fprstr,
bool *revoked
)
{
TRACE_FUNCS()
PEP_STATUS status = PEP_STATUS_OK;
PEP_comm_type comm_type;
assert(session);
assert(fprstr);
assert(revoked);
*revoked = false;
status = pgp_get_key_rating(session, fprstr, &comm_type);
if (status != PEP_STATUS_OK)
return status;
if (comm_type == PEP_ct_key_revoked){
*revoked = true;
}
return PEP_STATUS_OK;
}
PEP_STATUS pgp_key_created(
PEP_SESSION session,
const char *fprstr,
time_t *created
)
{
TRACE_FUNCS()
uint8_t *fpr;
pgp_key_t *key;
size_t length;
unsigned from = 0;
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
assert(fprstr);
assert(created);
if (!session || !fprstr || !created)
return PEP_UNKNOWN_ERROR;
*created = 0;
if(pthread_mutex_lock(&netpgp_mutex)){
return PEP_UNKNOWN_ERROR;
}
if (!string_to_uint(fprstr, &fpr, &length)) {
status = PEP_ILLEGAL_VALUE;
goto unlock_netpgp;
}
key = pgp_getkeybyfpr( netpgp->io, netpgp->pubring, fpr, length, &from, NULL,0,0);
if (key)
{
*created = (time_t) key->key.pubkey.birthtime;
}
else
{
status = PEP_KEY_NOT_FOUND;
printf("%s:%d, Key not found\n",__FILE__,__LINE__);
goto unlock_netpgp;
}
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return status;
}
PEP_STATUS pgp_list_keyinfo(
PEP_SESSION session, const char* pattern, stringpair_list_t** keyinfo_list)
{
TRACE_FUNCS()
if (!session || !keyinfo_list)
return PEP_UNKNOWN_ERROR;
if (pthread_mutex_lock(&netpgp_mutex))
{
return PEP_UNKNOWN_ERROR;
}
// Unused:
// pgp_key_t *key;
PEP_STATUS result;
result = find_keys_do(netpgp->pubring, pattern, &add_keyinfo_to_stringpair_list, (void*)keyinfo_list);
if (!keyinfo_list)
result = PEP_KEY_NOT_FOUND;
printf("%s:%d, Key not found\n",__FILE__,__LINE__);
pthread_mutex_unlock(&netpgp_mutex);
return result;
}
PEP_STATUS pgp_find_private_keys(
PEP_SESSION session, const char *pattern, stringlist_t **keylist
)
{
TRACE_FUNCS()
stringlist_t *_keylist, *_k;
PEP_STATUS result;
assert(session);
assert(keylist);
//if (!session || !keylist )
//{
// return PEP_ILLEGAL_VALUE;
//}
if (pthread_mutex_lock(&netpgp_mutex))
{
return PEP_UNKNOWN_ERROR;
}
*keylist = NULL;
_keylist = new_stringlist(NULL);
if (_keylist == NULL) {
result = PEP_OUT_OF_MEMORY;
goto unlock_netpgp;
}
_k = _keylist;
result = find_keys_do(netpgp->secring, pattern, &add_secret_key_uint_to_stringinglist, &_k);
if (result == PEP_STATUS_OK) {
*keylist = _keylist;
// Transfer ownership, no free
goto unlock_netpgp;
}
free_stringlist(_keylist);
unlock_netpgp:
pthread_mutex_unlock(&netpgp_mutex);
return result;
}
PEP_STATUS pgp_contains_priv_key(
PEP_SESSION session,
const char *fpr,
bool *has_private)
{
TRACE_FUNCS()
stringlist_t* keylist = NULL;
PEP_STATUS status = pgp_find_private_keys(session, fpr, &keylist);
if (status == PEP_STATUS_OK && keylist) {
free_stringlist(keylist);
*has_private = true;
}
else {
*has_private = false;
}
return status;
}
PEP_STATUS pgp_import_ultimately_trusted_keypairs(PEP_SESSION session) {
// Not implemented - netpgp doesn't appear to keep track of trust status in
// a meaningful way, though there is space for it in the structs.
TRACE_FUNCS()
return PEP_STATUS_OK;
}
PEP_STATUS pgp_config_cipher_suite(PEP_SESSION session, PEP_CIPHER_SUITE suite) {
TRACE_FUNCS()
if (suite == PEP_CIPHER_SUITE_DEFAULT) {
return PEP_STATUS_OK;
} else {
return PEP_CANNOT_CONFIG;
}
}