Line data Source code
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 :
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 16 : release_op_data (void *hook)
49 : {
50 16 : op_data_t opd = (op_data_t) hook;
51 16 : gpgme_import_status_t import = opd->result.imports;
52 :
53 54 : while (import)
54 : {
55 22 : gpgme_import_status_t next = import->next;
56 22 : free (import->fpr);
57 22 : free (import);
58 22 : import = next;
59 : }
60 16 : }
61 :
62 :
63 : gpgme_import_result_t
64 13 : gpgme_op_import_result (gpgme_ctx_t ctx)
65 : {
66 : void *hook;
67 : op_data_t opd;
68 : gpgme_error_t err;
69 :
70 13 : TRACE_BEG (DEBUG_CTX, "gpgme_op_import_result", ctx);
71 :
72 13 : err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL);
73 13 : opd = hook;
74 13 : if (err || !opd)
75 : {
76 0 : TRACE_SUC0 ("result=(null)");
77 0 : return NULL;
78 : }
79 :
80 :
81 : if (_gpgme_debug_trace ())
82 : {
83 : gpgme_import_status_t impstat;
84 : int i;
85 :
86 13 : 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 13 : 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 13 : TRACE_LOG3 ("%i secret keys, %i imported, %i unchanged",
95 : opd->result.secret_read, opd->result.secret_imported,
96 : opd->result.secret_unchanged);
97 13 : 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 13 : impstat = opd->result.imports;
102 13 : i = 0;
103 41 : while (impstat)
104 : {
105 15 : TRACE_LOG4 ("import[%i] for %s = 0x%x (%s)",
106 : i, impstat->fpr, impstat->status, impstat->result);
107 15 : impstat = impstat->next;
108 15 : i++;
109 : }
110 : }
111 :
112 13 : TRACE_SUC1 ("result=%p", &opd->result);
113 13 : return &opd->result;
114 : }
115 :
116 :
117 : static gpgme_error_t
118 23 : 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 23 : import = malloc (sizeof (*import));
125 23 : if (!import)
126 0 : return gpg_error_from_syserror ();
127 23 : import->next = NULL;
128 :
129 23 : gpg_err_set_errno (0);
130 23 : nr = strtol (args, &tail, 0);
131 23 : if (errno || args == tail || *tail != ' ')
132 : {
133 : /* The crypto backend does not behave. */
134 0 : free (import);
135 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
136 : }
137 23 : args = tail;
138 :
139 23 : if (problem)
140 : {
141 0 : switch (nr)
142 : {
143 : case 0:
144 : case 4:
145 : default:
146 0 : import->result = gpg_error (GPG_ERR_GENERAL);
147 0 : break;
148 :
149 : case 1:
150 0 : import->result = gpg_error (GPG_ERR_BAD_CERT);
151 0 : break;
152 :
153 : case 2:
154 0 : import->result = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
155 0 : break;
156 :
157 : case 3:
158 0 : import->result = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
159 0 : break;
160 : }
161 0 : import->status = 0;
162 : }
163 : else
164 : {
165 23 : import->result = gpg_error (GPG_ERR_NO_ERROR);
166 23 : import->status = nr;
167 : }
168 :
169 69 : while (*args == ' ')
170 23 : args++;
171 23 : tail = strchr (args, ' ');
172 23 : if (tail)
173 0 : *tail = '\0';
174 :
175 23 : import->fpr = strdup (args);
176 23 : if (!import->fpr)
177 : {
178 0 : free (import);
179 0 : return gpg_error_from_syserror ();
180 : }
181 :
182 23 : *import_status = import;
183 23 : return 0;
184 : }
185 :
186 :
187 :
188 : gpgme_error_t
189 17 : parse_import_res (char *args, gpgme_import_result_t result)
190 : {
191 : char *tail;
192 :
193 17 : 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 17 : PARSE_NEXT (result->considered);
203 17 : PARSE_NEXT (result->no_user_id);
204 17 : PARSE_NEXT (result->imported);
205 17 : PARSE_NEXT (result->imported_rsa);
206 17 : PARSE_NEXT (result->unchanged);
207 17 : PARSE_NEXT (result->new_user_ids);
208 17 : PARSE_NEXT (result->new_sub_keys);
209 17 : PARSE_NEXT (result->new_signatures);
210 17 : PARSE_NEXT (result->new_revocations);
211 17 : PARSE_NEXT (result->secret_read);
212 17 : PARSE_NEXT (result->secret_imported);
213 17 : PARSE_NEXT (result->secret_unchanged);
214 17 : PARSE_NEXT (result->skipped_new_keys);
215 17 : PARSE_NEXT (result->not_imported);
216 17 : if (args && *args)
217 : {
218 15 : PARSE_NEXT (result->skipped_v3_keys);
219 : }
220 :
221 17 : return 0;
222 : }
223 :
224 :
225 : static gpgme_error_t
226 88 : import_status_handler (void *priv, gpgme_status_code_t code, char *args)
227 : {
228 88 : gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
229 : gpgme_error_t err;
230 : void *hook;
231 : op_data_t opd;
232 :
233 88 : err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL);
234 88 : opd = hook;
235 88 : if (err)
236 0 : return err;
237 :
238 88 : switch (code)
239 : {
240 : case GPGME_STATUS_IMPORT_OK:
241 : case GPGME_STATUS_IMPORT_PROBLEM:
242 23 : err = parse_import (args, opd->lastp,
243 : code == GPGME_STATUS_IMPORT_OK ? 0 : 1);
244 23 : if (err)
245 0 : return err;
246 :
247 23 : opd->lastp = &(*opd->lastp)->next;
248 23 : break;
249 :
250 : case GPGME_STATUS_IMPORT_RES:
251 17 : err = parse_import_res (args, &opd->result);
252 17 : break;
253 :
254 : default:
255 48 : break;
256 : }
257 88 : return err;
258 : }
259 :
260 :
261 : static gpgme_error_t
262 17 : _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 17 : err = _gpgme_op_reset (ctx, synchronous);
269 17 : if (err)
270 0 : return err;
271 :
272 17 : err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
273 : sizeof (*opd), release_op_data);
274 17 : opd = hook;
275 17 : if (err)
276 0 : return err;
277 17 : opd->lastp = &opd->result.imports;
278 :
279 17 : if (!keydata)
280 0 : return gpg_error (GPG_ERR_NO_DATA);
281 :
282 17 : _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
283 :
284 17 : return _gpgme_engine_op_import (ctx->engine, keydata, NULL);
285 : }
286 :
287 :
288 : gpgme_error_t
289 0 : gpgme_op_import_start (gpgme_ctx_t ctx, gpgme_data_t keydata)
290 : {
291 : gpg_error_t err;
292 :
293 0 : TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import_start", ctx,
294 : "keydata=%p", keydata);
295 :
296 0 : if (!ctx)
297 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
298 :
299 0 : err = _gpgme_op_import_start (ctx, 0, keydata);
300 0 : return TRACE_ERR (err);
301 : }
302 :
303 :
304 : /* Import the key in KEYDATA into the keyring. */
305 : gpgme_error_t
306 17 : gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata)
307 : {
308 : gpgme_error_t err;
309 :
310 17 : TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import", ctx,
311 : "keydata=%p", keydata);
312 :
313 17 : if (!ctx)
314 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
315 :
316 17 : err = _gpgme_op_import_start (ctx, 1, keydata);
317 17 : if (!err)
318 17 : err = _gpgme_wait_one (ctx);
319 17 : return TRACE_ERR (err);
320 : }
321 :
322 :
323 :
324 : static gpgme_error_t
325 0 : _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 0 : err = _gpgme_op_reset (ctx, synchronous);
334 0 : if (err)
335 0 : return err;
336 :
337 0 : err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
338 : sizeof (*opd), release_op_data);
339 0 : opd = hook;
340 0 : if (err)
341 0 : return err;
342 0 : opd->lastp = &opd->result.imports;
343 :
344 0 : if (!keys)
345 0 : return gpg_error (GPG_ERR_NO_DATA);
346 :
347 0 : for (idx=nkeys=0, firstidx=-1; keys[idx]; idx++)
348 : {
349 : /* We only consider keys of the current protocol. */
350 0 : if (keys[idx]->protocol != ctx->protocol)
351 0 : continue;
352 0 : if (firstidx == -1)
353 0 : 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 0 : if (keys[idx]->keylist_mode != keys[firstidx]->keylist_mode)
359 0 : return gpg_error (GPG_ERR_CONFLICT);
360 0 : nkeys++;
361 : }
362 0 : if (!nkeys)
363 0 : return gpg_error (GPG_ERR_NO_DATA);
364 :
365 0 : _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
366 :
367 0 : 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 0 : gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t *keys)
374 : {
375 : gpg_error_t err;
376 :
377 0 : TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys_start", ctx);
378 :
379 0 : if (!ctx)
380 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
381 :
382 0 : if (_gpgme_debug_trace () && keys)
383 : {
384 0 : int i = 0;
385 :
386 0 : while (keys[i])
387 : {
388 0 : TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
389 : (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
390 : keys[i]->subkeys->fpr : "invalid");
391 0 : i++;
392 : }
393 : }
394 :
395 0 : err = _gpgme_op_import_keys_start (ctx, 0, keys);
396 0 : 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 0 : gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t *keys)
412 : {
413 : gpgme_error_t err;
414 :
415 0 : TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys", ctx);
416 :
417 0 : if (!ctx)
418 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
419 :
420 0 : if (_gpgme_debug_trace () && keys)
421 : {
422 0 : int i = 0;
423 :
424 0 : while (keys[i])
425 : {
426 0 : TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
427 : (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
428 : keys[i]->subkeys->fpr : "invalid");
429 0 : i++;
430 : }
431 : }
432 :
433 0 : err = _gpgme_op_import_keys_start (ctx, 1, keys);
434 0 : if (!err)
435 0 : err = _gpgme_wait_one (ctx);
436 0 : return TRACE_ERR (err);
437 : }
438 :
439 :
440 : /* Deprecated interface. */
441 : gpgme_error_t
442 0 : gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr)
443 : {
444 0 : gpgme_error_t err = gpgme_op_import (ctx, keydata);
445 0 : if (!err && nr)
446 : {
447 0 : gpgme_import_result_t result = gpgme_op_import_result (ctx);
448 0 : *nr = result->considered;
449 : }
450 0 : return err;
451 : }
|