core: Do not crash if CMS plaintext is ignored
[gpgme.git] / src / import.c
1 /* import.c - Import a key.
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 <errno.h>
27 #include <string.h>
28
29 #include "gpgme.h"
30 #include "debug.h"
31 #include "context.h"
32 #include "ops.h"
33 #include "util.h"
34
35 \f
36 typedef struct
37 {
38   struct _gpgme_op_import_result result;
39
40   /* A pointer to the next pointer of the last import status in the
41      list.  This makes appending new imports painless while preserving
42      the order.  */
43   gpgme_import_status_t *lastp;
44 } *op_data_t;
45
46
47 static void
48 release_op_data (void *hook)
49 {
50   op_data_t opd = (op_data_t) hook;
51   gpgme_import_status_t import = opd->result.imports;
52
53   while (import)
54     {
55       gpgme_import_status_t next = import->next;
56       free (import->fpr);
57       free (import);
58       import = next;
59     }
60 }
61
62
63 gpgme_import_result_t
64 gpgme_op_import_result (gpgme_ctx_t ctx)
65 {
66   void *hook;
67   op_data_t opd;
68   gpgme_error_t err;
69
70   TRACE_BEG (DEBUG_CTX, "gpgme_op_import_result", ctx);
71
72   err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL);
73   opd = hook;
74   if (err || !opd)
75     {
76       TRACE_SUC0 ("result=(null)");
77       return NULL;
78     }
79
80
81   if (_gpgme_debug_trace ())
82     {
83       gpgme_import_status_t impstat;
84       int i;
85
86       TRACE_LOG5 ("%i considered, %i no UID, %i imported, %i imported RSA, "
87                   "%i unchanged", opd->result.considered,
88                   opd->result.no_user_id, opd->result.imported,
89                   opd->result.imported_rsa, opd->result.unchanged);
90       TRACE_LOG4 ("%i new UIDs, %i new sub keys, %i new signatures, "
91                   "%i new revocations", opd->result.new_user_ids,
92                   opd->result.new_sub_keys, opd->result.new_signatures,
93                   opd->result.new_revocations);
94       TRACE_LOG3 ("%i secret keys, %i imported, %i unchanged",
95                   opd->result.secret_read, opd->result.secret_imported,
96                   opd->result.secret_unchanged);
97       TRACE_LOG3 ("%i skipped new keys, %i not imported, %i v3 skipped",
98                   opd->result.skipped_new_keys, opd->result.not_imported,
99                   opd->result.skipped_v3_keys);
100
101       impstat = opd->result.imports;
102       i = 0;
103       while (impstat)
104         {
105           TRACE_LOG4 ("import[%i] for %s = 0x%x (%s)",
106                       i, impstat->fpr, impstat->status, impstat->result);
107           impstat = impstat->next;
108           i++;
109         }
110     }
111
112   TRACE_SUC1 ("result=%p", &opd->result);
113   return &opd->result;
114 }
115
116 \f
117 static gpgme_error_t
118 parse_import (char *args, gpgme_import_status_t *import_status, int problem)
119 {
120   gpgme_import_status_t import;
121   char *tail;
122   long int nr;
123
124   import = malloc (sizeof (*import));
125   if (!import)
126     return gpg_error_from_syserror ();
127   import->next = NULL;
128
129   gpg_err_set_errno (0);
130   nr = strtol (args, &tail, 0);
131   if (errno || args == tail || *tail != ' ')
132     {
133       /* The crypto backend does not behave.  */
134       free (import);
135       return trace_gpg_error (GPG_ERR_INV_ENGINE);
136     }
137   args = tail;
138
139   if (problem)
140     {
141       switch (nr)
142         {
143         case 0:
144         case 4:
145         default:
146           import->result = gpg_error (GPG_ERR_GENERAL);
147           break;
148
149         case 1:
150           import->result = gpg_error (GPG_ERR_BAD_CERT);
151           break;
152
153         case 2:
154           import->result = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
155           break;
156
157         case 3:
158           import->result = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
159           break;
160         }
161       import->status = 0;
162     }
163   else
164     {
165       import->result = gpg_error (GPG_ERR_NO_ERROR);
166       import->status = nr;
167     }
168
169   while (*args == ' ')
170     args++;
171   tail = strchr (args, ' ');
172   if (tail)
173     *tail = '\0';
174
175   import->fpr = strdup (args);
176   if (!import->fpr)
177     {
178       free (import);
179       return gpg_error_from_syserror ();
180     }
181
182   *import_status = import;
183   return 0;
184 }
185
186
187
188 gpgme_error_t
189 parse_import_res (char *args, gpgme_import_result_t result)
190 {
191   char *tail;
192
193   gpg_err_set_errno (0);
194
195 #define PARSE_NEXT(x)                                   \
196   (x) = strtol (args, &tail, 0);                        \
197   if (errno || args == tail || !(*tail == ' ' || !*tail))   \
198     /* The crypto backend does not behave.  */          \
199     return trace_gpg_error (GPG_ERR_INV_ENGINE);        \
200   args = tail;
201
202   PARSE_NEXT (result->considered);
203   PARSE_NEXT (result->no_user_id);
204   PARSE_NEXT (result->imported);
205   PARSE_NEXT (result->imported_rsa);
206   PARSE_NEXT (result->unchanged);
207   PARSE_NEXT (result->new_user_ids);
208   PARSE_NEXT (result->new_sub_keys);
209   PARSE_NEXT (result->new_signatures);
210   PARSE_NEXT (result->new_revocations);
211   PARSE_NEXT (result->secret_read);
212   PARSE_NEXT (result->secret_imported);
213   PARSE_NEXT (result->secret_unchanged);
214   PARSE_NEXT (result->skipped_new_keys);
215   PARSE_NEXT (result->not_imported);
216   if (args && *args)
217     {
218       PARSE_NEXT (result->skipped_v3_keys);
219     }
220
221   return 0;
222 }
223
224
225 static gpgme_error_t
226 import_status_handler (void *priv, gpgme_status_code_t code, char *args)
227 {
228   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
229   gpgme_error_t err;
230   void *hook;
231   op_data_t opd;
232
233   err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL);
234   opd = hook;
235   if (err)
236     return err;
237
238   switch (code)
239     {
240     case GPGME_STATUS_IMPORT_OK:
241     case GPGME_STATUS_IMPORT_PROBLEM:
242       err = parse_import (args, opd->lastp,
243                           code == GPGME_STATUS_IMPORT_OK ? 0 : 1);
244       if (err)
245         return err;
246
247       opd->lastp = &(*opd->lastp)->next;
248       break;
249
250     case GPGME_STATUS_IMPORT_RES:
251       err = parse_import_res (args, &opd->result);
252       break;
253
254     default:
255       break;
256     }
257   return err;
258 }
259
260
261 static gpgme_error_t
262 _gpgme_op_import_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t keydata)
263 {
264   gpgme_error_t err;
265   void *hook;
266   op_data_t opd;
267
268   err = _gpgme_op_reset (ctx, synchronous);
269   if (err)
270     return err;
271
272   err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
273                                sizeof (*opd), release_op_data);
274   opd = hook;
275   if (err)
276     return err;
277   opd->lastp = &opd->result.imports;
278
279   if (!keydata)
280     return gpg_error (GPG_ERR_NO_DATA);
281
282   _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
283
284   return _gpgme_engine_op_import (ctx->engine, keydata, NULL);
285 }
286
287
288 gpgme_error_t
289 gpgme_op_import_start (gpgme_ctx_t ctx, gpgme_data_t keydata)
290 {
291   gpg_error_t err;
292
293   TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import_start", ctx,
294               "keydata=%p", keydata);
295
296   if (!ctx)
297     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
298
299   err = _gpgme_op_import_start (ctx, 0, keydata);
300   return TRACE_ERR (err);
301 }
302
303
304 /* Import the key in KEYDATA into the keyring.  */
305 gpgme_error_t
306 gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata)
307 {
308   gpgme_error_t err;
309
310   TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import", ctx,
311               "keydata=%p", keydata);
312
313   if (!ctx)
314     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
315
316   err = _gpgme_op_import_start (ctx, 1, keydata);
317   if (!err)
318     err = _gpgme_wait_one (ctx);
319   return TRACE_ERR (err);
320 }
321
322
323 \f
324 static gpgme_error_t
325 _gpgme_op_import_keys_start (gpgme_ctx_t ctx, int synchronous,
326                              gpgme_key_t *keys)
327 {
328   gpgme_error_t err;
329   void *hook;
330   op_data_t opd;
331   int idx, firstidx, nkeys;
332
333   err = _gpgme_op_reset (ctx, synchronous);
334   if (err)
335     return err;
336
337   err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
338                                sizeof (*opd), release_op_data);
339   opd = hook;
340   if (err)
341     return err;
342   opd->lastp = &opd->result.imports;
343
344   if (!keys)
345     return gpg_error (GPG_ERR_NO_DATA);
346
347   for (idx=nkeys=0, firstidx=-1; keys[idx]; idx++)
348     {
349       /* We only consider keys of the current protocol.  */
350       if (keys[idx]->protocol != ctx->protocol)
351         continue;
352       if (firstidx == -1)
353         firstidx = idx;
354       /* If a key has been found using a different key listing mode,
355          we bail out.  This makes the processing easier.  Fixme: To
356          allow a mix of keys we would need to sort them by key listing
357          mode and start two import operations one after the other.  */
358       if (keys[idx]->keylist_mode != keys[firstidx]->keylist_mode)
359         return gpg_error (GPG_ERR_CONFLICT);
360       nkeys++;
361     }
362   if (!nkeys)
363     return gpg_error (GPG_ERR_NO_DATA);
364
365   _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
366
367   return _gpgme_engine_op_import (ctx->engine, NULL, keys);
368 }
369
370
371 /* Asynchronous version of gpgme_op_import_key.  */
372 gpgme_error_t
373 gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t *keys)
374 {
375   gpg_error_t err;
376
377   TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys_start", ctx);
378
379   if (!ctx)
380     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
381
382   if (_gpgme_debug_trace () && keys)
383     {
384       int i = 0;
385
386       while (keys[i])
387         {
388           TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
389                       (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
390                       keys[i]->subkeys->fpr : "invalid");
391           i++;
392         }
393     }
394
395   err = _gpgme_op_import_keys_start (ctx, 0, keys);
396   return TRACE_ERR (err);
397 }
398
399
400 /* Import the keys from the array KEYS into the keyring.  In
401    particular it is used to actually import keys retrieved from an
402    external source (i.e. using GPGME_KEYLIST_MODE_EXTERN).  It
403    replaces the old workaround of exporting and then importing a key
404    as used to make an X.509 key permanent.  This function
405    automagically does the right thing.
406
407    KEYS is a NULL terminated array of gpgme key objects.  The result
408    is the usual import result structure.  Only keys matching the
409    current protocol are imported; other keys are ignored.  */
410 gpgme_error_t
411 gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t *keys)
412 {
413   gpgme_error_t err;
414
415   TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys", ctx);
416
417   if (!ctx)
418     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
419
420   if (_gpgme_debug_trace () && keys)
421     {
422       int i = 0;
423
424       while (keys[i])
425         {
426           TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
427                       (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
428                       keys[i]->subkeys->fpr : "invalid");
429           i++;
430         }
431     }
432
433   err = _gpgme_op_import_keys_start (ctx, 1, keys);
434   if (!err)
435     err = _gpgme_wait_one (ctx);
436   return TRACE_ERR (err);
437 }
438
439
440 /* Deprecated interface.  */
441 gpgme_error_t
442 gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr)
443 {
444   gpgme_error_t err = gpgme_op_import (ctx, keydata);
445   if (!err && nr)
446     {
447       gpgme_import_result_t result = gpgme_op_import_result (ctx);
448       *nr = result->considered;
449     }
450   return err;
451 }