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