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