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