Replace use of Sequoia's backend with a custom key store.
- Sequoia's key store doesn't meet pep's needs (in particular, the
ability to search on a key's user id) and trying to shoehorn pep's
needs onto Sequoia's key store abstractions is just introducing
overhead with no appreciable gain in functionality.
- This patch changes the Sequoia backend to use a local sqlite
database to store the public keys.
1 // This file is under GNU General Public License 3.0
4 #include "pEp_internal.h"
5 #include "message_api.h"
10 #include "baseprotocol.h"
11 #include "KeySync_fsm.h"
20 // These are globals used in generating message IDs and should only be
21 // computed once, as they're either really constants or OS-dependent
23 int _pEp_rand_max_bits;
26 static bool is_a_pEpmessage(const message *msg)
28 for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
29 if (strcasecmp(i->value->key, "X-pEp-Version") == 0)
35 static bool is_wrapper(message* src)
40 unsigned char pEpstr[] = PEP_SUBJ_STRING;
41 if (is_a_pEpmessage(src) || (src->shortmsg == NULL || strcmp(src->shortmsg, "pEp") == 0 ||
42 _unsigned_signed_strcmp(pEpstr, src->shortmsg, PEP_SUBJ_BYTELEN) == 0) ||
43 (strcmp(src->shortmsg, "p=p") == 0)) {
44 char* plaintext = src->longmsg;
46 const char *line_end = strchr(plaintext, '\n');
48 if (line_end != NULL) {
49 size_t n = line_end - plaintext;
51 char* copycat = calloc(n + 1, 1);
54 strlcpy(copycat, plaintext, n+1);
56 if (strstr(copycat, PEP_MSG_WRAP_KEY) && strstr(copycat, "OUTER"))
70 * static stringpair_t* search_optfields(const message* msg, const char* key) {
72 * stringpair_list_t* opt_fields = msg->opt_fields;
74 * const stringpair_list_t* curr;
76 * for (curr = opt_fields; curr && curr->value; curr = curr->next) {
77 * if (curr->value->key) {
78 * if (strcasecmp(curr->value->key, key) == 0)
87 static char * keylist_to_string(const stringlist_t *keylist)
90 size_t size = stringlist_length(keylist);
92 const stringlist_t *_kl;
93 for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
94 size += strlen(_kl->value);
97 char *result = calloc(size, 1);
102 for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
103 _r = stpcpy(_r, _kl->value);
104 if (_kl->next && _kl->next->value)
105 _r = stpcpy(_r, ",");
115 static const char * rating_to_string(PEP_rating rating)
118 case PEP_rating_cannot_decrypt:
119 return "cannot_decrypt";
120 case PEP_rating_have_no_key:
121 return "have_no_key";
122 case PEP_rating_unencrypted:
123 return "unencrypted";
124 case PEP_rating_unencrypted_for_some:
125 return "unencrypted_for_some";
126 case PEP_rating_unreliable:
128 case PEP_rating_reliable:
130 case PEP_rating_trusted:
132 case PEP_rating_trusted_and_anonymized:
133 return "trusted_and_anonymized";
134 case PEP_rating_fully_anonymous:
135 return "fully_anonymous";
136 case PEP_rating_mistrust:
138 case PEP_rating_b0rken:
140 case PEP_rating_under_attack:
141 return "under_attack";
147 bool _memnmemn(const char* needle,
149 const char* haystack,
150 size_t haystack_size)
152 if (needle_size > haystack_size) {
155 else if (needle_size == 0) {
160 const char* haystack_ptr = haystack;
162 size_t remaining_hay = haystack_size;
163 for (i = 0; i < haystack_size && (remaining_hay >= needle_size); i++, haystack_ptr++) {
165 const char* needle_ptr = needle;
166 if (*haystack_ptr == *needle) {
167 const char* haystack_tmp = haystack_ptr;
170 for (j = 0; j < needle_size; j++) {
171 if (*needle_ptr++ != *haystack_tmp++) {
184 void add_opt_field(message *msg, const char *name, const char *value)
186 assert(msg && name && value);
188 if (msg && name && value) {
189 stringpair_t *pair = new_stringpair(name, value);
193 stringpair_list_t *field = stringpair_list_add(msg->opt_fields, pair);
196 free_stringpair(pair);
200 if (msg->opt_fields == NULL)
201 msg->opt_fields = field;
205 void replace_opt_field(message *msg,
210 assert(msg && name && value);
212 if (msg && name && value) {
213 stringpair_list_t* opt_fields = msg->opt_fields;
214 stringpair_t* pair = NULL;
218 pair = opt_fields->value;
219 if (pair && (strcmp(name, pair->key) == 0))
223 opt_fields = opt_fields->next;
230 pair->value = strdup(value);
234 add_opt_field(msg, name, value);
239 static void decorate_message(
242 stringlist_t *keylist,
250 replace_opt_field(msg, "X-pEp-Version", PEP_VERSION, clobber);
252 if (rating != PEP_rating_undefined)
253 replace_opt_field(msg, "X-EncStatus", rating_to_string(rating), clobber);
256 char *_keylist = keylist_to_string(keylist);
257 replace_opt_field(msg, "X-KeyList", _keylist, clobber);
262 static char* _get_resource_ptr_noown(char* uri) {
263 char* uri_delim = strstr(uri, "://");
270 // static bool is_file_uri(char* str) {
271 // return(strncmp(str, "file://", 7) == 0);
274 static bool is_cid_uri(const char* str) {
275 return(strncmp(str, "cid://", 6) == 0);
278 static bool string_equality(const char *s1, const char *s2)
280 if (s1 == NULL || s2 == NULL)
285 return strcmp(s1, s2) == 0;
288 static bool is_mime_type(const bloblist_t *bl, const char *mt)
292 return bl && string_equality(bl->mime_type, mt);
296 // This function presumes the file ending is a proper substring of the
297 // filename (i.e. if bl->filename is "a.pgp" and fe is ".pgp", it will
298 // return true, but if bl->filename is ".pgp" and fe is ".pgp", it will
299 // return false. This is desired behaviour.
301 static bool is_fileending(const bloblist_t *bl, const char *fe)
305 if (bl == NULL || bl->filename == NULL || fe == NULL || is_cid_uri(bl->filename))
308 assert(bl && bl->filename);
310 size_t fe_len = strlen(fe);
311 size_t fn_len = strlen(bl->filename);
313 if (fn_len <= fe_len)
316 assert(fn_len > fe_len);
318 return strcmp(bl->filename + (fn_len - fe_len), fe) == 0;
321 char * encapsulate_message_wrap_info(const char *msg_wrap_info, const char *longmsg)
323 assert(msg_wrap_info);
325 if (!msg_wrap_info) {
329 char *result = strdup(longmsg);
338 const char * const newlines = "\n\n";
339 const size_t NL_LEN = 2;
341 const size_t bufsize = PEP_MSG_WRAP_KEY_LEN + strlen(msg_wrap_info) + NL_LEN + strlen(longmsg) + 1;
342 char * ptext = calloc(bufsize, 1);
347 strlcpy(ptext, PEP_MSG_WRAP_KEY, bufsize);
348 strlcat(ptext, msg_wrap_info, bufsize);
349 strlcat(ptext, newlines, bufsize);
350 strlcat(ptext, longmsg, bufsize);
355 static char * combine_short_and_long(const char *shortmsg, const char *longmsg)
359 unsigned char pEpstr[] = PEP_SUBJ_STRING;
360 assert(strcmp(shortmsg, "pEp") != 0 && _unsigned_signed_strcmp(pEpstr, shortmsg, PEP_SUBJ_BYTELEN) != 0);
362 if (!shortmsg || strcmp(shortmsg, "pEp") == 0 ||
363 _unsigned_signed_strcmp(pEpstr, shortmsg, PEP_SUBJ_BYTELEN) == 0) {
368 char *result = strdup(longmsg);
377 const char * const newlines = "\n\n";
378 const size_t NL_LEN = 2;
380 const size_t bufsize = PEP_SUBJ_KEY_LEN + strlen(shortmsg) + NL_LEN + strlen(longmsg) + 1;
381 char * ptext = calloc(bufsize, 1);
386 strlcpy(ptext, PEP_SUBJ_KEY, bufsize);
387 strlcat(ptext, shortmsg, bufsize);
388 strlcat(ptext, newlines, bufsize);
389 strlcat(ptext, longmsg, bufsize);
394 static PEP_STATUS replace_subject(message* msg) {
395 unsigned char pEpstr[] = PEP_SUBJ_STRING;
396 if (msg->shortmsg && *(msg->shortmsg) != '\0') {
397 char* longmsg = combine_short_and_long(msg->shortmsg, msg->longmsg);
399 return PEP_OUT_OF_MEMORY;
402 msg->longmsg = longmsg;
407 msg->shortmsg = strdup("pEp");
409 msg->shortmsg = strdup((char*)pEpstr);
413 return PEP_OUT_OF_MEMORY;
415 return PEP_STATUS_OK;
418 unsigned long long get_bitmask(int num_bits) {
422 unsigned long long bitmask = 0;
424 for (i = 1; i < num_bits; i++) {
425 bitmask = bitmask << 1;
431 static char* get_base_36_rep(unsigned long long value, int num_sig_bits) {
433 int bufsize = ((int) ceil((double) num_sig_bits / _pEp_log2_36)) + 1;
436 // https://en.wikipedia.org/wiki/Base36#C_implementation
437 // ok, we supposedly have a 64-bit kinda sorta random blob
438 const char base_36_symbols[37] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
440 char* retbuf = calloc(bufsize, 1);
445 int i = bufsize - 1; // (end index)
448 retbuf[--i] = base_36_symbols[value % 36];
456 static char* message_id_prand_part(void) {
458 int num_bits = _pEp_rand_max_bits;
463 const int DESIRED_BITS = 64;
465 num_bits = MIN(num_bits, DESIRED_BITS);
470 unsigned long long bitmask = get_bitmask(num_bits);
472 unsigned long long output_value = 0;
478 int randval = rand();
479 unsigned long long temp_val = randval & bitmask;
481 output_value |= temp_val;
483 i -= MIN(num_bits, i);
485 bitshift = MIN(num_bits, i);
486 output_value <<= bitshift;
487 bitmask = get_bitmask(bitshift);
490 return get_base_36_rep(output_value, DESIRED_BITS);
493 static PEP_STATUS generate_message_id(message* msg) {
495 if (!msg || !msg->from || !msg->from->address)
496 return PEP_ILLEGAL_VALUE;
498 char* time_prefix = NULL;
499 char* random_id = NULL;
502 size_t buf_len = 2; // NUL + @
504 char* from_addr = msg->from->address;
505 char* domain_ptr = strstr(from_addr, "@");
506 if (!domain_ptr || *(domain_ptr + 1) == '\0')
507 domain_ptr = "localhost";
511 buf_len += strlen(domain_ptr);
518 time_t curr_time = time(NULL);
520 time_prefix = get_base_36_rep(curr_time, (int) ceil(log2((double) curr_time)));
525 buf_len += strlen(time_prefix);
527 random_id = message_id_prand_part();
533 buf_len += strlen(random_id);
535 // make a new uuid - depending on rand() impl, time precision, etc,
536 // we may still not be unique. We'd better make sure. So.
539 uuid_generate_random(uuid);
540 uuid_unparse_upper(uuid, new_uuid);
542 buf_len += strlen(new_uuid);
544 buf_len += 6; // "pEp" and 3 '.' chars
546 retval = calloc(buf_len, 1);
551 strlcpy(retval, "pEp.", buf_len);
552 strlcat(retval, time_prefix, buf_len);
553 strlcat(retval, ".", buf_len);
554 strlcat(retval, random_id, buf_len);
555 strlcat(retval, ".", buf_len);
556 strlcat(retval, new_uuid, buf_len);
557 strlcat(retval, "@", buf_len);
558 strlcat(retval, domain_ptr, buf_len);
565 return PEP_STATUS_OK;
570 return PEP_OUT_OF_MEMORY;
574 WARNING: For the moment, this only works for the first line of decrypted
575 plaintext because we don't need more. IF WE DO, THIS MUST BE EXPANDED, or
576 we need a delineated section to parse separately
578 Does case-insensitive compare of keys, so sending in a lower-cased
579 string constant saves a bit of computation
581 static PEP_STATUS get_data_from_encapsulated_line(const char* plaintext, const char* key,
582 const size_t keylen, char** data,
583 char** modified_msg) {
585 char* _modified = NULL;
587 if (strncasecmp(plaintext, key, keylen) == 0) {
588 const char *line_end = strchr(plaintext, '\n');
590 if (line_end == NULL) {
591 _data = strdup(plaintext + keylen);
594 return PEP_OUT_OF_MEMORY;
597 size_t n = line_end - plaintext;
599 if (*(line_end - 1) == '\r')
600 _data = strndup(plaintext + keylen, n - (keylen + 1));
602 _data = strndup(plaintext + keylen, n - keylen);
605 return PEP_OUT_OF_MEMORY;
607 while (*(plaintext + n) && (*(plaintext + n) == '\n' || *(plaintext + n) == '\r'))
610 if (*(plaintext + n)) {
611 _modified = strdup(plaintext + n);
613 if (_modified == NULL)
614 return PEP_OUT_OF_MEMORY;
619 *modified_msg = _modified;
620 return PEP_STATUS_OK;
624 static int separate_short_and_long(const char *src, char **shortmsg, char** msg_wrap_info, char **longmsg)
626 char *_shortmsg = NULL;
627 char *_msg_wrap_info = NULL;
628 char *_longmsg = NULL;
632 assert(msg_wrap_info);
635 if (src == NULL || shortmsg == NULL || msg_wrap_info == NULL || longmsg == NULL)
640 *msg_wrap_info = NULL;
642 // We generated the input here. If we ever need more than one header value to be
643 // encapsulated and hidden in the encrypted text, we will have to modify this.
644 // As is, we're either doing this with a version 1.0 client, in which case
645 // the only encapsulated header value is subject, or 2.0+, in which the
646 // message wrap info is the only encapsulated header value. If we need this
647 // to be more complex, we're going to have to do something more elegant
649 PEP_STATUS status = get_data_from_encapsulated_line(src, PEP_SUBJ_KEY_LC,
651 &_shortmsg, &_longmsg);
654 if (status == PEP_STATUS_OK)
655 *shortmsg = _shortmsg;
660 status = get_data_from_encapsulated_line(src, PEP_MSG_WRAP_KEY_LC,
661 PEP_MSG_WRAP_KEY_LEN,
662 &_msg_wrap_info, &_longmsg);
663 if (_msg_wrap_info) {
664 if (status == PEP_STATUS_OK)
665 *msg_wrap_info = _msg_wrap_info;
671 // If there was no secret data hiding in the first line...
672 if (!_shortmsg && !_msg_wrap_info) {
673 _longmsg = strdup(src);
675 if (_longmsg == NULL)
685 free(_msg_wrap_info);
691 static PEP_STATUS copy_fields(message *dst, const message *src)
697 return PEP_ILLEGAL_VALUE;
699 free_timestamp(dst->sent);
702 dst->sent = timestamp_dup(src->sent);
703 if (dst->sent == NULL)
704 return PEP_OUT_OF_MEMORY;
707 free_timestamp(dst->recv);
710 dst->recv = timestamp_dup(src->recv);
711 if (dst->recv == NULL)
712 return PEP_OUT_OF_MEMORY;
715 free_identity(dst->from);
718 dst->from = identity_dup(src->from);
719 if (dst->from == NULL)
720 return PEP_OUT_OF_MEMORY;
723 free_identity_list(dst->to);
725 if (src->to && src->to->ident) {
726 dst->to = identity_list_dup(src->to);
728 return PEP_OUT_OF_MEMORY;
731 free_identity(dst->recv_by);
734 dst->recv_by = identity_dup(src->recv_by);
735 if (dst->recv_by == NULL)
736 return PEP_OUT_OF_MEMORY;
739 free_identity_list(dst->cc);
741 if (src->cc && src->cc->ident) {
742 dst->cc = identity_list_dup(src->cc);
744 return PEP_OUT_OF_MEMORY;
747 free_identity_list(dst->bcc);
749 if (src->bcc && src->bcc->ident) {
750 dst->bcc = identity_list_dup(src->bcc);
751 if (dst->bcc == NULL)
752 return PEP_OUT_OF_MEMORY;
755 free_identity_list(dst->reply_to);
756 dst->reply_to = NULL;
757 if (src->reply_to && src->reply_to->ident) {
758 dst->reply_to = identity_list_dup(src->reply_to);
759 if (dst->reply_to == NULL)
760 return PEP_OUT_OF_MEMORY;
763 free_stringlist(dst->in_reply_to);
764 dst->in_reply_to = NULL;
765 if (src->in_reply_to && src->in_reply_to->value) {
766 dst->in_reply_to = stringlist_dup(src->in_reply_to);
767 if (dst->in_reply_to == NULL)
768 return PEP_OUT_OF_MEMORY;
771 free_stringlist(dst->references);
772 dst->references = NULL;
773 if (src->references) {
774 dst->references = stringlist_dup(src->references);
775 if (dst->references == NULL)
776 return PEP_OUT_OF_MEMORY;
779 free_stringlist(dst->keywords);
780 dst->keywords = NULL;
781 if (src->keywords && src->keywords->value) {
782 dst->keywords = stringlist_dup(src->keywords);
783 if (dst->keywords == NULL)
784 return PEP_OUT_OF_MEMORY;
788 dst->comments = NULL;
790 dst->comments = strdup(src->comments);
791 assert(dst->comments);
792 if (dst->comments == NULL)
793 return PEP_OUT_OF_MEMORY;
796 free_stringpair_list(dst->opt_fields);
797 dst->opt_fields = NULL;
798 if (src->opt_fields) {
799 dst->opt_fields = stringpair_list_dup(src->opt_fields);
800 if (dst->opt_fields == NULL)
801 return PEP_OUT_OF_MEMORY;
804 return PEP_STATUS_OK;
807 // FIXME: error mem leakage
808 static message* extract_minimal_envelope(const message* src,
809 PEP_msg_direction direct) {
811 message* envelope = new_message(direct);
815 envelope->shortmsg = _pEp_subj_copy();
816 if (!envelope->shortmsg)
820 envelope->from = identity_dup(src->from);
826 envelope->to = identity_list_dup(src->to);
832 envelope->cc = identity_list_dup(src->cc);
838 envelope->bcc = identity_list_dup(src->bcc);
843 // For Outlook Force-Encryption
844 // const char* pull_keys[] = {"pEp-auto-consume",
845 // "pEp-force-protection",
846 // "X-pEp-Never-Unsecure"};
847 // int pull_keys_len = 3; // UPDATE WHEN MORE ADDED ABOVE
850 // stringpair_t* opt_add = NULL;
851 // for( ; i < pull_keys_len; i++) {
852 // opt_add = search_optfields(src, pull_keys[i]);
853 // stringpair_list_t* add_ptr = NULL;
855 // add_ptr = stringpair_list_add(src->opt_fields, stringpair_dup(opt_add));
863 envelope->enc_format = src->enc_format;
872 static message * clone_to_empty_message(const message * src)
875 message * msg = NULL;
881 msg = calloc(1, sizeof(message));
888 status = copy_fields(msg, src);
889 if (status != PEP_STATUS_OK)
899 static message* wrap_message_as_attachment(message* envelope,
900 message* attachment, message_wrap_type wrap_type, bool keep_orig_subject) {
905 message* _envelope = envelope;
907 PEP_STATUS status = PEP_STATUS_OK;
909 replace_opt_field(attachment, "X-pEp-Version", PEP_VERSION, true);
911 if (!_envelope && (wrap_type != PEP_message_transport)) {
912 _envelope = extract_minimal_envelope(attachment, PEP_dir_outgoing);
913 status = generate_message_id(_envelope);
915 if (status != PEP_STATUS_OK)
918 const char* inner_type_string = "";
920 case PEP_message_key_reset:
921 inner_type_string = "KEY_RESET";
924 inner_type_string = "INNER";
926 attachment->longmsg = encapsulate_message_wrap_info(inner_type_string, attachment->longmsg);
927 _envelope->longmsg = encapsulate_message_wrap_info("OUTER", _envelope->longmsg);
929 else if (_envelope) {
930 _envelope->longmsg = encapsulate_message_wrap_info("TRANSPORT", _envelope->longmsg);
936 if (!attachment->id || attachment->id[0] == '\0') {
937 free(attachment->id);
938 if (!_envelope->id) {
939 status = generate_message_id(_envelope);
941 if (status != PEP_STATUS_OK)
945 attachment->id = strdup(_envelope->id);
948 char* message_text = NULL;
950 /* prevent introduction of pEp in inner message */
952 if (!attachment->shortmsg) {
953 attachment->shortmsg = strdup("");
954 if (!attachment->shortmsg)
958 /* Turn message into a MIME-blob */
959 status = _mime_encode_message_internal(attachment, false, &message_text, true);
961 if (status != PEP_STATUS_OK)
964 size_t message_len = strlen(message_text);
966 bloblist_t* message_blob = new_bloblist(message_text, message_len,
967 "message/rfc822", NULL);
969 _envelope->attachments = message_blob;
970 if (keep_orig_subject && attachment->shortmsg)
971 _envelope->shortmsg = strdup(attachment->shortmsg);
976 free_message(_envelope);
981 static PEP_STATUS update_identity_recip_list(PEP_SESSION session,
982 identity_list* list) {
984 PEP_STATUS status = PEP_STATUS_OK;
987 return PEP_UNKNOWN_ERROR;
989 identity_list* id_list_ptr = NULL;
991 for (id_list_ptr = list; id_list_ptr; id_list_ptr = id_list_ptr->next) {
992 pEp_identity* curr_identity = id_list_ptr->ident;
994 if (!is_me(session, curr_identity)) {
995 char* name_bak = curr_identity->username;
996 curr_identity->username = NULL;
997 status = update_identity(session, curr_identity);
999 (EMPTYSTR(curr_identity->username) || strcmp(name_bak, curr_identity->username) != 0)) {
1000 free(curr_identity->username);
1001 curr_identity->username = name_bak;
1005 status = myself(session, curr_identity);
1006 if (status == PEP_ILLEGAL_VALUE || status == PEP_OUT_OF_MEMORY)
1011 return PEP_STATUS_OK;
1014 static PEP_STATUS encrypt_PGP_MIME(
1015 PEP_SESSION session,
1019 PEP_encrypt_flags_t flags
1022 PEP_STATUS status = PEP_STATUS_OK;
1023 bool free_ptext = false;
1026 char *mimetext = NULL;
1028 assert(dst->longmsg == NULL);
1029 dst->enc_format = PEP_enc_PGP_MIME;
1032 dst->shortmsg = strdup(src->shortmsg);
1034 message *_src = calloc(1, sizeof(message));
1038 // _src->longmsg = ptext;
1039 _src->longmsg = src->longmsg;
1040 _src->longmsg_formatted = src->longmsg_formatted;
1041 _src->attachments = src->attachments;
1042 _src->enc_format = PEP_enc_none;
1043 bool mime_encode = !is_wrapper(_src);
1044 status = _mime_encode_message_internal(_src, true, &mimetext, mime_encode);
1045 assert(status == PEP_STATUS_OK);
1046 if (status != PEP_STATUS_OK)
1056 if (mimetext == NULL)
1059 if (flags & PEP_encrypt_flag_force_unsigned)
1060 status = encrypt_only(session, keys, mimetext, strlen(mimetext),
1063 status = encrypt_and_sign(session, keys, mimetext, strlen(mimetext),
1069 dst->longmsg = strdup("this message was encrypted with p≡p "
1070 "https://pEp-project.org");
1071 assert(dst->longmsg);
1072 if (dst->longmsg == NULL)
1075 char *v = strdup("Version: 1");
1080 bloblist_t *_a = new_bloblist(v, strlen(v), "application/pgp-encrypted", NULL);
1083 dst->attachments = _a;
1085 _a = bloblist_add(_a, ctext, csize, "application/octet-stream",
1090 return PEP_STATUS_OK;
1093 status = PEP_OUT_OF_MEMORY;
1104 static PEP_rating _rating(PEP_comm_type ct, PEP_rating rating)
1106 if (ct == PEP_ct_unknown)
1107 return PEP_rating_undefined;
1109 else if (ct == PEP_ct_key_not_found)
1110 return PEP_rating_have_no_key;
1112 else if (ct == PEP_ct_compromised)
1113 return PEP_rating_under_attack;
1115 else if (ct == PEP_ct_mistrusted)
1116 return PEP_rating_mistrust;
1118 if (rating == PEP_rating_unencrypted_for_some)
1119 return PEP_rating_unencrypted_for_some;
1121 if (ct == PEP_ct_no_encryption || ct == PEP_ct_no_encrypted_channel ||
1122 ct == PEP_ct_my_key_not_included) {
1123 if (rating > PEP_rating_unencrypted_for_some)
1124 return PEP_rating_unencrypted_for_some;
1126 return PEP_rating_unencrypted;
1129 if (rating == PEP_rating_unencrypted)
1130 return PEP_rating_unencrypted_for_some;
1132 if (ct >= PEP_ct_confirmed_enc_anon)
1133 return PEP_rating_trusted_and_anonymized;
1135 else if (ct >= PEP_ct_strong_encryption)
1136 return PEP_rating_trusted;
1138 else if (ct >= PEP_ct_strong_but_unconfirmed && ct < PEP_ct_confirmed)
1139 return PEP_rating_reliable;
1142 return PEP_rating_unreliable;
1145 static bool is_encrypted_attachment(const bloblist_t *blob)
1149 if (blob == NULL || blob->filename == NULL || is_cid_uri(blob->filename))
1152 char *ext = strrchr(blob->filename, '.');
1156 if (strcmp(blob->mime_type, "application/octet-stream") == 0) {
1157 if (strcmp(ext, ".pgp") == 0 || strcmp(ext, ".gpg") == 0)
1160 if (strcmp(ext, ".asc") == 0 && blob->size > 0) {
1161 const char* pubk_needle = "BEGIN PGP PUBLIC KEY";
1162 size_t pubk_needle_size = strlen(pubk_needle);
1163 const char* privk_needle = "BEGIN PGP PRIVATE KEY";
1164 size_t privk_needle_size = strlen(privk_needle);
1166 if (!(_memnmemn(pubk_needle, pubk_needle_size, blob->value, blob->size)) &&
1167 !(_memnmemn(privk_needle, privk_needle_size, blob->value, blob->size)))
1174 static bool is_encrypted_html_attachment(const bloblist_t *blob)
1177 assert(blob->filename);
1178 if (blob == NULL || blob->filename == NULL || is_cid_uri(blob->filename))
1181 const char* bare_filename_ptr = _get_resource_ptr_noown(blob->filename);
1182 bare_filename_ptr += strlen(bare_filename_ptr) - 15;
1183 if (strncmp(bare_filename_ptr, "PGPexch.htm.", 12) == 0) {
1184 if (strcmp(bare_filename_ptr + 11, ".pgp") == 0 ||
1185 strcmp(bare_filename_ptr + 11, ".asc") == 0)
1192 static char * without_double_ending(const char *filename)
1195 if (filename == NULL || is_cid_uri(filename))
1198 char *ext = strrchr(filename, '.');
1202 char *result = strndup(filename, ext - filename);
1207 static PEP_rating decrypt_rating(PEP_STATUS status)
1210 case PEP_UNENCRYPTED:
1212 case PEP_VERIFY_NO_KEY:
1213 case PEP_VERIFIED_AND_TRUSTED:
1214 return PEP_rating_unencrypted;
1217 case PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH:
1218 return PEP_rating_unreliable;
1220 case PEP_DECRYPTED_AND_VERIFIED:
1221 return PEP_rating_reliable;
1223 case PEP_DECRYPT_NO_KEY:
1224 return PEP_rating_have_no_key;
1226 case PEP_DECRYPT_WRONG_FORMAT:
1227 case PEP_CANNOT_DECRYPT_UNKNOWN:
1228 return PEP_rating_cannot_decrypt;
1231 return PEP_rating_undefined;
1235 static PEP_rating key_rating(PEP_SESSION session, const char *fpr)
1241 if (session == NULL || fpr == NULL)
1242 return PEP_rating_undefined;
1245 PEP_comm_type bare_comm_type = PEP_ct_unknown;
1246 PEP_comm_type resulting_comm_type = PEP_ct_unknown;
1247 PEP_STATUS status = get_key_rating(session, fpr, &bare_comm_type);
1248 if (status != PEP_STATUS_OK)
1249 return PEP_rating_undefined;
1251 PEP_comm_type least_comm_type = PEP_ct_unknown;
1252 least_trust(session, fpr, &least_comm_type);
1254 if (least_comm_type == PEP_ct_unknown) {
1255 resulting_comm_type = bare_comm_type;
1256 } else if (least_comm_type < PEP_ct_strong_but_unconfirmed ||
1257 bare_comm_type < PEP_ct_strong_but_unconfirmed) {
1258 // take minimum if anything bad
1259 resulting_comm_type = least_comm_type < bare_comm_type ?
1263 resulting_comm_type = least_comm_type;
1265 return _rating(resulting_comm_type, PEP_rating_undefined);
1268 static PEP_rating worst_rating(PEP_rating rating1, PEP_rating rating2) {
1269 return ((rating1 < rating2) ? rating1 : rating2);
1272 static PEP_rating keylist_rating(PEP_SESSION session, stringlist_t *keylist, char* sender_fpr, PEP_rating sender_rating)
1274 PEP_rating rating = sender_rating;
1276 assert(keylist && keylist->value);
1277 if (keylist == NULL || keylist->value == NULL)
1278 return PEP_rating_undefined;
1281 for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
1284 if(_same_fpr(sender_fpr, strlen(sender_fpr), _kl->value, strlen(_kl->value)))
1287 PEP_rating _rating_ = key_rating(session, _kl->value);
1289 if (_rating_ <= PEP_rating_mistrust)
1292 if (_rating_ == PEP_rating_unencrypted)
1294 if (rating > PEP_rating_unencrypted_for_some)
1295 rating = worst_rating(rating, PEP_rating_unencrypted_for_some);
1299 rating = worst_rating(rating, _rating_);
1306 // Internal function WARNING:
1307 // Only call this on an ident that might have its FPR set from retrieval!
1308 // (or on one without an fpr)
1309 // We do not want myself() setting the fpr here.
1310 static PEP_comm_type _get_comm_type(
1311 PEP_SESSION session,
1312 PEP_comm_type max_comm_type,
1316 PEP_STATUS status = PEP_STATUS_OK;
1318 if (max_comm_type == PEP_ct_compromised)
1319 return PEP_ct_compromised;
1321 if (max_comm_type == PEP_ct_mistrusted)
1322 return PEP_ct_mistrusted;
1324 if (!is_me(session, ident))
1325 status = update_identity(session, ident);
1327 status = myself(session, ident);
1329 if (status == PEP_STATUS_OK) {
1330 if (ident->comm_type == PEP_ct_compromised)
1331 return PEP_ct_compromised;
1332 else if (ident->comm_type == PEP_ct_mistrusted)
1333 return PEP_ct_mistrusted;
1335 return MIN(max_comm_type, ident->comm_type);
1338 return PEP_ct_unknown;
1342 static PEP_comm_type _get_comm_type_preview(
1343 PEP_SESSION session,
1344 PEP_comm_type max_comm_type,
1351 PEP_STATUS status = PEP_STATUS_OK;
1353 if (max_comm_type == PEP_ct_compromised)
1354 return PEP_ct_compromised;
1356 if (max_comm_type == PEP_ct_mistrusted)
1357 return PEP_ct_mistrusted;
1359 PEP_comm_type comm_type = PEP_ct_unknown;
1360 if (ident && !EMPTYSTR(ident->address) && !EMPTYSTR(ident->user_id)) {
1361 pEp_identity *ident2;
1362 status = get_identity(session, ident->address, ident->user_id, &ident2);
1363 comm_type = ident2 ? ident2->comm_type : PEP_ct_unknown;
1364 free_identity(ident2);
1366 if (status == PEP_STATUS_OK) {
1367 if (comm_type == PEP_ct_compromised)
1368 comm_type = PEP_ct_compromised;
1369 else if (comm_type == PEP_ct_mistrusted)
1370 comm_type = PEP_ct_mistrusted;
1372 comm_type = _MIN(max_comm_type, comm_type);
1375 comm_type = PEP_ct_unknown;
1381 static void free_bl_entry(bloblist_t *bl)
1385 free(bl->mime_type);
1391 static bool is_key(const bloblist_t *bl)
1393 return (// workaround for Apple Mail bugs
1394 (is_mime_type(bl, "application/x-apple-msg-attachment") &&
1395 is_fileending(bl, ".asc")) ||
1396 // as binary, by file name
1397 ((bl->mime_type == NULL ||
1398 is_mime_type(bl, "application/octet-stream")) &&
1399 (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
1400 is_fileending(bl, ".key") || is_fileending(bl, ".asc"))) ||
1401 // explicit mime type
1402 is_mime_type(bl, "application/pgp-keys") ||
1403 // as text, by file name
1404 (is_mime_type(bl, "text/plain") &&
1405 (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
1406 is_fileending(bl, ".key") || is_fileending(bl, ".asc")))
1410 static void remove_attached_keys(message *msg)
1413 bloblist_t *last = NULL;
1414 for (bloblist_t *bl = msg->attachments; bl && bl->value; ) {
1415 bloblist_t *next = bl->next;
1422 msg->attachments = next;
1434 bool import_attached_keys(
1435 PEP_SESSION session,
1437 identity_list **private_idents
1443 if (session == NULL || msg == NULL)
1446 bool remove = false;
1449 for (bloblist_t *bl = msg->attachments; i < MAX_KEYS_TO_IMPORT && bl && bl->value;
1452 if (bl && bl->value && bl->size && bl->size < MAX_KEY_SIZE
1455 char* blob_value = bl->value;
1456 size_t blob_size = bl->size;
1457 bool free_blobval = false;
1459 if (is_encrypted_attachment(bl)) {
1461 char* bl_ptext = NULL;
1462 size_t bl_psize = 0;
1463 stringlist_t* bl_keylist = NULL;
1464 PEP_STATUS _status = decrypt_and_verify(session,
1465 blob_value, blob_size,
1467 &bl_ptext, &bl_psize,
1470 free_stringlist(bl_keylist); // we don't care about key encryption as long as we decrypt
1471 if (_status == PEP_DECRYPTED || _status == PEP_DECRYPTED_AND_VERIFIED) {
1472 free_blobval = true;
1473 blob_value = bl_ptext;
1474 blob_size = bl_psize;
1477 // This is an encrypted attachment we can do nothing with.
1478 // We shouldn't delete it or import it, because we can't
1484 identity_list *local_private_idents = NULL;
1485 import_key(session, blob_value, blob_size, &local_private_idents);
1487 if (private_idents && *private_idents == NULL && local_private_idents != NULL)
1488 *private_idents = local_private_idents;
1490 free_identity_list(local_private_idents);
1499 PEP_STATUS _attach_key(PEP_SESSION session, const char* fpr, message *msg)
1501 char *keydata = NULL;
1504 PEP_STATUS status = export_key(session, fpr, &keydata, &size);
1505 assert(status == PEP_STATUS_OK);
1506 if (status != PEP_STATUS_OK)
1510 bloblist_t *bl = bloblist_add(msg->attachments, keydata, size, "application/pgp-keys",
1511 "file://pEpkey.asc");
1513 if (msg->attachments == NULL && bl)
1514 msg->attachments = bl;
1516 return PEP_STATUS_OK;
1519 #define ONE_WEEK (7*24*3600)
1521 void attach_own_key(PEP_SESSION session, message *msg)
1526 if (msg->dir == PEP_dir_incoming)
1529 assert(msg->from && msg->from->fpr);
1530 if (msg->from == NULL || msg->from->fpr == NULL)
1533 if(_attach_key(session, msg->from->fpr, msg) != PEP_STATUS_OK)
1536 char *revoked_fpr = NULL;
1537 uint64_t revocation_date = 0;
1539 if(get_revoked(session, msg->from->fpr,
1540 &revoked_fpr, &revocation_date) == PEP_STATUS_OK &&
1541 revoked_fpr != NULL)
1543 time_t now = time(NULL);
1545 if (now < (time_t)revocation_date + ONE_WEEK)
1547 _attach_key(session, revoked_fpr, msg);
1553 PEP_cryptotech determine_encryption_format(message *msg)
1557 if (is_PGP_message_text(msg->longmsg)) {
1558 msg->enc_format = PEP_enc_pieces;
1559 return PEP_crypt_OpenPGP;
1561 else if (msg->attachments && msg->attachments->next &&
1562 is_mime_type(msg->attachments, "application/pgp-encrypted") &&
1563 is_PGP_message_text(msg->attachments->next->value)
1565 msg->enc_format = PEP_enc_PGP_MIME;
1566 return PEP_crypt_OpenPGP;
1568 else if (msg->attachments && msg->attachments->next &&
1569 is_mime_type(msg->attachments->next, "application/pgp-encrypted") &&
1570 is_PGP_message_text(msg->attachments->value)
1572 msg->enc_format = PEP_enc_PGP_MIME_Outlook1;
1573 return PEP_crypt_OpenPGP;
1576 msg->enc_format = PEP_enc_none;
1577 return PEP_crypt_none;
1581 static void _cleanup_src(message* src, bool remove_attached_key) {
1587 char* longmsg = NULL;
1588 char* shortmsg = NULL;
1589 char* msg_wrap_info = NULL;
1590 separate_short_and_long(src->longmsg, &shortmsg, &msg_wrap_info,
1595 free(msg_wrap_info);
1596 src->longmsg = longmsg;
1598 if (remove_attached_key) {
1599 // End of the attachment list
1600 if (src->attachments) {
1601 bloblist_t* tmp = src->attachments;
1602 while (tmp->next && tmp->next->next) {
1605 free_bloblist(tmp->next);
1611 DYNAMIC_API PEP_STATUS encrypt_message(
1612 PEP_SESSION session,
1614 stringlist_t * extra,
1616 PEP_enc_format enc_format,
1617 PEP_encrypt_flags_t flags
1620 PEP_STATUS status = PEP_STATUS_OK;
1621 message * msg = NULL;
1622 stringlist_t * keys = NULL;
1623 message* _src = src;
1625 bool added_key_to_real_src = false;
1628 assert(src && src->from);
1631 if (!(session && src && src->from && dst))
1632 return PEP_ILLEGAL_VALUE;
1634 if (src->dir == PEP_dir_incoming)
1635 return PEP_ILLEGAL_VALUE;
1637 determine_encryption_format(src);
1638 // TODO: change this for multi-encryption in message format 2.0
1639 if (src->enc_format != PEP_enc_none)
1640 return PEP_ILLEGAL_VALUE;
1642 bool force_v_1 = flags & PEP_encrypt_flag_force_version_1;
1646 if (!src->from->user_id || src->from->user_id[0] == '\0') {
1647 char* own_id = NULL;
1648 status = get_default_own_userid(session, &own_id);
1650 free(src->from->user_id);
1651 src->from->user_id = own_id; // ownership transfer
1655 status = myself(session, src->from);
1656 if (status != PEP_STATUS_OK)
1659 keys = new_stringlist(src->from->fpr);
1663 stringlist_t *_k = keys;
1666 _k = stringlist_append(_k, extra);
1671 bool dest_keys_found = true;
1672 bool has_pEp_user = false;
1674 PEP_comm_type max_comm_type = PEP_ct_pEp;
1676 identity_list * _il = NULL;
1678 if (enc_format != PEP_enc_none && (_il = src->bcc) && _il->ident)
1679 // BCC limited support:
1681 // - App splits mails with BCC in multiple mails.
1682 // - Each email is encrypted separately
1684 if(_il->next || (src->to && src->to->ident) || (src->cc && src->cc->ident))
1686 // Only one Bcc with no other recipient allowed for now
1687 return PEP_ILLEGAL_VALUE;
1690 PEP_STATUS _status = PEP_STATUS_OK;
1691 if (!is_me(session, _il->ident)) {
1692 _status = update_identity(session, _il->ident);
1693 if (_status == PEP_CANNOT_FIND_IDENTITY) {
1694 _il->ident->comm_type = PEP_ct_key_not_found;
1695 _status = PEP_STATUS_OK;
1697 bool is_blacklisted = false;
1698 if (_il->ident->fpr && IS_PGP_CT(_il->ident->comm_type)) {
1699 _status = blacklist_is_listed(session, _il->ident->fpr, &is_blacklisted);
1700 if (_status != PEP_STATUS_OK) {
1702 status = PEP_UNENCRYPTED;
1705 if (is_blacklisted) {
1706 bool user_default, ident_default, address_default;
1707 _status = get_valid_pubkey(session, _il->ident,
1708 &ident_default, &user_default,
1711 if (_status != PEP_STATUS_OK || _il->ident->fpr == NULL) {
1712 _il->ident->comm_type = PEP_ct_key_not_found;
1713 _status = PEP_STATUS_OK;
1717 if (!has_pEp_user && !EMPTYSTR(_il->ident->user_id))
1718 is_pEp_user(session, _il->ident, &has_pEp_user);
1721 _status = myself(session, _il->ident);
1722 if (_status != PEP_STATUS_OK) {
1723 status = PEP_UNENCRYPTED;
1727 if (_il->ident->fpr && _il->ident->fpr[0]) {
1728 _k = stringlist_add(_k, _il->ident->fpr);
1731 max_comm_type = _get_comm_type(session, max_comm_type,
1735 dest_keys_found = false;
1736 status = PEP_KEY_NOT_FOUND;
1741 for (_il = src->to; _il && _il->ident; _il = _il->next) {
1742 PEP_STATUS _status = PEP_STATUS_OK;
1743 if (!is_me(session, _il->ident)) {
1744 _status = update_identity(session, _il->ident);
1745 if (_status == PEP_CANNOT_FIND_IDENTITY) {
1746 _il->ident->comm_type = PEP_ct_key_not_found;
1747 _status = PEP_STATUS_OK;
1749 bool is_blacklisted = false;
1750 if (_il->ident->fpr && IS_PGP_CT(_il->ident->comm_type)) {
1751 _status = blacklist_is_listed(session, _il->ident->fpr, &is_blacklisted);
1752 if (_status != PEP_STATUS_OK) {
1754 status = PEP_UNENCRYPTED;
1757 if (is_blacklisted) {
1758 bool user_default, ident_default, address_default;
1759 _status = get_valid_pubkey(session, _il->ident,
1760 &ident_default, &user_default,
1763 if (_status != PEP_STATUS_OK || _il->ident->fpr == NULL) {
1764 _il->ident->comm_type = PEP_ct_key_not_found;
1765 _status = PEP_STATUS_OK;
1769 if (!has_pEp_user && !EMPTYSTR(_il->ident->user_id))
1770 is_pEp_user(session, _il->ident, &has_pEp_user);
1772 _status = bind_own_ident_with_contact_ident(session, src->from, _il->ident);
1773 if (_status != PEP_STATUS_OK) {
1774 status = PEP_UNKNOWN_DB_ERROR;
1780 _status = myself(session, _il->ident);
1782 if (_status != PEP_STATUS_OK) {
1783 status = PEP_UNENCRYPTED;
1787 if (_il->ident->fpr && _il->ident->fpr[0]) {
1788 _k = stringlist_add(_k, _il->ident->fpr);
1791 max_comm_type = _get_comm_type(session, max_comm_type,
1795 dest_keys_found = false;
1796 status = PEP_KEY_NOT_FOUND;
1800 for (_il = src->cc; _il && _il->ident; _il = _il->next) {
1801 PEP_STATUS _status = PEP_STATUS_OK;
1802 if (!is_me(session, _il->ident)) {
1803 _status = update_identity(session, _il->ident);
1804 if (_status == PEP_CANNOT_FIND_IDENTITY) {
1805 _il->ident->comm_type = PEP_ct_key_not_found;
1806 _status = PEP_STATUS_OK;
1808 bool is_blacklisted = false;
1809 if (_il->ident->fpr && IS_PGP_CT(_il->ident->comm_type)) {
1810 _status = blacklist_is_listed(session, _il->ident->fpr, &is_blacklisted);
1811 if (_status != PEP_STATUS_OK) {
1813 status = PEP_UNENCRYPTED;
1816 if (is_blacklisted) {
1817 bool user_default, ident_default, address_default;
1818 _status = get_valid_pubkey(session, _il->ident,
1819 &ident_default, &user_default,
1822 if (_status != PEP_STATUS_OK || _il->ident->fpr == NULL) {
1823 _il->ident->comm_type = PEP_ct_key_not_found;
1824 _status = PEP_STATUS_OK;
1828 if (!has_pEp_user && !EMPTYSTR(_il->ident->user_id))
1829 is_pEp_user(session, _il->ident, &has_pEp_user);
1832 _status = myself(session, _il->ident);
1833 if (_status != PEP_STATUS_OK)
1835 status = PEP_UNENCRYPTED;
1839 if (_il->ident->fpr && _il->ident->fpr[0]) {
1840 _k = stringlist_add(_k, _il->ident->fpr);
1843 max_comm_type = _get_comm_type(session, max_comm_type,
1847 dest_keys_found = false;
1852 if (enc_format == PEP_enc_none || !dest_keys_found ||
1853 stringlist_length(keys) == 0 ||
1854 _rating(max_comm_type,
1855 PEP_rating_undefined) < PEP_rating_reliable)
1857 free_stringlist(keys);
1858 if ((has_pEp_user || !session->passive_mode) &&
1859 !(flags & PEP_encrypt_flag_force_no_attached_key)) {
1860 attach_own_key(session, src);
1861 added_key_to_real_src = true;
1863 decorate_message(src, PEP_rating_undefined, NULL, true, true);
1864 return PEP_UNENCRYPTED;
1867 // FIXME - we need to deal with transport types (via flag)
1868 if ((!force_v_1) && ((max_comm_type | PEP_ct_confirmed) == PEP_ct_pEp)) {
1869 message_wrap_type wrap_type = ((flags & PEP_encrypt_flag_key_reset_only) ? PEP_message_key_reset : PEP_message_default);
1870 _src = wrap_message_as_attachment(NULL, src, wrap_type, false);
1876 if (!session->unencrypted_subject) {
1877 status = replace_subject(_src);
1878 if (status == PEP_OUT_OF_MEMORY)
1881 if (!(flags & PEP_encrypt_flag_force_no_attached_key))
1882 added_key_to_real_src = true;
1884 if (!(flags & PEP_encrypt_flag_force_no_attached_key))
1885 attach_own_key(session, _src);
1887 msg = clone_to_empty_message(_src);
1891 switch (enc_format) {
1892 case PEP_enc_PGP_MIME:
1893 case PEP_enc_PEP: // BUG: should be implemented extra
1894 status = encrypt_PGP_MIME(session, _src, keys, msg, flags);
1897 /* case PEP_enc_PEP:
1903 status = PEP_ILLEGAL_VALUE;
1907 if (status == PEP_OUT_OF_MEMORY)
1910 if (status != PEP_STATUS_OK)
1914 free_stringlist(keys);
1916 if (msg && msg->shortmsg == NULL) {
1917 msg->shortmsg = strdup("");
1918 assert(msg->shortmsg);
1919 if (msg->shortmsg == NULL)
1924 decorate_message(msg, PEP_rating_undefined, NULL, true, true);
1926 msg->id = strdup(_src->id);
1928 if (msg->id == NULL)
1935 // ??? FIXME: Check to be sure we don't have references btw _src and msg.
1936 // I don't think we do.
1937 if (_src && _src != src)
1940 _cleanup_src(src, added_key_to_real_src);
1945 status = PEP_OUT_OF_MEMORY;
1948 free_stringlist(keys);
1950 if (_src && _src != src)
1953 _cleanup_src(src, added_key_to_real_src);
1958 DYNAMIC_API PEP_STATUS encrypt_message_and_add_priv_key(
1959 PEP_SESSION session,
1963 PEP_enc_format enc_format,
1964 PEP_encrypt_flags_t flags
1972 if (!session || !src || !dst || !to_fpr)
1973 return PEP_ILLEGAL_VALUE;
1975 if (enc_format == PEP_enc_none)
1976 return PEP_ILLEGAL_VALUE;
1978 if (src->cc || src->bcc)
1979 return PEP_ILLEGAL_VALUE;
1981 if (!src->to || src->to->next)
1982 return PEP_ILLEGAL_VALUE;
1984 if (!src->from->address || !src->to->ident || !src->to->ident->address)
1985 return PEP_ILLEGAL_VALUE;
1987 if (strcasecmp(src->from->address, src->to->ident->address) != 0)
1988 return PEP_ILLEGAL_VALUE;
1990 stringlist_t* keys = NULL;
1992 char* own_id = NULL;
1993 char* default_id = NULL;
1995 pEp_identity* own_identity = NULL;
1996 char* own_private_fpr = NULL;
1998 char* priv_key_data = NULL;
2000 PEP_STATUS status = get_default_own_userid(session, &own_id);
2003 return PEP_UNKNOWN_ERROR; // Probably a DB error at this point
2005 if (src->from->user_id) {
2006 if (strcmp(src->from->user_id, own_id) != 0) {
2007 status = get_userid_alias_default(session, src->from->user_id, &default_id);
2008 if (status != PEP_STATUS_OK || !default_id || strcmp(default_id, own_id) != 0) {
2009 status = PEP_ILLEGAL_VALUE;
2015 // Ok, we are at least marginally sure the initial stuff is ok.
2017 // Let's get our own, normal identity
2018 own_identity = identity_dup(src->from);
2019 status = myself(session, own_identity);
2021 if (status != PEP_STATUS_OK)
2024 // Ok, now we know the address is an own address. All good. Then...
2025 own_private_fpr = own_identity->fpr;
2026 own_identity->fpr = strdup(to_fpr);
2028 status = get_trust(session, own_identity);
2030 if (status != PEP_STATUS_OK) {
2031 if (status == PEP_CANNOT_FIND_IDENTITY)
2032 status = PEP_ILLEGAL_VALUE;
2036 if ((own_identity->comm_type & PEP_ct_confirmed) != PEP_ct_confirmed) {
2037 status = PEP_ILLEGAL_VALUE;
2041 // Ok, so all the things are now allowed.
2042 // So let's get our own private key and roll with it.
2043 size_t priv_key_size = 0;
2045 status = export_secret_key(session, own_private_fpr, &priv_key_data,
2048 if (status != PEP_STATUS_OK)
2051 if (!priv_key_data) {
2052 status = PEP_CANNOT_EXPORT_KEY;
2056 // Ok, fine... let's encrypt yon blob
2057 keys = new_stringlist(own_private_fpr);
2059 status = PEP_OUT_OF_MEMORY;
2063 stringlist_add(keys, to_fpr);
2065 char* encrypted_key_text = NULL;
2066 size_t encrypted_key_size = 0;
2068 if (flags & PEP_encrypt_flag_force_unsigned)
2069 status = encrypt_only(session, keys, priv_key_data, priv_key_size,
2070 &encrypted_key_text, &encrypted_key_size);
2072 status = encrypt_and_sign(session, keys, priv_key_data, priv_key_size,
2073 &encrypted_key_text, &encrypted_key_size);
2075 if (!encrypted_key_text) {
2076 status = PEP_UNKNOWN_ERROR;
2080 // We will have to delete this before returning, as we allocated it.
2081 bloblist_t* created_bl = NULL;
2082 bloblist_t* created_predecessor = NULL;
2084 bloblist_t* old_head = NULL;
2086 if (!src->attachments || src->attachments->value == NULL) {
2087 if (src->attachments && src->attachments->value == NULL) {
2088 old_head = src->attachments;
2089 src->attachments = NULL;
2091 src->attachments = new_bloblist(encrypted_key_text, encrypted_key_size,
2092 "application/octet-stream",
2093 "file://pEpkey.asc.pgp");
2094 created_bl = src->attachments;
2097 bloblist_t* tmp = src->attachments;
2098 while (tmp && tmp->next) {
2101 created_predecessor = tmp;
2102 created_bl = bloblist_add(tmp,
2103 encrypted_key_text, encrypted_key_size,
2104 "application/octet-stream",
2105 "file://pEpkey.asc.pgp");
2109 status = PEP_OUT_OF_MEMORY;
2113 // Ok, it's in there. Let's do this.
2114 status = encrypt_message(session, src, keys, dst, enc_format, flags);
2116 // Delete what we added to src
2117 free_bloblist(created_bl);
2118 if (created_predecessor)
2119 created_predecessor->next = NULL;
2122 src->attachments = old_head;
2124 src->attachments = NULL;
2130 free(own_private_fpr);
2131 free(priv_key_data);
2132 free_identity(own_identity);
2133 free_stringlist(keys);
2138 DYNAMIC_API PEP_STATUS encrypt_message_for_self(
2139 PEP_SESSION session,
2140 pEp_identity* target_id,
2142 stringlist_t* extra,
2144 PEP_enc_format enc_format,
2145 PEP_encrypt_flags_t flags
2148 PEP_STATUS status = PEP_STATUS_OK;
2149 message * msg = NULL;
2150 stringlist_t * keys = NULL;
2151 message* _src = src;
2157 assert(enc_format != PEP_enc_none);
2159 if (!(session && target_id && src && dst && enc_format != PEP_enc_none))
2160 return PEP_ILLEGAL_VALUE;
2162 // if (src->dir == PEP_dir_incoming)
2163 // return PEP_ILLEGAL_VALUE;
2165 determine_encryption_format(src);
2166 if (src->enc_format != PEP_enc_none)
2167 return PEP_ILLEGAL_VALUE;
2169 if (!target_id->user_id || target_id->user_id[0] == '\0') {
2170 char* own_id = NULL;
2171 status = get_default_own_userid(session, &own_id);
2173 free(target_id->user_id);
2174 target_id->user_id = own_id; // ownership transfer
2178 if (!target_id->user_id || target_id->user_id[0] == '\0')
2179 return PEP_CANNOT_FIND_IDENTITY;
2181 if (target_id->address) {
2182 status = myself(session, target_id);
2183 if (status != PEP_STATUS_OK)
2186 else if (!target_id->fpr) {
2187 return PEP_ILLEGAL_VALUE;
2192 // PEP_STATUS _status = update_identity(session, target_id);
2193 // if (_status != PEP_STATUS_OK) {
2194 // status = _status;
2198 char* target_fpr = target_id->fpr;
2200 return PEP_KEY_NOT_FOUND; // FIXME: Error condition
2202 keys = new_stringlist(target_fpr);
2204 stringlist_t *_k = keys;
2207 _k = stringlist_append(_k, extra);
2212 /* KG: did we ever do this??? */
2213 // if (!(flags & PEP_encrypt_flag_force_no_attached_key))
2214 // _attach_key(session, target_fpr, src);
2216 _src = wrap_message_as_attachment(NULL, src, PEP_message_default, false);
2220 msg = clone_to_empty_message(_src);
2224 switch (enc_format) {
2225 case PEP_enc_PGP_MIME:
2226 case PEP_enc_PEP: // BUG: should be implemented extra
2227 status = encrypt_PGP_MIME(session, _src, keys, msg, flags);
2228 if (status == PEP_STATUS_OK || (src->longmsg && strstr(src->longmsg, "INNER")))
2229 _cleanup_src(src, false);
2234 status = PEP_ILLEGAL_VALUE;
2238 if (status == PEP_OUT_OF_MEMORY)
2241 if (status != PEP_STATUS_OK)
2244 if (msg && msg->shortmsg == NULL) {
2245 msg->shortmsg = _pEp_subj_copy();
2246 assert(msg->shortmsg);
2247 if (msg->shortmsg == NULL)
2253 msg->id = strdup(_src->id);
2255 if (msg->id == NULL)
2258 decorate_message(msg, PEP_rating_undefined, NULL, true, true);
2269 status = PEP_OUT_OF_MEMORY;
2272 free_stringlist(keys);
2280 // static PEP_STATUS _update_identity_for_incoming_message(
2281 // PEP_SESSION session,
2282 // const message *src
2285 // PEP_STATUS status;
2287 // if (src->from && src->from->address) {
2288 // if (!is_me(session, src->from))
2289 // status = update_identity(session, src->from);
2291 // status = myself(session, src->from);
2292 // if (status == PEP_STATUS_OK
2293 // && is_a_pEpmessage(src)
2294 // && src->from->comm_type >= PEP_ct_OpenPGP_unconfirmed
2295 // && src->from->comm_type != PEP_ct_pEp_unconfirmed
2296 // && src->from->comm_type != PEP_ct_pEp)
2298 // src->from->comm_type |= PEP_ct_pEp_unconfirmed;
2299 // status = set_identity(session, src->from);
2303 // return PEP_ILLEGAL_VALUE;
2307 static PEP_STATUS _get_detached_signature(message* msg,
2308 bloblist_t** signature_blob) {
2309 bloblist_t* attach_curr = msg->attachments;
2311 *signature_blob = NULL;
2313 while (attach_curr) {
2314 if (attach_curr->mime_type &&
2315 (strcasecmp(attach_curr->mime_type, "application/pgp-signature") == 0)) {
2316 *signature_blob = attach_curr;
2319 attach_curr = attach_curr->next;
2322 return PEP_STATUS_OK;
2325 static PEP_STATUS _get_signed_text(const char* ptext, const size_t psize,
2326 char** stext, size_t* ssize) {
2328 char* signed_boundary = NULL;
2329 char* signpost = strstr(ptext, "Content-Type: multipart/signed");
2335 return PEP_UNKNOWN_ERROR;
2337 char* curr_line = signpost;
2338 // const char* end_text = ptext + psize;
2339 const char* boundary_key = "boundary=";
2340 const size_t BOUNDARY_KEY_SIZE = 9;
2342 char* start_boundary = strstr(curr_line, boundary_key);
2343 if (!start_boundary)
2344 return PEP_UNKNOWN_ERROR;
2346 start_boundary += BOUNDARY_KEY_SIZE;
2348 bool quoted = (*start_boundary == '"');
2353 char* end_boundary = (quoted ? strstr(start_boundary, "\"") : strstr(start_boundary, ";")); // FIXME: third possiblity is CRLF, or?
2356 return PEP_UNKNOWN_ERROR;
2358 // Add space for the "--"
2359 size_t boundary_strlen = (end_boundary - start_boundary) + 2;
2361 signed_boundary = calloc(boundary_strlen + 1, 1);
2362 assert(signed_boundary);
2363 if (!signed_boundary)
2364 return PEP_OUT_OF_MEMORY;
2366 strlcpy(signed_boundary, "--", boundary_strlen + 1);
2367 strlcat(signed_boundary, start_boundary, boundary_strlen + 1);
2369 start_boundary = strstr(end_boundary, signed_boundary);
2371 if (!start_boundary)
2372 return PEP_UNKNOWN_ERROR;
2374 start_boundary += boundary_strlen;
2376 if (*start_boundary == '\r') {
2377 if (*(start_boundary + 1) == '\n')
2378 start_boundary += 2;
2380 else if (*start_boundary == '\n')
2383 end_boundary = strstr(start_boundary + boundary_strlen, signed_boundary);
2386 return PEP_UNKNOWN_ERROR;
2388 // See RFC3156 section 5...
2390 if (*(end_boundary - 1) == '\r')
2393 *ssize = end_boundary - start_boundary;
2394 *stext = start_boundary;
2395 free(signed_boundary);
2397 return PEP_STATUS_OK;
2400 static PEP_STATUS combine_keylists(PEP_SESSION session, stringlist_t** verify_in,
2401 stringlist_t** keylist_in_out,
2402 pEp_identity* from) {
2404 if (!verify_in || !(*verify_in)) // this isn't really a problem.
2405 return PEP_STATUS_OK;
2407 stringlist_t* orig_verify = *verify_in;
2409 stringlist_t* verify_curr = NULL;
2410 stringlist_t* from_keys = NULL;
2412 /* FIXME: what to do if head needs to be null */
2413 PEP_STATUS status = find_keys(session, from->address, &from_keys);
2415 stringlist_t* from_fpr_node = NULL;
2416 stringlist_t* from_curr;
2418 for (from_curr = from_keys; from_curr; from_curr = from_curr->next) {
2419 for (verify_curr = orig_verify; verify_curr; verify_curr = verify_curr->next) {
2420 if (from_curr->value && verify_curr->value &&
2421 _same_fpr(from_curr->value, strlen(from_curr->value),
2422 verify_curr->value, strlen(verify_curr->value))) {
2423 from_fpr_node = from_curr;
2429 if (!from_fpr_node) {
2430 status = PEP_KEY_NOT_FOUND;
2434 verify_curr = orig_verify;
2436 /* put "from" signer at the beginning of the list */
2437 if (!_same_fpr(orig_verify->value, strlen(orig_verify->value),
2438 from_fpr_node->value, strlen(from_fpr_node->value))) {
2439 orig_verify = stringlist_delete(orig_verify, from_fpr_node->value);
2440 verify_curr = new_stringlist(from_fpr_node->value);
2441 verify_curr->next = orig_verify;
2444 if (keylist_in_out) {
2445 /* append keylist to signers */
2446 if (*keylist_in_out && (*keylist_in_out)->value) {
2447 stringlist_t** tail_pp = &verify_curr->next;
2450 tail_pp = &((*tail_pp)->next);
2452 stringlist_t* second_list = *keylist_in_out;
2454 char* listhead_val = second_list->value;
2455 if (!listhead_val || listhead_val[0] == '\0') {
2456 /* remove head, basically. This can happen when,
2457 for example, the signature is detached and
2458 verification is not seen directly after
2459 decryption, so no signer is presumed in
2460 the first construction of the keylist */
2461 *keylist_in_out = (*keylist_in_out)->next;
2462 second_list->next = NULL;
2463 free_stringlist(second_list);
2466 *tail_pp = *keylist_in_out;
2469 *keylist_in_out = verify_curr;
2472 status = PEP_STATUS_OK;
2475 free_stringlist(from_keys);
2479 static PEP_STATUS amend_rating_according_to_sender_and_recipients(
2480 PEP_SESSION session,
2482 pEp_identity *sender,
2483 stringlist_t *recipients) {
2485 PEP_STATUS status = PEP_STATUS_OK;
2487 if (*rating > PEP_rating_mistrust) {
2489 if (recipients == NULL) {
2490 *rating = PEP_rating_undefined;
2491 return PEP_STATUS_OK;
2494 char *fpr = recipients->value;
2496 if (!(sender && sender->user_id && sender->user_id[0] && fpr && fpr[0])) {
2497 *rating = PEP_rating_unreliable;
2500 pEp_identity *_sender = new_identity(sender->address, fpr,
2501 sender->user_id, sender->username);
2502 if (_sender == NULL)
2503 return PEP_OUT_OF_MEMORY;
2505 status = get_trust(session, _sender);
2506 if (_sender->comm_type == PEP_ct_unknown) {
2507 get_key_rating(session, fpr, &_sender->comm_type);
2509 if (_sender->comm_type != PEP_ct_unknown) {
2510 *rating = keylist_rating(session, recipients,
2511 fpr, _rating(_sender->comm_type,
2512 PEP_rating_undefined));
2515 free_identity(_sender);
2516 if (status == PEP_CANNOT_FIND_IDENTITY)
2517 status = PEP_STATUS_OK;
2523 // FIXME: Do we need to remove the attachment? I think we do...
2524 static bool pull_up_attached_main_msg(message* src) {
2525 char* slong = src->longmsg;
2526 char* sform = src->longmsg_formatted;
2527 bloblist_t* satt = src->attachments;
2529 if ((!slong || slong[0] == '\0')
2530 && (!sform || sform[0] == '\0')) {
2532 const char* inner_mime_type = satt->mime_type;
2533 if (strcasecmp(inner_mime_type, "text/plain") == 0) {
2534 free(slong); /* in case of "" */
2535 src->longmsg = strndup(satt->value, satt->size);
2537 bloblist_t* next_node = satt->next;
2539 inner_mime_type = next_node->mime_type;
2540 if (strcasecmp(inner_mime_type, "text/html") == 0) {
2542 src->longmsg_formatted = strndup(next_node->value, next_node->size);
2546 else if (strcasecmp(inner_mime_type, "text/html") == 0) {
2548 src->longmsg_formatted = strndup(satt->value, satt->size);
2558 static PEP_STATUS unencapsulate_hidden_fields(message* src, message* msg,
2559 char** msg_wrap_info) {
2561 return PEP_ILLEGAL_VALUE;
2562 unsigned char pEpstr[] = PEP_SUBJ_STRING;
2563 PEP_STATUS status = PEP_STATUS_OK;
2565 bool change_source_in_place = (msg ? false : true);
2567 if (change_source_in_place)
2571 switch (src->enc_format) {
2572 case PEP_enc_PGP_MIME:
2573 case PEP_enc_pieces:
2574 case PEP_enc_PGP_MIME_Outlook1:
2575 // case PEP_enc_none: // FIXME - this is wrong
2577 if (!change_source_in_place)
2578 status = copy_fields(msg, src);
2580 if (status != PEP_STATUS_OK)
2583 // FIXME: This is a mess. Talk with VB about how far we go to identify
2584 if (is_a_pEpmessage(src) || (src->shortmsg == NULL || strcmp(src->shortmsg, "pEp") == 0 ||
2585 _unsigned_signed_strcmp(pEpstr, src->shortmsg, PEP_SUBJ_BYTELEN) == 0) ||
2586 (strcmp(src->shortmsg, "p=p") == 0))
2588 char * shortmsg = NULL;
2589 char * longmsg = NULL;
2592 int r = separate_short_and_long(msg->longmsg,
2598 return PEP_OUT_OF_MEMORY;
2601 // We only use the shortmsg in version 1.0 messages; if it occurs where we
2602 // didn't replace the subject, we ignore this all
2603 if (!(*msg_wrap_info || change_source_in_place)) {
2605 (src->shortmsg != NULL && strcmp(src->shortmsg, "pEp") != 0 &&
2606 _unsigned_signed_strcmp(pEpstr, src->shortmsg, PEP_SUBJ_BYTELEN) != 0 &&
2607 strcmp(src->shortmsg, "p=p") != 0)) {
2609 if (shortmsg != NULL)
2612 if (src->shortmsg == NULL) {
2613 shortmsg = strdup("");
2616 // FIXME: is msg->shortmsg always a copy of
2617 // src->shortmsg already?
2618 // if so, we need to change the logic so
2619 // that in this case, we don't free msg->shortmsg
2620 // and do this strdup, etc
2621 shortmsg = strdup(src->shortmsg);
2624 free(msg->shortmsg);
2625 msg->shortmsg = shortmsg;
2630 msg->longmsg = longmsg;
2633 if (!change_source_in_place) {
2634 msg->shortmsg = strdup(src->shortmsg);
2635 assert(msg->shortmsg);
2636 if (msg->shortmsg == NULL)
2637 return PEP_OUT_OF_MEMORY;
2642 // BUG: must implement more
2645 return PEP_STATUS_OK;
2649 static PEP_STATUS get_crypto_text(message* src, char** crypto_text, size_t* text_size) {
2651 // this is only here because of how NOT_IMPLEMENTED works
2652 PEP_STATUS status = PEP_STATUS_OK;
2654 switch (src->enc_format) {
2655 case PEP_enc_PGP_MIME:
2656 *crypto_text = src->attachments->next->value;
2657 *text_size = src->attachments->next->size;
2660 case PEP_enc_PGP_MIME_Outlook1:
2661 *crypto_text = src->attachments->value;
2662 *text_size = src->attachments->size;
2665 case PEP_enc_pieces:
2666 *crypto_text = src->longmsg;
2667 *text_size = strlen(*crypto_text);
2678 static PEP_STATUS verify_decrypted(PEP_SESSION session,
2682 size_t plaintext_size,
2683 stringlist_t** keylist,
2684 PEP_STATUS* decrypt_status,
2685 PEP_cryptotech crypto) {
2687 assert(src && src->from);
2689 if (!src && !src->from)
2690 return PEP_ILLEGAL_VALUE;
2692 PEP_STATUS _cached_decrypt_status = *decrypt_status;
2694 pEp_identity* sender = src->from;
2696 bloblist_t* detached_sig = NULL;
2697 PEP_STATUS status = _get_detached_signature(msg, &detached_sig);
2698 stringlist_t *verify_keylist = NULL;
2702 char* dsig_text = detached_sig->value;
2703 size_t dsig_size = detached_sig->size;
2707 status = _get_signed_text(plaintext, plaintext_size, &stext, &ssize);
2709 if (ssize > 0 && stext) {
2710 status = cryptotech[crypto].verify_text(session, stext,
2711 ssize, dsig_text, dsig_size,
2715 if (status == PEP_VERIFIED || status == PEP_VERIFIED_AND_TRUSTED)
2717 *decrypt_status = PEP_DECRYPTED_AND_VERIFIED;
2719 status = combine_keylists(session, &verify_keylist, keylist, sender);
2723 size_t csize, psize;
2726 get_crypto_text(src, &ctext, &csize);
2727 // reverify - we may have imported a key in the meantime
2728 // status = cryptotech[crypto].verify_text(session, ctext,
2730 // &verify_keylist);
2731 free_stringlist(*keylist);
2732 *decrypt_status = decrypt_and_verify(session, ctext, csize,
2734 &ptext, &psize, keylist,
2739 if (*decrypt_status != PEP_DECRYPTED_AND_VERIFIED)
2740 *decrypt_status = _cached_decrypt_status;
2742 return PEP_STATUS_OK;
2745 static PEP_STATUS _decrypt_in_pieces(PEP_SESSION session,
2751 PEP_STATUS status = PEP_STATUS_OK;
2753 *msg_ptr = clone_to_empty_message(src);
2755 if (*msg_ptr == NULL)
2756 return PEP_OUT_OF_MEMORY;
2758 message* msg = *msg_ptr;
2760 msg->longmsg = strdup(ptext);
2763 bloblist_t *_m = msg->attachments;
2764 if (_m == NULL && src->attachments && src->attachments->value) {
2765 msg->attachments = new_bloblist(NULL, 0, NULL, NULL);
2766 _m = msg->attachments;
2770 for (_s = src->attachments; _s && _s->value; _s = _s->next) {
2771 if (_s->value == NULL && _s->size == 0){
2772 _m = bloblist_add(_m, NULL, 0, _s->mime_type, _s->filename);
2774 return PEP_OUT_OF_MEMORY;
2777 else if (is_encrypted_attachment(_s)) {
2778 stringlist_t *_keylist = NULL;
2779 char *attctext = _s->value;
2780 size_t attcsize = _s->size;
2785 char* pgp_filename = NULL;
2786 status = decrypt_and_verify(session, attctext, attcsize,
2788 &ptext, &psize, &_keylist,
2791 free_stringlist(_keylist);
2794 if (is_encrypted_html_attachment(_s)) {
2795 msg->longmsg_formatted = ptext;
2799 static const char * const mime_type = "application/octet-stream";
2801 _m = bloblist_add(_m, ptext, psize, mime_type,
2805 return PEP_OUT_OF_MEMORY;
2808 char * const filename =
2809 without_double_ending(_s->filename);
2810 if (filename == NULL)
2811 return PEP_OUT_OF_MEMORY;
2813 _m = bloblist_add(_m, ptext, psize, mime_type,
2817 return PEP_OUT_OF_MEMORY;
2821 if (msg->attachments == NULL)
2822 msg->attachments = _m;
2826 char *copy = malloc(_s->size);
2829 return PEP_OUT_OF_MEMORY;
2830 memcpy(copy, _s->value, _s->size);
2831 _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
2833 return PEP_OUT_OF_MEMORY;
2837 char *copy = malloc(_s->size);
2840 return PEP_OUT_OF_MEMORY;
2841 memcpy(copy, _s->value, _s->size);
2842 _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
2844 return PEP_OUT_OF_MEMORY;
2851 static PEP_STATUS import_priv_keys_from_decrypted_msg(PEP_SESSION session,
2854 bool* imported_keys,
2855 bool* imported_private,
2856 identity_list** private_il) {
2858 PEP_STATUS status = PEP_STATUS_OK;
2860 // check for private key in decrypted message attachment while importing
2861 identity_list *_private_il = NULL;
2862 *imported_keys = import_attached_keys(session, msg, &_private_il);
2864 if (_private_il && identity_list_length(_private_il) == 1 &&
2865 _private_il->ident->address)
2866 *imported_private = true;
2868 if (private_il && imported_private) {
2869 // the private identity list should NOT be subject to myself() or
2870 // update_identity() at this point.
2871 // If the receiving app wants them to be in the trust DB, it
2872 // should call set_own_key() on them upon return.
2873 // We do, however, prepare these so the app can use them
2874 // directly in a set_own_key() call by putting the own_id on it.
2875 char* own_id = NULL;
2876 status = get_default_own_userid(session, &own_id);
2878 if (status != PEP_STATUS_OK) {
2883 identity_list* il = _private_il;
2884 for ( ; il; il = il->next) {
2886 free(il->ident->user_id);
2887 il->ident->user_id = strdup(own_id);
2889 il->ident->me = true;
2891 *private_il = _private_il;
2896 free_identity_list(_private_il);
2902 static PEP_STATUS update_sender_to_pEp_trust(
2903 PEP_SESSION session,
2904 pEp_identity* sender,
2905 stringlist_t* keylist)
2909 assert(keylist && !EMPTYSTR(keylist->value));
2911 if (!session || !sender || !keylist || EMPTYSTR(keylist->value))
2912 return PEP_ILLEGAL_VALUE;
2918 is_me(session, sender) ? myself(session, sender) : update_identity(session, sender);
2920 if (EMPTYSTR(sender->fpr) || strcmp(sender->fpr, keylist->value) != 0) {
2922 sender->fpr = strdup(keylist->value);
2924 return PEP_OUT_OF_MEMORY;
2925 status = set_pgp_keypair(session, sender->fpr);
2926 if (status != PEP_STATUS_OK)
2929 status = get_trust(session, sender);
2931 if (status == PEP_CANNOT_FIND_IDENTITY || sender->comm_type == PEP_ct_unknown) {
2932 PEP_comm_type ct = PEP_ct_unknown;
2933 status = get_key_rating(session, sender->fpr, &ct);
2934 if (status != PEP_STATUS_OK)
2937 sender->comm_type = ct;
2941 // Could be done elegantly, but we do this explicitly here for readability.
2942 // This file's code is difficult enough to parse. But change at will.
2943 switch (sender->comm_type) {
2944 case PEP_ct_OpenPGP_unconfirmed:
2945 case PEP_ct_OpenPGP:
2946 sender->comm_type = PEP_ct_pEp_unconfirmed | (sender->comm_type & PEP_ct_confirmed);
2947 status = set_trust(session, sender);
2950 status = PEP_CANNOT_SET_TRUST;
2957 static PEP_STATUS reconcile_identity(pEp_identity* srcid,
2958 pEp_identity* resultid) {
2962 if (!srcid || !resultid)
2963 return PEP_ILLEGAL_VALUE;
2965 if (!EMPTYSTR(srcid->user_id)) {
2966 if (EMPTYSTR(resultid->user_id) ||
2967 strcmp(srcid->user_id, resultid->user_id) != 0) {
2968 free(resultid->user_id);
2969 resultid->user_id = strdup(srcid->user_id);
2973 resultid->lang[0] = srcid->lang[0];
2974 resultid->me = srcid->me;
2975 resultid->flags = srcid->flags;
2977 return PEP_STATUS_OK;
2980 static PEP_STATUS reconcile_identity_lists(identity_list* src_ids,
2981 identity_list* result_ids) {
2983 identity_list* curr_id = result_ids;
2985 PEP_STATUS status = PEP_STATUS_OK;
2988 identity_list* curr_src_id = src_ids;
2989 pEp_identity* result_identity = curr_id->ident;
2991 while (curr_src_id) {
2992 pEp_identity* source_identity = curr_src_id->ident;
2994 if (EMPTYSTR(source_identity->address) || EMPTYSTR(result_identity->address))
2995 return PEP_ILLEGAL_VALUE; // something went badly wrong
2997 if (strcasecmp(source_identity->address, result_identity->address) == 0) {
2998 status = reconcile_identity(source_identity, result_identity);
2999 if (status != PEP_STATUS_OK)
3002 curr_src_id = curr_src_id->next;
3004 curr_id = curr_id->next;
3009 static PEP_STATUS reconcile_src_and_inner_messages(message* src,
3010 message* inner_message) {
3012 PEP_STATUS status = PEP_STATUS_OK;
3014 if (strcasecmp(src->from->address, inner_message->from->address) == 0)
3015 status = reconcile_identity(src->from, inner_message->from);
3017 if (status == PEP_STATUS_OK && inner_message->to)
3018 status = reconcile_identity_lists(src->to, inner_message->to);
3020 if (status == PEP_STATUS_OK && inner_message->cc)
3021 status = reconcile_identity_lists(src->cc, inner_message->cc);
3023 if (status == PEP_STATUS_OK && inner_message->bcc)
3024 status = reconcile_identity_lists(src->bcc, inner_message->bcc);
3027 // FIXME - are there any flags or anything else we need to be sure are carried?
3030 static bool is_trusted_own_priv_fpr(PEP_SESSION session,
3035 bool retval = false;
3036 if (!EMPTYSTR(fpr)) {
3037 pEp_identity* test_identity = new_identity(NULL, fpr, own_id, NULL);
3038 if (test_identity) {
3039 PEP_STATUS status = get_trust(session, test_identity);
3040 if (status == PEP_STATUS_OK) {
3041 if (test_identity->comm_type & PEP_ct_confirmed) {
3042 bool has_priv = false;
3043 status = contains_priv_key(session, fpr, &has_priv);
3044 if (status == PEP_STATUS_OK && has_priv)
3048 free(test_identity);
3054 static bool reject_fpr(PEP_SESSION session, const char* fpr) {
3057 PEP_STATUS status = key_revoked(session, fpr, &reject);
3060 status = key_expired(session, fpr, time(NULL), &reject);
3062 timestamp *ts = new_timestamp(time(NULL) + KEY_EXPIRE_DELTA);
3063 status = renew_key(session, fpr, ts);
3065 if (status == PEP_STATUS_OK)
3072 static char* seek_good_trusted_private_fpr(PEP_SESSION session, char* own_id,
3073 stringlist_t* keylist) {
3074 if (!own_id || !keylist)
3077 stringlist_t* kl_curr = keylist;
3079 char* fpr = kl_curr->value;
3081 if (is_trusted_own_priv_fpr(session, own_id, fpr)) {
3082 if (!reject_fpr(session, fpr))
3086 kl_curr = kl_curr->next;
3089 char* target_own_fpr = NULL;
3092 PEP_STATUS status = get_user_default_key(session, own_id,
3095 if (status == PEP_STATUS_OK && !EMPTYSTR(target_own_fpr)) {
3096 if (is_trusted_own_priv_fpr(session, own_id, target_own_fpr)) {
3097 if (!reject_fpr(session, target_own_fpr))
3098 return target_own_fpr;
3102 // TODO: We can also go through all of the other available fprs for the
3103 // own identity, but then I submit this function requires a little refactoring
3108 static bool import_header_keys(PEP_SESSION session, message* src) {
3109 stringpair_list_t* header_keys = stringpair_list_find(src->opt_fields, "Autocrypt");
3110 if (!header_keys || !header_keys->value)
3112 const char* value = header_keys->value->value;
3115 const char* start_key = strstr(value, "keydata=");
3118 start_key += 8; // length of "keydata="
3119 int length = strlen(start_key);
3120 bloblist_t* the_key = base64_str_to_binary_blob(start_key, length);
3123 PEP_STATUS status = import_key(session, the_key->value, the_key->size, NULL);
3124 free_bloblist(the_key);
3125 if (status == PEP_STATUS_OK)
3130 PEP_STATUS check_for_own_revoked_key(
3131 PEP_SESSION session,
3132 stringlist_t* keylist,
3133 stringpair_list_t** revoked_fpr_pairs
3136 if (!session || !revoked_fpr_pairs)
3137 return PEP_ILLEGAL_VALUE;
3139 *revoked_fpr_pairs = NULL;
3141 PEP_STATUS status = PEP_STATUS_OK;
3142 stringpair_list_t* _the_list = new_stringpair_list(NULL);
3144 stringlist_t* _k = keylist;
3145 for ( ; _k; _k = _k->next) {
3147 if (EMPTYSTR(_k->value))
3148 continue; // Maybe the right thing to do is choke.
3149 // But we can have NULL-valued empty list heads.
3151 const char* recip_fpr = _k->value;
3152 char* replace_fpr = NULL;
3153 uint64_t revoke_date = 0;
3154 status = get_replacement_fpr(session,
3159 bool own_key = false;
3162 case PEP_CANNOT_FIND_IDENTITY:
3163 status = PEP_STATUS_OK;
3167 status = is_own_key(session, recip_fpr, &own_key);
3169 if (status != PEP_STATUS_OK) {
3175 stringpair_list_add(_the_list, new_stringpair(recip_fpr, replace_fpr));
3185 if (_the_list && _the_list->value) {
3186 *revoked_fpr_pairs = _the_list;
3191 free_stringpair_list(_the_list);
3196 DYNAMIC_API PEP_STATUS _decrypt_message(
3197 PEP_SESSION session,
3200 stringlist_t **keylist,
3202 PEP_decrypt_flags_t *flags,
3203 identity_list **private_il
3214 if (!(session && src && dst && keylist && rating && flags))
3215 return PEP_ILLEGAL_VALUE;
3217 /*** Begin init ***/
3218 PEP_STATUS status = PEP_STATUS_OK;
3219 PEP_STATUS decrypt_status = PEP_CANNOT_DECRYPT_UNKNOWN;
3220 PEP_STATUS _decrypt_in_pieces_status = PEP_CANNOT_DECRYPT_UNKNOWN;
3221 message* msg = NULL;
3222 message* calculated_src = src;
3223 message* reset_msg = NULL;
3229 stringlist_t *_keylist = NULL;
3230 char* signer_fpr = NULL;
3231 bool is_pEp_msg = is_a_pEpmessage(src);
3234 bool reencrypt = (((*flags & PEP_decrypt_flag_untrusted_server) > 0) && *keylist && !EMPTYSTR((*keylist)->value));
3236 // We own this pointer, and we take control of *keylist if reencrypting.
3237 stringlist_t* extra = NULL;
3246 *rating = PEP_rating_undefined;
3251 // Ok, before we do anything, if it's a pEp message, regardless of whether it's
3252 // encrypted or not, we set the sender as a pEp user. This has NOTHING to do
3254 if (src->from && !(is_me(session, src->from))) {
3256 pEp_identity* tmp_from = src->from;
3258 // Ensure there's a user id
3259 if (EMPTYSTR(tmp_from->user_id) && tmp_from->address) {
3260 status = update_identity(session, tmp_from);
3261 if (status == PEP_CANNOT_FIND_IDENTITY) {
3262 tmp_from->user_id = calloc(1, strlen(tmp_from->address) + 6);
3263 if (!tmp_from->user_id)
3264 return PEP_OUT_OF_MEMORY;
3265 snprintf(tmp_from->user_id, strlen(tmp_from->address) + 6,
3266 "TOFU_%s", tmp_from->address);
3267 status = PEP_STATUS_OK;
3270 if (status == PEP_STATUS_OK) {
3271 // Now set user as PEP (may also create an identity if none existed yet)
3272 status = set_as_pEp_user(session, tmp_from);
3276 // We really need key used in signing to do anything further on the pEp comm_type.
3277 // So we can't adjust the rating of the sender just yet.
3279 /*** Begin Import any attached public keys and update identities accordingly ***/
3280 // Private key in unencrypted mail are ignored -> NULL
3281 bool imported_keys = import_attached_keys(session, src, NULL);
3282 import_header_keys(session, src);
3284 // FIXME: is this really necessary here?
3286 if (!is_me(session, src->from))
3287 status = update_identity(session, src->from);
3289 status = myself(session, src->from);
3291 // We absolutely should NOT be bailing here unless it's a serious error
3292 if (status == PEP_OUT_OF_MEMORY)
3296 /*** End Import any attached public keys and update identities accordingly ***/
3298 /*** Begin get detached signatures that are attached to the encrypted message ***/
3299 // Get detached signature, if any
3300 bloblist_t* detached_sig = NULL;
3301 char* dsig_text = NULL;
3302 size_t dsig_size = 0;
3303 status = _get_detached_signature(src, &detached_sig);
3305 dsig_text = detached_sig->value;
3306 dsig_size = detached_sig->size;
3308 /*** End get detached signatures that are attached to the encrypted message ***/
3310 /*** Determine encryption format ***/
3311 PEP_cryptotech crypto = determine_encryption_format(src);
3313 // Check for and deal with unencrypted messages
3314 if (src->enc_format == PEP_enc_none) {
3316 *rating = PEP_rating_unencrypted;
3319 remove_attached_keys(src);
3321 pull_up_attached_main_msg(src);
3323 return PEP_UNENCRYPTED;
3326 status = get_crypto_text(src, &ctext, &csize);
3327 if (status != PEP_STATUS_OK)
3330 /** Ok, we should be ready to decrypt. Try decrypt and verify first! **/
3331 status = cryptotech[crypto].decrypt_and_verify(session, ctext,
3332 csize, dsig_text, dsig_size,
3333 &ptext, &psize, &_keylist,
3336 if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
3339 decrypt_status = status;
3341 bool imported_private_key_address = false;
3344 /* we got a plaintext from decryption */
3345 switch (src->enc_format) {
3347 case PEP_enc_PGP_MIME:
3348 case PEP_enc_PGP_MIME_Outlook1:
3350 status = mime_decode_message(ptext, psize, &msg);
3351 if (status != PEP_STATUS_OK)
3354 /* Ensure messages whose maintext is in the attachments
3355 move main text into message struct longmsg et al */
3356 /* KG: This IS a src modification of old - we're adding to it
3357 w/ memhole subject, but the question is whether or not
3358 this is OK overall... */
3359 pull_up_attached_main_msg(msg);
3360 if (msg->shortmsg) {
3361 free(src->shortmsg);
3362 src->shortmsg = strdup(msg->shortmsg);
3365 // check for private key in decrypted message attachment while importing
3366 // N.B. Apparently, we always import private keys into the keyring; however,
3367 // we do NOT always allow those to be used for encryption. THAT is controlled
3368 // by setting it as an own identity associated with the key in the DB.
3369 status = import_priv_keys_from_decrypted_msg(session, src, msg,
3371 &imported_private_key_address,
3373 if (status != PEP_STATUS_OK)
3376 /* if decrypted, but not verified... */
3377 if (decrypt_status == PEP_DECRYPTED) {
3380 status = verify_decrypted(session,
3389 case PEP_enc_pieces:
3390 status = PEP_STATUS_OK;
3392 _decrypt_in_pieces_status = _decrypt_in_pieces(session, src, &msg, ptext, psize);
3394 switch (_decrypt_in_pieces_status) {
3396 case PEP_DECRYPTED_AND_VERIFIED:
3397 if (decrypt_status <= PEP_DECRYPTED_AND_VERIFIED)
3398 decrypt_status = MIN(decrypt_status, _decrypt_in_pieces_status);
3402 case PEP_OUT_OF_MEMORY:
3405 decrypt_status = _decrypt_in_pieces_status;
3410 // BUG: must implement more
3414 if (status == PEP_OUT_OF_MEMORY)
3417 if (status != PEP_STATUS_OK)
3420 if (decrypt_status == PEP_DECRYPTED || decrypt_status == PEP_DECRYPTED_AND_VERIFIED) {
3421 char* wrap_info = NULL;
3423 status = unencapsulate_hidden_fields(src, msg, &wrap_info);
3425 // bool is_transport_wrapper = false;
3427 // FIXME: replace with enums, check status
3429 if (strcmp(wrap_info, "OUTER") == 0) {
3430 // this only occurs in with a direct outer wrapper
3431 // where the actual content is in the inner wrapper
3432 message* inner_message = NULL;
3433 bloblist_t* actual_message = msg->attachments;
3435 while (actual_message) {
3436 char* mime_type = actual_message->mime_type;
3439 // libetpan appears to change the mime_type on this one.
3441 if (strcmp("message/rfc822", mime_type) == 0 ||
3442 strcmp("text/rfc822", mime_type) == 0) {
3444 status = mime_decode_message(actual_message->value,
3445 actual_message->size,
3447 if (status != PEP_STATUS_OK)
3450 if (inner_message) {
3451 // Though this will strip any message info on the
3452 // attachment, this is safe, as we do not
3453 // produce more than one attachment-as-message,
3454 // and those are the only ones with such info.
3455 // Since we capture the information, this is ok.
3457 inner_message->enc_format = src->enc_format;
3459 status = unencapsulate_hidden_fields(inner_message, NULL, &wrap_info);
3462 if (status != PEP_STATUS_OK) {
3463 free_message(inner_message);
3468 bool is_inner = (strcmp(wrap_info, "INNER") == 0);
3469 bool is_key_reset = (strcmp(wrap_info, "KEY_RESET") == 0);
3472 if (decrypt_status == PEP_DECRYPTED || decrypt_status == PEP_DECRYPTED_AND_VERIFIED) {
3473 status = receive_key_reset(session,
3475 if (status != PEP_STATUS_OK) {
3476 free_message(inner_message);
3479 *flags |= PEP_decrypt_flag_consume;
3482 else if (is_inner) {
3484 // check for private key in decrypted message attachment while importing
3485 // N.B. Apparently, we always import private keys into the keyring; however,
3486 // we do NOT always allow those to be used for encryption. THAT is controlled
3487 // by setting it as an own identity associated with the key in the DB.
3489 // If we have a message 2.0 message, we are ONLY going to be ok with keys
3490 // we imported from THIS part of the message.
3491 imported_private_key_address = false;
3494 status = import_priv_keys_from_decrypted_msg(session, src, inner_message,
3496 &imported_private_key_address,
3498 if (status != PEP_STATUS_OK)
3501 // THIS is our message
3502 // Now, let's make sure we've copied in
3503 // any information sent in by the app if
3505 reconcile_src_and_inner_messages(src, inner_message);
3508 // FIXME: free msg, but check references
3509 //src = msg = inner_message;
3510 calculated_src = msg = inner_message;
3512 // FIXME: should this be msg???
3514 if (!is_me(session, src->from))
3515 update_identity(session, (src->from));
3517 myself(session, src->from);
3521 else { // should never happen
3522 status = PEP_UNKNOWN_ERROR;
3523 free_message(inner_message);
3527 inner_message->enc_format = PEP_enc_none;
3529 else { // forwarded message, leave it alone
3530 free_message(inner_message);
3534 actual_message = actual_message->next;
3537 else if (strcmp(wrap_info, "TRANSPORT") == 0) {
3538 // FIXME: this gets even messier.
3539 // (TBI in ENGINE-278)
3541 else {} // shouldn't be anything to be done here
3545 *rating = decrypt_rating(decrypt_status);
3547 // Ok, so if it was signed and it's all verified, we can update
3548 // eligible signer comm_types to PEP_ct_pEp_*
3549 if (decrypt_status == PEP_DECRYPTED_AND_VERIFIED && is_pEp_msg && calculated_src->from)
3550 status = update_sender_to_pEp_trust(session, calculated_src->from, _keylist);
3552 /* Ok, now we have a keylist used for decryption/verification.
3553 now we need to update the message rating with the
3554 sender and recipients in mind */
3555 status = amend_rating_according_to_sender_and_recipients(session,
3556 rating, calculated_src->from, _keylist);
3558 if (status != PEP_STATUS_OK)
3561 /* We decrypted ok, hallelujah. */
3562 msg->enc_format = PEP_enc_none;
3565 // We did not get a plaintext out of the decryption process.
3566 // Abort and return error.
3567 *rating = decrypt_rating(decrypt_status);
3572 Ok, at this point, we know we have a reliably decrypted message.
3573 Prepare the output message for return.
3576 // 1. Check to see if this message is to us and contains an own key imported
3577 // from own trusted message
3578 if (*rating >= PEP_rating_trusted && imported_private_key_address) {
3580 if (msg && msg->to && msg->to->ident) {
3581 // This will only happen rarely, so we can do this.
3582 PEP_STATUS _tmp_status = PEP_STATUS_OK;
3584 if (!is_me(session, msg->to->ident))
3585 _tmp_status = update_identity(session, msg->to->ident);
3587 if (_tmp_status == PEP_STATUS_OK && is_me(session, msg->to->ident)) {
3589 *flags |= PEP_decrypt_flag_own_private_key;
3594 // 2. Clean up message and prepare for return
3597 /* add pEp-related status flags to header */
3598 decorate_message(msg, *rating, _keylist, false, false);
3601 remove_attached_keys(msg);
3603 if (calculated_src->id && calculated_src != msg) {
3604 msg->id = strdup(calculated_src->id);
3606 if (msg->id == NULL)
3609 } // End prepare output message for return
3611 // 3. Check to see if the sender used any of our revoked keys
3612 stringpair_list_t* revoke_replace_pairs = NULL;
3613 status = check_for_own_revoked_key(session, _keylist, &revoke_replace_pairs);
3615 //assert(status != PEP_STATUS_OK); // FIXME: FOR DEBUGGING ONLY DO NOT LEAVE IN
3616 if (status != PEP_STATUS_OK) {
3617 // This should really never choke unless the DB is broken.
3618 status = PEP_UNKNOWN_DB_ERROR;
3623 stringpair_list_t* curr_pair_node;
3624 stringpair_t* curr_pair;
3626 for (curr_pair_node = revoke_replace_pairs; curr_pair_node; curr_pair_node = curr_pair_node->next) {
3627 curr_pair = curr_pair_node->value;
3630 continue; // Again, shouldn't occur
3632 if (curr_pair->key && curr_pair->value) {
3633 status = create_standalone_key_reset_message(session,
3639 // If we can't find the identity, this is someone we've never mailed, so we just
3640 // go on letting them use the wrong key until we mail them ourselves. (Spammers, etc)
3641 if (status != PEP_CANNOT_FIND_IDENTITY) {
3642 if (status != PEP_STATUS_OK)
3646 status = PEP_OUT_OF_MEMORY;
3649 // insert into queue
3650 if (session->messageToSend)
3651 status = session->messageToSend(reset_msg);
3653 status = PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
3656 if (status == PEP_STATUS_OK) {
3657 // Put into notified DB
3658 status = set_reset_contact_notified(session, curr_pair->key, msg->from->user_id);
3659 if (status != PEP_STATUS_OK) // It's ok to barf because it's a DB problem??
3663 // According to Volker, this would only be a fatal error, so...
3664 free_message(reset_msg); // ??
3665 reset_msg = NULL; // ??
3673 // 4. Set up return values
3675 *keylist = _keylist;
3677 // 5. Reencrypt if necessary
3679 if (decrypt_status == PEP_DECRYPTED || decrypt_status == PEP_DECRYPTED_AND_VERIFIED) {
3680 message* reencrypt_msg = NULL;
3681 PEP_STATUS reencrypt_status = PEP_CANNOT_REENCRYPT;
3682 char* own_id = NULL;
3683 status = get_default_own_userid(session, &own_id);
3685 char* target_own_fpr = seek_good_trusted_private_fpr(session,
3688 if (target_own_fpr) {
3689 pEp_identity* target_id = new_identity(NULL, target_own_fpr,
3692 reencrypt_status = encrypt_message_for_self(session, target_id, msg,
3693 extra, &reencrypt_msg, PEP_enc_PGP_MIME,
3695 if (reencrypt_status != PEP_STATUS_OK)
3696 reencrypt_status = PEP_CANNOT_REENCRYPT;
3698 free_identity(target_id);
3700 free(target_own_fpr);
3704 free_stringlist(extra); // This was an input variable for us. Keylist is overwritten above.
3706 if (reencrypt_status != PEP_CANNOT_REENCRYPT && reencrypt_msg) {
3707 message_transfer(src, reencrypt_msg);
3708 *flags |= PEP_decrypt_flag_src_modified;
3709 free_message(reencrypt_msg);
3712 decrypt_status = PEP_CANNOT_REENCRYPT;
3716 if(decrypt_status == PEP_DECRYPTED_AND_VERIFIED)
3717 return PEP_STATUS_OK;
3719 return decrypt_status;
3722 status = PEP_OUT_OF_MEMORY;
3728 free_message(reset_msg);
3729 free_stringlist(_keylist);
3734 DYNAMIC_API PEP_STATUS decrypt_message(
3735 PEP_SESSION session,
3738 stringlist_t **keylist,
3740 PEP_decrypt_flags_t *flags
3750 if (!(session && src && dst && keylist && rating && flags))
3751 return PEP_ILLEGAL_VALUE;
3753 if (!(*flags & PEP_decrypt_flag_untrusted_server))
3755 //*keylist = NULL; // NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO!!!!!! This fucks up reencryption
3756 PEP_STATUS status = _decrypt_message(session, src, dst, keylist, rating, flags, NULL);
3758 message *msg = *dst ? *dst : src;
3760 if (session->inject_sync_event && msg && msg->from) {
3763 char *sync_fpr = NULL;
3764 status = base_extract_message(session, msg, &size, &data, &sync_fpr);
3765 if (!status && size && data) {
3766 pEp_identity *_from = identity_dup(msg->from);
3770 free_stringlist(*keylist);
3772 return PEP_OUT_OF_MEMORY;
3774 session->sync_state.common.from = _from;
3775 signal_Sync_message(session, *rating, data, size, sync_fpr);
3783 DYNAMIC_API PEP_STATUS own_message_private_key_details(
3784 PEP_SESSION session,
3786 pEp_identity **ident
3793 if (!(session && msg && ident))
3794 return PEP_ILLEGAL_VALUE;
3796 message *dst = NULL;
3797 stringlist_t *keylist = NULL;
3799 PEP_decrypt_flags_t flags;
3803 identity_list *private_il = NULL;
3804 PEP_STATUS status = _decrypt_message(session, msg, &dst, &keylist, &rating, &flags, &private_il);
3806 free_stringlist(keylist);
3808 if (status == PEP_STATUS_OK &&
3809 flags & PEP_decrypt_flag_own_private_key &&
3812 *ident = identity_dup(private_il->ident);
3815 free_identity_list(private_il);
3820 // Note: if comm_type_determine is false, it generally means that
3821 // we were unable to get key information for anyone in the list,
3822 // likely because a key is missing.
3823 static void _max_comm_type_from_identity_list(
3824 identity_list *identities,
3825 PEP_SESSION session,
3826 PEP_comm_type *max_comm_type,
3827 bool *comm_type_determined
3831 for (il = identities; il != NULL; il = il->next)
3835 PEP_STATUS status = PEP_STATUS_OK;
3836 *max_comm_type = _get_comm_type(session, *max_comm_type,
3838 *comm_type_determined = true;
3840 bool is_blacklisted = false;
3841 if (il->ident->fpr && IS_PGP_CT(il->ident->comm_type)) {
3842 status = blacklist_is_listed(session, il->ident->fpr, &is_blacklisted);
3843 if (is_blacklisted) {
3844 bool user_default, ident_default, address_default;
3845 status = get_valid_pubkey(session, il->ident,
3846 &ident_default, &user_default,
3849 if (status != PEP_STATUS_OK || il->ident->fpr == NULL) {
3850 il->ident->comm_type = PEP_ct_key_not_found;
3851 if (*max_comm_type > PEP_ct_no_encryption)
3852 *max_comm_type = PEP_ct_no_encryption;
3857 // check for the return statuses which might not a representative
3858 // value in the comm_type
3859 if (status == PEP_ILLEGAL_VALUE || status == PEP_CANNOT_SET_PERSON ||
3860 status == PEP_CANNOT_FIND_IDENTITY) {
3861 // PEP_CANNOT_FIND_IDENTITY only comes back when we've really
3862 // got nothing from update_identity after applying the whole
3864 *max_comm_type = PEP_ct_no_encryption;
3865 *comm_type_determined = true;
3871 static void _max_comm_type_from_identity_list_preview(
3872 identity_list *identities,
3873 PEP_SESSION session,
3874 PEP_comm_type *max_comm_type
3878 for (il = identities; il != NULL; il = il->next)
3882 *max_comm_type = _get_comm_type_preview(session, *max_comm_type,
3888 DYNAMIC_API PEP_STATUS outgoing_message_rating(
3889 PEP_SESSION session,
3894 PEP_comm_type max_comm_type = PEP_ct_pEp;
3895 bool comm_type_determined = false;
3899 assert(msg->dir == PEP_dir_outgoing);
3902 if (!(session && msg && rating))
3903 return PEP_ILLEGAL_VALUE;
3905 if (msg->dir != PEP_dir_outgoing)
3906 return PEP_ILLEGAL_VALUE;
3908 *rating = PEP_rating_undefined;
3910 _max_comm_type_from_identity_list(msg->to, session,
3911 &max_comm_type, &comm_type_determined);
3913 _max_comm_type_from_identity_list(msg->cc, session,
3914 &max_comm_type, &comm_type_determined);
3916 _max_comm_type_from_identity_list(msg->bcc, session,
3917 &max_comm_type, &comm_type_determined);
3919 if (comm_type_determined == false) {
3920 // likely means there was a massive screwup with no sender or recipient
3922 *rating = PEP_rating_undefined;
3925 *rating = MAX(_rating(max_comm_type, PEP_rating_undefined),
3926 PEP_rating_unencrypted);
3928 return PEP_STATUS_OK;
3931 DYNAMIC_API PEP_STATUS outgoing_message_rating_preview(
3932 PEP_SESSION session,
3937 PEP_comm_type max_comm_type = PEP_ct_pEp;
3941 assert(msg->dir == PEP_dir_outgoing);
3944 if (!(session && msg && rating))
3945 return PEP_ILLEGAL_VALUE;
3947 if (msg->dir != PEP_dir_outgoing)
3948 return PEP_ILLEGAL_VALUE;
3950 *rating = PEP_rating_undefined;
3952 _max_comm_type_from_identity_list_preview(msg->to, session,
3955 _max_comm_type_from_identity_list_preview(msg->cc, session,
3958 _max_comm_type_from_identity_list_preview(msg->bcc, session,
3961 *rating = _MAX(_rating(max_comm_type, PEP_rating_undefined),
3962 PEP_rating_unencrypted);
3964 return PEP_STATUS_OK;
3967 DYNAMIC_API PEP_STATUS identity_rating(
3968 PEP_SESSION session,
3969 pEp_identity *ident,
3973 PEP_STATUS status = PEP_STATUS_OK;
3979 if (!(session && ident && rating))
3980 return PEP_ILLEGAL_VALUE;
3983 status = _myself(session, ident, false, true);
3985 status = update_identity(session, ident);
3987 bool is_blacklisted = false;
3989 if (ident->fpr && IS_PGP_CT(ident->comm_type)) {
3990 status = blacklist_is_listed(session, ident->fpr, &is_blacklisted);
3991 if (status != PEP_STATUS_OK) {
3992 return status; // DB ERROR
3994 if (is_blacklisted) {
3995 bool user_default, ident_default, address_default;
3996 status = get_valid_pubkey(session, ident,
3997 &ident_default, &user_default,
4000 if (status != PEP_STATUS_OK || ident->fpr == NULL) {
4001 ident->comm_type = PEP_ct_key_not_found;
4002 status = PEP_STATUS_OK;
4007 if (status == PEP_STATUS_OK)
4008 *rating = _rating(ident->comm_type, PEP_rating_undefined);
4013 DYNAMIC_API PEP_STATUS get_binary_path(PEP_cryptotech tech, const char **path)
4015 PEP_STATUS status = PEP_STATUS_OK;
4019 return PEP_ILLEGAL_VALUE;
4021 if (cryptotech[tech].binary_path == NULL)
4024 status = cryptotech[tech].binary_path(path);
4030 DYNAMIC_API PEP_color color_from_rating(PEP_rating rating)
4032 if (rating == PEP_rating_b0rken || rating == PEP_rating_have_no_key)
4033 return PEP_color_no_color;
4035 if (rating < PEP_rating_undefined)
4036 return PEP_color_red;
4038 if (rating < PEP_rating_reliable)
4039 return PEP_color_no_color;
4041 if (rating < PEP_rating_trusted)
4042 return PEP_color_yellow;
4044 if (rating >= PEP_rating_trusted)
4045 return PEP_color_green;
4047 // this should never happen
4049 return PEP_color_no_color;
4052 /* [0-9]: 0x30 - 0x39; [A-F] = 0x41 - 0x46; [a-f] = 0x61 - 0x66 */
4053 static short asciihex_to_num(char a) {
4054 short conv_num = -1;
4055 if (a >= 0x30 && a <= 0x39)
4056 conv_num = a - 0x30;
4058 // convert case, subtract offset, get number
4059 conv_num = ((a | 0x20) - 0x61) + 10;
4060 if (conv_num < 0xa || conv_num > 0xf)
4066 static char num_to_asciihex(short h) {
4067 if (h < 0 || h > 16)
4070 return (char)(h + 0x30);
4071 return (char)((h - 10) + 0x41); // for readability
4074 static char xor_hex_chars(char a, char b) {
4075 short a_num = asciihex_to_num(a);
4076 short b_num = asciihex_to_num(b);
4077 if (a_num < 0 || b_num < 0)
4079 short xor_num = a_num^b_num;
4080 return num_to_asciihex(xor_num);
4083 static char* skip_separators(char* current, char* begin) {
4084 while (current >= begin) {
4085 /* .:,;-_ ' ' - [2c-2e] [3a-3b] [20] [5f] */
4086 char check_char = *current;
4087 switch (check_char) {
4105 PEP_STATUS check_for_zero_fpr(char* fpr) {
4106 PEP_STATUS status = PEP_TRUSTWORDS_DUPLICATE_FPR;
4110 status = PEP_STATUS_OK;
4120 DYNAMIC_API PEP_STATUS get_trustwords(
4121 PEP_SESSION session, const pEp_identity* id1, const pEp_identity* id2,
4122 const char* lang, char **words, size_t *wsize, bool full
4133 int SHORT_NUM_TWORDS = 5;
4135 PEP_STATUS status = PEP_STATUS_OK;
4137 if (!(session && id1 && id2 && words && wsize) ||
4138 !(id1->fpr) || (!id2->fpr))
4139 return PEP_ILLEGAL_VALUE;
4141 char *source1 = id1->fpr;
4142 char *source2 = id2->fpr;
4144 int source1_len = strlen(source1);
4145 int source2_len = strlen(source2);
4151 max_len = (source1_len > source2_len ? source1_len : source2_len);
4153 char* XORed_fpr = (char*)(calloc(max_len + 1, 1));
4154 *(XORed_fpr + max_len) = '\0';
4155 char* result_curr = XORed_fpr + max_len - 1;
4156 char* source1_curr = source1 + source1_len - 1;
4157 char* source2_curr = source2 + source2_len - 1;
4159 while (source1 <= source1_curr && source2 <= source2_curr) {
4160 source1_curr = skip_separators(source1_curr, source1);
4161 source2_curr = skip_separators(source2_curr, source2);
4163 if (source1_curr < source1 || source2_curr < source2)
4166 char xor_hex = xor_hex_chars(*source1_curr, *source2_curr);
4167 if (xor_hex == '\0') {
4168 status = PEP_ILLEGAL_VALUE;
4172 *result_curr = xor_hex;
4173 result_curr--; source1_curr--; source2_curr--;
4176 char* remainder_start = NULL;
4177 char* remainder_curr = NULL;
4179 if (source1 <= source1_curr) {
4180 remainder_start = source1;
4181 remainder_curr = source1_curr;
4183 else if (source2 <= source2_curr) {
4184 remainder_start = source2;
4185 remainder_curr = source2_curr;
4187 if (remainder_curr) {
4188 while (remainder_start <= remainder_curr) {
4189 remainder_curr = skip_separators(remainder_curr, remainder_start);
4191 if (remainder_curr < remainder_start)
4194 char the_char = *remainder_curr;
4196 if (asciihex_to_num(the_char) < 0) {
4197 status = PEP_ILLEGAL_VALUE;
4201 *result_curr = the_char;
4209 if (result_curr > XORed_fpr) {
4210 char* tempstr = strdup(result_curr);
4212 XORed_fpr = tempstr;
4215 status = check_for_zero_fpr(XORed_fpr);
4217 if (status != PEP_STATUS_OK)
4220 size_t max_words_per_id = (full ? 0 : SHORT_NUM_TWORDS);
4222 char* the_words = NULL;
4223 size_t the_size = 0;
4225 status = trustwords(session, XORed_fpr, lang, &the_words, &the_size, max_words_per_id);
4226 if (status != PEP_STATUS_OK)
4232 status = PEP_STATUS_OK;
4243 DYNAMIC_API PEP_STATUS get_message_trustwords(
4244 PEP_SESSION session,
4246 stringlist_t *keylist,
4247 pEp_identity* received_by,
4248 const char* lang, char **words, bool full
4253 assert(received_by);
4254 assert(received_by->address);
4261 received_by->address &&
4264 return PEP_ILLEGAL_VALUE;
4266 pEp_identity* partner = NULL;
4268 PEP_STATUS status = PEP_STATUS_OK;
4272 // We want fingerprint of key that did sign the message
4274 if (keylist == NULL) {
4276 // Message is to be decrypted
4277 message *dst = NULL;
4278 stringlist_t *_keylist = keylist;
4280 PEP_decrypt_flags_t flags;
4281 status = decrypt_message( session, msg, &dst, &_keylist, &rating, &flags);
4283 if (status != PEP_STATUS_OK) {
4285 free_stringlist(_keylist);
4289 if (dst && dst->from && _keylist) {
4290 partner = identity_dup(dst->from);
4293 partner->fpr = strdup(_keylist->value);
4294 if (partner->fpr == NULL)
4295 status = PEP_OUT_OF_MEMORY;
4297 status = PEP_OUT_OF_MEMORY;
4300 status = PEP_UNKNOWN_ERROR;
4304 free_stringlist(_keylist);
4308 // Message already decrypted
4309 if (keylist->value) {
4310 partner = identity_dup(msg->from);
4313 partner->fpr = strdup(keylist->value);
4314 if (partner->fpr == NULL)
4315 status = PEP_OUT_OF_MEMORY;
4317 status = PEP_OUT_OF_MEMORY;
4320 status = PEP_ILLEGAL_VALUE;
4324 if (status != PEP_STATUS_OK) {
4325 free_identity(partner);
4329 // Find own identity corresponding to given account address.
4330 // In that case we want default key attached to own identity
4331 pEp_identity *stored_identity = NULL;
4332 status = get_identity(session,
4333 received_by->address,
4337 if (status != PEP_STATUS_OK) {
4338 free_identity(stored_identity);
4342 // get the trustwords
4344 status = get_trustwords(session,
4345 partner, received_by,
4346 lang, words, &wsize, full);
4351 DYNAMIC_API PEP_STATUS MIME_decrypt_message(
4352 PEP_SESSION session,
4353 const char *mimetext,
4355 char** mime_plaintext,
4356 stringlist_t **keylist,
4358 PEP_decrypt_flags_t *flags,
4363 assert(mime_plaintext);
4367 assert(modified_src);
4369 if (!(mimetext && mime_plaintext && keylist && rating && flags && modified_src))
4370 return PEP_ILLEGAL_VALUE;
4372 PEP_STATUS status = PEP_STATUS_OK;
4373 message* tmp_msg = NULL;
4374 message* dec_msg = NULL;
4375 *mime_plaintext = NULL;
4377 status = mime_decode_message(mimetext, size, &tmp_msg);
4378 if (status != PEP_STATUS_OK)
4381 // MIME decode message delivers only addresses. We need more.
4382 if (tmp_msg->from) {
4383 if (!is_me(session, tmp_msg->from))
4384 status = update_identity(session, (tmp_msg->from));
4386 status = myself(session, tmp_msg->from);
4388 if (status == PEP_ILLEGAL_VALUE || status == PEP_OUT_OF_MEMORY)
4392 status = update_identity_recip_list(session, tmp_msg->to);
4393 if (status != PEP_STATUS_OK)
4396 status = update_identity_recip_list(session, tmp_msg->cc);
4397 if (status != PEP_STATUS_OK)
4400 status = update_identity_recip_list(session, tmp_msg->bcc);
4401 if (status != PEP_STATUS_OK)
4404 PEP_STATUS decrypt_status = decrypt_message(session,
4412 if (!dec_msg && (decrypt_status == PEP_UNENCRYPTED || decrypt_status == PEP_VERIFIED)) {
4413 dec_msg = message_dup(tmp_msg);
4416 if (decrypt_status > PEP_CANNOT_DECRYPT_UNKNOWN || !dec_msg)
4418 status = decrypt_status;
4422 if (*flags & PEP_decrypt_flag_src_modified) {
4423 _mime_encode_message_internal(tmp_msg, false, modified_src, true);
4424 if (!modified_src) {
4425 *flags &= (~PEP_decrypt_flag_src_modified);
4426 decrypt_status = PEP_CANNOT_REENCRYPT; // Because we couldn't return it, I guess.
4430 // FIXME: test with att
4431 status = _mime_encode_message_internal(dec_msg, false, mime_plaintext, true);
4433 if (status == PEP_STATUS_OK)
4437 return decrypt_status;
4441 free_message(tmp_msg);
4442 free_message(dec_msg);
4448 DYNAMIC_API PEP_STATUS MIME_encrypt_message(
4449 PEP_SESSION session,
4450 const char *mimetext,
4452 stringlist_t* extra,
4453 char** mime_ciphertext,
4454 PEP_enc_format enc_format,
4455 PEP_encrypt_flags_t flags
4458 PEP_STATUS status = PEP_STATUS_OK;
4459 message* tmp_msg = NULL;
4460 message* enc_msg = NULL;
4462 status = mime_decode_message(mimetext, size, &tmp_msg);
4463 if (status != PEP_STATUS_OK)
4466 // MIME decode message delivers only addresses. We need more.
4467 if (tmp_msg->from) {
4468 char* own_id = NULL;
4469 status = get_default_own_userid(session, &own_id);
4470 free(tmp_msg->from->user_id);
4472 if (status != PEP_STATUS_OK || !own_id) {
4473 tmp_msg->from->user_id = strdup(PEP_OWN_USERID);
4476 tmp_msg->from->user_id = own_id; // ownership transfer
4479 status = myself(session, tmp_msg->from);
4480 if (status != PEP_STATUS_OK)
4484 // Own identities can be retrieved here where they would otherwise
4485 // fail because we lack all other information. This is ok and even
4486 // desired. FIXME: IS it?
4487 status = update_identity_recip_list(session, tmp_msg->to);
4488 if (status != PEP_STATUS_OK)
4491 status = update_identity_recip_list(session, tmp_msg->cc);
4492 if (status != PEP_STATUS_OK)
4495 status = update_identity_recip_list(session, tmp_msg->bcc);
4496 if (status != PEP_STATUS_OK)
4499 // This isn't incoming, though... so we need to reverse the direction
4500 tmp_msg->dir = PEP_dir_outgoing;
4501 status = encrypt_message(session,
4508 if (status != PEP_STATUS_OK)
4513 status = PEP_UNKNOWN_ERROR;
4517 status = _mime_encode_message_internal(enc_msg, false, mime_ciphertext, false);
4520 free_message(tmp_msg);
4521 free_message(enc_msg);
4527 DYNAMIC_API PEP_STATUS MIME_encrypt_message_for_self(
4528 PEP_SESSION session,
4529 pEp_identity* target_id,
4530 const char *mimetext,
4532 stringlist_t* extra,
4533 char** mime_ciphertext,
4534 PEP_enc_format enc_format,
4535 PEP_encrypt_flags_t flags
4538 PEP_STATUS status = PEP_STATUS_OK;
4539 message* tmp_msg = NULL;
4540 message* enc_msg = NULL;
4542 status = mime_decode_message(mimetext, size, &tmp_msg);
4543 if (status != PEP_STATUS_OK)
4546 // This isn't incoming, though... so we need to reverse the direction
4547 tmp_msg->dir = PEP_dir_outgoing;
4548 status = encrypt_message_for_self(session,
4555 if (status != PEP_STATUS_OK)
4559 status = PEP_UNKNOWN_ERROR;
4563 status = mime_encode_message(enc_msg, false, mime_ciphertext);
4566 free_message(tmp_msg);
4567 free_message(enc_msg);
4572 static PEP_rating string_to_rating(const char * rating)
4575 return PEP_rating_undefined;
4576 if (strcmp(rating, "cannot_decrypt") == 0)
4577 return PEP_rating_cannot_decrypt;
4578 if (strcmp(rating, "have_no_key") == 0)
4579 return PEP_rating_have_no_key;
4580 if (strcmp(rating, "unencrypted") == 0)
4581 return PEP_rating_unencrypted;
4582 if (strcmp(rating, "unencrypted_for_some") == 0)
4583 return PEP_rating_unencrypted_for_some;
4584 if (strcmp(rating, "unreliable") == 0)
4585 return PEP_rating_unreliable;
4586 if (strcmp(rating, "reliable") == 0)
4587 return PEP_rating_reliable;
4588 if (strcmp(rating, "trusted") == 0)
4589 return PEP_rating_trusted;
4590 if (strcmp(rating, "trusted_and_anonymized") == 0)
4591 return PEP_rating_trusted_and_anonymized;
4592 if (strcmp(rating, "fully_anonymous") == 0)
4593 return PEP_rating_fully_anonymous;
4594 if (strcmp(rating, "mistrust") == 0)
4595 return PEP_rating_mistrust;
4596 if (strcmp(rating, "b0rken") == 0)
4597 return PEP_rating_b0rken;
4598 if (strcmp(rating, "under_attack") == 0)
4599 return PEP_rating_under_attack;
4600 return PEP_rating_undefined;
4603 static PEP_STATUS string_to_keylist(const char * skeylist, stringlist_t **keylist)
4605 if (skeylist == NULL || keylist == NULL)
4606 return PEP_ILLEGAL_VALUE;
4608 stringlist_t *rkeylist = NULL;
4609 stringlist_t *_kcurr = NULL;
4610 const char * fpr_begin = skeylist;
4611 const char * fpr_end = NULL;
4614 fpr_end = strstr(fpr_begin, ",");
4616 char * fpr = strndup(
4618 (fpr_end == NULL) ? strlen(fpr_begin) : fpr_end - fpr_begin);
4623 _kcurr = stringlist_add(_kcurr, fpr);
4624 if (_kcurr == NULL) {
4629 if (rkeylist == NULL)
4632 fpr_begin = fpr_end ? fpr_end + 1 : NULL;
4634 } while (fpr_begin);
4636 *keylist = rkeylist;
4637 return PEP_STATUS_OK;
4640 free_stringlist(rkeylist);
4641 return PEP_OUT_OF_MEMORY;
4644 DYNAMIC_API PEP_STATUS re_evaluate_message_rating(
4645 PEP_SESSION session,
4647 stringlist_t *x_keylist,
4648 PEP_rating x_enc_status,
4652 PEP_STATUS status = PEP_STATUS_OK;
4653 stringlist_t *_keylist = x_keylist;
4654 bool must_free_keylist = false;
4661 if (!(session && msg && rating))
4662 return PEP_ILLEGAL_VALUE;
4664 *rating = PEP_rating_undefined;
4666 if (x_enc_status == PEP_rating_undefined){
4667 for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
4668 if (strcasecmp(i->value->key, "X-EncStatus") == 0){
4669 x_enc_status = string_to_rating(i->value->value);
4673 return PEP_ILLEGAL_VALUE;
4678 _rating = x_enc_status;
4680 if (_keylist == NULL){
4681 for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
4682 if (strcasecmp(i->value->key, "X-KeyList") == 0){
4683 status = string_to_keylist(i->value->value, &_keylist);
4684 if (status != PEP_STATUS_OK)
4686 must_free_keylist = true;
4691 // there was no rcpt fpr, it could be an unencrypted mail
4692 if(_rating == PEP_rating_unencrypted) {
4694 return PEP_STATUS_OK;
4697 return PEP_ILLEGAL_VALUE;
4701 if (!is_me(session, msg->from))
4702 status = update_identity(session, msg->from);
4704 status = myself(session, msg->from);
4707 case PEP_KEY_NOT_FOUND:
4708 case PEP_KEY_UNSUITABLE:
4709 case PEP_KEY_BLACKLISTED:
4710 case PEP_CANNOT_FIND_IDENTITY:
4711 case PEP_CANNOT_FIND_ALIAS:
4712 status = PEP_STATUS_OK;
4719 status = amend_rating_according_to_sender_and_recipients(session, &_rating,
4720 msg->from, _keylist);
4721 if (status == PEP_STATUS_OK)
4725 if (must_free_keylist)
4726 free_stringlist(_keylist);