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