2009-10-26 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / src / decrypt.c
1 /* decrypt.c - Decrypt function.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
4
5    This file is part of GPGME.
6  
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11    
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16    
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28
29 #include "debug.h"
30 #include "gpgme.h"
31 #include "util.h"
32 #include "context.h"
33 #include "ops.h"
34
35 \f
36 typedef struct
37 {
38   struct _gpgme_op_decrypt_result result;
39
40   int okay;
41   int failed;
42   
43   /* A pointer to the next pointer of the last recipient in the list.
44      This makes appending new invalid signers painless while
45      preserving the order.  */
46   gpgme_recipient_t *last_recipient_p;
47 } *op_data_t;
48
49
50 static void
51 release_op_data (void *hook)
52 {
53   op_data_t opd = (op_data_t) hook;
54   gpgme_recipient_t recipient = opd->result.recipients;
55
56   if (opd->result.unsupported_algorithm)
57     free (opd->result.unsupported_algorithm);
58
59   if (opd->result.file_name)
60     free (opd->result.file_name);
61
62   while (recipient)
63     {
64       gpgme_recipient_t next = recipient->next;
65       free (recipient);
66       recipient = next;
67     }
68 }
69
70
71 gpgme_decrypt_result_t
72 gpgme_op_decrypt_result (gpgme_ctx_t ctx)
73 {
74   void *hook;
75   op_data_t opd;
76   gpgme_error_t err;
77
78   TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_result", ctx);
79
80   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
81   opd = hook;
82   if (err || !opd)
83     {
84       TRACE_SUC0 ("result=(null)");
85       return NULL;
86     }
87
88   if (_gpgme_debug_trace ())
89     {
90       gpgme_recipient_t rcp;
91       int signatures = 0;
92
93       if (opd->result.unsupported_algorithm)
94         {
95           TRACE_LOG1 ("result: unsupported_algorithm: %s",
96                       opd->result.unsupported_algorithm);
97         }
98       if (opd->result.wrong_key_usage)
99         {
100           TRACE_LOG ("result: wrong key usage");
101         }
102       rcp = opd->result.recipients;
103       while (rcp)
104         {
105           TRACE_LOG3 ("result: recipient: keyid=%s, pubkey_algo=%i, "
106                       "status=%s", rcp->keyid, rcp->pubkey_algo,
107                       gpg_strerror (rcp->status));
108           rcp = rcp->next;
109         }
110       if (opd->result.file_name)
111         {
112           TRACE_LOG1 ("result: original file name: %s", opd->result.file_name);
113         }
114     }
115
116   TRACE_SUC1 ("result=%p", &opd->result);
117   return &opd->result;
118 }
119
120 \f
121 static gpgme_error_t
122 parse_enc_to (char *args, gpgme_recipient_t *recp)
123 {
124   gpgme_recipient_t rec;
125   char *tail;
126   int i;
127
128   rec = malloc (sizeof (*rec));
129   if (!rec)
130     return gpg_error_from_errno (errno);
131
132   rec->next = NULL;
133   rec->keyid = rec->_keyid;
134   rec->status = 0;
135
136   for (i = 0; i < sizeof (rec->_keyid) - 1; i++)
137     {
138       if (args[i] == '\0' || args[i] == ' ')
139         break;
140
141       rec->_keyid[i] = args[i];
142     }
143   rec->_keyid[i] = '\0';
144
145   args = &args[i];
146   if (*args != '\0' && *args != ' ')
147     {
148       free (rec);
149       return gpg_error (GPG_ERR_INV_ENGINE);
150     }
151
152   while (*args == ' ')
153     args++;
154
155   if (*args)
156     {
157       errno = 0;
158       rec->pubkey_algo = strtol (args, &tail, 0);
159       if (errno || args == tail || *tail != ' ')
160         {
161           /* The crypto backend does not behave.  */
162           free (rec);
163           return gpg_error (GPG_ERR_INV_ENGINE);
164         }
165     }
166
167   /* FIXME: The key length is always 0 right now, so no need to parse
168      it.  */
169
170   *recp = rec;
171   return 0;
172 }
173
174
175 gpgme_error_t
176 _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
177                                char *args)
178 {
179   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
180   gpgme_error_t err;
181   void *hook;
182   op_data_t opd;
183
184   err = _gpgme_passphrase_status_handler (priv, code, args);
185   if (err)
186     return err;
187
188   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
189   opd = hook;
190   if (err)
191     return err;
192
193   switch (code)
194     {
195     case GPGME_STATUS_EOF:
196       /* FIXME: These error values should probably be attributed to
197          the underlying crypto engine (as error source).  */
198       if (opd->failed)
199         return gpg_error (GPG_ERR_DECRYPT_FAILED);
200       else if (!opd->okay)
201         return gpg_error (GPG_ERR_NO_DATA);
202       break;
203
204     case GPGME_STATUS_DECRYPTION_OKAY:
205       opd->okay = 1;
206       break;
207
208     case GPGME_STATUS_DECRYPTION_FAILED:
209       opd->failed = 1;
210       break;
211
212     case GPGME_STATUS_ERROR:
213       /* Note that this is an informational status code which should
214          not lead to an error return unless it is something not
215          related to the backend.  */
216       {
217         const char d_alg[] = "decrypt.algorithm";
218         const char k_alg[] = "decrypt.keyusage";
219
220         if (!strncmp (args, d_alg, sizeof (d_alg) - 1))
221           {
222             args += sizeof (d_alg) - 1;
223             while (*args == ' ')
224               args++;
225
226             if (gpg_err_code (atoi (args)) == GPG_ERR_UNSUPPORTED_ALGORITHM)
227               {
228                 char *end;
229
230                 while (*args && *args != ' ')
231                   args++;
232                 while (*args == ' ')
233                   args++;
234
235                 end = strchr (args, ' ');
236                 if (end)
237                   *end = '\0';
238
239                 if (!(*args == '?' && *(args + 1) == '\0'))
240                   {
241                     opd->result.unsupported_algorithm = strdup (args);
242                     if (!opd->result.unsupported_algorithm)
243                       return gpg_error_from_errno (errno);
244                   }
245               }
246           }
247         else if (!strncmp (args, k_alg, sizeof (k_alg) - 1))
248           {
249             args += sizeof (k_alg) - 1;
250             while (*args == ' ')
251               args++;
252
253             if (gpg_err_code (atoi (args)) == GPG_ERR_WRONG_KEY_USAGE)
254               opd->result.wrong_key_usage = 1;
255           }
256       }
257       break;
258
259     case GPGME_STATUS_ENC_TO:
260       err = parse_enc_to (args, opd->last_recipient_p);
261       if (err)
262         return err;
263
264       opd->last_recipient_p = &(*opd->last_recipient_p)->next;
265       break;
266
267     case GPGME_STATUS_NO_SECKEY:
268       {
269         gpgme_recipient_t rec = opd->result.recipients;
270
271         while (rec)
272           {
273             if (!strcmp (rec->keyid, args))
274               {
275                 rec->status = gpg_error (GPG_ERR_NO_SECKEY);
276                 break;
277               }
278             rec = rec->next;
279           }
280         /* FIXME: Is this ok?  */
281         if (!rec)
282           return gpg_error (GPG_ERR_INV_ENGINE);
283       }
284       break;
285
286     case GPGME_STATUS_PLAINTEXT:
287       err = _gpgme_parse_plaintext (args, &opd->result.file_name);
288       if (err)
289         return err;
290       
291     default:
292       break;
293     }
294
295   return 0;
296 }
297
298
299 static gpgme_error_t
300 decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)
301 {
302   gpgme_error_t err;
303
304   err = _gpgme_progress_status_handler (priv, code, args);
305   if (!err)
306     err = _gpgme_decrypt_status_handler (priv, code, args);
307   return err;
308 }
309
310
311 gpgme_error_t
312 _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx)
313 {
314   gpgme_error_t err;
315   void *hook;
316   op_data_t opd;
317
318   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook,
319                                sizeof (*opd), release_op_data);
320   opd = hook;
321   if (err)
322     return err;
323
324   opd->last_recipient_p = &opd->result.recipients;
325   return 0;
326 }
327
328
329 static gpgme_error_t
330 decrypt_start (gpgme_ctx_t ctx, int synchronous,
331                       gpgme_data_t cipher, gpgme_data_t plain)
332 {
333   gpgme_error_t err;
334
335   err = _gpgme_op_reset (ctx, synchronous);
336   if (err)
337     return err;
338
339   err = _gpgme_op_decrypt_init_result (ctx);
340   if (err)
341     return err;
342
343   if (!cipher)
344     return gpg_error (GPG_ERR_NO_DATA);
345   if (!plain)
346     return gpg_error (GPG_ERR_INV_VALUE);
347
348   if (err)
349     return err;
350
351   if (ctx->passphrase_cb)
352     {
353       err = _gpgme_engine_set_command_handler
354         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
355       if (err)
356         return err;
357     }
358
359   _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx);
360
361   return _gpgme_engine_op_decrypt (ctx->engine, cipher, plain);
362 }
363
364
365 gpgme_error_t
366 gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher,
367                         gpgme_data_t plain)
368 {
369   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_start", ctx,
370               "cipher=%p, plain=%p", cipher, plain);
371   return TRACE_ERR (decrypt_start (ctx, 0, cipher, plain));
372 }
373
374
375 /* Decrypt ciphertext CIPHER within CTX and store the resulting
376    plaintext in PLAIN.  */
377 gpgme_error_t
378 gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)
379 {
380   gpgme_error_t err;
381
382   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_start", ctx,
383               "cipher=%p, plain=%p", cipher, plain);
384   err = decrypt_start (ctx, 1, cipher, plain);
385   if (!err)
386     err = _gpgme_wait_one (ctx);
387   return TRACE_ERR (err);
388 }