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