1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2016 Colin Leroy and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #  include "config.h"
21 #include "claws-features.h"
22 #endif
23 
24 #ifdef USE_GPGME
25 
26 #include "defs.h"
27 #include <glib.h>
28 #include <gpgme.h>
29 #include <ctype.h>
30 #include <glib/gi18n.h>
31 
32 #include "utils.h"
33 #include "privacy.h"
34 #include "procmime.h"
35 
36 #include "smime.h"
37 #include <plugins/pgpcore/sgpgme.h>
38 #include <plugins/pgpcore/prefs_gpg.h>
39 #include <plugins/pgpcore/pgp_utils.h>
40 #include <plugins/pgpcore/passphrase.h>
41 
42 #include "alertpanel.h"
43 #include "prefs_common.h"
44 #include "procmime.h"
45 #include "plugin.h"
46 #include "file-utils.h"
47 
48 typedef struct _PrivacyDataPGP PrivacyDataPGP;
49 
50 struct _PrivacyDataPGP
51 {
52 	PrivacyData	data;
53 
54 	gboolean	done_sigtest;
55 	gboolean	is_signed;
56 	gpgme_verify_result_t	sigstatus;
57 	gpgme_ctx_t 	ctx;
58 };
59 
60 static PrivacySystem smime_system;
61 
62 static gint smime_check_signature(MimeInfo *mimeinfo);
63 
smime_new_privacydata()64 static PrivacyDataPGP *smime_new_privacydata()
65 {
66 	PrivacyDataPGP *data;
67 	gpgme_ctx_t 	ctx;
68 
69 	if (gpgme_new(&ctx) != GPG_ERR_NO_ERROR) {
70 		debug_print("gpgme_new failed\n");
71 		return NULL;
72 	}
73 
74 	data = g_new0(PrivacyDataPGP, 1);
75 	data->data.system = &smime_system;
76 	data->done_sigtest = FALSE;
77 	data->is_signed = FALSE;
78 	data->sigstatus = NULL;
79 	data->ctx = ctx;
80 
81 	return data;
82 }
83 
smime_free_privacydata(PrivacyData * _data)84 static void smime_free_privacydata(PrivacyData *_data)
85 {
86 	PrivacyDataPGP *data = (PrivacyDataPGP *) _data;
87 	gpgme_release(data->ctx);
88 	g_free(data);
89 }
90 
smime_is_signed(MimeInfo * mimeinfo)91 static gboolean smime_is_signed(MimeInfo *mimeinfo)
92 {
93 	MimeInfo *parent;
94 	MimeInfo *signature;
95 	const gchar *protocol, *tmpstr;
96 	PrivacyDataPGP *data = NULL;
97 
98 	cm_return_val_if_fail(mimeinfo != NULL, FALSE);
99 	if (mimeinfo->privacy != NULL) {
100 		data = (PrivacyDataPGP *) mimeinfo->privacy;
101 		if (data->done_sigtest)
102 			return data->is_signed;
103 	}
104 
105 	if (!g_ascii_strcasecmp(mimeinfo->subtype, "pkcs7-mime") ||
106 	    !g_ascii_strcasecmp(mimeinfo->subtype, "x-pkcs7-mime")) {
107 		tmpstr = procmime_mimeinfo_get_parameter(mimeinfo, "smime-type");
108 		if (tmpstr && !g_ascii_strcasecmp(tmpstr, "signed-data")) {
109 			if (data == NULL) {
110 				data = smime_new_privacydata();
111 				if (!data)
112 					return FALSE;
113 				mimeinfo->privacy = (PrivacyData *) data;
114 			}
115 
116 			data->done_sigtest = TRUE;
117 			data->is_signed = TRUE;
118 			smime_check_signature(mimeinfo);
119 			return TRUE;
120 		}
121 	}
122 
123 	/* check parent */
124 	parent = procmime_mimeinfo_parent(mimeinfo);
125 	if (parent == NULL)
126 		return FALSE;
127 
128 	if ((parent->type != MIMETYPE_MULTIPART) ||
129 	    g_ascii_strcasecmp(parent->subtype, "signed"))
130 		return FALSE;
131 	protocol = procmime_mimeinfo_get_parameter(parent, "protocol");
132 	if ((protocol == NULL) ||
133 	    (g_ascii_strcasecmp(protocol, "application/pkcs7-signature") &&
134 	     g_ascii_strcasecmp(protocol, "application/x-pkcs7-signature")))
135 		return FALSE;
136 
137 	/* check if mimeinfo is the first child */
138 	if (parent->node->children->data != mimeinfo)
139 		return FALSE;
140 
141 
142 	/* check signature */
143 	signature = parent->node->children->next != NULL ?
144 	    (MimeInfo *) parent->node->children->next->data : NULL;
145 	if (signature == NULL)
146 		return FALSE;
147 	if ((signature->type != MIMETYPE_APPLICATION) ||
148 	    (g_ascii_strcasecmp(signature->subtype, "pkcs7-signature") &&
149 	     g_ascii_strcasecmp(signature->subtype, "x-pkcs7-signature")))
150 		return FALSE;
151 
152 	if (data == NULL) {
153 		data = smime_new_privacydata();
154 		if (!data)
155 			return FALSE;
156 		mimeinfo->privacy = (PrivacyData *) data;
157 	}
158 
159 	data->done_sigtest = TRUE;
160 	data->is_signed = TRUE;
161 
162 	return TRUE;
163 }
164 
get_canonical_content(FILE * fp,const gchar * boundary)165 static gchar *get_canonical_content(FILE *fp, const gchar *boundary)
166 {
167 	gchar *ret;
168 	GString *textbuffer;
169 	guint boundary_len = 0;
170 	gchar buf[BUFFSIZE];
171 
172 	if (boundary) {
173 		boundary_len = strlen(boundary);
174 		while (claws_fgets(buf, sizeof(buf), fp) != NULL)
175 			if (IS_BOUNDARY(buf, boundary, boundary_len))
176 				break;
177 	}
178 
179 	textbuffer = g_string_new("");
180 	while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
181 		gchar *buf2;
182 
183 		if (boundary && IS_BOUNDARY(buf, boundary, boundary_len))
184 			break;
185 
186 		buf2 = canonicalize_str(buf);
187 		g_string_append(textbuffer, buf2);
188 		g_free(buf2);
189 	}
190 	g_string_truncate(textbuffer, textbuffer->len - 2);
191 
192 	ret = textbuffer->str;
193 	g_string_free(textbuffer, FALSE);
194 
195 	return ret;
196 }
197 
smime_check_signature(MimeInfo * mimeinfo)198 static gint smime_check_signature(MimeInfo *mimeinfo)
199 {
200 	PrivacyDataPGP *data;
201 	MimeInfo *parent, *signature;
202 	FILE *fp;
203 	gchar *boundary;
204 	gchar *textstr = NULL;
205 	const gchar *tmpstr;
206 	gpgme_data_t sigdata = NULL, textdata = NULL;
207 	gpgme_error_t err;
208 	EncodingType oldenc = ENC_BINARY;
209 
210 	cm_return_val_if_fail(mimeinfo != NULL, -1);
211 	cm_return_val_if_fail(mimeinfo->privacy != NULL, -1);
212 
213 	data = (PrivacyDataPGP *) mimeinfo->privacy;
214 
215 	if (!data->ctx) {
216 		if ((err = gpgme_new(&data->ctx)) != GPG_ERR_NO_ERROR) {
217 			debug_print("gpgme_new failed: %s\n",
218 				gpgme_strerror(err));
219 			return -1;
220 		}
221 	}
222 
223 	debug_print("Checking S/MIME signature\n");
224 
225 	err = gpgme_set_protocol(data->ctx, GPGME_PROTOCOL_CMS);
226 
227 	if (err) {
228 		debug_print ("gpgme_set_protocol failed: %s\n",
229                    gpgme_strerror (err));
230 	}
231 	parent = procmime_mimeinfo_parent(mimeinfo);
232 
233 	fp = claws_fopen(parent->data.filename, "rb");
234 	cm_return_val_if_fail(fp != NULL, SIGNATURE_INVALID);
235 
236 	boundary = g_hash_table_lookup(parent->typeparameters, "boundary");
237 	if (!boundary) {
238 		gchar *tmpfile = get_tmp_file();
239 		debug_print("no boundary\n");
240 		if (tmpfile) {
241 			if (mimeinfo->encoding_type != ENC_BASE64) {
242 				procmime_encode_content(mimeinfo, ENC_BASE64);
243 			}
244 			oldenc = mimeinfo->encoding_type;
245 			if (mimeinfo->encoding_type == ENC_BASE64)
246 				mimeinfo->encoding_type = ENC_BINARY;
247 			if (procmime_get_part(tmpfile, mimeinfo) == 0) {
248 				textstr = file_read_to_str(tmpfile);
249 			} else {
250 				textstr = NULL;
251 			}
252 			if (mimeinfo->encoding_type != oldenc)
253 				mimeinfo->encoding_type = oldenc;
254 		}
255 		g_free(tmpfile);
256 	} else {
257 		textstr = get_canonical_content(fp, boundary);
258 	}
259 	err = gpgme_data_new_from_mem(&textdata, textstr, textstr?strlen(textstr):0, 0);
260 
261 	if (err) {
262 		debug_print ("gpgme_data_new_from_mem failed: %s\n",
263                    gpgme_strerror (err));
264 	}
265 
266 	if (!g_ascii_strcasecmp(mimeinfo->subtype, "pkcs7-mime") ||
267 	    !g_ascii_strcasecmp(mimeinfo->subtype, "x-pkcs7-mime")) {
268 		tmpstr = procmime_mimeinfo_get_parameter(mimeinfo, "smime-type");
269 		if (tmpstr && !g_ascii_strcasecmp(tmpstr, "signed-data")) {
270 			gpgme_data_t cipher;
271 			size_t len;
272 			if (oldenc == ENC_BASE64)
273 				gpgme_data_set_encoding (textdata, GPGME_DATA_ENCODING_BASE64);
274 			gpgme_data_new(&cipher);
275 			data->sigstatus =
276 				sgpgme_verify_signature	(data->ctx, textdata, NULL, cipher);
277 			gpgme_data_release(textdata);
278 			g_free(textstr);
279 			cm_gpgme_data_rewind(cipher);
280 			textstr = sgpgme_data_release_and_get_mem(cipher, &len);
281 			claws_fclose(fp);
282 			if (textstr && len > 0)
283 				textstr[len-1]='\0';
284 
285 			if (textstr && len) {
286 				gchar *tmp_file = get_tmp_file();
287 				MimeInfo *newinfo = NULL, *decinfo = NULL, *parentinfo = NULL;
288 
289 				str_write_to_file(textstr, tmp_file, TRUE);
290 				newinfo = procmime_scan_file(tmp_file);
291 				decinfo = g_node_first_child(newinfo->node) != NULL ?
292 					g_node_first_child(newinfo->node)->data : NULL;
293 
294 				if (decinfo == NULL) {
295 					g_free(textstr);
296 					return -1;
297 				}
298 
299 				g_node_unlink(decinfo->node);
300 				procmime_mimeinfo_free_all(&newinfo);
301 				decinfo->tmp = TRUE;
302 				parentinfo = procmime_mimeinfo_parent(mimeinfo);
303 
304 				if (parentinfo->type == MIMETYPE_MESSAGE &&
305 				    !strcmp(parentinfo->subtype, "rfc822")) {
306 					procmime_decode_content(parentinfo);
307 					procmime_encode_content(parentinfo, ENC_BASE64);
308 					procmime_encode_content(parentinfo, ENC_8BIT);
309 					if (parentinfo->content == MIMECONTENT_MEM) {
310 						gint newlen =
311 							(gint)(strstr(parentinfo->data.mem, "\n\n") - parentinfo->data.mem);
312 						if (newlen > 0)
313 							parentinfo->length = newlen;
314 					}
315 				}
316 				g_node_prepend(parentinfo->node, decinfo->node);
317 				g_free(textstr);
318 				return 0;
319 			} else {
320 				if (textstr)
321 					g_free(textstr);
322 				return -1;
323 			}
324 		}
325 	}
326 
327 	signature = (MimeInfo *) mimeinfo->node->next->data;
328 	sigdata = sgpgme_data_from_mimeinfo(signature);
329 
330 	err = 0;
331 	if (signature->encoding_type == ENC_BASE64) {
332 		err = gpgme_data_set_encoding (sigdata, GPGME_DATA_ENCODING_BASE64);
333 	}
334 
335 	if (err) {
336 		debug_print ("gpgme_data_set_encoding failed: %s\n",
337 			gpgme_strerror (err));
338 	}
339 
340 	data->sigstatus =
341 		sgpgme_verify_signature	(data->ctx, sigdata, textdata, NULL);
342 
343 	gpgme_data_release(sigdata);
344 	gpgme_data_release(textdata);
345 	g_free(textstr);
346 	claws_fclose(fp);
347 
348 	return 0;
349 }
350 
smime_get_sig_status(MimeInfo * mimeinfo)351 static SignatureStatus smime_get_sig_status(MimeInfo *mimeinfo)
352 {
353 	PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
354 
355 	cm_return_val_if_fail(data != NULL, SIGNATURE_INVALID);
356 
357 	return sgpgme_sigstat_gpgme_to_privacy(data->ctx, data->sigstatus);
358 }
359 
smime_get_sig_info_short(MimeInfo * mimeinfo)360 static gchar *smime_get_sig_info_short(MimeInfo *mimeinfo)
361 {
362 	PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
363 
364 	cm_return_val_if_fail(data != NULL, g_strdup("Error"));
365 
366 	return sgpgme_sigstat_info_short(data->ctx, data->sigstatus);
367 }
368 
smime_get_sig_info_full(MimeInfo * mimeinfo)369 static gchar *smime_get_sig_info_full(MimeInfo *mimeinfo)
370 {
371 	PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
372 
373 	cm_return_val_if_fail(data != NULL, g_strdup("Error"));
374 
375 	return sgpgme_sigstat_info_full(data->ctx, data->sigstatus);
376 }
377 
smime_is_encrypted(MimeInfo * mimeinfo)378 static gboolean smime_is_encrypted(MimeInfo *mimeinfo)
379 {
380 	const gchar *tmpstr;
381 
382 	if (mimeinfo->type != MIMETYPE_APPLICATION)
383 		return FALSE;
384 	if (!g_ascii_strcasecmp(mimeinfo->subtype, "pkcs7-mime")) {
385 		tmpstr = procmime_mimeinfo_get_parameter(mimeinfo, "smime-type");
386 		if (tmpstr && g_ascii_strcasecmp(tmpstr, "enveloped-data"))
387 			return FALSE;
388 		else
389 			return TRUE;
390 
391 	} else if (!g_ascii_strcasecmp(mimeinfo->subtype, "x-pkcs7-mime")) {
392 		tmpstr = procmime_mimeinfo_get_parameter(mimeinfo, "smime-type");
393 		if (tmpstr && g_ascii_strcasecmp(tmpstr, "enveloped-data"))
394 			return FALSE;
395 		else
396 			return TRUE;
397 	}
398 	return FALSE;
399 }
400 
smime_decrypt(MimeInfo * mimeinfo)401 static MimeInfo *smime_decrypt(MimeInfo *mimeinfo)
402 {
403 	MimeInfo *encinfo, *decinfo, *parseinfo;
404 	gpgme_data_t cipher = NULL, plain = NULL;
405 	static gint id = 0;
406 	FILE *dstfp;
407 	gchar *fname;
408 	gpgme_verify_result_t sigstat = NULL;
409 	PrivacyDataPGP *data = NULL;
410 	gpgme_ctx_t ctx;
411 	gpgme_error_t err;
412 	gchar *chars;
413 	size_t len;
414 
415 	cm_return_val_if_fail(smime_is_encrypted(mimeinfo), NULL);
416 
417 	if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) {
418 		privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err));
419 		return NULL;
420 	}
421 
422 	err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
423 	if (err) {
424 		debug_print ("gpgme_set_protocol failed: %s\n",
425                    gpgme_strerror (err));
426 		privacy_set_error(_("Couldn't set GPG protocol, %s"), gpgme_strerror(err));
427 		gpgme_release(ctx);
428 		return NULL;
429 	}
430 	gpgme_set_armor(ctx, TRUE);
431 
432 	encinfo = mimeinfo;
433 
434 	cipher = sgpgme_data_from_mimeinfo(encinfo);
435 
436 	plain = sgpgme_decrypt_verify(cipher, &sigstat, ctx);
437 
438 	gpgme_data_release(cipher);
439 	if (plain == NULL) {
440 		debug_print("plain is null!\n");
441 		gpgme_release(ctx);
442 		return NULL;
443 	}
444 
445     	fname = g_strdup_printf("%s%cplaintext.%08x",
446 		get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
447 
448     	if ((dstfp = claws_fopen(fname, "wb")) == NULL) {
449         	FILE_OP_ERROR(fname, "claws_fopen");
450         	g_free(fname);
451         	gpgme_data_release(plain);
452 		gpgme_release(ctx);
453 		debug_print("can't open!\n");
454 		privacy_set_error(_("Couldn't open temporary file"));
455 		return NULL;
456     	}
457 
458 	if (fprintf(dstfp, "MIME-Version: 1.0\n") < 0) {
459         	FILE_OP_ERROR(fname, "fprintf");
460         	g_free(fname);
461 		claws_fclose(dstfp);
462         	gpgme_data_release(plain);
463 		gpgme_release(ctx);
464 		debug_print("can't close!\n");
465 		privacy_set_error(_("Couldn't write to temporary file"));
466 		return NULL;
467 	}
468 
469 	chars = sgpgme_data_release_and_get_mem(plain, &len);
470 
471 	if (len > 0) {
472 		if (claws_fwrite(chars, 1, len, dstfp) < len) {
473         		FILE_OP_ERROR(fname, "claws_fwrite");
474         		claws_fclose(dstfp);
475         		g_free(fname);
476         		g_free(chars);
477         		gpgme_data_release(plain);
478 			gpgme_release(ctx);
479 			debug_print("can't write!\n");
480 			privacy_set_error(_("Couldn't write to temporary file"));
481 			return NULL;
482 		}
483 	}
484 	if (claws_safe_fclose(dstfp) == EOF) {
485         	FILE_OP_ERROR(fname, "claws_fclose");
486         	g_free(fname);
487        		g_free(chars);
488         	gpgme_data_release(plain);
489 		gpgme_release(ctx);
490 		debug_print("can't close!\n");
491 		privacy_set_error(_("Couldn't close temporary file"));
492 		return NULL;
493 	}
494 	g_free(chars);
495 
496 	parseinfo = procmime_scan_file(fname);
497 	g_free(fname);
498 	if (parseinfo == NULL) {
499 		privacy_set_error(_("Couldn't parse decrypted file."));
500 		gpgme_release(ctx);
501 		return NULL;
502 	}
503 	decinfo = g_node_first_child(parseinfo->node) != NULL ?
504 		g_node_first_child(parseinfo->node)->data : NULL;
505 	if (decinfo == NULL) {
506 		privacy_set_error(_("Couldn't parse decrypted file parts."));
507 		gpgme_release(ctx);
508 		return NULL;
509 	}
510 
511 	g_node_unlink(decinfo->node);
512 	procmime_mimeinfo_free_all(&parseinfo);
513 
514 	decinfo->tmp = TRUE;
515 
516 	if (sigstat != NULL && sigstat->signatures != NULL) {
517 		if (decinfo->privacy != NULL) {
518 			data = (PrivacyDataPGP *) decinfo->privacy;
519 		} else {
520 			data = smime_new_privacydata();
521 			if (!data) {
522 				gpgme_release(ctx);
523 				return NULL;
524 			}
525 			decinfo->privacy = (PrivacyData *) data;
526 		}
527 		data->done_sigtest = TRUE;
528 		data->is_signed = TRUE;
529 		data->sigstatus = sigstat;
530 		if (data->ctx)
531 			gpgme_release(data->ctx);
532 		data->ctx = ctx;
533 	} else
534 		gpgme_release(ctx);
535 
536 
537 
538 	return decinfo;
539 }
540 
smime_sign(MimeInfo * mimeinfo,PrefsAccount * account,const gchar * from_addr)541 gboolean smime_sign(MimeInfo *mimeinfo, PrefsAccount *account, const gchar *from_addr)
542 {
543 	MimeInfo *msgcontent, *sigmultipart, *newinfo;
544 	gchar *textstr, *micalg = NULL;
545 	FILE *fp;
546 	gchar *boundary = NULL;
547 	gchar *sigcontent;
548 	gpgme_ctx_t ctx;
549 	gpgme_data_t gpgtext, gpgsig;
550 	gpgme_error_t err;
551 	size_t len;
552 	struct passphrase_cb_info_s info;
553 	gpgme_sign_result_t result = NULL;
554 	gchar *test_msg;
555 	gchar *real_content = NULL;
556 
557 	fp = my_tmpfile();
558 	if (fp == NULL) {
559 		perror("my_tmpfile");
560 		return FALSE;
561 	}
562 	procmime_write_mimeinfo(mimeinfo, fp);
563 	rewind(fp);
564 
565 	/* read temporary file into memory */
566 	test_msg = file_read_stream_to_str(fp);
567 	claws_fclose(fp);
568 
569 	memset (&info, 0, sizeof info);
570 
571 	/* remove content node from message */
572 	msgcontent = (MimeInfo *) mimeinfo->node->children->data;
573 	g_node_unlink(msgcontent->node);
574 
575 	/* create temporary multipart for content */
576 	sigmultipart = procmime_mimeinfo_new();
577 	sigmultipart->type = MIMETYPE_MULTIPART;
578 	sigmultipart->subtype = g_strdup("signed");
579 
580 	do {
581 		if (boundary)
582 			g_free(boundary);
583 		boundary = generate_mime_boundary("Sig");
584 	} while (strstr(test_msg, boundary) != NULL);
585 
586 	g_free(test_msg);
587 
588 	g_hash_table_insert(sigmultipart->typeparameters, g_strdup("boundary"),
589                             g_strdup(boundary));
590 	g_hash_table_insert(sigmultipart->typeparameters, g_strdup("protocol"),
591                             g_strdup("application/pkcs7-signature"));
592 	g_node_append(sigmultipart->node, msgcontent->node);
593 	g_node_append(mimeinfo->node, sigmultipart->node);
594 
595 	/* write message content to temporary file */
596 	fp = my_tmpfile();
597 	if (fp == NULL) {
598 		perror("my_tmpfile");
599 		return FALSE;
600 	}
601 	procmime_write_mimeinfo(sigmultipart, fp);
602 	rewind(fp);
603 
604 	/* read temporary file into memory */
605 	textstr = get_canonical_content(fp, boundary);
606 
607 	g_free(boundary);
608 
609 	claws_fclose(fp);
610 
611 	gpgme_data_new_from_mem(&gpgtext, textstr, textstr?strlen(textstr):0, 0);
612 	gpgme_data_new(&gpgsig);
613 	gpgme_new(&ctx);
614 	gpgme_set_armor(ctx, TRUE);
615 	gpgme_signers_clear (ctx);
616 
617 	err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
618 
619 	if (err) {
620 		debug_print ("gpgme_set_protocol failed: %s\n",
621                    gpgme_strerror (err));
622 		gpgme_data_release(gpgtext);
623 		gpgme_release(ctx);
624 		return FALSE;
625 	}
626 
627 	if (!sgpgme_setup_signers(ctx, account, from_addr)) {
628 		debug_print("setup_signers failed\n");
629 		gpgme_data_release(gpgtext);
630 		gpgme_release(ctx);
631 		return FALSE;
632 	}
633 
634 	info.c = ctx;
635 	prefs_gpg_enable_agent(TRUE);
636     	gpgme_set_passphrase_cb (ctx, NULL, &info);
637 
638 	err = gpgme_op_sign(ctx, gpgtext, gpgsig, GPGME_SIG_MODE_DETACH);
639 	if (err != GPG_ERR_NO_ERROR) {
640 		alertpanel_error("S/MIME : Cannot sign, %s (%d)", gpg_strerror(err), gpg_err_code(err));
641 		gpgme_data_release(gpgtext);
642 		gpgme_release(ctx);
643 		return FALSE;
644 	}
645 	result = gpgme_op_sign_result(ctx);
646 	if (result && result->signatures) {
647 	    if (gpgme_get_protocol(ctx) == GPGME_PROTOCOL_OpenPGP) {
648 		gchar *down_algo = g_ascii_strdown(gpgme_hash_algo_name(
649 			    result->signatures->hash_algo), -1);
650 		micalg = g_strdup_printf("pgp-%s", down_algo);
651 		g_free(down_algo);
652 	    } else {
653 		micalg = g_strdup(gpgme_hash_algo_name(
654 			    result->signatures->hash_algo));
655 	    }
656 	} else {
657 	    /* can't get result (maybe no signing key?) */
658 	    debug_print("gpgme_op_sign_result error\n");
659 	    return FALSE;
660 	}
661 
662 	gpgme_release(ctx);
663 	sigcontent = sgpgme_data_release_and_get_mem(gpgsig, &len);
664 	gpgme_data_release(gpgtext);
665 	g_free(textstr);
666 
667 	if (!sigcontent) {
668 		gpgme_release(ctx);
669 		g_free(micalg);
670 		return FALSE;
671 	}
672 	real_content = sigcontent+strlen("-----BEGIN SIGNED MESSAGE-----\n");
673 	if (!strstr(real_content, "-----END SIGNED MESSAGE-----")) {
674 		debug_print("missing end\n");
675 		gpgme_release(ctx);
676 		g_free(micalg);
677 		return FALSE;
678 	}
679 	*strstr(real_content, "-----END SIGNED MESSAGE-----") = '\0';
680 	/* add signature */
681 	g_hash_table_insert(sigmultipart->typeparameters, g_strdup("micalg"),
682                             micalg);
683 
684 	newinfo = procmime_mimeinfo_new();
685 	newinfo->type = MIMETYPE_APPLICATION;
686 	newinfo->subtype = g_strdup("pkcs7-signature");
687 	g_hash_table_insert(newinfo->typeparameters, g_strdup("name"),
688 			     g_strdup("smime.p7s"));
689 	newinfo->content = MIMECONTENT_MEM;
690 	newinfo->disposition = DISPOSITIONTYPE_ATTACHMENT;
691 	g_hash_table_insert(newinfo->dispositionparameters, g_strdup("filename"),
692 			    g_strdup("smime.p7s"));
693 	newinfo->data.mem = g_malloc(len + 1);
694 	newinfo->tmp = TRUE;
695 	memmove(newinfo->data.mem, real_content, len);
696 	newinfo->data.mem[len] = '\0';
697 	newinfo->encoding_type = ENC_BASE64;
698 	g_node_append(sigmultipart->node, newinfo->node);
699 
700 	g_free(sigcontent);
701 
702 	return TRUE;
703 }
smime_get_encrypt_data(GSList * recp_names)704 gchar *smime_get_encrypt_data(GSList *recp_names)
705 {
706 	return sgpgme_get_encrypt_data(recp_names, GPGME_PROTOCOL_CMS);
707 }
708 
smime_get_encrypt_warning(void)709 static const gchar *smime_get_encrypt_warning(void)
710 {
711 	if (prefs_gpg_should_skip_encryption_warning(smime_system.id))
712 		return NULL;
713 	else
714 		return _("Please note that email headers, like Subject, "
715 			 "are not encrypted by the S/MIME system.");
716 }
717 
smime_inhibit_encrypt_warning(gboolean inhibit)718 static void smime_inhibit_encrypt_warning(gboolean inhibit)
719 {
720 	if (inhibit)
721 		prefs_gpg_add_skip_encryption_warning(smime_system.id);
722 	else
723 		prefs_gpg_remove_skip_encryption_warning(smime_system.id);
724 }
725 
smime_encrypt(MimeInfo * mimeinfo,const gchar * encrypt_data)726 gboolean smime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
727 {
728 	MimeInfo *msgcontent, *encmultipart;
729 	FILE *fp;
730 	gchar *enccontent;
731 	size_t len;
732 	gchar *textstr = NULL;
733 	gpgme_data_t gpgtext = NULL, gpgenc = NULL;
734 	gpgme_ctx_t ctx = NULL;
735 	gpgme_key_t *kset = NULL;
736 	gchar **fprs = g_strsplit(encrypt_data, " ", -1);
737 	gint i = 0;
738 	gpgme_error_t err;
739 	gchar *tmpfile = NULL;
740 
741 	while (fprs[i] && strlen(fprs[i])) {
742 		i++;
743 	}
744 
745 	if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) {
746 		debug_print ("gpgme_new failed: %s\n", gpgme_strerror(err));
747 		return FALSE;
748 	}
749 
750 	err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
751 
752 	if (err) {
753 		debug_print ("gpgme_set_protocol failed: %s\n",
754                    gpgme_strerror (err));
755 		return FALSE;
756 	}
757 
758 	kset = g_malloc(sizeof(gpgme_key_t)*(i+1));
759 	memset(kset, 0, sizeof(gpgme_key_t)*(i+1));
760 	i = 0;
761 
762 	while (fprs[i] && strlen(fprs[i])) {
763 		gpgme_key_t key;
764 		gpgme_error_t err;
765 		err = gpgme_get_key(ctx, fprs[i], &key, 0);
766 		if (err) {
767 			debug_print("can't add key '%s'[%d] (%s)\n", fprs[i],i, gpgme_strerror(err));
768 			break;
769 		}
770 		debug_print("found %s at %d\n", fprs[i], i);
771 		kset[i] = key;
772 		i++;
773 	}
774 
775 	debug_print("Encrypting message content\n");
776 
777 	/* remove content node from message */
778 	msgcontent = (MimeInfo *) mimeinfo->node->children->data;
779 	g_node_unlink(msgcontent->node);
780 
781 
782 	/* create temporary multipart for content */
783 	encmultipart = procmime_mimeinfo_new();
784 	encmultipart->type = MIMETYPE_APPLICATION;
785 	encmultipart->subtype = g_strdup("x-pkcs7-mime");
786 	g_hash_table_insert(encmultipart->typeparameters, g_strdup("name"),
787                             g_strdup("smime.p7m"));
788 	g_hash_table_insert(encmultipart->typeparameters,
789 			    g_strdup("smime-type"),
790 			    g_strdup("enveloped-data"));
791 
792 	encmultipart->disposition = DISPOSITIONTYPE_ATTACHMENT;
793 	g_hash_table_insert(encmultipart->dispositionparameters, g_strdup("filename"),
794                             g_strdup("smime.p7m"));
795 
796 	g_node_append(encmultipart->node, msgcontent->node);
797 
798 	/* write message content to temporary file */
799 	tmpfile = get_tmp_file();
800 	fp = claws_fopen(tmpfile, "wb");
801 	if (fp == NULL) {
802 		FILE_OP_ERROR(tmpfile, "create");
803 		g_free(kset);
804 		return FALSE;
805 	}
806 	procmime_decode_content(msgcontent);
807 	procmime_write_mime_header(msgcontent, fp);
808 	procmime_write_mimeinfo(msgcontent, fp);
809 	claws_safe_fclose(fp);
810 	canonicalize_file_replace(tmpfile);
811 	fp = claws_fopen(tmpfile, "rb");
812 	if (fp == NULL) {
813 		FILE_OP_ERROR(tmpfile, "open");
814 		g_free(kset);
815 		return FALSE;
816 	}
817 	g_free(tmpfile);
818 
819 	/* read temporary file into memory */
820 	textstr = file_read_stream_to_str_no_recode(fp);
821 
822 	claws_fclose(fp);
823 
824 	/* encrypt data */
825 	gpgme_data_new_from_mem(&gpgtext, textstr, textstr?strlen(textstr):0, 0);
826 	gpgme_data_new(&gpgenc);
827 	cm_gpgme_data_rewind(gpgtext);
828 
829 	gpgme_op_encrypt(ctx, kset, GPGME_ENCRYPT_ALWAYS_TRUST, gpgtext, gpgenc);
830 
831 	gpgme_release(ctx);
832 	g_free(kset);
833 	enccontent = sgpgme_data_release_and_get_mem(gpgenc, &len);
834 
835 	if (!enccontent) {
836 		g_warning("no enccontent");
837 		return FALSE;
838 	}
839 
840 	tmpfile = get_tmp_file();
841 	fp = claws_fopen(tmpfile, "wb");
842 	if (fp) {
843 		if (claws_fwrite(enccontent, 1, len, fp) < len) {
844 			FILE_OP_ERROR(tmpfile, "claws_fwrite");
845 			claws_fclose(fp);
846 			claws_unlink(tmpfile);
847 			g_free(tmpfile);
848 			g_free(enccontent);
849 			return FALSE;
850 		}
851 		if (claws_safe_fclose(fp) == EOF) {
852 			FILE_OP_ERROR(tmpfile, "claws_fclose");
853 			claws_unlink(tmpfile);
854 			g_free(tmpfile);
855 			g_free(enccontent);
856 			return FALSE;
857 		}
858 	} else {
859 		FILE_OP_ERROR(tmpfile, "create");
860 		g_free(tmpfile);
861 		g_free(enccontent);
862 		return FALSE;
863 	}
864 	gpgme_data_release(gpgtext);
865 	g_free(textstr);
866 
867 	/* create encrypted multipart */
868 	procmime_mimeinfo_free_all(&msgcontent);
869 	g_node_append(mimeinfo->node, encmultipart->node);
870 
871 	encmultipart->content = MIMECONTENT_FILE;
872 	encmultipart->data.filename = tmpfile;
873 	encmultipart->tmp = TRUE;
874 	procmime_encode_content(encmultipart, ENC_BASE64);
875 
876 	g_free(enccontent);
877 
878 	return TRUE;
879 }
880 
881 static PrivacySystem smime_system = {
882 	"smime",			/* id */
883 	"S-MIME",			/* name */
884 
885 	smime_free_privacydata,	/* free_privacydata */
886 
887 	smime_is_signed,		/* is_signed(MimeInfo *) */
888 	smime_check_signature,	/* check_signature(MimeInfo *) */
889 	smime_get_sig_status,		/* get_sig_status(MimeInfo *) */
890 	smime_get_sig_info_short,	/* get_sig_info_short(MimeInfo *) */
891 	smime_get_sig_info_full,	/* get_sig_info_full(MimeInfo *) */
892 
893 	smime_is_encrypted,		/* is_encrypted(MimeInfo *) */
894 	smime_decrypt,			/* decrypt(MimeInfo *) */
895 
896 	TRUE,
897 	smime_sign,
898 
899 	TRUE,
900 	smime_get_encrypt_data,
901 	smime_encrypt,
902 	smime_get_encrypt_warning,
903 	smime_inhibit_encrypt_warning,
904 	prefs_gpg_auto_check_signatures,
905 };
906 
smime_init()907 void smime_init()
908 {
909 	privacy_register_system(&smime_system);
910 }
911 
smime_done()912 void smime_done()
913 {
914 	privacy_unregister_system(&smime_system);
915 }
916 
plugin_provides(void)917 struct PluginFeature *plugin_provides(void)
918 {
919 	static struct PluginFeature features[] =
920 		{ {PLUGIN_PRIVACY, N_("S/MIME")},
921 		  {PLUGIN_NOTHING, NULL}};
922 	return features;
923 }
924 #endif /* USE_GPGME */
925