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