1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2020 Jeffrey Stedfast
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *
10  *  This library 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 GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18  *  02110-1301, USA.
19  */
20 
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <string.h>
27 
28 #include "gmime-application-pkcs7-mime.h"
29 #include "gmime-filter-dos2unix.h"
30 #include "gmime-stream-filter.h"
31 #include "gmime-filter-basic.h"
32 #include "gmime-stream-mem.h"
33 #include "gmime-internal.h"
34 #include "gmime-parser.h"
35 #include "gmime-error.h"
36 
37 #define _(x) x
38 
39 /**
40  * SECTION: gmime-application-pkcs7-mime
41  * @title: GMimeApplicationPkcs7Mime
42  * @short_description: Pkcs7 MIME parts
43  * @see_also:
44  *
45  * A #GMimeApplicationPkcs7Mime represents the application/pkcs7-mime MIME part.
46  **/
47 
48 /* GObject class methods */
49 static void g_mime_application_pkcs7_mime_class_init (GMimeApplicationPkcs7MimeClass *klass);
50 static void g_mime_application_pkcs7_mime_init (GMimeApplicationPkcs7Mime *catpart, GMimeApplicationPkcs7MimeClass *klass);
51 static void g_mime_application_pkcs7_mime_finalize (GObject *object);
52 
53 /* GMimeObject class methods */
54 static void application_pkcs7_mime_set_content_type (GMimeObject *object, GMimeContentType *content_type);
55 
56 
57 static GMimePartClass *parent_class = NULL;
58 
59 
60 GType
g_mime_application_pkcs7_mime_get_type(void)61 g_mime_application_pkcs7_mime_get_type (void)
62 {
63 	static GType type = 0;
64 
65 	if (!type) {
66 		static const GTypeInfo info = {
67 			sizeof (GMimeApplicationPkcs7MimeClass),
68 			NULL, /* base_class_init */
69 			NULL, /* base_class_finalize */
70 			(GClassInitFunc) g_mime_application_pkcs7_mime_class_init,
71 			NULL, /* class_finalize */
72 			NULL, /* class_data */
73 			sizeof (GMimeApplicationPkcs7Mime),
74 			0,    /* n_preallocs */
75 			(GInstanceInitFunc) g_mime_application_pkcs7_mime_init,
76 		};
77 
78 		type = g_type_register_static (GMIME_TYPE_PART, "GMimeApplicationPkcs7Mime", &info, 0);
79 	}
80 
81 	return type;
82 }
83 
84 
85 static void
g_mime_application_pkcs7_mime_class_init(GMimeApplicationPkcs7MimeClass * klass)86 g_mime_application_pkcs7_mime_class_init (GMimeApplicationPkcs7MimeClass *klass)
87 {
88 	GMimeObjectClass *object_class = GMIME_OBJECT_CLASS (klass);
89 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
90 
91 	parent_class = g_type_class_ref (GMIME_TYPE_PART);
92 
93 	gobject_class->finalize = g_mime_application_pkcs7_mime_finalize;
94 
95 	object_class->set_content_type = application_pkcs7_mime_set_content_type;
96 }
97 
98 static void
g_mime_application_pkcs7_mime_init(GMimeApplicationPkcs7Mime * pkcs7_mime,GMimeApplicationPkcs7MimeClass * klass)99 g_mime_application_pkcs7_mime_init (GMimeApplicationPkcs7Mime *pkcs7_mime, GMimeApplicationPkcs7MimeClass *klass)
100 {
101 	pkcs7_mime->smime_type = GMIME_SECURE_MIME_TYPE_UNKNOWN;
102 }
103 
104 static void
g_mime_application_pkcs7_mime_finalize(GObject * object)105 g_mime_application_pkcs7_mime_finalize (GObject *object)
106 {
107 	G_OBJECT_CLASS (parent_class)->finalize (object);
108 }
109 
110 
111 static void
application_pkcs7_mime_set_content_type(GMimeObject * object,GMimeContentType * content_type)112 application_pkcs7_mime_set_content_type (GMimeObject *object, GMimeContentType *content_type)
113 {
114 	GMimeApplicationPkcs7Mime *pkcs7_mime = (GMimeApplicationPkcs7Mime *) object;
115 	const char *value;
116 
117 	if ((value = g_mime_content_type_get_parameter (content_type, "smime-type")) != NULL) {
118 		if (!g_ascii_strcasecmp (value, "compressed-data"))
119 			pkcs7_mime->smime_type = GMIME_SECURE_MIME_TYPE_COMPRESSED_DATA;
120 		else if (!g_ascii_strcasecmp (value, "enveloped-data"))
121 			pkcs7_mime->smime_type = GMIME_SECURE_MIME_TYPE_ENVELOPED_DATA;
122 		else if (!g_ascii_strcasecmp (value, "signed-data"))
123 			pkcs7_mime->smime_type = GMIME_SECURE_MIME_TYPE_SIGNED_DATA;
124 		else if (!g_ascii_strcasecmp (value, "certs-only"))
125 			pkcs7_mime->smime_type = GMIME_SECURE_MIME_TYPE_CERTS_ONLY;
126 		else
127 			pkcs7_mime->smime_type = GMIME_SECURE_MIME_TYPE_UNKNOWN;
128 	} else {
129 		pkcs7_mime->smime_type = GMIME_SECURE_MIME_TYPE_UNKNOWN;
130 	}
131 
132 	GMIME_OBJECT_CLASS (parent_class)->set_content_type (object, content_type);
133 }
134 
135 /**
136  * g_mime_application_pkcs7_mime_new:
137  * @type: The type of S/MIME data contained within the part.
138  *
139  * Creates a new application/pkcs7-mime object.
140  *
141  * Returns: an empty application/pkcs7-mime object.
142  **/
143 GMimeApplicationPkcs7Mime *
g_mime_application_pkcs7_mime_new(GMimeSecureMimeType type)144 g_mime_application_pkcs7_mime_new (GMimeSecureMimeType type)
145 {
146 	GMimeApplicationPkcs7Mime *pkcs7_mime;
147 	GMimeContentType *content_type;
148 	const char *name;
149 
150 	g_return_val_if_fail (type != GMIME_SECURE_MIME_TYPE_UNKNOWN, NULL);
151 
152 	pkcs7_mime = g_object_new (GMIME_TYPE_APPLICATION_PKCS7_MIME, NULL);
153 	content_type = g_mime_content_type_new ("application", "pkcs7-mime");
154 
155 	switch (type) {
156 	case GMIME_SECURE_MIME_TYPE_COMPRESSED_DATA:
157 		g_mime_content_type_set_parameter (content_type, "smime-type", "compressed-data");
158 		name = "smime.p7z";
159 		break;
160 	case GMIME_SECURE_MIME_TYPE_ENVELOPED_DATA:
161 		g_mime_content_type_set_parameter (content_type, "smime-type", "enveloped-data");
162 		name = "smime.p7m";
163 		break;
164 	case GMIME_SECURE_MIME_TYPE_SIGNED_DATA:
165 		g_mime_content_type_set_parameter (content_type, "smime-type", "signed-data");
166 		name = "smime.p7m";
167 		break;
168 	case GMIME_SECURE_MIME_TYPE_CERTS_ONLY:
169 		g_mime_content_type_set_parameter (content_type, "smime-type", "certs-only");
170 		name = "smime.p7c";
171 		break;
172 	default:
173 		g_assert_not_reached ();
174 		break;
175 	}
176 
177 	g_mime_object_set_content_type ((GMimeObject *) pkcs7_mime, content_type);
178 	g_object_unref (content_type);
179 
180 	g_mime_part_set_filename ((GMimePart *) pkcs7_mime, name);
181 	g_mime_part_set_content_encoding ((GMimePart *) pkcs7_mime, GMIME_CONTENT_ENCODING_BASE64);
182 
183 	return pkcs7_mime;
184 }
185 
186 
187 /**
188  * g_mime_application_pkcs7_mime_get_smime_type:
189  * @pkcs7_mime: A #GMimeApplicationPkcs7Mime object
190  *
191  * Gets the smime-type value of the Content-Type header.
192  *
193  * Returns: the smime-type value.
194  **/
195 GMimeSecureMimeType
g_mime_application_pkcs7_mime_get_smime_type(GMimeApplicationPkcs7Mime * pkcs7_mime)196 g_mime_application_pkcs7_mime_get_smime_type (GMimeApplicationPkcs7Mime *pkcs7_mime)
197 {
198 	g_return_val_if_fail (GMIME_IS_APPLICATION_PKCS7_MIME (pkcs7_mime), GMIME_SECURE_MIME_TYPE_UNKNOWN);
199 
200 	return pkcs7_mime->smime_type;
201 }
202 
203 
204 #if 0
205 GMimeApplicationPkcs7Mime *
206 g_mime_application_pkcs7_mime_compress (GMimeObject *entity, GError **err)
207 {
208 	GMimeApplicationPkcs7Mime *pkcs7_mime;
209 	GMimeStream *compressed, *stream;
210 	GMimeFormatOptions *options;
211 	GMimeDataWrapper *content;
212 	GMimeCryptoContext *ctx;
213 
214 	g_return_val_if_fail (GMIME_IS_OBJECT (entity), NULL);
215 
216 	if (!(ctx = g_mime_crypto_context_new ("application/pkcs7-mime"))) {
217 		g_set_error (err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR,
218 			     _("Cannot compress application/pkcs7-mime part: no crypto context registered for this type."));
219 
220 		return NULL;
221 	}
222 
223 	options = _g_mime_format_options_clone (NULL, FALSE);
224 	g_mime_format_options_set_newline_format (options, GMIME_NEWLINE_FORMAT_DOS);
225 
226 	/* get the cleartext */
227 	stream = g_mime_stream_mem_new ();
228 	g_mime_object_write_to_stream (entity, options, stream);
229 	g_mime_format_options_free (options);
230 
231 	/* reset the content stream */
232 	g_mime_stream_reset (stream);
233 
234 	/* compress the content stream */
235 	compressed = g_mime_stream_mem_new ();
236 	if (g_mime_crypto_context_compress (ctx, stream, compressed, err) == -1) {
237 		g_object_unref (compressed);
238 		g_object_unref (stream);
239 		g_object_unref (ctx);
240 		return NULL;
241 	}
242 
243 	g_object_unref (stream);
244 	g_mime_stream_reset (compressed);
245 	g_object_unref (ctx);
246 
247 	/* construct the application/pkcs7-mime part */
248 	pkcs7_mime = g_mime_application_pkcs7_mime_new (GMIME_SECURE_MIME_TYPE_COMPRESSED_DATA);
249 	content = g_mime_data_wrapper_new_with_stream (compressed, GMIME_CONTENT_ENCODING_DEFAULT);
250 	g_mime_part_set_content ((GMimePart *) pkcs7_mime, content);
251 	g_object_unref (compressed);
252 	g_object_unref (content);
253 
254 	return pkcs7_mime;
255 }
256 
257 
258 GMimeObject *
259 g_mime_application_pkcs7_mime_decompress (GMimeApplicationPkcs7Mime *pkcs7_mime)
260 {
261 	GMimeStream *filtered, *compressed, *stream;
262 	GMimeObject *decompressed;
263 	GMimeDataWrapper *content;
264 	GMimeCryptoContext *ctx;
265 	GMimeDecryptResult *res;
266 	GMimeFilter *filter;
267 	GMimeParser *parser;
268 
269 	g_return_val_if_fail (GMIME_IS_APPLICATION_PKCS7_MIME (pkcs7_mime), NULL);
270 
271 	if (!(ctx = g_mime_crypto_context_new ("application/pkcs7-mime"))) {
272 		g_set_error (err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR,
273 			     _("Cannot decompress application/pkcs7-mime part: no crypto context registered for this type."));
274 
275 		return NULL;
276 	}
277 
278 	/* get the compressed stream */
279 	content = g_mime_part_get_content ((GMimePart *) pkcs7_mime);
280 	compressed = g_mime_stream_mem_new ();
281 	g_mime_data_wrapper_write_to_stream (content, compressed);
282 	g_mime_stream_reset (compressed);
283 
284 	stream = g_mime_stream_mem_new ();
285 	filtered = g_mime_stream_filter_new (stream);
286 	filter = g_mime_filter_dos2unix_new (FALSE);
287 	g_mime_stream_filter_add ((GMimeStreamFilter *) filtered, filter);
288 	g_object_unref (filter);
289 
290 	/* decompress the content stream */
291 	if (g_mime_crypto_context_decompress (ctx, compressed, stream, err) == -1) {
292 		g_object_unref (compressed);
293 		g_object_unref (filtered);
294 		g_object_unref (stream);
295 		g_object_unref (ctx);
296 
297 		return NULL;
298 	}
299 
300 	g_mime_stream_flush (filtered);
301 	g_object_unref (compressed);
302 	g_object_unref (filtered);
303 	g_object_unref (ctx);
304 
305 	g_mime_stream_reset (stream);
306 	parser = g_mime_parser_new ();
307 	g_mime_parser_init_with_stream (parser, stream);
308 	g_object_unref (stream);
309 
310 	decompressed = g_mime_parser_construct_part (parser, NULL);
311 	g_object_unref (parser);
312 
313 	if (!decompressed) {
314 		g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_PARSE_ERROR,
315 				     _("Cannot decompress application/pkcs7-mime part: failed to parse decompressed content."));
316 
317 		return NULL;
318 	}
319 
320 	return decompressed;
321 }
322 #endif
323 
324 
325 /**
326  * g_mime_application_pkcs7_mime_encrypt:
327  * @entity: a #GMimeObject to encrypt
328  * @flags: a #GMimeEncryptFlags
329  * @recipients: (element-type utf8): an array of recipients to encrypt to
330  * @err: a #GError
331  *
332  * Attempts to encrypt the @entity MIME part to the public keys of @recipients
333  * using S/MIME. If successful, a new application/pkcs7-mime object is returned.
334  *
335  * Returns: (nullable) (transfer full): a new #GMimeApplicationPkcs7Mime object on success
336  * or %NULL on fail. If encrypting fails, an exception will be set on @err to provide
337  * information as to why the failure occurred.
338  **/
339 GMimeApplicationPkcs7Mime *
g_mime_application_pkcs7_mime_encrypt(GMimeObject * entity,GMimeEncryptFlags flags,GPtrArray * recipients,GError ** err)340 g_mime_application_pkcs7_mime_encrypt (GMimeObject *entity, GMimeEncryptFlags flags, GPtrArray *recipients, GError **err)
341 {
342 	GMimeApplicationPkcs7Mime *pkcs7_mime;
343 	GMimeStream *ciphertext, *stream;
344 	GMimeFormatOptions *options;
345 	GMimeDataWrapper *content;
346 	GMimeCryptoContext *ctx;
347 
348 	g_return_val_if_fail (GMIME_IS_OBJECT (entity), NULL);
349 	g_return_val_if_fail (recipients != NULL, NULL);
350 
351 	if (!(ctx = g_mime_crypto_context_new ("application/pkcs7-mime"))) {
352 		g_set_error (err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR,
353 			     _("Cannot encrypt application/pkcs7-mime part: no crypto context registered for this type."));
354 
355 		return NULL;
356 	}
357 
358 	options = _g_mime_format_options_clone (NULL, FALSE);
359 	g_mime_format_options_set_newline_format (options, GMIME_NEWLINE_FORMAT_DOS);
360 
361 	/* get the cleartext */
362 	stream = g_mime_stream_mem_new ();
363 	g_mime_object_write_to_stream (entity, options, stream);
364 	g_mime_format_options_free (options);
365 
366 	/* reset the content stream */
367 	g_mime_stream_reset (stream);
368 
369 	/* encrypt the content stream */
370 	ciphertext = g_mime_stream_mem_new ();
371 	if (g_mime_crypto_context_encrypt (ctx, FALSE, NULL, flags, recipients, stream, ciphertext, err) == -1) {
372 		g_object_unref (ciphertext);
373 		g_object_unref (stream);
374 		g_object_unref (ctx);
375 		return NULL;
376 	}
377 
378 	g_object_unref (stream);
379 	g_mime_stream_reset (ciphertext);
380 	g_object_unref (ctx);
381 
382 	/* construct the application/pkcs7-mime part */
383 	pkcs7_mime = g_mime_application_pkcs7_mime_new (GMIME_SECURE_MIME_TYPE_ENVELOPED_DATA);
384 	content = g_mime_data_wrapper_new_with_stream (ciphertext, GMIME_CONTENT_ENCODING_DEFAULT);
385 	g_mime_part_set_content ((GMimePart *) pkcs7_mime, content);
386 	g_object_unref (ciphertext);
387 	g_object_unref (content);
388 
389 	return pkcs7_mime;
390 }
391 
392 
393 /**
394  * g_mime_application_pkcs7_mime_decrypt:
395  * @pkcs7_mime: a #GMimeApplicationPkcs7Mime
396  * @flags: a #GMimeDecryptFlags
397  * @session_key: session key to use or %NULL
398  * @result: the decryption result
399  * @err: a #GError
400  *
401  * Attempts to decrypt the encrypted application/pkcs7-mime part.
402  *
403  * When non-%NULL, @session_key should be a %NULL-terminated string,
404  * such as the one returned by g_mime_decrypt_result_get_session_key()
405  * from a previous decryption. If the @session_key is not valid, decryption
406  * will fail.
407  *
408  * If @result is non-%NULL, then on a successful decrypt operation, it will be
409  * updated to point to a newly-allocated #GMimeDecryptResult with signature
410  * status information as well as a list of recipients that the part was
411  * encrypted to.
412  *
413  * Returns: (nullable) (transfer full): the decrypted MIME part on success or
414  * %NULL on fail. If the decryption fails, an exception will be set on
415  * @err to provide information as to why the failure occurred.
416  **/
417 GMimeObject *
g_mime_application_pkcs7_mime_decrypt(GMimeApplicationPkcs7Mime * pkcs7_mime,GMimeDecryptFlags flags,const char * session_key,GMimeDecryptResult ** result,GError ** err)418 g_mime_application_pkcs7_mime_decrypt (GMimeApplicationPkcs7Mime *pkcs7_mime,
419 				       GMimeDecryptFlags flags, const char *session_key,
420 				       GMimeDecryptResult **result, GError **err)
421 {
422 	GMimeStream *filtered, *ciphertext, *stream;
423 	GMimeDataWrapper *content;
424 	GMimeCryptoContext *ctx;
425 	GMimeDecryptResult *res;
426 	GMimeObject *decrypted;
427 	GMimeFilter *filter;
428 	GMimeParser *parser;
429 
430 	g_return_val_if_fail (GMIME_IS_APPLICATION_PKCS7_MIME (pkcs7_mime), NULL);
431 
432 	if (result)
433 		*result = NULL;
434 
435 	if (!(ctx = g_mime_crypto_context_new ("application/pkcs7-mime"))) {
436 		g_set_error (err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR,
437 			     _("Cannot decrypt application/pkcs7-mime part: no crypto context registered for this type."));
438 
439 		return NULL;
440 	}
441 
442 	/* get the ciphertext stream */
443 	content = g_mime_part_get_content ((GMimePart *) pkcs7_mime);
444 	ciphertext = g_mime_stream_mem_new ();
445 	g_mime_data_wrapper_write_to_stream (content, ciphertext);
446 	g_mime_stream_reset (ciphertext);
447 
448 	stream = g_mime_stream_mem_new ();
449 	filtered = g_mime_stream_filter_new (stream);
450 	filter = g_mime_filter_dos2unix_new (FALSE);
451 	g_mime_stream_filter_add ((GMimeStreamFilter *) filtered, filter);
452 	g_object_unref (filter);
453 
454 	/* decrypt the content stream */
455 	if (!(res = g_mime_crypto_context_decrypt (ctx, flags, session_key, ciphertext, filtered, err))) {
456 		g_object_unref (ciphertext);
457 		g_object_unref (filtered);
458 		g_object_unref (stream);
459 		g_object_unref (ctx);
460 
461 		return NULL;
462 	}
463 
464 	g_mime_stream_flush (filtered);
465 	g_object_unref (ciphertext);
466 	g_object_unref (filtered);
467 	g_object_unref (ctx);
468 
469 	g_mime_stream_reset (stream);
470 	parser = g_mime_parser_new ();
471 	g_mime_parser_init_with_stream (parser, stream);
472 	g_object_unref (stream);
473 
474 	decrypted = g_mime_parser_construct_part (parser, NULL);
475 	g_object_unref (parser);
476 
477 	if (!decrypted) {
478 		g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_PARSE_ERROR,
479 				     _("Cannot decrypt application/pkcs7-mime part: failed to parse decrypted content."));
480 
481 		g_object_unref (res);
482 
483 		return NULL;
484 	}
485 
486 	if (!result)
487 		g_object_unref (res);
488 	else
489 		*result = res;
490 
491 	return decrypted;
492 }
493 
494 
495 /**
496  * g_mime_application_pkcs7_mime_sign:
497  * @entity: a #GMimeObject
498  * @userid: the user id to sign with
499  * @err: a #GError
500  *
501  * Attempts to sign the @entity MIME part with @userid's private key using
502  * S/MIME. If successful, a new application/pkcs7-mime object is returned.
503  *
504  * Returns: (nullable) (transfer full): a new #GMimeApplicationPkcs7Mime object on success
505  * or %NULL on fail. If signing fails, an exception will be set on @err to provide
506  * information as to why the failure occurred.
507  **/
508 GMimeApplicationPkcs7Mime *
g_mime_application_pkcs7_mime_sign(GMimeObject * entity,const char * userid,GError ** err)509 g_mime_application_pkcs7_mime_sign (GMimeObject *entity, const char *userid, GError **err)
510 {
511 	GMimeApplicationPkcs7Mime *pkcs7_mime;
512 	GMimeStream *ciphertext, *stream;
513 	GMimeFormatOptions *options;
514 	GMimeDataWrapper *content;
515 	GMimeCryptoContext *ctx;
516 
517 	g_return_val_if_fail (GMIME_IS_OBJECT (entity), NULL);
518 	g_return_val_if_fail (userid != NULL, NULL);
519 
520 	if (!(ctx = g_mime_crypto_context_new ("application/pkcs7-mime"))) {
521 		g_set_error (err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR,
522 			     _("Cannot sign application/pkcs7-mime part: no crypto context registered for this type."));
523 
524 		return NULL;
525 	}
526 
527 	options = _g_mime_format_options_clone (NULL, FALSE);
528 	g_mime_format_options_set_newline_format (options, GMIME_NEWLINE_FORMAT_DOS);
529 
530 	/* get the cleartext */
531 	stream = g_mime_stream_mem_new ();
532 	g_mime_object_write_to_stream (entity, options, stream);
533 	g_mime_format_options_free (options);
534 
535 	/* reset the content stream */
536 	g_mime_stream_reset (stream);
537 
538 	/* sign the content stream */
539 	ciphertext = g_mime_stream_mem_new ();
540 	if (g_mime_crypto_context_sign (ctx, FALSE, userid, stream, ciphertext, err) == -1) {
541 		g_object_unref (ciphertext);
542 		g_object_unref (stream);
543 		g_object_unref (ctx);
544 		return NULL;
545 	}
546 
547 	g_object_unref (stream);
548 	g_mime_stream_reset (ciphertext);
549 	g_object_unref (ctx);
550 
551 	/* construct the application/pkcs7-mime part */
552 	pkcs7_mime = g_mime_application_pkcs7_mime_new (GMIME_SECURE_MIME_TYPE_SIGNED_DATA);
553 	content = g_mime_data_wrapper_new_with_stream (ciphertext, GMIME_CONTENT_ENCODING_DEFAULT);
554 	g_mime_part_set_content ((GMimePart *) pkcs7_mime, content);
555 	g_object_unref (ciphertext);
556 	g_object_unref (content);
557 
558 	return pkcs7_mime;
559 }
560 
561 
562 /**
563  * g_mime_application_pkcs7_mime_verify:
564  * @pkcs7_mime: a #GMimeApplicationPkcs7Mime
565  * @flags: a #GMimeVerifyFlags
566  * @entity: (out) (transfer full): the extracted entity
567  * @err: a #GError
568  *
569  * Attempts to verify the signed @pkcs7_mime part and extract the original
570  * MIME entity.
571  *
572  * Returns: (nullable) (transfer full): a new #GMimeSignatureList object on
573  * success or %NULL on fail. If the verification fails, an exception
574  * will be set on @err to provide information as to why the failure
575  * occurred.
576  **/
577 GMimeSignatureList *
g_mime_application_pkcs7_mime_verify(GMimeApplicationPkcs7Mime * pkcs7_mime,GMimeVerifyFlags flags,GMimeObject ** entity,GError ** err)578 g_mime_application_pkcs7_mime_verify (GMimeApplicationPkcs7Mime *pkcs7_mime, GMimeVerifyFlags flags, GMimeObject **entity, GError **err)
579 {
580 	GMimeStream *filtered, *ciphertext, *stream;
581 	GMimeSignatureList *signatures;
582 	GMimeDataWrapper *content;
583 	GMimeCryptoContext *ctx;
584 	GMimeFilter *filter;
585 	GMimeParser *parser;
586 
587 	g_return_val_if_fail (GMIME_IS_APPLICATION_PKCS7_MIME (pkcs7_mime), NULL);
588 	g_return_val_if_fail (entity != NULL, NULL);
589 
590 	*entity = NULL;
591 
592 	if (!(ctx = g_mime_crypto_context_new ("application/pkcs7-mime"))) {
593 		g_set_error (err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR,
594 			     _("Cannot verify application/pkcs7-mime part: no crypto context registered for this type."));
595 
596 		return NULL;
597 	}
598 
599 	/* get the ciphertext stream */
600 	content = g_mime_part_get_content ((GMimePart *) pkcs7_mime);
601 	ciphertext = g_mime_stream_mem_new ();
602 	g_mime_data_wrapper_write_to_stream (content, ciphertext);
603 	g_mime_stream_reset (ciphertext);
604 
605 	stream = g_mime_stream_mem_new ();
606 	filtered = g_mime_stream_filter_new (stream);
607 	filter = g_mime_filter_dos2unix_new (FALSE);
608 	g_mime_stream_filter_add ((GMimeStreamFilter *) filtered, filter);
609 	g_object_unref (filter);
610 
611 	/* decrypt the content stream */
612 	if (!(signatures = g_mime_crypto_context_verify (ctx, flags, ciphertext, NULL, filtered, err))) {
613 		g_object_unref (ciphertext);
614 		g_object_unref (filtered);
615 		g_object_unref (stream);
616 		g_object_unref (ctx);
617 
618 		return NULL;
619 	}
620 
621 	g_mime_stream_flush (filtered);
622 	g_object_unref (ciphertext);
623 	g_object_unref (filtered);
624 	g_object_unref (ctx);
625 
626 	g_mime_stream_reset (stream);
627 	parser = g_mime_parser_new ();
628 	g_mime_parser_init_with_stream (parser, stream);
629 	g_object_unref (stream);
630 
631 	*entity = g_mime_parser_construct_part (parser, NULL);
632 	g_object_unref (parser);
633 
634 	if (*entity == NULL) {
635 		g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_PARSE_ERROR,
636 				     _("Cannot verify application/pkcs7-mime part: failed to parse extracted content."));
637 
638 		g_object_unref (signatures);
639 
640 		return NULL;
641 	}
642 
643 	return signatures;
644 }
645