2009-11-02 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
92       if (opd->result.unsupported_algorithm)
93         {
94           TRACE_LOG1 ("result: unsupported_algorithm: %s",
95                       opd->result.unsupported_algorithm);
96         }
97       if (opd->result.wrong_key_usage)
98         {
99           TRACE_LOG ("result: wrong key usage");
100         }
101       rcp = opd->result.recipients;
102       while (rcp)
103         {
104           TRACE_LOG3 ("result: recipient: keyid=%s, pubkey_algo=%i, "
105                       "status=%s", rcp->keyid, rcp->pubkey_algo,
106                       gpg_strerror (rcp->status));
107           rcp = rcp->next;
108         }
109       if (opd->result.file_name)
110         {
111           TRACE_LOG1 ("result: original file name: %s", opd->result.file_name);
112         }
113     }
114
115   TRACE_SUC1 ("result=%p", &opd->result);
116   return &opd->result;
117 }
118
119 \f
120 static gpgme_error_t
121 parse_enc_to (char *args, gpgme_recipient_t *recp)
122 {
123   gpgme_recipient_t rec;
124   char *tail;
125   int i;
126
127   rec = malloc (sizeof (*rec));
128   if (!rec)
129     return gpg_error_from_errno (errno);
130
131   rec->next = NULL;
132   rec->keyid = rec->_keyid;
133   rec->status = 0;
134
135   for (i = 0; i < sizeof (rec->_keyid) - 1; i++)
136     {
137       if (args[i] == '\0' || args[i] == ' ')
138         break;
139
140       rec->_keyid[i] = args[i];
141     }
142   rec->_keyid[i] = '\0';
143
144   args = &args[i];
145   if (*args != '\0' && *args != ' ')
146     {
147       free (rec);
148       return gpg_error (GPG_ERR_INV_ENGINE);
149     }
150
151   while (*args == ' ')
152     args++;
153
154   if (*args)
155     {
156       errno = 0;
157       rec->pubkey_algo = strtol (args, &tail, 0);
158       if (errno || args == tail || *tail != ' ')
159         {
160           /* The crypto backend does not behave.  */
161           free (rec);
162           return gpg_error (GPG_ERR_INV_ENGINE);
163         }
164     }
165
166   /* FIXME: The key length is always 0 right now, so no need to parse
167      it.  */
168
169   *recp = rec;
170   return 0;
171 }
172
173
174 gpgme_error_t
175 _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
176                                char *args)
177 {
178   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
179   gpgme_error_t err;
180   void *hook;
181   op_data_t opd;
182
183   err = _gpgme_passphrase_status_handler (priv, code, args);
184   if (err)
185     return err;
186
187   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
188   opd = hook;
189   if (err)
190     return err;
191
192   switch (code)
193     {
194     case GPGME_STATUS_EOF:
195       /* FIXME: These error values should probably be attributed to
196          the underlying crypto engine (as error source).  */
197       if (opd->failed)
198         return gpg_error (GPG_ERR_DECRYPT_FAILED);
199       else if (!opd->okay)
200         return gpg_error (GPG_ERR_NO_DATA);
201       break;
202
203     case GPGME_STATUS_DECRYPTION_OKAY:
204       opd->okay = 1;
205       break;
206
207     case GPGME_STATUS_DECRYPTION_FAILED:
208       opd->failed = 1;
209       break;
210
211     case GPGME_STATUS_ERROR:
212       /* Note that this is an informational status code which should
213          not lead to an error return unless it is something not
214          related to the backend.  */
215       {
216         const char d_alg[] = "decrypt.algorithm";
217         const char k_alg[] = "decrypt.keyusage";
218
219         if (!strncmp (args, d_alg, sizeof (d_alg) - 1))
220           {
221             args += sizeof (d_alg) - 1;
222             while (*args == ' ')
223               args++;
224
225             if (gpg_err_code (atoi (args)) == GPG_ERR_UNSUPPORTED_ALGORITHM)
226               {
227                 char *end;
228
229                 while (*args && *args != ' ')
230                   args++;
231                 while (*args == ' ')
232                   args++;
233
234                 end = strchr (args, ' ');
235                 if (end)
236                   *end = '\0';
237
238                 if (!(*args == '?' && *(args + 1) == '\0'))
239                   {
240                     opd->result.unsupported_algorithm = strdup (args);
241                     if (!opd->result.unsupported_algorithm)
242                       return gpg_error_from_errno (errno);
243                   }
244               }
245           }
246         else if (!strncmp (args, k_alg, sizeof (k_alg) - 1))
247           {
248             args += sizeof (k_alg) - 1;
249             while (*args == ' ')
250               args++;
251
252             if (gpg_err_code (atoi (args)) == GPG_ERR_WRONG_KEY_USAGE)
253               opd->result.wrong_key_usage = 1;
254           }
255       }
256       break;
257
258     case GPGME_STATUS_ENC_TO:
259       err = parse_enc_to (args, opd->last_recipient_p);
260       if (err)
261         return err;
262
263       opd->last_recipient_p = &(*opd->last_recipient_p)->next;
264       break;
265
266     case GPGME_STATUS_NO_SECKEY:
267       {
268         gpgme_recipient_t rec = opd->result.recipients;
269
270         while (rec)
271           {
272             if (!strcmp (rec->keyid, args))
273               {
274                 rec->status = gpg_error (GPG_ERR_NO_SECKEY);
275                 break;
276               }
277             rec = rec->next;
278           }
279         /* FIXME: Is this ok?  */
280         if (!rec)
281           return gpg_error (GPG_ERR_INV_ENGINE);
282       }
283       break;
284
285     case GPGME_STATUS_PLAINTEXT:
286       err = _gpgme_parse_plaintext (args, &opd->result.file_name);
287       if (err)
288         return err;
289       
290     default:
291       break;
292     }
293
294   return 0;
295 }
296
297
298 static gpgme_error_t
299 decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)
300 {
301   gpgme_error_t err;
302
303   err = _gpgme_progress_status_handler (priv, code, args);
304   if (!err)
305     err = _gpgme_decrypt_status_handler (priv, code, args);
306   return err;
307 }
308
309
310 gpgme_error_t
311 _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx)
312 {
313   gpgme_error_t err;
314   void *hook;
315   op_data_t opd;
316
317   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook,
318                                sizeof (*opd), release_op_data);
319   opd = hook;
320   if (err)
321     return err;
322
323   opd->last_recipient_p = &opd->result.recipients;
324   return 0;
325 }
326
327
328 static gpgme_error_t
329 decrypt_start (gpgme_ctx_t ctx, int synchronous,
330                       gpgme_data_t cipher, gpgme_data_t plain)
331 {
332   gpgme_error_t err;
333
334   err = _gpgme_op_reset (ctx, synchronous);
335   if (err)
336     return err;
337
338   err = _gpgme_op_decrypt_init_result (ctx);
339   if (err)
340     return err;
341
342   if (!cipher)
343     return gpg_error (GPG_ERR_NO_DATA);
344   if (!plain)
345     return gpg_error (GPG_ERR_INV_VALUE);
346
347   if (err)
348     return err;
349
350   if (ctx->passphrase_cb)
351     {
352       err = _gpgme_engine_set_command_handler
353         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
354       if (err)
355         return err;
356     }
357
358   _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx);
359
360   return _gpgme_engine_op_decrypt (ctx->engine, cipher, plain);
361 }
362
363
364 gpgme_error_t
365 gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher,
366                         gpgme_data_t plain)
367 {
368   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_start", ctx,
369               "cipher=%p, plain=%p", cipher, plain);
370   return TRACE_ERR (decrypt_start (ctx, 0, cipher, plain));
371 }
372
373
374 /* Decrypt ciphertext CIPHER within CTX and store the resulting
375    plaintext in PLAIN.  */
376 gpgme_error_t
377 gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)
378 {
379   gpgme_error_t err;
380
381   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt", ctx,
382               "cipher=%p, plain=%p", cipher, plain);
383   err = decrypt_start (ctx, 1, cipher, plain);
384   if (!err)
385     err = _gpgme_wait_one (ctx);
386   return TRACE_ERR (err);
387 }