Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-multipart-signed.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2012 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-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"
39
40 #ifdef ENABLE_DEBUG
41 #define d(x) x
42 #else
43 #define d(x)
44 #endif
45
46 #define _(x) x
47
48
49 /**
50  * SECTION: gmime-multipart-signed
51  * @title: GMimeMultipartSigned
52  * @short_description: Signed MIME multiparts
53  * @see_also: #GMimeMultipart
54  *
55  * A #GMimeMultipartSigned part is a special subclass of
56  * #GMimeMultipart to make it easier to manipulate the
57  * multipart/signed MIME type.
58  **/
59
60
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);
65
66 /* GMimeObject class methods */
67 static void multipart_signed_encode (GMimeObject *object, GMimeEncodingConstraint constraint);
68
69
70 static GMimeMultipartClass *parent_class = NULL;
71
72
73 GType
74 g_mime_multipart_signed_get_type (void)
75 {
76         static GType type = 0;
77         
78         if (!type) {
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),
87                         0,    /* n_preallocs */
88                         (GInstanceInitFunc) g_mime_multipart_signed_init,
89                 };
90                 
91                 type = g_type_register_static (GMIME_TYPE_MULTIPART, "GMimeMultipartSigned", &info, 0);
92         }
93         
94         return type;
95 }
96
97
98 static void
99 g_mime_multipart_signed_class_init (GMimeMultipartSignedClass *klass)
100 {
101         GMimeObjectClass *object_class = GMIME_OBJECT_CLASS (klass);
102         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
103         
104         parent_class = g_type_class_ref (GMIME_TYPE_MULTIPART);
105         
106         gobject_class->finalize = g_mime_multipart_signed_finalize;
107         
108         object_class->encode = multipart_signed_encode;
109 }
110
111 static void
112 g_mime_multipart_signed_init (GMimeMultipartSigned *mps, GMimeMultipartSignedClass *klass)
113 {
114         
115 }
116
117 static void
118 g_mime_multipart_signed_finalize (GObject *object)
119 {
120         G_OBJECT_CLASS (parent_class)->finalize (object);
121 }
122
123 static void
124 multipart_signed_encode (GMimeObject *object, GMimeEncodingConstraint constraint)
125 {
126         /* Do NOT encode subparts of a multipart/signed */
127         return;
128 }
129
130
131 /**
132  * g_mime_multipart_signed_new:
133  *
134  * Creates a new MIME multipart/signed object.
135  *
136  * Returns: an empty MIME multipart/signed object.
137  **/
138 GMimeMultipartSigned *
139 g_mime_multipart_signed_new (void)
140 {
141         GMimeMultipartSigned *multipart;
142         GMimeContentType *content_type;
143         
144         multipart = g_object_newv (GMIME_TYPE_MULTIPART_SIGNED, 0, NULL);
145         
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);
149         
150         return multipart;
151 }
152
153
154 /**
155  * sign_prepare:
156  * @mime_part: MIME part
157  *
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.
161  **/
162 static void
163 sign_prepare (GMimeObject *mime_part)
164 {
165         GMimeContentEncoding encoding;
166         GMimeMultipart *multipart;
167         GMimeObject *subpart;
168         int i, n;
169         
170         if (GMIME_IS_MULTIPART (mime_part)) {
171                 multipart = (GMimeMultipart *) mime_part;
172                 
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 */
176                         return;
177                 }
178                 
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);
183                 }
184         } else if (GMIME_IS_MESSAGE_PART (mime_part)) {
185                 subpart = GMIME_MESSAGE_PART (mime_part)->message->mime_part;
186                 sign_prepare (subpart);
187         } else {
188                 encoding = g_mime_part_get_content_encoding (GMIME_PART (mime_part));
189                 
190                 if (encoding != GMIME_CONTENT_ENCODING_BASE64)
191                         g_mime_part_set_content_encoding (GMIME_PART (mime_part),
192                                                           GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE);
193         }
194 }
195
196
197 /**
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
204  * @err: exception
205  *
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.
210  *
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
213  * failure occured.
214  **/
215 int
216 g_mime_multipart_signed_sign (GMimeMultipartSigned *mps, GMimeObject *content,
217                               GMimeCryptoContext *ctx, const char *userid,
218                               GMimeDigestAlgo digest, GError **err)
219 {
220         GMimeStream *stream, *filtered, *sigstream;
221         GMimeContentType *content_type;
222         GMimeDataWrapper *wrapper;
223         GMimePart *signature;
224         GMimeFilter *filter;
225         GMimeParser *parser;
226         const char *protocol;
227         const char *micalg;
228         int rv;
229         
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);
233         
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."));
236                 return -1;
237         }
238         
239         /* Prepare all the parts for signing... */
240         sign_prepare (content);
241         
242         /* get the cleartext */
243         stream = g_mime_stream_mem_new ();
244         filtered = g_mime_stream_filter_new (stream);
245         
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);
250         
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);
255         
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);
260         
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);
266         
267         /* construct the signature stream */
268         sigstream = g_mime_stream_mem_new ();
269         
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);
275                 return -1;
276         }
277         
278         g_object_unref (filtered);
279         g_mime_stream_reset (sigstream);
280         g_mime_stream_reset (stream);
281         
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);
288         
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);
294         
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);
299         
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);
305         
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");
312         }
313         
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);
320         
321         return 0;
322 }
323
324 static gboolean
325 check_protocol_supported (const char *protocol, const char *supported)
326 {
327         const char *subtype;
328         char *xsupported;
329         gboolean rv;
330         
331         if (!supported)
332                 return FALSE;
333         
334         if (!g_ascii_strcasecmp (protocol, supported))
335                 return TRUE;
336         
337         if (!(subtype = strrchr (supported, '/')))
338                 return FALSE;
339         
340         subtype++;
341         
342         /* If the subtype already begins with "x-", then there's
343          * nothing else to check. */
344         if (!g_ascii_strncasecmp (subtype, "x-", 2))
345                 return FALSE;
346         
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);
353         g_free (xsupported);
354         
355         return rv;
356 }
357
358
359 /**
360  * g_mime_multipart_signed_verify:
361  * @mps: multipart/signed object
362  * @ctx: encryption crypto context
363  * @err: exception
364  *
365  * Attempts to verify the signed MIME part contained within the
366  * multipart/signed object @mps using the @ctx crypto context.
367  *
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.
371  **/
372 GMimeSignatureList *
373 g_mime_multipart_signed_verify (GMimeMultipartSigned *mps, GMimeCryptoContext *ctx,
374                                 GError **err)
375 {
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;
384         char *content_type;
385         
386         g_return_val_if_fail (GMIME_IS_MULTIPART_SIGNED (mps), NULL);
387         g_return_val_if_fail (GMIME_IS_CRYPTO_CONTEXT (ctx), NULL);
388         
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."));
392                 return NULL;
393         }
394         
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");
397         
398         supported = g_mime_crypto_context_get_signature_protocol (ctx);
399         
400         if (protocol) {
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'."),
405                                      protocol);
406                         return NULL;
407                 }
408         } else if (supported != NULL) {
409                 /* *shrug* - I guess just go on as if they match? */
410                 protocol = supported;
411         } else {
412                 g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR,
413                                      _("Cannot verify multipart/signed part: unspecified signature protocol."));
414                 
415                 return NULL;
416         }
417         
418         signature = g_mime_multipart_get_part (GMIME_MULTIPART (mps), GMIME_MULTIPART_SIGNED_SIGNATURE);
419         
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);
426                 
427                 return NULL;
428         }
429         g_free (content_type);
430         
431         content = g_mime_multipart_get_part (GMIME_MULTIPART (mps), GMIME_MULTIPART_SIGNED_CONTENT);
432         
433         /* get the content stream */
434         stream = g_mime_stream_mem_new ();
435         filtered_stream = g_mime_stream_filter_new (stream);
436         
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);
441         
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);
446         
447         /* get the signature stream */
448         wrapper = g_mime_part_get_content_object (GMIME_PART (signature));
449         
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);
457         } else {
458                 sigstream = g_mime_data_wrapper_get_stream (wrapper);
459         }
460         
461         g_mime_stream_reset (sigstream);
462         
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);
466         
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));
469         
470         g_object_unref (stream);
471         
472         return signatures;
473 }