1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: smime.c 1176 2008-09-29 21:16:42Z hubert@u.washington.edu $";
3 #endif
4 
5 /*
6  * ========================================================================
7  * Copyright 2013-2021 Eduardo Chappa
8  * Copyright 2008 University of Washington
9  *
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
13  *
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * ========================================================================
17  */
18 
19 /*
20  *  This is based on a contribution from Jonathan Paisley
21  *
22  *  File:   	    smime.c
23  *  Author: 	    paisleyj@dcs.gla.ac.uk
24  *  Date:   	    01/2001
25  */
26 
27 
28 #include "../pith/headers.h"
29 
30 #ifdef SMIME
31 
32 #include "../pith/osdep/canaccess.h"
33 #include "../pith/helptext.h"
34 #include "../pith/store.h"
35 #include "../pith/status.h"
36 #include "../pith/detach.h"
37 #include "../pith/conf.h"
38 #include "../pith/smkeys.h"
39 #include "../pith/smime.h"
40 #include "../pith/mailpart.h"
41 #include "../pith/reply.h"
42 #include "../pith/tempfile.h"
43 #include "../pith/readfile.h"
44 #include "../pith/remote.h"
45 #include "../pith/body.h"
46 #ifdef PASSFILE
47 #include "../pith/imap.h"
48 #endif /* PASSFILE */
49 
50 #include <openssl/buffer.h>
51 #include <openssl/x509v3.h>
52 #include <openssl/evp.h>
53 
54 /* internal prototypes */
55 static void            forget_private_keys(void);
56 static int             app_RAND_load_file(const char *file);
57 static void            openssl_extra_randomness(void);
58 static int             app_RAND_write_file(const char *file);
59 static const char     *openssl_error_string(void);
60 static int             load_private_key(PERSONAL_CERT *pcert);
61 static void            create_local_cache(char *h, char *base, BODY *b, int type);
62 static long            rfc822_output_func(void *b, char *string);
63 static void            setup_pkcs7_body_for_signature(BODY *b, char *description,
64 						      char *type, char *filename, char *smime_type);
65 static BIO            *body_to_bio(BODY *body);
66 static BIO            *bio_from_store(STORE_S *store);
67 static STORE_S        *get_part_contents(long msgno, const char *section);
68 static PKCS7         *get_pkcs7_from_part(long msgno, const char *section);
69 static int            do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent);
70 static int            do_detached_signature_verify(BODY *b, long msgno, char *section);
71 static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
72 static int            do_decoding(BODY *b, long msgno, const char *section);
73 static void           free_smime_struct(SMIME_STUFF_S **smime);
74 static void           setup_storage_locations(void);
75 static int            copy_container_to_dir(WhichCerts which);
76 static int	      do_fiddle_smime_message(BODY *b, long msgno, char *section);
77 void		      setup_privatekey_storage(void);
78 int		      smime_extract_and_save_cert(PKCS7 *p7);
79 int		      same_cert(X509 *, X509 *);
80 #ifdef PASSFILE
81 int		      load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert);
82 #endif /* PASSFILE */
83 EVP_PKEY 	     *load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *);
84 void		      smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
85 void		      smime_remove_folding_space(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
86 int		      smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag);
87 
88 int  (*pith_opt_smime_get_passphrase)(void);
89 int  (*pith_smime_import_certificate)(char *, char *, char *, size_t);
90 int  (*pith_smime_enter_password)(char *, char *, size_t);
91 int  (*pith_smime_confirm_save)(char *);
92 
93 static X509_STORE   *s_cert_store;
94 
95 /* State management for randomness functions below */
96 static int seeded = 0;
97 
98 #ifdef PASSFILE
99 /*
100  * This code does not work in Windows, because of the PASSFILE thing, so
101  * I did not try to fix it. If you think it does need to be applied to
102  * the Windows version of alpine, there are more changes that are needed
103  * than fixing this function in this module. E. Chappa 09/28/17.
104  *
105  * load key from pathkeydir and cert from pathcertdir. It chooses the first
106  * key/certificate pair that matches. Delete pairs that you do not want used,
107  * if you do not want them selected. All parameters must be non-null.
108  * Memory freed by caller.
109  * Return values:
110  * -1 : user cancelled load
111  *  0 : load was successful
112  *  1 : there was an error in the loading.
113  */
114 int
load_key_and_cert(char * pathkeydir,char * pathcertdir,char ** keyfile,char ** certfile,EVP_PKEY ** pkey,X509 ** pcert)115 load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile,
116 		char **certfile, EVP_PKEY **pkey, X509 **pcert)
117 {
118    char buf[MAXPATH+1], pathkey[MAXPATH+1], prompt[MAILTMPLEN];
119    DIR *dirp;
120    struct dirent *d;
121    int b = 0, ret = 1; /* assume error */
122 
123    if(pathkeydir == NULL || pathcertdir == NULL || keyfile == NULL
124 	|| pkey == NULL	|| certfile == NULL || pcert == NULL)
125      return 1;
126 
127    *keyfile = NULL;
128    *certfile = NULL;
129    *pkey = NULL;
130    *pcert = NULL;
131 
132    if((dirp = opendir(pathkeydir)) != NULL){
133       while(b == 0 && (d=readdir(dirp)) != NULL){
134 	size_t ll;
135 
136         if((ll=strlen(d->d_name)) && ll > 4){
137 	   if(!strcmp(d->d_name+ll-4, ".key")){
138               strncpy(buf, d->d_name, sizeof(buf));
139               buf[sizeof(buf)-1] = '\0';
140 	      build_path(pathkey, pathkeydir, buf, sizeof(pathkey));
141  	      buf[strlen(buf)-4] = '\0';
142 	      snprintf(prompt, sizeof(prompt),
143 		_("Enter password of key <%s> to unlock password file: "), buf);
144 	      if((*pkey = load_pkey_with_prompt(pathkey, NULL, prompt, &ret)) != NULL){
145 		if(load_cert_for_key(pathcertdir, *pkey, certfile, pcert)){
146 		  b = 1;	/* break */
147 		  *keyfile = cpystr(buf);
148 		} else {
149 		  EVP_PKEY_free(*pkey);
150 		  *pkey = NULL;
151 		  q_status_message1(SM_ORDER, 0, 2,
152 		     _("Cannot find certificate that matches key <%s>. Continuing..."), buf);
153 		}
154 	      }
155 	   }
156 	}
157       }
158       closedir(dirp);
159    }
160    return ret;
161 }
162 
163 
164 /* setup a key and certificate to encrypt and decrypt a password file.
165  * These files will be saved in the .alpine-smime/.pwd directory, but its
166  * location can be setup in the command line with the -pwdcertdir option.
167  * Here are the rules:
168  *
169  *  Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
170  *  if not create it. If we are successful, move to the next step
171  *
172  *  - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
173  *    setup is successful;
174  *  - if the user does not have a key/cert pair, look to see if
175  *    ps_global->smime->personal_certs is already setup, if so, use it.
176  *  - if ps_global->smime->personal_certs is not set up, see if we can
177  *    find a certificate/cert pair in the default locations at compilation
178  *    time. (~/.alpine-smime/private and ~/.alpine-smime/public
179  *  - if none of this is successful, create a key/certificate pair
180  *    (TODO: implement this)
181  *  - in any other case, setup is not successful.
182  *
183  *  If setup is successful, setup ps_global->pwdcert.
184  *  If any of this fails, ps_global->pwdcert will be null.
185  *  Ok, that should do it.
186  *
187  * return values: 0 - everything is normal
188  *		  1 - User could not unlock key or no key in directory.
189  *		  2 - User cancelled to create self signed certificate
190  *		 -1 - we do not know which directory to use
191  *		 -2 - "-pwdcertdir" was given by user, but directory does not exist
192  *		 -3 - "DF_PASSWORD_DIR" exists but it is not a directory!!??
193  * 		 -4 - we tried to create DF_PASSWORD_DIR but failed.
194  *		 -5 - password directory exists, but it is empty
195  *
196  */
197 int
setup_pwdcert(void ** pwdcert)198 setup_pwdcert(void **pwdcert)
199 {
200   int rv;
201   int we_inited = 0;
202   int setup_dir = 0;	/* make it non zero if we know which dir to use */
203   struct stat sbuf;
204   char pathdir[MAXPATH+1], pathkey[MAXPATH+1], fpath[MAXPATH+1], pathcert[MAXPATH+1];
205   char fpath2[MAXPATH+1], prompt[MAILTMPLEN];
206   char *keyfile, *certfile, *text = NULL;
207   EVP_PKEY *pkey = NULL;
208   X509 *pcert = NULL;
209   PERSONAL_CERT *pc, *pc2 = NULL;
210   static int was_here = 0;
211 
212   if(pwdcert == NULL || was_here == 1)
213     return -1;
214 
215   was_here++;
216   if(ps_global->pwdcertdir){
217      if(our_stat(ps_global->pwdcertdir, &sbuf) == 0
218 	&& ((sbuf.st_mode & S_IFMT) == S_IFDIR)){
219 	setup_dir++;
220 	strncpy(pathdir, ps_global->pwdcertdir, sizeof(pathdir));
221 	pathdir[sizeof(pathdir)-1] = '\0';
222      }
223      else rv = -2;
224   } else {
225       smime_path(DF_PASSWORD_DIR, pathdir, sizeof(pathdir));
226       if(our_stat(pathdir, &sbuf) == 0){
227 	if((sbuf.st_mode & S_IFMT) == S_IFDIR)
228 	  setup_dir++;
229 	else rv = -3;
230       } else if(can_access(pathdir, ACCESS_EXISTS) != 0
231 	    && our_mkpath(pathdir, 0700) == 0)
232 	  setup_dir++;
233 	else rv = -4;
234   }
235 
236   if(setup_dir == 0){
237     was_here = 0;
238     return rv;
239   }
240 
241   if(load_key_and_cert(pathdir, pathdir, &keyfile, &certfile, &pkey, &pcert) < 0){
242     was_here = 0;
243     return 1;
244   }
245 
246   if(ps_global->pwdcertdir == NULL){	/* save the result of pwdcertdir */
247     ps_global->pwdcertdir = cpystr(pathdir);
248     /* if the user gave a pwdcertdir and there is nothing there, do not
249      * continue. Let the user initialize on their own this directory.
250      */
251     if(certfile == NULL || keyfile == NULL){
252       was_here = 0;
253       return -5;
254     }
255   }
256 
257   if(certfile && keyfile){
258      pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
259      memset((void *)pc, 0, sizeof(PERSONAL_CERT));
260      pc->name = keyfile;
261      pc->key  = pkey;
262      pc->cert = pcert;
263      pc->cname = certfile;
264      *pwdcert = (void *) pc;
265      was_here = 0;
266      return 0;
267   }
268 
269   /* look to see if there are any certificates lying around, first
270    * we try to load ps_global->smime to see if that has information
271    * we can use. If we are the process filling the smime structure
272    * we deinit at the end, since this might not do a full init.
273    */
274   if(ps_global && ps_global->smime && !ps_global->smime->inited){
275      we_inited++;
276      smime_init();
277   }
278 
279   /* at this point ps_global->smime->inited == 1 */
280   if(ps_global->smime && ps_global->smime->personal_certs != NULL){
281     pc = (PERSONAL_CERT *) ps_global->smime->personal_certs;
282     if(ps_global->smime->privatetype == Directory){
283 	 build_path(pathkey, ps_global->smime->privatepath, pc->name, sizeof(pathkey));
284 	 strncat(pathkey, ".key", 5);
285 	 pathkey[sizeof(pathkey)-1] = '\0';
286 	 text = NULL;
287     } else if (ps_global->smime->privatetype == Container){
288 	 if(pc->keytext == NULL){	/* we should *never* be here, but just in case */
289 	   if(ps_global->smime->privatecontent != NULL){
290 	     char tmp[MAILTMPLEN], *s, *t, c;
291 	     snprintf(tmp, sizeof(tmp), "%s%s", EMAILADDRLEADER, pc->name);
292 	     tmp[sizeof(tmp)-1] = '\0';
293 	     if((s = strstr(ps_global->smime->privatecontent, tmp)) != NULL){
294 		if((t = strstr(s+strlen(tmp), EMAILADDRLEADER)) != NULL){
295 		   c = *t;
296 		   *t = '\0';
297 		   pc->keytext = cpystr(s + strlen(tmp) + strlen(NEWLINE));
298 		   *t = c;
299 	        }
300 		else
301 		   pc->keytext = cpystr(s + strlen(tmp) + strlen(NEWLINE));
302 	     }
303 	   }
304 	 }
305 	 if(pc->keytext != NULL)	/* we should go straight here */
306 	   text = pc->keytext;
307     } else if (ps_global->smime->privatetype == Keychain){
308 	   pathkey[0] = '\0';	/* no apple key chain support yet */
309 	   text = NULL;
310     }
311     if((pathkey && *pathkey) || text){
312       snprintf(prompt, sizeof(prompt),
313 	_("Enter password of key <%s> to unlock password file: "), pc->name);
314 
315       if((pkey = load_pkey_with_prompt(pathkey, text, prompt, NULL)) != NULL){
316 	 pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
317 	 memset((void *)pc2, 0, sizeof(PERSONAL_CERT));
318 	 pc2->name = cpystr(pc->name);
319 	 pc2->key  = pkey;
320 	 pc2->cert = X509_dup(pc->cert);
321 
322 	 /* now copy the keys and certs, starting by the key...  */
323 	 build_path(fpath, pathdir, pc->name, sizeof(fpath));
324 	 strncat(fpath, ".key", 5);
325 	 fpath[sizeof(fpath)-1] = '\0';
326 	 if(our_stat(fpath, &sbuf) == 0){	/* if fpath exists */
327 	     if((sbuf.st_mode & S_IFMT) == S_IFREG) /* and is a regular file */
328 	       setup_dir++;			/* we are done */
329 	 } else if(ps_global->smime->privatetype == Directory){
330 		   if(our_copy(fpath, pathkey) == 0)
331 		     setup_dir++;
332 	 } else if(ps_global->smime->privatetype == Container){
333 		     BIO *out;
334 		     if((out = BIO_new_file(fpath, "w")) != NULL){
335 			if(BIO_puts(out, pc->keytext) > 0)
336 			   setup_dir++;
337 		        BIO_free(out);
338 		     }
339 	 } else if(ps_global->smime->privatetype == Keychain){
340 			/* add support for Apple Mac OS X */
341 	 }
342       }
343 
344 	/* successful copy of key, now continue with certificate */
345       if(setup_dir){
346 	setup_dir = 0;
347 
348 	build_path(pathkey, ps_global->smime->publicpath, pc->name, sizeof(pathkey));
349 	strncat(pathkey, ".crt", 5);
350 	pathkey[sizeof(pathkey)-1] = '\0';
351 
352 	build_path(fpath, pathdir, pc->name, sizeof(fpath));
353 	strncat(fpath, ".crt", 5);
354 	fpath[sizeof(fpath)-1] = '\0';
355 
356 	if(our_stat(fpath, &sbuf) == 0){
357 	   if((sbuf.st_mode & S_IFMT) == S_IFREG)
358 	      setup_dir++;
359 	}
360 	else if(ps_global->smime->privatetype == Directory){
361 		if(our_copy(fpath, pathkey) == 0)
362 		   setup_dir++;
363 	} else if(ps_global->smime->privatetype == Container) {
364 		  BIO *out;
365 		  if((out = BIO_new_file(fpath, "w")) != NULL){
366 		     if(PEM_write_bio_X509(out, pc->cert))
367 			setup_dir++;
368 			   BIO_free(out);
369 		  }
370 	} else if (ps_global->smime->privatetype == Keychain) {
371 			/* add support for Mac OS X */
372 	}
373       }
374 
375       if(setup_dir){
376 	*pwdcert = (void *) pc2;
377 	was_here = 0;
378 	return 0;
379       }
380       else if(pc2 != NULL)
381 	free_personal_certs(&pc2);
382     }		/* if (pathkey...) */
383   }		/* if(ps_global->smime->personal_certs) */
384 
385 
386   if(setup_dir == 0){
387      /* PATHCERTDIR(Private) must be null, so create a path */
388      set_current_val(&ps_global->vars[V_PRIVATEKEY_DIR], TRUE, TRUE);
389      smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, sizeof(pathkey));
390 
391      /* PATHCERTDIR(Public) must be null, so create a path */
392      set_current_val(&ps_global->vars[V_PUBLICCERT_DIR], TRUE, TRUE);
393      smime_path(ps_global->VAR_PUBLICCERT_DIR, pathcert, sizeof(pathcert));
394 
395      /* BUG: this does not support local containers */
396      load_key_and_cert(pathkey, pathcert, &keyfile, &certfile, &pkey, &pcert);
397 
398      if(certfile && keyfile){
399 	build_path(fpath, pathdir, keyfile, sizeof(fpath));
400 	strncat(fpath, ".key", 5);
401 	fpath[sizeof(fpath)-1] = '\0';
402 
403 	build_path(fpath2, pathkey, keyfile, sizeof(fpath));
404 	strncat(fpath2, ".key", 5);
405 	fpath2[sizeof(fpath2)-1] = '\0';
406 
407 	if(our_copy(fpath, fpath2) == 0)
408 	   setup_dir++;
409 
410 	if(setup_dir){
411 	  setup_dir = 0;
412 
413 	  build_path(fpath, pathdir, certfile, sizeof(fpath));
414 	  build_path(fpath2, pathcert, certfile, sizeof(fpath2));
415 
416 	  if(our_copy(fpath, fpath2) == 0)
417 	     setup_dir++;
418 	}
419      }
420   }
421 
422   if(keyfile && certfile){
423      pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
424      memset((void *)pc, 0, sizeof(PERSONAL_CERT));
425      pc->name = keyfile;
426      pc->key  = pkey;
427      pc->cert = pcert;
428      *pwdcert = (void *) pc;
429      fs_give((void **)&certfile);
430      was_here = 0;
431      return 0;
432   }
433 
434   was_here = 0;
435   if(we_inited)
436     smime_deinit();
437   return 0;
438 }
439 #endif /* PASSFILE */
440 
441 /* smime_expunge_cert.
442  * Return values: < 0 there was an error.
443  *                >=0 the number of messages expunged
444  */
445 int
smime_expunge_cert(WhichCerts ctype)446 smime_expunge_cert(WhichCerts ctype)
447 {
448   int count = 0, removed;
449   CertList *cl, *dummy, *data;
450   char *path, buf[MAXPATH+1];
451   char *contents;
452 
453   if(DATACERT(ctype)== NULL)
454     return -1;
455 
456   /* data cert is the way we unify certificate management across
457    * functions, but it is not where we really save the information in the
458    * case ctype is equal to Private. What we will do is to update the
459    * datacert, and in the case of ctype equal to Private use the updated
460    * certdata to update the personal_certs data.
461    */
462 
463   path = PATHCERTDIR(ctype);
464 
465   if(path){
466     /* add a fake certificate at the beginning of the list */
467     dummy = fs_get(sizeof(CertList));
468     memset((void *)dummy, 0, sizeof(CertList));
469     dummy->next = DATACERT(ctype);
470 
471     for(cl = dummy, count = 0; cl && cl->next;){
472 	if(cl->next->data.deleted == 0){
473 	   cl = cl->next;
474 	   continue;
475 	}
476 
477 	removed = 1;		/* assume success */
478 	if(SMHOLDERTYPE(ctype) == Directory){
479 	  build_path(buf, path, cl->next->name, sizeof(buf));
480 	  if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf)){
481 	    strncat(buf, EXTCERT(Private), 5);
482 	    buf[sizeof(buf)-1] = '\0';
483 	  }
484 
485 	  if(our_unlink(buf) < 0){
486 	     q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
487 	     cl = cl->next;
488 	     removed = 0;
489 	  }
490 	}
491 	else if(SMHOLDERTYPE(ctype) == Container){
492 	  char *prefix= ctype == CACert ? CACERTSTORELEADER : EMAILADDRLEADER;
493 	  char tmp[MAILTMPLEN], *s, *t;
494 
495 	  contents = CONTENTCERTLIST(ctype);
496 	  snprintf(tmp, sizeof(tmp), "%s%s", prefix, cl->next->name);
497 	  tmp[sizeof(tmp) - 1] = '\0';
498 	  if((s = strstr(contents, tmp)) != NULL){
499 	     if((t = strstr(s+strlen(tmp), prefix)) == NULL)
500 		*s = '\0';
501 	     else
502 		memmove(s, t, strlen(t)+1);
503 	     fs_resize((void **)&contents, strlen(contents)+1);
504 	     switch(ctype){
505 		case Private: ps_global->smime->privatecontent = contents; break;
506 		case Public : ps_global->smime->publiccontent = contents; break;
507 		case CACert : ps_global->smime->cacontent = contents; break;
508 		default  : break;
509 	     }
510 	  }
511 	  else
512 	     removed = 0;
513 	} else { /* unhandled case */
514 	}
515 
516  	if(removed > 0){
517 	   count++;	/* count it! */
518 	   data = cl->next;
519 	   cl->next = data->next;
520 	   if(data->name) fs_give((void **)&data->name);
521 	   fs_give((void **)&data);
522 	}
523     }
524   } else
525 	q_status_message(SM_ORDER, 3, 3, _("Error expunging certificate"));
526 
527   switch(ctype){
528      case Private: ps_global->smime->privatecertlist = dummy->next; break;
529      case Public : ps_global->smime->publiccertlist = dummy->next; break;
530      case CACert : ps_global->smime->cacertlist = dummy->next; break;
531 	default  : break;
532   }
533   fs_give((void **)&dummy);
534   if(SMHOLDERTYPE(ctype) == Container){
535     if(copy_dir_to_container(ctype, contents) < 0)
536       count = 0;
537   }
538   if(count > 0){
539     q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
540   }
541   else
542     q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
543   return count;
544 }
545 
546 void
mark_cert_deleted(WhichCerts ctype,int num,unsigned state)547 mark_cert_deleted(WhichCerts ctype, int num, unsigned state)
548 {
549   CertList *cl;
550   int i;
551 
552   for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
553   cl->data.deleted = state;
554 }
555 
556 unsigned
get_cert_deleted(WhichCerts ctype,int num)557 get_cert_deleted(WhichCerts ctype, int num)
558 {
559   CertList *cl;
560   int i;
561 
562   for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
563   return (cl && cl->data.deleted) ? 1 : 0;
564 }
565 
566 EVP_PKEY *
load_pkey_with_prompt(char * fpath,char * text,char * prompt,int * ret)567 load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *ret)
568 {
569   EVP_PKEY *pkey;
570   int rc = 0;   /* rc == 1, cancel, rc == 0 success */
571   char pass[MAILTMPLEN+1];
572   BIO *in;
573 
574   /* attempt to load with empty password */
575   in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
576   if(in != NULL){
577      pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, "");
578      if(pkey != NULL) return pkey;
579   } else return NULL;
580 
581   if(pith_smime_enter_password)
582     while(pkey == NULL && rc != 1){
583 	do {
584 	   rc = (*pith_smime_enter_password)(prompt, (char *)pass, sizeof(pass));
585 	 } while (rc!=0 && rc!=1 && rc>0);
586 
587 	 (void) BIO_reset(in);
588 	 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (char *)pass);
589     }
590 
591   BIO_free(in);
592 
593   if(ret) *ret = rc == 1 ? -1 : pkey != NULL ? 0 : 1;
594   return pkey;
595 }
596 
597 /* This is a tool for conf_screen, The return value must be zero when
598  * nothing changed, so if there is a failure in the import return 0
599  * and return 1 when we succeeded.\
600  * We call this function in two ways:
601  * either fname is null or not. If they fname is null, so is p_cert.
602  * if p_cert is not null, it is the PERSONAL_CERT structure of fname if this
603  * is available, otherwise we will fill it up here.
604  */
605 int
import_certificate(WhichCerts ctype,PERSONAL_CERT * p_cert,char * fname)606 import_certificate(WhichCerts ctype, PERSONAL_CERT *p_cert, char *fname)
607 {
608    int   r = 1, rc;
609    char  filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
610    char *what;
611 
612    if(pith_smime_import_certificate == NULL
613 	|| pith_smime_enter_password == NULL){
614      q_status_message(SM_ORDER, 0, 2,
615                       _("import of certificates not implemented yet!"));
616      return 0;
617   }
618 
619    if(fname == NULL){
620       what = ctype == Public || ctype == CACert ? "certificate" : "key";
621       r = (*pith_smime_import_certificate)(filename, full_filename, what, sizeof(filename) - 20);
622 
623      if(r < 0)
624        return 0;
625    } else {
626      char *s;
627      strncpy(full_filename, fname, sizeof(full_filename));
628      if((s = strrchr(full_filename, '/')) != NULL)
629        strncpy(filename, s+1, sizeof(filename));
630    }
631 
632    /* we are trying to import a new key for the password file. First we ask for the
633     * private key. Once this is loaded, we make a reasonable attempt to find the
634     * public key in the same directory as the key was loaded from. We do this by
635     * looking for a file with the correct public certificate name, then we look
636     * in the same private key, and if not, we ask the user for its location. If all
637     * of this works, we import the key and public to the password directory.
638     */
639 #ifdef PASSFILE
640    if(ctype == Password){
641      char PrivateKeyPath[MAXPATH+1], PublicCertPath[MAXPATH+1], s[MAXPATH+1];
642      char full_name_key[MAXPATH+1], full_name_cert[MAXPATH+1];
643      char *use_this_file = NULL;
644      char prompt[500];
645      EVP_PKEY *key = p_cert ? p_cert->key : NULL;
646 
647      rc = 1;	/* assume success :) */
648      if(strlen(filename) > 4){
649 	strncpy(s, filename, sizeof(s));
650 	s[sizeof(s)-1] = '\0';
651 	if(!strcmp(s + strlen(s) - strlen(EXTCERT(Private)), EXTCERT(Private)))
652 	  s[strlen(s) - strlen(EXTCERT(Private))] = '\0';
653 	else
654 	  rc = 0;
655      } else rc = 0;
656 
657      if(rc == 0){
658 	q_status_message(SM_ORDER, 1, 3, _("Error in key name. Check file extension"));
659 	return 0;
660      }
661 
662      snprintf(prompt, sizeof(prompt), _("Enter passphrase to unlock new key <%s>: "), filename);
663      prompt[sizeof(prompt)-1] = '\0';
664      if(key != NULL
665 	|| (key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
666 	BIO *ins = NULL;
667 	X509 *cert = p_cert ? p_cert->cert : NULL, *cert2;
668 
669 	strncpy(full_name_key, full_filename, sizeof(full_filename));
670         full_name_key[sizeof(full_name_key)-1] = '\0';
671 
672 	build_path(buf, PATHCERTDIR(ctype), s, sizeof(buf));
673 
674 	strncpy(PrivateKeyPath, buf, sizeof(PrivateKeyPath));
675 	PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
676 	if(strlen(PrivateKeyPath) + 4 < sizeof(PrivateKeyPath)){
677 	   strncat(PrivateKeyPath, EXTCERT(Private), 5);
678 	   PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
679 	}
680 
681 	/* remove .key extension and replace it with .crt extension */
682 	strncpy(full_name_cert, full_name_key, sizeof(full_name_key));
683 	full_name_cert[sizeof(full_name_cert)-1] = '\0';
684 	full_name_cert[strlen(full_name_cert) - strlen(EXTCERT(Private))] = '\0';
685 	strncat(full_name_cert, EXTCERT(Public), 5);
686 	full_name_cert[sizeof(full_name_cert)-1] = '\0';
687 
688 
689 	/* set up path to location where we will save public cert */
690 	strncpy(PublicCertPath, buf, sizeof(PublicCertPath));
691 	PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
692 	if(strlen(PublicCertPath) + 4 < sizeof(PublicCertPath)){
693 	  strncat(PublicCertPath, EXTCERT(Public), 5);
694 	  PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
695 	}
696 	/* attempt #1, use provided certificate,
697 	 * assumption is that full_name_cert is the file that this
698 	 * certificate derives from (which is obtained by substitution
699 	 * of .key extension in key by .crt extension)
700 	 */
701 	if(cert != NULL)  /* attempt #1 */
702 	    use_this_file = &full_name_cert[0];
703 	else if((ins = BIO_new_file(full_name_cert, "r")) != NULL){
704 	/* attempt #2 to guess public cert name, use .crt extension */
705 	    if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
706 	    use_this_file = &full_name_cert[0];
707 	    }
708 	}
709 	else{ /* attempt #3 to guess public cert name: use the original key */
710 	  if((ins = BIO_new_file(full_name_key, "r")) != NULL){
711 	    if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
712 	       use_this_file = &full_name_key[0];
713 	    }
714 	  }
715 	  else {
716 	   int done = 0;
717 	    /* attempt #4, ask the user */
718 	   do {
719 	      r = (*pith_smime_import_certificate)(filename, use_this_file, "certificate", sizeof(filename) - 20);
720 	      if(r < 0){
721 		 if(ins != NULL) BIO_free(ins);
722 		 if(cert != NULL) X509_free(cert);
723 		 return 0;
724 	      }
725 	      if((ins = BIO_new_file(use_this_file, "r")) != NULL){
726 		if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL)
727 		  done++;
728 		else
729 		  q_status_message(SM_ORDER, 1, 3, _("Error parsing certificate"));
730 	      }
731 	      else
732 		  q_status_message(SM_ORDER, 1, 3, _("Error reading certificate"));
733 	   } while (done == 0);
734 	  }
735 	}
736 	if(ins != NULL){
737 	   if(cert != NULL){	/* check that certificate matches key */
738 	      if(!X509_check_private_key(cert, key)){
739 		rc = 0;
740 		q_status_message(SM_ORDER, 1, 3, _("Certificate does not match key"));
741 	      }
742 	      else
743 		 rc = 1;	/* Success! */
744 	   }
745 	   else
746 	     q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
747 	}
748 	if(rc == 1){	/* if everything has been successful,
749 			 * copy the files to their final destination */
750 	   if(our_copy(PrivateKeyPath, full_filename) == 0){	/* <-- save the private key */
751 	      q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
752 	      if(our_copy(PublicCertPath, use_this_file) == 0){
753 		char  tmp[MAILTMPLEN];
754 	        FILE *fp;
755 
756 		if(!passfile_name(ps_global->pinerc, tmp, sizeof(tmp))
757 			|| !(fp = our_fopen(tmp, "rb"))){
758 		   q_status_message(SM_ORDER, 1, 3, _("Error reading password file!"));
759 		   rc = 0;
760 	        }
761 		else {
762 		   char tmp2[MAILTMPLEN];
763 		   int encrypted = 0;
764 		   char *text;
765 		   PERSONAL_CERT *pwdcert, *pc = p_cert;
766 
767 		   pwdcert = (PERSONAL_CERT *) ps_global->pwdcert;
768 		   if(pwdcert == NULL)
769 		      setup_pwdcert((void **)&pwdcert);
770 
771 		   tmp2[0] = '\0';
772 		   fgets(tmp2, sizeof(tmp2), fp);
773 		   fclose(fp);
774 		   if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){
775 	              if(encrypt_file((char *)tmp, NULL, pwdcert))
776 	                 encrypted++;
777 		   }
778 		   else
779 		     encrypted++;
780 
781 		   if(encrypted){
782 		     text = decrypt_file((char *)tmp, NULL, pwdcert);
783 		     if(text != NULL){
784 			if(pc == NULL){
785 			   filename[strlen(filename)-strlen(EXTCERT(Private))] = '\0';
786 			   if(strlen(filename) + strlen(EXTCERT(Public)) < MAXPATH){
787 			      pc = fs_get(sizeof(PERSONAL_CERT));
788 			      memset((void *)pc, 0, sizeof(PERSONAL_CERT));
789 			      pc->name = cpystr(filename);
790 			      pc->cname = fs_get(strlen(filename) + strlen(EXTCERT(Public)) + 1);
791 			      sprintf(pc->cname, "%s%s", filename, EXTCERT(Public));
792 			      pc->key  = key;
793 			      pc->cert = cert;
794 			   }
795 			}
796 
797 			if(encrypt_file((char *)tmp, text, pc)){ /* we did it! */
798 			   build_path(buf, PATHCERTDIR(ctype), pwdcert->name, sizeof(buf));
799 			   strncat(buf, EXTCERT(Private), 5);
800 			   buf[sizeof(buf)-1] = '\0';
801 			   if(strcmp(PrivateKeyPath, buf)){
802 			      if (unlink(buf) < 0)
803 				q_status_message(SM_ORDER, 1, 3, _("Failed to remove old key"));
804 			   }
805 			   build_path(buf, PATHCERTDIR(ctype), pwdcert->cname, sizeof(buf));
806 			   if(strcmp(PublicCertPath, buf)){
807 			      if(unlink(buf) < 0)
808 				q_status_message(SM_ORDER, 1, 3, _("Failed to remove old certificate"));
809 			   }
810 			   free_personal_certs((PERSONAL_CERT **)&ps_global->pwdcert);
811 			   ps_global->pwdcert = pc;
812 			   rc = 1;
813 			   q_status_message(SM_ORDER, 1, 3, _("Password file reencrypted"));
814 			} else {
815 			   q_status_message(SM_ORDER, 1, 3, _("Failed to reencrypt password file"));
816 			   rc = 0;
817 			}
818 		     } else {
819 		        q_status_message(SM_ORDER, 1, 3, _("Error decrypting Password file"));
820 		     }
821 		   } else {
822 		     q_status_message(SM_ORDER, 1, 3, _("Password file not encrypted and could not encrypt"));
823 		     rc = 0;
824 		   }
825 		}
826 	      }
827 	      else{
828 	        q_status_message(SM_ORDER, 1, 3, _("Error saving public certificate"));
829 		if(our_unlink(PrivateKeyPath) < 0)
830 		   q_status_message(SM_ORDER, 1, 3, _("Error while cleaning private key"));
831 	        rc = 0;
832 	      }
833 	   }
834 	   else{
835 	      rc = 0;
836 	      q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
837 	   }
838 	   if(ins != NULL) BIO_free(ins);
839 	   if(rc == 0 && cert != NULL) X509_free(cert);
840 	}
841      } else {
842 	rc = 0;
843 	q_status_message(SM_ORDER, 1, 3, _("Error unlocking private key"));
844      }
845 
846      return rc;
847    }
848 #endif /* PASSFILE */
849 
850    smime_init();
851    ps_global->mangled_screen = 1;
852 
853    if (ctype == Private){
854 	char prompt[500], *s, *t;
855 	EVP_PKEY *key = NULL;
856 
857 	if(!ps_global->smime->privatecertlist){
858 	  ps_global->smime->privatecertlist = fs_get(sizeof(CertList));
859 	  memset((void *) ps_global->smime->privatecertlist, 0, sizeof(CertList));
860 	}
861 
862 	for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
863 	if(s) *(s-1) = 0;
864 
865 	snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
866 	prompt[sizeof(prompt)-1] = '\0';
867         if((key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
868 	  if(SMHOLDERTYPE(ctype) == Directory){
869 	    build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
870 	    if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf)){
871 	       strncat(buf, EXTCERT(ctype), 5);
872 	       buf[sizeof(buf)-1] = '\0';
873 	    }
874 	    rc = our_copy(buf, full_filename);
875 	  }
876 	  else /* if(SMHOLDERTYPE(ctype) == Container){ */
877 	     rc = add_file_to_container(ctype, full_filename, NULL);
878 	  if(rc == 0)
879 	     q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
880 	  else
881 	     q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
882 	  if(ps_global->smime->publiccertlist)
883 	     ps_global->smime->publiccertlist->data.renew = 1;
884 	}
885 	else
886 	  q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate or wrong password)"));
887    } else  if (ctype == CACert){
888       BIO *ins;
889       X509 *cert;
890 
891       if((ins = BIO_new_file(full_filename, "r")) != NULL){
892 	if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
893 	  if(SMHOLDERTYPE(ctype) == Directory){
894 	    build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
895 	    if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf)){
896 	       strncat(buf, EXTCERT(ctype), 5);
897 	       buf[sizeof(buf)-1] = '\0';
898 	    }
899 
900 	    rc = our_copy(buf, full_filename);
901 	  }
902 	  else /* if(SMHOLDERTYPE(ctype) == Container){ */
903 	     rc = add_file_to_container(ctype, full_filename, NULL);
904 	  if(rc == 0)
905 	      q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
906 	  else
907 	      q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
908 	  X509_free(cert);	/* not needed anymore */
909 	}
910 	else
911 	  q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
912 	BIO_free(ins);
913       }
914       renew_store();
915    } else { /* ctype == Public. save certificate, but first validate that it is one */
916       BIO *ins;
917       X509 *cert;
918 
919       if((ins = BIO_new_file(full_filename, "r")) != NULL){
920 	if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
921 	  if(SMHOLDERTYPE(ctype) == Directory){
922 	    char **email;
923 
924 	    if((email = get_x509_subject_email(cert)) != NULL){
925 	       int i;
926 	       for(i = 0; email[i] != NULL; i++){
927 		  save_cert_for(email[i], cert, Public);
928 		  fs_give((void **)&email[i]);
929 	       }
930 	       fs_give((void **)email);
931 	    }
932 	    if(strcmp(filename + strlen(filename) - 4, ".crt") == 0)
933 	       filename[strlen(filename) - 4] = '\0';
934 	    save_cert_for(filename, cert, Public);
935 	  }
936 	  else  /* if(SMHOLDERTYPE(ctype) == Container){ */
937 	     add_file_to_container(ctype, full_filename, NULL);
938 	  X509_free(cert);
939 	  if(ps_global->smime->publiccertlist)
940 	     ps_global->smime->publiccertlist->data.renew = 1;
941 	}
942 	else
943 	  q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
944 	BIO_free(ins);
945       }
946    }
947    if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
948    return 1;
949 }
950 
951 /* itype: information type to add: 0 - public, 1 - private.
952  * Memory freed by caller
953  */
954 BIO *
print_private_key_information(char * email,int itype)955 print_private_key_information(char *email, int itype)
956 {
957   BIO *out;
958   PERSONAL_CERT *pc;
959 
960   if(ps_global->smime == NULL
961 	|| ps_global->smime->personal_certs == NULL
962 	|| (itype != 0 && itype != 1))
963     return NULL;
964 
965   for(pc = ps_global->smime->personal_certs;
966         pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
967   if(pc->key == NULL
968 	&& !load_private_key(pc)
969 	&& ps_global->smime
970 	&& ps_global->smime->need_passphrase){
971 	if (pith_opt_smime_get_passphrase)
972 	   (*pith_opt_smime_get_passphrase)();
973 	load_private_key(pc);
974   }
975 
976   if(pc->key == NULL)
977      return NULL;
978 
979   out = BIO_new(BIO_s_mem());
980   if(itype == 0)		/* 0 means public */
981     EVP_PKEY_print_public(out, pc->key, 0, NULL);
982   else if (itype == 1)		/* 1 means private */
983     EVP_PKEY_print_private(out, pc->key, 0, NULL);
984 
985   if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
986     forget_private_keys();
987 
988   return out;
989 }
990 
991 /*
992  * Forget any cached private keys
993  */
994 static void
forget_private_keys(void)995 forget_private_keys(void)
996 {
997     PERSONAL_CERT *pcert;
998     size_t len;
999     volatile char *p;
1000 
1001     dprint((9, "forget_private_keys()"));
1002     if(ps_global->smime){
1003 	ps_global->smime->already_auto_asked = 0;
1004 	for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
1005 	    pcert;
1006 	    pcert=pcert->next){
1007 
1008 	    if(pcert->key){
1009 		EVP_PKEY_free(pcert->key);
1010 		pcert->key = NULL;
1011 	    }
1012 	}
1013 
1014 	ps_global->smime->entered_passphrase = 0;
1015 	len = sizeof(ps_global->smime->passphrase);
1016 	p = ps_global->smime->passphrase;
1017 
1018 	while(len-- > 0)
1019 	  *p++ = '\0';
1020     }
1021 }
1022 
1023 /* modelled after signature_path in reply.c, but uses home dir instead of the
1024  * directory where the .pinerc is located, since according to documentation,
1025  * the .alpine-smime directories are subdirectories of the home directory
1026  */
1027 int
smime_path(char * rpath,char * fpath,size_t len)1028 smime_path(char *rpath, char *fpath, size_t len)
1029 {
1030     *fpath = '\0';
1031     if(rpath && *rpath){
1032         size_t spl = strlen(rpath);
1033 
1034         if(IS_REMOTE(rpath)){
1035             if(spl < len - 1)
1036               strncpy(fpath, rpath, len-1);
1037             fpath[len-1] = '\0';
1038         }
1039         else if(is_absolute_path(rpath)){
1040             strncpy(fpath, rpath, len-1);
1041             fpath[len-1] = '\0';
1042             fnexpand(fpath, len);
1043         }
1044         else if(ps_global->VAR_OPER_DIR){
1045             if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
1046               build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
1047         }
1048 	else if(ps_global->home_dir){
1049             if(strlen(ps_global->home_dir) + spl < len - 1)
1050               build_path(fpath, ps_global->home_dir, rpath, len);
1051 	}
1052     }
1053     return fpath && *fpath ? 1 : 0;
1054 }
1055 
1056 
1057 
1058 /*
1059  * taken from openssl/apps/app_rand.c
1060  */
1061 static int
app_RAND_load_file(const char * file)1062 app_RAND_load_file(const char *file)
1063 {
1064 #define RANDBUFLEN 200
1065     char buffer[RANDBUFLEN];
1066 
1067     if(file == NULL)
1068       file = RAND_file_name(buffer, RANDBUFLEN);
1069 
1070     if(file == NULL || !RAND_load_file(file, -1)){
1071 	if(RAND_status() == 0){
1072 	    dprint((1, "unable to load 'random state'\n"));
1073 	    dprint((1, "This means that the random number generator has not been seeded\n"));
1074 	    dprint((1, "with much random data.\n"));
1075 	}
1076 
1077 	return 0;
1078     }
1079 
1080     seeded = 1;
1081     return 1;
1082 }
1083 
1084 
1085 /*
1086  * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
1087  */
1088 static void
openssl_extra_randomness(void)1089 openssl_extra_randomness(void)
1090 {
1091 #if !defined(WIN32)
1092     int fd;
1093     unsigned long i;
1094     char *tf = NULL;
1095     char tmp[MAXPATH];
1096     struct stat sbuf;
1097 				/* if system doesn't have /dev/urandom */
1098     if(stat ("/dev/urandom", &sbuf)){
1099       tmp[0] = '0';
1100       tf = temp_nam(NULL, NULL);
1101       if(tf){
1102 	strncpy(tmp, tf, sizeof(tmp));
1103 	tmp[sizeof(tmp)-1] = '\0';
1104 	fs_give((void **) &tf);
1105       }
1106 
1107       if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
1108 	i = (unsigned long) tmp;
1109       else{
1110 	unlink(tmp);		/* don't need the file */
1111 	fstat(fd, &sbuf);	/* get information about the file */
1112 	i = sbuf.st_ino;	/* remember its inode */
1113 	close(fd);		/* or its descriptor */
1114       }
1115 				/* not great but it'll have to do */
1116       snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
1117 	       tcp_serverhost (),i,
1118 	       (unsigned long) (time (0) ^ gethostid ()),
1119 	       (unsigned long) getpid ());
1120       RAND_seed(tmp, strlen(tmp));
1121     }
1122 #endif
1123 }
1124 
1125 
1126 /* taken from openssl/apps/app_rand.c */
1127 static int
app_RAND_write_file(const char * file)1128 app_RAND_write_file(const char *file)
1129 {
1130     char buffer[200];
1131 
1132     if(!seeded)
1133 	/*
1134 	 * If we did not manage to read the seed file,
1135 	 * we should not write a low-entropy seed file back --
1136 	 * it would suppress a crucial warning the next time
1137 	 * we want to use it.
1138 	 */
1139 	return 0;
1140 
1141     if(file == NULL)
1142       file = RAND_file_name(buffer, sizeof buffer);
1143 
1144     if(file == NULL || !RAND_write_file(file)){
1145 	dprint((1, "unable to write 'random state'\n"));
1146 	return 0;
1147     }
1148 
1149     return 1;
1150 }
1151 
1152 CertList *
certlist_from_personal_certs(PERSONAL_CERT * pc)1153 certlist_from_personal_certs(PERSONAL_CERT *pc)
1154 {
1155    CertList *cl = NULL;
1156    X509 *x;
1157 
1158    if(pc == NULL)
1159      return NULL;
1160 
1161    if((x = get_cert_for(pc->name, Public, 1)) != NULL){
1162      cl = smime_X509_to_cert_info(x, pc->name);
1163      cl->next = certlist_from_personal_certs(pc->next);
1164    }
1165 
1166    return cl;
1167 }
1168 
1169 void
renew_cert_data(CertList ** data,WhichCerts ctype)1170 renew_cert_data(CertList **data, WhichCerts ctype)
1171 {
1172   smime_init();
1173   if(ctype == Private){
1174      if(data){
1175 	PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
1176 	if(*data)
1177 	  free_certlist(data);
1178 	free_personal_certs(&pc);
1179 	setup_privatekey_storage();
1180         *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
1181 	if(data && *data){
1182 	    resort_certificates(data, ctype);
1183 	    RENEWCERT(*data) = 0;
1184 	}
1185         ps_global->smime->privatecertlist = *data;
1186      }
1187      if(ps_global->smime->privatecertlist)
1188        RENEWCERT(ps_global->smime->privatecertlist) = 0;
1189   } else {
1190     X509_LOOKUP    *lookup = NULL;
1191     X509_STORE     *store = NULL;
1192 
1193     if((store = X509_STORE_new()) != NULL){
1194        if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) != NULL){
1195  	  free_certlist(data);
1196 	  if(SMHOLDERTYPE(ctype) == Directory)
1197 	    add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
1198 	  else /* if(SMHOLDERTYPE(ctype) == Container) */
1199 	    *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
1200 	  if(data && *data){
1201 	    resort_certificates(data, ctype);
1202 	    RENEWCERT(*data) = 0;
1203 	  }
1204 	  if(ctype == Public)
1205 	    ps_global->smime->publiccertlist = *data;
1206 	  else
1207 	    ps_global->smime->cacertlist = *data;
1208        }
1209        free_x509_store(&store);
1210     }
1211   }
1212   setup_certs_backup_by_type(ctype);
1213 }
1214 
1215 void
smime_reinit(void)1216 smime_reinit(void)
1217 {
1218    smime_deinit();
1219    smime_init();
1220 }
1221 
1222 /* Installed as an atexit() handler to save the random data */
1223 void
smime_deinit(void)1224 smime_deinit(void)
1225 {
1226     dprint((9, "smime_deinit()"));
1227     app_RAND_write_file(NULL);
1228     if (s_cert_store != NULL) free_x509_store(&s_cert_store);
1229 #ifdef ERR_free_strings
1230     ERR_free_strings();
1231 #endif /* ERR_free_strings */
1232 #ifdef EVP_cleanup
1233     EVP_cleanup();
1234 #endif /* EVP_cleanup */
1235     free_smime_struct(&ps_global->smime);
1236 }
1237 
1238 /* we renew the store when it has changed */
1239 void
renew_store(void)1240 renew_store(void)
1241 {
1242     if(ps_global->smime->inited){
1243        if(s_cert_store != NULL)
1244 	 free_x509_store(&s_cert_store);
1245 	s_cert_store = get_ca_store();
1246     }
1247 }
1248 
1249 /* Initialise openssl stuff if needed */
1250 void
smime_init(void)1251 smime_init(void)
1252 {
1253     if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
1254 
1255 	dprint((9, "smime_init()"));
1256 	if(!ps_global->smime)
1257 	  ps_global->smime = new_smime_struct();
1258 
1259 	setup_storage_locations();
1260 
1261 	s_cert_store = get_ca_store();
1262 	setup_certs_backup_by_type(CACert);
1263 
1264 #ifdef OPENSSL_1_1_0
1265 	OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS|OPENSSL_INIT_ADD_ALL_DIGESTS|OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
1266 #else
1267         OpenSSL_add_all_algorithms();
1268         ERR_load_crypto_strings();
1269 #endif /* OPENSSL_1_1_0 */
1270 
1271 	app_RAND_load_file(NULL);
1272     	openssl_extra_randomness();
1273 	ps_global->smime->inited = 1;
1274     }
1275 
1276     ERR_clear_error();
1277 }
1278 
1279 
1280 /* validate a certificate. Return value : 0 for no error, -1 for error.
1281  * In the latter case, set the openssl smime error in *error.
1282  */
1283 int
smime_validate_cert(X509 * cert,long * error)1284 smime_validate_cert(X509 *cert, long *error)
1285 {
1286    X509_STORE_CTX *csc;
1287 
1288    ERR_clear_error();
1289    *error = 0;
1290    if((s_cert_store != NULL) && (csc = X509_STORE_CTX_new()) != NULL){
1291      X509_STORE_set_flags(s_cert_store, 0);
1292      if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1293 	&& X509_verify_cert(csc) <= 0)
1294 	*error   = X509_STORE_CTX_get_error(csc);
1295      X509_STORE_CTX_free(csc);
1296    }
1297    return *error ? -1 : 0;
1298 }
1299 
1300 PERSONAL_CERT *
get_personal_certs(char * path)1301 get_personal_certs(char *path)
1302 {
1303     PERSONAL_CERT *result = NULL;
1304     char buf2[MAXPATH], *fname;
1305     X509 *cert;
1306     size_t ll;
1307 #ifndef _WINDOWS
1308     struct dirent *d;
1309     DIR *dirp;
1310 #else /* _WINDOWS */
1311     struct _finddata_t dbuf;
1312     char buf[_MAX_PATH + 4];
1313     long findrv;
1314 #endif /* _WINDOWS */
1315 
1316     ps_global->smime->privatepath = cpystr(path);
1317 
1318 #ifndef _WINDOWS
1319     dirp = opendir(path);
1320     if(dirp){
1321 	while((d=readdir(dirp)) != NULL){
1322 	   fname = d->d_name;
1323 #else /* _WINDOWS */
1324     snprintf(buf, sizeof(buf), "%s%s*.*", path, (path[strlen(path)-1] == '\\') ? "" : "\\");
1325     buf[sizeof(buf)-1] = '\0';
1326     if((findrv = _findfirst(buf, &dbuf)) < 0)
1327         return(NULL);
1328 
1329     do {
1330             fname = fname_to_utf8(dbuf.name);
1331 #endif
1332 	    if((ll=strlen(fname)) && ll > 4 && !strcmp(fname+ll-4, ".key")){
1333 
1334 		/* copy file name to temp buffer */
1335 		strncpy(buf2, fname, sizeof(buf2)-1);
1336 		buf2[sizeof(buf2)-1] = '\0';
1337 		/* chop off ".key" trailier */
1338 		buf2[strlen(buf2)-4] = '\0';
1339 		/* Look for certificate */
1340 		cert = get_cert_for(buf2, Public, 1);
1341 
1342 		if(cert){
1343 		    PERSONAL_CERT *pc;
1344 
1345 		    /* create a new PERSONAL_CERT, fill it in */
1346 
1347 		    pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1348 		    pc->cert = cert;
1349 		    pc->name = cpystr(buf2);
1350 		    strncat(buf2, EXTCERT(Public), 5);
1351 		    pc->cname = cpystr(buf2);
1352 
1353 		    /* Try to load the key with an empty password */
1354 		    pc->key = load_key(pc, "", SM_NORMALCERT);
1355 
1356 		    pc->next = result;
1357 		    result = pc;
1358 		}
1359 	    }
1360 #ifndef _WINDOWS
1361 	}
1362 	closedir(dirp);
1363     }
1364 #else /* _WINDOWS */
1365     } while(_findnext(findrv, &dbuf) == 0);
1366     _findclose(findrv);
1367 #endif /* !_WINDOWS */
1368     return result;
1369 }
1370 
1371 
1372 void
setup_privatekey_storage(void)1373 setup_privatekey_storage(void)
1374 {
1375     char path[MAXPATH+1], *contents;
1376     int privatekeycontainer = 0;
1377 
1378     /* private keys in a container */
1379     if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1380 
1381 	privatekeycontainer = 1;
1382 	contents = NULL;
1383 	path[0] = '\0';
1384 	if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1385 	  privatekeycontainer = 0;
1386 
1387 	if(privatekeycontainer && !IS_REMOTE(path)
1388 	   && ps_global->VAR_OPER_DIR
1389            && !in_dir(ps_global->VAR_OPER_DIR, path)){
1390 	    q_status_message2(SM_ORDER | SM_DING, 3, 4,
1391 			  /* TRANSLATORS: First arg is the directory name, second is
1392 			     the file user wants to read but can't. */
1393 			  _("Can't read file outside %s: %s"),
1394 			  ps_global->VAR_OPER_DIR, path);
1395 	    privatekeycontainer = 0;
1396 	}
1397 
1398 	if(privatekeycontainer
1399 	   && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1400 	    if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1401 	       &&
1402 	       !(contents = read_file(path, READ_FROM_LOCALE)))
1403 	      privatekeycontainer = 0;
1404 	}
1405 
1406 	if(privatekeycontainer && path[0]){
1407 	    ps_global->smime->privatetype = Container;
1408 	    ps_global->smime->privatepath = cpystr(path);
1409 
1410 	    if(contents){
1411 		ps_global->smime->privatecontent = contents;
1412 		ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1413 	    }
1414 	}
1415     }
1416 
1417     /* private keys in a directory of files */
1418     if(!privatekeycontainer){
1419 	ps_global->smime->privatetype = Directory;
1420 
1421 	path[0] = '\0';
1422 	if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1423 	     && !IS_REMOTE(path)))
1424 	  ps_global->smime->privatetype = Nada;
1425 	else if(can_access(path, ACCESS_EXISTS)){
1426 	    if(our_mkpath(path, 0700)){
1427 		q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1428 		ps_global->smime->privatetype = Nada;
1429 	    }
1430 	}
1431 
1432 	if(ps_global->smime->privatetype == Directory)
1433 	   ps_global->smime->personal_certs = get_personal_certs(path);
1434     }
1435     setup_certs_backup_by_type(Private);
1436 }
1437 
1438 static void
setup_storage_locations(void)1439 setup_storage_locations(void)
1440 {
1441     int publiccertcontainer = 0, cacertcontainer = 0;
1442     char path[MAXPATH+1], *contents;
1443 
1444     if(!ps_global->smime)
1445       return;
1446 
1447 #ifdef APPLEKEYCHAIN
1448     if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1449 	ps_global->smime->publictype = Keychain;
1450     }
1451     else{
1452 #endif /* APPLEKEYCHAIN */
1453     /* Public certificates in a container */
1454     if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1455 
1456 	publiccertcontainer = 1;
1457 	contents = NULL;
1458 	path[0] = '\0';
1459 	if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1460 	  publiccertcontainer = 0;
1461 
1462 	if(publiccertcontainer && !IS_REMOTE(path)
1463 	   && ps_global->VAR_OPER_DIR
1464            && !in_dir(ps_global->VAR_OPER_DIR, path)){
1465 	    q_status_message2(SM_ORDER | SM_DING, 3, 4,
1466 			  /* TRANSLATORS: First arg is the directory name, second is
1467 			     the file user wants to read but can't. */
1468 			  _("Can't read file outside %s: %s"),
1469 			  ps_global->VAR_OPER_DIR, path);
1470 	    publiccertcontainer = 0;
1471 	}
1472 
1473 	if(publiccertcontainer
1474 	   && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1475 	    if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1476 	       &&
1477 	       !(contents = read_file(path, READ_FROM_LOCALE)))
1478 	      publiccertcontainer = 0;
1479 	}
1480 
1481 	if(publiccertcontainer && path[0]){
1482 	    ps_global->smime->publictype = Container;
1483 	    ps_global->smime->publicpath = cpystr(path);
1484 
1485 	    if(contents){
1486 		ps_global->smime->publiccontent = contents;
1487 		ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1488 	    }
1489 	}
1490     }
1491 
1492     /* Public certificates in a directory of files */
1493     if(!publiccertcontainer){
1494 	ps_global->smime->publictype = Directory;
1495 
1496 	path[0] = '\0';
1497 	if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1498 	     && !IS_REMOTE(path)))
1499 	  ps_global->smime->publictype = Nada;
1500 	else if(can_access(path, ACCESS_EXISTS)){
1501 	    if(our_mkpath(path, 0700)){
1502 		q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1503 		ps_global->smime->publictype = Nada;
1504 	    }
1505 	}
1506 
1507 	if(ps_global->smime->publictype == Directory)
1508 	  ps_global->smime->publicpath = cpystr(path);
1509     }
1510 
1511 #ifdef APPLEKEYCHAIN
1512     }
1513 #endif /* APPLEKEYCHAIN */
1514 
1515     setup_privatekey_storage();
1516 
1517     /* extra cacerts in a container */
1518     if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1519 
1520 	cacertcontainer = 1;
1521 	contents = NULL;
1522 	path[0] = '\0';
1523 	if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1524 	  cacertcontainer = 0;
1525 
1526 	if(cacertcontainer && !IS_REMOTE(path)
1527 	   && ps_global->VAR_OPER_DIR
1528            && !in_dir(ps_global->VAR_OPER_DIR, path)){
1529 	    q_status_message2(SM_ORDER | SM_DING, 3, 4,
1530 			  /* TRANSLATORS: First arg is the directory name, second is
1531 			     the file user wants to read but can't. */
1532 			  _("Can't read file outside %s: %s"),
1533 			  ps_global->VAR_OPER_DIR, path);
1534 	    cacertcontainer = 0;
1535 	}
1536 
1537 	if(cacertcontainer
1538 	   && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1539 	    if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1540 	       &&
1541 	       !(contents = read_file(path, READ_FROM_LOCALE)))
1542 	      cacertcontainer = 0;
1543 	}
1544 
1545 	if(cacertcontainer && path[0]){
1546 	    ps_global->smime->catype = Container;
1547 	    ps_global->smime->capath = cpystr(path);
1548 	    ps_global->smime->cacontent = contents;
1549 	    if(contents)
1550 	      ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1551 	}
1552     }
1553 
1554     if(!cacertcontainer){
1555 	ps_global->smime->catype = Directory;
1556 
1557 	path[0] = '\0';
1558 	if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1559 	     && !IS_REMOTE(path)))
1560 	  ps_global->smime->catype = Nada;
1561 	else if(can_access(path, ACCESS_EXISTS)){
1562 	    if(our_mkpath(path, 0700)){
1563 		q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1564 		ps_global->smime->catype = Nada;
1565 	    }
1566 	}
1567 
1568 	if(ps_global->smime->catype == Directory)
1569 	  ps_global->smime->capath = cpystr(path);
1570     }
1571 }
1572 
1573 
1574 int
copy_publiccert_dir_to_container(void)1575 copy_publiccert_dir_to_container(void)
1576 {
1577     return(copy_dir_to_container(Public, NULL));
1578 }
1579 
1580 
1581 int
copy_publiccert_container_to_dir(void)1582 copy_publiccert_container_to_dir(void)
1583 {
1584     return(copy_container_to_dir(Public));
1585 }
1586 
1587 
1588 int
copy_privatecert_dir_to_container(void)1589 copy_privatecert_dir_to_container(void)
1590 {
1591     return(copy_dir_to_container(Private, NULL));
1592 }
1593 
1594 
1595 int
copy_privatecert_container_to_dir(void)1596 copy_privatecert_container_to_dir(void)
1597 {
1598     return(copy_container_to_dir(Private));
1599 }
1600 
1601 
1602 int
copy_cacert_dir_to_container(void)1603 copy_cacert_dir_to_container(void)
1604 {
1605     return(copy_dir_to_container(CACert, NULL));
1606 }
1607 
1608 
1609 int
copy_cacert_container_to_dir(void)1610 copy_cacert_container_to_dir(void)
1611 {
1612     return(copy_container_to_dir(CACert));
1613 }
1614 
1615 /* Add the contents of a file to a container. Do not check the content
1616  * of the file, just add it using the format for that container. The
1617  * caller must check the format, so that there is no data corruption
1618  * in the future.
1619  * return value: 0 - success,
1620  *               != 0 - failure.
1621  */
1622 int
add_file_to_container(WhichCerts ctype,char * fpath,char * altname)1623 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1624 {
1625     char *sep = (ctype == Public || ctype == Private)
1626                 ? EMAILADDRLEADER : CACERTSTORELEADER;
1627     char *content = ctype == Public ? ps_global->smime->publiccontent
1628 		    : (ctype == Private ? ps_global->smime->privatecontent
1629 		    : ps_global->smime->cacontent);
1630     char *name;
1631     char *s;
1632     unsigned char c;
1633     struct stat sbuf;
1634     STORE_S *in = NULL;
1635     int rv = -1; 	/* assume error */
1636     size_t clen;	/* content buffer size */
1637 
1638     if(our_stat(fpath, &sbuf) < 0
1639 	|| (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1640       goto endadd;
1641 
1642     if(altname != NULL)
1643       name = altname;
1644     else if((name = strrchr(fpath, '/')) != NULL){
1645       size_t ll;
1646       if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1647         name[ll-strlen(EXTCERT(ctype))] = '\0';
1648     }
1649     else
1650       goto endadd;
1651 
1652     if(content){
1653       clen = strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 2*strlen(NEWLINE) + 1;
1654       fs_resize((void **)&content, clen);
1655       s = content;
1656       content += strlen(content);
1657     }
1658     else{
1659       clen = strlen(sep) + strlen(name) + sbuf.st_size + strlen(NEWLINE) + 1;
1660       s = content = fs_get(clen);
1661       *content = '\0';
1662     }
1663     strncat(content, sep, clen - strlen(content));
1664     strncat(content, name, clen - strlen(content));
1665     content += strlen(content);
1666 #ifdef _WINDOWS
1667     *content++ = '\r';
1668 #endif /* _WINDOWS */
1669     *content++ = '\n';
1670 
1671     while(so_readc(&c, in))
1672        *content++ = (char) c;
1673     *content = '\0';
1674 
1675     switch(ctype){
1676       case Private:   ps_global->smime->privatecontent = s; break;
1677       case Public :   ps_global->smime->publiccontent = s; break;
1678       case CACert :   ps_global->smime->cacontent = s; break;
1679       default : break;
1680     }
1681 
1682     rv = copy_dir_to_container(ctype, s);
1683 
1684 endadd:
1685     if(in) so_give(&in);
1686 
1687     return rv;
1688 }
1689 
1690 
1691 /*
1692  * returns 0 on success, -1 on failure
1693  * contents is an argument which tells this function to write the value
1694  * of this variable instead of reading the contents of the directory.
1695  * If the var contents is not null use its value as the value of the
1696  * container.
1697  */
1698 int
copy_dir_to_container(WhichCerts which,char * contents)1699 copy_dir_to_container(WhichCerts which, char *contents)
1700 {
1701     int ret = 0, container = 0;
1702     BIO *bio_out = NULL, *bio_in = NULL;
1703     char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1704     char *tempfile = NULL, fpath[MAXPATH+1], *fname;
1705     size_t ll;
1706 #ifndef _WINDOWS
1707     DIR *dirp;
1708     struct dirent *d;
1709 #else /* _WINDOWS */
1710     struct _finddata_t dbuf;
1711     char buf[_MAX_PATH + 4];
1712     long findrv;
1713 #endif /* _WINDOWS */
1714     REMDATA_S *rd = NULL;
1715     char *configdir = NULL;
1716     char *configpath = NULL;
1717     char *configcontainer = NULL;
1718     char *filesuffix = NULL;
1719     char *ret_dir = NULL;
1720 
1721     dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1722     smime_init();
1723 
1724     srcpath[0] = '\0';
1725     dstpath[0] = '\0';
1726     file[0] = '\0';
1727     emailaddr[0] = '\0';
1728 
1729     if(which == Public){
1730 	configdir  = ps_global->VAR_PUBLICCERT_DIR;
1731 	configpath = ps_global->smime->publicpath;
1732 	configcontainer = cpystr(DF_PUBLIC_CONTAINER);
1733 	filesuffix = ".crt";
1734     }
1735     else if(which == Private){
1736 	configdir = ps_global->VAR_PRIVATEKEY_DIR;
1737 	configpath = ps_global->smime->privatepath;
1738 	configcontainer = cpystr(DF_PRIVATE_CONTAINER);
1739 	filesuffix = ".key";
1740     }
1741     else if(which == CACert){
1742 	configdir = ps_global->VAR_CACERT_DIR;
1743 	configpath = ps_global->smime->capath;
1744 	configcontainer = cpystr(DF_CA_CONTAINER);
1745 	filesuffix = ".crt";
1746     }
1747     container = SMHOLDERTYPE(which) == Container;
1748 
1749     if(!(configdir && configdir[0])){
1750 	q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1751 	return -1;
1752     }
1753 
1754     if(!(configpath && configpath[0])){
1755 #ifdef APPLEKEYCHAIN
1756 	if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1757 	    q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1758 	    return -1;
1759 	}
1760 #endif /* APPLEKEYCHAIN */
1761 	q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1762 	return -1;
1763     }
1764 
1765     if(!(filesuffix && strlen(filesuffix) == 4)){
1766 	return -1;
1767     }
1768 
1769 
1770     /*
1771      * If there is a legit directory to read from set up the
1772      * container file to write to.
1773      */
1774     if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1775 
1776 	if(IS_REMOTE(configpath)){
1777 	    rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1778 				  NULL, "Error: ",
1779 				  _("Can't access remote smime configuration."));
1780 	    if(!rd)
1781 	      return -1;
1782 
1783 	    (void) rd_read_metadata(rd);
1784 
1785 	    if(rd->access == MaybeRorW){
1786 		if(rd->read_status == 'R')
1787 		  rd->access = ReadOnly;
1788 		else
1789 		  rd->access = ReadWrite;
1790 	    }
1791 
1792 	    if(rd->access != NoExists){
1793 
1794 		rd_check_remvalid(rd, 1L);
1795 
1796 		/*
1797 		 * If the cached info says it is readonly but
1798 		 * it looks like it's been fixed now, change it to readwrite.
1799 		 */
1800 		if(rd->read_status == 'R'){
1801 		    rd_check_readonly_access(rd);
1802 		    if(rd->read_status == 'W'){
1803 			rd->access = ReadWrite;
1804 			rd->flags |= REM_OUTOFDATE;
1805 		    }
1806 		    else
1807 		      rd->access = ReadOnly;
1808 		}
1809 	    }
1810 
1811 	    if(rd->flags & REM_OUTOFDATE){
1812 		if(rd_update_local(rd) != 0){
1813 
1814 		    dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1815 		    rd_close_remdata(&rd);
1816 		    return -1;
1817 		}
1818 	    }
1819 	    else
1820 	      rd_open_remote(rd);
1821 
1822 	    if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1823 		rd_close_remdata(&rd);
1824 		return -1;
1825 	    }
1826 
1827 	    rd->flags |= DO_REMTRIM;
1828 
1829 	    strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1830 	    dstpath[sizeof(dstpath)-1] = '\0';
1831 	}
1832 	else{
1833 	    strncpy(dstpath, configpath, sizeof(dstpath)-1);
1834 	    dstpath[sizeof(dstpath)-1] = '\0';
1835 	}
1836 
1837 	/*
1838 	 * dstpath is either the local Container file or the local cache file
1839 	 * for the remote Container file.
1840 	 */
1841 	tempfile = tempfile_in_same_dir(dstpath, "az", &ret_dir);
1842     }
1843 
1844     /*
1845      * If there is a legit directory to read from and a tempfile
1846      * to write to we continue.
1847      */
1848     if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1849 
1850 	if(contents != NULL){
1851 	   if(BIO_puts(bio_out, contents) < 0)
1852 	     ret = -1;
1853 	}
1854 	else {
1855 #ifndef _WINDOWS
1856 	  if((dirp = opendir(srcpath)) != NULL){
1857 
1858 	    while((d=readdir(dirp)) && !ret){
1859 		fname = d->d_name;
1860 #else /* _WINDOWS */
1861     	  snprintf(buf, sizeof(buf), "%s%s*.*", srcpath, (srcpath[strlen(srcpath)-1] == '\\') ? "" : "\\");
1862 	  buf[sizeof(buf)-1] = '\0';
1863 	  if((findrv = _findfirst(buf, &dbuf)) < 0)
1864 	        return -1;
1865 
1866 	  do{
1867                 fname = fname_to_utf8(dbuf.name);
1868 #endif /* ! _WINDOWS */
1869 		if((ll=strlen(fname)) && ll > 4 && !strcmp(fname+ll-4, filesuffix)){
1870 
1871 		    /* copy file name to temp buffer */
1872 		    strncpy(emailaddr, fname, sizeof(emailaddr)-1);
1873 		    emailaddr[sizeof(emailaddr)-1] = '\0';
1874 		    /* chop off suffix trailier */
1875 		    emailaddr[strlen(emailaddr)-4] = 0;
1876 
1877 		    /*
1878 		     * This is the separator between the contents of
1879 		     * different files.
1880 		     */
1881 		    if(which == CACert){
1882 			if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1883 			     && (BIO_puts(bio_out, emailaddr) > 0)
1884 			     && (BIO_puts(bio_out, NEWLINE) > 0)))
1885 			  ret = -1;
1886 		    }
1887 		    else{
1888 			if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1889 			     && (BIO_puts(bio_out, emailaddr) > 0)
1890 			     && (BIO_puts(bio_out, NEWLINE) > 0)))
1891 			  ret = -1;
1892 		    }
1893 
1894 		    /* read then write contents of file */
1895 		    build_path(file, srcpath, fname, sizeof(file));
1896 		    if(!(bio_in = BIO_new_file(file, "r")))
1897 		      ret = -1;
1898 
1899 		    if(!ret){
1900 			int good_stuff = 0;
1901 
1902 			while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1903 			    if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1904 			      good_stuff = 1;
1905 
1906 			    if(good_stuff)
1907 			      BIO_puts(bio_out, line);
1908 
1909 			    if(strncmp("-----END", line, strlen("-----END")) == 0)
1910 			      good_stuff = 0;
1911 			}
1912 		    }
1913 
1914 		    BIO_free(bio_in);
1915 		}
1916 #ifndef _WINDOWS
1917 	    }
1918 	    closedir(dirp);
1919 	  }
1920 #else /* _WINDOWS */
1921 	    } while (_findnext(findrv, &dbuf) == 0);
1922 		_findclose(findrv);
1923 #endif /* ! _WINDOWS */
1924 	}
1925 
1926 	BIO_free(bio_out);
1927 
1928 	if(!ret){
1929 	    if(container && configpath && *configpath){
1930 	      strncpy(fpath, configpath, sizeof(fpath));
1931 	      fpath[sizeof(fpath) - 1] = '\0';
1932 	    }
1933 	    else if(ret_dir){
1934                 if(strlen(dstpath) + strlen(configcontainer) + 2 < sizeof(dstpath))
1935                    snprintf(fpath, sizeof(fpath), "%.*s%c%.*s",
1936 			(int) strlen(dstpath), dstpath,
1937 			tempfile[strlen(ret_dir)],
1938 			(int) (sizeof(fpath) - strlen(dstpath) - 1), configcontainer);
1939                 else
1940                    ret = -1;
1941             }
1942             else ret = -1;
1943 
1944 	    if(!ret){
1945 	      if(!IS_REMOTE(configpath)){
1946 		if(rename_file(tempfile, fpath) < 0){
1947 		   q_status_message2(SM_ORDER, 3, 3,
1948 		      _("Can't rename %s to %s"), tempfile, fpath);
1949 		   ret = -1;
1950 	        } else q_status_message1(SM_ORDER, 3, 3,
1951 		      _("saved container to %s"), fpath);
1952 	      }
1953 	      else { /* if the container is remote, copy it */
1954 		int   e;
1955 		char datebuf[200];
1956 
1957 		if(rd != NULL && rename_file(tempfile, rd->lf) < 0){
1958 		   q_status_message2(SM_ORDER, 3, 3,
1959 		      _("Can't rename %s to %s"), tempfile, rd->lf);
1960 		   ret = -1;
1961 		}
1962 
1963 		datebuf[0] = '\0';
1964 
1965 		if((e = rd_update_remote(rd, datebuf)) != 0){
1966 		    if(e == -1){
1967 			q_status_message2(SM_ORDER | SM_DING, 3, 5,
1968 			  _("Error opening temporary smime file %s: %s"),
1969 			    rd->lf, error_description(errno));
1970 			dprint((1,
1971 			   "write_remote_smime: error opening temp file %s\n",
1972 			   rd->lf ? rd->lf : "?"));
1973 		    }
1974 		    else{
1975 			q_status_message2(SM_ORDER | SM_DING, 3, 5,
1976 					_("Error copying to %s: %s"),
1977 					rd->rn, error_description(errno));
1978 			dprint((1,
1979 			  "write_remote_smime: error copying from %s to %s\n",
1980 			  rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1981 		    }
1982 
1983 		    q_status_message(SM_ORDER | SM_DING, 5, 5,
1984        _("Copy of smime key to remote folder failed, NOT saved remotely"));
1985 		}
1986 		else{
1987 		    rd_update_metadata(rd, datebuf);
1988 		    rd->read_status = 'W';
1989 		}
1990 
1991 		rd_close_remdata(&rd);
1992 	      }
1993 	    }
1994 	}
1995     }
1996 
1997     if(tempfile)
1998       fs_give((void **) &tempfile);
1999 
2000     if(ret_dir)
2001       fs_give((void **) &ret_dir);
2002 
2003     if(configcontainer)
2004       fs_give((void **) &configcontainer);
2005 
2006     return ret;
2007 }
2008 
2009 
2010 /*
2011  * returns 0 on success, -1 on failure
2012  */
2013 int
copy_container_to_dir(WhichCerts which)2014 copy_container_to_dir(WhichCerts which)
2015 {
2016     char  path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
2017     char  iobuf[4096];
2018     char *contents = NULL;
2019     char *leader = NULL;
2020     char *filesuffix = NULL;
2021     char *configdir = NULL;
2022     char *configpath = NULL;
2023     char *tempfile = NULL;
2024     char *p, *q, *line, *name, *certtext, *save_p;
2025     int  len;
2026     BIO *in, *out;
2027 
2028     dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
2029     smime_init();
2030 
2031     path[0] = '\0';
2032 
2033     if(which == Public){
2034 	leader = EMAILADDRLEADER;
2035 	contents  = ps_global->smime->publiccontent;
2036 	configdir  = ps_global->VAR_PUBLICCERT_DIR;
2037 	configpath = ps_global->smime->publicpath;
2038 	filesuffix = ".crt";
2039 	if(!(configpath && configpath[0])){
2040 #ifdef APPLEKEYCHAIN
2041 	    if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
2042 		q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
2043 		return -1;
2044 	    }
2045 #endif /* APPLEKEYCHAIN */
2046 	    q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2047 	    return -1;
2048 	}
2049 
2050 	fs_give((void **) &ps_global->smime->publicpath);
2051 
2052 	path[0] = '\0';
2053 	if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
2054 	     && !IS_REMOTE(path))){
2055 	    q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2056 	    return -1;
2057 	}
2058 
2059 	if(can_access(path, ACCESS_EXISTS)){
2060 	    if(our_mkpath(path, 0700)){
2061 		q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2062 		return -1;
2063 	    }
2064 	}
2065 
2066 	ps_global->smime->publicpath = cpystr(path);
2067 	configpath = ps_global->smime->publicpath;
2068     }
2069     else if(which == Private){
2070 	leader = EMAILADDRLEADER;
2071 	contents  = ps_global->smime->privatecontent;
2072 	configdir = ps_global->VAR_PRIVATEKEY_DIR;
2073 	configpath = ps_global->smime->privatepath;
2074 	filesuffix = ".key";
2075 	if(!(configpath && configpath[0])){
2076 	    q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2077 	    return -1;
2078 	}
2079 
2080 	fs_give((void **) &ps_global->smime->privatepath);
2081 
2082 	path[0] = '\0';
2083 	if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
2084 	     && !IS_REMOTE(path))){
2085 	    q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2086 	    return -1;
2087 	}
2088 
2089 	if(can_access(path, ACCESS_EXISTS)){
2090 	    if(our_mkpath(path, 0700)){
2091 		q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2092 		return -1;
2093 	    }
2094 	}
2095 
2096 	ps_global->smime->privatepath = cpystr(path);
2097 	configpath = ps_global->smime->privatepath;
2098     }
2099     else if(which == CACert){
2100 	leader = CACERTSTORELEADER;
2101 	contents  = ps_global->smime->cacontent;
2102 	configdir = ps_global->VAR_CACERT_DIR;
2103 	configpath = ps_global->smime->capath;
2104 	filesuffix = ".crt";
2105 	if(!(configpath && configpath[0])){
2106 	    q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2107 	    return -1;
2108 	}
2109 
2110 	fs_give((void **) &ps_global->smime->capath);
2111 
2112 	path[0] = '\0';
2113 	if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
2114 	     && !IS_REMOTE(path))){
2115 	    q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2116 	    return -1;
2117 	}
2118 
2119 	if(can_access(path, ACCESS_EXISTS)){
2120 	    if(our_mkpath(path, 0700)){
2121 		q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2122 		return -1;
2123 	    }
2124 	}
2125 
2126 	ps_global->smime->capath = cpystr(path);
2127 	configpath = ps_global->smime->capath;
2128     }
2129 
2130     if(!(configdir && configdir[0])){
2131 	q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
2132 	return -1;
2133     }
2134 
2135     if(!(configpath && configpath[0])){
2136 	q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2137 	return -1;
2138     }
2139 
2140     if(!(filesuffix && strlen(filesuffix) == 4)){
2141 	return -1;
2142     }
2143 
2144 
2145     if(contents && *contents){
2146 	for(p = contents; *p != '\0';){
2147 	    line = p;
2148 
2149 	    while(*p && *p != '\n')
2150 	      p++;
2151 
2152 	    save_p = NULL;
2153 	    if(*p == '\n'){
2154 		save_p = p;
2155 		*p++ = '\0';
2156 	    }
2157 
2158 	    if(strncmp(leader, line, strlen(leader)) == 0){
2159 		name = line + strlen(leader);
2160 		certtext = p;
2161 		if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
2162 		    if((q = strstr(certtext, leader)) != NULL){
2163 			p = q;
2164 		    }
2165 		    else{		/* end of file */
2166 			q = certtext + strlen(certtext);
2167 			p = q;
2168 		    }
2169 
2170 		    strncpy(buf, name, sizeof(buf)-5);
2171 		    buf[sizeof(buf)-5] = '\0';
2172 		    strncat(buf, filesuffix, 5);
2173 		    build_path(file, configpath, buf, sizeof(file));
2174 
2175 		    in = BIO_new_mem_buf(certtext, q-certtext);
2176 		    if(in){
2177 			tempfile = tempfile_in_same_dir(file, "az", NULL);
2178 			out = NULL;
2179 			if(tempfile)
2180 			  out = BIO_new_file(tempfile, "w");
2181 
2182 			if(out){
2183 			    while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
2184 			      BIO_write(out, iobuf, len);
2185 
2186 			    BIO_free(out);
2187 
2188 			    if(rename_file(tempfile, file) < 0){
2189 				q_status_message2(SM_ORDER, 3, 3,
2190 				                  _("Can't rename %s to %s"),
2191 						  tempfile, file);
2192 				return -1;
2193 			    }
2194 
2195 			    fs_give((void **) &tempfile);
2196 			}
2197 
2198 			BIO_free(in);
2199 		    }
2200 		}
2201 	    }
2202 
2203 	    if(save_p)
2204 	      *save_p = '\n';
2205 	}
2206     }
2207 
2208     return 0;
2209 }
2210 
2211 
2212 #ifdef APPLEKEYCHAIN
2213 
2214 int
copy_publiccert_container_to_keychain(void)2215 copy_publiccert_container_to_keychain(void)
2216 {
2217     /* NOT IMPLEMNTED */
2218     return -1;
2219 }
2220 
2221 int
copy_publiccert_keychain_to_container(void)2222 copy_publiccert_keychain_to_container(void)
2223 {
2224     /* NOT IMPLEMNTED */
2225     return -1;
2226 }
2227 
2228 #endif /* APPLEKEYCHAIN */
2229 
2230 
2231 /*
2232  * Get a pointer to a string describing the most recent OpenSSL error.
2233  * It's statically allocated, so don't change or attempt to free it.
2234  */
2235 static const char *
openssl_error_string(void)2236 openssl_error_string(void)
2237 {
2238     char	*errs;
2239     const char	*data = NULL;
2240     long errn;
2241 
2242     errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2243     errs = (char*) ERR_reason_error_string(errn);
2244 
2245     if(errs)
2246       return errs;
2247     else if(data)
2248       return data;
2249 
2250     return "unknown error";
2251 }
2252 
2253 
2254 /* Return true if the body looks like a PKCS7 object */
2255 int
is_pkcs7_body(BODY * body)2256 is_pkcs7_body(BODY *body)
2257 {
2258     int result;
2259 
2260     result = body->type==TYPEAPPLICATION &&
2261              body->subtype &&
2262              (strucmp(body->subtype,"pkcs7-mime")==0 ||
2263               strucmp(body->subtype,"x-pkcs7-mime")==0 ||
2264 	      strucmp(body->subtype,"pkcs7-signature")==0 ||
2265 	      strucmp(body->subtype,"x-pkcs7-signature")==0);
2266 
2267     return result;
2268 }
2269 
2270 
2271 /*
2272  * Recursively stash a pointer to the decrypted data in our
2273  * manufactured body.
2274  * parameters: type: call of type 1, save the base and header for multipart messages
2275 		     call of type 0, do not save the base and header for multipart messages
2276  */
2277 static void
create_local_cache(char * h,char * base,BODY * b,int type)2278 create_local_cache(char *h, char *base, BODY *b, int type)
2279 {
2280     if(b->type==TYPEMULTIPART){
2281         PART *p;
2282 
2283         if(type == 1){
2284 	  cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2285 	  cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2286 	} else if(type == 0){
2287 	/*
2288 	 * We don't really want to copy the real body contents. It shouldn't be
2289 	 * used, and in the case of a message with attachments, we'll be
2290 	 * duplicating the files multiple times.
2291 	 */
2292 	  cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
2293 
2294           for(p=b->nested.part; p; p=p->next)
2295             create_local_cache(h, base, (BODY *) p, type);
2296 	}
2297     }
2298     else{
2299 	cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2300         cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2301     }
2302 }
2303 
2304 
2305 static long
rfc822_output_func(void * b,char * string)2306 rfc822_output_func(void *b, char *string)
2307 {
2308     BIO *bio = (BIO *) b;
2309 
2310     return(string ? *string ? (BIO_puts(bio, string) >  0 ? 1L : 0L)
2311 			    : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
2312 		  : 0L);
2313 }
2314 
2315 
2316 /*
2317  * Attempt to load the private key for the given PERSONAL_CERT.
2318  * This sets the appropriate passphrase globals in order to
2319  * interact with the user correctly.
2320  */
2321 static int
load_private_key(PERSONAL_CERT * pcert)2322 load_private_key(PERSONAL_CERT *pcert)
2323 {
2324     if(!pcert->key){
2325 
2326     	/* Try empty password by default */
2327     	char	*password = "";
2328 
2329     	if(ps_global->smime
2330            && (ps_global->smime->need_passphrase
2331                || ps_global->smime->entered_passphrase)){
2332 	    /* We've already been in here and discovered we need a different password */
2333 
2334 	    if(ps_global->smime->entered_passphrase)
2335     	      password = (char *) ps_global->smime->passphrase;	/* already entered */
2336 	    else
2337 	      return 0;
2338 	}
2339 
2340         ERR_clear_error();
2341 
2342         if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){
2343             long err = ERR_get_error();
2344 
2345     	    /* Couldn't load key... */
2346 
2347 	    if(ps_global->smime && ps_global->smime->entered_passphrase){
2348 
2349     	    	/* The user got the password wrong maybe? */
2350 
2351         	if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2352                 	(ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2353                   q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2354         	else
2355 		  q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2356 
2357 		/* This passphrase is no good; forget it */
2358 		ps_global->smime->entered_passphrase = 0;
2359 	    }
2360 
2361 	    if(ps_global->smime){
2362 	    /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2363 		ps_global->smime->need_passphrase = 1;
2364 		if(ps_global->smime->passphrase_emailaddr){
2365 		  int i;
2366 		  for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2367 		     fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2368 		  fs_give((void **) ps_global->smime->passphrase_emailaddr);
2369 		}
2370 
2371 		ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2372 	    }
2373 
2374             return 0;
2375         }
2376 	else{
2377 	    /* This key will be cached, so we won't be called again */
2378 	    if(ps_global->smime){
2379 		ps_global->smime->entered_passphrase = 0;
2380 		ps_global->smime->need_passphrase = 0;
2381 	    }
2382 	}
2383 
2384 	return 1;
2385     }
2386 
2387     return 0;
2388 }
2389 
2390 
2391 static void
setup_pkcs7_body_for_signature(BODY * b,char * description,char * type,char * filename,char * smime_type)2392 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2393 {
2394     b->type = TYPEAPPLICATION;
2395     b->subtype = cpystr(type);
2396     b->encoding = ENCBINARY;
2397     b->description = cpystr(description);
2398 
2399     b->disposition.type = cpystr("attachment");
2400     set_parameter(&b->disposition.parameter, "filename", filename);
2401 
2402     set_parameter(&b->parameter, "name", filename);
2403     if(smime_type && *smime_type)
2404       set_parameter(&b->parameter, "smime-type", smime_type);
2405 }
2406 
2407 
2408 /*
2409  * Look for a personal certificate matching the
2410  * given address
2411  */
2412 PERSONAL_CERT *
match_personal_cert_to_email(ADDRESS * a)2413 match_personal_cert_to_email(ADDRESS *a)
2414 {
2415     PERSONAL_CERT   *pcert = NULL;
2416     char	buf[MAXPATH];
2417     char    	**email;
2418     int i, done;
2419 
2420     if(!a || !a->mailbox || !a->host)
2421       return NULL;
2422 
2423     snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2424 
2425     if(ps_global->smime){
2426 	for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2427 	    pcert;
2428 	    pcert=pcert->next){
2429 
2430 	    if(!pcert->cert)
2431 	      continue;
2432 
2433 	    email = get_x509_subject_email(pcert->cert);
2434 
2435 	    done = 0;
2436 	    if(email != NULL){
2437 		for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2438 		if(email[i] != NULL) done++;
2439 		for(i = 0; email[i] != NULL; i++)
2440 		   fs_give((void **)&email[i]);
2441 		fs_give((void **)email);
2442 	    }
2443 
2444 	    if(done > 0)
2445 	      break;
2446 	}
2447     }
2448 
2449     return pcert;
2450 }
2451 
2452 
2453 /*
2454  * Look for a personal certificate matching the from
2455  * (or reply_to? in the given envelope)
2456  */
2457 PERSONAL_CERT *
match_personal_cert(ENVELOPE * env)2458 match_personal_cert(ENVELOPE *env)
2459 {
2460     PERSONAL_CERT   *pcert;
2461 
2462     pcert = match_personal_cert_to_email(env->reply_to);
2463     if(!pcert)
2464       pcert = match_personal_cert_to_email(env->from);
2465 
2466     return pcert;
2467 }
2468 
2469 
2470 /*
2471  * Flatten the given body into its MIME representation.
2472  * Return the result in a BIO.
2473  */
2474 static BIO *
body_to_bio(BODY * body)2475 body_to_bio(BODY *body)
2476 {
2477     BIO *bio = NULL;
2478     int  len;
2479 
2480     bio = BIO_new(BIO_s_mem());
2481     if(!bio)
2482       return NULL;
2483 
2484     pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2485     pine_write_body_header(body, rfc822_output_func, bio);
2486     pine_rfc822_output_body(body, rfc822_output_func, bio);
2487 
2488     /*
2489      * Now need to truncate by two characters since the above
2490      * appends CRLF.
2491      */
2492     if((len=BIO_ctrl_pending(bio)) > 1){
2493 	BUF_MEM *biobuf = NULL;
2494 
2495 	/* this code used to truncate without closing the bio, and
2496 	   then resetting the memory, causing non validation in
2497 	   signatures. Fix contributed by Bernd Edlinger.
2498 	 */
2499 	BIO_get_mem_ptr(bio, &biobuf);
2500 	BIO_set_close(bio, BIO_NOCLOSE);
2501 	BUF_MEM_grow(biobuf, len-2);	/* remove CRLF */
2502 	BIO_set_mem_buf(bio, biobuf, BIO_CLOSE);
2503     }
2504 
2505     return bio;
2506 }
2507 
2508 
2509 static BIO *
bio_from_store(STORE_S * store)2510 bio_from_store(STORE_S *store)
2511 {
2512     BIO *ret = NULL;
2513 
2514     if(store && store->src == BioType && store->txt){
2515 	ret = (BIO *) store->txt;
2516     }
2517 
2518     return(ret);
2519 }
2520 
2521 /*
2522  * Encrypt file; given a path (char *) fp, replace the file
2523  * by an encrypted version of it. If (char *) text is not null, then
2524  * replace the text of (char *) fp by the encrypted version of (char *) text.
2525  * certpath is the FULL path to the file containing the certificate used for
2526  * encryption.
2527  * return value: 0 - failed to encrypt; 1 - success!
2528  */
2529 int
encrypt_file(char * fp,char * text,PERSONAL_CERT * pc)2530 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2531 {
2532   const EVP_CIPHER *cipher = NULL;
2533   STACK_OF(X509) *encerts = NULL;
2534   BIO *out = NULL;
2535   PKCS7 *p7 = NULL;
2536   int rv = 0;
2537 
2538   if(pc == NULL)
2539     return 0;
2540 
2541   cipher = EVP_aes_256_cbc();
2542   encerts = sk_X509_new_null();
2543 
2544   sk_X509_push(encerts, X509_dup(pc->cert));
2545 
2546   if(text){
2547     if((out = BIO_new(BIO_s_mem())) != NULL){
2548       (void) BIO_reset(out);
2549       BIO_puts(out, text);
2550     }
2551   }
2552   else if((out = BIO_new_file(fp, "rb")) != NULL)
2553     BIO_read_filename(out, fp);
2554 
2555   if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) != NULL){
2556 	BIO_set_close(out, BIO_CLOSE);
2557 	BIO_free(out);
2558 	if((out = BIO_new_file(fp, "w")) != NULL){
2559 	  BIO_reset(out);
2560 	  rv = PEM_write_bio_PKCS7(out, p7);
2561 	  BIO_flush(out);
2562         }
2563   }
2564 
2565   if(out != NULL)
2566     BIO_free(out);
2567   PKCS7_free(p7);
2568   sk_X509_pop_free(encerts, X509_free);
2569 
2570   return rv;
2571 }
2572 
2573 /*
2574  * Encrypt a message on the way out. Called from call_mailer in send.c
2575  * The body may be reallocated.
2576  */
2577 int
encrypt_outgoing_message(METAENV * header,BODY ** bodyP)2578 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2579 {
2580     PKCS7 *p7 = NULL;
2581     BIO	*in = NULL;
2582     BIO *out = NULL;
2583     const EVP_CIPHER *cipher = NULL;
2584     STACK_OF(X509) *encerts = NULL;
2585     STORE_S *outs = NULL;
2586     PINEFIELD	*pf;
2587     ADDRESS	*a;
2588     BODY	*body = *bodyP;
2589     BODY	*newBody = NULL;
2590     int		 result = 0;
2591     X509	*cert;
2592     char	buf[MAXPATH];
2593 
2594     dprint((9, "encrypt_outgoing_message()"));
2595     smime_init();
2596 
2597     cipher = EVP_aes_256_cbc();
2598 
2599     encerts = sk_X509_new_null();
2600 
2601     /* Look for a certificate for each of the recipients */
2602     for(pf = header->local; pf && pf->name; pf = pf->next)
2603         if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2604             for(a=*pf->addr; a; a=a->next){
2605                 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2606 
2607                 if((cert = get_cert_for(buf, Public, 1)) != NULL){
2608                   sk_X509_push(encerts,cert);
2609                 }else{
2610                     q_status_message2(SM_ORDER, 1, 1,
2611                                       _("Unable to find certificate for <%s@%s>"),
2612 				      a->mailbox, a->host);
2613                     goto end;
2614                 }
2615             }
2616         }
2617 
2618     /* add the sender's certificate so that they can decrypt the message too */
2619     for(a=header->env->from; a ; a = a->next){
2620        snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2621 
2622        if((cert = get_cert_for(buf, Public, 1)) != NULL
2623 	  && sk_X509_find(encerts, cert) == -1)
2624          sk_X509_push(encerts,cert);
2625     }
2626 
2627     in = body_to_bio(body);
2628 
2629     p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2630 
2631     outs = so_get(BioType, NULL, EDIT_ACCESS);
2632     out = bio_from_store(outs);
2633 
2634     i2d_PKCS7_bio(out, p7);
2635     (void) BIO_flush(out);
2636 
2637     so_seek(outs, 0, SEEK_SET);
2638 
2639     newBody = mail_newbody();
2640 
2641     newBody->type = TYPEAPPLICATION;
2642     newBody->subtype = cpystr("pkcs7-mime");
2643     newBody->encoding = ENCBINARY;
2644 
2645     newBody->disposition.type = cpystr("attachment");
2646     set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2647 
2648     newBody->description = cpystr("S/MIME Encrypted Message");
2649     set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2650     set_parameter(&newBody->parameter, "name", "smime.p7m");
2651 
2652     newBody->contents.text.data = (unsigned char *) outs;
2653 
2654     *bodyP = newBody;
2655 
2656     result = 1;
2657 
2658 end:
2659 
2660     BIO_free(in);
2661     PKCS7_free(p7);
2662     sk_X509_pop_free(encerts, X509_free);
2663 
2664     dprint((9, "encrypt_outgoing_message returns %d", result));
2665     return result;
2666 }
2667 
2668 
2669 /*
2670     Get (and decode) the body of the given section of msg
2671  */
2672 static STORE_S*
get_part_contents(long msgno,const char * section)2673 get_part_contents(long msgno, const char *section)
2674 {
2675     long len;
2676     gf_io_t     pc;
2677     STORE_S *store = NULL;
2678     char	*err;
2679 
2680     store = so_get(CharStar, NULL, EDIT_ACCESS);
2681     if(store){
2682         gf_set_so_writec(&pc,store);
2683 
2684         err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2685 
2686         gf_clear_so_writec(store);
2687 
2688         so_seek(store, 0, SEEK_SET);
2689 
2690         if(err)
2691           so_give(&store);
2692     }
2693 
2694     return store;
2695 }
2696 
2697 
2698 static PKCS7 *
get_pkcs7_from_part(long msgno,const char * section)2699 get_pkcs7_from_part(long msgno,const char *section)
2700 {
2701     STORE_S *store = NULL;
2702     PKCS7   *p7 = NULL;
2703     BIO	   *in = NULL;
2704 
2705     store = get_part_contents(msgno, section);
2706 
2707     if(store){
2708 	if(store->src == CharStar){
2709 	    int len;
2710 
2711 	    /*
2712 	     * We're reaching inside the STORE_S structure. We should
2713 	     * probably have a way to get the length, instead.
2714 	     */
2715 	    len = (int) (store->eod - store->dp);
2716 	    in = BIO_new_mem_buf(store->txt, len);
2717 	}
2718 	else{				/* just copy it */
2719 	    unsigned char c;
2720 
2721 	    in = BIO_new(BIO_s_mem());
2722 	    (void) BIO_reset(in);
2723 
2724 	    so_seek(store, 0L, 0);
2725 	    while(so_readc(&c, store)){
2726 		BIO_write(in, &c, 1);
2727 	    }
2728 	}
2729 
2730         if(in){
2731 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2732             if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2733 		/* error */
2734 	    }
2735 
2736 	    BIO_free(in);
2737         }
2738 
2739 	so_give(&store);
2740     }
2741 
2742     return p7;
2743 }
2744 
2745 int
same_cert(X509 * x,X509 * cert)2746 same_cert(X509 *x, X509 *cert)
2747 {
2748    char    bufcert[256],  bufx[256];
2749    int rv = 0;
2750 
2751    get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":");
2752    get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":");
2753    if(strcmp(bufx, bufcert) == 0)
2754      rv = 1;
2755 
2756    return rv;
2757 }
2758 
2759 
2760 /* extract and save certificates from a PKCS7 package.
2761  * Return value:
2762  *	0 - no errors. Either the certificate was in public/
2763  *	    or we could save it there.
2764  *    < 0 - the certificate was not in public/ and the user
2765  *	    did not save it there.
2766  */
2767 
2768 int
smime_extract_and_save_cert(PKCS7 * p7)2769 smime_extract_and_save_cert(PKCS7 *p7)
2770 {
2771     STACK_OF(X509) *signers;
2772     X509 *x, *cert;
2773     char **email;
2774     int i, j, rv, already_saved;
2775 
2776     /* any signers for this message? */
2777     if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2778       return -1;
2779 
2780     rv = 0;
2781     for(i = 0; i < sk_X509_num(signers); i++){
2782 	if((x = sk_X509_value(signers,i)) == NULL)
2783 	    continue;
2784 
2785 	if((email = get_x509_subject_email(x)) != NULL){
2786 	  for(j = 0; email[j] != NULL; j++){
2787 	     already_saved = 0;
2788 			/* check if we have the certificate for this address */
2789 	     cert = get_cert_for(email[j], Public, 1);
2790 			/* if we have one, check if it is the one packaged */
2791 	     if(cert != NULL){
2792 		already_saved = same_cert(x, cert);
2793 		X509_free(cert);
2794 	     }
2795 
2796 			/* if not saved, try to save it */
2797 	     if(already_saved == 0
2798 		  && (*pith_smime_confirm_save)(email[j]) == 1){
2799 		save_cert_for(email[j], x, Public);
2800 		if(ps_global->smime->publiccertlist)	/* renew store */
2801 		  free_certlist(&ps_global->smime->publiccertlist);
2802 	     }
2803 
2804 			/* check if it got saved */
2805 	     cert = get_cert_for(email[j], Public, 1);
2806 			/* if saved, all is good */
2807 	     if(cert != NULL)
2808 		X509_free(cert);
2809 	     else	/* else, we do not have this certificate saved */
2810 		rv += -1;
2811 
2812 	     fs_give((void **) &email[j]);
2813 	  }
2814 	  fs_give((void **) email);
2815 	}
2816     }
2817     sk_X509_free(signers);
2818 
2819     return rv;
2820 }
2821 
2822 /*
2823  * Try to verify a signature.
2824  *
2825  * p7  - the pkcs7 object to verify
2826  * in  - the plain data to verify (NULL if not detached)
2827  * out - BIO to which to write the opaque data
2828  * silent - if non zero, do not print errors, only print success.
2829  */
2830 static int
do_signature_verify(PKCS7 * p7,BIO * in,BIO * out,int silent)2831 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2832 {
2833     STACK_OF(X509) *otherCerts = NULL;
2834     CertList *cl;
2835     int result, flags;
2836     const char *data;
2837     long err;
2838 
2839     if(!s_cert_store){
2840 	if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2841 		_("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2842 
2843     	return -1;
2844     }
2845 
2846     flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0;
2847 
2848     if(ps_global->smime->publiccertlist == NULL){
2849        renew_cert_data(&ps_global->smime->publiccertlist, Public);
2850        for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){
2851 	  if(cl->x509_cert == NULL){
2852 	    char *s = strrchr(cl->name, '.');
2853 	    *s = '\0';
2854 	    cl->x509_cert = get_cert_for(cl->name, Public, 1);
2855 	    *s = '.';
2856 	  }
2857        }
2858     }
2859 
2860     if(ps_global->smime->publiccertlist){
2861        otherCerts = sk_X509_new_null();
2862        for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next)
2863 	   if(cl->x509_cert != NULL)
2864 	      sk_X509_push(otherCerts, X509_dup(cl->x509_cert));
2865     }
2866 
2867     result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags);
2868 
2869     if(result){
2870 	q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2871     }
2872     else{
2873 	err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2874 
2875 	if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2876 
2877 	    /*
2878 	     * verification failed due to an error in verifying a certificate.
2879 	     * Just write the "out" BIO, and leave. Of course let the user
2880 	     * know about this. Make two more attempts to get the data out. The
2881 	     * last one should succeed. In any case, let the user know why it
2882 	     * failed.
2883 	     */
2884 	    if(PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY) == 0)
2885 		PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY|PKCS7_NOSIGS);
2886 	}
2887 	if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2888 		_("Couldn't verify S/MIME signature: %s"), (char *) openssl_error_string());
2889     }
2890 
2891     sk_X509_pop_free(otherCerts, X509_free);
2892 
2893     return result;
2894 }
2895 
2896 /* Big comment, explaining the mess that exists out there, and how we deal
2897    with it, and also how we solve the problems that are created this way.
2898 
2899   When Alpine sends a message, it constructs that message, computes the
2900   signature, but then it forgets the message it signed and reconstructs it
2901   again. Since it signs a message containing a notice about "mime aware
2902   tools", but it does not send that we do not include that in the part
2903   that is signed, and that takes care of much of the problems.
2904 
2905   Another problem is what is received from the servers. All servers tested
2906   seem to transmit the message that was signed intact and Alpine can check
2907   the signature correctly. That is not a problem. The problem arises when
2908   the message includes attachments. In this case different servers send
2909   different things, so it will be up to us to figure out what is the text
2910   that was actually signed. Confused? here is the story:
2911 
2912   When a message containing and attachment is sent by Alpine, UW-IMAP,
2913   Panda-IMAP, Gmail, and local reading of folders send exactly the message
2914   that was sent by Alpine, but GMX.com, Exchange, and probably other
2915   servers add a trailing \r\n in the message, so when validating the
2916   signature, these messages will not validate. There are several things
2917   that can be done.
2918 
2919   1. Add a trailing \r\n to any message that contains attachments, sign that
2920      and send that. In this way, all messages will validate with all
2921      servers.
2922 
2923   2. Compatibility mode: If a message has an attachment, contains a trailing
2924      \r\n and does not validate (sent by an earlier version of Alpine),
2925      remove the trailing \r\n and try to revalidate again.
2926 
2927   3. We do not add \r\n to validate a message that we sent, because that
2928      would only work in Alpine, and not in any other client. That would
2929      not be a good thing to do.
2930 
2931   PART II
2932 
2933   Now we have to deal with encrypted and signed messages. The problem is
2934   that c-client makes all its pointers point to "on disk" content, but
2935   since we decrypted the data earlier, we have to make sure of two things.
2936   One is that we saved that data (so we do not have to decrypt it again)
2937   and second that we can use it.
2938 
2939   In order to save the data we use create_local_cache, so that we do not
2940   have to redecrypt the message. Once this is saved, c-client functions will
2941   find it and send it to us in mail_fetch_mime and mail_fetch_body.
2942 
2943   PART III
2944 
2945   When we are trying to verify messages with detached signatures, some
2946   imap servers send incorrect information in the mail_fetch_mime call. By
2947   incorrect I mean that this is not fetched directly from the message, but
2948   it is read from the message, processed, and then the processed part is
2949   sent to us, so this text might not agree with what is in the message,
2950   and so the validation of the signature might fail. However, the good
2951   news is that the message validates if saved to a local folder. This
2952   means that if normal validation does not work we can make it work by
2953   saving the message locally and validating that. This is implemented
2954   below, and causes delay in the display of the message. I am considering
2955   at this time not to do this automatically, but wait for the user to tell
2956   us to do it for them by means of a command available in the
2957   mail_view_screen. This might help in other situations, where a message
2958   is supposed to have an attachment, but it can not be seen in the
2959   processed text. Nevertheless, at this time, this is automatic, and is
2960   causing a delay in the processing of the message, but it is validating
2961   correctly all messages.
2962 
2963   PART IV
2964 
2965   When the user sends a message as encrypted and signed, this code used to
2966   encrypt first, and then sign the pkcs7 body, but it turns out that some
2967   other clients can not handle these messages. While we could argue that the
2968   other clients need to improve, we will support reading messages in both
2969   ways, and will send messages using this technique; that is, signed first,
2970   encrypted second. It seems that all tested clients support this way, so it
2971   should be safe to do so.
2972  */
2973 
2974 typedef struct smime_filter_s {
2975   void (*filter)();
2976 } SMIME_FILTER_S;
2977 
2978 SMIME_FILTER_S sig_filter[] = {
2979    {smime_remove_trailing_crlf},
2980    {smime_remove_folding_space}
2981 };
2982 
2983 #define TOTAL_FILTERS  (sizeof(sig_filter)/sizeof(sig_filter[0]))
2984 #define TOTAL_SIGFLTR  (1 << TOTAL_FILTERS)  /* not good, keep filters to a low number */
2985 
2986 void
smime_remove_trailing_crlf(char ** mimetext,unsigned long * mimelen,char ** bodytext,unsigned long * bodylen)2987 smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen,
2988 			char **bodytext, unsigned long *bodylen)
2989 {
2990   if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2))
2991     *bodylen -= 2;
2992 }
2993 
2994 void
smime_remove_folding_space(char ** mimetext,unsigned long * mimelen,char ** bodytext,unsigned long * bodylen)2995 smime_remove_folding_space(char **mimetext, unsigned long *mimelen,
2996 			char **bodytext, unsigned long *bodylen)
2997 {
2998    char *s = NULL, *t;
2999    unsigned long mlen = *mimelen;
3000 
3001    if(*mimetext){
3002       for (s = t = *mimetext; t - *mimetext < *mimelen; ){
3003 	 if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){
3004 	    *s++ = ' ';
3005 	    t += 3;
3006 	    mlen -= 2;
3007 	 }
3008 	 else
3009 	    *s++ = *t++;
3010       }
3011       *mimelen = mlen;
3012    }
3013 }
3014 
3015 int
smime_validate_extra_test(char * mimetext,unsigned long mimelen,char * bodytext,unsigned long bodylen,PKCS7 * p7,int nflag)3016 smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag)
3017 {
3018   int result, i, j, flag;
3019   char *mtext, *btext;
3020   unsigned long mlen, blen;
3021   BIO *in;
3022 
3023   mtext = mimelen ? fs_get(mimelen+1) : NULL;
3024   btext = fs_get(bodylen+1);
3025   result = 0;
3026 
3027   flag = 1;	/* silence all failures */
3028   for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
3029      if((in = BIO_new(BIO_s_mem())) == NULL)
3030        return -1;
3031 
3032      (void) BIO_reset(in);
3033 
3034      if(i+1 == TOTAL_SIGFLTR)
3035 	flag = nflag;
3036 
3037      if(mimelen)
3038 	strncpy(mtext, mimetext, mlen = mimelen);
3039      strncpy(btext, bodytext, blen = bodylen);
3040      for(j = 0; j < TOTAL_FILTERS; j++)
3041 	if((i >> j) & 1)
3042 	  (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
3043      if(mtext != NULL)
3044 	BIO_write(in, mtext, mlen);
3045      BIO_write(in, btext, blen);
3046      result = do_signature_verify(p7, in, NULL, flag);
3047      BIO_free(in);
3048   }
3049   if(mtext) fs_give((void **)&mtext);
3050   if(btext) fs_give((void **)&btext);
3051   return result;
3052 }
3053 
3054 /*
3055  * Given a multipart body of type multipart/signed, attempt to verify it.
3056  * Returns non-zero if the body was changed.
3057  */
3058 static int
do_detached_signature_verify(BODY * b,long msgno,char * section)3059 do_detached_signature_verify(BODY *b, long msgno, char *section)
3060 {
3061     PKCS7   *p7 = NULL;
3062     BIO	    *in = NULL;
3063     PART    *p;
3064     int	     result, modified_the_body = 0;
3065     int      flag;	/* 1 silent, 0 not silent */
3066     unsigned long mimelen, bodylen;
3067     char     newSec[100], *mimetext, *bodytext;
3068     char    *what_we_did;
3069     SIZEDTEXT *st;
3070 
3071     dprint((9, "do_detached_signature_verify(msgno=%ld type=%d subtype=%s section=%s)", msgno, b->type, b->subtype ? b->subtype : "NULL", (section && *section) ? section : (section != NULL) ? "Top" : "NULL"));
3072 
3073     smime_init();
3074     mimetext = bodytext = NULL;
3075 
3076     /* if it was signed and then encrypted, use the decrypted text
3077      * to check the validity of the signature
3078      */
3079     if(b->sparep){
3080 	if(get_body_sparep_type(b->sparep) == SizedText){
3081 	   /* bodytext includes mimetext */
3082 	   st = (SIZEDTEXT *) get_body_sparep_data(b->sparep);
3083 	   bodytext = (char *) st->data;
3084 	   bodylen  = st->size;
3085 	   mimetext = NULL;
3086 	   mimelen  = 0L;
3087 	}
3088     }
3089     else{
3090       snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3091       mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3092       if(mimetext)
3093         bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3094 
3095       if(mimetext == NULL || bodytext ==  NULL)
3096          return modified_the_body;
3097     }
3098 
3099     snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3100 
3101     if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
3102        || (in = BIO_new(BIO_s_mem())) == NULL)
3103 	return modified_the_body;
3104 
3105     (void) BIO_reset(in);
3106     if(mimetext != NULL)
3107       BIO_write(in, mimetext, mimelen);
3108     BIO_write(in, bodytext, bodylen);
3109 
3110     smime_extract_and_save_cert(p7);
3111 
3112     if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3113       flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
3114 		? 0 : 1;
3115       result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
3116       if(result < 0)
3117          return modified_the_body;
3118       if(result == 0
3119 	   && mimelen > 0	/* do not do this for encrypted messages */
3120 	   && IS_REMOTE(ps_global->mail_stream->mailbox)){
3121 	   char *fetch;
3122 	   unsigned long hlen, tlen;
3123 	   STORE_S *msg_so;
3124 
3125 	   BIO_free(in);
3126 	   if((in = BIO_new(BIO_s_mem())) != NULL
3127 	      && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
3128 				NULL, &hlen, FT_PEEK)) != NULL
3129 	      && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
3130 	      && so_nputs(msg_so, fetch, (long) hlen)
3131 	      && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
3132 				&tlen, FT_PEEK)) != NULL
3133 	      && so_nputs(msg_so, fetch, tlen)){
3134 		STRING bs;
3135 		char *h = (char *) so_text(msg_so);
3136 		char *bstart = strstr(h, "\r\n\r\n");
3137 		ENVELOPE *env;
3138 		BODY *body, *tmpB;
3139 
3140 		bstart += 4;
3141 		INIT(&bs, mail_string, bstart, tlen);
3142 		rfc822_parse_msg_full(&env, &body, h, bstart-h-4, &bs, BADHOST, 0, 0);
3143 		mail_free_envelope(&env);
3144 
3145 		mail_free_body_part(&b->nested.part);
3146 		tmpB = mail_body_section(body, (unsigned char *) section);
3147 		if(MIME_MSG(tmpB->type, tmpB->subtype))
3148 		   b->nested.part = tmpB->nested.msg->body->nested.part;
3149 		else
3150 		   b->nested.part = tmpB->nested.part;
3151 		create_local_cache(bstart, bstart, &b->nested.part->body, 1);
3152 		modified_the_body = 1;
3153 
3154 		snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3155 
3156 		mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3157 
3158 		if(mimetext)
3159 		   bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3160 
3161 		if (mimetext == NULL || bodytext ==  NULL)
3162 		   return modified_the_body;
3163 
3164 		snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3165 
3166 		if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
3167 		  return modified_the_body;
3168 
3169 		(void) BIO_reset(in);
3170 		BIO_write(in, mimetext, mimelen);
3171 		BIO_write(in, bodytext, bodylen);
3172 		so_give(&msg_so);
3173 
3174 		if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3175 		  result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
3176 		  if(result < 0)
3177 		    return modified_the_body;
3178 		}
3179 	   }
3180 	}
3181     }
3182 
3183     BIO_free(in);
3184     if(b->subtype)
3185 	fs_give((void**) &b->subtype);
3186 
3187     b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3188     b->encoding = ENC8BIT;
3189 
3190     if(b->description)
3191 	fs_give ((void**) &b->description);
3192 
3193     what_we_did = result ? _("This message was cryptographically signed.") :
3194 			   _("This message was cryptographically signed but the signature could not be verified.");
3195 
3196     b->description = cpystr(what_we_did);
3197 
3198     b->sparep = create_body_sparep(P7Type, p7);
3199 
3200     p = b->nested.part;
3201 
3202     /* p is signed plaintext */
3203     if(p && p->next)
3204 	mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
3205 
3206     modified_the_body = 1;
3207 
3208     return modified_the_body;
3209 }
3210 
3211 
3212 PERSONAL_CERT *
find_certificate_matching_recip_info(PKCS7_RECIP_INFO * ri)3213 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
3214 {
3215     PERSONAL_CERT *x = NULL;
3216 
3217     if(ps_global->smime){
3218 	for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
3219 	    X509 *mine;
3220 
3221 	    mine = x->cert;
3222 
3223 	    if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,X509_get_issuer_name(mine)) &&
3224 		    !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,X509_get_serialNumber(mine))){
3225 		break;
3226 	    }
3227 	}
3228     }
3229 
3230     return x;
3231 }
3232 
3233 
3234 static PERSONAL_CERT *
find_certificate_matching_pkcs7(PKCS7 * p7)3235 find_certificate_matching_pkcs7(PKCS7 *p7)
3236 {
3237     int i;
3238     STACK_OF(PKCS7_RECIP_INFO) *recips;
3239     PERSONAL_CERT *x = NULL;
3240 
3241     recips = p7->d.enveloped->recipientinfo;
3242 
3243     for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
3244         PKCS7_RECIP_INFO	*ri;
3245 
3246         ri = sk_PKCS7_RECIP_INFO_value(recips, i);
3247 
3248         if((x=find_certificate_matching_recip_info(ri))!=0){
3249             break;
3250         }
3251     }
3252 
3253     return x;
3254 }
3255 
3256 /* decrypt an encrypted file.
3257    Args: fp - the path to the encrypted file.
3258 	 rv - a code that tells the caller what happened inside the function
3259 	 pcert - a personal certificate that was used to encrypt this file
3260    Returns the decoded text allocated in a char *, whose memory must be
3261    freed by caller
3262  */
3263 
3264 char *
decrypt_file(char * fp,int * rv,PERSONAL_CERT * pc)3265 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
3266 {
3267   PKCS7 *p7 = NULL;
3268   char *text, *tmp;
3269   BIO *in = NULL, *out = NULL;
3270   long unsigned int len;
3271   void *ret;
3272 
3273   if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
3274     return NULL;
3275 
3276   tmp = strchr(text + strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE), '-');
3277   if(tmp != NULL) *tmp = '\0';
3278   tmp = text + strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE);
3279 
3280   ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
3281 
3282   if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
3283      p7 = d2i_PKCS7_bio(in, NULL);
3284      BIO_free(in);
3285   }
3286 
3287   if (text) fs_give((void **)&text);
3288   if (ret)  fs_give((void **)&ret);
3289 
3290   if (rv) *rv = pc->key == NULL ? -1 : 1;
3291 
3292   out = BIO_new(BIO_s_mem());
3293   (void) BIO_reset(out);
3294 
3295   if(PKCS7_decrypt(p7, pc->key, pc->cert, out, 0) != 0){
3296     len = BIO_get_mem_data(out, &tmp);
3297     text = fs_get((len+1)*sizeof(char));
3298     strncpy(text, tmp, len);
3299     text[len] = '\0';
3300     BIO_free(out);
3301   } else
3302     q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3303                               (char *) openssl_error_string());
3304   PKCS7_free(p7);
3305 
3306   return text;
3307 }
3308 
3309 /*
3310  * Try to decode (decrypt or verify a signature) a PKCS7 body
3311  * Returns non-zero if something was changed.
3312  */
3313 static int
do_decoding(BODY * b,long msgno,const char * section)3314 do_decoding(BODY *b, long msgno, const char *section)
3315 {
3316     int modified_the_body = 0;
3317     BIO *out = NULL;
3318     PKCS7 *p7 = NULL;
3319     X509 *recip = NULL;
3320     EVP_PKEY *key = NULL;
3321     PERSONAL_CERT 	*pcert = NULL;
3322     char    *what_we_did = "";
3323     char     null[1];
3324 
3325     dprint((9, "do_decoding(msgno=%ld type=%d subtype=%s section=%s)", msgno, b->type, b->subtype ? b->subtype : "NULL", (section && *section) ? section : (section != NULL) ? "Top" : "NULL"));
3326     null[0] = '\0';
3327     smime_init();
3328 
3329     /*
3330      *	Extract binary data from part to an in-memory store
3331      */
3332 
3333     if(b->sparep){
3334         if(get_body_sparep_type(b->sparep) == P7Type)
3335 	  p7 = (PKCS7*) get_body_sparep_data(b->sparep);
3336     }
3337     else{
3338 	p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3339 	if(!p7){
3340             q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3341 			     (char*) openssl_error_string());
3342             goto end;
3343 	}
3344 
3345     	/*
3346     	 * Save the PKCS7 object for later dealings by the user interface.
3347 	 * It will be cleaned up when the body is garbage collected.
3348 	 */
3349 	b->sparep = create_body_sparep(P7Type, p7);
3350     }
3351 
3352     dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3353 
3354     if(PKCS7_type_is_signed(p7)){
3355     	int 	sigok;
3356 
3357 	out = BIO_new(BIO_s_mem());
3358 	(void) BIO_reset(out);
3359 	BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3360 
3361     	sigok = do_signature_verify(p7, NULL, out, 0);
3362 
3363 	what_we_did = sigok ? _("This message was cryptographically signed.") :
3364 			      _("This message was cryptographically signed but the signature could not be verified.");
3365 
3366 	/* make sure it's null terminated */
3367 	BIO_write(out, null, 1);
3368     }
3369     else if(!PKCS7_type_is_enveloped(p7)){
3370         q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3371         goto end;
3372     }
3373     else{ /* It *is* enveloped */
3374 	int decrypt_result;
3375 
3376 	what_we_did = _("This message was encrypted.");
3377 
3378 	/* now need to find a cert that can decrypt this */
3379 	pcert = find_certificate_matching_pkcs7(p7);
3380 
3381 	if(!pcert){
3382             q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3383             goto end;
3384 	}
3385 
3386 	recip = pcert->cert;
3387 
3388 	if(!load_private_key(pcert)
3389 	   && ps_global->smime
3390 	   && ps_global->smime->need_passphrase
3391 	   && !ps_global->smime->already_auto_asked){
3392 	    /* Couldn't load key with blank password, ask user */
3393 	    ps_global->smime->already_auto_asked = 1;
3394 	    if(pith_opt_smime_get_passphrase){
3395 	      (*pith_opt_smime_get_passphrase)();
3396 	      load_private_key(pcert);
3397 	    }
3398 	}
3399 
3400 	key = pcert->key;
3401 	if(!key)
3402     	  goto end;
3403 
3404 	out = BIO_new(BIO_s_mem());
3405 	(void) BIO_reset(out);
3406 	BIO_puts(out, "MIME-Version: 1.0\r\n");
3407 
3408 	decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3409 
3410 	if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3411 	  forget_private_keys();
3412 
3413 	if(!decrypt_result){
3414             q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3415 			      (char*) openssl_error_string());
3416             goto end;	}
3417 
3418 	BIO_write(out, null, 1);
3419     }
3420 
3421     /*
3422      * We've now produced a flattened MIME object in BIO out.
3423      * It needs to be turned back into a BODY.
3424      */
3425 
3426     if(out){
3427         BODY	 *body;
3428         ENVELOPE *env;
3429         char	 *h = NULL;
3430         char	 *bstart;
3431         STRING	  s;
3432 	BUF_MEM  *bptr = NULL;
3433 	int we_free = 0;
3434 
3435 	BIO_get_mem_ptr(out, &bptr);
3436 	if(bptr)
3437 	   h = bptr->data;
3438 
3439         /* look for start of body */
3440         bstart = strstr(h, "\r\n\r\n");
3441 
3442 	if(!bstart){
3443 	   /*
3444 	    * Some clients do not canonicalize before encrypting, so
3445 	    * look for "\n\n" instead.
3446 	    */
3447 	   bstart = strstr(h, "\n\n");
3448 	   if(bstart){
3449 	      int lines;
3450 	      char *s, *t;
3451 	      for(lines = 0, bstart = h; (bstart = strchr(bstart, '\n')) != NULL;
3452 					bstart++, lines++);
3453 	      h = t = fs_get(strlen(bptr->data) + lines + 1);
3454 	      we_free++;
3455 	      for(s = bptr->data; *s != '\0'; s++)
3456 		if(*s == '\n' && *(s-1) != '\r'){
3457 		  *t++ = '\r';
3458 		  *t++ = '\n';
3459 		}
3460 		else
3461 		  *t++ = *s;
3462 	      *t = '\0';
3463 	      bstart = strstr(h, "\r\n\r\n");
3464 	   }
3465 	}
3466 
3467         if(!bstart){
3468             q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3469      	}
3470 	else{
3471 	    SIZEDTEXT *st;
3472             bstart += 4; /* skip over CRLF*2 */
3473 
3474             INIT(&s, mail_string, bstart, strlen(bstart));
3475             rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3476             mail_free_envelope(&env); /* Don't care about this */
3477 
3478 	    if(body->type == TYPEMULTIPART
3479 		&& !strucmp(body->subtype, "SIGNED")){
3480 	      char *cookie = NULL;
3481 	      PARAMETER *param;
3482 	      for (param = body->parameter; param && !cookie; param = param->next)
3483 		if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3484 	      if(cookie != NULL){
3485 	        st = fs_get(sizeof(SIZEDTEXT));
3486 	        st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3487 	        st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3488 	        body->sparep = create_body_sparep(SizedText, (void *)st);
3489 	      }
3490 	      else
3491 		q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3492 	    }
3493 	    body->mime.offset    = 0;
3494 	    body->mime.text.size = 0;
3495 
3496 	    /*
3497 	     * Now convert original body (application/pkcs7-mime)
3498 	     * to a multipart body with one sub-part (the decrypted body).
3499 	     * Note that the sub-part may also be multipart!
3500 	     */
3501 
3502 	    b->type = TYPEMULTIPART;
3503 	    if(b->subtype)
3504 	      fs_give((void **) &b->subtype);
3505 
3506 	    /*
3507 	     * This subtype is used in mailview.c to annotate the display of
3508 	     * encrypted or signed messages. We know for sure then that it's a PKCS7
3509 	     * part because the sparep field is set to the PKCS7 object (see above).
3510 	     */
3511 	    b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3512 	    b->encoding = ENC8BIT;
3513 
3514 	    if(b->description)
3515 	      fs_give((void **) &b->description);
3516 
3517 	    b->description = cpystr(what_we_did);
3518 
3519 	    if(b->disposition.type)
3520 	      fs_give((void **) &b->disposition.type);
3521 
3522 	    if(b->contents.text.data)
3523 	      fs_give((void **) &b->contents.text.data);
3524 
3525 	    if(b->parameter)
3526 	      mail_free_body_parameter(&b->parameter);
3527 
3528 	    /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3529 	    b->nested.part = fs_get(sizeof(PART));
3530 	    b->nested.part->body = *body;
3531 	    b->nested.part->next = NULL;
3532 
3533 	    fs_give((void**) &body);
3534 
3535             /*
3536              * IMPORTANT BIT: set the body->contents.text.data elements to contain
3537              * the decrypted data. Otherwise, it'll try to load it from the original
3538              * data. Eek.
3539 	     */
3540             create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3541 
3542             modified_the_body = 1;
3543         }
3544 	if(we_free)
3545 	   fs_give((void **) &h);
3546     }
3547 
3548 end:
3549     if(out)
3550       BIO_free(out);
3551 
3552     return modified_the_body;
3553 }
3554 
3555 
3556 /*
3557  * Recursively handle PKCS7 bodies in our message.
3558  *
3559  * Returns non-zero if some fiddling was done.
3560  */
3561 static int
do_fiddle_smime_message(BODY * b,long msgno,char * section)3562 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3563 {
3564     int modified_the_body = 0;
3565 
3566     if(!b)
3567       return 0;
3568 
3569     dprint((9, "do_fiddle_smime_message(msgno=%ld type=%d subtype=%s section=%s)", msgno, b->type, b->subtype ? b->subtype : "NULL", (section && *section) ? section : (section != NULL) ? "Top" : "NULL"));
3570 
3571     if(is_pkcs7_body(b)){
3572 
3573         if(do_decoding(b, msgno, section)){
3574             /*
3575              *	b should now be a multipart message:
3576 	     *   fiddle it too in case it's been multiply-encrypted!
3577              */
3578 
3579             /* fallthru */
3580             modified_the_body = 1;
3581         }
3582     }
3583 
3584     if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3585 
3586         PART	*p;
3587         int		partNum;
3588         char	newSec[100];
3589 
3590 	if(MIME_MULT_SIGNED(b->type, b->subtype)){
3591 
3592 
3593             /*
3594              * Ahah. We have a multipart signed entity.
3595 	     *
3596 	     * Multipart/signed
3597              *   part 1 (signed thing)
3598              *   part 2 (the pkcs7 signature)
3599 	     *
3600 	     * We're going to convert that to
3601 	     *
3602 	     * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3603              *   part 1 (signed thing)
3604 	     *   part 2 has been freed
3605 	     *
3606 	     * We also extract the signature from part 2 and save it
3607 	     * in the multipart body->sparep, and we add a description
3608 	     * in the multipart body->description.
3609 	     *
3610 	     *
3611 	     * The results of a decrypted message will be similar. It
3612 	     * will be
3613 	     *
3614 	     * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3615              *   part 1 (decrypted thing)
3616              */
3617 
3618             modified_the_body += do_detached_signature_verify(b, msgno, section);
3619         }
3620 	else if(MIME_MSG(b->type, b->subtype)){
3621 	    modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3622 	}
3623 	else{
3624 
3625             for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3626                 /* Append part number to the section string */
3627 
3628                 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3629 
3630                 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3631             }
3632         }
3633     }
3634 
3635     return modified_the_body;
3636 }
3637 
3638 
3639 /*
3640  * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3641  * Returns non-zero if something was changed.
3642  */
3643 int
fiddle_smime_message(BODY * b,long msgno)3644 fiddle_smime_message(BODY *b, long msgno)
3645 {
3646     return do_fiddle_smime_message(b, msgno, "");
3647 }
3648 
3649 
3650 /********************************************************************************/
3651 
3652 
3653 /*
3654  *  Output a string in a distinctive style
3655  */
3656 void
gf_puts_uline(char * txt,gf_io_t pc)3657 gf_puts_uline(char *txt, gf_io_t pc)
3658 {
3659     pc(TAG_EMBED); pc(TAG_BOLDON);
3660     gf_puts(txt, pc);
3661     pc(TAG_EMBED); pc(TAG_BOLDOFF);
3662 }
3663 
3664 /* get_chain_for_cert: error and level are mandatory arguments */
STACK_OF(X509)3665 STACK_OF(X509) *
3666 get_chain_for_cert(X509 *cert, int *error, int *level)
3667 {
3668   STACK_OF(X509) *chain = NULL;
3669   X509_STORE_CTX *ctx;
3670   X509 *x, *xtmp;
3671   int rc;       /* return code */
3672 
3673   *level = -1;
3674   *error = 0;
3675   ERR_clear_error();
3676   if((s_cert_store != NULL) && (ctx = X509_STORE_CTX_new()) != NULL){
3677       X509_STORE_set_flags(s_cert_store, 0);
3678       if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3679 	*error   = X509_STORE_CTX_get_error(ctx);
3680       else if((chain = sk_X509_new_null()) != NULL){
3681 	for(x = cert; ; x = xtmp){
3682 	    if(++*level > 0)
3683 	      sk_X509_push(chain, X509_dup(x));
3684 	    rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3685 	    if(rc < 0)
3686 	      *error = 1;
3687 	    if(rc <= 0)
3688 	      break;
3689 	    if(!X509_check_issued(xtmp, xtmp))
3690 	       break;
3691 	}
3692       }
3693       X509_STORE_CTX_free(ctx);
3694   }
3695   return chain;
3696 }
3697 
3698 
3699 /*
3700  * Sign a message. Called from call_mailer in send.c.
3701  *
3702  * This takes the header for the outgoing message as well as a pointer
3703  * to the current body (which may be reallocated).
3704  * The last argument (BODY **bp) is an argument that tells Alpine
3705  * if the body has 8 bit. if *bp is not null we compute two signatures
3706  * one for the quoted-printable encoded message, and another for the
3707  * 8bit encoded message. We return the signature for the 8bit encoded
3708  * part in p2->body.mime.text.data.
3709  * The reason why we compute two signatures is so that we can decide
3710  * which one to use later, and we only do it in the case that *bp is
3711  * not null. If we did not do this, then we might not be able to sign
3712  * a message until we log in to the smtp server, so instead of doing
3713  * that, we get ready for any possible situation we might find.
3714  */
3715 int
sign_outgoing_message(METAENV * header,BODY ** bodyP,int dont_detach,BODY ** bp)3716 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3717 {
3718     STORE_S *outs = NULL;
3719     STORE_S *outs_2 = NULL;
3720     BODY    *body = *bodyP;
3721     BODY    *newBody = NULL;
3722     PART    *p1 = NULL;
3723     PART    *p2 = NULL;
3724     PERSONAL_CERT   *pcert;
3725     BIO *in = NULL;
3726     BIO *in_2 = NULL;
3727     BIO *out = NULL;
3728     BIO *out_2 = NULL;
3729     PKCS7   *p7 = NULL;
3730     PKCS7   *p7_2 = NULL;
3731     STACK_OF(X509) *chain;
3732     const EVP_MD *md = EVP_sha256();	/* use this digest instead of sha1 */
3733     int result = 0, error;
3734     int flags = dont_detach ? 0 : PKCS7_DETACHED;
3735     int level;
3736 
3737     dprint((9, "sign_outgoing_message()"));
3738 
3739     smime_init();
3740 
3741     /* Look for a private key matching the sender address... */
3742 
3743     pcert = match_personal_cert(header->env);
3744 
3745     if(!pcert){
3746         q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3747 	goto end;
3748     }
3749 
3750     if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3751     	/* Couldn't load key with blank password, try again */
3752 	if(pith_opt_smime_get_passphrase){
3753 	  (*pith_opt_smime_get_passphrase)();
3754 	  load_private_key(pcert);
3755 	}
3756     }
3757 
3758     if(!pcert->key)
3759       goto end;
3760 
3761     if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3762 	|| level == 0){
3763 	sk_X509_pop_free(chain, X509_free);
3764 	chain = NULL;
3765     }
3766 
3767     if(error)
3768       q_status_message(SM_ORDER, 1, 1,
3769 	_("Not all certificates needed to verify signature included in signed message"));
3770 
3771     in = body_to_bio(body);
3772 
3773     flags |= PKCS7_PARTIAL;
3774     if((p7 = PKCS7_sign(NULL, NULL, chain, in, flags)) != NULL
3775 	&& PKCS7_sign_add_signer(p7, pcert->cert, pcert->key, md, flags))
3776 	   PKCS7_final(p7, in, flags);
3777 
3778     if(bp && *bp){
3779       int i, save_encoding;
3780 
3781       for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3782 
3783       if(i > ENCMAX){             /* no empty encoding slots! */
3784          *bp = NULL;
3785       }
3786       else {
3787 	save_encoding = (*bp)->encoding;
3788 	body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3789 
3790 	in_2 = body_to_bio(body);
3791 
3792 	body_encodings[i] = NULL;
3793 	(*bp)->encoding = save_encoding;
3794       }
3795     }
3796 
3797     if(bp && *bp){
3798        if((p7_2 = PKCS7_sign(NULL, NULL, chain, in_2, flags)) != NULL
3799 	&& PKCS7_sign_add_signer(p7_2, pcert->cert, pcert->key, md, flags))
3800 	   PKCS7_final(p7_2, in_2, flags);
3801     }
3802 
3803     if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3804       forget_private_keys();
3805 
3806     if(chain)
3807       sk_X509_pop_free(chain, X509_free);
3808 
3809     if(!p7){
3810         q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3811 	goto end;
3812     }
3813 
3814     outs = so_get(BioType, NULL, EDIT_ACCESS);
3815     out = bio_from_store(outs);
3816 
3817     i2d_PKCS7_bio(out, p7);
3818     (void) BIO_flush(out);
3819 
3820     so_seek(outs, 0, SEEK_SET);
3821 
3822     if(bp && *bp && p7_2){
3823       outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3824       out_2 = bio_from_store(outs_2);
3825 
3826       i2d_PKCS7_bio(out_2, p7_2);
3827       (void) BIO_flush(out_2);
3828 
3829       so_seek(outs_2, 0, SEEK_SET);
3830     }
3831 
3832     if((flags&PKCS7_DETACHED)==0){
3833 
3834     	/* the simple case: the signed data is in the pkcs7 object */
3835 
3836 	newBody = mail_newbody();
3837 
3838 	setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3839 
3840     	newBody->contents.text.data = (unsigned char *) outs;
3841 	*bodyP = newBody;
3842 
3843 	result = 1;
3844     }
3845     else{
3846 
3847 	/*
3848 	 * OK.
3849     	 * We have to create a new body as follows:
3850 	 *
3851 	 * multipart/signed; blah blah blah
3852 	 *      reference to existing body
3853 	 *
3854 	 *	pkcs7 object
3855 	 */
3856 
3857 	newBody = mail_newbody();
3858 
3859 	newBody->type = TYPEMULTIPART;
3860 	newBody->subtype = cpystr("signed");
3861 	newBody->encoding = ENC7BIT;
3862 
3863 	set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3864 	set_parameter(&newBody->parameter, "micalg", "sha-256");
3865 
3866 	p1 = mail_newbody_part();
3867 	p2 = mail_newbody_part();
3868 
3869     	/*
3870     	 * This is nasty. We're just copying the body in here,
3871 	 * but since our newBody is freed at the end of call_mailer,
3872 	 * we mustn't let this body (the original one) be freed twice.
3873 	 */
3874 	p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3875 
3876 	p1->next = p2;
3877 
3878 	setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3879     	p2->body.mime.text.data = (unsigned char *) outs_2;
3880     	p2->body.contents.text.data = (unsigned char *) outs;
3881 
3882     	newBody->nested.part = p1;
3883 
3884 	*bodyP = newBody;
3885 
3886 	result = 1;
3887     }
3888 
3889 end:
3890 
3891     PKCS7_free(p7);
3892     BIO_free(in);
3893 
3894     if(bp && *bp){
3895       if(p7_2) PKCS7_free(p7_2);
3896       BIO_free(in_2);
3897     }
3898 
3899     dprint((9, "sign_outgoing_message returns %d", result));
3900     return result;
3901 }
3902 
3903 
3904 SMIME_STUFF_S *
new_smime_struct(void)3905 new_smime_struct(void)
3906 {
3907     SMIME_STUFF_S *ret = NULL;
3908 
3909     ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3910     memset((void *) ret, 0, sizeof(*ret));
3911     ret->publictype = Nada;
3912 
3913     return ret;
3914 }
3915 
3916 
3917 static void
free_smime_struct(SMIME_STUFF_S ** smime)3918 free_smime_struct(SMIME_STUFF_S **smime)
3919 {
3920     if(smime && *smime){
3921 	if((*smime)->passphrase_emailaddr){
3922 	  int i;
3923 	  for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3924 	     fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3925 	  fs_give((void **) (*smime)->passphrase_emailaddr);
3926 	}
3927 
3928 	if((*smime)->publicpath)
3929 	  fs_give((void **) &(*smime)->publicpath);
3930 
3931 	if((*smime)->publiccertlist)
3932 	  free_certlist(&(*smime)->publiccertlist);
3933 
3934 	if((*smime)->backuppubliccertlist)
3935 	  free_certlist(&(*smime)->backuppubliccertlist);
3936 
3937 	if((*smime)->cacertlist)
3938 	  free_certlist(&(*smime)->cacertlist);
3939 
3940 	if((*smime)->backupcacertlist)
3941 	  free_certlist(&(*smime)->backupcacertlist);
3942 
3943 	if((*smime)->privatecertlist)
3944 	  free_certlist(&(*smime)->privatecertlist);
3945 
3946 	if((*smime)->backupprivatecertlist)
3947 	  free_certlist(&(*smime)->backupprivatecertlist);
3948 
3949 	if((*smime)->publiccontent)
3950 	  fs_give((void **) &(*smime)->publiccontent);
3951 
3952 	if((*smime)->privatepath)
3953 	  fs_give((void **) &(*smime)->privatepath);
3954 
3955 	if((*smime)->personal_certs){
3956 	    PERSONAL_CERT *pc;
3957 
3958 	    pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3959 	    free_personal_certs(&pc);
3960 	    (*smime)->personal_certs = NULL;
3961 	}
3962 
3963 	if((*smime)->privatecontent)
3964 	  fs_give((void **) &(*smime)->privatecontent);
3965 
3966 	if((*smime)->capath)
3967 	  fs_give((void **) &(*smime)->capath);
3968 
3969 	if((*smime)->cacontent)
3970 	  fs_give((void **) &(*smime)->cacontent);
3971 
3972 	fs_give((void **) smime);
3973     }
3974 }
3975 
3976 #endif /* SMIME */
3977