pEpEngine/src/keymanagement.c

556 lines
16 KiB
C
Raw Normal View History

2015-03-26 15:30:21 +01:00
#include "platform.h"
2014-06-25 18:44:58 +02:00
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
2014-06-25 18:44:58 +02:00
2015-04-21 15:43:28 +02:00
#include "pEp_internal.h"
2014-06-25 18:44:58 +02:00
#include "keymanagement.h"
#ifndef EMPTYSTR
#define EMPTYSTR(STR) ((STR) == NULL || (STR)[0] == '\0')
2014-07-11 17:43:11 +02:00
#endif
#define KEY_EXPIRE_DELTA (60 * 60 * 24 * 365)
// Space tolerant and case insensitive fingerprint string compare
static int _same_fpr(
const char* fpra,
size_t fpras,
const char* fprb,
size_t fprbs
)
{
size_t ai = 0;
size_t bi = 0;
do
{
if(fpra[ai] == 0 || fprb[bi] == 0)
{
return 0;
}
else if(fpra[ai] == ' ')
{
ai++;
}
else if(fprb[bi] == ' ')
{
bi++;
}
else if(toupper(fpra[ai]) == toupper(fprb[bi]))
{
ai++;
bi++;
}
else
{
return 0;
}
}
while(ai < fpras && bi < fprbs);
return ai == fpras && bi == fprbs;
}
2014-06-25 18:44:58 +02:00
DYNAMIC_API PEP_STATUS update_identity(
PEP_SESSION session, pEp_identity * identity
)
{
pEp_identity *stored_identity;
PEP_STATUS status;
assert(session);
assert(identity);
assert(!EMPTYSTR(identity->address));
2014-06-25 18:44:58 +02:00
if (!(session && identity && !EMPTYSTR(identity->address)))
2015-04-15 12:53:50 +02:00
return PEP_ILLEGAL_VALUE;
int _no_user_id = EMPTYSTR(identity->user_id);
if (_no_user_id)
{
free(identity->user_id);
status = get_best_user(session,
identity->address,
&identity->user_id);
// Default user_id, aka Virtual user_id
if (status == PEP_CANNOT_FIND_IDENTITY)
{
identity->user_id = calloc(1, identity->address_size + 5);
if (!identity->user_id)
{
return PEP_OUT_OF_MEMORY;
}
snprintf(identity->user_id, identity->address_size + 5,
"TOFU_%s", identity->address);
}
else if (status != PEP_STATUS_OK)
{
return status;
}
if(identity->user_id)
{
identity->user_id_size = strlen(identity->user_id);
}
}
status = get_identity(session,
identity->address,
identity->user_id,
&stored_identity);
2014-06-25 18:44:58 +02:00
assert(status != PEP_OUT_OF_MEMORY);
if (status == PEP_OUT_OF_MEMORY)
return PEP_OUT_OF_MEMORY;
if (stored_identity) {
2014-08-05 20:33:20 +02:00
PEP_comm_type _comm_type_key;
status = get_key_rating(session, stored_identity->fpr, &_comm_type_key);
assert(status != PEP_OUT_OF_MEMORY);
if (status == PEP_OUT_OF_MEMORY)
return PEP_OUT_OF_MEMORY;
if (EMPTYSTR(identity->username)) {
2014-08-20 20:20:07 +02:00
free(identity->username);
identity->username = strndup(stored_identity->username, stored_identity->username_size);
assert(identity->username);
2014-08-20 20:20:07 +02:00
if (identity->username == NULL)
return PEP_OUT_OF_MEMORY;
identity->username_size = stored_identity->username_size;
}
if (EMPTYSTR(identity->fpr)) {
identity->fpr = strndup(stored_identity->fpr, stored_identity->fpr_size);
2014-07-11 17:43:11 +02:00
assert(identity->fpr);
if (identity->fpr == NULL)
return PEP_OUT_OF_MEMORY;
identity->fpr_size = stored_identity->fpr_size;
2014-08-05 20:33:20 +02:00
if (_comm_type_key < PEP_ct_unconfirmed_encryption) {
identity->comm_type = _comm_type_key;
}
else {
identity->comm_type = stored_identity->comm_type;
}
2014-07-11 17:43:11 +02:00
}
else /* !EMPTYSTR(identity->fpr) */ {
if (_same_fpr(identity->fpr,
identity->fpr_size,
stored_identity->fpr,
stored_identity->fpr_size)) {
2014-08-05 20:33:20 +02:00
if (_comm_type_key < PEP_ct_unconfirmed_encryption) {
identity->comm_type = _comm_type_key;
}else{
identity->comm_type = stored_identity->comm_type;
if (identity->comm_type == PEP_ct_unknown) {
identity->comm_type = _comm_type_key;
2014-07-11 17:43:11 +02:00
}
}
2016-03-03 19:33:46 +01:00
} else {
status = get_trust(session, identity);
assert(status != PEP_OUT_OF_MEMORY);
if (status == PEP_OUT_OF_MEMORY)
return PEP_OUT_OF_MEMORY;
2016-03-03 19:33:46 +01:00
if (identity->comm_type < stored_identity->comm_type)
identity->comm_type = PEP_ct_unknown;
2014-07-11 17:43:11 +02:00
}
}
if (identity->lang[0] == 0) {
identity->lang[0] = stored_identity->lang[0];
identity->lang[1] = stored_identity->lang[1];
identity->lang[2] = 0;
}
}
else /* stored_identity == NULL */ {
if (!EMPTYSTR(identity->fpr)) {
PEP_comm_type _comm_type_key;
status = get_key_rating(session, identity->fpr, &_comm_type_key);
assert(status != PEP_OUT_OF_MEMORY);
if (status == PEP_OUT_OF_MEMORY)
return PEP_OUT_OF_MEMORY;
identity->comm_type = _comm_type_key;
}
else /* EMPTYSTR(identity->fpr) */ {
PEP_STATUS status;
stringlist_t *keylist;
char *_fpr = NULL;
identity->comm_type = PEP_ct_unknown;
status = find_keys(session, identity->address, &keylist);
assert(status != PEP_OUT_OF_MEMORY);
if (status == PEP_OUT_OF_MEMORY)
return PEP_OUT_OF_MEMORY;
stringlist_t *_keylist;
for (_keylist = keylist; _keylist && _keylist->value; _keylist = _keylist->next) {
PEP_comm_type _comm_type_key;
status = get_key_rating(session, _keylist->value, &_comm_type_key);
assert(status != PEP_OUT_OF_MEMORY);
if (status == PEP_OUT_OF_MEMORY) {
free_stringlist(keylist);
return PEP_OUT_OF_MEMORY;
}
if (identity->comm_type == PEP_ct_unknown) {
if (_comm_type_key != PEP_ct_compromized && _comm_type_key != PEP_ct_unknown) {
identity->comm_type = _comm_type_key;
_fpr = _keylist->value;
}
}
else {
if (_comm_type_key != PEP_ct_compromized && _comm_type_key != PEP_ct_unknown) {
if (_comm_type_key > identity->comm_type) {
identity->comm_type = _comm_type_key;
_fpr = _keylist->value;
}
}
}
}
if (_fpr) {
free(identity->fpr);
identity->fpr = strdup(_fpr);
if (identity->fpr == NULL) {
free_stringlist(keylist);
return PEP_OUT_OF_MEMORY;
}
identity->fpr_size = strlen(identity->fpr);
}
free_stringlist(keylist);
}
2014-06-25 18:44:58 +02:00
}
2014-07-11 17:43:11 +02:00
status = PEP_STATUS_OK;
2014-06-25 18:44:58 +02:00
if (identity->comm_type != PEP_ct_unknown && !EMPTYSTR(identity->user_id)) {
assert(!EMPTYSTR(identity->username)); // this should not happen
2014-08-20 20:20:07 +02:00
if (EMPTYSTR(identity->username)) { // mitigate
free(identity->username);
2014-08-20 20:20:07 +02:00
identity->username = strdup("anonymous");
if (identity->username == NULL)
return PEP_OUT_OF_MEMORY;
2014-08-20 20:20:07 +02:00
identity->username_size = 9;
}
// Identity doesn't get stored if is was just about checking existing
// user by address (i.e. no user id but already stored)
if (!(_no_user_id && stored_identity))
{
status = set_identity(session, identity);
assert(status == PEP_STATUS_OK);
if (status != PEP_STATUS_OK) {
return status;
}
}
2014-07-11 17:43:11 +02:00
}
2015-05-30 00:29:53 +02:00
if (identity->comm_type != PEP_ct_compromized &&
identity->comm_type < PEP_ct_strong_but_unconfirmed)
if (session->examine_identity)
session->examine_identity(identity, session->examine_management);
2014-07-11 17:43:11 +02:00
return status;
2014-06-25 18:44:58 +02:00
}
DYNAMIC_API PEP_STATUS myself(PEP_SESSION session, pEp_identity * identity)
{
pEp_identity *stored_identity;
2014-06-25 18:44:58 +02:00
PEP_STATUS status;
2015-01-24 15:26:40 +01:00
stringlist_t *keylist = NULL;
2014-06-25 18:44:58 +02:00
assert(session);
assert(identity);
assert(identity->address);
assert(identity->username);
assert(identity->user_id);
2015-04-15 12:53:50 +02:00
if (!(session && identity && identity->address && identity->username &&
identity->user_id))
return PEP_ILLEGAL_VALUE;
2014-06-25 18:44:58 +02:00
identity->comm_type = PEP_ct_pEp;
identity->me = true;
2015-04-21 15:40:18 +02:00
DEBUG_LOG("myself", "debug", identity->address);
2014-06-25 18:44:58 +02:00
status = get_identity(session,
identity->address,
identity->user_id,
&stored_identity);
2014-06-25 18:44:58 +02:00
assert(status != PEP_OUT_OF_MEMORY);
if (status == PEP_OUT_OF_MEMORY)
return PEP_OUT_OF_MEMORY;
if (stored_identity)
{
if (EMPTYSTR(identity->fpr)) {
identity->fpr = strndup(stored_identity->fpr, stored_identity->fpr_size);
assert(identity->fpr);
if (identity->fpr == NULL)
{
return PEP_OUT_OF_MEMORY;
}
identity->fpr_size = stored_identity->fpr_size;
}
}
else
{
free(identity->fpr);
identity->fpr_size = 0;
status = find_keys(session, identity->address, &keylist);
assert(status != PEP_OUT_OF_MEMORY);
if (status == PEP_OUT_OF_MEMORY)
return PEP_OUT_OF_MEMORY;
if (keylist != NULL && keylist->value != NULL)
{
// BUG : Vulnerable to auto-key-import poisoning.
// Attacker's key with forged userId could have been
// auto imported from already received email and be used here
// TODO : iterate over list to elect best key
// TODO : discard keys which aren't private
// TODO : discard keys which aren't either
// - own generated key
// - own from synchronized device group
// - already fully trusted as a public key of known
// identity, for that same address
// (case of imported key for mailing lists)
identity->fpr = strdup(keylist->value);
assert(identity->fpr);
if (identity->fpr == NULL)
{
return PEP_OUT_OF_MEMORY;
}
identity->fpr_size = strlen(identity->fpr);
}
}
// TODO : Check key for revoked state
if (EMPTYSTR(identity->fpr) /* or revoked */)
{
2015-04-21 15:40:18 +02:00
DEBUG_LOG("generating key pair", "debug", identity->address);
2014-06-25 18:44:58 +02:00
status = generate_keypair(session, identity);
assert(status != PEP_OUT_OF_MEMORY);
if (status != PEP_STATUS_OK) {
char buf[11];
snprintf(buf, 11, "%d", status);
2015-04-21 15:40:18 +02:00
DEBUG_LOG("generating key pair failed", "debug", buf);
2014-06-25 18:44:58 +02:00
return status;
}
2014-06-25 18:44:58 +02:00
status = find_keys(session, identity->address, &keylist);
assert(status != PEP_OUT_OF_MEMORY);
if (status == PEP_OUT_OF_MEMORY)
return PEP_OUT_OF_MEMORY;
assert(keylist && keylist->value);
if (keylist == NULL || keylist->value == NULL) {
return PEP_UNKNOWN_ERROR;
}
2014-06-25 18:44:58 +02:00
}
else
{
bool expired;
status = key_expired(session, identity->fpr, &expired);
assert(status == PEP_STATUS_OK);
if (status != PEP_STATUS_OK) {
goto free_keylist;
}
if (status == PEP_STATUS_OK && expired) {
timestamp *ts = new_timestamp(time(NULL) + KEY_EXPIRE_DELTA);
renew_key(session, identity->fpr, ts);
free_timestamp(ts);
}
}
2014-06-25 18:44:58 +02:00
status = set_identity(session, identity);
assert(status == PEP_STATUS_OK);
if (status != PEP_STATUS_OK) {
goto free_keylist;
}
2014-06-25 18:44:58 +02:00
return PEP_STATUS_OK;
free_keylist:
free_stringlist(keylist);
return status;
2014-06-25 18:44:58 +02:00
}
2015-05-30 00:19:39 +02:00
DYNAMIC_API PEP_STATUS register_examine_function(
2015-05-29 23:59:04 +02:00
PEP_SESSION session,
examine_identity_t examine_identity,
void *management
)
{
assert(session);
if (!session)
return PEP_ILLEGAL_VALUE;
session->examine_management = management;
session->examine_identity = examine_identity;
return PEP_STATUS_OK;
}
2014-06-25 18:44:58 +02:00
DYNAMIC_API PEP_STATUS do_keymanagement(
retrieve_next_identity_t retrieve_next_identity,
void *management
)
{
PEP_SESSION session;
pEp_identity *identity;
PEP_STATUS status;
2014-06-25 18:44:58 +02:00
assert(retrieve_next_identity);
assert(management);
if (!retrieve_next_identity || !management)
return PEP_ILLEGAL_VALUE;
status = init(&session);
2014-06-25 18:44:58 +02:00
assert(status == PEP_STATUS_OK);
if (status != PEP_STATUS_OK)
return status;
log_event(session, "keymanagement thread started", "pEp engine", NULL, NULL);
while ((identity = retrieve_next_identity(management)))
{
2014-06-25 18:44:58 +02:00
assert(identity->address);
if(identity->address)
{
DEBUG_LOG("do_keymanagement", "retrieve_next_identity", identity->address);
if (identity->me) {
status = myself(session, identity);
} else {
status = recv_key(session, identity->address);
}
2014-06-25 18:44:58 +02:00
assert(status != PEP_OUT_OF_MEMORY);
if(status == PEP_OUT_OF_MEMORY)
return PEP_OUT_OF_MEMORY;
2014-06-25 18:44:58 +02:00
}
free_identity(identity);
}
log_event(session, "keymanagement thread shutdown", "pEp engine", NULL, NULL);
release(session);
return PEP_STATUS_OK;
}
2015-09-28 15:06:07 +02:00
DYNAMIC_API PEP_STATUS key_compromized(
PEP_SESSION session,
pEp_identity *ident
)
2015-04-21 15:40:18 +02:00
{
2015-04-21 15:44:31 +02:00
PEP_STATUS status = PEP_STATUS_OK;
2015-04-21 15:40:18 +02:00
assert(session);
2015-09-28 15:06:07 +02:00
assert(ident);
assert(!EMPTYSTR(ident->fpr));
2015-04-21 15:40:18 +02:00
2015-09-28 15:06:07 +02:00
if (!(session && ident && ident->fpr))
2015-04-21 15:40:18 +02:00
return PEP_ILLEGAL_VALUE;
2015-09-28 15:06:07 +02:00
if (ident->me)
revoke_key(session, ident->fpr, NULL);
status = mark_as_compromized(session, ident->fpr);
2015-04-21 15:44:31 +02:00
return status;
2015-04-21 15:40:18 +02:00
}
DYNAMIC_API PEP_STATUS key_reset_trust(
PEP_SESSION session,
pEp_identity *ident
)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
assert(ident);
assert(!ident->me);
assert(!EMPTYSTR(ident->fpr));
assert(!EMPTYSTR(ident->address));
assert(!EMPTYSTR(ident->user_id));
if (!(session && ident && !ident->me && ident->fpr && ident->address &&
ident->user_id))
return PEP_ILLEGAL_VALUE;
status = update_identity(session, ident);
if (status != PEP_STATUS_OK)
return status;
if (ident->comm_type == PEP_ct_mistrusted)
ident->comm_type = PEP_ct_unknown;
else
ident->comm_type &= ~PEP_ct_confirmed;
status = set_identity(session, ident);
if (status != PEP_STATUS_OK)
return status;
2016-01-20 20:23:28 +01:00
if (ident->comm_type == PEP_ct_unknown)
status = update_identity(session, ident);
return status;
}
2015-09-28 11:08:26 +02:00
DYNAMIC_API PEP_STATUS trust_personal_key(
PEP_SESSION session,
pEp_identity *ident
)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
assert(ident);
assert(!EMPTYSTR(ident->address));
assert(!EMPTYSTR(ident->user_id));
assert(!EMPTYSTR(ident->fpr));
2015-09-28 11:08:26 +02:00
assert(!ident->me);
if (!ident || EMPTYSTR(ident->address) || EMPTYSTR(ident->user_id) ||
EMPTYSTR(ident->fpr) || ident->me)
2015-09-28 11:08:26 +02:00
return PEP_ILLEGAL_VALUE;
status = update_identity(session, ident);
if (status != PEP_STATUS_OK)
return status;
if (ident->comm_type > PEP_ct_strong_but_unconfirmed) {
ident->comm_type |= PEP_ct_confirmed;
status = set_identity(session, ident);
}
else {
// MISSING: S/MIME has to be handled depending on trusted CAs
2015-10-10 17:17:57 +02:00
status = PEP_CANNOT_SET_TRUST;
}
2015-09-28 11:08:26 +02:00
return status;
}