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