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