* Replace body.c by body.obj in pith/makefile.wnt to fix
[alpine.git] / contrib / smime / pine-smime-20030115.diff
blob5744ce0d35f6160c1dcbc5ab671168d8c205499b
1 diff -Ncr pine4.53/imap/src/c-client/mail.c pine4.53-smime/imap/src/c-client/mail.c
2 *** pine4.53/imap/src/c-client/mail.c Thu Jan 2 17:28:42 2003
3 --- pine4.53-smime/imap/src/c-client/mail.c Wed Jan 15 12:48:15 2003
4 ***************
5 *** 5198,5203 ****
6 --- 5198,5207 ----
8 void mail_free_body_data (BODY *body)
10 + /* cleanup body if requested by application */
11 + if (body->cleanup)
12 + (*body->cleanup)(body);
14 switch (body->type) { /* free contents */
15 case TYPEMULTIPART: /* multiple part */
16 mail_free_body_part (&body->nested.part);
17 diff -Ncr pine4.53/imap/src/c-client/mail.h pine4.53-smime/imap/src/c-client/mail.h
18 *** pine4.53/imap/src/c-client/mail.h Tue Jan 7 12:33:50 2003
19 --- pine4.53-smime/imap/src/c-client/mail.h Wed Jan 15 12:48:15 2003
20 ***************
21 *** 655,660 ****
22 --- 655,663 ----
23 unsigned long bytes; /* size of text in octets */
24 } size;
25 char *md5; /* MD5 checksum */
27 + void *sparep; /* spare pointer reserved for main program */
28 + void (*cleanup)(BODY *); /* cleanup function */
32 diff -Ncr pine4.53/pine/README.smime pine4.53-smime/pine/README.smime
33 *** pine4.53/pine/README.smime Wed Dec 31 16:00:00 1969
34 --- pine4.53-smime/pine/README.smime Wed Jan 15 12:48:15 2003
35 ***************
36 *** 0 ****
37 --- 1,129 ----
38 + Quick Start
39 + ===========
41 + To enable S/MIME support, ensure the SSL related lines in the platform
42 + makefile are uncommented (they're all next to one another).
44 + CA certificates are expected to be found in the OpenSSL 'cert' dir, in the
45 + standard hashed format.
47 + User Configuration
48 + ==================
50 + A directory '~/.pine-smime' must be created. Within this, a further
51 + three directories are required:
53 + ~/.pine-smime/
54 + ca/
55 + private/
56 + public/
58 + Other people's certificate files should be copied into the 'public' directory.
59 + Your certificate file(s) should be copied into the 'private' directory.
60 + Certificates for any additional trusted CAs should be put in the 'ca' directory.
62 + There are three extra configuration options:
64 + sign-default-on
65 + encrypt-default-on
66 + remember-smime-passphrase
68 + Certificates
69 + ============
71 + The certificate files specified above should have the following form:
73 + public certificates: user@emaildomain.crt
74 + private keys: user@emaildomain.key
76 + Thus, a typical installation might look like this:
78 + ~/.pine-smime/
79 + ca/
80 + [additional trusted CAs here]
81 + private/
82 + paisleyj@dcs.gla.ac.uk.crt
83 + paisleyj@dcs.gla.ac.uk.key
84 + public/
85 + myfriend@dcs.gla.ac.uk.crt
86 + myotherfriend@dcs.gla.ac.uk.crt
88 + Implementation Details
89 + ======================
91 + Link with the OpenSSL crypto library for PKCS7 support.
92 + Only tested on linux (slx) and solaris (so5).
94 + Added three extra source files (+headers):
96 + smime.c Main S/MIME support code
97 + smkeys.c Very basic X509 key handling/storage (using the above dirs)
98 + bss_so.c OpenSSL BIO using pine STORE_S objects
100 + Patches to existing pine sources:
102 + init.c
103 + Add references to new configuration options.
105 + mailcmd.c
106 + Add implementation of MC_DECRYPT command which prompts
107 + the user for a passphrase if it's required.
109 + mailpart.c
110 + Comment added to help me remember what I'd done.
112 + mailview.c
113 + Added description of Decrypt menu option.
114 + Make calls out to smime.c functions to handle the decryption.
115 + This is done shortly after the BODY of a message is
116 + obtained.
117 + Added function to describe encrypted messages when they're
118 + being displayed.
119 + Added code to describe the special case of PKCS7 attachments.
121 + makefile.lnx
122 + makefile.so5
123 + Added SSL variables etc.
125 + pine.h
126 + Add enumerations for new configuration options and definition
127 + of MC_DECRYPT command
128 + Exported the prototype of pine_write_body_header,
129 + pine_rfc822_output_body and pine_encode_body since they're
130 + needed in smime.c.
132 + pine.hlp
133 + Added help info for new configuration options.
135 + send.c
136 + Added 'Encrypt' and 'Sign' menu options when sending email.
137 + Make calls to smime.c functions to fiddle message on the
138 + way out.
139 + Extend pine_encode_body so it makes a few more checks
140 + before adding a boundary.
142 + Basic method:
144 + Incoming
146 + Scan BODY of viewed message before it is formatted. If it contains
147 + PKCS7 parts, decode them and attempt to decrypt/verify signatures. The
148 + original BODY is fiddled in place and turned into a multipart body
149 + with one subpart -- the decrypted data. This may consist of a multipart
150 + with attachments, for example.
152 + This all depends on stashing a pointer to the decrypted data in
153 + body->contents.text.data and relying on the fact that the mail_* routines
154 + will use this data in preference to fetching it over the network. We
155 + also depend on it not being garbage collected while the message is
156 + being viewed!
158 + Outgoing
160 + smime.c pre-builds the message using pine_encode_body, pine_write_body_header
161 + and pine_rfc822_output_body, encrypting/signing the resulting data. The
162 + body that was going to be sent is then fiddled appropriately after
163 + the PKCS7 objects have been built.
165 + paisleyj@dcs.gla.ac.uk
166 + Mar 7 2001
167 diff -Ncr pine4.53/pine/TODO.smime pine4.53-smime/pine/TODO.smime
168 *** pine4.53/pine/TODO.smime Wed Dec 31 16:00:00 1969
169 --- pine4.53-smime/pine/TODO.smime Wed Jan 15 12:48:15 2003
170 ***************
171 *** 0 ****
172 --- 1,31 ----
174 + Need to be able to view stored certificates to see details
175 + (particularly the fingerprint for comparing over the phone, say)
176 + --> proper key management system
178 + Add client private key and certificate request generation.
180 + Send certificate for CA along with certificate of signer.
182 + Verify recipient certificate before sending encrypted message.
184 + Verify certificates in general.
186 + Cache the result of pre-formatting the message during the send/encrypt/sign
187 + phase rather than letting call_mailer re-format it all over again.
189 + Tidy up the use of global variables considerably.
191 + Intelligently pick a certificate for signing purposes based on the
192 + From address rather than just picking the first one on the list.
194 + Figure out platform dependancies from using readdir() in smkeys.c
196 + Handle message/rfc822 sub-parts!
198 + Consider what happens with all our cached data.
200 + S/MIME info screen help.
202 + paisleyj@dcs.gla.ac.uk
203 + Mar 7 2001
204 diff -Ncr pine4.53/pine/bss_so.c pine4.53-smime/pine/bss_so.c
205 *** pine4.53/pine/bss_so.c Wed Dec 31 16:00:00 1969
206 --- pine4.53-smime/pine/bss_so.c Wed Jan 15 12:48:15 2003
207 ***************
208 *** 0 ****
209 --- 1,184 ----
210 + #ifdef SMIME
212 + /*
213 + bss_so.c
215 + Basic implementation of an OpenSSL BIO which is
216 + backed by a pine STORE_S object
217 + */
219 + #include "headers.h"
221 + #include <stdio.h>
222 + #include <errno.h>
223 + #include <openssl/bio.h>
224 + #include <openssl/err.h>
226 + static int bss_so_write(BIO *h, char *buf, int num);
227 + static int bss_so_read(BIO *h, char *buf, int size);
228 + static int bss_so_puts(BIO *h, char *str);
229 + static int bss_so_gets(BIO *h, char *str, int size);
230 + static long bss_so_ctrl(BIO *h, int cmd, long arg1, char *arg2);
231 + static int bss_so_new(BIO *h);
232 + static int bss_so_free(BIO *data);
233 + static BIO_METHOD methods_sop =
235 + BIO_TYPE_MEM,
236 + "Storage Object",
237 + bss_so_write,
238 + bss_so_read,
239 + bss_so_puts,
240 + bss_so_gets,
241 + bss_so_ctrl,
242 + bss_so_new,
243 + bss_so_free,
244 + NULL,
245 + };
247 + BIO *BIO_new_so(STORE_S *so)
249 + BIO *ret;
251 + if ((ret = BIO_new(&methods_sop)) == NULL)
252 + return (NULL);
254 + BIO_set_fp(ret, (FILE*) so, 0);
255 + return (ret);
259 + static int bss_so_new(BIO *bi)
261 + bi->init = 0;
262 + bi->num = 0;
263 + bi->ptr = NULL;
264 + return (1);
267 + static int bss_so_free(BIO *a)
269 + if (a == NULL) return (0);
270 + if (a->shutdown) {
271 + if ((a->init) && (a->ptr != NULL)) {
272 + a->ptr = NULL;
274 + a->init = 0;
276 + return (1);
279 + static int bss_so_read(BIO *b, char *out, int outl)
281 + int ret = 0;
282 + STORE_S *so = (STORE_S*) b->ptr;
284 + if (b->init && (out != NULL)) {
286 + while (ret < outl) {
287 + if (!so->readc((unsigned char *)out, so))
288 + break;
289 + out++;
290 + ret++;
294 + return (ret);
297 + static int bss_so_write(BIO *b, char *in, int inl)
299 + int ret = 0;
301 + if (b->init && (in != NULL)) {
302 + if (so_nputs((STORE_S *)b->ptr, in, inl))
303 + ret = inl;
306 + return (ret);
309 + static long bss_so_ctrl(BIO *b, int cmd, long num, char *ptr)
311 + long ret = 1;
312 + STORE_S *so = (STORE_S *)b->ptr;
313 + FILE **fpp;
314 + char p[4];
316 + switch (cmd) {
317 + case BIO_C_FILE_SEEK:
318 + case BIO_CTRL_RESET:
319 + ret = so_seek(so, num, 0);
320 + break;
321 + case BIO_CTRL_EOF:
322 + ret = 0;
323 + break;
324 + case BIO_C_FILE_TELL:
325 + case BIO_CTRL_INFO:
326 + ret = 0;
327 + break;
328 + case BIO_C_SET_FILE_PTR:
329 + bss_so_free(b);
330 + b->shutdown = (int)num & BIO_CLOSE;
331 + b->ptr = (char *)ptr;
332 + b->init = 1;
333 + break;
334 + case BIO_C_SET_FILENAME:
335 + ret = 0;
336 + break;
337 + case BIO_C_GET_FILE_PTR:
338 + if (ptr != NULL) {
339 + fpp = (FILE **)ptr;
340 + *fpp = (FILE *)NULL;
342 + break;
343 + case BIO_CTRL_GET_CLOSE:
344 + ret = (long)b->shutdown;
345 + break;
346 + case BIO_CTRL_SET_CLOSE:
347 + b->shutdown = (int)num;
348 + break;
349 + case BIO_CTRL_FLUSH:
350 + break;
351 + case BIO_CTRL_DUP:
352 + ret = 1;
353 + break;
355 + case BIO_CTRL_WPENDING:
356 + case BIO_CTRL_PENDING:
357 + case BIO_CTRL_PUSH:
358 + case BIO_CTRL_POP:
359 + default:
360 + ret = 0;
361 + break;
363 + return (ret);
366 + static int bss_so_gets(BIO *bp, char *buf, int size)
368 + int ret = 0;
369 + char *b = buf;
370 + char *bend = buf + size - 1;
371 + STORE_S *so = (STORE_S*) bp->ptr;
373 + do {
374 + if (!so->readc((unsigned char *)b, so))
375 + break;
376 + b++;
377 + } while (b < bend && b[ -1] != '\n');
379 + *b = 0;
381 + ret = b - buf;
382 + return (ret);
385 + static int bss_so_puts(BIO *bp, char *str)
387 + STORE_S *so = (STORE_S*) bp->ptr;
389 + return so->puts(so, str);
393 + #endif /* SMIME */
394 diff -Ncr pine4.53/pine/bss_so.h pine4.53-smime/pine/bss_so.h
395 *** pine4.53/pine/bss_so.h Wed Dec 31 16:00:00 1969
396 --- pine4.53-smime/pine/bss_so.h Wed Jan 15 12:48:15 2003
397 ***************
398 *** 0 ****
399 --- 1 ----
400 + BIO *BIO_new_so(STORE_S *so);
401 diff -Ncr pine4.53/pine/filter.c pine4.53-smime/pine/filter.c
402 *** pine4.53/pine/filter.c Wed Jan 8 12:13:50 2003
403 --- pine4.53-smime/pine/filter.c Wed Jan 15 12:48:15 2003
404 ***************
405 *** 903,909 ****
408 /* get a character from a file */
409 ! /* assumes gf_out struct is filled in */
411 gf_freadc(c)
412 unsigned char *c;
413 --- 903,909 ----
416 /* get a character from a file */
417 ! /* assumes gf_in struct is filled in */
419 gf_freadc(c)
420 unsigned char *c;
421 ***************
422 *** 938,944 ****
425 /* get a character from a string, return nonzero if things OK */
426 ! /* assumes gf_out struct is filled in */
428 gf_sreadc(c)
429 unsigned char *c;
430 --- 938,944 ----
433 /* get a character from a string, return nonzero if things OK */
434 ! /* assumes gf_in struct is filled in */
436 gf_sreadc(c)
437 unsigned char *c;
438 diff -Ncr pine4.53/pine/init.c pine4.53-smime/pine/init.c
439 *** pine4.53/pine/init.c Mon Jan 6 19:39:16 2003
440 --- pine4.53-smime/pine/init.c Wed Jan 15 12:48:15 2003
441 ***************
442 *** 2445,2451 ****
443 F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND},
444 {"use-sender-not-x-sender",
445 F_USE_SENDER_NOT_X, h_config_use_sender_not_x, PREF_SEND},
447 /* Folder */
448 {"combined-subdirectory-display",
449 F_CMBND_SUBDIR_DISP, h_config_combined_subdir_display, PREF_FLDR},
450 --- 2445,2458 ----
451 F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND},
452 {"use-sender-not-x-sender",
453 F_USE_SENDER_NOT_X, h_config_use_sender_not_x, PREF_SEND},
454 ! #ifdef SMIME
455 ! {"sign-default-on",
456 ! F_SIGN_DEFAULT_ON, h_config_sign_default_on, PREF_SEND},
457 ! {"encrypt-default-on",
458 ! F_ENCRYPT_DEFAULT_ON, h_config_encrypt_default_on, PREF_SEND},
459 ! {"remember-smime-passphrase",
460 ! F_REMEMBER_SMIME_PASSPHRASE, h_config_remember_smime_passphrase, PREF_SEND},
461 ! #endif
462 /* Folder */
463 {"combined-subdirectory-display",
464 F_CMBND_SUBDIR_DISP, h_config_combined_subdir_display, PREF_FLDR},
465 diff -Ncr pine4.53/pine/mailcmd.c pine4.53-smime/pine/mailcmd.c
466 *** pine4.53/pine/mailcmd.c Fri Jan 10 15:29:29 2003
467 --- pine4.53-smime/pine/mailcmd.c Wed Jan 15 12:48:15 2003
468 ***************
469 *** 53,58 ****
470 --- 53,59 ----
471 #include "headers.h"
472 #include "../c-client/imap4r1.h"
474 + #include "smime.h"
477 * Internal Prototypes
478 ***************
479 *** 1439,1444 ****
480 --- 1440,1458 ----
481 break;
484 + #ifdef SMIME
485 + /*------- Try to decrypt message -----------*/
486 + case MC_DECRYPT:
487 + if (g_need_passphrase)
488 + get_passphrase();
489 + a_changed = TRUE;
490 + break;
492 + case MC_SECURITY:
493 + state->next_screen = smime_info_screen;
494 + break;
495 + #endif
497 /*------- Bounce -----------*/
498 case MC_BOUNCE :
499 cmd_bounce(state, msgmap, 0);
500 diff -Ncr pine4.53/pine/mailpart.c pine4.53-smime/pine/mailpart.c
501 *** pine4.53/pine/mailpart.c Wed Nov 27 15:22:54 2002
502 --- pine4.53-smime/pine/mailpart.c Wed Jan 15 12:48:15 2003
503 ***************
504 *** 4534,4539 ****
505 --- 4534,4545 ----
506 frd->flags = flags;
507 frd->size = size;
508 frd->readc = fetch_readc;
510 + /* The call to imap_cache below will return true in the case where
511 + we've already stashed fake data in the content of the part.
512 + This happens when an S/MIME message is decrypted.
513 + */
515 if(modern_imap_stream(stream)
516 && !imap_cache(stream, msgno, section, NULL, NULL)
517 && size > INIT_FETCH_CHUNK
518 diff -Ncr pine4.53/pine/mailview.c pine4.53-smime/pine/mailview.c
519 *** pine4.53/pine/mailview.c Tue Jan 7 16:17:00 2003
520 --- pine4.53-smime/pine/mailview.c Wed Jan 15 12:48:15 2003
521 ***************
522 *** 47,52 ****
523 --- 47,53 ----
526 #include "headers.h"
527 + #include "smime.h"
530 /*----------------------------------------------------------------------
531 ***************
532 *** 207,214 ****
533 --- 208,220 ----
535 HELP_MENU,
536 OTHER_MENU,
537 + #ifdef SMIME
538 + {"^D","Decrypt", {MC_DECRYPT,1,{ctrl('d')},KS_NONE}},
539 + {"^E","Security", {MC_SECURITY,1,{ctrl('e')},KS_NONE}},
540 + #else
541 NULL_MENU,
542 NULL_MENU,
543 + #endif
544 RCOMPOSE_MENU,
545 NULL_MENU,
546 NULL_MENU,
547 ***************
548 *** 227,232 ****
549 --- 233,240 ----
550 #define BOUNCE_KEY 33
551 #define FLAG_KEY 34
552 #define VIEW_PIPE_KEY 35
553 + #define DECRYPT_KEY (VIEW_PIPE_KEY + 3)
554 + #define SECURITY_KEY (DECRYPT_KEY + 1)
556 static struct key simple_text_keys[] =
557 {HELP_MENU,
558 ***************
559 *** 432,437 ****
560 --- 440,447 ----
561 else
562 ps->unseen_in_view = !mc->seen;
564 + flags = 0;
566 #if defined(DOS) && !defined(WIN32)
568 * Handle big text for DOS here.
569 ***************
570 *** 459,465 ****
571 ps->ttyo->screen_rows - (SCROLL_LINES_ABOVE(ps)
572 + SCROLL_LINES_BELOW(ps)));
574 ! flags = FM_DISPLAY;
575 if((last_message_viewed != mn_get_cur(ps->msgmap)
576 || last_was_full_header == 1))
577 flags |= FM_NEW_MESS;
578 --- 469,475 ----
579 ps->ttyo->screen_rows - (SCROLL_LINES_ABOVE(ps)
580 + SCROLL_LINES_BELOW(ps)));
582 ! flags |= FM_DISPLAY;
583 if((last_message_viewed != mn_get_cur(ps->msgmap)
584 || last_was_full_header == 1))
585 flags |= FM_NEW_MESS;
586 ***************
587 *** 467,472 ****
588 --- 477,488 ----
589 if(offset) /* no pre-paint during resize */
590 view_writec_killbuf();
592 + #ifdef SMIME
593 + /* Attempt to handle S/MIME bodies */
594 + if (fiddle_smime_message(body,raw_msgno,(flags&FM_NEW_MESS)!=0))
595 + flags |= FM_NEW_MESS; /* body was changed, force a reload */
596 + #endif
598 #ifdef _WINDOWS
599 mswin_noscrollupdate(1);
600 #endif
601 ***************
602 *** 541,546 ****
603 --- 557,567 ----
605 if(F_OFF(F_ENABLE_FULL_HDR, ps_global))
606 clrbitn(VIEW_FULL_HEADERS_KEY, scrollargs.keys.bitmap);
608 + #ifdef SMIME
609 + if (!g_need_passphrase)
610 + clrbitn(DECRYPT_KEY, scrollargs.keys.bitmap);
611 + #endif
613 if(!handles){
615 ***************
616 *** 754,760 ****
617 --- 775,821 ----
621 + #ifdef SMIME
622 + /*----------------------------------------------------------------------
623 + Add descriptive lines to the top of a message being formatted
624 + that describe the status of any S/MIME enclosures that
625 + have been encountered.
627 + Args: body -- top-level body of the message being described
628 + pc -- output function for writing to the message display
630 + ----*/
631 + static int describe_smime_bodies(BODY *body,gf_io_t pc)
633 + PART *part;
634 + int result = 0;
636 + if (!body)
637 + return result;
639 + if (body->type == TYPEMULTIPART) {
641 + if (body->subtype && strucmp(body->subtype,"x-pkcs7-enclosure")==0) {
643 + if (body->description) {
644 + format_editorial(body->description,ps_global->ttyo->screen_cols,pc);
645 + gf_puts(NEWLINE,pc);
646 + result = 1;
649 + for (part=body->nested.part; part; part=part->next) {
650 + result |= describe_smime_bodies(&(part->body),pc);
654 + } else if (body->type == TYPEMESSAGE &&
655 + body->subtype && strucmp(body->subtype, "rfc822")==0) {
656 + result |= describe_smime_bodies(body->nested.msg->body,pc);
659 + return result;
661 + #endif
663 /*----------------------------------------------------------------------
664 Add lines to the attachments structure
665 ***************
666 *** 1677,1682 ****
667 --- 1738,1751 ----
669 show_parts = 0;
671 + #ifdef SMIME
672 + if (flgs & FM_DISPLAY) {
673 + if (describe_smime_bodies(body,pc)) {
674 + gf_puts(NEWLINE, pc);
677 + #endif
679 /*======== Now loop through formatting all the parts =======*/
680 for(a = ps_global->atmts; a->description != NULL; a++) {
682 ***************
683 *** 6150,6155 ****
684 --- 6219,6236 ----
686 t = &tmp_20k_buf[strlen(tmp_20k_buf)];
688 + #ifdef SMIME
689 + if (is_pkcs7_body(body) && type!=3) { /* if smime and not attempting print */
691 + sstrcpy(&t,"\015\012");
693 + sstrcpy(&t,
694 + "This part is a PKCS7 S/MIME enclosure. "
695 + "You may be able to view it by entering the correct passphrase "
696 + "with the \"^D\" command. Press \"^E\" for more information.");
698 + } else
699 + #endif
700 if(type){
701 sstrcpy(&t, "\015\012");
702 switch(type) {
703 diff -Ncr pine4.53/pine/makefile.lnx pine4.53-smime/pine/makefile.lnx
704 *** pine4.53/pine/makefile.lnx Tue Sep 10 14:34:39 2002
705 --- pine4.53-smime/pine/makefile.lnx Wed Jan 15 12:48:15 2003
706 ***************
707 *** 60,79 ****
708 LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \
709 mailview.o other.o pine.o strings.o takeaddr.o
711 STDLIBS= -lncurses
712 LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
713 LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
714 `cat $(CCLIENTDIR)/LDFLAGS`
716 STDCFLAGS= -DLNX -DSYSTYPE=\"LNX\" -DMOUSE
717 CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
718 $(STDCFLAGS)
720 OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
721 folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
722 mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
723 reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
724 ! os.o
726 HFILES= headers.h os.h pine.h context.h helptext.h \
727 $(PICODIR)/headers.h $(PICODIR)/estruct.h \
728 --- 60,92 ----
729 LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \
730 mailview.o other.o pine.o strings.o takeaddr.o
732 + SSLDIR= $(HOME)/local/ssl
733 + SSLCERTS= $(SSLDIR)/certs
734 + SSLINCLUDE= $(SSLDIR)/include
735 + SSLLIB= $(SSLDIR)/lib
737 + SSLCFLAGS= -I$(SSLINCLUDE) \
738 + -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" \
739 + -DSMIME
740 + SSLLDFLAGS= -L$(SSLLIB) -lcrypto
743 STDLIBS= -lncurses
744 LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
745 LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
746 + $(SSLLDFLAGS) \
747 `cat $(CCLIENTDIR)/LDFLAGS`
749 STDCFLAGS= -DLNX -DSYSTYPE=\"LNX\" -DMOUSE
750 CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
751 + $(SSLCFLAGS) \
752 $(STDCFLAGS)
754 OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
755 folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
756 mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
757 reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
758 ! os.o bss_so.o smime.o smkeys.o
760 HFILES= headers.h os.h pine.h context.h helptext.h \
761 $(PICODIR)/headers.h $(PICODIR)/estruct.h \
762 ***************
763 *** 135,137 ****
764 --- 148,154 ----
765 osdep/sendmail osdep/execview \
766 osdep/postreap.wtp osdep/os-lnx.ic
767 cd osdep; $(MAKE) includer os-lnx.c; cd ..
769 + jon.o: jon.c
770 + $(CC) $(CFLAGS) -Wall -Wstrict-prototypes -c $< -o $@
772 diff -Ncr pine4.53/pine/makefile.so5 pine4.53-smime/pine/makefile.so5
773 *** pine4.53/pine/makefile.so5 Tue Oct 23 15:24:51 2001
774 --- pine4.53-smime/pine/makefile.so5 Wed Jan 15 12:48:15 2003
775 ***************
776 *** 62,67 ****
777 --- 62,78 ----
778 LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \
779 mailview.o other.o pine.o strings.o takeaddr.o
781 + SSLDIR= $(HOME)/local/ssl
782 + SSLCERTS= $(SSLDIR)/certs
783 + SSLINCLUDE= $(SSLDIR)/include
784 + SSLLIB= $(SSLDIR)/lib
786 + SSLCFLAGS= -I$(SSLINCLUDE) \
787 + -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" \
788 + -DSMIME
789 + SSLLDFLAGS= -L$(SSLLIB) -lcrypto
792 # LDCC= /usr/bin/cc
793 # If you don't have /usr/bin/cc (our Solaris 2.2 doesn't seem to have it,
794 # it only has /usr/ucb/cc) then change LDCC to the following line and
795 ***************
796 *** 72,88 ****
797 STDLIBS= -ltermlib
798 LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
799 LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
800 `cat $(CCLIENTDIR)/LDFLAGS`
802 STDCFLAGS= -Dconst= -DSV4 -DSYSTYPE=\"SOL\" -DMOUSE
803 CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
804 $(STDCFLAGS)
806 OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
807 folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
808 mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
809 reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
810 ! os.o
812 HFILES= headers.h os.h pine.h context.h helptext.h \
813 $(PICODIR)/headers.h $(PICODIR)/estruct.h \
814 --- 83,101 ----
815 STDLIBS= -ltermlib
816 LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
817 LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
818 + $(SSLLDFLAGS) \
819 `cat $(CCLIENTDIR)/LDFLAGS`
821 STDCFLAGS= -Dconst= -DSV4 -DSYSTYPE=\"SOL\" -DMOUSE
822 CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
823 + $(SSLCFLAGS) \
824 $(STDCFLAGS)
826 OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
827 folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
828 mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
829 reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
830 ! os.o bss_so.o smime.o smkeys.o
832 HFILES= headers.h os.h pine.h context.h helptext.h \
833 $(PICODIR)/headers.h $(PICODIR)/estruct.h \
834 diff -Ncr pine4.53/pine/pine.h pine4.53-smime/pine/pine.h
835 *** pine4.53/pine/pine.h Fri Jan 10 15:25:55 2003
836 --- pine4.53-smime/pine/pine.h Wed Jan 15 12:48:15 2003
837 ***************
838 *** 1134,1139 ****
839 --- 1134,1144 ----
840 F_DISABLE_SHARED_NAMESPACES,
841 F_EXPOSE_HIDDEN_CONFIG,
842 F_ALT_COMPOSE_MENU,
843 + #ifdef SMIME
844 + F_SIGN_DEFAULT_ON,
845 + F_ENCRYPT_DEFAULT_ON,
846 + F_REMEMBER_SMIME_PASSPHRASE,
847 + #endif
848 F_ALWAYS_SPELL_CHECK,
849 F_QUELL_TIMEZONE,
850 F_COLOR_LINE_IMPORTANT,
851 ***************
852 *** 2031,2036 ****
853 --- 2036,2043 ----
854 } cmdline_val; /* user typed as cmdline arg */
857 + #define MC_DECRYPT 800
858 + #define MC_SECURITY 801
862 ***************
863 *** 4368,4373 ****
864 --- 4375,4383 ----
865 char *pine_send_status PROTO((int, char *, char *, int *));
866 void phone_home PROTO((char *));
867 void pine_free_body PROTO((BODY **));
868 + int pine_write_body_header PROTO((BODY *, soutr_t, TCPSTREAM *));
869 + long pine_rfc822_output_body PROTO((BODY *,soutr_t,TCPSTREAM *));
870 + void pine_encode_body PROTO((BODY *));
871 void simple_header_parse PROTO((char *, char **, char **));
872 int valid_subject PROTO((char *, char **, char **,BUILDER_ARG *,int *));
873 long new_mail_for_pico PROTO((int, int));
874 diff -Ncr pine4.53/pine/pine.hlp pine4.53-smime/pine/pine.hlp
875 *** pine4.53/pine/pine.hlp Wed Jan 15 11:55:09 2003
876 --- pine4.53-smime/pine/pine.hlp Wed Jan 15 12:48:15 2003
877 ***************
878 *** 24338,24340 ****
879 --- 24338,24347 ----
880 ========== h_select_by_smaller_size ==========
881 Enter a number or ^C to cancel. All messages less than this many characters
882 in size will be selected. Examples: 2176, 1.53K (1530), or 3M (3000000).
883 + ========== h_config_sign_default_on ==========
884 + If enabled, the 'Sign' option will default to on when sending messages.
885 + ========== h_config_encrypt_default_on ==========
886 + If enabled, the 'Encrypt' option will default to on when sending messages.
887 + ========== h_config_remember_smime_passphrase ==========
888 + If enabled, you will only have to enter your passphrase for your private key
889 + once during a pine session.
890 diff -Ncr pine4.53/pine/send.c pine4.53-smime/pine/send.c
891 *** pine4.53/pine/send.c Tue Jan 14 13:22:59 2003
892 --- pine4.53-smime/pine/send.c Wed Jan 15 12:48:15 2003
893 ***************
894 *** 50,55 ****
895 --- 50,56 ----
896 #include "../c-client/smtp.h"
897 #include "../c-client/nntp.h"
899 + #include "smime.h"
901 #ifndef TCPSTREAM
902 #define TCPSTREAM void
903 ***************
904 *** 5490,5495 ****
905 --- 5491,5513 ----
906 opts[i++].label = "";
909 + #ifdef SMIME
911 + opts[i].ch = 'e';
912 + opts[i].rval = 'e';
913 + opts[i].name = "E";
914 + opts[i++].label = "Encrypt";
916 + opts[i].ch = 'g';
917 + opts[i].rval = 'g';
918 + opts[i].name = "G";
919 + opts[i++].label = "Sign";
921 + g_do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON,ps_global);
922 + g_do_sign = F_ON(F_SIGN_DEFAULT_ON,ps_global);
923 + }
924 + #endif
926 opts[i].ch = -1;
927 no_help = (i >= 12);
929 ***************
930 *** 5574,5579 ****
931 --- 5592,5627 ----
932 sstrcpy(&optp, dsn_string);
935 + #ifdef SMIME
936 + if (g_do_encrypt) {
937 + if(!lparen){
938 + *optp++ = ' ';
939 + *optp++ = '(';
940 + lparen++;
942 + else{
943 + *optp++ = ',';
944 + *optp++ = ' ';
947 + sstrcpy(&optp, "Encrypted");
950 + if (g_do_sign) {
951 + if(!lparen){
952 + *optp++ = ' ';
953 + *optp++ = '(';
954 + lparen++;
956 + else{
957 + *optp++ = ',';
958 + *optp++ = ' ';
961 + sstrcpy(&optp, "Signed");
963 + #endif
965 if(lparen)
966 *optp++ = ')';
968 ***************
969 *** 5675,5680 ****
970 --- 5723,5734 ----
971 * body on failure.
973 dsn_requested = (DSN_SHOW | DSN_SUCCESS | DSN_DELAY | DSN_FULL);
974 + #ifdef SMIME
975 + } else if (rv=='e') {
976 + g_do_encrypt = !g_do_encrypt;
977 + } else if (rv=='g') {
978 + g_do_sign = !g_do_sign;
979 + #endif
982 sprintf(dsn_string, "DSN requested[%s%s%s%s]",
983 ***************
984 *** 6418,6423 ****
985 --- 6472,6478 ----
986 char *verbose_file = NULL;
987 BODY *bp = NULL;
988 PINEFIELD *pf;
989 + BODY *origBody = body;
991 #define MAX_ADDR_ERROR 2 /* Only display 2 address errors */
993 ***************
994 *** 6434,6439 ****
995 --- 6489,6518 ----
996 return(0);
1000 + #ifdef SMIME
1001 + if (g_do_encrypt || g_do_sign) {
1002 + int result;
1004 + STORE_S *so = lmc.so;
1005 + lmc.so = NULL;
1007 + result = 1;
1009 + if (g_do_encrypt)
1010 + result = encrypt_outgoing_message(header,&body);
1012 + /* need to free new body from encrypt if sign fails? */
1013 + if (result && g_do_sign)
1014 + result = sign_outgoing_message(header,&body,g_do_encrypt);
1016 + lmc.so = so;
1018 + if (!result)
1019 + return 0;
1021 + #endif
1023 /* set up counts and such to keep track sent percentage */
1024 send_bytes_sent = 0;
1025 gf_filter_init(); /* zero piped byte count, 'n */
1026 ***************
1027 *** 6742,6747 ****
1028 --- 6821,6844 ----
1029 mail_free_envelope(&fake_env);
1031 done:
1033 + #ifdef SMIME
1034 + /* Free replacement encrypted body */
1035 + if (body != origBody) {
1037 + if (body->type==TYPEMULTIPART) {
1038 + /* Just get rid of first part, it's actually origBody */
1039 + void *x = body->nested.part;
1041 + body->nested.part = body->nested.part->next;
1043 + fs_give(&x);
1046 + pine_free_body(&body);
1048 + #endif
1050 if(we_cancel)
1051 cancel_busy_alarm(0);
1053 ***************
1054 *** 8721,8733 ****
1055 dprint(4, (debugfile, "-- pine_encode_body: %d\n", body ? body->type : 0));
1056 if (body) switch (body->type) {
1057 case TYPEMULTIPART: /* multi-part */
1058 ! if (!body->parameter) { /* cookie not set up yet? */
1059 char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/
1060 sprintf (tmp,"%ld-%ld-%ld=:%ld",gethostid (),random (),time (0),
1061 getpid ());
1062 ! body->parameter = mail_newbody_parameter ();
1063 ! body->parameter->attribute = cpystr ("BOUNDARY");
1064 ! body->parameter->value = cpystr (tmp);
1066 part = body->nested.part; /* encode body parts */
1067 do pine_encode_body (&part->body);
1068 --- 8818,8834 ----
1069 dprint(4, (debugfile, "-- pine_encode_body: %d\n", body ? body->type : 0));
1070 if (body) switch (body->type) {
1071 case TYPEMULTIPART: /* multi-part */
1072 ! if (!body->parameter || strucmp(body->parameter->attribute,"BOUNDARY")!=0) { /* cookie not set up yet? */
1073 char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/
1074 + PARAMETER *param;
1076 sprintf (tmp,"%ld-%ld-%ld=:%ld",gethostid (),random (),time (0),
1077 getpid ());
1078 ! param = mail_newbody_parameter ();
1079 ! param->next = body->parameter;
1080 ! param->attribute = cpystr ("BOUNDARY");
1081 ! param->value = cpystr (tmp);
1082 ! body->parameter = param;
1084 part = body->nested.part; /* encode body parts */
1085 do pine_encode_body (&part->body);
1086 diff -Ncr pine4.53/pine/smime.c pine4.53-smime/pine/smime.c
1087 *** pine4.53/pine/smime.c Wed Dec 31 16:00:00 1969
1088 --- pine4.53-smime/pine/smime.c Wed Jan 15 12:48:15 2003
1089 ***************
1090 *** 0 ****
1091 --- 1,1776 ----
1092 + /*
1093 + File: smime.c
1094 + Author: paisleyj@dcs.gla.ac.uk
1095 + Date: 01/2001
1097 + Description:
1098 + This file contains all the low-level functions
1099 + required for dealing with S/MIME objects.
1101 + References are made to the functions in this file
1102 + from the following locations:
1104 + mailview.c:part_desc() -> is_pkcs7_body()
1105 + send.c:call_mailer() -> encrypt_outgoing_message()
1106 + send.c:call_mailer() -> sign_outgoing_message()
1107 + mailcmd.c:process_cmd() -> get_passphrase()
1108 + mailcmd.c:process_cmd() -> smime_info_screen()
1109 + */
1111 + #ifdef SMIME
1113 + #include "headers.h"
1115 + #include <stdio.h>
1116 + #include <stdlib.h>
1117 + #include <string.h>
1118 + #include <time.h>
1120 + #include <openssl/err.h>
1121 + #include <openssl/objects.h>
1122 + #include <openssl/evp.h>
1124 + #include <openssl/x509.h>
1125 + #include <openssl/pkcs7.h>
1126 + #include <openssl/pem.h>
1127 + #include <openssl/rand.h>
1129 + #include "bss_so.h"
1130 + #include "smkeys.h"
1131 + #include "smime.h"
1133 + #define PINE_SMIME_DIRNAME ".pine-smime"
1135 + /* Set true if loading a key failed due to lack of passphrase.
1136 + Queried in mailcmd.c:process_cmd() before calling get_passphrase()
1137 + */
1138 + int g_need_passphrase = 0;
1139 + /* User has entered a passphrase */
1140 + static int s_entered_passphrase = 0;
1141 + /* Storage for the entered passphrase */
1142 + static char s_passphrase[80];
1143 + static char *s_passphrase_emailaddr;
1145 + /* Set true if encrypting/signing (respectively)
1146 + Referenced from send.c:call_mailer() and send.c:send_exit_for_pico
1147 + */
1148 + int g_do_encrypt;
1149 + int g_do_sign;
1151 + /* Full pathname to ~/.pine-smime */
1152 + static char *g_pine_smime_dir;
1154 + static BIO *bio_err;
1156 + /* Linked list of PERSONAL_CERT objects */
1157 + static PERSONAL_CERT *s_personal_certs;
1159 + static X509_STORE *s_cert_store;
1161 + /* State management for randomness functions below */
1162 + static int seeded = 0;
1163 + static int egdsocket = 0;
1165 + /* Forget any cached private keys */
1166 + static void forget_private_keys()
1168 + PERSONAL_CERT *pcert;
1170 + for (pcert=s_personal_certs; pcert; pcert=pcert->next) {
1172 + if (pcert->key) {
1173 + EVP_PKEY_free(pcert->key);
1174 + pcert->key = NULL;
1179 + /* taken from openssl/apps/app_rand.c */
1180 + static int app_RAND_load_file(const char *file, BIO *bio_e, int dont_warn)
1182 + int consider_randfile = (file == NULL);
1183 + char buffer[200];
1185 + if (file == NULL)
1186 + file = RAND_file_name(buffer, sizeof buffer);
1187 + else if (RAND_egd(file) > 0)
1189 + /* we try if the given filename is an EGD socket.
1190 + if it is, we don't write anything back to the file. */
1191 + egdsocket = 1;
1192 + return 1;
1194 + if (file == NULL || !RAND_load_file(file, -1))
1196 + if (RAND_status() == 0 && !dont_warn)
1198 + BIO_printf(bio_e,"unable to load 'random state'\n");
1199 + BIO_printf(bio_e,"This means that the random number generator has not been seeded\n");
1200 + BIO_printf(bio_e,"with much random data.\n");
1201 + if (consider_randfile) /* explanation does not apply when a file is explicitly named */
1203 + BIO_printf(bio_e,"Consider setting the RANDFILE environment variable to point at a file that\n");
1204 + BIO_printf(bio_e,"'random' data can be kept in (the file will be overwritten).\n");
1207 + return 0;
1209 + seeded = 1;
1210 + return 1;
1213 + /* copied and fiddled from pine/imap/src/osdep/unix/auth_ssl.c */
1214 + static void openssl_extra_randomness(void)
1216 + #if !defined(WIN32)
1217 + int fd;
1218 + unsigned long i;
1219 + char tmp[MAXPATH];
1220 + struct stat sbuf;
1221 + /* if system doesn't have /dev/urandom */
1222 + if (stat ("/dev/urandom",&sbuf)) {
1223 + if ((fd = open (tmpnam (tmp),O_WRONLY|O_CREAT,0600)) < 0)
1224 + i = (unsigned long) tmp;
1225 + else {
1226 + unlink (tmp); /* don't need the file */
1227 + fstat (fd,&sbuf); /* get information about the file */
1228 + i = sbuf.st_ino; /* remember its inode */
1229 + close (fd); /* or its descriptor */
1231 + /* not great but it'll have to do */
1232 + sprintf (tmp + strlen (tmp),"%.80s%lx%lx%lx",
1233 + tcp_serverhost (),i,
1234 + (unsigned long) (time (0) ^ gethostid ()),
1235 + (unsigned long) getpid ());
1236 + RAND_seed (tmp,strlen (tmp));
1238 + #endif
1241 + /* taken from openssl/apps/app_rand.c */
1242 + static int app_RAND_write_file(const char *file, BIO *bio_e)
1244 + char buffer[200];
1246 + if (egdsocket || !seeded)
1247 + /* If we did not manage to read the seed file,
1248 + * we should not write a low-entropy seed file back --
1249 + * it would suppress a crucial warning the next time
1250 + * we want to use it. */
1251 + return 0;
1253 + if (file == NULL)
1254 + file = RAND_file_name(buffer, sizeof buffer);
1255 + if (file == NULL || !RAND_write_file(file))
1257 + BIO_printf(bio_e,"unable to write 'random state'\n");
1258 + return 0;
1260 + return 1;
1263 + /* Installed as an atexit() handler to save the random data */
1264 + static void openssl_deinit(void)
1266 + app_RAND_write_file(NULL, bio_err);
1269 + /* Initialise openssl stuff if needed */
1270 + static void openssl_init(void)
1272 + static int inited = 0;
1274 + if (!inited) {
1276 + char *p;
1277 + char buf[MAXPATH];
1279 + /* Find where that .pine-smime thing is */
1280 + /* Perhaps we should just use the user's home directory as a start? */
1281 + p = last_cmpnt(ps_global->pinerc);
1282 + buf[0] = '\0';
1283 + if(p != NULL) {
1284 + strncpy(buf, ps_global->pinerc, min(p - ps_global->pinerc, sizeof(buf)-1));
1285 + buf[min(p - ps_global->pinerc, sizeof(buf)-1)] = '\0';
1288 + strncat(buf, PINE_SMIME_DIRNAME, sizeof(buf)-1-strlen(buf));
1289 + buf[sizeof(buf)-1] = 0;
1291 + if (can_access(buf, ACCESS_EXISTS)==0) {
1293 + g_pine_smime_dir = cpystr(buf);
1294 + s_cert_store = get_ca_store(g_pine_smime_dir);
1295 + s_personal_certs = get_personal_certs(g_pine_smime_dir);
1297 + } else g_pine_smime_dir = ""; /* prevent null dereference later */
1299 + SSLeay_add_all_algorithms();
1300 + ERR_load_crypto_strings();
1302 + /* this won't make any sense (since the terminal's in a funny mode) */
1303 + if ((bio_err=BIO_new(BIO_s_file())) != NULL)
1304 + BIO_set_fp(bio_err,stderr,BIO_NOCLOSE|BIO_FP_TEXT);
1306 + app_RAND_load_file(NULL, bio_err, 1);
1308 + openssl_extra_randomness();
1310 + /* save the rand file when we're done */
1311 + atexit(openssl_deinit);
1313 + inited = 1;
1316 + ERR_clear_error();
1319 + /* Get a pointer to a string describing the most recent OpenSSL error.
1320 + It's statically allocated, so don't change or attempt to free it.
1321 + */
1322 + static const char *openssl_error_string(void)
1324 + char *errs;
1325 + const char *data = NULL;
1326 + long errn;
1328 + errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1329 + errs = (char*)ERR_reason_error_string(ERR_GET_REASON(errn));
1331 + if (errs)
1332 + return errs;
1333 + else if (data)
1334 + return data;
1336 + return "unknown error";
1339 + /* Return true if the body looks like a PKCS7 object */
1340 + int is_pkcs7_body(BODY *body)
1342 + int result;
1344 + result = body->type==TYPEAPPLICATION &&
1345 + body->subtype &&
1346 + (strucmp(body->subtype,"pkcs7-mime")==0 ||
1347 + strucmp(body->subtype,"x-pkcs7-mime")==0 ||
1348 + strucmp(body->subtype,"pkcs7-signature")==0 ||
1349 + strucmp(body->subtype,"x-pkcs7-signature")==0);
1351 + return result;
1354 + /* debug utility to dump the contents of a BIO to a file */
1355 + static void dump_bio_to_file(BIO *in,char *filename)
1357 + char iobuf[4096];
1358 + int len;
1359 + BIO *out;
1361 + out = BIO_new_file(filename,"w");
1363 + if (out) {
1364 + BIO_reset(in);
1366 + while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1367 + BIO_write(out, iobuf, len);
1368 + BIO_free(out);
1372 + /* prompt the user for their passphrase
1373 + (possibly prompting with the email address in s_passphrase_emailaddr)
1374 + */
1375 + int get_passphrase(void)
1377 + int rc;
1378 + int flags;
1379 + char prompt[50];
1380 + HelpType help = NO_HELP;
1382 + sprintf(prompt,
1383 + "Enter passphrase for <%s>: ",s_passphrase_emailaddr ? s_passphrase_emailaddr : "unknown");
1385 + do {
1386 + flags = OE_PASSWD | OE_DISALLOW_HELP;
1387 + rc = optionally_enter(s_passphrase, -FOOTER_ROWS(ps_global), 0, sizeof(s_passphrase),
1388 + prompt, NULL, help, &flags);
1389 + } while (rc!=0 && rc!=1 && rc>0);
1391 + if (rc==0)
1392 + s_entered_passphrase = 1;
1394 + return rc==0;
1397 + /* Recursively stash a pointer to the decrypted data in our
1398 + manufactured body.
1399 + */
1400 + static void create_local_cache(char *base,BODY *b)
1402 + if (b->type==TYPEMULTIPART) {
1403 + PART *p;
1405 + #if 0
1406 + cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1407 + #else
1408 + /* don't really want to copy the real body contents. It shouldn't be
1409 + used, and in the case of a message with attachments, we'll be
1410 + duplicating the files multiple times
1411 + */
1412 + cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
1413 + #endif
1415 + for (p=b->nested.part;p;p=p->next) {
1416 + create_local_cache(base,(BODY*) p);
1418 + } else {
1419 + cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1423 + static long rfc822_output_func(void *stream,char *string)
1425 + STORE_S *so = (STORE_S*) stream;
1427 + return so_puts(so,string)!=0;
1430 + /* Load a private key from the given file */
1431 + static EVP_PKEY *load_key(char *file, char *pass)
1433 + BIO *in;
1434 + EVP_PKEY *key;
1435 + if(!(in = BIO_new_file(file, "r"))) return NULL;
1436 + key = PEM_read_bio_PrivateKey(in, NULL,NULL,pass);
1437 + BIO_free(in);
1438 + return key;
1441 + /* Attempt to load the private key for the given PERSONAL_CERT.
1442 + This sets the appropriate passphrase globals in order to
1443 + interact with the user correctly.
1444 + */
1445 + static int load_private_key(PERSONAL_CERT *pcert)
1447 + if (!pcert->key) {
1449 + /* Try empty password by default */
1450 + char *password = "";
1452 + if (g_need_passphrase) {
1453 + /* We've already been in here and discovered we need a different password */
1455 + if (s_entered_passphrase)
1456 + password = s_passphrase; /* Use the passphrase if it's been entered */
1457 + else return 0;
1460 + ERR_clear_error();
1462 + if(!(pcert->key = load_key(pcert->file, password))) {
1463 + long err = ERR_get_error();
1465 + /* Couldn't load key... */
1467 + if (s_entered_passphrase) {
1469 + /* The user got the password wrong maybe? */
1471 + if ((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
1472 + (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
1473 + q_status_message(SM_ORDER | SM_DING,1,1,"Wrong password");
1474 + else q_status_message1(SM_ORDER,1,1,"Couldn't read key: %s",(char*)openssl_error_string());
1476 + /* This passphrase is no good; forget it */
1477 + s_entered_passphrase = 0;
1480 + /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
1481 + g_need_passphrase = 1;
1483 + fs_give((void**) &s_passphrase_emailaddr);
1484 + s_passphrase_emailaddr = get_x509_subject_email(pcert->cert);
1485 + return 0;
1486 + } else {
1487 + /* This key will be cached, so we won't be called again */
1488 + s_entered_passphrase = 0;
1489 + g_need_passphrase = 0;
1492 + return 1;
1495 + return 0;
1498 + static void setup_pkcs7_body_for_signature(BODY *b,char *description,char *type,char *filename)
1500 + b->type = TYPEAPPLICATION;
1501 + b->subtype = cpystr(type);
1502 + b->encoding = ENCBINARY;
1504 + b->description = cpystr(description);
1506 + b->disposition.type = cpystr("attachment");
1507 + b->disposition.parameter = mail_newbody_parameter();
1508 + b->disposition.parameter->attribute = cpystr("filename");
1509 + b->disposition.parameter->value = cpystr(filename);
1511 + b->parameter = mail_newbody_parameter();
1512 + b->parameter->attribute = cpystr("name");
1513 + b->parameter->value = cpystr(filename);
1516 + /*
1517 + Look for a personal certificate matching the
1518 + given address
1519 + */
1520 + PERSONAL_CERT *match_personal_cert_to_email(ADDRESS *a)
1522 + PERSONAL_CERT *pcert;
1523 + char buf[MAXPATH];
1524 + char *email;
1526 + if (!a || !a->mailbox || !a->host)
1527 + return NULL;
1529 + snprintf(buf,sizeof(buf),"%s@%s",a->mailbox,a->host);
1531 + for (pcert=s_personal_certs;pcert;pcert=pcert->next) {
1533 + if (!pcert->cert)
1534 + continue;
1536 + email = get_x509_subject_email(pcert->cert);
1538 + if (email && strucmp(email,buf)==0) {
1539 + fs_give((void**) &email);
1540 + break;
1543 + fs_give((void**) &email);
1546 + return pcert;
1549 + /*
1550 + Look for a personal certificate matching the from
1551 + (or reply_to? in the given envelope)
1552 + */
1553 + PERSONAL_CERT *match_personal_cert(ENVELOPE *env)
1555 + PERSONAL_CERT *pcert;
1557 + pcert = match_personal_cert_to_email(env->reply_to);
1558 + if (!pcert)
1559 + pcert = match_personal_cert_to_email(env->from);
1561 + return pcert;
1564 + /*
1565 + Flatten the given body into its MIME representation.
1566 + Return the result in a CharStar STORE_S.
1567 + */
1568 + static STORE_S *body_to_store(BODY *body)
1570 + STORE_S *store;
1571 + store = so_get(CharStar, NULL, EDIT_ACCESS);
1572 + if (!store)
1573 + return NULL;
1575 + pine_encode_body(body); /* this attaches random boundary strings to multiparts */
1576 + pine_write_body_header (body, rfc822_output_func,store);
1577 + pine_rfc822_output_body(body, rfc822_output_func,store);
1579 + /* now need to truncate by two characters since the above
1580 + appends CRLF to the stream
1581 + */
1583 + /** Eek! No way of telling size of a STORE_S. We depend on knowing it's
1584 + a CharStar */
1585 + so_truncate(store,((char*)store->eod-(char*)store->txt)-2);
1587 + so_seek(store,0,SEEK_SET);
1589 + return store;
1594 + /*
1595 + Sign a message. Called from call_mailer in send.c.
1597 + This takes the header for the outgoing message as well as a pointer
1598 + to the current body (which may be reallocated).
1599 + */
1600 + int sign_outgoing_message(METAENV *header,BODY **bodyP,int dont_detach)
1602 + STORE_S *store = NULL;
1603 + STORE_S *outs = NULL;
1604 + BODY *body = *bodyP;
1605 + BODY *newBody = NULL;
1606 + PART *p1 = NULL;
1607 + PART *p2 = NULL;
1608 + PERSONAL_CERT *pcert;
1609 + BIO *in = NULL;
1610 + BIO *out = NULL;
1611 + PKCS7 *p7 = NULL;
1612 + int result = 0;
1613 + PARAMETER *param;
1615 + int flags = dont_detach ? 0 : PKCS7_DETACHED;
1617 + openssl_init();
1619 + store = body_to_store(body);
1621 + /* Look for a private key matching the sender address... */
1623 + pcert = match_personal_cert(header->env);
1625 + if (!pcert) {
1626 + q_status_message(SM_ORDER,1,1,"Couldn't find the certificate needed to sign.");
1627 + goto end;
1630 + if (!load_private_key(pcert) && g_need_passphrase) {
1631 + /* Couldn't load key with blank password, try again */
1632 + get_passphrase();
1633 + load_private_key(pcert);
1636 + if (!pcert->key)
1637 + goto end;
1639 + in = BIO_new_so(store);
1641 + #if 0
1642 + dump_bio_to_file(in,"/tmp/signed-data");
1643 + #endif
1645 + BIO_reset(in);
1647 + p7 = PKCS7_sign(pcert->cert, pcert->key, NULL, in, flags);
1648 + if (!p7) {
1649 + q_status_message(SM_ORDER,1,1,"Error creating PKCS7 object.");
1650 + goto end;
1653 + outs = so_get(CharStar,NULL,EDIT_ACCESS);
1654 + out = BIO_new_so(outs);
1656 + i2d_PKCS7_bio(out, p7);
1657 + BIO_flush(out);
1659 + so_seek(outs,0,SEEK_SET);
1661 + if ((flags&PKCS7_DETACHED)==0) {
1663 + /* the simple case: the signed data is in the pkcs7 object */
1665 + newBody = mail_newbody();
1667 + setup_pkcs7_body_for_signature(newBody,"S/MIME Cryptographically Signed Message","x-pkcs7-mime","smime.p7m");
1669 + newBody->contents.text.data = (char*) outs;
1670 + *bodyP = newBody;
1672 + result = 1;
1673 + } else {
1675 + /* OK.
1676 + We have to create a new body as follows:
1678 + multipart/signed; blah blah blah
1679 + reference to existing body
1681 + pkcs7 object
1682 + */
1684 + newBody = mail_newbody();
1686 + newBody->type = TYPEMULTIPART;
1687 + newBody->subtype = cpystr("signed");
1688 + newBody->encoding = ENC7BIT;
1690 + newBody->parameter = param = mail_newbody_parameter();
1691 + param->attribute = cpystr("protocol");
1692 + param->value = cpystr("application/x-pkcs7-signature");
1694 + newBody->parameter->next = param = mail_newbody_parameter();
1695 + param->attribute = cpystr("micalg");
1696 + param->value = cpystr("sha1");
1698 + p1 = mail_newbody_part();
1699 + p2 = mail_newbody_part();
1701 + /* this is nasty. We're just copying the body in here,
1702 + but since our newBody is freed at the end of call_mailer,
1703 + we mustn't let this body (the original one) be freed twice.
1704 + */
1705 + p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
1707 + p1->next = p2;
1709 + setup_pkcs7_body_for_signature(&p2->body,"S/MIME Cryptographic Signature","x-pkcs7-signature","smime.p7s");
1710 + p2->body.contents.text.data = (char*) outs;
1712 + newBody->nested.part = p1;
1714 + *bodyP = newBody;
1716 + result = 1;
1719 + end:
1721 + PKCS7_free(p7);
1722 + BIO_free(in);
1723 + BIO_free(out);
1724 + if (store)
1725 + so_give(&store);
1727 + return result;
1730 + /*
1731 + Encrypt a message on the way out. Called from call_mailer in send.c
1732 + The body may be reallocated.
1733 + */
1734 + int encrypt_outgoing_message(METAENV *header,BODY **bodyP)
1736 + PKCS7 *p7 = NULL;
1737 + BIO *in = NULL;
1738 + BIO *out = NULL;
1739 + EVP_CIPHER *cipher = NULL;
1740 + STACK_OF(X509) *encerts = NULL;
1741 + STORE_S *store = NULL;
1742 + STORE_S *outs = NULL;
1743 + PINEFIELD *pf;
1744 + ADDRESS *a;
1745 + BODY *body = *bodyP;
1746 + BODY *newBody = NULL;
1747 + int result = 0;
1749 + openssl_init();
1751 + cipher = EVP_des_cbc();
1753 + encerts = sk_X509_new_null();
1755 + /* Look for a certificate for each of the recipients */
1756 + for(pf = header->local; pf && pf->name; pf = pf->next)
1757 + if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr) {
1759 + for (a=*pf->addr;a;a=a->next) {
1760 + X509 *cert;
1761 + char buf[MAXPATH];
1763 + snprintf(buf,sizeof(buf),"%s@%s",a->mailbox,a->host);
1765 + cert = get_cert_for(g_pine_smime_dir,buf);
1766 + if (cert)
1767 + sk_X509_push(encerts,cert);
1768 + else {
1769 + q_status_message2(SM_ORDER,1,1,
1770 + "Unable to find certificate for <%s@%s>",a->mailbox,a->host);
1771 + goto end;
1777 + store = body_to_store(body);
1779 + in = BIO_new_so(store);
1781 + p7 = PKCS7_encrypt(encerts, in, cipher, 0);
1783 + outs = so_get(CharStar,NULL,EDIT_ACCESS);
1784 + out = BIO_new_so(outs);
1786 + i2d_PKCS7_bio(out, p7);
1787 + BIO_flush(out);
1788 + so_seek(outs,0,SEEK_SET);
1790 + newBody = mail_newbody();
1792 + newBody->type = TYPEAPPLICATION;
1793 + newBody->subtype = cpystr("x-pkcs7-mime");
1794 + newBody->encoding = ENCBINARY;
1796 + newBody->description = cpystr("S/MIME Encrypted Message");
1798 + newBody->contents.text.data = (char*) outs;
1800 + *bodyP = newBody;
1802 + result = 1;
1804 + end:
1806 + BIO_free(in);
1807 + BIO_free(out);
1808 + PKCS7_free(p7);
1809 + sk_X509_pop_free(encerts, X509_free);
1810 + if (store)
1811 + so_give(&store);
1813 + return result;
1816 + /*
1817 + Plonk the contents (mime headers and body) of the given
1818 + section of a message to a CharStar STORE_S object.
1819 + */
1820 + static STORE_S *get_raw_part(int msgno,const char *section)
1822 + long len;
1823 + STORE_S *store = NULL;
1824 + char *text;
1826 + store = so_get(CharStar, NULL, EDIT_ACCESS);
1827 + if (store) {
1829 + /* First grab headers of the chap */
1830 + text = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) section, &len, 0);
1832 + if (text) {
1833 + so_nputs(store,text,len);
1835 + /** Now grab actual body */
1836 + text = mail_fetch_body (ps_global->mail_stream, msgno, (char*) section, &len, 0);
1837 + if (text) {
1838 + so_nputs(store,text,len);
1840 + so_seek(store,0,SEEK_SET);
1842 + } else so_give(&store);
1844 + } else so_give(&store);
1847 + return store;
1850 + /*
1851 + Get (and decode) the body of the given section of msg
1852 + */
1853 + static STORE_S *get_part_contents(int msgno,const char *section)
1855 + long len;
1856 + gf_io_t pc;
1857 + STORE_S *store = NULL;
1858 + char *err;
1860 + store = so_get(CharStar, NULL, EDIT_ACCESS);
1861 + if (store) {
1862 + gf_set_so_writec(&pc,store);
1864 + err = detach(ps_global->mail_stream, msgno, (char*) section,&len, pc, NULL);
1866 + gf_clear_so_writec(store);
1868 + so_seek(store,0,SEEK_SET);
1870 + if (err)
1871 + so_give(&store);
1873 + return store;
1876 + static PKCS7 *get_pkcs7_from_part(int msgno,const char *section)
1878 + STORE_S *store = NULL;
1879 + PKCS7 *p7 = NULL;
1880 + BIO *in = NULL;
1882 + store = get_part_contents(msgno,section);
1884 + if (store) {
1885 + in = BIO_new_so(store);
1886 + if (in) {
1887 + p7=d2i_PKCS7_bio(in,NULL);
1891 + if (store)
1892 + so_give(&store);
1894 + BIO_free(in);
1896 + return p7;
1899 + /*
1900 + Try to verify a signature.
1902 + p7 - the pkcs7 object to verify
1903 + in - the plain data to verify (NULL if not detached)
1904 + out - BIO to which to write the opaque data
1905 + */
1906 + static int do_signature_verify(PKCS7 *p7,BIO *in,BIO *out)
1908 + STACK_OF(X509) *otherCerts = NULL;
1909 + int result;
1910 + const char *data;
1911 + long err;
1913 + #if 0
1914 + dump_bio_to_file(in,"/tmp/verified-data");
1915 + #endif
1917 + BIO_reset(in);
1919 + #if 0
1920 + /* testing verification stuff */
1922 + X509 *c;
1924 + c = get_cert_for(g_pine_smime_dir,"xx");
1925 + if (c) {
1926 + X509_add1_reject_object(c, OBJ_nid2obj(NID_email_protect));
1928 + X509_STORE_add_cert(s_cert_store,c);
1930 + save_cert_for(g_pine_smime_dir,cpystr("yy"),c);
1934 + ERR_clear_error();
1937 + #endif
1939 + result = PKCS7_verify(p7, otherCerts, s_cert_store,
1940 + in, out, 0);
1942 + if (result) {
1943 + q_status_message(SM_ORDER,1,1,"S/MIME signature verified ok");
1944 + } else {
1945 + err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1947 + if (out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)) {
1949 + /* Retry verification so we can get the plain text */
1950 + /* Might be better to reimplement PKCS7_verify here? */
1952 + PKCS7_verify(p7, otherCerts, s_cert_store,
1953 + in, out, PKCS7_NOVERIFY);
1957 + q_status_message1(SM_ORDER | SM_DING,1,1,"Couldn't verify S/MIME signature: %s",(char*) openssl_error_string());
1959 + return result;
1962 + /* now try to extract the certificates of any signers */
1964 + STACK_OF(X509) *signers;
1965 + int i;
1967 + signers = PKCS7_get0_signers(p7, NULL, 0);
1969 + if (signers)
1970 + for (i=0;i<sk_X509_num(signers);i++) {
1971 + char *email;
1972 + X509 *x = sk_X509_value(signers,i);
1973 + X509 *cert;
1975 + if (!x)
1976 + continue;
1978 + email = get_x509_subject_email(x);
1980 + if (email) {
1981 + cert = get_cert_for(g_pine_smime_dir,email);
1982 + if (cert) {
1983 + X509_free(cert);
1984 + } else {
1985 + save_cert_for(g_pine_smime_dir,email,x);
1987 + fs_give((void**) &email);
1991 + sk_X509_free(signers);
1994 + return result;
1997 + /* Hook inside BODY structure for cleaning up S/MIME message bodies */
1998 + static void smime_body_cleanup(BODY *b)
2000 + #if 0
2001 + q_status_message(SM_ORDER,1,1,"smime_body_cleanup called");
2002 + #endif
2004 + if (b->sparep) {
2005 + PKCS7_free((PKCS7*) b->sparep);
2006 + b->sparep = NULL;
2010 + /*
2011 + Given a multipart body of type multipart/signed, attempt to verify
2012 + it
2013 + */
2014 + static int do_detached_signature_verify(BODY *b,int msgno,char *section)
2016 + STORE_S *toVerify = NULL;
2017 + PKCS7 *p7 = NULL;
2018 + BIO *in = NULL;
2019 + PART *p;
2020 + int result = 0;
2021 + char seq[100];
2022 + char *what_we_did;
2024 + openssl_init();
2026 + snprintf(seq,sizeof(seq),"%s1",section);
2027 + toVerify = get_raw_part(msgno,seq);
2029 + if (toVerify) {
2031 + in = BIO_new_so(toVerify);
2032 + if (!in)
2033 + goto end;
2035 + snprintf(seq,sizeof(seq),"%s2",section);
2036 + p7 = get_pkcs7_from_part(msgno,seq);
2038 + if (!p7)
2039 + goto end;
2041 + result = do_signature_verify(p7,in,NULL);
2043 + if (b->subtype) fs_give((void**) &b->subtype);
2044 + b->subtype = cpystr("x-pkcs7-enclosure");
2045 + b->encoding = ENC8BIT;
2047 + if (b->description) fs_give ((void**) &b->description);
2049 + what_we_did = result ? "This message was cryptographically signed." :
2050 + "This message was cryptographically signed but the signature could not be verified.";
2052 + b->description = cpystr(what_we_did);
2054 + b->sparep = p7;
2055 + p7 = NULL;
2056 + b->cleanup = smime_body_cleanup;
2058 + p = b->nested.part;
2060 + /* p is signed plaintext */
2061 + if (p && p->next)
2062 + mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
2064 + result = 0;
2066 + end:
2067 + BIO_free(in);
2069 + PKCS7_free(p7);
2071 + if (toVerify)
2072 + so_give(&toVerify);
2074 + return result;
2077 + static PERSONAL_CERT *find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
2079 + PERSONAL_CERT *x;
2081 + for (x=s_personal_certs;x;x=x->next) {
2082 + X509 *mine;
2084 + mine = x->cert;
2086 + if (!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
2087 + !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)) {
2088 + break;
2092 + return x;
2095 + static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7)
2097 + int i;
2098 + STACK_OF(PKCS7_RECIP_INFO) *recips;
2099 + PERSONAL_CERT *x = NULL;
2101 + recips = p7->d.enveloped->recipientinfo;
2103 + for (i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++) {
2104 + PKCS7_RECIP_INFO *ri;
2106 + ri=sk_PKCS7_RECIP_INFO_value(recips,i);
2108 + if ((x=find_certificate_matching_recip_info(ri))!=0) {
2109 + break;
2113 + return x;
2116 + /*
2117 + Try to decode (decrypt or verify a signature) a PKCS7 body
2118 + */
2119 + static int do_decoding(BODY *b,int msgno,const char *section)
2121 + STORE_S *outs = NULL;
2122 + int result = 0;
2124 + BIO *out = NULL;
2125 + PKCS7 *p7 = NULL;
2126 + X509 *recip = NULL;
2127 + EVP_PKEY *key = NULL;
2128 + PERSONAL_CERT *pcert = NULL;
2130 + char *what_we_did = "";
2132 + openssl_init();
2134 + /*
2135 + Extract binary data from part to an in-memory store
2136 + */
2138 + if (b->sparep) {
2140 + p7 = (PKCS7*) b->sparep;
2142 + } else {
2144 + p7 = get_pkcs7_from_part(msgno,section);
2145 + if (p7 == NULL) {
2146 + q_status_message1(SM_ORDER,1,1,"Couldn't load PKCS7 object: %s",(char*)openssl_error_string());
2147 + goto end;
2150 + /* Save the PKCS7 object for later dealings by the user interface.
2151 + It will be cleaned up when the body is garbage collected
2152 + */
2153 + b->sparep = p7;
2154 + b->cleanup = smime_body_cleanup;
2157 + if (PKCS7_type_is_signed(p7)) {
2158 + int sigok;
2160 + outs = so_get(CharStar, NULL, EDIT_ACCESS);
2161 + so_puts(outs,"MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
2162 + out = BIO_new_so(outs);
2164 + sigok = do_signature_verify(p7,NULL,out);
2166 + /* shouldn't really duplicate these messages */
2167 + what_we_did = sigok ? "This message was cryptographically signed." :
2168 + "This message was cryptographically signed but the signature could not be verified.";
2170 + } else if (!PKCS7_type_is_enveloped(p7)) {
2171 + q_status_message(SM_ORDER,1,1,"PKCS7 object not recognised.");
2172 + goto end;
2173 + } else { /* It *is* enveloped */
2175 + what_we_did = "This message was encrypted.";
2177 + /*
2178 + Now need to find a cert that can decrypt this boy
2179 + */
2180 + pcert = find_certificate_matching_pkcs7(p7);
2182 + if (!pcert) {
2183 + q_status_message(SM_ORDER,1,1,"Couldn't find the certificate needed to decrypt.");
2184 + goto end;
2187 + recip = pcert->cert;
2189 + load_private_key(pcert);
2191 + key = pcert->key;
2192 + if (!key)
2193 + goto end;
2195 + outs = so_get(CharStar, NULL, EDIT_ACCESS);
2196 + so_puts(outs,"MIME-Version: 1.0\r\n");
2198 + out = BIO_new_so(outs);
2200 + if(!PKCS7_decrypt(p7, key, recip, out, 0)) {
2201 + q_status_message1(SM_ORDER,1,1,"Error decrypting PKCS7: %s",(char*) openssl_error_string());
2202 + goto end;
2207 + /* We've now produced a flattened MIME object in store outs.
2208 + It needs to be turned back into a BODY
2209 + */
2212 + BODY *body;
2213 + ENVELOPE *env;
2214 + char *h;
2215 + char *bstart;
2216 + STRING s;
2218 + h = so_text(outs);
2220 + /* look for start of body */
2221 + bstart = strstr(h,"\r\n\r\n");
2223 + if (!bstart) {
2224 + q_status_message(SM_ORDER,1,1,"Encrypted data couldn't be parsed.");
2225 + } else {
2226 + bstart += 4; /* skip over CRLF*2 */
2228 + INIT(&s,mail_string,bstart,strlen(bstart));
2229 + rfc822_parse_msg_full(&env,&body,h,bstart-h-2,&s,BADHOST,0,0);
2230 + mail_free_envelope(&env); /* Don't care about this */
2232 + /*
2233 + Now convert original body (application/pkcs7-mime)
2234 + to a multipart body with one sub-part (the decrypted body)
2235 + Note that the sub-part may also be multipart!
2236 + */
2238 + b->type = TYPEMULTIPART;
2239 + if (b->subtype) fs_give((void**) &b->subtype);
2241 + /* This subtype is used in mailview.c to annotate the display of
2242 + encrypted or signed messages. We know for sure then that it's a PKCS7
2243 + part because the sparep field is set to the PKCS7 object (see above)
2244 + */
2245 + b->subtype = cpystr("x-pkcs7-enclosure");
2246 + b->encoding = ENC8BIT;
2248 + if (b->description) fs_give ((void**) &b->description);
2249 + b->description = cpystr(what_we_did);
2251 + if (b->disposition.type) fs_give ((void **) &b->disposition.type);
2253 + if (b->contents.text.data) fs_give ((void **) &b->contents.text.data);
2255 + if (b->parameter) mail_free_body_parameter(&b->parameter);
2257 + /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
2258 + b->nested.part = fs_get(sizeof (PART));
2259 + b->nested.part->body = *body;
2260 + b->nested.part->next = NULL;
2262 + fs_give((void**) &body);
2264 + /* IMPORTANT BIT: set the body->contents.text.data elements to contain the decrypted
2265 + data. Otherwise, it'll try to load it from the original data. Eek.
2266 + */
2267 + create_local_cache(bstart,&b->nested.part->body);
2269 + result = 1;
2273 + end:
2275 + BIO_free(out);
2277 + if (outs)
2278 + so_give(&outs);
2280 + return result;
2283 + /*
2284 + Recursively handle PKCS7 bodies in our message.
2286 + Returns non-zero if some fiddling was done.
2287 + */
2288 + static int do_fiddle_smime_message(BODY *b,int msgno,char *section)
2290 + int result = 0;
2292 + if (is_pkcs7_body(b)) {
2294 + if (do_decoding(b,msgno,*section ? section : "1")) {
2295 + /*
2296 + b should now be a multipart message: fiddle it too in case it's been multiply-encrypted!
2297 + */
2299 + /* fallthru */
2300 + result = 1;
2304 + if (b->type==TYPEMULTIPART) {
2306 + PART *p;
2307 + int partNum;
2308 + char newSec[100];
2310 + if (b->subtype && strucmp(b->subtype,"signed")==0) {
2312 + /* Ahah. We have a multipart signed entity. */
2314 + /* part 1 (signed thing)
2315 + part 2 (the pkcs7 object)
2316 + */
2318 + do_detached_signature_verify(b,msgno,section);
2320 + } else {
2322 + for (p=b->nested.part,partNum=1;p;p=p->next,partNum++) {
2324 + /* Append part number to the section string */
2326 + snprintf(newSec,sizeof(newSec),"%s%s%d",section,*section ? "." : "",partNum);
2328 + result |= do_fiddle_smime_message(&p->body,msgno,newSec);
2335 + return result;
2338 + /*
2339 + Fiddle a message in-place by decrypting/verifying S/MIME entities.
2340 + Returns non-zero if something was changed.
2341 + */
2343 + int fiddle_smime_message(BODY *b,int msgno,int is_new_message)
2345 + if (F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
2346 + forget_private_keys();
2347 + return do_fiddle_smime_message(b,msgno,"");
2351 + /********************************************************************************/
2353 + static struct key smime_info_keys[] =
2354 + {HELP_MENU,
2355 + OTHER_MENU,
2356 + {"<","Back",{MC_VIEW_TEXT,2,{'<',','}},KS_EXITMODE},
2357 + NULL_MENU,
2358 + NULL_MENU,
2359 + NULL_MENU,
2360 + PREVPAGE_MENU,
2361 + NEXTPAGE_MENU,
2362 + NULL_MENU,
2363 + NULL_MENU,
2364 + NULL_MENU,
2365 + NULL_MENU,
2367 + HELP_MENU,
2368 + OTHER_MENU,
2369 + MAIN_MENU,
2370 + QUIT_MENU,
2371 + NULL_MENU,
2372 + NULL_MENU,
2373 + NULL_MENU,
2374 + NULL_MENU,
2375 + NULL_MENU,
2376 + INDEX_MENU,
2377 + NULL_MENU,
2378 + NULL_MENU,
2379 + };
2380 + INST_KEY_MENU(smime_info_keymenu, smime_info_keys);
2382 + #define SMIME_PARENT_KEY 2
2384 + static void get_fingerprint(X509 *cert,const EVP_MD *type,char *buf,int maxLen)
2386 + unsigned char md[128];
2387 + char *b;
2388 + int len,i;
2390 + len = sizeof(md);
2392 + X509_digest(cert,type,md,&len);
2394 + b = buf;
2395 + *b = 0;
2396 + for (i=0; i<len; i++)
2398 + if (b-buf+3>=maxLen)
2399 + break;
2401 + if (i != 0)
2402 + *b++ = ':';
2403 + sprintf(b,"%02x",md[i]);
2404 + b+=2;
2408 + static void output_X509_NAME(X509_NAME *name,gf_io_t pc)
2410 + int i,c;
2411 + int nid;
2412 + char buf[256];
2414 + c = X509_NAME_entry_count(name);
2416 + for (i=c-1;i>=0;i--) {
2417 + X509_NAME_ENTRY *e;
2419 + e = X509_NAME_get_entry(name,i);
2420 + if (!e) continue;
2422 + X509_NAME_get_text_by_OBJ(name, e->object,buf,sizeof(buf));
2424 + gf_puts(buf,pc);
2425 + gf_puts(NEWLINE,pc);
2430 + /*
2431 + Output a string in a distinctive style
2432 + */
2433 + static void gf_puts_uline(const char *txt,gf_io_t pc)
2435 + #if 0
2436 + pc(TAG_EMBED); pc(TAG_ULINEON);
2437 + gf_puts(txt,pc);
2438 + pc(TAG_EMBED); pc(TAG_ULINEOFF);
2439 + #else
2440 + pc(TAG_EMBED); pc(TAG_BOLDON);
2441 + gf_puts(txt,pc);
2442 + pc(TAG_EMBED); pc(TAG_BOLDOFF);
2443 + #endif
2446 + /*
2447 + Get a line from the given store (including \n)
2448 + */
2449 + static int so_gets(STORE_S *store,char *buf,int len)
2451 + unsigned char c;
2452 + char *bend = buf + len - 1;
2453 + char *b = buf;
2455 + do {
2456 + if (!store->readc(&c,store)) {
2457 + *b = 0;
2458 + return b!=buf;
2460 + *b++ = c;
2461 + } while (c!='\n' && b<bend);
2463 + *b = 0;
2465 + return 1;
2468 + /*
2469 + Wrap the text in the given store to the given width.
2470 + A new store is created for the result.
2471 + */
2472 + static STORE_S *wrap_store(STORE_S *in,int width)
2474 + STORE_S *result;
2475 + void *ws;
2476 + gf_io_t ipc,opc;
2478 + if (width<10)
2479 + width = 10;
2481 + result = so_get(CharStar,NULL,EDIT_ACCESS);
2482 + ws = gf_wrap_filter_opt(width,width,0,0);
2484 + gf_filter_init();
2485 + gf_link_filter(gf_wrap,ws);
2487 + gf_set_so_writec(&opc,result);
2488 + gf_set_so_readc(&ipc,in);
2490 + gf_pipe(ipc,opc);
2492 + gf_clear_so_readc(in);
2493 + gf_clear_so_writec(result);
2495 + return result;
2498 + /*
2499 + Output the contents of the given stores (left and right)
2500 + to the given gf_io_t.
2501 + The width of the terminal is inspected and two columns
2502 + are created to fit the stores into. They are then wrapped
2503 + and merged.
2504 + */
2505 + static void side_by_side(STORE_S *left,STORE_S *right,gf_io_t pc)
2507 + STORE_S *left_wrapped;
2508 + STORE_S *right_wrapped;
2510 + char buf_l[256];
2511 + char buf_r[256];
2512 + char *b;
2513 + int i;
2514 + int w = ps_global->ttyo->screen_cols/2 - 1;
2516 + so_seek(left,0,0);
2517 + so_seek(right,0,0);
2519 + left_wrapped = wrap_store(left,w);
2520 + right_wrapped = wrap_store(right,w);
2522 + so_seek(left_wrapped,0,0);
2523 + so_seek(right_wrapped,0,0);
2525 + for (;;) {
2527 + i = so_gets(left_wrapped,buf_l,sizeof(buf_l));
2528 + i += so_gets(right_wrapped,buf_r,sizeof(buf_r));
2530 + if (i==0)
2531 + break;
2533 + for (i=0, b=buf_l;i<w && *b && *b!='\r' && *b!='\n';i++,b++) {
2534 + pc(*b);
2535 + /* reduce accumulated width if an embed tag is discovered */
2536 + if (*b==TAG_EMBED)
2537 + i-=2;
2540 + if (buf_r[0]) {
2542 + while (i<w) {
2543 + pc(' ');
2544 + i++;
2547 + for (i=0, b=buf_r;i<w && *b && *b!='\r' && *b!='\n';i++,b++) {
2548 + pc(*b);
2552 + gf_puts(NEWLINE,pc);
2555 + so_give(&left_wrapped);
2556 + so_give(&right_wrapped);
2559 + static void print_separator_line(int percent,int ch,gf_io_t pc)
2561 + int i;
2562 + int start,len;
2564 + len = ps_global->ttyo->screen_cols * percent / 100;
2565 + start = (ps_global->ttyo->screen_cols - len)/2;
2567 + for (i=0;i<start;i++)
2568 + pc(' ');
2569 + for (i=start;i<start+len;i++)
2570 + pc(ch);
2571 + gf_puts(NEWLINE,pc);
2574 + static void output_cert_info(X509 *cert,gf_io_t pc)
2576 + char buf[256];
2577 + STORE_S *left,*right;
2578 + gf_io_t spc;
2579 + int i;
2581 + left = so_get(CharStar,NULL,EDIT_ACCESS);
2582 + right = so_get(CharStar,NULL,EDIT_ACCESS);
2584 + gf_set_so_writec(&spc,left);
2586 + if (!cert->cert_info) {
2587 + gf_puts("Couldn't find certificate info.",spc);
2588 + gf_puts(NEWLINE,spc);
2589 + } else {
2591 + gf_puts_uline("Subject (whose certificate it is)",spc);
2592 + gf_puts(NEWLINE, spc);
2594 + output_X509_NAME(cert->cert_info->subject,spc);
2595 + gf_puts(NEWLINE,spc);
2597 + gf_puts_uline("Serial Number",spc);
2598 + gf_puts(NEWLINE,spc);
2600 + sprintf(buf,"%d",ASN1_INTEGER_get(cert->cert_info->serialNumber));
2601 + gf_puts(buf,spc);
2602 + gf_puts(NEWLINE,spc);
2603 + gf_puts(NEWLINE,spc);
2605 + gf_puts_uline("Validity",spc);
2606 + gf_puts(NEWLINE,spc);
2608 + BIO *mb = BIO_new_so(left);
2610 + gf_puts("Not Before: ",spc);
2611 + ASN1_UTCTIME_print(mb,cert->cert_info->validity->notBefore);
2612 + BIO_flush(mb);
2613 + gf_puts(NEWLINE,spc);
2615 + gf_puts("Not After: ",spc);
2616 + ASN1_UTCTIME_print(mb,cert->cert_info->validity->notAfter);
2617 + BIO_flush(mb);
2619 + gf_puts(NEWLINE,spc);
2620 + gf_puts(NEWLINE,spc);
2622 + BIO_free(mb);
2627 + gf_clear_so_writec(left);
2629 + gf_set_so_writec(&spc,right);
2631 + if (!cert->cert_info) {
2632 + gf_puts("Couldn't find certificate info.",spc);
2633 + gf_puts(NEWLINE,spc);
2634 + } else {
2635 + gf_puts_uline("Issuer",spc);
2636 + gf_puts(NEWLINE, spc);
2638 + output_X509_NAME(cert->cert_info->issuer,spc);
2639 + gf_puts(NEWLINE,spc);
2642 + gf_clear_so_writec(right);
2644 + side_by_side(left,right,pc);
2646 + gf_puts_uline("SHA1 Fingerprint",pc);
2647 + gf_puts(NEWLINE,pc);
2648 + get_fingerprint(cert,EVP_sha1(),buf,sizeof(buf));
2649 + gf_puts(buf,pc);
2650 + gf_puts(NEWLINE,pc);
2652 + gf_puts_uline("MD5 Fingerprint",pc);
2653 + gf_puts(NEWLINE,pc);
2654 + get_fingerprint(cert,EVP_md5(),buf,sizeof(buf));
2655 + gf_puts(buf,pc);
2656 + gf_puts(NEWLINE,pc);
2658 + so_give(&left);
2659 + so_give(&right);
2662 + void format_smime_info(int pass,BODY *body,int msgno,gf_io_t pc)
2664 + PKCS7 *p7;
2665 + int i;
2667 + if (body->type==TYPEMULTIPART) {
2668 + PART *p;
2670 + for (p=body->nested.part;p;p=p->next) {
2671 + format_smime_info(pass,&p->body,msgno,pc);
2675 + p7 = body->sparep;
2676 + if (p7) {
2678 + if (PKCS7_type_is_signed(p7)) {
2679 + STACK_OF(X509) *signers;
2681 + switch (pass) {
2682 + case 1:
2683 + gf_puts("This message was cryptographically signed." NEWLINE,pc);
2684 + break;
2685 + case 2:
2687 + signers = PKCS7_get0_signers(p7, NULL, 0);
2689 + if (signers) {
2691 + sprintf(tmp_20k_buf,"Certificate%s used for signing",plural(sk_X509_num(signers)));
2692 + gf_puts_uline(tmp_20k_buf,pc);
2693 + gf_puts(NEWLINE,pc);
2694 + print_separator_line(100,'-',pc);
2696 + for (i=0;i<sk_X509_num(signers);i++) {
2697 + X509 *x = sk_X509_value(signers,i);
2699 + if (x) {
2700 + output_cert_info(x,pc);
2701 + gf_puts(NEWLINE,pc);
2707 + sk_X509_free(signers);
2708 + break;
2711 + } else if (PKCS7_type_is_enveloped(p7)) {
2713 + switch (pass) {
2714 + case 1:
2715 + gf_puts("This message was encrypted." NEWLINE,pc);
2716 + break;
2717 + case 2:
2719 + if (p7->d.enveloped && p7->d.enveloped->enc_data) {
2720 + X509_ALGOR *alg = p7->d.enveloped->enc_data->algorithm;
2721 + STACK_OF(PKCS7_RECIP_INFO) *ris = p7->d.enveloped->recipientinfo;
2722 + int found = 0;
2724 + gf_puts("The algorithm used to encrypt was ",pc);
2727 + if (alg) {
2728 + char *n = OBJ_nid2sn( OBJ_obj2nid(alg->algorithm ));
2730 + gf_puts(n ? n : "<unknown>",pc);
2732 + } else gf_puts("<unknown>",pc);
2734 + gf_puts("." NEWLINE NEWLINE,pc);
2736 + sprintf(tmp_20k_buf,"Certificate%s for decrypting",plural(sk_PKCS7_RECIP_INFO_num(ris)));
2737 + gf_puts_uline(tmp_20k_buf,pc);
2738 + gf_puts(NEWLINE,pc);
2739 + print_separator_line(100,'-',pc);
2741 + for (i=0;i<sk_PKCS7_RECIP_INFO_num(ris);i++) {
2742 + PKCS7_RECIP_INFO *ri;
2743 + PERSONAL_CERT *pcert;
2745 + ri = sk_PKCS7_RECIP_INFO_value(ris,i);
2746 + if (!ri) continue;
2748 + pcert = find_certificate_matching_recip_info(ri);
2750 + if (pcert) {
2752 + if (found) {
2753 + print_separator_line(25,'*',pc);
2754 + gf_puts(NEWLINE,pc);
2756 + found = 1;
2758 + output_cert_info(pcert->cert,pc);
2759 + gf_puts(NEWLINE,pc);
2764 + if (!found) {
2765 + gf_puts("No certificate capable of decrypting could not be found.",pc);
2768 + break;
2775 + void view_writec();
2777 + void smime_info_screen(struct pine *ps)
2779 + int msgno;
2780 + OtherMenu what;
2781 + int cmd;
2782 + char backtag[64];
2783 + BODY *body;
2784 + ENVELOPE *env;
2785 + HANDLE_S *handles = NULL;
2786 + SCROLL_S scrollargs;
2787 + STORE_S *store = NULL;
2788 + int offset = 0;
2790 + ps->prev_screen = smime_info_screen;
2791 + ps->next_screen = SCREEN_FUN_NULL;
2793 + if(mn_total_cur(ps->msgmap) > 1L){
2794 + q_status_message(SM_ORDER | SM_DING, 0, 3,
2795 + "Can only view one message's information at a time.");
2796 + return;
2798 + /* else check for existence of smime bits */
2800 + msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
2802 + env = mail_fetch_structure (ps->mail_stream,msgno,
2803 + &body,0);
2804 + if (!env || !body) {
2805 + q_status_message(SM_ORDER, 0, 3,
2806 + "Can't fetch body of message.");
2807 + return;
2810 + what = FirstMenu;
2812 + store = so_get(CharStar, NULL, EDIT_ACCESS);
2814 + while(ps->next_screen == SCREEN_FUN_NULL){
2816 + ClearLine(1);
2818 + so_truncate(store,0);
2820 + view_writec_init(store, &handles, HEADER_ROWS(ps),
2821 + HEADER_ROWS(ps) +
2822 + ps->ttyo->screen_rows - (HEADER_ROWS(ps)
2823 + + HEADER_ROWS(ps)));
2825 + gf_puts_uline("Overview",view_writec);
2826 + gf_puts(NEWLINE,view_writec);
2828 + format_smime_info(1,body,msgno,view_writec);
2829 + gf_puts(NEWLINE,view_writec);
2830 + format_smime_info(2,body,msgno,view_writec);
2832 + view_writec_destroy();
2835 + ps->next_screen = SCREEN_FUN_NULL;
2837 + memset(&scrollargs, 0, sizeof(SCROLL_S));
2838 + scrollargs.text.text = so_text(store);
2839 + scrollargs.text.src = CharStar;
2840 + scrollargs.text.desc = "S/MIME information";
2841 + scrollargs.body_valid = 1;
2843 + if(offset){ /* resize? preserve paging! */
2844 + scrollargs.start.on = Offset;
2845 + scrollargs.start.loc.offset = offset;
2846 + offset = 0L;
2849 + scrollargs.bar.title = "S/MIME INFORMATION";
2850 + /* scrollargs.end_scroll = view_end_scroll; */
2851 + scrollargs.resize_exit = 1;
2852 + scrollargs.help.text = NULL;
2853 + scrollargs.help.title = "HELP FOR S/MIME INFORMATION VIEW";
2854 + scrollargs.keys.menu = &smime_info_keymenu;
2855 + scrollargs.keys.what = what;
2856 + setbitmap(scrollargs.keys.bitmap);
2858 + if(scrolltool(&scrollargs) == MC_RESIZE)
2859 + offset = scrollargs.start.loc.offset;
2862 + so_give(&store);
2867 + #endif /* SMIME */
2868 diff -Ncr pine4.53/pine/smime.h pine4.53-smime/pine/smime.h
2869 *** pine4.53/pine/smime.h Wed Dec 31 16:00:00 1969
2870 --- pine4.53-smime/pine/smime.h Wed Jan 15 12:48:15 2003
2871 ***************
2872 *** 0 ****
2873 --- 1,17 ----
2874 + #ifdef SMIME
2876 + int is_pkcs7_body(BODY *b);
2878 + int fiddle_smime_message(BODY *b,int msgno,int is_new_message);
2880 + int encrypt_outgoing_message(METAENV *header,BODY **bodyP);
2881 + int sign_outgoing_message(METAENV *header,BODY **bodyP,int dont_detach);
2882 + int get_passphrase(void);
2883 + void smime_info_screen(struct pine *ps);
2885 + extern int g_need_passphrase;
2887 + extern int g_do_encrypt;
2888 + extern int g_do_sign;
2890 + #endif /* SMIME */
2891 diff -Ncr pine4.53/pine/smkeys.c pine4.53-smime/pine/smkeys.c
2892 *** pine4.53/pine/smkeys.c Wed Dec 31 16:00:00 1969
2893 --- pine4.53-smime/pine/smkeys.c Wed Jan 15 12:48:15 2003
2894 ***************
2895 *** 0 ****
2896 --- 1,343 ----
2897 + #ifdef SMIME
2899 + #include "headers.h"
2901 + #include <openssl/err.h>
2902 + #include <openssl/objects.h>
2903 + #include <openssl/evp.h>
2904 + #include <openssl/x509.h>
2905 + #include <openssl/pkcs7.h>
2906 + #include <openssl/pem.h>
2908 + #include "smkeys.h"
2910 + /*---------------------------------------------------
2911 + Remove leading whitespace, trailing whitespace and convert
2912 + to lowercase. Also remove slash characters
2914 + Args: s, -- The string to clean
2916 + Result: the cleaned string
2917 + ----*/
2918 + static char *
2919 + emailstrclean(string)
2920 + char *string;
2922 + char *s = string, *sc = NULL, *p = NULL;
2924 + for(; *s; s++){ /* single pass */
2925 + if(!isspace((unsigned char)*s)){
2926 + p = NULL; /* not start of blanks */
2927 + if(!sc) /* first non-blank? */
2928 + sc = string; /* start copying */
2930 + else if(!p) /* it's OK if sc == NULL */
2931 + p = sc; /* start of blanks? */
2933 + if(sc && *s!='/' && *s!='\\') /* if copying, copy */
2934 + *sc++ = isupper((unsigned char)(*s))
2935 + ? (unsigned char)tolower((unsigned char)(*s))
2936 + : (unsigned char)(*s);
2939 + if(p) /* if ending blanks */
2940 + *p = '\0'; /* tie off beginning */
2941 + else if(!sc) /* never saw a non-blank */
2942 + *string = '\0'; /* so tie whole thing off */
2944 + return(string);
2947 + /*
2948 + Add a lookup for each "*.crt" file in the given directory.
2949 + */
2950 + static void add_certs_in_dir(X509_LOOKUP *lookup,const char *path)
2952 + char buf[MAXPATH];
2953 + struct direct *d;
2954 + DIR *dirp;
2955 + PERSONAL_CERT *result;
2957 + result = NULL;
2959 + dirp = opendir(path);
2960 + if (dirp) {
2962 + while (d=readdir(dirp)) {
2963 + BIO *in;
2964 + X509 *cert;
2966 + if (srchrstr(d->d_name,".crt")) {
2968 + build_path(buf,(char*) path,d->d_name,sizeof(buf));
2970 + X509_LOOKUP_load_file(lookup,buf,X509_FILETYPE_PEM);
2975 + closedir(dirp);
2979 + /* Get an X509_STORE. This consists of the system
2980 + certs directory and any certificates in the user's
2981 + ~/.pine-smime/ca directory.
2982 + */
2983 + X509_STORE *get_ca_store(const char *path)
2985 + X509_LOOKUP *lookup;
2986 + char buf[MAXPATH];
2988 + X509_STORE *store;
2990 + store=X509_STORE_new();
2992 + lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
2994 + build_path(buf,(char*) path,"ca",sizeof(buf));
2996 + add_certs_in_dir(lookup,buf);
2998 + /* X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT); */
3000 + lookup = X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
3002 + X509_LOOKUP_add_dir(lookup,SSL_CERT_DIRECTORY,X509_FILETYPE_PEM);
3004 + /* X509_STORE_set_default_paths(cert_store);
3005 + X509_STORE_load_locations(cert_store,NULL,"../../certs");
3006 + X509_STORE_set_verify_cb_func(cert_store,verify_callback);
3007 + */
3009 + return store;
3012 + static EVP_PKEY *load_key(char *file, char *pass)
3014 + BIO *in;
3015 + EVP_PKEY *key;
3016 + if(!(in = BIO_new_file(file, "r"))) return NULL;
3017 + key = PEM_read_bio_PrivateKey(in, NULL,NULL,pass);
3018 + BIO_free(in);
3019 + return key;
3022 + char *get_x509_name_entry(const char *key,X509_NAME *name)
3024 + int i,c,n;
3025 + char buf[256];
3026 + char *id;
3028 + if (!name)
3029 + return NULL;
3031 + c = X509_NAME_entry_count(name);
3033 + for (i=0;i<c;i++) {
3034 + X509_NAME_ENTRY *e;
3036 + e = X509_NAME_get_entry(name,i);
3037 + if (!e) continue;
3039 + buf[0] = 0;
3040 + id = buf;
3042 + n = OBJ_obj2nid(e->object);
3043 + if ((n == NID_undef) || ((id=(char*) OBJ_nid2sn(n)) == NULL)) {
3044 + i2t_ASN1_OBJECT(buf,sizeof(buf),e->object);
3045 + id = buf;
3048 + if ((strucmp(id,"email")==0) || (strucmp(id,"emailAddress")==0)) {
3049 + X509_NAME_get_text_by_OBJ(name, e->object,(char*) buf,sizeof(buf)-1);
3050 + return cpystr(buf);
3053 + return NULL;
3056 + char *get_x509_subject_email(X509 *x)
3058 + char* result;
3059 + result = get_x509_name_entry("email",X509_get_subject_name(x));
3060 + if ( !result ) {
3061 + result = get_x509_name_entry("emailAddress",X509_get_subject_name(x));
3064 + return result;
3067 + /* Save the certificate for the given email address in
3068 + ~/.pine-smime/public.
3070 + Should consider the security hazards in making a file with
3071 + the email address that has come from the certificate.
3073 + The argument email is destroyed.
3074 + */
3076 + void save_cert_for(const char *path,char *email,X509 *cert)
3078 + char sf[MAXPATH];
3079 + char sf2[MAXPATH];
3080 + BIO *tmp;
3082 + build_path(sf,(char*) path,"public",sizeof(sf));
3084 + build_path(sf2,sf,emailstrclean(email),sizeof(sf2));
3085 + strncat(sf2,".crt",sizeof(sf2)-1-strlen(sf2));
3086 + sf2[sizeof(sf2)-1] = 0;
3088 + tmp = BIO_new_file(sf2, "w");
3089 + if (tmp) {
3090 + X509_print(tmp,cert);
3091 + PEM_write_bio_X509_AUX(tmp, cert);
3092 + BIO_free(tmp);
3093 + q_status_message1(SM_ORDER,1,1,"Saved certificate for <%s>",(char*)email);
3094 + } else {
3095 + q_status_message1(SM_ORDER,1,1,"Couldn't save certificate for <%s>",(char*)email);
3099 + /*
3100 + Try to retrieve the certificate for the given email address.
3101 + */
3102 + X509 *get_cert_for(const char *path,const char *email)
3104 + char buf[MAXPATH];
3105 + char buf2[MAXPATH];
3106 + char buf3[MAXPATH];
3107 + char *p;
3108 + X509 *cert = NULL;
3109 + BIO *in;
3111 + strncpy(buf3,email,sizeof(buf3)-1);
3112 + buf3[sizeof(buf3)-1] = 0;
3114 + /* clean it up (lowercase, space removal) */
3115 + emailstrclean(buf3);
3117 + build_path(buf,(char*)path,"public",sizeof(buf));
3118 + build_path(buf2,buf,buf3,sizeof(buf2));
3119 + strncat(buf2,".crt",sizeof(buf2)-1-strlen(buf2));
3120 + buf2[sizeof(buf2)-1] = 0;
3122 + if((in = BIO_new_file(buf2, "r"))!=0) {
3124 + cert = PEM_read_bio_X509(in, NULL, NULL,NULL);
3126 + if (cert) {
3127 + /* could check email addr in cert matches */
3130 + BIO_free(in);
3132 + return cert;
3135 + EVP_PKEY *get_key_for(const char *path,const char *email,const char *pass)
3137 + char buf[MAXPATH];
3138 + char buf2[MAXPATH];
3139 + char buf3[MAXPATH];
3140 + char *p;
3141 + EVP_PKEY *key = NULL;
3142 + BIO *in;
3144 + strncpy(buf3,email,sizeof(buf3)-1);
3145 + buf3[sizeof(buf3)-1] = 0;
3147 + /* clean it up (lowercase, space removal) */
3148 + emailstrclean(buf3);
3150 + build_path(buf,(char*)path,"private",sizeof(buf));
3151 + build_path(buf2,buf,buf3,sizeof(buf2));
3152 + strncat(buf2,".key",sizeof(buf2)-1-strlen(buf2));
3153 + buf2[sizeof(buf2)-1] = 0;
3155 + key = load_key(buf2,pass);
3157 + return key;
3160 + /*
3161 + Load the user's personal certificates from
3162 + ~/.pine-smime/private
3163 + */
3164 + PERSONAL_CERT *get_personal_certs(const char *path)
3166 + char buf[MAXPATH];
3167 + char buf2[MAXPATH];
3168 + struct direct *d;
3169 + DIR *dirp;
3170 + PERSONAL_CERT *result;
3172 + result = NULL;
3174 + build_path(buf,(char*) path,"private",sizeof(buf));
3176 + dirp = opendir(buf);
3177 + if (dirp) {
3179 + while (d=readdir(dirp)) {
3180 + BIO *in;
3181 + X509 *cert;
3183 + if (srchrstr(d->d_name,".key")) {
3185 + /* copy file name to temp buffer */
3186 + strcpy(buf2,d->d_name);
3187 + /* chop off ".key" trailier */
3188 + buf2[strlen(buf2)-4] = 0;
3189 + /* Look for certificate */
3190 + cert = get_cert_for(path,buf2);
3192 + if (cert) {
3193 + PERSONAL_CERT *pc;
3195 + /* create a new PERSONAL_CERT, fill it in */
3197 + pc = fs_get(sizeof(PERSONAL_CERT));
3198 + pc->cert = cert;
3199 + build_path(buf2,buf,d->d_name,sizeof(buf2));
3200 + pc->file = cpystr(buf2);
3202 + strcpy(pc->file + strlen(pc->file) - 4, ".key");
3204 + /* Try to load the key with an empty password */
3205 + pc->key = load_key(pc->file,"");
3207 + pc->next = result;
3208 + result = pc;
3214 + closedir(dirp);
3217 + return result;
3220 + void personal_cert_free(PERSONAL_CERT **pcp)
3222 + if (pcp && *pcp) {
3224 + PERSONAL_CERT *pc = *pcp;
3226 + fs_give((void**) &pc->file);
3228 + X509_free(pc->cert);
3230 + if (pc->key)
3231 + EVP_PKEY_free(pc->key);
3233 + personal_cert_free(&pc->next);
3235 + fs_give((void**) pcp);
3239 + #endif /* SMIME */
3240 diff -Ncr pine4.53/pine/smkeys.h pine4.53-smime/pine/smkeys.h
3241 *** pine4.53/pine/smkeys.h Wed Dec 31 16:00:00 1969
3242 --- pine4.53-smime/pine/smkeys.h Wed Jan 15 12:48:15 2003
3243 ***************
3244 *** 0 ****
3245 --- 1,20 ----
3247 + #define PERSONAL_CERT struct personal_cert
3249 + PERSONAL_CERT {
3250 + X509 *cert;
3251 + EVP_PKEY *key;
3252 + char *file;
3253 + PERSONAL_CERT *next;
3254 + };
3256 + X509_STORE *get_ca_store(const char *d);
3258 + PERSONAL_CERT *get_personal_certs(const char *d);
3260 + X509 *get_cert_for(const char *path,const char *email);
3261 + void save_cert_for(const char *path,char *email,X509 *cert);
3263 + void personal_cert_free(PERSONAL_CERT **pc);
3265 + char *get_x509_subject_email(X509 *x);