Line data Source code
1 : /* export.c - Export a key.
2 : Copyright (C) 2000 Werner Koch (dd9jn)
3 : Copyright (C) 2001-2004, 2010, 2014 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 <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #if HAVE_CONFIG_H
22 : #include <config.h>
23 : #endif
24 : #include <stdlib.h>
25 : #include <string.h>
26 :
27 : #include "gpgme.h"
28 : #include "util.h"
29 : #include "debug.h"
30 : #include "context.h"
31 : #include "ops.h"
32 :
33 :
34 : /* Local operation data. */
35 : typedef struct
36 : {
37 : gpg_error_t err; /* Error encountred during the export. */
38 : } *op_data_t;
39 :
40 :
41 : static void
42 6 : release_op_data (void *hook)
43 : {
44 6 : op_data_t opd = (op_data_t) hook;
45 :
46 : (void)opd; /* Nothing to release here. */
47 6 : }
48 :
49 :
50 : /* Parse an error status line. Return the error location and the
51 : error code. The function may modify ARGS. */
52 : static char *
53 0 : parse_error (char *args, gpg_error_t *r_err)
54 : {
55 0 : char *where = strchr (args, ' ');
56 : char *which;
57 :
58 0 : if (where)
59 : {
60 0 : *where = '\0';
61 0 : which = where + 1;
62 :
63 0 : where = strchr (which, ' ');
64 0 : if (where)
65 0 : *where = '\0';
66 :
67 0 : where = args;
68 : }
69 : else
70 : {
71 0 : *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE);
72 0 : return NULL;
73 : }
74 :
75 0 : *r_err = atoi (which);
76 :
77 0 : return where;
78 : }
79 :
80 :
81 : static gpgme_error_t
82 18 : export_status_handler (void *priv, gpgme_status_code_t code, char *args)
83 : {
84 18 : gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
85 : gpgme_error_t err;
86 : void *hook;
87 : op_data_t opd;
88 : const char *loc;
89 :
90 18 : err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, -1, NULL);
91 18 : opd = hook;
92 18 : if (err)
93 0 : return err;
94 :
95 18 : switch (code)
96 : {
97 : case GPGME_STATUS_ERROR:
98 0 : loc = parse_error (args, &err);
99 0 : if (!loc)
100 0 : return err;
101 0 : else if (opd->err)
102 : ; /* We only want to report the first error. */
103 0 : else if (!strcmp (loc, "keyserver_send"))
104 0 : opd->err = err;
105 0 : break;
106 :
107 : default:
108 18 : break;
109 : }
110 18 : return 0;
111 : }
112 :
113 :
114 : static gpgme_error_t
115 0 : export_start (gpgme_ctx_t ctx, int synchronous, const char *pattern,
116 : gpgme_export_mode_t mode, gpgme_data_t keydata)
117 : {
118 : gpgme_error_t err;
119 : void *hook;
120 : op_data_t opd;
121 :
122 0 : if ((mode & ~(GPGME_EXPORT_MODE_EXTERN
123 : |GPGME_EXPORT_MODE_MINIMAL
124 : |GPGME_EXPORT_MODE_SECRET
125 : |GPGME_EXPORT_MODE_RAW
126 : |GPGME_EXPORT_MODE_PKCS12)))
127 0 : return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */
128 :
129 0 : if ((mode & GPGME_EXPORT_MODE_SECRET))
130 : {
131 0 : if ((mode & GPGME_EXPORT_MODE_EXTERN))
132 0 : return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */
133 0 : if ((mode & GPGME_EXPORT_MODE_RAW)
134 0 : && (mode & GPGME_EXPORT_MODE_PKCS12))
135 0 : return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */
136 :
137 0 : if (ctx->protocol != GPGME_PROTOCOL_CMS
138 0 : && (mode & (GPGME_EXPORT_MODE_RAW|GPGME_EXPORT_MODE_PKCS12)))
139 0 : return gpg_error (GPG_ERR_INV_FLAG); /* Only supported for X.509. */
140 : }
141 :
142 0 : if ((mode & GPGME_EXPORT_MODE_EXTERN))
143 : {
144 0 : if (keydata)
145 0 : return gpg_error (GPG_ERR_INV_VALUE);
146 : }
147 : else
148 : {
149 0 : if (!keydata)
150 0 : return gpg_error (GPG_ERR_INV_VALUE);
151 : }
152 :
153 0 : err = _gpgme_op_reset (ctx, synchronous);
154 0 : if (err)
155 0 : return err;
156 :
157 0 : err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook,
158 : sizeof (*opd), release_op_data);
159 0 : opd = hook;
160 0 : if (err)
161 0 : return err;
162 :
163 0 : _gpgme_engine_set_status_handler (ctx->engine, export_status_handler, ctx);
164 :
165 0 : return _gpgme_engine_op_export (ctx->engine, pattern, mode, keydata,
166 0 : ctx->use_armor);
167 : }
168 :
169 :
170 : /* Export the keys listed in PATTERN into KEYDATA. */
171 : gpgme_error_t
172 0 : gpgme_op_export_start (gpgme_ctx_t ctx, const char *pattern,
173 : gpgme_export_mode_t mode, gpgme_data_t keydata)
174 : {
175 : gpgme_error_t err;
176 :
177 0 : TRACE_BEG3 (DEBUG_CTX, "gpgme_op_export_start", ctx,
178 : "pattern=%s, mode=0x%x, keydata=%p", pattern, mode, keydata);
179 :
180 0 : if (!ctx)
181 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
182 :
183 0 : err = export_start (ctx, 0, pattern, mode, keydata);
184 0 : return TRACE_ERR (err);
185 : }
186 :
187 :
188 : /* Export the keys listed in PATTERN into KEYDATA. */
189 : gpgme_error_t
190 0 : gpgme_op_export (gpgme_ctx_t ctx, const char *pattern,
191 : gpgme_export_mode_t mode, gpgme_data_t keydata)
192 : {
193 : gpgme_error_t err;
194 :
195 0 : TRACE_BEG3 (DEBUG_CTX, "gpgme_op_export", ctx,
196 : "pattern=%s, mode=0x%x, keydata=%p", pattern, mode, keydata);
197 :
198 0 : if (!ctx)
199 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
200 :
201 0 : err = export_start (ctx, 1, pattern, mode, keydata);
202 0 : if (!err)
203 0 : err = _gpgme_wait_one (ctx);
204 0 : return err;
205 : }
206 :
207 :
208 : static gpgme_error_t
209 6 : export_ext_start (gpgme_ctx_t ctx, int synchronous, const char *pattern[],
210 : gpgme_export_mode_t mode, gpgme_data_t keydata)
211 : {
212 : gpgme_error_t err;
213 : void *hook;
214 : op_data_t opd;
215 :
216 6 : if ((mode & ~(GPGME_EXPORT_MODE_EXTERN
217 : |GPGME_EXPORT_MODE_MINIMAL
218 : |GPGME_EXPORT_MODE_SECRET
219 : |GPGME_EXPORT_MODE_RAW
220 : |GPGME_EXPORT_MODE_PKCS12)))
221 0 : return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */
222 :
223 6 : if ((mode & GPGME_EXPORT_MODE_SECRET))
224 : {
225 0 : if ((mode & GPGME_EXPORT_MODE_EXTERN))
226 0 : return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */
227 0 : if ((mode & GPGME_EXPORT_MODE_RAW)
228 0 : && (mode & GPGME_EXPORT_MODE_PKCS12))
229 0 : return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */
230 :
231 0 : if (ctx->protocol != GPGME_PROTOCOL_CMS
232 0 : && (mode & (GPGME_EXPORT_MODE_RAW|GPGME_EXPORT_MODE_PKCS12)))
233 0 : return gpg_error (GPG_ERR_INV_FLAG); /* Only supported for X.509. */
234 : }
235 :
236 6 : if ((mode & GPGME_EXPORT_MODE_EXTERN))
237 : {
238 0 : if (keydata)
239 0 : return gpg_error (GPG_ERR_INV_VALUE);
240 : }
241 : else
242 : {
243 6 : if (!keydata)
244 0 : return gpg_error (GPG_ERR_INV_VALUE);
245 : }
246 :
247 6 : err = _gpgme_op_reset (ctx, synchronous);
248 6 : if (err)
249 0 : return err;
250 :
251 6 : err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook,
252 : sizeof (*opd), release_op_data);
253 6 : opd = hook;
254 6 : if (err)
255 0 : return err;
256 :
257 6 : _gpgme_engine_set_status_handler (ctx->engine, export_status_handler, ctx);
258 :
259 6 : return _gpgme_engine_op_export_ext (ctx->engine, pattern, mode, keydata,
260 6 : ctx->use_armor);
261 : }
262 :
263 :
264 : /* Export the keys listed in PATTERN into KEYDATA. */
265 : gpgme_error_t
266 0 : gpgme_op_export_ext_start (gpgme_ctx_t ctx, const char *pattern[],
267 : gpgme_export_mode_t mode, gpgme_data_t keydata)
268 : {
269 : gpgme_error_t err;
270 :
271 0 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_ext_start", ctx,
272 : "mode=0x%x, keydata=%p", mode, keydata);
273 :
274 0 : if (!ctx)
275 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
276 :
277 0 : if (_gpgme_debug_trace () && pattern)
278 : {
279 0 : int i = 0;
280 :
281 0 : while (pattern[i])
282 : {
283 0 : TRACE_LOG2 ("pattern[%i] = %s", i, pattern[i]);
284 0 : i++;
285 : }
286 : }
287 :
288 0 : err = export_ext_start (ctx, 0, pattern, mode, keydata);
289 0 : return TRACE_ERR (err);
290 : }
291 :
292 :
293 : /* Export the keys listed in PATTERN into KEYDATA. */
294 : gpgme_error_t
295 4 : gpgme_op_export_ext (gpgme_ctx_t ctx, const char *pattern[],
296 : gpgme_export_mode_t mode, gpgme_data_t keydata)
297 : {
298 : gpgme_error_t err;
299 :
300 4 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_ext_start", ctx,
301 : "mode=0x%x, keydata=%p", mode, keydata);
302 :
303 4 : if (!ctx)
304 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
305 :
306 4 : if (_gpgme_debug_trace () && pattern)
307 : {
308 4 : int i = 0;
309 :
310 15 : while (pattern[i])
311 : {
312 7 : TRACE_LOG2 ("pattern[%i] = %s", i, pattern[i]);
313 7 : i++;
314 : }
315 : }
316 :
317 4 : err = export_ext_start (ctx, 1, pattern, mode, keydata);
318 4 : if (!err)
319 : {
320 4 : err = _gpgme_wait_one (ctx);
321 4 : if (!err)
322 : {
323 : /* For this synchronous operation we check for operational
324 : errors and return them. For asynchronous operations
325 : there is currently no way to do this - we need to add a
326 : gpgme_op_export_result function to fix that. */
327 : void *hook;
328 : op_data_t opd;
329 :
330 4 : err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, -1, NULL);
331 4 : opd = hook;
332 4 : if (!err)
333 4 : err = opd->err;
334 : }
335 : }
336 :
337 4 : return TRACE_ERR (err);
338 : }
339 :
340 :
341 :
342 :
343 :
344 : static gpgme_error_t
345 2 : export_keys_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t keys[],
346 : gpgme_export_mode_t mode, gpgme_data_t keydata)
347 : {
348 : gpgme_error_t err;
349 : int nkeys, idx;
350 : char **pattern;
351 :
352 2 : if (!keys)
353 0 : return gpg_error (GPG_ERR_INV_VALUE);
354 :
355 : /* Create a list of pattern from the keys. */
356 6 : for (idx=nkeys=0; keys[idx]; idx++)
357 4 : if (keys[idx]->protocol == ctx->protocol)
358 4 : nkeys++;
359 2 : if (!nkeys)
360 0 : return gpg_error (GPG_ERR_NO_DATA);
361 :
362 2 : pattern = calloc (nkeys+1, sizeof *pattern);
363 2 : if (!pattern)
364 0 : return gpg_error_from_syserror ();
365 :
366 6 : for (idx=nkeys=0; keys[idx]; idx++)
367 4 : if (keys[idx]->protocol == ctx->protocol
368 4 : && keys[idx]->subkeys
369 4 : && keys[idx]->subkeys->fpr
370 4 : && *keys[idx]->subkeys->fpr)
371 : {
372 4 : pattern[nkeys] = strdup (keys[idx]->subkeys->fpr);
373 4 : if (!pattern[nkeys])
374 : {
375 0 : err = gpg_error_from_syserror ();
376 0 : goto leave;
377 : }
378 4 : nkeys++;
379 : }
380 :
381 :
382 : /* Pass on to the regular function. */
383 2 : err = export_ext_start (ctx, synchronous, (const char**)pattern,
384 : mode, keydata);
385 :
386 : leave:
387 6 : for (idx=0; pattern[idx]; idx++)
388 4 : free (pattern[idx]);
389 2 : free (pattern);
390 :
391 2 : return err;
392 : }
393 :
394 :
395 : /* Export the keys from the array KEYS into KEYDATA. Only keys of the
396 : current protocol are exported and only those which have a
397 : fingerprint set; that is keys received with some external search
398 : methods are silently skipped. */
399 : gpgme_error_t
400 0 : gpgme_op_export_keys_start (gpgme_ctx_t ctx,
401 : gpgme_key_t keys[],
402 : gpgme_export_mode_t mode,
403 : gpgme_data_t keydata)
404 : {
405 : gpg_error_t err;
406 :
407 0 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_keys_start", ctx,
408 : "mode=0x%x, keydata=%p", mode, keydata);
409 :
410 0 : if (!ctx)
411 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
412 :
413 0 : if (_gpgme_debug_trace () && keys)
414 : {
415 0 : int i = 0;
416 :
417 0 : while (keys[i])
418 : {
419 0 : TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
420 : (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
421 : keys[i]->subkeys->fpr : "invalid");
422 0 : i++;
423 : }
424 : }
425 :
426 0 : err = export_keys_start (ctx, 0, keys, mode, keydata);
427 0 : return TRACE_ERR (err);
428 : }
429 :
430 : gpgme_error_t
431 2 : gpgme_op_export_keys (gpgme_ctx_t ctx,
432 : gpgme_key_t keys[],
433 : gpgme_export_mode_t mode,
434 : gpgme_data_t keydata)
435 : {
436 : gpgme_error_t err;
437 :
438 2 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_keys", ctx,
439 : "mode=0x%x, keydata=%p", mode, keydata);
440 :
441 2 : if (!ctx)
442 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
443 :
444 2 : if (_gpgme_debug_trace () && keys)
445 : {
446 2 : int i = 0;
447 :
448 8 : while (keys[i])
449 : {
450 4 : TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
451 : (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
452 : keys[i]->subkeys->fpr : "invalid");
453 4 : i++;
454 : }
455 : }
456 :
457 2 : err = export_keys_start (ctx, 1, keys, mode, keydata);
458 2 : if (!err)
459 : {
460 2 : err = _gpgme_wait_one (ctx);
461 2 : if (!err)
462 : {
463 : /* For this synchronous operation we check for operational
464 : errors and return them. For asynchronous operations
465 : there is currently no way to do this - we need to add a
466 : gpgme_op_export_result function to fix that. */
467 : void *hook;
468 : op_data_t opd;
469 :
470 2 : err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, -1, NULL);
471 2 : opd = hook;
472 2 : if (!err)
473 2 : err = opd->err;
474 : }
475 : }
476 :
477 2 : return TRACE_ERR (err);
478 : }
479 :
|