core: Do not crash if CMS plaintext is ignored
[gpgme.git] / src / genkey.c
1 /* genkey.c - Key generation.
2  * Copyright (C) 2000 Werner Koch (dd9jn)
3  * Copyright (C) 2001, 2002, 2003, 2004, 2016 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 #include <errno.h>
27
28 #include "gpgme.h"
29 #include "debug.h"
30 #include "context.h"
31 #include "ops.h"
32 #include "util.h"
33
34 \f
35 typedef struct
36 {
37   struct _gpgme_op_genkey_result result;
38
39   /* The error code from a FAILURE status line or 0.  */
40   gpg_error_t failure_code;
41
42   /* The error code from certain ERROR status lines or 0.  */
43   gpg_error_t error_code;
44
45   /* Flag to indicate that a UID is to be added.  */
46   gpg_error_t uidmode;
47
48   /* The key parameters passed to the crypto engine.  */
49   gpgme_data_t key_parameter;
50 } *op_data_t;
51
52
53 static void
54 release_op_data (void *hook)
55 {
56   op_data_t opd = (op_data_t) hook;
57
58   if (opd->result.fpr)
59     free (opd->result.fpr);
60   if (opd->key_parameter)
61     gpgme_data_release (opd->key_parameter);
62 }
63
64
65 gpgme_genkey_result_t
66 gpgme_op_genkey_result (gpgme_ctx_t ctx)
67 {
68   void *hook;
69   op_data_t opd;
70   gpgme_error_t err;
71
72   TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey_result", ctx);
73
74   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL);
75   opd = hook;
76   if (err || !opd)
77     {
78       TRACE_SUC0 ("result=(null)");
79       return NULL;
80     }
81
82   TRACE_LOG3 ("fpr = %s, %s, %s", opd->result.fpr,
83               opd->result.primary ? "primary" : "no primary",
84               opd->result.sub ? "sub" : "no sub");
85
86   TRACE_SUC1 ("result=%p", &opd->result);
87   return &opd->result;
88 }
89
90
91 \f
92 /* Parse an error status line.  Return the error location and the
93    error code.  The function may modify ARGS. */
94 static char *
95 parse_error (char *args, gpg_error_t *r_err)
96 {
97   char *where = strchr (args, ' ');
98   char *which;
99
100   if (where)
101     {
102       *where = '\0';
103       which = where + 1;
104
105       where = strchr (which, ' ');
106       if (where)
107         *where = '\0';
108
109       where = args;
110     }
111   else
112     {
113       *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE);
114       return NULL;
115     }
116
117   *r_err = atoi (which);
118
119   return where;
120 }
121
122
123 static gpgme_error_t
124 genkey_status_handler (void *priv, gpgme_status_code_t code, char *args)
125 {
126   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
127   gpgme_error_t err;
128   void *hook;
129   op_data_t opd;
130   char *loc;
131
132   /* Pipe the status code through the progress status handler.  */
133   err = _gpgme_progress_status_handler (ctx, code, args);
134   if (err)
135     return err;
136
137   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL);
138   opd = hook;
139   if (err)
140     return err;
141
142   switch (code)
143     {
144     case GPGME_STATUS_KEY_CREATED:
145       if (args && *args)
146         {
147           if (*args == 'B' || *args == 'P')
148             {
149               opd->result.primary = 1;
150               opd->result.uid = 1;
151             }
152           if (*args == 'B' || *args == 'S')
153             opd->result.sub = 1;
154           if (args[1] == ' ')
155             {
156               if (opd->result.fpr)
157                 free (opd->result.fpr);
158               opd->result.fpr = strdup (&args[2]);
159               if (!opd->result.fpr)
160                 return gpg_error_from_syserror ();
161             }
162         }
163       break;
164
165     case GPGME_STATUS_ERROR:
166       loc = parse_error (args, &err);
167       if (!loc)
168         return err;
169       if (!opd->error_code)
170         opd->error_code = err;
171       break;
172
173     case GPGME_STATUS_FAILURE:
174       opd->failure_code = _gpgme_parse_failure (args);
175       break;
176
177     case GPGME_STATUS_EOF:
178       if (opd->error_code)
179         return opd->error_code;
180       else if (!opd->uidmode && !opd->result.primary && !opd->result.sub)
181         return gpg_error (GPG_ERR_GENERAL);
182       else if (opd->failure_code)
183         return opd->failure_code;
184       else if (opd->uidmode == 1)
185         opd->result.uid = 1;  /* We have no status line, thus this hack.  */
186       break;
187
188     case GPGME_STATUS_INQUIRE_MAXLEN:
189       if (ctx->status_cb && !ctx->full_status)
190         {
191           err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args);
192           if (err)
193             return err;
194         }
195       break;
196
197     default:
198       break;
199     }
200   return 0;
201 }
202
203
204 static gpgme_error_t
205 get_key_parameter (const char *parms, gpgme_data_t *key_parameter)
206 {
207   const char *content;
208   const char *attrib;
209   const char *endtag;
210
211   /* Extract the key parameter from the XML structure.  */
212   parms = strstr (parms, "<GnupgKeyParms ");
213   if (!parms)
214     return gpg_error (GPG_ERR_INV_VALUE);
215
216   content = strchr (parms, '>');
217   if (!content)
218     return gpg_error (GPG_ERR_INV_VALUE);
219   content++;
220
221   attrib = strstr (parms, "format=\"internal\"");
222   if (!attrib || attrib >= content)
223     return gpg_error (GPG_ERR_INV_VALUE);
224
225   endtag = strstr (content, "</GnupgKeyParms>");
226   if (!endtag)
227     endtag = content + strlen (content);
228
229   /* FIXME: Check that there are no control statements inside.  */
230   while (content < endtag
231          && (content[0] == '\n'
232              || (content[0] == '\r' && content[1] == '\n')))
233     content++;
234
235   return gpgme_data_new_from_mem (key_parameter, content,
236                                   endtag - content, 1);
237 }
238
239
240 static gpgme_error_t
241 genkey_start (gpgme_ctx_t ctx, int synchronous, const char *parms,
242               gpgme_data_t pubkey, gpgme_data_t seckey)
243 {
244   gpgme_error_t err;
245   void *hook;
246   op_data_t opd;
247   err = _gpgme_op_reset (ctx, synchronous);
248   if (err)
249     return err;
250
251   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
252                                sizeof (*opd), release_op_data);
253   opd = hook;
254   if (err)
255     return err;
256
257   err = get_key_parameter (parms, &opd->key_parameter);
258   if (err)
259     return err;
260
261   _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
262
263   if (ctx->passphrase_cb)
264     {
265       err = _gpgme_engine_set_command_handler
266         (ctx->engine, _gpgme_passphrase_command_handler, ctx);
267       if (err)
268         return err;
269     }
270
271   return _gpgme_engine_op_genkey (ctx->engine,
272                                   NULL, NULL, 0, 0, NULL, 0,
273                                   opd->key_parameter,
274                                   ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0,
275                                   pubkey, seckey);
276 }
277
278
279 /* Generate a new keypair and add it to the keyring.  PUBKEY and
280    SECKEY should be null for now.  PARMS specifies what keys should be
281    generated.  */
282 gpgme_error_t
283 gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms,
284                        gpgme_data_t pubkey, gpgme_data_t seckey)
285 {
286   gpgme_error_t err;
287
288   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey_start", ctx,
289               "pubkey=%p, seckey=%p", pubkey, seckey);
290   TRACE_LOGBUF (parms, parms? strlen (parms):0);
291
292   if (!ctx || parms)
293     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
294
295   err = genkey_start (ctx, 0, parms, pubkey, seckey);
296   return TRACE_ERR (err);
297 }
298
299
300 /* Generate a new keypair and add it to the keyring.  PUBKEY and
301    SECKEY should be null for now.  PARMS specifies what keys should be
302    generated.  */
303 gpgme_error_t
304 gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey,
305                  gpgme_data_t seckey)
306 {
307   gpgme_error_t err;
308
309   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey", ctx,
310               "pubkey=%p, seckey=%p", pubkey, seckey);
311   TRACE_LOGBUF (parms, parms? strlen (parms):0);
312
313   if (!ctx)
314     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
315
316   err = genkey_start (ctx, 1, parms, pubkey, seckey);
317   if (!err)
318     err = _gpgme_wait_one (ctx);
319   return TRACE_ERR (err);
320 }
321
322
323 \f
324 static gpgme_error_t
325 createkey_start (gpgme_ctx_t ctx, int synchronous,
326                  const char *userid, const char *algo,
327                  unsigned long reserved, unsigned long expires,
328                  gpgme_key_t anchorkey, unsigned int flags)
329 {
330   gpgme_error_t err;
331   void *hook;
332   op_data_t opd;
333
334   err = _gpgme_op_reset (ctx, synchronous);
335   if (err)
336     return err;
337
338   if (reserved || anchorkey || !userid)
339     return gpg_error (GPG_ERR_INV_ARG);
340
341   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
342                                sizeof (*opd), release_op_data);
343   opd = hook;
344   if (err)
345     return err;
346
347   _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
348
349   if (ctx->passphrase_cb)
350     {
351       err = _gpgme_engine_set_command_handler
352         (ctx->engine, _gpgme_passphrase_command_handler, ctx);
353       if (err)
354         return err;
355     }
356
357   return _gpgme_engine_op_genkey (ctx->engine,
358                                   userid, algo, reserved, expires,
359                                   anchorkey, flags,
360                                   NULL,
361                                   ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0,
362                                   NULL, NULL);
363
364 }
365
366
367 gpgme_error_t
368 gpgme_op_createkey_start (gpgme_ctx_t ctx, const char *userid, const char *algo,
369                           unsigned long reserved, unsigned long expires,
370                           gpgme_key_t anchorkey, unsigned int flags)
371 {
372   gpgme_error_t err;
373
374   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createkey_start", ctx,
375               "userid='%s', algo='%s' flags=0x%x", userid, algo, flags);
376
377   if (!ctx)
378     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
379
380   err = createkey_start (ctx, 0,
381                          userid, algo, reserved, expires, anchorkey, flags);
382   return TRACE_ERR (err);
383 }
384
385
386 gpgme_error_t
387 gpgme_op_createkey (gpgme_ctx_t ctx, const char *userid, const char *algo,
388                     unsigned long reserved, unsigned long expires,
389                     gpgme_key_t anchorkey, unsigned int flags)
390 {
391   gpgme_error_t err;
392
393   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createkey", ctx,
394               "userid='%s', algo='%s' flags=0x%x", userid, algo, flags);
395
396   if (!ctx)
397     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
398
399   err = createkey_start (ctx, 1,
400                          userid, algo, reserved, expires, anchorkey, flags);
401   if (!err)
402     err = _gpgme_wait_one (ctx);
403   return TRACE_ERR (err);
404 }
405
406
407 \f
408 static gpgme_error_t
409 createsubkey_start (gpgme_ctx_t ctx, int synchronous,
410                     gpgme_key_t key,
411                     const char *algo,
412                     unsigned long reserved, unsigned long expires,
413                     unsigned int flags)
414 {
415   gpgme_error_t err;
416   void *hook;
417   op_data_t opd;
418
419   if (ctx->protocol != GPGME_PROTOCOL_OPENPGP)
420     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
421
422   err = _gpgme_op_reset (ctx, synchronous);
423   if (err)
424     return err;
425
426   if (reserved || !key)
427     return gpg_error (GPG_ERR_INV_ARG);
428
429   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
430                                sizeof (*opd), release_op_data);
431   opd = hook;
432   if (err)
433     return err;
434
435   _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
436
437   if (ctx->passphrase_cb)
438     {
439       err = _gpgme_engine_set_command_handler
440         (ctx->engine, _gpgme_passphrase_command_handler, ctx);
441       if (err)
442         return err;
443     }
444
445   return _gpgme_engine_op_genkey (ctx->engine,
446                                   NULL, algo, reserved, expires,
447                                   key, flags,
448                                   NULL,
449                                   ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0,
450                                   NULL, NULL);
451
452 }
453
454
455 /* Add a subkey to an existing KEY.  */
456 gpgme_error_t
457 gpgme_op_createsubkey_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo,
458                              unsigned long reserved, unsigned long expires,
459                              unsigned int flags)
460 {
461   gpgme_error_t err;
462
463   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createsubkey_start", ctx,
464               "key=%p, algo='%s' flags=0x%x", key, algo, flags);
465
466   if (!ctx)
467     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
468
469   err = createsubkey_start (ctx, 0, key, algo, reserved, expires, flags);
470   return TRACE_ERR (err);
471 }
472
473
474 gpgme_error_t
475 gpgme_op_createsubkey (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo,
476                        unsigned long reserved, unsigned long expires,
477                        unsigned int flags)
478 {
479   gpgme_error_t err;
480
481   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createsubkey", ctx,
482               "key=%p, algo='%s' flags=0x%x", key, algo, flags);
483
484   if (!ctx)
485     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
486
487   err = createsubkey_start (ctx, 1, key, algo, reserved, expires, flags);
488   if (!err)
489     err = _gpgme_wait_one (ctx);
490   return TRACE_ERR (err);
491 }
492
493
494 \f
495 static gpgme_error_t
496 addrevuid_start (gpgme_ctx_t ctx, int synchronous, int extraflags,
497                  gpgme_key_t key, const char *userid, unsigned int flags)
498 {
499   gpgme_error_t err;
500   void *hook;
501   op_data_t opd;
502
503   if (ctx->protocol != GPGME_PROTOCOL_OPENPGP)
504     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
505
506   if (!key || !userid)
507     return gpg_error (GPG_ERR_INV_ARG);
508
509   err = _gpgme_op_reset (ctx, synchronous);
510   if (err)
511     return err;
512
513   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
514                                sizeof (*opd), release_op_data);
515   opd = hook;
516   if (err)
517     return err;
518
519   opd->uidmode = extraflags? 2 : 1;
520
521   _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
522
523   if (ctx->passphrase_cb)
524     {
525       err = _gpgme_engine_set_command_handler
526         (ctx->engine, _gpgme_passphrase_command_handler, ctx);
527       if (err)
528         return err;
529     }
530
531   return _gpgme_engine_op_genkey (ctx->engine,
532                                   userid, NULL, 0, 0,
533                                   key, flags,
534                                   NULL,
535                                   extraflags,
536                                   NULL, NULL);
537
538 }
539
540
541 /* Add USERID to an existing KEY.  */
542 gpgme_error_t
543 gpgme_op_adduid_start (gpgme_ctx_t ctx,
544                        gpgme_key_t key, const char *userid, unsigned int flags)
545 {
546   gpgme_error_t err;
547
548   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_adduid_start", ctx,
549               "uid='%s' flags=0x%x", userid, flags);
550
551   if (!ctx)
552     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
553
554   err = addrevuid_start (ctx, 0, 0, key, userid, flags);
555   return TRACE_ERR (err);
556 }
557
558
559 gpgme_error_t
560 gpgme_op_adduid (gpgme_ctx_t ctx,
561                  gpgme_key_t key, const char *userid, unsigned int flags)
562 {
563   gpgme_error_t err;
564
565   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_adduid", ctx,
566               "uid='%s' flags=0x%x", userid, flags);
567
568   if (!ctx)
569     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
570
571   err = addrevuid_start (ctx, 1, 0, key, userid, flags);
572   if (!err)
573     err = _gpgme_wait_one (ctx);
574   return TRACE_ERR (err);
575 }
576
577
578 /* Revoke USERID from KEY.  */
579 gpgme_error_t
580 gpgme_op_revuid_start (gpgme_ctx_t ctx,
581                        gpgme_key_t key, const char *userid, unsigned int flags)
582 {
583   gpgme_error_t err;
584
585   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_revuid_start", ctx,
586               "uid='%s' flags=0x%x", userid, flags);
587
588   if (!ctx)
589     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
590
591   err = addrevuid_start (ctx, 0, GENKEY_EXTRAFLAG_REVOKE, key, userid, flags);
592   return TRACE_ERR (err);
593 }
594
595
596 gpgme_error_t
597 gpgme_op_revuid (gpgme_ctx_t ctx,
598                  gpgme_key_t key, const char *userid, unsigned int flags)
599 {
600   gpgme_error_t err;
601
602   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_revuid", ctx,
603               "uid='%s' flags=0x%x", userid, flags);
604
605   if (!ctx)
606     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
607
608   err = addrevuid_start (ctx, 1, GENKEY_EXTRAFLAG_REVOKE, key, userid, flags);
609   if (!err)
610     err = _gpgme_wait_one (ctx);
611   return TRACE_ERR (err);
612 }
613
614
615 /* Set a flag on the USERID of KEY.  The only supported flag right now
616  * is "primary" to mark the primary key.  */
617 static gpg_error_t
618 set_uid_flag (gpgme_ctx_t ctx, int synchronous,
619               gpgme_key_t key, const char *userid,
620               const char *name, const char *value)
621 {
622   gpgme_error_t err;
623
624   TRACE_BEG4 (DEBUG_CTX, "gpgme_op_set_uid_flag", ctx,
625               "%d uid='%s' '%s'='%s'", synchronous, userid, name, value);
626
627   if (!ctx || !name || !key || !userid)
628     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
629
630   if (!strcmp (name, "primary"))
631     {
632       if (value)
633         err = gpg_error (GPG_ERR_INV_ARG);
634       else
635         err = addrevuid_start (ctx, synchronous,
636                                GENKEY_EXTRAFLAG_SETPRIMARY, key, userid, 0);
637     }
638   else
639     return err = gpg_error (GPG_ERR_UNKNOWN_NAME);
640
641   if (synchronous && !err)
642     err = _gpgme_wait_one (ctx);
643   return TRACE_ERR (err);
644 }
645
646
647 /* See set_uid_flag. */
648 gpgme_error_t
649 gpgme_op_set_uid_flag_start (gpgme_ctx_t ctx,
650                              gpgme_key_t key, const char *userid,
651                              const char *name, const char *value)
652 {
653   return set_uid_flag (ctx, 0, key, userid, name, value);
654 }
655
656
657 /* See set_uid_flag.  This is the synchronous variant.  */
658 gpgme_error_t
659 gpgme_op_set_uid_flag (gpgme_ctx_t ctx,
660                        gpgme_key_t key, const char *userid,
661                        const char *name, const char *value)
662 {
663   return set_uid_flag (ctx, 1, key, userid, name, value);
664 }