/[gpgoe]/trunk/src/OECrypto.c
ViewVC logotype

Contents of /trunk/src/OECrypto.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (show annotations)
Fri Apr 7 10:46:41 2006 UTC (19 years, 1 month ago) by twoaday
File MIME type: text/plain
File size: 16957 byte(s)


1 /* OECrypto.c - OE crypto functions
2 * Copyright (C) 2001, 2002, 2003, 2006 Timo Schulz
3 *
4 * This file is part of GPGOE.
5 *
6 * GPGOE is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
10 *
11 * GPGOE is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with GPGOE; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <windows.h>
25 #include <assert.h>
26 #include <commctrl.h>
27
28 #include "resource.h"
29 #include "gpgme.h"
30 #include "GPGOE.h"
31
32
33 /* Valid OpenPGP message types. */
34 enum {
35 PGP_MESSAGE = 1,
36 PGP_CLEARSIG = 2,
37 PGP_SIG = 4,
38 PGP_KEY = 8,
39 };
40
41
42 /* Move the keyboard focus to the message window
43 to copy the text to the clipboard. */
44 static void
45 set_focus (HWND main)
46 {
47 HWND doc, mime, msg;
48
49 doc = FindWindowEx (main, NULL, "ME_DocHost", NULL );
50 mime = FindWindowEx (doc, NULL, "##MimeEdit_Server", NULL);
51 msg = FindWindowEx (mime, NULL, "Internet Explorer_Server", NULL);
52
53 AttachThreadInput (GetCurrentThreadId (),
54 GetWindowThreadProcessId (main, NULL),
55 TRUE);
56
57 SetFocus (doc);
58 SetFocus (mime);
59 SetFocus (msg);
60
61 AttachThreadInput (GetCurrentThreadId (),
62 GetWindowThreadProcessId (main, NULL),
63 FALSE);
64 }
65
66
67 /* Copy the message to the clipboard and then return the clipboard
68 data (which is the message). */
69 static char*
70 window_get_message (HWND main_hwnd)
71 {
72 set_focus (main_hwnd);
73 SetForegroundWindow (main_hwnd);
74 SendMessage (main_hwnd, WM_COMMAND, MAKEWPARAM (ID_OE_SELECTALL, 0), 0);
75 SendMessage (main_hwnd, WM_COMMAND, MAKEWPARAM (ID_OE_COPY, 0), 0);
76
77 /* even so SendMessage() should wait, we wait for safety reasons. */
78 Sleep (200);
79 return get_clip_text (NULL);
80 }
81
82
83 /* Paste the message given in @msg into the window @main_hwnd.
84 To make sure all existing text is overwritten, "select all"
85 is send to the window first. */
86 static void
87 window_set_message (HWND main_hwnd, const char *msg)
88 {
89 set_clip_text (NULL, msg, strlen (msg));
90 set_focus (main_hwnd);
91 SetForegroundWindow (main_hwnd);
92 SendMessage (main_hwnd, WM_COMMAND, MAKEWPARAM (ID_OE_SELECTALL, 0), 0);
93 SendMessage (main_hwnd, WM_COMMAND, MAKEWPARAM (ID_OE_PASTE, 0), 0);
94 }
95
96
97 /* Try to find the given recipient by key @key and
98 return it (NULL in case of error). */
99 static recip_list_t
100 find_recipient (recip_list_t rset, gpgme_key_t key)
101 {
102 recip_list_t n;
103 gpgme_user_id_t u;
104
105 for (n = rset; n; n = n->next) {
106 for (u = key->uids; u; u = u->next) {
107 if (u->email && strstr (u->email, n->addr))
108 return n;
109 }
110 }
111 return NULL;
112 }
113
114
115 /* Add recipient with address @addr and key @key to @rset. */
116 void
117 add_recipient (recip_list_t *rset, const char *addr, gpgme_key_t key)
118 {
119 recip_list_t r, n;
120
121 r = xcalloc (1, sizeof *r);
122 r->addr = xstrdup (addr);
123 r->key = key;
124 if (!*rset)
125 *rset = r;
126 else {
127 for (n=*rset; n->next; n=n->next)
128 ;
129 n->next = r;
130 }
131 }
132
133
134 /* Release recipient list @rset. */
135 static void
136 release_recipient (recip_list_t rset)
137 {
138 recip_list_t n;
139
140 while (rset != NULL) {
141 n = rset->next;
142 if (rset->key)
143 gpgme_key_release (rset->key);
144 free_if_alloc (rset->addr);
145 free_if_alloc (rset);
146 rset = n;
147 }
148 }
149
150
151 /* This functions converts the OE recipient fields to GPG compatible fields.
152 Currently the function support the following 'From' styles:
153 John Hacker <[email protected]>; [email protected]
154 or Foo Bar <[email protected]>, [email protected].
155 only the part between '<' '>' are added.
156 */
157 static int
158 parse_recipients (plugin_ctx_t ctx, recip_list_t *rset, char *recpt)
159 {
160 char *token, *p;
161 int count = 0;
162 char *p1, *p2;
163 int n1, n2;
164
165 assert (recpt && rset);
166
167 token = strtok (recpt, ",;");
168 while (token != NULL ) {
169 if ((p1=strchr (token, '<')) && (p2=strchr (token, '>'))
170 && strchr (token, '@')) {
171 n1 = p1 - token + 1 ;
172 n2 = p2 - token + 1;
173
174 p = xcalloc (1, strlen (token) + 1);
175 memcpy (p, token+n1, n2-n1-1);
176 add_recipient (rset, p, NULL);
177 count++;
178 free_if_alloc (p);
179 }
180 else if (strchr (token, '@') && !strchr (token, ' ')) {
181 p = xcalloc (1, strlen (token) + 1);
182 memcpy (p, token, strlen (token));
183 add_recipient (rset, p, NULL);
184 count++;
185 free_if_alloc (p);
186 }
187 token = strtok (NULL, ",;");
188 }
189 return count;
190 }
191
192
193 /* Fill in the key part of the recipient list @addrs if possible. */
194 static int
195 get_keys_from_addresses (recip_list_t addrs)
196 {
197 gpgme_ctx_t ctx;
198 gpgme_error_t err;
199 gpgme_key_t pk;
200 recip_list_t n, fnd;
201 char *p;
202 int nkeys=0;
203 DWORD len=0;
204
205 for (n = addrs; n; n = n->next)
206 len += strlen (n->addr) + 3;
207 p = xcalloc (1, len + 1);
208 for (n = addrs; n; n = n->next) {
209 strcat (p, n->addr);
210 strcat (p, " ");
211 }
212
213 err = gpgme_new (&ctx);
214 if (!err)
215 err = gpgme_op_keylist_start (ctx, p, 0);
216 while (!err) {
217 err = gpgme_op_keylist_next (ctx, &pk);
218 if (err)
219 break;
220 /* do not add invalid keys. */
221 if (pk->disabled || pk->expired || pk->revoked)
222 continue;
223 fnd = find_recipient (addrs, pk);
224 if (fnd != NULL && pk->can_encrypt) {
225 fnd->key = pk;
226 nkeys++;
227 }
228 }
229
230 gpgme_release (ctx);
231 free_if_alloc (p);
232 return nkeys;
233 }
234
235
236 /* Create gpgme input data object. If @encode is 1, use UTF8 conversion. */
237 gpgme_error_t
238 create_in_data (gpgme_data_t *in, const char *buf, int encode)
239 {
240 gpgme_error_t err;
241 char *enc_buf;
242
243 enc_buf = encode? native_to_utf8 (buf) : xstrdup (buf);
244 err = gpgme_data_new_from_mem (in, enc_buf, strlen (enc_buf), 1);
245 free (enc_buf);
246 return err;
247 }
248
249
250 /* Map gpgme data object to a string. */
251 void
252 map_gpgme_data (gpgme_data_t out, char **r_msg)
253 {
254 size_t len = 0;
255 char *p;
256
257 p = gpgme_data_release_and_get_mem (out, &len);
258 free_if_alloc (*r_msg);
259 *r_msg = xcalloc (1, len+1);
260 memcpy (*r_msg, p, len);
261 gpgme_free (p);
262 }
263
264
265 /* Try to extract the needed key information and retrieve
266 the keys if possible. */
267 static gpgme_error_t
268 get_keys (plugin_ctx_t ctx, recip_list_t *r_list,
269 gpgme_key_t **r_keys, int *r_n)
270 {
271 gpgme_key_t *keys;
272 recip_list_t addrs = NULL, n;
273 int rcount = 0, nkeys = 0;
274 int i;
275
276 if (ctx->to && strlen (ctx->to) >= 3)
277 rcount += parse_recipients (ctx, &addrs, ctx->to);
278 if (ctx->cc && strlen (ctx->cc) >= 3)
279 rcount += parse_recipients (ctx, &addrs, ctx->cc);
280 if (ctx->bcc && strlen (ctx->bcc) >= 3)
281 rcount += parse_recipients (ctx, &addrs, ctx->bcc);
282
283 nkeys = get_keys_from_addresses (addrs);
284 /* at least some keys were not found, so we offer to select them. */
285 if (nkeys != rcount) {
286 ctx->rset = addrs;
287 rcount = DialogBoxParam (mod_hinst_dll, (LPCTSTR)IDD_ENCRYPT,
288 ctx->main_wnd, encrypt_dlg_proc, (LPARAM)ctx);
289 if (rcount == 0)
290 return gpg_error (GPG_ERR_NO_PUBKEY);
291 nkeys += rcount;
292 }
293
294 keys = xcalloc (nkeys+1, sizeof *keys);
295 for (n = addrs, i=0; n; n = n->next) {
296 if (n->key != NULL)
297 keys[i++] = n->key;
298 }
299 *r_list = addrs;
300 *r_keys = keys;
301 *r_n = nkeys;
302 return 0;
303 }
304
305
306 /* Encrypt the given message @r_msg with the recipients from the
307 To, Cc, Bcc fields. In case that a recipient was not found
308 use the recipient dialog so the user can select the missing keys. */
309 static gpgme_error_t
310 encrypt_msg (plugin_ctx_t ctx, char **r_msg)
311 {
312 gpgme_error_t err;
313 gpgme_key_t *keys = NULL;
314 gpgme_ctx_t gctx = NULL;
315 gpgme_data_t in = NULL, out = NULL;
316 recip_list_t list = NULL;
317 int nkeys=0;
318 char *msg = *r_msg;
319
320 assert (ctx);
321
322 err = get_keys (ctx, &list, &keys, &nkeys);
323 if (err)
324 return err;
325
326 err = gpgme_new (&gctx);
327 if (!err)
328 err = create_in_data (&in, msg, ctx->use_utf8);
329 if (!err)
330 err = gpgme_data_new (&out);
331 if (!err) {
332 gpgme_set_armor (gctx, 1);
333 gpgme_set_textmode (gctx, 1);
334 err = gpgme_op_encrypt (gctx, keys,
335 GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
336 }
337
338 gpgme_release (gctx);
339 gpgme_data_release (in);
340 release_recipient (list);
341 free (keys);
342
343 if (err)
344 gpgme_data_release (out);
345 else
346 map_gpgme_data (out, r_msg);
347 return err;
348 }
349
350
351 /* Sign the message given in @r_msg with the GPG default key. */
352 static gpgme_error_t
353 sign_msg (plugin_ctx_t ctx, char **r_msg)
354 {
355 gpgme_error_t err;
356 gpgme_ctx_t gctx;
357 gpgme_data_t in, out;
358 pass_cb_t cb_val;
359
360 cb_val = new_pass_cb (ctx->main_wnd);
361
362 err = gpgme_new (&gctx);
363 if (!err)
364 err = gpgme_data_new_from_mem (&in, *r_msg, strlen (*r_msg), 1);
365 if (!err)
366 err = gpgme_data_new (&out);
367 if (!err) {
368 gpgme_set_passphrase_cb (gctx, passphrase_cb, cb_val);
369 err = gpgme_op_sign (gctx, in, out, GPGME_SIG_MODE_CLEAR);
370 }
371
372 gpgme_release (gctx);
373 gpgme_data_release (in);
374 free_pass_cb (cb_val);
375
376 if (err)
377 gpgme_data_release (out);
378 else
379 map_gpgme_data (out, r_msg);
380
381 return err;
382 }
383
384
385 /* Sign and encrypt the message @r_msg. */
386 static gpgme_error_t
387 sign_encrypt_msg (plugin_ctx_t ctx, char **r_msg)
388 {
389 gpgme_ctx_t gctx;
390 gpgme_data_t in, out;
391 gpgme_error_t err;
392 gpgme_key_t *keys;
393 pass_cb_t cb_val;
394 recip_list_t list = NULL;
395 int ec, nkeys = 0;
396
397 ec = get_keys (ctx, &list, &keys, &nkeys);
398 if (ec)
399 return ec;
400
401 cb_val = new_pass_cb (ctx->main_wnd);
402
403 err = gpgme_new (&gctx);
404 if (!err)
405 err = create_in_data (&in, *r_msg, ctx->use_utf8);
406 if (!err)
407 err = gpgme_data_new (&out);
408 if (!err) {
409 gpgme_set_armor (gctx, 1);
410 gpgme_set_textmode (gctx, 1);
411 gpgme_set_passphrase_cb (gctx, passphrase_cb, cb_val);
412 err = gpgme_op_encrypt_sign (gctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST,
413 in, out);
414 }
415
416 gpgme_release (gctx);
417 gpgme_data_release (in);
418 release_recipient (list);
419 free (keys);
420 free_pass_cb (cb_val);
421
422 if (err)
423 gpgme_data_release (out);
424 else
425 map_gpgme_data (out, r_msg);
426 return err;
427 }
428
429
430 static gpgme_error_t
431 verify_msg (plugin_ctx_t ctx, char **r_msg)
432 {
433 gpgme_error_t err;
434 gpgme_ctx_t gctx;
435 gpgme_data_t out = NULL;
436 gpgme_data_t sig = NULL;
437 gpgme_verify_result_t res;
438
439 err = gpgme_new (&gctx);
440 if (!err)
441 err = gpgme_data_new_from_mem (&sig, *r_msg, strlen (*r_msg), 1);
442 if (!err)
443 err = gpgme_data_new (&out);
444 if (!err)
445 err = gpgme_op_verify (gctx, sig, NULL, out);
446 if (!err) {
447 res = gpgme_op_verify_result (gctx);
448 if (res && res->signatures)
449 DialogBoxParam (mod_hinst_dll, (LPCTSTR)IDD_VERIFY, ctx->main_wnd,
450 verify_dlg_proc, (LPARAM)res->signatures);
451 }
452
453 gpgme_release (gctx);
454 gpgme_data_release (sig);
455
456 if (!err)
457 map_gpgme_data (out, r_msg);
458 else
459 gpgme_data_release (out);
460 return err;
461 }
462
463
464 /* Detailed error message when the decryption failed. */
465 static void
466 store_decrypt_info (char *buf, size_t buflen,
467 gpgme_decrypt_result_t res)
468 {
469 gpgme_key_t key = NULL;
470 gpgme_ctx_t ctx;
471 int algo;
472
473 if (!res->recipients)
474 return;
475
476 if (!gpgme_new (&ctx)) {
477 gpgme_get_key (ctx, res->recipients->keyid, &key, 0);
478 gpgme_release (ctx);
479 }
480
481 algo = res->recipients->pubkey_algo;
482 if (!key)
483 _snprintf (buf, buflen, _("encrypted with %s key, ID %s\n"
484 "decryption failed: secret key not available"),
485 algo == 1? "RSA" : algo==16? "ELG": "???",
486 res->recipients->keyid+8);
487 else {
488 char *uid = utf8_to_native (key->uids->uid);
489 _snprintf (buf, buflen, _("encrypted with %d-bit %s key, ID %s\n"
490 "\t\"%s\"\n"
491 "decryption failed: secret key not available"),
492 key->subkeys->length,
493 algo == 1? "RSA" : algo==16? "ELG": "???",
494 key->subkeys->keyid+8, uid);
495 free_if_alloc (uid);
496 }
497 if (key)
498 gpgme_key_release (key);
499 }
500
501
502 /* Decrypt the message @r_msg. If the type @type is actually a signature,
503 the verify function is called instead of decryption. */
504 static gpgme_error_t
505 decrypt_msg (plugin_ctx_t ctx, char **r_msg, int type)
506 {
507 gpgme_ctx_t gctx = NULL;
508 gpgme_data_t in = NULL, out = NULL;
509 gpgme_error_t err;
510 gpgme_verify_result_t res;
511 pass_cb_t cb_val;
512 char *msg = *r_msg;
513
514 if ((type & PGP_SIG) || (type & PGP_CLEARSIG))
515 return verify_msg (ctx, r_msg);
516
517 cb_val = new_pass_cb (ctx->main_wnd);
518
519 err = gpgme_new (&gctx);
520 if (!err)
521 err = gpgme_data_new_from_mem (&in, msg, strlen (msg), 1);
522 if (!err)
523 err = gpgme_data_new (&out);
524 if (!err) {
525 gpgme_set_passphrase_cb (gctx, passphrase_cb, cb_val);
526 err = gpgme_op_decrypt_verify (gctx, in, out);
527 res = gpgme_op_verify_result (gctx);
528 if (res && res->signatures)
529 DialogBoxParam (mod_hinst_dll, (LPCTSTR)IDD_VERIFY, ctx->main_wnd,
530 verify_dlg_proc, (LPARAM)res->signatures);
531 }
532 if (err) {
533 gpgme_decrypt_result_t r = gpgme_op_decrypt_result (gctx);
534 store_decrypt_info (ctx->errbuf, sizeof (ctx->errbuf)-1, r);
535 }
536
537 gpgme_release (gctx);
538 gpgme_data_release (in);
539 free_pass_cb (cb_val);
540
541 if (err)
542 gpgme_data_release (out);
543 else
544 map_gpgme_data (out, r_msg);
545
546 return err;
547 }
548
549
550 /* Return what kind of OpenPGP message @msg is. */
551 static int
552 parse_pgp_id (const char *msg)
553 {
554 int id = 0;
555
556 if (strstr (msg, "BEGIN PGP MESSAGE"))
557 id |= PGP_MESSAGE;
558 if (strstr (msg, "BEGIN PGP PUBLIC KEY") ||
559 strstr (msg, "BEGIN PGP SECRET KEY") ||
560 strstr (msg, "BEGIN PGP PRIVATE KEY"))
561 id |= PGP_KEY;
562 if (strstr (msg, "BEGIN PGP SIGNED MESSAGE"))
563 id |= PGP_CLEARSIG;
564 return id;
565 }
566
567
568 static int
569 winpt_key_import (void)
570 {
571 HWND winpt;
572
573 winpt = FindWindow ("WinPT", "WinPT");
574 if (winpt != NULL) {
575 PostMessage (winpt, WM_COMMAND, 40014, 0);
576 return 0;
577 }
578 return -1;
579 }
580
581
582 /* Try to decrypt a PGP/MIME message. */
583 static gpgme_error_t
584 oe_handle_pgp_mime_mail (plugin_ctx_t ctx)
585 {
586 SetEvent (plugin_active);
587
588 /* Select attachment number 0. */
589 AttachThreadInput (GetCurrentThreadId (),
590 GetWindowThreadProcessId (ctx->main_wnd, NULL),
591 TRUE);
592
593 SetFocus (ctx->addr_wnd);
594 SetFocus (ctx->attach);
595 ListView_SetItemState (ctx->attach, 0, LVIS_SELECTED|LVIS_FOCUSED,
596 LVIS_FOCUSED|LVIS_SELECTED);
597
598 AttachThreadInput (GetCurrentThreadId (),
599 GetWindowThreadProcessId (ctx->main_wnd, NULL),
600 FALSE);
601
602 SendMessage (ctx->addr_wnd, WM_COMMAND, ID_OE_SAVE_ATT, 0);
603
604 ResetEvent (plugin_active);
605
606 return 0;
607 }
608
609
610 /* This function can be use for all kind of OE messages.
611 It automatically choose the right procedure to handle the data. */
612 gpgme_error_t
613 oe_handle_mail (plugin_ctx_t ctx)
614 {
615 gpgme_error_t rc = 0;
616 char *msg;
617 int msg_type = 0;
618
619 assert (ctx);
620
621 msg = window_get_message (ctx->main_wnd);
622 #if 0
623 if ((!msg || strlen (msg)) < 2 &&
624 ctx->attach && ListView_GetItemCount (ctx->attach) == 2) {
625 free_if_alloc (msg);
626 return oe_handle_pgp_mime_mail (ctx);
627 }
628 #endif
629 if (!msg || strlen (msg) == 2) {
630 free_if_alloc (msg);
631 return 0;
632 }
633
634 if (strstr (msg, "-----BEGIN PGP") &&
635 strstr (msg, "-----END PGP"))
636 msg_type = parse_pgp_id (msg);
637
638 if (msg_type & PGP_KEY) {
639 if (winpt_key_import ())
640 MessageBox (ctx->main_wnd,
641 _("This mail contains one or more public or secret keys.\n\n"
642 "Please save the mail text in a file to use WinPT to import them."),
643 _("GPG Plug-in Info"), MB_ICONINFORMATION|MB_OK);
644 }
645 else if (msg_type) {
646 rc = decrypt_msg (ctx, &msg, msg_type);
647 SendMessage (ctx->msg_wnd, WM_CLEAR, 0, 0);
648 SendMessage (ctx->msg_wnd, WM_UNDO, 0, 0);
649 if (!rc && (msg_type & PGP_MESSAGE) && msg && strlen (msg) > 0) {
650 struct viewer_ctx_s viewer;
651 viewer.msg = msg;
652 DialogBoxParam (mod_hinst_dll, (LPCTSTR)IDD_VIEWER, ctx->main_wnd,
653 viewer_dlg_proc, (LPARAM)&viewer);
654 }
655 }
656 else if (ctx->sign || ctx->encrypt) {
657 if (!ctx->to && !ctx->cc && !ctx->bcc) {
658 free_if_alloc (msg);
659 return gpg_error (GPG_ERR_NO_DATA);
660 }
661 if (ctx->sign && ctx->encrypt)
662 rc = sign_encrypt_msg (ctx, &msg);
663 else if (ctx->sign)
664 rc = sign_msg (ctx, &msg);
665 else if (ctx->encrypt)
666 rc = encrypt_msg (ctx, &msg);
667 window_set_message (ctx->main_wnd, msg);
668 }
669
670 free_if_alloc (msg);
671 return rc;
672 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26