1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2000-2012 Jeffrey Stedfast
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.
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.
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
28 #include "gmime-multipart-signed.h"
29 #include "gmime-multipart-encrypted.h"
30 #include "gmime-message-part.h"
31 #include "gmime-stream-filter.h"
32 #include "gmime-filter-strip.h"
33 #include "gmime-filter-from.h"
34 #include "gmime-filter-crlf.h"
35 #include "gmime-stream-mem.h"
36 #include "gmime-parser.h"
37 #include "gmime-error.h"
38 #include "gmime-part.h"
50 * SECTION: gmime-multipart-signed
51 * @title: GMimeMultipartSigned
52 * @short_description: Signed MIME multiparts
53 * @see_also: #GMimeMultipart
55 * A #GMimeMultipartSigned part is a special subclass of
56 * #GMimeMultipart to make it easier to manipulate the
57 * multipart/signed MIME type.
61 /* GObject class methods */
62 static void g_mime_multipart_signed_class_init (GMimeMultipartSignedClass *klass);
63 static void g_mime_multipart_signed_init (GMimeMultipartSigned *mps, GMimeMultipartSignedClass *klass);
64 static void g_mime_multipart_signed_finalize (GObject *object);
66 /* GMimeObject class methods */
67 static void multipart_signed_encode (GMimeObject *object, GMimeEncodingConstraint constraint);
70 static GMimeMultipartClass *parent_class = NULL;
74 g_mime_multipart_signed_get_type (void)
76 static GType type = 0;
79 static const GTypeInfo info = {
80 sizeof (GMimeMultipartSignedClass),
81 NULL, /* base_class_init */
82 NULL, /* base_class_finalize */
83 (GClassInitFunc) g_mime_multipart_signed_class_init,
84 NULL, /* class_finalize */
85 NULL, /* class_data */
86 sizeof (GMimeMultipartSigned),
88 (GInstanceInitFunc) g_mime_multipart_signed_init,
91 type = g_type_register_static (GMIME_TYPE_MULTIPART, "GMimeMultipartSigned", &info, 0);
99 g_mime_multipart_signed_class_init (GMimeMultipartSignedClass *klass)
101 GMimeObjectClass *object_class = GMIME_OBJECT_CLASS (klass);
102 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
104 parent_class = g_type_class_ref (GMIME_TYPE_MULTIPART);
106 gobject_class->finalize = g_mime_multipart_signed_finalize;
108 object_class->encode = multipart_signed_encode;
112 g_mime_multipart_signed_init (GMimeMultipartSigned *mps, GMimeMultipartSignedClass *klass)
118 g_mime_multipart_signed_finalize (GObject *object)
120 G_OBJECT_CLASS (parent_class)->finalize (object);
124 multipart_signed_encode (GMimeObject *object, GMimeEncodingConstraint constraint)
126 /* Do NOT encode subparts of a multipart/signed */
132 * g_mime_multipart_signed_new:
134 * Creates a new MIME multipart/signed object.
136 * Returns: an empty MIME multipart/signed object.
138 GMimeMultipartSigned *
139 g_mime_multipart_signed_new (void)
141 GMimeMultipartSigned *multipart;
142 GMimeContentType *content_type;
144 multipart = g_object_newv (GMIME_TYPE_MULTIPART_SIGNED, 0, NULL);
146 content_type = g_mime_content_type_new ("multipart", "signed");
147 g_mime_object_set_content_type (GMIME_OBJECT (multipart), content_type);
148 g_object_unref (content_type);
156 * @mime_part: MIME part
158 * Prepare a part (and all subparts) to be signed. To do this we need
159 * to set the encoding of all parts (that are not already encoded to
160 * either QP or Base64) to QP.
163 sign_prepare (GMimeObject *mime_part)
165 GMimeContentEncoding encoding;
166 GMimeMultipart *multipart;
167 GMimeObject *subpart;
170 if (GMIME_IS_MULTIPART (mime_part)) {
171 multipart = (GMimeMultipart *) mime_part;
173 if (GMIME_IS_MULTIPART_SIGNED (multipart) ||
174 GMIME_IS_MULTIPART_ENCRYPTED (multipart)) {
175 /* must not modify these parts as they must be treated as opaque */
179 n = g_mime_multipart_get_count (multipart);
180 for (i = 0; i < n; i++) {
181 subpart = g_mime_multipart_get_part (multipart, i);
182 sign_prepare (subpart);
184 } else if (GMIME_IS_MESSAGE_PART (mime_part)) {
185 subpart = GMIME_MESSAGE_PART (mime_part)->message->mime_part;
186 sign_prepare (subpart);
188 encoding = g_mime_part_get_content_encoding (GMIME_PART (mime_part));
190 if (encoding != GMIME_CONTENT_ENCODING_BASE64)
191 g_mime_part_set_content_encoding (GMIME_PART (mime_part),
192 GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE);
198 * g_mime_multipart_signed_sign:
199 * @mps: multipart/signed object
200 * @content: MIME part to sign
201 * @ctx: encryption crypto context
202 * @userid: user id to sign with
203 * @digest: preferred digest algorithm
206 * Attempts to sign the @content MIME part with @userid's private key
207 * using the @ctx signing context with the @digest algorithm. If
208 * successful, the signed #GMimeObject is set as the signed part of
209 * the multipart/signed object @mps.
211 * Returns: %0 on success or %-1 on fail. If the signing fails, an
212 * exception will be set on @err to provide information as to why the
216 g_mime_multipart_signed_sign (GMimeMultipartSigned *mps, GMimeObject *content,
217 GMimeCryptoContext *ctx, const char *userid,
218 GMimeDigestAlgo digest, GError **err)
220 GMimeStream *stream, *filtered, *sigstream;
221 GMimeContentType *content_type;
222 GMimeDataWrapper *wrapper;
223 GMimePart *signature;
226 const char *protocol;
230 g_return_val_if_fail (GMIME_IS_MULTIPART_SIGNED (mps), -1);
231 g_return_val_if_fail (GMIME_IS_CRYPTO_CONTEXT (ctx), -1);
232 g_return_val_if_fail (GMIME_IS_OBJECT (content), -1);
234 if (!(protocol = g_mime_crypto_context_get_signature_protocol (ctx))) {
235 g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED, _("Signing not supported."));
239 /* Prepare all the parts for signing... */
240 sign_prepare (content);
242 /* get the cleartext */
243 stream = g_mime_stream_mem_new ();
244 filtered = g_mime_stream_filter_new (stream);
246 /* Note: see rfc3156, section 3 - second note */
247 filter = g_mime_filter_from_new (GMIME_FILTER_FROM_MODE_ARMOR);
248 g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered), filter);
249 g_object_unref (filter);
251 /* Note: see rfc3156, section 5.4 (this is the main difference between rfc2015 and rfc3156) */
252 filter = g_mime_filter_strip_new ();
253 g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered), filter);
254 g_object_unref (filter);
256 g_mime_object_write_to_stream (content, filtered);
257 g_mime_stream_flush (filtered);
258 g_object_unref (filtered);
259 g_mime_stream_reset (stream);
261 /* Note: see rfc2015 or rfc3156, section 5.1 */
262 filtered = g_mime_stream_filter_new (stream);
263 filter = g_mime_filter_crlf_new (TRUE, FALSE);
264 g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered), filter);
265 g_object_unref (filter);
267 /* construct the signature stream */
268 sigstream = g_mime_stream_mem_new ();
270 /* sign the content stream */
271 if ((rv = g_mime_crypto_context_sign (ctx, userid, digest, filtered, sigstream, err)) == -1) {
272 g_object_unref (sigstream);
273 g_object_unref (filtered);
274 g_object_unref (stream);
278 g_object_unref (filtered);
279 g_mime_stream_reset (sigstream);
280 g_mime_stream_reset (stream);
282 /* set the multipart/signed protocol and micalg */
283 content_type = g_mime_object_get_content_type (GMIME_OBJECT (mps));
284 g_mime_content_type_set_parameter (content_type, "protocol", protocol);
285 micalg = g_strdup (g_mime_crypto_context_digest_name (ctx, (GMimeDigestAlgo) rv));
286 g_mime_content_type_set_parameter (content_type, "micalg", micalg);
287 g_mime_multipart_set_boundary (GMIME_MULTIPART (mps), NULL);
289 /* construct the content part */
290 parser = g_mime_parser_new_with_stream (stream);
291 content = g_mime_parser_construct_part (parser);
292 g_object_unref (stream);
293 g_object_unref (parser);
295 /* construct the signature part */
296 content_type = g_mime_content_type_new_from_string (protocol);
297 signature = g_mime_part_new_with_type (content_type->type, content_type->subtype);
298 g_object_unref (content_type);
300 wrapper = g_mime_data_wrapper_new ();
301 g_mime_data_wrapper_set_stream (wrapper, sigstream);
302 g_mime_part_set_content_object (signature, wrapper);
303 g_object_unref (sigstream);
304 g_object_unref (wrapper);
306 /* FIXME: temporary hack, this info should probably be set in
307 * the CryptoContext class - maybe ::sign can take/output a
308 * GMimePart instead. */
309 if (!g_ascii_strcasecmp (protocol, "application/pkcs7-signature")) {
310 g_mime_part_set_content_encoding (signature, GMIME_CONTENT_ENCODING_BASE64);
311 g_mime_part_set_filename (signature, "smime.p7m");
314 /* save the content and signature parts */
315 /* FIXME: make sure there aren't any other parts?? */
316 g_mime_multipart_add (GMIME_MULTIPART (mps), content);
317 g_mime_multipart_add (GMIME_MULTIPART (mps), (GMimeObject *) signature);
318 g_object_unref (signature);
319 g_object_unref (content);
325 check_protocol_supported (const char *protocol, const char *supported)
334 if (!g_ascii_strcasecmp (protocol, supported))
337 if (!(subtype = strrchr (supported, '/')))
342 /* If the subtype already begins with "x-", then there's
343 * nothing else to check. */
344 if (!g_ascii_strncasecmp (subtype, "x-", 2))
347 /* Check if the "x-" version of the subtype matches the
348 * protocol. For example, if the supported protocol is
349 * "application/pkcs7-signature", then we also want to
350 * match "application/x-pkcs7-signature". */
351 xsupported = g_strdup_printf ("%.*sx-%s", (int) (subtype - supported), supported, subtype);
352 rv = !g_ascii_strcasecmp (protocol, xsupported);
360 * g_mime_multipart_signed_verify:
361 * @mps: multipart/signed object
362 * @ctx: encryption crypto context
365 * Attempts to verify the signed MIME part contained within the
366 * multipart/signed object @mps using the @ctx crypto context.
368 * Returns: a new #GMimeSignatureList object on success or %NULL on fail. If
369 * the verification fails, an exception will be set on @err to provide
370 * information as to why the failure occured.
373 g_mime_multipart_signed_verify (GMimeMultipartSigned *mps, GMimeCryptoContext *ctx,
376 const char *supported, *protocol, *micalg;
377 GMimeObject *content, *signature;
378 GMimeStream *stream, *sigstream;
379 GMimeSignatureList *signatures;
380 GMimeStream *filtered_stream;
381 GMimeDataWrapper *wrapper;
382 GMimeFilter *crlf_filter;
383 GMimeDigestAlgo digest;
386 g_return_val_if_fail (GMIME_IS_MULTIPART_SIGNED (mps), NULL);
387 g_return_val_if_fail (GMIME_IS_CRYPTO_CONTEXT (ctx), NULL);
389 if (g_mime_multipart_get_count ((GMimeMultipart *) mps) < 2) {
390 g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_PARSE_ERROR,
391 _("Cannot verify multipart/signed part due to missing subparts."));
395 protocol = g_mime_object_get_content_type_parameter (GMIME_OBJECT (mps), "protocol");
396 micalg = g_mime_object_get_content_type_parameter (GMIME_OBJECT (mps), "micalg");
398 supported = g_mime_crypto_context_get_signature_protocol (ctx);
401 /* make sure the protocol matches the crypto sign protocol */
402 if (!check_protocol_supported (protocol, supported)) {
403 g_set_error (err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR,
404 _("Cannot verify multipart/signed part: unsupported signature protocol '%s'."),
408 } else if (supported != NULL) {
409 /* *shrug* - I guess just go on as if they match? */
410 protocol = supported;
412 g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR,
413 _("Cannot verify multipart/signed part: unspecified signature protocol."));
418 signature = g_mime_multipart_get_part (GMIME_MULTIPART (mps), GMIME_MULTIPART_SIGNED_SIGNATURE);
420 /* make sure the protocol matches the signature content-type */
421 content_type = g_mime_content_type_to_string (signature->content_type);
422 if (g_ascii_strcasecmp (content_type, protocol) != 0) {
423 g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_PARSE_ERROR,
424 _("Cannot verify multipart/signed part: signature content-type does not match protocol."));
425 g_free (content_type);
429 g_free (content_type);
431 content = g_mime_multipart_get_part (GMIME_MULTIPART (mps), GMIME_MULTIPART_SIGNED_CONTENT);
433 /* get the content stream */
434 stream = g_mime_stream_mem_new ();
435 filtered_stream = g_mime_stream_filter_new (stream);
437 /* Note: see rfc2015 or rfc3156, section 5.1 */
438 crlf_filter = g_mime_filter_crlf_new (TRUE, FALSE);
439 g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), crlf_filter);
440 g_object_unref (crlf_filter);
442 g_mime_object_write_to_stream (content, filtered_stream);
443 g_mime_stream_flush (filtered_stream);
444 g_object_unref (filtered_stream);
445 g_mime_stream_reset (stream);
447 /* get the signature stream */
448 wrapper = g_mime_part_get_content_object (GMIME_PART (signature));
450 /* FIXME: temporary hack for Balsa to support S/MIME,
451 * ::verify() should probably take a mime part so it can
452 * decode this itself if it needs to. */
453 if (!g_ascii_strcasecmp (protocol, "application/pkcs7-signature") ||
454 !g_ascii_strcasecmp (protocol, "application/x-pkcs7-signature")) {
455 sigstream = g_mime_stream_mem_new ();
456 g_mime_data_wrapper_write_to_stream (wrapper, sigstream);
458 sigstream = g_mime_data_wrapper_get_stream (wrapper);
461 g_mime_stream_reset (sigstream);
463 /* verify the signature */
464 digest = g_mime_crypto_context_digest_id (ctx, micalg);
465 signatures = g_mime_crypto_context_verify (ctx, digest, stream, sigstream, err);
467 d(printf ("attempted to verify:\n----- BEGIN SIGNED PART -----\n%.*s----- END SIGNED PART -----\n",
468 (int) GMIME_STREAM_MEM (stream)->buffer->len, GMIME_STREAM_MEM (stream)->buffer->data));
470 g_object_unref (stream);