* New token SHORTSUBJECT, SHORTSUBJKEY and SHORTSUBJKEYINIT to shorten
[alpine.git] / alpine / smime.c
blob182779beb43ac92ac27a8b8911eb4aa7547c82d4
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: smime.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2016 Eduardo Chappa
8 * Copyright 2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * This is based on a contribution from Jonathan Paisley
22 * File: smime.c
23 * Author: paisleyj@dcs.gla.ac.uk
24 * Date: 01/2001
28 #include "headers.h"
30 #ifdef SMIME
32 #include "../pith/charconv/utf8.h"
33 #include "../pith/status.h"
34 #include "../pith/store.h"
35 #include "../pith/conf.h"
36 #include "../pith/list.h"
37 #include "../pith/mailcmd.h"
38 #include "../pith/tempfile.h"
39 #include "radio.h"
40 #include "keymenu.h"
41 #include "mailview.h"
42 #include "conftype.h"
43 #include "confscroll.h"
44 #include "setup.h"
45 #include "smime.h"
47 /* internal prototypes */
48 void format_smime_info(int pass, BODY *body, long msgno, gf_io_t pc);
49 void print_separator_line(int percent, int ch, gf_io_t pc);
50 void output_cert_info(X509 *cert, gf_io_t pc);
51 void output_X509_NAME(X509_NAME *name, gf_io_t pc);
52 void side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc);
53 STORE_S *wrap_store(STORE_S *in, int width);
54 void smime_config_init_display(struct pine *, CONF_S **, CONF_S **);
55 void revert_to_saved_smime_config(struct pine *ps, SAVED_CONFIG_S *vsave);
56 SAVED_CONFIG_S *save_smime_config_vars(struct pine *ps);
57 void free_saved_smime_config(struct pine *ps, SAVED_CONFIG_S **vsavep);
58 int smime_helper_tool(struct pine *, int, CONF_S **, unsigned);
59 void manage_certificates(struct pine *, WhichCerts);
60 #ifdef PASSFILE
61 void manage_password_file_certificates(struct pine *);
62 #endif /* PASSFILE */
63 void smime_manage_certs_init (struct pine *, CONF_S **, CONF_S **, WhichCerts, int);
64 void smime_manage_password_file_certs_init(struct pine *, CONF_S **, CONF_S **, int, int *);
65 void display_certificate_information(struct pine *, X509 *, char *, WhichCerts, int num);
66 int manage_certs_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags);
67 int manage_certificate_info_tool(int, MSGNO_S *, SCROLL_S *);
68 void smime_setup_size(char **, size_t, size_t);
72 * prompt the user for their passphrase
73 * (possibly prompting with the email address in s_passphrase_emailaddr)
75 int
76 smime_get_passphrase(void)
78 int rc;
79 int flags;
80 char prompt[500];
81 HelpType help = NO_HELP;
83 assert(ps_global->smime != NULL);
84 snprintf(prompt, sizeof(prompt),
85 _("Enter passphrase for <%s>: "), (ps_global->smime && ps_global->smime->passphrase_emailaddr) ? ps_global->smime->passphrase_emailaddr[0] : "unknown");
87 do {
88 flags = OE_PASSWD | OE_DISALLOW_HELP;
89 ((char *) ps_global->smime->passphrase)[0] = '\0';
90 rc = optionally_enter((char *) ps_global->smime->passphrase,
91 -FOOTER_ROWS(ps_global), 0,
92 sizeof(ps_global->smime->passphrase),
93 prompt, NULL, help, &flags);
94 } while (rc!=0 && rc!=1 && rc>0);
96 if(rc==0){
97 if(ps_global->smime)
98 ps_global->smime->entered_passphrase = 1;
101 return rc; /* better return rc and make the caller check its return value */
105 smime_check(BODY *body)
107 int rv = 0;
108 PKCS7 *p7 = NULL;
110 if(body->type == TYPEMULTIPART){
111 PART *p;
113 for(p=body->nested.part; p && rv == 0; p=p->next)
114 rv += smime_check(&p->body);
116 if(rv > 0) return rv;
117 if(body->sparep)
118 p7 = get_smime_sparep_type(body->sparep) == P7Type
119 ? (PKCS7 *)get_smime_sparep_data(body->sparep)
120 : NULL;
121 if(p7 && (PKCS7_type_is_signed(p7) || PKCS7_type_is_enveloped(p7)))
122 rv += 1;
123 return rv;
127 void
128 display_smime_info(struct pine *ps, ENVELOPE *env, BODY *body)
130 OtherMenu what = FirstMenu;
131 HANDLE_S *handles = NULL;
132 SCROLL_S scrollargs;
133 STORE_S *store = NULL;
134 long msgno;
135 int offset = 0;
137 msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
138 store = so_get(CharStar, NULL, EDIT_ACCESS);
140 while(ps->next_screen == SCREEN_FUN_NULL){
142 ClearLine(1);
144 so_truncate(store, 0);
146 view_writec_init(store, &handles, HEADER_ROWS(ps),
147 HEADER_ROWS(ps) +
148 ps->ttyo->screen_rows - (HEADER_ROWS(ps)
149 + HEADER_ROWS(ps)));
151 gf_puts_uline("Overview", view_writec);
152 gf_puts(NEWLINE, view_writec);
154 format_smime_info(1, body, msgno, view_writec);
155 gf_puts(NEWLINE, view_writec);
156 format_smime_info(2, body, msgno, view_writec);
158 view_writec_destroy();
160 ps->next_screen = SCREEN_FUN_NULL;
162 memset(&scrollargs, 0, sizeof(SCROLL_S));
163 scrollargs.text.text = so_text(store);
164 scrollargs.text.src = CharStar;
165 scrollargs.text.desc = "S/MIME information";
166 scrollargs.body_valid = 1;
168 if(offset){ /* resize? preserve paging! */
169 scrollargs.start.on = Offset;
170 scrollargs.start.loc.offset = offset;
171 offset = 0L;
174 scrollargs.bar.title = "S/MIME INFORMATION";
175 /* scrollargs.end_scroll = view_end_scroll; */
176 scrollargs.resize_exit = 1;
177 scrollargs.help.text = NULL;
178 scrollargs.help.title = "HELP FOR S/MIME INFORMATION VIEW";
179 scrollargs.keys.menu = &smime_info_keymenu;
180 scrollargs.keys.what = what;
181 setbitmap(scrollargs.keys.bitmap);
183 if(scrolltool(&scrollargs) == MC_RESIZE)
184 offset = scrollargs.start.loc.offset;
187 so_give(&store);
190 void
191 smime_info_screen(struct pine *ps)
193 long msgno;
194 BODY *body;
195 ENVELOPE *env;
197 /* ps->prev_screen = smime_info_screen;
198 ps->next_screen = SCREEN_FUN_NULL; */
200 msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
202 env = mail_fetch_structure(ps->mail_stream, msgno, &body, 0);
204 if(!env || !body){
205 q_status_message(SM_ORDER, 0, 3,
206 _("Can't fetch body of message."));
207 return;
210 if(smime_check(body) == 0){
211 q_status_message(SM_ORDER | SM_DING, 0, 3,
212 _("Not a signed or encrypted message"));
213 return;
216 if(mn_total_cur(ps->msgmap) > 1L){
217 q_status_message(SM_ORDER | SM_DING, 0, 3,
218 _("Can only view one message's information at a time."));
219 return;
222 display_smime_info(ps, env, body);
226 void
227 format_smime_info(int pass, BODY *body, long msgno, gf_io_t pc)
229 PKCS7 *p7 = NULL;
230 int i;
232 if(body->type == TYPEMULTIPART){
233 PART *p;
235 for(p=body->nested.part; p; p=p->next)
236 format_smime_info(pass, &p->body, msgno, pc);
238 if(body->sparep)
239 p7 = get_smime_sparep_type(body->sparep) == P7Type
240 ? (PKCS7 *)get_smime_sparep_data(body->sparep)
241 : NULL;
242 if(p7){
244 if(PKCS7_type_is_signed(p7)){
245 STACK_OF(X509) *signers;
247 switch(pass){
248 case 1:
249 gf_puts(_("This message was cryptographically signed."), pc);
250 gf_puts(NEWLINE, pc);
251 break;
253 case 2:
254 signers = PKCS7_get0_signers(p7, NULL, 0);
256 if(signers){
258 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Certificate%s used for signing"),
259 plural(sk_X509_num(signers)));
260 gf_puts_uline(tmp_20k_buf, pc);
261 gf_puts(NEWLINE, pc);
262 print_separator_line(100, '-', pc);
264 for(i=0; i<sk_X509_num(signers); i++){
265 X509 *x = sk_X509_value(signers, i);
267 if(x){
268 output_cert_info(x, pc);
269 gf_puts(NEWLINE, pc);
274 sk_X509_free(signers);
275 break;
279 else if(PKCS7_type_is_enveloped(p7)){
281 switch(pass){
282 case 1:
283 gf_puts(_("This message was encrypted."), pc);
284 gf_puts(NEWLINE, pc);
285 break;
287 case 2:
288 if(p7->d.enveloped && p7->d.enveloped->enc_data){
289 X509_ALGOR *alg = p7->d.enveloped->enc_data->algorithm;
290 STACK_OF(PKCS7_RECIP_INFO) *ris = p7->d.enveloped->recipientinfo;
291 int found = 0;
293 gf_puts(_("The algorithm used to encrypt was "), pc);
295 if(alg){
296 char *n = (char *) OBJ_nid2sn( OBJ_obj2nid(alg->algorithm));
298 gf_puts(n ? n : "<unknown>", pc);
301 else
302 gf_puts("<unknown>", pc);
304 gf_puts("." NEWLINE NEWLINE, pc);
306 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Certificate%s for decrypting"),
307 plural(sk_PKCS7_RECIP_INFO_num(ris)));
308 gf_puts_uline(tmp_20k_buf, pc);
309 gf_puts(NEWLINE, pc);
310 print_separator_line(100, '-', pc);
312 for(i=0; i<sk_PKCS7_RECIP_INFO_num(ris); i++){
313 PKCS7_RECIP_INFO *ri;
314 PERSONAL_CERT *pcert;
316 ri = sk_PKCS7_RECIP_INFO_value(ris, i);
317 if(!ri)
318 continue;
320 pcert = find_certificate_matching_recip_info(ri);
322 if(pcert){
323 if(found){
324 print_separator_line(25, '*', pc);
325 gf_puts(NEWLINE, pc);
328 found = 1;
330 output_cert_info(pcert->cert, pc);
331 gf_puts(NEWLINE, pc);
336 if(!found){
337 gf_puts(_("No certificate capable of decrypting could be found."), pc);
338 gf_puts(NEWLINE, pc);
339 gf_puts(NEWLINE, pc);
343 break;
350 void
351 print_separator_line(int percent, int ch, gf_io_t pc)
353 int i, start, len;
355 len = ps_global->ttyo->screen_cols * percent / 100;
356 start = (ps_global->ttyo->screen_cols - len)/2;
358 for(i=0; i<start; i++)
359 pc(' ');
361 for(i=start; i<start+len; i++)
362 pc(ch);
364 gf_puts(NEWLINE, pc);
368 void
369 output_cert_info(X509 *cert, gf_io_t pc)
371 char buf[256];
372 STORE_S *left,*right;
373 gf_io_t spc;
374 int len, error;
375 STACK_OF(X509) *chain;
377 left = so_get(CharStar, NULL, EDIT_ACCESS);
378 right = so_get(CharStar, NULL, EDIT_ACCESS);
379 if(!(left && right))
380 return;
382 gf_set_so_writec(&spc, left);
384 gf_puts_uline("Certificate Owner", spc);
385 gf_puts(NEWLINE, spc);
387 output_X509_NAME(X509_get_subject_name(cert), spc);
388 gf_puts(NEWLINE, spc);
390 gf_puts_uline("Serial Number", spc);
391 gf_puts(NEWLINE, spc);
393 { ASN1_INTEGER *bs;
394 long l;
395 const char *neg;
396 int i;
398 bs = X509_get_serialNumber(cert);
399 if (bs->length <= (int)sizeof(long)){
400 l = ASN1_INTEGER_get(bs);
401 if (bs->type == V_ASN1_NEG_INTEGER){
402 l = -l;
403 neg="-";
405 else
406 neg="";
407 snprintf(buf, sizeof(buf), " %s%lu (%s0x%lx)", neg, l, neg, l);
408 } else {
409 snprintf(buf, sizeof(buf), "%s", bs->type == V_ASN1_NEG_INTEGER ? "(Negative)" : "");
410 for (i = 0; i < bs->length; i++)
411 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%02x%s", bs->data[i],
412 i+1 == bs->length ? "" : ":");
415 gf_puts(buf, spc);
416 gf_puts(NEWLINE, spc);
417 gf_puts(NEWLINE, spc);
419 gf_puts_uline("Validity", spc);
420 gf_puts(NEWLINE, spc);
421 { BIO *mb = BIO_new(BIO_s_mem());
422 char iobuf[4096];
424 gf_puts("Not Before: ", spc);
426 (void) BIO_reset(mb);
427 ASN1_UTCTIME_print(mb, X509_get0_notBefore(cert));
428 (void) BIO_flush(mb);
429 while((len = BIO_read(mb, iobuf, sizeof(iobuf))) > 0)
430 gf_nputs(iobuf, len, spc);
432 gf_puts(NEWLINE, spc);
434 gf_puts("Not After: ", spc);
436 (void) BIO_reset(mb);
437 ASN1_UTCTIME_print(mb, X509_get0_notAfter(cert));
438 (void) BIO_flush(mb);
439 while((len = BIO_read(mb, iobuf, sizeof(iobuf))) > 0)
440 gf_nputs(iobuf, len, spc);
442 gf_puts(NEWLINE, spc);
443 gf_puts(NEWLINE, spc);
445 BIO_free(mb);
448 gf_clear_so_writec(left);
450 gf_set_so_writec(&spc, right);
452 gf_puts_uline("Issuer", spc);
453 gf_puts(NEWLINE, spc);
455 output_X509_NAME(X509_get_issuer_name(cert), spc);
456 gf_puts(NEWLINE, spc);
458 gf_clear_so_writec(right);
460 side_by_side(left, right, pc);
462 gf_puts_uline("SHA1 Fingerprint", pc);
463 gf_puts(NEWLINE, pc);
464 get_fingerprint(cert, EVP_sha1(), buf, sizeof(buf), ":");
465 gf_puts(buf, pc);
466 gf_puts(NEWLINE, pc);
468 gf_puts_uline("MD5 Fingerprint", pc);
469 gf_puts(NEWLINE, pc);
470 get_fingerprint(cert, EVP_md5(), buf, sizeof(buf), ":");
471 gf_puts(buf, pc);
472 gf_puts(NEWLINE, pc);
473 gf_puts(NEWLINE, pc);
475 gf_puts_uline("Certificate Chain Information", pc);
476 gf_puts(NEWLINE, pc);
478 if((chain = get_chain_for_cert(cert, &error, &len)) != NULL){
479 X509 *x;
480 X509_NAME_ENTRY *e;
481 int i, offset = 2;
482 char space[256];
483 X509_NAME *subject;
485 for(i = 0; i < offset; i++) space[i] = ' ';
487 for(i = -1; i < sk_X509_num(chain); i++){
488 char buf[256];
490 x = i == -1 ? cert : sk_X509_value(chain, i);
492 if(x){
493 if(i>=0){
494 space[offset + i + 0] = ' ';
495 space[offset + i + 1] = '\\';
496 space[offset + i + 2] = '-';
497 space[offset + i + 3] = ' ';
498 space[offset + i + 4] = '\0';
499 gf_puts(space, pc);
501 else{
502 space[offset] = '\0';
503 gf_puts(space, pc);
505 if(i >= 0)
506 gf_puts_uline("Signed by: ", pc);
507 else
508 gf_puts_uline("Issued to: ", pc);
510 subject = X509_get_subject_name(x);
512 if((e = X509_NAME_get_entry(subject, X509_NAME_entry_count(subject)-1)) != NULL){
513 X509_NAME_get_text_by_OBJ(subject, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
514 gf_puts(buf, pc);
515 gf_puts(NEWLINE, pc);
518 else{
519 gf_puts("No certificate info found", pc);
520 gf_puts(NEWLINE, pc);
521 break;
524 e = X509_NAME_get_entry(X509_get_issuer_name(x),
525 X509_NAME_entry_count(X509_get_issuer_name(x))-1);
526 if(e){
527 X509_NAME_get_text_by_OBJ(X509_get_issuer_name(x), X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
528 space[offset + i + 0] = ' ';
529 space[offset + i + 1] = '\\';
530 space[offset + i + 2] = '-';
531 space[offset + i + 3] = ' ';
532 space[offset + i + 4] = '\0';
533 gf_puts(space, pc);
534 gf_puts_uline("Signed by: ", pc);
535 gf_puts(buf, pc);
536 gf_puts(NEWLINE, pc);
538 sk_X509_pop_free(chain, X509_free);
540 gf_puts(NEWLINE, pc);
542 so_give(&left);
543 so_give(&right);
547 void
548 output_X509_NAME(X509_NAME *name, gf_io_t pc)
550 int i, c;
551 char buf[256];
553 c = X509_NAME_entry_count(name);
555 for(i=c-1; i>=0; i--){
556 X509_NAME_ENTRY *e;
558 e = X509_NAME_get_entry(name,i);
559 if(!e)
560 continue;
562 X509_NAME_get_text_by_OBJ(name, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
564 gf_puts(buf, pc);
565 gf_puts(NEWLINE, pc);
571 * Output the contents of the given stores (left and right)
572 * to the given gf_io_t.
573 * The width of the terminal is inspected and two columns
574 * are created to fit the stores into. They are then wrapped
575 * and merged.
577 void
578 side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc)
580 STORE_S *left_wrapped;
581 STORE_S *right_wrapped;
582 char buf_l[256];
583 char buf_r[256];
584 char *l, *r;
585 char *b;
586 int i;
587 int w = ps_global->ttyo->screen_cols/2 - 1;
589 so_seek(left, 0, 0);
590 so_seek(right, 0, 0);
592 left_wrapped = wrap_store(left, w);
593 right_wrapped = wrap_store(right, w);
595 so_seek(left_wrapped, 0, 0);
596 so_seek(right_wrapped, 0, 0);
598 for(;;){
600 l = so_fgets(left_wrapped, buf_l, sizeof(buf_l));
601 r = so_fgets(right_wrapped, buf_r, sizeof(buf_r));
602 if(l == NULL && r == NULL)
603 break;
605 for(i=0, b=buf_l; i<w && *b && *b!='\r' && *b!='\n'; i++,b++){
606 pc(*b);
607 /* reduce accumulated width if an embed tag is discovered */
608 if(*b==TAG_EMBED)
609 i-=2;
612 if(buf_r[0]){
613 while(i<w){
614 pc(' ');
615 i++;
617 pc(' ');
619 for(i=0, b=buf_r; i<w && *b && *b!='\r' && *b!='\n'; i++,b++)
620 pc(*b);
623 gf_puts(NEWLINE, pc);
626 so_give(&left_wrapped);
627 so_give(&right_wrapped);
631 * Wrap the text in the given store to the given width.
632 * A new store is created for the result.
634 STORE_S *
635 wrap_store(STORE_S *in, int width)
637 STORE_S *result;
638 void *ws;
639 gf_io_t ipc,opc;
641 if(width<10)
642 width = 10;
644 result = so_get(CharStar, NULL, EDIT_ACCESS);
645 ws = gf_wrap_filter_opt(width, width, NULL, 0, 0);
647 gf_filter_init();
648 gf_link_filter(gf_wrap, ws);
650 gf_set_so_writec(&opc, result);
651 gf_set_so_readc(&ipc, in);
653 gf_pipe(ipc, opc);
655 gf_clear_so_readc(in);
656 gf_clear_so_writec(result);
658 return result;
662 void
663 smime_config_screen(struct pine *ps, int edit_exceptions)
665 CONF_S *ctmp = NULL, *first_line = NULL;
666 SAVED_CONFIG_S *vsave;
667 OPT_SCREEN_S screen;
668 int ew, readonly_warning = 0;
670 dprint((9, "smime_config_screen()"));
671 ps->next_screen = SCREEN_FUN_NULL;
674 * this is necessary because we need to know the correct paths
675 * to configure certificates and keys, and we could get here
676 * without having done that before we reach this place.
678 smime_reinit();
680 if(ps->fix_fixed_warning)
681 offer_to_fix_pinerc(ps);
683 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
685 if(ps->restricted)
686 readonly_warning = 1;
687 else{
688 PINERC_S *prc = NULL;
690 switch(ew){
691 case Main:
692 prc = ps->prc;
693 break;
694 case Post:
695 prc = ps->post_prc;
696 break;
697 default:
698 break;
701 readonly_warning = prc ? prc->readonly : 1;
702 if(prc && prc->quit_to_edit){
703 quit_to_edit_msg(prc);
704 return;
708 smime_config_init_display(ps, &ctmp, &first_line);
710 vsave = save_smime_config_vars(ps);
712 memset(&screen, 0, sizeof(screen));
713 screen.deferred_ro_warning = readonly_warning;
714 switch(conf_scroll_screen(ps, &screen, first_line,
715 edit_exceptions ? _("SETUP S/MIME EXCEPTIONS")
716 : _("SETUP S/MIME"),
717 /* TRANSLATORS: Print something1 using something2.
718 configuration is something1 */
719 _("configuration"), 0, NULL)){
720 case 0:
721 break;
723 case 1:
724 write_pinerc(ps, ew, WRP_NONE);
725 break;
727 case 10:
728 revert_to_saved_smime_config(ps, vsave);
729 break;
731 default:
732 q_status_message(SM_ORDER, 7, 10,
733 _("conf_scroll_screen bad ret in smime_config"));
734 break;
737 free_saved_smime_config(ps, &vsave);
738 smime_reinit();
743 smime_related_var(struct pine *ps, struct variable *var)
745 return(var == &ps->vars[V_PUBLICCERT_DIR] ||
746 var == &ps->vars[V_PUBLICCERT_CONTAINER] ||
747 var == &ps->vars[V_PRIVATEKEY_DIR] ||
748 var == &ps->vars[V_PRIVATEKEY_CONTAINER] ||
749 var == &ps->vars[V_CACERT_DIR] ||
750 var == &ps->vars[V_CACERT_CONTAINER]);
753 void
754 smime_config_init_display(struct pine *ps, CONF_S **ctmp, CONF_S **first_line)
756 char tmp[200];
757 int i, ind, ln = 0;
758 struct variable *vtmp;
759 CONF_S *ctmpb;
760 FEATURE_S *feature;
762 /* find longest variable name */
763 for(vtmp = ps->vars; vtmp->name; vtmp++){
764 if(!(smime_related_var(ps, vtmp)))
765 continue;
767 if((i = utf8_width(pretty_var_name(vtmp->name))) > ln)
768 ln = i;
771 for(vtmp = ps->vars; vtmp->name; vtmp++){
772 if(!(smime_related_var(ps, vtmp)))
773 continue;
775 new_confline(ctmp)->var = vtmp;
776 if(first_line && !*first_line)
777 *first_line = *ctmp;
779 (*ctmp)->valoffset = ln+3;
780 (*ctmp)->keymenu = &config_text_keymenu;
781 (*ctmp)->help = config_help(vtmp - ps->vars, 0);
782 (*ctmp)->tool = text_tool;
784 utf8_snprintf(tmp, sizeof(tmp), "%-*.100w =", ln, pretty_var_name(vtmp->name));
785 tmp[sizeof(tmp)-1] = '\0';
787 (*ctmp)->varname = cpystr(tmp);
788 (*ctmp)->varnamep = (*ctmp);
789 (*ctmp)->flags = CF_STARTITEM;
790 (*ctmp)->value = pretty_value(ps, *ctmp);
794 vtmp = &ps->vars[V_FEATURE_LIST];
796 new_confline(ctmp);
797 ctmpb = (*ctmp);
798 (*ctmp)->flags |= CF_NOSELECT | CF_STARTITEM;
799 (*ctmp)->keymenu = &config_checkbox_keymenu;
800 (*ctmp)->tool = NULL;
802 /* put a nice delimiter before list */
803 new_confline(ctmp)->var = NULL;
804 (*ctmp)->varnamep = ctmpb;
805 (*ctmp)->keymenu = &config_checkbox_keymenu;
806 (*ctmp)->help = NO_HELP;
807 (*ctmp)->tool = checkbox_tool;
808 (*ctmp)->valoffset = feature_indent();
809 (*ctmp)->flags |= CF_NOSELECT;
810 (*ctmp)->value = cpystr("Set Feature Name");
812 new_confline(ctmp)->var = NULL;
813 (*ctmp)->varnamep = ctmpb;
814 (*ctmp)->keymenu = &config_checkbox_keymenu;
815 (*ctmp)->help = NO_HELP;
816 (*ctmp)->tool = checkbox_tool;
817 (*ctmp)->valoffset = feature_indent();
818 (*ctmp)->flags |= CF_NOSELECT;
819 (*ctmp)->value = cpystr("--- ----------------------");
821 ind = feature_list_index(F_DONT_DO_SMIME);
822 feature = feature_list(ind);
823 new_confline(ctmp)->var = vtmp;
824 (*ctmp)->varnamep = ctmpb;
825 (*ctmp)->keymenu = &config_checkbox_keymenu;
826 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
827 (*ctmp)->tool = checkbox_tool;
828 (*ctmp)->valoffset = feature_indent();
829 (*ctmp)->varmem = ind;
830 (*ctmp)->value = pretty_value(ps, (*ctmp));
832 ind = feature_list_index(F_ENCRYPT_DEFAULT_ON);
833 feature = feature_list(ind);
834 new_confline(ctmp)->var = vtmp;
835 (*ctmp)->varnamep = ctmpb;
836 (*ctmp)->keymenu = &config_checkbox_keymenu;
837 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
838 (*ctmp)->tool = checkbox_tool;
839 (*ctmp)->valoffset = feature_indent();
840 (*ctmp)->varmem = ind;
841 (*ctmp)->value = pretty_value(ps, (*ctmp));
843 ind = feature_list_index(F_REMEMBER_SMIME_PASSPHRASE);
844 feature = feature_list(ind);
845 new_confline(ctmp)->var = vtmp;
846 (*ctmp)->varnamep = ctmpb;
847 (*ctmp)->keymenu = &config_checkbox_keymenu;
848 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
849 (*ctmp)->tool = checkbox_tool;
850 (*ctmp)->valoffset = feature_indent();
851 (*ctmp)->varmem = ind;
852 (*ctmp)->value = pretty_value(ps, (*ctmp));
854 ind = feature_list_index(F_SIGN_DEFAULT_ON);
855 feature = feature_list(ind);
856 new_confline(ctmp)->var = vtmp;
857 (*ctmp)->varnamep = ctmpb;
858 (*ctmp)->keymenu = &config_checkbox_keymenu;
859 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
860 (*ctmp)->tool = checkbox_tool;
861 (*ctmp)->valoffset = feature_indent();
862 (*ctmp)->varmem = ind;
863 (*ctmp)->value = pretty_value(ps, (*ctmp));
865 ind = feature_list_index(F_USE_CERT_STORE_ONLY);
866 feature = feature_list(ind);
867 new_confline(ctmp)->var = vtmp;
868 (*ctmp)->varnamep = ctmpb;
869 (*ctmp)->keymenu = &config_checkbox_keymenu;
870 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
871 (*ctmp)->tool = checkbox_tool;
872 (*ctmp)->valoffset = feature_indent();
873 (*ctmp)->varmem = ind;
874 (*ctmp)->value = pretty_value(ps, (*ctmp));
876 #ifdef APPLEKEYCHAIN
877 new_confline(ctmp);
878 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
880 new_confline(ctmp);
881 (*ctmp)->flags |= CF_NOSELECT;
882 (*ctmp)->value = cpystr(_("Mac OS X specific features"));
884 ind = feature_list_index(F_PUBLICCERTS_IN_KEYCHAIN);
885 feature = feature_list(ind);
886 new_confline(ctmp)->var = vtmp;
887 (*ctmp)->varnamep = ctmpb;
888 (*ctmp)->keymenu = &config_checkbox_keymenu;
889 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
890 (*ctmp)->tool = checkbox_tool;
891 (*ctmp)->valoffset = feature_indent();
892 (*ctmp)->varmem = ind;
893 (*ctmp)->value = pretty_value(ps, (*ctmp));
894 #endif /* APPLEKEYCHAIN */
896 new_confline(ctmp);
897 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
899 for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
900 tmp[i] = '-';
901 new_confline(ctmp);
902 (*ctmp)->flags |= CF_NOSELECT;
903 (*ctmp)->value = cpystr(tmp);
905 new_confline(ctmp);
906 (*ctmp)->flags |= CF_NOSELECT;
907 (*ctmp)->value = cpystr(_("Be careful with the following commands, they REPLACE contents in the target"));
909 new_confline(ctmp);
910 (*ctmp)->flags |= CF_NOSELECT;
911 (*ctmp)->value = cpystr(tmp);
913 new_confline(ctmp);
914 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
916 /* copy public directory to container */
917 new_confline(ctmp);
918 (*ctmp)->tool = smime_helper_tool;
919 (*ctmp)->keymenu = &config_smime_helper_keymenu;
920 (*ctmp)->help = h_config_smime_transfer_pub_to_con;
921 (*ctmp)->value = cpystr(_("Transfer public certs FROM directory TO container"));
922 (*ctmp)->varmem = 1;
924 /* copy private directory to container */
925 new_confline(ctmp);
926 (*ctmp)->tool = smime_helper_tool;
927 (*ctmp)->keymenu = &config_smime_helper_keymenu;
928 (*ctmp)->help = h_config_smime_transfer_priv_to_con;
929 (*ctmp)->value = cpystr(_("Transfer private keys FROM directory TO container"));
930 (*ctmp)->varmem = 3;
932 /* copy cacert directory to container */
933 new_confline(ctmp);
934 (*ctmp)->tool = smime_helper_tool;
935 (*ctmp)->keymenu = &config_smime_helper_keymenu;
936 (*ctmp)->help = h_config_smime_transfer_cacert_to_con;
937 (*ctmp)->value = cpystr(_("Transfer CA certs FROM directory TO container"));
938 (*ctmp)->varmem = 5;
940 new_confline(ctmp)->var = vtmp;
941 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
943 /* copy public container to directory */
944 new_confline(ctmp);
945 (*ctmp)->tool = smime_helper_tool;
946 (*ctmp)->keymenu = &config_smime_helper_keymenu;
947 (*ctmp)->help = h_config_smime_transfer_pub_to_dir;
948 (*ctmp)->value = cpystr(_("Transfer public certs FROM container TO directory"));
949 (*ctmp)->varmem = 2;
951 /* copy private container to directory */
952 new_confline(ctmp);
953 (*ctmp)->tool = smime_helper_tool;
954 (*ctmp)->keymenu = &config_smime_helper_keymenu;
955 (*ctmp)->help = h_config_smime_transfer_priv_to_dir;
956 (*ctmp)->value = cpystr(_("Transfer private keys FROM container TO directory"));
957 (*ctmp)->varmem = 4;
959 /* copy cacert container to directory */
960 new_confline(ctmp);
961 (*ctmp)->tool = smime_helper_tool;
962 (*ctmp)->keymenu = &config_smime_helper_keymenu;
963 (*ctmp)->help = h_config_smime_transfer_cacert_to_dir;
964 (*ctmp)->value = cpystr(_("Transfer CA certs FROM container TO directory"));
965 (*ctmp)->varmem = 6;
967 #ifdef APPLEKEYCHAIN
969 new_confline(ctmp)->var = vtmp;
970 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
972 /* copy public container to keychain */
973 new_confline(ctmp);
974 (*ctmp)->tool = smime_helper_tool;
975 (*ctmp)->keymenu = &config_smime_helper_keymenu;
976 (*ctmp)->help = h_config_smime_transfer_pubcon_to_key;
977 (*ctmp)->value = cpystr(_("Transfer public certs FROM container TO keychain"));
978 (*ctmp)->varmem = 7;
980 /* copy public keychain to container */
981 new_confline(ctmp);
982 (*ctmp)->tool = smime_helper_tool;
983 (*ctmp)->keymenu = &config_smime_helper_keymenu;
984 (*ctmp)->help = h_config_smime_transfer_pubkey_to_con;
985 (*ctmp)->value = cpystr(_("Transfer public certs FROM keychain TO container"));
986 (*ctmp)->varmem = 8;
988 #endif /* APPLEKEYCHAIN */
990 if(SMHOLDERTYPE(Private) == Keychain
991 && SMHOLDERTYPE(Public) == Keychain
992 && SMHOLDERTYPE(CACert) == Keychain)
993 return;
995 new_confline(ctmp)->var = vtmp;
996 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
998 new_confline(ctmp);
999 (*ctmp)->flags |= CF_NOSELECT;
1000 (*ctmp)->value = cpystr(tmp);
1002 new_confline(ctmp);
1003 (*ctmp)->flags |= CF_NOSELECT;
1004 (*ctmp)->value = cpystr(_("Manage your own certificates"));
1006 new_confline(ctmp);
1007 (*ctmp)->flags |= CF_NOSELECT;
1008 (*ctmp)->value = cpystr(tmp);
1010 new_confline(ctmp)->var = vtmp;
1011 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1013 /* manage public certificates */
1014 new_confline(ctmp);
1015 (*ctmp)->tool = smime_helper_tool;
1016 (*ctmp)->keymenu = &config_smime_manage_certs_menu_keymenu;
1017 (*ctmp)->help = h_config_smime_public_certificates;
1018 (*ctmp)->value = cpystr(_("Manage Public Certificates"));
1019 (*ctmp)->varmem = 9;
1020 (*ctmp)->d.s.ctype = Public;
1022 /* manage private keys */
1023 new_confline(ctmp);
1024 (*ctmp)->tool = smime_helper_tool;
1025 (*ctmp)->keymenu = &config_smime_manage_certs_menu_keymenu;
1026 (*ctmp)->help = h_config_smime_private_keys;
1027 (*ctmp)->value = cpystr(_("Manage Private Keys"));
1028 (*ctmp)->varmem = 10;
1029 (*ctmp)->d.s.ctype = Private;
1031 /* manage Certificate Authorities */
1032 new_confline(ctmp);
1033 (*ctmp)->tool = smime_helper_tool;
1034 (*ctmp)->keymenu = &config_smime_manage_certs_menu_keymenu;
1035 (*ctmp)->help = h_config_smime_certificate_authorities;
1036 (*ctmp)->value = cpystr(_("Manage Certificate Authorities"));
1037 (*ctmp)->varmem = 11;
1038 (*ctmp)->d.s.ctype = CACert;
1040 #ifdef PASSFILE
1041 new_confline(ctmp)->var = vtmp;
1042 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1044 new_confline(ctmp);
1045 (*ctmp)->flags |= CF_NOSELECT;
1046 (*ctmp)->value = cpystr(tmp);
1048 new_confline(ctmp);
1049 (*ctmp)->flags |= CF_NOSELECT;
1050 (*ctmp)->value = cpystr(_("Manage Key and Certificate for Password File"));
1052 new_confline(ctmp);
1053 (*ctmp)->flags |= CF_NOSELECT;
1054 (*ctmp)->value = cpystr(tmp);
1056 new_confline(ctmp)->var = vtmp;
1057 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1059 /* manage password file certificates */
1060 new_confline(ctmp);
1061 (*ctmp)->tool = smime_helper_tool;
1062 (*ctmp)->keymenu = &config_smime_manage_password_file_menu_keymenu;
1063 (*ctmp)->help = h_config_smime_password_file_certificates;
1064 (*ctmp)->value = cpystr(_("Manage Password File Key and Certificate"));
1065 (*ctmp)->varmem = 12;
1066 (*ctmp)->d.s.ctype = Password;
1067 #endif /* PASSFILE */
1069 (*ctmp)->next = NULL;
1072 void display_certificate_information(struct pine *ps, X509 *cert, char *email, WhichCerts ctype, int num)
1074 STORE_S *store;
1075 SCROLL_S scrollargs;
1076 int cmd, offset;
1077 int pub_cert, priv_cert, new_store;
1078 long error;
1079 BIO *out = NULL;
1081 cmd = offset = pub_cert = priv_cert = 0;
1082 new_store = 1;
1083 ps->next_screen = SCREEN_FUN_NULL;
1084 do {
1085 /* MC_PRIVATE and MC_PUBLIC cancel each other,
1086 * they can not be active at the same time
1088 switch(cmd){
1089 case MC_PRIVATE:
1090 pub_cert = 0;
1091 priv_cert = 1 - priv_cert;
1092 smime_certificate_info_keymenu.keys[PUBLIC_KEY].label = N_("Public Key");
1093 smime_certificate_info_keymenu.keys[PRIVATE_KEY].label = priv_cert ? N_("No Priv Key") : N_("Pivate Key");
1094 break;
1096 case MC_PUBLIC:
1097 priv_cert = 0;
1098 pub_cert = 1 - pub_cert;
1099 smime_certificate_info_keymenu.keys[PRIVATE_KEY].label = priv_cert ? N_("No Priv Key") : N_("Pivate Key");
1100 smime_certificate_info_keymenu.keys[PUBLIC_KEY].label = N_("Public Key");
1101 break;
1103 case MC_TRUST:
1104 if(SMHOLDERTYPE(CACert) == Directory)
1105 save_cert_for(email, cert, CACert);
1106 else{ /* if(SMHOLDERTYPE(CACert) == Container) */
1107 char path[MAXPATH];
1108 char *upath = PATHCERTDIR(ctype);
1109 char *tempfile = tempfile_in_same_dir(path, "az", NULL);
1110 CertList *clist;
1112 if(IS_REMOTE(upath))
1113 strncpy(path, temp_nam(NULL, "a6"), sizeof(path)-1);
1114 else
1115 strncpy(path, upath, sizeof(path)-1);
1116 path[sizeof(path)-1] = '\0';
1118 add_to_end_of_certlist(&ps_global->smime->cacertlist, email, X509_dup(cert));
1119 for(clist=ps_global->smime->cacertlist; clist && clist->next; clist = clist->next);
1120 certlist_to_file(tempfile, clist);
1121 add_file_to_container(CACert, tempfile, email);
1122 unlink(tempfile);
1124 renew_store();
1125 new_store = 1;
1126 break;
1128 case MC_DELETE:
1129 if (get_cert_deleted(ctype, num) != 0)
1130 q_status_message(SM_ORDER, 1, 3, _("Certificate already deleted"));
1131 else{
1132 mark_cert_deleted(ctype, num, 1);
1133 q_status_message(SM_ORDER, 1, 3, _("Certificate marked deleted"));
1135 break;
1137 case MC_UNDELETE:
1138 if (get_cert_deleted(ctype, num) != 0){
1139 mark_cert_deleted(ctype, num, 0);
1140 q_status_message(SM_ORDER, 1, 3, _("Certificate marked UNdeleted"));
1142 else
1143 q_status_message(SM_ORDER, 1, 3, _("Certificate not marked deleted"));
1144 break;
1146 default: break;
1149 if((pub_cert || priv_cert)
1150 && (out = print_private_key_information(email, priv_cert)) == NULL)
1151 q_status_message(SM_ORDER, 1, 3, _("Problem Reading Private Certificate Information"));
1153 if(new_store){
1154 store = so_get(CharStar, NULL, EDIT_ACCESS);
1155 view_writec_init(store, NULL, HEADER_ROWS(ps),
1156 HEADER_ROWS(ps) + ps->ttyo->screen_rows - (HEADER_ROWS(ps)+ FOOTER_ROWS(ps)));
1158 snprintf(tmp_20k_buf, SIZEOF_20KBUF,"%s", _("Certificate Information"));
1159 gf_puts_uline(tmp_20k_buf, view_writec);
1160 gf_puts(NEWLINE, view_writec);
1161 print_separator_line(100, '-', view_writec);
1163 output_cert_info(cert, view_writec);
1164 gf_puts(NEWLINE, view_writec);
1166 if(smime_validate_cert(cert, &error) < 0){
1167 const char *errorp = X509_verify_cert_error_string(error);
1168 snprintf(tmp_20k_buf, SIZEOF_20KBUF,_("Error validating certificate: %s"), errorp);
1169 } else
1170 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s", _("Certificate validated without errors"));
1172 gf_puts_uline(tmp_20k_buf, view_writec);
1173 gf_puts(NEWLINE, view_writec);
1175 if(out != NULL){ /* print private key information */
1176 unsigned char ch[2];
1178 gf_puts(NEWLINE, view_writec);
1179 ch[1] = '\0';
1180 while(BIO_read(out, ch, 1) >= 1)
1181 gf_puts((char *)ch, view_writec);
1182 gf_puts(NEWLINE, view_writec);
1183 q_status_message1(SM_ORDER, 1, 3, _("%s information shown at bottom of certificate information"), pub_cert ? _("Public") : _("Private"));
1184 BIO_free_all(out);
1185 out = NULL;
1187 view_writec_destroy();
1188 new_store = 0;
1191 memset(&scrollargs, 0, sizeof(SCROLL_S));
1193 scrollargs.text.text = so_text(store);
1194 scrollargs.text.src = CharStar;
1195 scrollargs.text.desc = "certificate information";
1196 scrollargs.body_valid = 1;
1198 if(offset){ /* resize? preserve paging! */
1199 scrollargs.start.on = Offset;
1200 scrollargs.start.loc.offset = offset;
1201 scrollargs.body_valid = 0;
1202 offset = 0L;
1205 scrollargs.use_indexline_color = 1;
1207 scrollargs.bar.title = _("CERTIFICATE INFORMATION");
1208 scrollargs.proc.tool = manage_certificate_info_tool;
1209 scrollargs.resize_exit = 1;
1210 scrollargs.help.text = h_certificate_information;
1211 scrollargs.help.title = _("HELP FOR MESSAGE TEXT VIEW");
1212 scrollargs.keys.what = FirstMenu;
1213 scrollargs.keys.menu = &smime_certificate_info_keymenu;
1214 setbitmap(scrollargs.keys.bitmap);
1215 if(ctype != Public || error != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
1216 /*error != X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)*/
1217 clrbitn(TRUST_KEY, scrollargs.keys.bitmap);
1218 if(ctype != Private){
1219 clrbitn(PUBLIC_KEY, scrollargs.keys.bitmap);
1220 clrbitn(PRIVATE_KEY, scrollargs.keys.bitmap);
1222 if(ctype == Password){
1223 clrbitn(DELETE_CERT_KEY, scrollargs.keys.bitmap);
1224 clrbitn(UNDELETE_CERT_KEY, scrollargs.keys.bitmap);
1227 cmd = scrolltool(&scrollargs);
1229 switch(cmd){
1230 case MC_RESIZE :
1231 case MC_PRIVATE:
1232 case MC_PUBLIC : if(scrollargs.start.on == Offset)
1233 offset = scrollargs.start.loc.offset;
1234 new_store = 1;
1235 default: break;
1237 if(new_store)
1238 so_give(&store);
1239 } while (cmd != MC_EXIT);
1240 ps->mangled_screen = 1;
1244 * This is silly, we just need this function so that we can tell scrolltool
1245 * that some commands are recognized. We use scrolltool because we do not
1246 * want to rewrite output_cert_info.
1249 manage_certificate_info_tool(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
1251 int rv;
1252 switch(cmd){
1253 case MC_DELETE:
1254 case MC_UNDELETE:
1255 case MC_PRIVATE:
1256 case MC_PUBLIC:
1257 case MC_TRUST: rv = 1; break;
1258 default: rv = 0; break;
1260 return rv;
1265 manage_certs_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags)
1267 int rv = 0;
1268 X509 *cert = NULL;
1269 WhichCerts ctype = (*cl)->d.s.ctype;
1271 switch(cmd){
1272 case MC_ADD: /* create a self signed certificate and import it */
1273 if(ctype == Password){
1274 PERSONAL_CERT *pc;
1275 char pathdir[MAXPATH+1], filename[MAXPATH+1];
1276 struct stat sbuf;
1277 int st;
1278 smime_path(DF_SMIMETMPDIR, pathdir, sizeof(pathdir));
1279 if(((st = our_stat(pathdir, &sbuf)) == 0
1280 && (sbuf.st_mode & S_IFMT) == S_IFDIR)
1281 || (st != 0
1282 && can_access(pathdir, ACCESS_EXISTS) != 0
1283 && our_mkpath(pathdir, 0700) == 0)){
1284 pc = ALPINE_self_signed_certificate(NULL, 0, pathdir, MASTERNAME);
1285 snprintf(filename, sizeof(filename), "%s/%s.key",
1286 pathdir, MASTERNAME);
1287 filename[sizeof(filename)-1] = '\0';
1288 rv = import_certificate(ctype, pc, filename);
1289 if(our_stat(pathdir, &sbuf) == 0){
1290 if(unlink(filename) < 0)
1291 q_status_message1(SM_ORDER, 0, 2,
1292 _("Could not remove private key %s.key"), MASTERNAME);
1293 filename[strlen(filename)-4] = '\0';
1294 strcat(filename, ".crt");
1295 if(unlink(filename) < 0)
1296 q_status_message1(SM_ORDER, 0, 2,
1297 _("Could not remove public certifica %s.crt"), MASTERNAME);
1298 if(rmdir(pathdir) < 0)
1299 q_status_message1(SM_ORDER, 0, 2,
1300 _("Could not remove temporary directory %s"), pathdir);
1303 rv = 10; /* forces redraw */
1305 break;
1307 case MC_CHOICE:
1308 if(PATHCERTDIR(ctype) == NULL)
1309 return 0;
1311 if((cert = get_cert_for((*cl)->d.s.address, ctype, 0)) == NULL){
1312 q_status_message(SM_ORDER, 1, 3, _("Problem Reading Certificate"));
1313 rv = 0;
1315 else{
1316 display_certificate_information(ps, cert, (*cl)->d.s.address, ctype, (*cl)->varmem);
1317 rv = 10 + (*cl)->varmem;
1319 break;
1321 case MC_DELETE:
1322 if ((*cl)->d.s.deleted != 0)
1323 q_status_message(SM_ORDER, 1, 3, _("Certificate already deleted"));
1324 else{
1325 (*cl)->d.s.deleted = 1;
1326 rv = 10 + (*cl)->varmem; /* forces redraw */
1327 mark_cert_deleted(ctype, (*cl)->varmem, 1);
1328 q_status_message(SM_ORDER, 1, 3, _("Certificate marked deleted"));
1330 break;
1332 case MC_UNDELETE:
1333 if ((*cl)->d.s.deleted == 0)
1334 q_status_message(SM_ORDER, 1, 3, _("Certificate not marked deleted"));
1335 else{
1336 (*cl)->d.s.deleted = 0;
1337 mark_cert_deleted(ctype, (*cl)->varmem, 0);
1338 rv = 10 + (*cl)->varmem; /* forces redraw */
1339 q_status_message(SM_ORDER, 1, 3, _("Certificate marked UNdeleted"));
1341 break;
1343 case MC_EXPUNGE:
1344 { CertList *cl;
1346 for(cl = DATACERT(ctype); cl != NULL && DELETEDCERT(cl) == 0; cl = cl->next);
1347 if(cl != NULL && DELETEDCERT(cl) != 0){
1348 smime_expunge_cert(ctype);
1349 rv = 10; /* forces redraw */
1351 else{
1352 q_status_message(SM_ORDER, 3, 3, _("No certificates marked deleted"));
1353 rv = 0;
1355 break;
1357 case MC_IMPORT:
1358 rv = import_certificate(ctype, NULL, NULL);
1359 if(rv < 0){
1360 switch(rv){
1361 default:
1362 case -1:
1363 cmd_cancelled("Import certificate");
1364 break;
1366 case -2:
1367 q_status_message1(SM_ORDER, 0, 2, _("Can't import certificate outside of %s"),
1368 ps_global->VAR_OPER_DIR);
1369 break;
1372 rv = 10; /* forces redraw */
1373 break;
1375 case MC_EXIT:
1376 rv = config_exit_cmd(flags);
1377 break;
1379 default:
1380 rv = -1;
1381 break;
1384 X509_free(cert);
1385 return rv;
1388 void
1389 smime_setup_size(char **s, size_t buflen, size_t n)
1391 char *t = *s;
1392 *t++ = ' ';
1393 *t++ = '%';
1394 *t++ = '-';
1395 snprintf(t, buflen-3, "%zu.%zu", n, n);
1396 t += strlen(t);
1397 *t++ = 's';
1398 *s = t;
1401 #ifdef PASSFILE
1402 void manage_password_file_certificates(struct pine *ps)
1404 OPT_SCREEN_S screen;
1405 int readonly_warning = 0, rv = 10, fline, state = 0;
1407 dprint((9, "manage_password_file_certificates"));
1408 ps->next_screen = SCREEN_FUN_NULL;
1410 do {
1411 CONF_S *ctmp = NULL, *first_line = NULL;
1413 fline = rv >= 10 ? rv - 10 : 0;
1415 smime_manage_password_file_certs_init(ps, &ctmp, &first_line, fline, &state);
1417 if(ctmp == NULL){
1418 ps->mangled_screen = 1;
1419 q_status_message(SM_ORDER, 1, 3, _("Failed to initialize password management screen (no key)"));
1420 return;
1423 memset(&screen, 0, sizeof(screen));
1424 screen.deferred_ro_warning = readonly_warning;
1425 rv = conf_scroll_screen(ps, &screen, first_line,
1426 _("MANAGE PASSWORD FILE CERTS"),
1427 /* TRANSLATORS: Print something1 using something2.
1428 configuration is something1 */
1429 _("configuration"), 0, NULL);
1430 } while (rv != 0);
1432 ps->mangled_screen = 1;
1433 smime_reinit();
1436 /* state: 0 = first time,
1437 * 1 = second or another time
1439 void
1440 smime_manage_password_file_certs_init(struct pine *ps, CONF_S **ctmp, CONF_S **first_line, int fline, int *state)
1442 char tmp[200];
1443 char *ext;
1444 CertList *cl;
1445 int i;
1446 void *pwdcert = NULL; /* this is our current password file */
1447 PERSONAL_CERT *pc;
1448 X509_LOOKUP *lookup = NULL;
1449 X509_STORE *store = NULL;
1451 if(*state == 0){ /* first time around? */
1452 setup_pwdcert(&pwdcert);
1453 if(pwdcert == NULL) return;
1454 if(ps->pwdcert == NULL)
1455 ps->pwdcert = pwdcert;
1456 else
1457 free_personal_certs((PERSONAL_CERT **) &pwdcert);
1458 (*state)++;
1461 pc = (PERSONAL_CERT *) ps_global->pwdcert;
1462 ps->pwdcertlist = cl = smime_X509_to_cert_info(X509_dup(pc->cert), pc->name);
1464 for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
1465 tmp[i] = '-';
1467 new_confline(ctmp);
1468 (*ctmp)->flags |= CF_NOSELECT;
1469 (*ctmp)->value = cpystr(tmp);
1471 new_confline(ctmp);
1472 (*ctmp)->flags |= CF_NOSELECT;
1473 (*ctmp)->value = cpystr(_("Manage Certificates and Keys Used to Encrypt your Password File"));
1475 new_confline(ctmp);
1476 (*ctmp)->flags |= CF_NOSELECT;
1477 (*ctmp)->value = cpystr(tmp);
1479 new_confline(ctmp);
1480 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1482 if(cl){
1483 int s, e, df, dt, md5; /* sizes of certain fields */
1484 int nf; /* number of fields */
1485 char u[MAILTMPLEN], *t;
1487 e = MIN(strlen(cl->name), ps->ttyo->screen_cols/3); /* do not use too much screen */
1488 nf = 5; /* there are 5 fields */
1489 s = 3; /* status has fixed size */
1490 df = dt = 10; /* date from and date to have fixed size */
1491 md5 = ps->ttyo->screen_cols - s - df - dt - e - (nf - 1);
1493 t = u;
1494 smime_setup_size(&t, sizeof(u), s);
1495 smime_setup_size(&t, sizeof(u) - strlen(t), e);
1496 smime_setup_size(&t, sizeof(u) - strlen(t), df);
1497 *t++ = ' '; /* leave an extra space between dates */
1498 smime_setup_size(&t, sizeof(u) - strlen(t), dt);
1499 *t++ = ' '; /* and another space between date and md5 sum */
1500 smime_setup_size(&t, sizeof(u) - strlen(t), md5);
1501 *t = '\0'; /* tie off */
1503 new_confline(ctmp);
1504 (*ctmp)->flags |= CF_NOSELECT;
1505 (*ctmp)->value = cpystr(_("New Public Certificate and Key:"));
1507 new_confline(ctmp);
1508 (*ctmp)->d.s.ctype = Password;
1509 (*ctmp)->help = h_config_smime_password_file_certificates;
1510 (*ctmp)->tool = manage_certs_tool;
1511 (*ctmp)->keymenu = &config_smime_add_new_key_keymenu;
1512 s += 2;
1513 for(i = 0; i < s; i++) tmp[i] = ' ';
1514 tmp[i] = '\0';
1515 strncpy(tmp+s, _("Press \"RETURN\" to add new personal key"), sizeof(tmp)-s-1);
1516 for(i = strlen(tmp); i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp) - 1); i++)
1517 tmp[i] = ' ';
1518 tmp[i] = '\0';
1519 (*ctmp)->value = cpystr(tmp);
1520 *first_line = *ctmp;
1522 new_confline(ctmp);
1523 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1525 new_confline(ctmp);
1526 (*ctmp)->flags |= CF_NOSELECT;
1527 (*ctmp)->value = cpystr(_("Current Public Certificate and Key:"));
1529 new_confline(ctmp);
1530 (*ctmp)->d.s.ctype = Password;
1531 (*ctmp)->d.s.deleted = 0;
1532 (*ctmp)->help = h_config_smime_password_file_certificates;
1533 (*ctmp)->tool = manage_certs_tool;
1534 (*ctmp)->keymenu = &config_smime_manage_view_cert_keymenu;
1535 (*ctmp)->varmem = 0;
1536 (*ctmp)->help = h_config_smime_manage_public_menu;
1537 strncpy((*ctmp)->d.s.address, cl->name, sizeof((*ctmp)->d.s.address));
1538 (*ctmp)->d.s.address[sizeof((*ctmp)->d.s.address) - 1] = '\0';
1539 snprintf(tmp, sizeof(tmp), u,
1540 (*ctmp)->d.s.deleted ? "D" : " ",
1541 cl->name,
1542 DATEFROMCERT(cl), DATETOCERT(cl), MD5CERT(cl));
1543 (*ctmp)->value = cpystr(tmp);
1547 #endif /* PASSFILE */
1550 void smime_manage_certs_init(struct pine *ps, CONF_S **ctmp, CONF_S **first_line, WhichCerts ctype, int fline)
1552 char tmp[200];
1553 char *ext;
1554 CertList *data;
1555 int i;
1557 smime_init();
1559 data = DATACERT(ctype);
1560 ext = EXTCERT(ctype);
1562 if(data == NULL || RENEWCERT(data))
1563 renew_cert_data(&data, ctype);
1565 for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
1566 tmp[i] = '-';
1567 new_confline(ctmp);
1568 (*ctmp)->flags |= CF_NOSELECT;
1569 (*ctmp)->value = cpystr(tmp);
1571 (*ctmp)->keymenu = &config_text_keymenu;
1573 new_confline(ctmp);
1574 (*ctmp)->flags |= CF_NOSELECT;
1575 sprintf(tmp, _("List of %s certificates"), ctype == Public ? _("public")
1576 : (ctype == Private ? _("private")
1577 : (ctype == CACert ? _("certificate authority") : "unknown (?)")));
1578 (*ctmp)->value = cpystr(tmp);
1580 for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
1581 tmp[i] = '-';
1582 new_confline(ctmp);
1583 (*ctmp)->flags |= CF_NOSELECT;
1584 (*ctmp)->value = cpystr(tmp);
1586 new_confline(ctmp);
1587 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1589 if(data){
1590 CertList *cl; int i;
1591 int s, e, df, dt, md5; /* sizes of certain fields */
1592 int nf; /* number of fields */
1593 char u[MAILTMPLEN], *t;
1595 for(cl = data, e = 0; cl; cl = cl->next)
1596 if(cl->name && strlen(cl->name) > e)
1597 e = strlen(cl->name);
1599 if(ctype != Private && SMHOLDERTYPE(ctype) == Directory)
1600 e -= 4; /* remove extension length */
1601 e = MIN(e, ps->ttyo->screen_cols/3); /* do not use too much screen */
1602 nf = 5; /* there are 5 fields */
1603 s = 3; /* status has fixed size */
1604 df = dt = 10; /* date from and date to have fixed size */
1605 md5 = ps->ttyo->screen_cols - s - df - dt - e - (nf - 1);
1607 t = u;
1608 smime_setup_size(&t, sizeof(u), s);
1609 smime_setup_size(&t, sizeof(u) - strlen(t), e);
1610 smime_setup_size(&t, sizeof(u) - strlen(t), df);
1611 *t++ = ' '; /* leave an extra space between dates */
1612 smime_setup_size(&t, sizeof(u) - strlen(t), dt);
1613 *t++ = ' '; /* and another space between date and md5 sum */
1614 smime_setup_size(&t, sizeof(u) - strlen(t), md5);
1615 *t = '\0'; /* tie off */
1617 for(cl = data, i = 0; cl; cl = cl->next)
1618 if(cl->name){
1619 char *s, *t;
1621 new_confline(ctmp);
1622 (*ctmp)->d.s.ctype = ctype;
1623 (*ctmp)->d.s.deleted = get_cert_deleted(ctype, i);
1624 (*ctmp)->tool = manage_certs_tool;
1625 (*ctmp)->keymenu = &config_smime_manage_certs_work_keymenu;
1626 (*ctmp)->varmem = i++;
1627 (*ctmp)->help = ctype == Public ? h_config_smime_manage_public_menu
1628 : (ctype == Private ? h_config_smime_manage_private_menu
1629 : h_config_smime_manage_cacerts_menu);
1630 if(ctype != Private && SMHOLDERTYPE(ctype) == Directory)
1631 cl->name[strlen(cl->name) - 4] = '\0'; /* FIX FIX FIX */
1632 strncpy((*ctmp)->d.s.address, cl->name, sizeof((*ctmp)->d.s.address));
1633 (*ctmp)->d.s.address[sizeof((*ctmp)->d.s.address) - 1] = '\0';
1634 snprintf(tmp, sizeof(tmp), u,
1635 (*ctmp)->d.s.deleted ? "D" : " ",
1636 ctype == CACert ? cl->cn : cl->name,
1637 DATEFROMCERT(cl), DATETOCERT(cl), MD5CERT(cl));
1638 if(ctype != Private && SMHOLDERTYPE(ctype) == Directory)
1639 cl->name[strlen(cl->name)] = '.';
1640 (*ctmp)->value = cpystr(tmp);
1641 if(i == fline+1 && first_line && !*first_line)
1642 *first_line = *ctmp;
1645 else {
1646 new_confline(ctmp);
1647 (*ctmp)->d.s.ctype = ctype;
1648 (*ctmp)->tool = manage_certs_tool;
1649 (*ctmp)->keymenu = &config_smime_add_certs_keymenu;
1650 (*ctmp)->value = cpystr(_(" \tNo certificates found, press \"RETURN\" to add one."));
1651 if(first_line && !*first_line)
1652 *first_line = *ctmp;
1656 void manage_certificates(struct pine *ps, WhichCerts ctype)
1658 OPT_SCREEN_S screen;
1659 int readonly_warning = 0, rv = 10, fline;
1661 dprint((9, "manage_certificates(ps, %s)", ctype == Public ? _("Public") : (ctype == Private ? _("Private") : (ctype == CACert ? _("certificate authority") : _("unknown")))));
1662 ps->next_screen = SCREEN_FUN_NULL;
1664 do {
1665 CONF_S *ctmp = NULL, *first_line = NULL;
1667 fline = rv >= 10 ? rv - 10 : 0;
1669 smime_init();
1671 smime_manage_certs_init(ps, &ctmp, &first_line, ctype, fline);
1673 if(ctmp == NULL){
1674 ps->mangled_screen = 1;
1675 smime_reinit();
1676 return;
1679 memset(&screen, 0, sizeof(screen));
1680 screen.deferred_ro_warning = readonly_warning;
1681 rv = conf_scroll_screen(ps, &screen, first_line,
1682 _("MANAGE CERTIFICATES"),
1683 /* TRANSLATORS: Print something1 using something2.
1684 configuration is something1 */
1685 _("configuration"), 0, NULL);
1686 } while (rv != 0);
1688 ps->mangled_screen = 1;
1689 smime_reinit();
1693 smime_helper_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags)
1695 int rv = 0;
1697 switch(cmd){
1698 case MC_CHOICE:
1699 switch((*cl)->varmem){
1700 case 1:
1701 rv = copy_publiccert_dir_to_container();
1702 if(rv == 0)
1703 q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to container"));
1704 else{
1705 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1706 rv = 0;
1709 break;
1711 case 2:
1712 rv = copy_publiccert_container_to_dir();
1713 if(rv == 0)
1714 q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to directory, delete Container config to use"));
1715 else{
1716 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1717 rv = 0;
1720 break;
1722 case 3:
1723 rv = copy_privatecert_dir_to_container();
1724 if(rv == 0)
1725 q_status_message(SM_ORDER, 1, 3, _("Private keys transferred to container"));
1726 else{
1727 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1728 rv = 0;
1731 break;
1733 case 4:
1734 rv = copy_privatecert_container_to_dir();
1735 if(rv == 0)
1736 q_status_message(SM_ORDER, 1, 3, _("Private keys transferred to directory, delete Container config to use"));
1737 else{
1738 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1739 rv = 0;
1742 break;
1744 case 5:
1745 rv = copy_cacert_dir_to_container();
1746 if(rv == 0)
1747 q_status_message(SM_ORDER, 1, 3, _("CA certs transferred to container"));
1748 else{
1749 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1750 rv = 0;
1753 break;
1755 case 6:
1756 rv = copy_cacert_container_to_dir();
1757 if(rv == 0)
1758 q_status_message(SM_ORDER, 1, 3, _("CA certs transferred to directory, delete Container config to use"));
1759 else{
1760 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1761 rv = 0;
1764 break;
1766 #ifdef APPLEKEYCHAIN
1767 case 7:
1768 rv = copy_publiccert_container_to_keychain();
1769 if(rv == 0)
1770 q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to keychain"));
1771 else{
1772 q_status_message(SM_ORDER, 3, 3, _("Command not implemented yet"));
1773 rv = 0;
1776 break;
1778 case 8:
1779 rv = copy_publiccert_keychain_to_container();
1780 if(rv == 0)
1781 q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to container"));
1782 else{
1783 q_status_message(SM_ORDER, 3, 3, _("Command not implemented yet"));
1784 rv = 0;
1787 break;
1788 #endif /* APPLEKEYCHAIN */
1790 case 9: manage_certificates(ps, Public) ; break;
1791 case 10: manage_certificates(ps, Private); break;
1792 case 11: manage_certificates(ps, CACert) ; break;
1794 #ifdef PASSFILE
1795 case 12: manage_password_file_certificates(ps); break;
1796 #endif /* PASSFILE */
1798 default:
1799 rv = -1;
1800 break;
1803 break;
1805 case MC_EXIT:
1806 rv = config_exit_cmd(flags);
1807 break;
1809 case MC_IMPORT:
1810 rv = import_certificate((*cl)->d.s.ctype, NULL, NULL);
1811 break;
1813 default:
1814 rv = -1;
1815 break;
1818 return rv;
1823 * Compare saved user_val with current user_val to see if it changed.
1824 * If any have changed, change it back and take the appropriate action.
1826 void
1827 revert_to_saved_smime_config(struct pine *ps, SAVED_CONFIG_S *vsave)
1829 struct variable *vreal;
1830 SAVED_CONFIG_S *v;
1831 int i, n;
1832 int changed = 0;
1833 char *pval, **apval, **lval, ***alval;
1835 v = vsave;
1836 for(vreal = ps->vars; vreal->name; vreal++,v++){
1837 if(!(smime_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
1838 continue;
1840 if(vreal->is_list){
1841 lval = LVAL(vreal, ew);
1842 alval = ALVAL(vreal, ew);
1844 if((v->saved_user_val.l && !lval)
1845 || (!v->saved_user_val.l && lval))
1846 changed++;
1847 else if(!v->saved_user_val.l && !lval)
1848 ;/* no change, nothing to do */
1849 else
1850 for(i = 0; v->saved_user_val.l[i] || lval[i]; i++)
1851 if((v->saved_user_val.l[i]
1852 && (!lval[i]
1853 || strcmp(v->saved_user_val.l[i], lval[i])))
1855 (!v->saved_user_val.l[i] && lval[i])){
1856 changed++;
1857 break;
1860 if(changed){
1861 char **list;
1863 if(alval){
1864 if(*alval)
1865 free_list_array(alval);
1867 /* copy back the original one */
1868 if(v->saved_user_val.l){
1869 list = v->saved_user_val.l;
1870 n = 0;
1871 /* count how many */
1872 while(list[n])
1873 n++;
1875 *alval = (char **)fs_get((n+1) * sizeof(char *));
1877 for(i = 0; i < n; i++)
1878 (*alval)[i] = cpystr(v->saved_user_val.l[i]);
1880 (*alval)[n] = NULL;
1885 else{
1886 pval = PVAL(vreal, ew);
1887 apval = APVAL(vreal, ew);
1889 if((v->saved_user_val.p &&
1890 (!pval || strcmp(v->saved_user_val.p, pval))) ||
1891 (!v->saved_user_val.p && pval)){
1892 /* It changed, fix it */
1893 changed++;
1894 if(apval){
1895 /* free the changed value */
1896 if(*apval)
1897 fs_give((void **)apval);
1899 if(v->saved_user_val.p)
1900 *apval = cpystr(v->saved_user_val.p);
1905 if(changed){
1906 if(vreal == &ps->vars[V_FEATURE_LIST])
1907 set_feature_list_current_val(vreal);
1908 else
1909 set_current_val(vreal, TRUE, FALSE);
1911 fix_side_effects(ps, vreal, 1);
1917 SAVED_CONFIG_S *
1918 save_smime_config_vars(struct pine *ps)
1920 struct variable *vreal;
1921 SAVED_CONFIG_S *vsave, *v;
1923 vsave = (SAVED_CONFIG_S *)fs_get((V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
1924 memset((void *)vsave, 0, (V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
1925 v = vsave;
1926 for(vreal = ps->vars; vreal->name; vreal++,v++){
1927 if(!(smime_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
1928 continue;
1930 if(vreal->is_list){
1931 int n, i;
1932 char **list;
1934 if(LVAL(vreal, ew)){
1935 /* count how many */
1936 n = 0;
1937 list = LVAL(vreal, ew);
1938 while(list[n])
1939 n++;
1941 v->saved_user_val.l = (char **)fs_get((n+1) * sizeof(char *));
1942 memset((void *)v->saved_user_val.l, 0, (n+1)*sizeof(char *));
1943 for(i = 0; i < n; i++)
1944 v->saved_user_val.l[i] = cpystr(list[i]);
1946 v->saved_user_val.l[n] = NULL;
1949 else{
1950 if(PVAL(vreal, ew))
1951 v->saved_user_val.p = cpystr(PVAL(vreal, ew));
1955 return(vsave);
1959 void
1960 free_saved_smime_config(struct pine *ps, SAVED_CONFIG_S **vsavep)
1962 struct variable *vreal;
1963 SAVED_CONFIG_S *v;
1965 if(vsavep && *vsavep){
1966 for(v = *vsavep, vreal = ps->vars; vreal->name; vreal++,v++){
1967 if(!(smime_related_var(ps, vreal)))
1968 continue;
1970 if(vreal->is_list){ /* free saved_user_val.l */
1971 if(v && v->saved_user_val.l)
1972 free_list_array(&v->saved_user_val.l);
1974 else if(v && v->saved_user_val.p)
1975 fs_give((void **)&v->saved_user_val.p);
1978 fs_give((void **)vsavep);
1982 #endif /* SMIME */