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 <https://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 encountered during the export. */
38 : } *op_data_t;
39 :
40 :
41 : static void
42 10 : release_op_data (void *hook)
43 : {
44 10 : op_data_t opd = (op_data_t) hook;
45 :
46 : (void)opd; /* Nothing to release here. */
47 10 : }
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 88 : export_status_handler (void *priv, gpgme_status_code_t code, char *args)
83 : {
84 88 : 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 88 : err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, -1, NULL);
91 88 : opd = hook;
92 88 : if (err)
93 0 : return err;
94 :
95 88 : 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 88 : break;
109 : }
110 88 : 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_NOUID
127 : |GPGME_EXPORT_MODE_PKCS12)))
128 0 : return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */
129 :
130 0 : if ((mode & GPGME_EXPORT_MODE_SECRET))
131 : {
132 0 : if ((mode & GPGME_EXPORT_MODE_EXTERN))
133 0 : return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */
134 0 : if ((mode & GPGME_EXPORT_MODE_RAW)
135 0 : && (mode & GPGME_EXPORT_MODE_PKCS12))
136 0 : return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */
137 :
138 0 : if (ctx->protocol != GPGME_PROTOCOL_CMS
139 0 : && (mode & (GPGME_EXPORT_MODE_RAW|GPGME_EXPORT_MODE_PKCS12)))
140 0 : return gpg_error (GPG_ERR_INV_FLAG); /* Only supported for X.509. */
141 : }
142 :
143 0 : if ((mode & GPGME_EXPORT_MODE_EXTERN))
144 : {
145 0 : if (keydata)
146 0 : return gpg_error (GPG_ERR_INV_VALUE);
147 : }
148 : else
149 : {
150 0 : if (!keydata)
151 0 : return gpg_error (GPG_ERR_INV_VALUE);
152 : }
153 :
154 0 : err = _gpgme_op_reset (ctx, synchronous);
155 0 : if (err)
156 0 : return err;
157 :
158 0 : err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook,
159 : sizeof (*opd), release_op_data);
160 0 : opd = hook;
161 0 : if (err)
162 0 : return err;
163 :
164 0 : _gpgme_engine_set_status_handler (ctx->engine, export_status_handler, ctx);
165 :
166 0 : return _gpgme_engine_op_export (ctx->engine, pattern, mode, keydata,
167 0 : ctx->use_armor);
168 : }
169 :
170 :
171 : /* Export the keys listed in PATTERN into KEYDATA. */
172 : gpgme_error_t
173 0 : gpgme_op_export_start (gpgme_ctx_t ctx, const char *pattern,
174 : gpgme_export_mode_t mode, gpgme_data_t keydata)
175 : {
176 : gpgme_error_t err;
177 :
178 0 : TRACE_BEG3 (DEBUG_CTX, "gpgme_op_export_start", ctx,
179 : "pattern=%s, mode=0x%x, keydata=%p", pattern, mode, keydata);
180 :
181 0 : if (!ctx)
182 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
183 :
184 0 : err = export_start (ctx, 0, pattern, mode, keydata);
185 0 : return TRACE_ERR (err);
186 : }
187 :
188 :
189 : /* Export the keys listed in PATTERN into KEYDATA. */
190 : gpgme_error_t
191 0 : gpgme_op_export (gpgme_ctx_t ctx, const char *pattern,
192 : gpgme_export_mode_t mode, gpgme_data_t keydata)
193 : {
194 : gpgme_error_t err;
195 :
196 0 : TRACE_BEG3 (DEBUG_CTX, "gpgme_op_export", ctx,
197 : "pattern=%s, mode=0x%x, keydata=%p", pattern, mode, keydata);
198 :
199 0 : if (!ctx)
200 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
201 :
202 0 : err = export_start (ctx, 1, pattern, mode, keydata);
203 0 : if (!err)
204 0 : err = _gpgme_wait_one (ctx);
205 0 : return err;
206 : }
207 :
208 :
209 : static gpgme_error_t
210 12 : export_ext_start (gpgme_ctx_t ctx, int synchronous, const char *pattern[],
211 : gpgme_export_mode_t mode, gpgme_data_t keydata)
212 : {
213 : gpgme_error_t err;
214 : void *hook;
215 : op_data_t opd;
216 :
217 12 : if ((mode & ~(GPGME_EXPORT_MODE_EXTERN
218 : |GPGME_EXPORT_MODE_MINIMAL
219 : |GPGME_EXPORT_MODE_SECRET
220 : |GPGME_EXPORT_MODE_RAW
221 : |GPGME_EXPORT_MODE_PKCS12)))
222 0 : return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */
223 :
224 12 : if ((mode & GPGME_EXPORT_MODE_SECRET))
225 : {
226 0 : if ((mode & GPGME_EXPORT_MODE_EXTERN))
227 0 : return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */
228 0 : if ((mode & GPGME_EXPORT_MODE_RAW)
229 0 : && (mode & GPGME_EXPORT_MODE_PKCS12))
230 0 : return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */
231 :
232 0 : if (ctx->protocol != GPGME_PROTOCOL_CMS
233 0 : && (mode & (GPGME_EXPORT_MODE_RAW|GPGME_EXPORT_MODE_PKCS12)))
234 0 : return gpg_error (GPG_ERR_INV_FLAG); /* Only supported for X.509. */
235 : }
236 :
237 12 : if ((mode & GPGME_EXPORT_MODE_EXTERN))
238 : {
239 0 : if (keydata)
240 0 : return gpg_error (GPG_ERR_INV_VALUE);
241 : }
242 : else
243 : {
244 12 : if (!keydata)
245 0 : return gpg_error (GPG_ERR_INV_VALUE);
246 : }
247 :
248 12 : err = _gpgme_op_reset (ctx, synchronous);
249 12 : if (err)
250 0 : return err;
251 :
252 12 : err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook,
253 : sizeof (*opd), release_op_data);
254 12 : opd = hook;
255 12 : if (err)
256 0 : return err;
257 :
258 12 : _gpgme_engine_set_status_handler (ctx->engine, export_status_handler, ctx);
259 :
260 12 : return _gpgme_engine_op_export_ext (ctx->engine, pattern, mode, keydata,
261 12 : ctx->use_armor);
262 : }
263 :
264 :
265 : /* Export the keys listed in PATTERN into KEYDATA. */
266 : gpgme_error_t
267 0 : gpgme_op_export_ext_start (gpgme_ctx_t ctx, const char *pattern[],
268 : gpgme_export_mode_t mode, gpgme_data_t keydata)
269 : {
270 : gpgme_error_t err;
271 :
272 0 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_ext_start", ctx,
273 : "mode=0x%x, keydata=%p", mode, keydata);
274 :
275 0 : if (!ctx)
276 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
277 :
278 0 : if (_gpgme_debug_trace () && pattern)
279 : {
280 0 : int i = 0;
281 :
282 0 : while (pattern[i])
283 : {
284 0 : TRACE_LOG2 ("pattern[%i] = %s", i, pattern[i]);
285 0 : i++;
286 : }
287 : }
288 :
289 0 : err = export_ext_start (ctx, 0, pattern, mode, keydata);
290 0 : return TRACE_ERR (err);
291 : }
292 :
293 :
294 : /* Export the keys listed in PATTERN into KEYDATA. */
295 : gpgme_error_t
296 7 : gpgme_op_export_ext (gpgme_ctx_t ctx, const char *pattern[],
297 : gpgme_export_mode_t mode, gpgme_data_t keydata)
298 : {
299 : gpgme_error_t err;
300 :
301 7 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_ext_start", ctx,
302 : "mode=0x%x, keydata=%p", mode, keydata);
303 :
304 7 : if (!ctx)
305 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
306 :
307 7 : if (_gpgme_debug_trace () && pattern)
308 : {
309 7 : int i = 0;
310 :
311 25 : while (pattern[i])
312 : {
313 11 : TRACE_LOG2 ("pattern[%i] = %s", i, pattern[i]);
314 11 : i++;
315 : }
316 : }
317 :
318 7 : err = export_ext_start (ctx, 1, pattern, mode, keydata);
319 7 : if (!err)
320 : {
321 7 : err = _gpgme_wait_one (ctx);
322 7 : if (!err)
323 : {
324 : /* For this synchronous operation we check for operational
325 : errors and return them. For asynchronous operations
326 : there is currently no way to do this - we need to add a
327 : gpgme_op_export_result function to fix that. */
328 : void *hook;
329 : op_data_t opd;
330 :
331 7 : err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, -1, NULL);
332 7 : opd = hook;
333 7 : if (!err)
334 7 : err = opd->err;
335 : }
336 : }
337 :
338 7 : return TRACE_ERR (err);
339 : }
340 :
341 :
342 :
343 :
344 :
345 : static gpgme_error_t
346 5 : export_keys_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t keys[],
347 : gpgme_export_mode_t mode, gpgme_data_t keydata)
348 : {
349 : gpgme_error_t err;
350 : int nkeys, idx;
351 : char **pattern;
352 :
353 5 : if (!keys)
354 0 : return gpg_error (GPG_ERR_INV_VALUE);
355 :
356 : /* Create a list of pattern from the keys. */
357 63 : for (idx=nkeys=0; keys[idx]; idx++)
358 58 : if (keys[idx]->protocol == ctx->protocol)
359 58 : nkeys++;
360 5 : if (!nkeys)
361 0 : return gpg_error (GPG_ERR_NO_DATA);
362 :
363 5 : pattern = calloc (nkeys+1, sizeof *pattern);
364 5 : if (!pattern)
365 0 : return gpg_error_from_syserror ();
366 :
367 63 : for (idx=nkeys=0; keys[idx]; idx++)
368 58 : if (keys[idx]->protocol == ctx->protocol
369 58 : && keys[idx]->subkeys
370 58 : && keys[idx]->subkeys->fpr
371 58 : && *keys[idx]->subkeys->fpr)
372 : {
373 58 : pattern[nkeys] = strdup (keys[idx]->subkeys->fpr);
374 58 : if (!pattern[nkeys])
375 : {
376 0 : err = gpg_error_from_syserror ();
377 0 : goto leave;
378 : }
379 58 : nkeys++;
380 : }
381 :
382 :
383 : /* Pass on to the regular function. */
384 5 : err = export_ext_start (ctx, synchronous, (const char**)pattern,
385 : mode, keydata);
386 :
387 : leave:
388 63 : for (idx=0; pattern[idx]; idx++)
389 58 : free (pattern[idx]);
390 5 : free (pattern);
391 :
392 5 : return err;
393 : }
394 :
395 :
396 : /* Export the keys from the array KEYS into KEYDATA. Only keys of the
397 : current protocol are exported and only those which have a
398 : fingerprint set; that is keys received with some external search
399 : methods are silently skipped. */
400 : gpgme_error_t
401 0 : gpgme_op_export_keys_start (gpgme_ctx_t ctx,
402 : gpgme_key_t keys[],
403 : gpgme_export_mode_t mode,
404 : gpgme_data_t keydata)
405 : {
406 : gpg_error_t err;
407 :
408 0 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_keys_start", ctx,
409 : "mode=0x%x, keydata=%p", mode, keydata);
410 :
411 0 : if (!ctx)
412 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
413 :
414 0 : if (_gpgme_debug_trace () && keys)
415 : {
416 0 : int i = 0;
417 :
418 0 : while (keys[i])
419 : {
420 0 : TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
421 : (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
422 : keys[i]->subkeys->fpr : "invalid");
423 0 : i++;
424 : }
425 : }
426 :
427 0 : err = export_keys_start (ctx, 0, keys, mode, keydata);
428 0 : return TRACE_ERR (err);
429 : }
430 :
431 : gpgme_error_t
432 5 : gpgme_op_export_keys (gpgme_ctx_t ctx,
433 : gpgme_key_t keys[],
434 : gpgme_export_mode_t mode,
435 : gpgme_data_t keydata)
436 : {
437 : gpgme_error_t err;
438 :
439 5 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_keys", ctx,
440 : "mode=0x%x, keydata=%p", mode, keydata);
441 :
442 5 : if (!ctx)
443 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
444 :
445 5 : if (_gpgme_debug_trace () && keys)
446 : {
447 5 : int i = 0;
448 :
449 68 : while (keys[i])
450 : {
451 58 : TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
452 : (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
453 : keys[i]->subkeys->fpr : "invalid");
454 58 : i++;
455 : }
456 : }
457 :
458 5 : err = export_keys_start (ctx, 1, keys, mode, keydata);
459 5 : if (!err)
460 : {
461 5 : err = _gpgme_wait_one (ctx);
462 5 : if (!err)
463 : {
464 : /* For this synchronous operation we check for operational
465 : errors and return them. For asynchronous operations
466 : there is currently no way to do this - we need to add a
467 : gpgme_op_export_result function to fix that. */
468 : void *hook;
469 : op_data_t opd;
470 :
471 5 : err = _gpgme_op_data_lookup (ctx, OPDATA_EXPORT, &hook, -1, NULL);
472 5 : opd = hook;
473 5 : if (!err)
474 5 : err = opd->err;
475 : }
476 : }
477 :
478 5 : return TRACE_ERR (err);
479 : }
480 :
|