1 /* run-genkey.c - Test tool to perform key generation
2 * Copyright (C) 2016 g10 Code GmbH
3 *
4 * This file is part of GPGME.
5 *
6 * GPGME is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
10 *
11 * GPGME is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, see <https://gnu.org/licenses/>.
18 * SPDX-License-Identifier: LGPL-2.1-or-later
19 */
20
21 /* We need to include config.h so that we know whether we are building
22 with large file system (LFS) support. */
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <assert.h>
31
32 #include <gpgme.h>
33
34 #define PGM "run-genkey"
35
36 #include "run-support.h"
37
38
39 static int verbose;
40
41
42 /* Tokenize STRING using the set of delimiters in DELIM. Leading
43 * spaces and tabs are removed from all tokens. The caller must free
44 * the result.
45 *
46 * Returns: A malloced and NULL delimited array with the tokens. On
47 * memory error NULL is returned and ERRNO is set.
48 */
49 static char **
strtokenize(const char * string,const char * delim)50 strtokenize (const char *string, const char *delim)
51 {
52 const char *s;
53 size_t fields;
54 size_t bytes, n;
55 char *buffer;
56 char *p, *px, *pend;
57 char **result;
58
59 /* Count the number of fields. */
60 for (fields = 1, s = strpbrk (string, delim); s; s = strpbrk (s + 1, delim))
61 fields++;
62 fields++; /* Add one for the terminating NULL. */
63
64 /* Allocate an array for all fields, a terminating NULL, and space
65 for a copy of the string. */
66 bytes = fields * sizeof *result;
67 if (bytes / sizeof *result != fields)
68 {
69 gpg_err_set_errno (ENOMEM);
70 return NULL;
71 }
72 n = strlen (string) + 1;
73 bytes += n;
74 if (bytes < n)
75 {
76 gpg_err_set_errno (ENOMEM);
77 return NULL;
78 }
79 result = malloc (bytes);
80 if (!result)
81 return NULL;
82 buffer = (char*)(result + fields);
83
84 /* Copy and parse the string. */
85 strcpy (buffer, string);
86 for (n = 0, p = buffer; (pend = strpbrk (p, delim)); p = pend + 1)
87 {
88 *pend = 0;
89 while (*p == ' ' || *p == '\t')
90 p++;
91 for (px = pend - 1; px >= p && (*px == ' ' || *px == '\t'); px--)
92 *px = 0;
93 result[n++] = p;
94 }
95 while (*p == ' ' || *p == '\t')
96 p++;
97 for (px = p + strlen (p) - 1; px >= p && (*px == ' ' || *px == '\t'); px--)
98 *px = 0;
99 result[n++] = p;
100 result[n] = NULL;
101
102 assert ((char*)(result + n + 1) == buffer);
103
104 return result;
105 }
106
107
108 static gpg_error_t
status_cb(void * opaque,const char * keyword,const char * value)109 status_cb (void *opaque, const char *keyword, const char *value)
110 {
111 (void)opaque;
112 fprintf (stderr, "status_cb: %s %s\n", nonnull(keyword), nonnull(value));
113 return 0;
114 }
115
116
117 static void
progress_cb(void * opaque,const char * what,int type,int current,int total)118 progress_cb (void *opaque, const char *what, int type, int current, int total)
119 {
120 (void)opaque;
121 (void)type;
122
123 if (total)
124 fprintf (stderr, "progress for '%s' %u%% (%d of %d)\n",
125 nonnull (what),
126 (unsigned)(((double)current / total) * 100), current, total);
127 else
128 fprintf (stderr, "progress for '%s' %d\n", nonnull(what), current);
129 fflush (stderr);
130 }
131
132
133 static unsigned long
parse_expire_string(const char * string)134 parse_expire_string (const char *string)
135 {
136 unsigned long seconds;
137
138 if (!string || !*string || !strcmp (string, "none")
139 || !strcmp (string, "never") || !strcmp (string, "-"))
140 seconds = 0;
141 else if (strspn (string, "01234567890") == strlen (string))
142 seconds = strtoul (string, NULL, 10);
143 else
144 {
145 fprintf (stderr, PGM ": invalid value '%s'\n", string);
146 exit (1);
147 }
148
149 return seconds;
150 }
151
152
153 /* Parse a usage string and return flags for gpgme_op_createkey. */
154 static unsigned int
parse_usage_string(const char * string)155 parse_usage_string (const char *string)
156 {
157 gpg_error_t err;
158 char **tokens = NULL;
159 const char *s;
160 int i;
161 unsigned int flags = 0;
162
163 tokens = strtokenize (string, " \t,");
164 if (!tokens)
165 {
166 err = gpg_error_from_syserror ();
167 fprintf (stderr, PGM": strtokenize failed: %s\n", gpg_strerror (err));
168 exit (1);
169 }
170
171 for (i=0; (s = tokens[i]); i++)
172 {
173 if (!*s)
174 ;
175 else if (!strcmp (s, "default"))
176 ;
177 else if (!strcmp (s, "sign"))
178 flags |= GPGME_CREATE_SIGN;
179 else if (!strcmp (s, "encr"))
180 flags |= GPGME_CREATE_ENCR;
181 else if (!strcmp (s, "cert"))
182 flags |= GPGME_CREATE_CERT;
183 else if (!strcmp (s, "auth"))
184 flags |= GPGME_CREATE_AUTH;
185 else
186 {
187 free (tokens);
188 fprintf (stderr, PGM": invalid value '%s': %s\n",
189 string, "bad usage");
190 exit (1);
191 }
192 }
193
194 free (tokens);
195 return flags;
196 }
197
198
199
200 static int
show_usage(int ex)201 show_usage (int ex)
202 {
203 fputs ("usage: " PGM " [options] ARGS\n"
204 " args: USERID [ALGO [USAGE [EXPIRESECONDS]]]\n"
205 " for addkey: FPR [ALGO [USAGE [EXPIRESECONDS]]]\n"
206 " for adduid: FPR USERID\n"
207 " for revuid: FPR USERID\n"
208 " for setexpire: FPR EXPIRE [SUBFPRS]\n"
209 " for set-primary: FPR USERID\n"
210 "Options:\n"
211 " --addkey add a subkey to the key with FPR\n"
212 " --adduid add a user id to the key with FPR\n"
213 " --revuid revoke a user id from the key with FPR\n"
214 " --set-primary set the primary key flag on USERID\n"
215 " --setexpire set the expiration time of the key FPR\n"
216 " or of its subkeys SUBFPRS\n"
217 " --verbose run in verbose mode\n"
218 " --status print status lines from the backend\n"
219 " --progress print progress info\n"
220 " --openpgp use the OpenPGP protocol (default)\n"
221 " --cms use the CMS protocol\n"
222 " --loopback use a loopback pinentry\n"
223 " --unprotected do not use a passphrase\n"
224 " --force do not check for a duplicated user id\n"
225 , stderr);
226 exit (ex);
227 }
228
229
230 int
main(int argc,char ** argv)231 main (int argc, char **argv)
232 {
233 int last_argc = -1;
234 gpgme_error_t err;
235 gpgme_ctx_t ctx;
236 gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP;
237 int print_status = 0;
238 int print_progress = 0;
239 int use_loopback = 0;
240 int addkey = 0;
241 int adduid = 0;
242 int revuid = 0;
243 int setpri = 0;
244 int setexpire = 0;
245 const char *userid;
246 const char *algo = NULL;
247 const char *newuserid = NULL;
248 const char *subfprs = NULL;
249 unsigned int flags = 0;
250 unsigned long expire = 0;
251 gpgme_genkey_result_t result;
252 int i;
253 size_t n;
254 char *subfprs_buffer = NULL;
255
256 if (argc)
257 { argc--; argv++; }
258
259 while (argc && last_argc != argc )
260 {
261 last_argc = argc;
262 if (!strcmp (*argv, "--"))
263 {
264 argc--; argv++;
265 break;
266 }
267 else if (!strcmp (*argv, "--help"))
268 show_usage (0);
269 else if (!strcmp (*argv, "--addkey"))
270 {
271 addkey = 1;
272 adduid = 0;
273 revuid = 0;
274 setpri = 0;
275 setexpire = 0;
276 argc--; argv++;
277 }
278 else if (!strcmp (*argv, "--adduid"))
279 {
280 addkey = 0;
281 adduid = 1;
282 revuid = 0;
283 setpri = 0;
284 setexpire = 0;
285 argc--; argv++;
286 }
287 else if (!strcmp (*argv, "--revuid"))
288 {
289 addkey = 0;
290 adduid = 0;
291 revuid = 1;
292 setpri = 0;
293 setexpire = 0;
294 argc--; argv++;
295 }
296 else if (!strcmp (*argv, "--set-primary"))
297 {
298 addkey = 0;
299 adduid = 0;
300 revuid = 0;
301 setpri = 1;
302 setexpire = 0;
303 argc--; argv++;
304 }
305 else if (!strcmp (*argv, "--setexpire"))
306 {
307 addkey = 0;
308 adduid = 0;
309 revuid = 0;
310 setpri = 0;
311 setexpire = 1;
312 argc--; argv++;
313 }
314 else if (!strcmp (*argv, "--verbose"))
315 {
316 verbose = 1;
317 argc--; argv++;
318 }
319 else if (!strcmp (*argv, "--status"))
320 {
321 print_status = 1;
322 argc--; argv++;
323 }
324 else if (!strcmp (*argv, "--progress"))
325 {
326 print_progress = 1;
327 argc--; argv++;
328 }
329 else if (!strcmp (*argv, "--openpgp"))
330 {
331 protocol = GPGME_PROTOCOL_OpenPGP;
332 argc--; argv++;
333 }
334 else if (!strcmp (*argv, "--cms"))
335 {
336 protocol = GPGME_PROTOCOL_CMS;
337 argc--; argv++;
338 }
339 else if (!strcmp (*argv, "--loopback"))
340 {
341 use_loopback = 1;
342 argc--; argv++;
343 }
344 else if (!strcmp (*argv, "--unprotected"))
345 {
346 flags |= GPGME_CREATE_NOPASSWD;
347 argc--; argv++;
348 }
349 else if (!strcmp (*argv, "--force"))
350 {
351 flags |= GPGME_CREATE_FORCE;
352 argc--; argv++;
353 }
354 else if (!strncmp (*argv, "--", 2))
355 show_usage (1);
356 }
357
358 if (adduid || revuid || setpri)
359 {
360 if (argc != 2)
361 show_usage (1);
362 userid = argv[0];
363 newuserid = argv[1];
364 }
365 else if (setexpire)
366 {
367 if (argc < 2)
368 {
369 show_usage (1);
370 }
371 userid = argv[0];
372 argc--; argv++;
373 expire = parse_expire_string (argv[0]);
374 argc--; argv++;
375 if (argc > 1)
376 {
377 /* Several subkey fprs given */
378 for (i=0, n = 0; i < argc; i++)
379 n += strlen (argv[1]) + 1;
380 n++;
381 subfprs_buffer = malloc (n);
382 if (!subfprs_buffer)
383 {
384 fprintf (stderr, PGM ": malloc failed: %s\n",
385 gpg_strerror (gpg_error_from_syserror ()));
386 exit (1);
387 }
388 *subfprs_buffer = 0;
389 for (i=0; i < argc; i++)
390 {
391 strcat (subfprs_buffer, argv[i]);
392 strcat (subfprs_buffer, "\n");
393 }
394 subfprs = subfprs_buffer;
395 }
396 else if (argc)
397 {
398 /* One subkey fpr (or '*') given */
399 subfprs = *argv;
400 }
401 else
402 {
403 /* No subkey fpr given. */
404 subfprs = NULL;
405 }
406 }
407 else
408 {
409 if (!argc || argc > 4)
410 show_usage (1);
411 userid = argv[0];
412 if (argc > 1)
413 algo = argv[1];
414 if (argc > 2)
415 flags |= parse_usage_string (argv[2]);
416 if (argc > 3)
417 expire = parse_expire_string (argv[3]);
418 }
419
420 init_gpgme (protocol);
421
422 err = gpgme_new (&ctx);
423 fail_if_err (err);
424 gpgme_set_protocol (ctx, protocol);
425 gpgme_set_armor (ctx, 1);
426 if (print_status)
427 {
428 gpgme_set_status_cb (ctx, status_cb, NULL);
429 gpgme_set_ctx_flag (ctx, "full-status", "1");
430 }
431 if (print_progress)
432 gpgme_set_progress_cb (ctx, progress_cb, NULL);
433 if (use_loopback)
434 {
435 gpgme_set_pinentry_mode (ctx, GPGME_PINENTRY_MODE_LOOPBACK);
436 gpgme_set_passphrase_cb (ctx, passphrase_cb, NULL);
437 }
438
439
440 if (addkey || adduid || revuid || setpri || setexpire)
441 {
442 gpgme_key_t akey;
443
444 err = gpgme_get_key (ctx, userid, &akey, 1);
445 if (err)
446 {
447 fprintf (stderr, PGM ": error getting secret key for '%s': %s\n",
448 userid, gpg_strerror (err));
449 exit (1);
450 }
451
452 if (addkey)
453 {
454 err = gpgme_op_createsubkey (ctx, akey, algo, 0, expire, flags);
455 if (err)
456 {
457 fprintf (stderr, PGM ": gpgme_op_createsubkey failed: %s\n",
458 gpg_strerror (err));
459 exit (1);
460 }
461 }
462 else if (adduid)
463 {
464 err = gpgme_op_adduid (ctx, akey, newuserid, flags);
465 if (err)
466 {
467 fprintf (stderr, PGM ": gpgme_op_adduid failed: %s\n",
468 gpg_strerror (err));
469 exit (1);
470 }
471 }
472 else if (revuid)
473 {
474 err = gpgme_op_revuid (ctx, akey, newuserid, flags);
475 if (err)
476 {
477 fprintf (stderr, PGM ": gpgme_op_revuid failed: %s\n",
478 gpg_strerror (err));
479 exit (1);
480 }
481 }
482 else if (setpri)
483 {
484 err = gpgme_op_set_uid_flag (ctx, akey, newuserid, "primary", NULL);
485 if (err)
486 {
487 fprintf (stderr, PGM ": gpgme_op_set_uid_flag failed: %s\n",
488 gpg_strerror (err));
489 exit (1);
490 }
491 }
492 else if (setexpire)
493 {
494 err = gpgme_op_setexpire (ctx, akey, expire, subfprs, 0);
495 if (err)
496 {
497 fprintf (stderr, PGM ": gpgme_op_setexpire failed: %s\n",
498 gpg_strerror (err));
499 exit (1);
500 }
501 }
502
503 gpgme_key_unref (akey);
504 }
505 else
506 {
507 err = gpgme_op_createkey (ctx, userid, algo, 0, expire, NULL, flags);
508 if (err)
509 {
510 fprintf (stderr, PGM ": gpgme_op_createkey failed: %s\n",
511 gpg_strerror (err));
512 exit (1);
513 }
514 }
515
516 if (!setpri && !setexpire)
517 {
518 result = gpgme_op_genkey_result (ctx);
519 if (!result)
520 {
521 fprintf (stderr, PGM": gpgme_op_genkey_result returned NULL\n");
522 exit (1);
523 }
524
525 printf ("Generated key: %s (%s)\n",
526 result->fpr ? result->fpr : "none",
527 result->primary ? (result->sub ? "primary, sub" : "primary")
528 /**/ : (result->sub ? "sub" : "none"));
529
530 if (result->fpr && strlen (result->fpr) < 40)
531 fprintf (stderr, PGM": generated key has unexpected fingerprint\n");
532 if (!result->primary)
533 fprintf (stderr, PGM": primary key was not generated\n");
534 if (!result->sub)
535 fprintf (stderr, PGM": sub key was not generated\n");
536 if (!result->uid)
537 fprintf (stderr, PGM": uid was not generated\n");
538 }
539
540 gpgme_release (ctx);
541 return 0;
542 }
543