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/baseprotocol.c

284 lines
7.7 KiB
C

// This file is under GNU General Public License 3.0
// see LICENSE.txt
#include "pEp_internal.h"
#include "message_api.h"
#include "baseprotocol.h"
static const char *_base_type[] = {
"application/pEp.sign",
"application/pEp.sync",
"application/pEp.distribution"
};
PEP_STATUS base_decorate_message(
PEP_SESSION session,
message *msg,
base_protocol_type type,
char *payload,
size_t size,
const char *fpr
)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(msg);
assert(payload);
assert(size);
assert(type == BASE_SYNC || type == BASE_KEYRESET);
if (!(msg && payload && size && type))
return PEP_ILLEGAL_VALUE;
bloblist_t *bl;
switch (type) {
case BASE_SYNC:
bl = bloblist_add(msg->attachments, payload, size,
_base_type[type], "sync.pEp");
break;
case BASE_KEYRESET:
bl = bloblist_add(msg->attachments, payload, size,
_base_type[type], "distribution.pEp");
break;
default:
bl = bloblist_add(msg->attachments, payload, size,
_base_type[type], "ignore_this_attachment.pEp");
}
if (bl == NULL)
goto enomem;
else if (!msg->attachments)
msg->attachments = bl;
if (fpr && fpr[0] != '\0') {
char *sign;
size_t sign_size;
status = sign_only(session, payload, size, fpr, &sign, &sign_size);
if (status)
goto error;
assert(sign && sign_size);
bl = bloblist_add(bl, sign, sign_size,
_base_type[BASE_SIGN], "electronic_signature.asc");
if (!bl)
goto enomem;
}
return status;
enomem:
status = PEP_OUT_OF_MEMORY;
error:
return status;
}
PEP_STATUS base_prepare_message(
PEP_SESSION session,
const pEp_identity *me,
const pEp_identity *partner,
base_protocol_type type,
char *payload,
size_t size,
const char *fpr,
message **result
)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(me);
assert(partner);
assert(payload);
assert(size);
assert(result);
assert(type == BASE_SYNC || type == BASE_KEYRESET);
if (!(me && partner && payload && size && result && type))
return PEP_ILLEGAL_VALUE;
*result = NULL;
message *msg = new_message(PEP_dir_outgoing);
if (!msg)
goto enomem;
add_opt_field(msg, "pEp-auto-consume", "yes");
msg->in_reply_to = stringlist_add(msg->in_reply_to, "pEp-auto-consume@pEp.foundation");
msg->from = identity_dup(me);
if (!msg->from)
goto enomem;
msg->to = new_identity_list(identity_dup(partner));
if (!msg->to)
goto enomem;
msg->shortmsg = strdup("p≡p key management message - please ignore");
assert(msg->shortmsg);
if (!msg->shortmsg)
goto enomem;
msg->longmsg = strdup("This message is part of p≡p's concept to manage keys.\n\n"
"You can safely ignore it. It will be deleted automatically.\n");
assert(msg->longmsg);
if (!msg->longmsg)
goto enomem;
status = base_decorate_message(session, msg, type, payload, size, fpr);
if (status == PEP_STATUS_OK)
*result = msg;
return status;
enomem:
free_message(msg);
return PEP_OUT_OF_MEMORY;
}
PEP_STATUS base_extract_message(
PEP_SESSION session,
message *msg,
base_protocol_type type,
size_t *size,
const char **payload,
char **fpr
)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(session && msg && size && payload && fpr);
assert(type == BASE_SYNC || type == BASE_KEYRESET);
if (!(session && msg && size && payload && fpr && type))
return PEP_ILLEGAL_VALUE;
*size = 0;
*payload = NULL;
*fpr = NULL;
const char *_payload = NULL;
size_t _payload_size = 0;
const char *_sign = NULL;
size_t _sign_size = 0;
stringlist_t *keylist = NULL;
for (bloblist_t *bl = msg->attachments; bl ; bl = bl->next) {
if (bl->mime_type && strcasecmp(bl->mime_type, _base_type[type]) == 0) {
if (!_payload) {
_payload = bl->value;
_payload_size = bl->size;
}
else {
status = PEP_DECRYPT_WRONG_FORMAT;
goto the_end;
}
}
else if (bl->mime_type && strcasecmp(bl->mime_type, _base_type[BASE_SIGN]) == 0) {
if (!_sign) {
_sign = bl->value;
_sign_size = bl->size;
}
else {
status = PEP_DECRYPT_WRONG_FORMAT;
goto the_end;
}
}
}
if (!(_payload && _payload_size))
goto the_end;
char *_fpr = NULL;
if (_sign) {
status = verify_text(session, _payload, _payload_size, _sign, _sign_size, &keylist);
if (!(status == PEP_VERIFIED || status == PEP_VERIFIED_AND_TRUSTED) || !keylist || !keylist->value) {
// signature invalid or does not match; ignore message
status = PEP_STATUS_OK;
goto the_end;
}
_fpr = strdup(keylist->value);
assert(_fpr);
if (!_fpr) {
status = PEP_OUT_OF_MEMORY;
goto the_end;
}
}
*size = _payload_size;
*payload = _payload;
*fpr = _fpr;
status = PEP_STATUS_OK;
the_end:
free_stringlist(keylist);
return status;
}
PEP_STATUS try_base_prepare_message(
PEP_SESSION session,
const pEp_identity *me,
const pEp_identity *partner,
base_protocol_type type,
char *payload,
size_t size,
const char *fpr,
message **result
)
{
PEP_STATUS status = PEP_STATUS_OK;
assert(session && session->messageToSend && session->notifyHandshake);
assert(me);
assert(partner);
assert(payload);
assert(size);
assert(result);
assert(type == BASE_SYNC || type == BASE_KEYRESET);
if (!(session && session->messageToSend && session->notifyHandshake))
return PEP_ILLEGAL_VALUE;
if (!(me && partner && payload && size && result && type))
return PEP_ILLEGAL_VALUE;
// https://dev.pep.foundation/Engine/MessageToSendPassphrase
// first try with empty passphrase
char *passphrase = session->curr_passphrase;
session->curr_passphrase = NULL;
status = base_prepare_message(session, me, partner, type, payload, size, fpr, result);
session->curr_passphrase = passphrase;
if (!(status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE))
return status;
if (!EMPTYSTR(session->curr_passphrase)) {
// try configured passphrase
status = base_prepare_message(session, me, partner, type, payload, size, fpr, result);
if (!(status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE))
return status;
}
do {
// then try passphrases from the cache
status = session->messageToSend(NULL);
// if there will be no passphrase then exit
if (status == PEP_SYNC_NO_CHANNEL)
break;
// if a passphrase is needed ask the app
if (status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE) {
pEp_identity* _me = identity_dup(me);
if (!_me)
return PEP_OUT_OF_MEMORY;
session->notifyHandshake(_me, NULL, SYNC_PASSPHRASE_REQUIRED);
}
else if (status == PEP_STATUS_OK) {
status = base_prepare_message(session, me, partner, type, payload, size, fpr, result);
}
} while (status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE);
return status;
}