Line data Source code
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://www.gnu.org/licenses/>.
18 : */
19 :
20 : /* We need to include config.h so that we know whether we are building
21 : with large file system (LFS) support. */
22 : #ifdef HAVE_CONFIG_H
23 : #include <config.h>
24 : #endif
25 :
26 : #include <stdlib.h>
27 : #include <stdio.h>
28 : #include <string.h>
29 : #include <assert.h>
30 :
31 : #include <gpgme.h>
32 :
33 : #define PGM "run-genkey"
34 :
35 : #include "run-support.h"
36 :
37 :
38 : static int verbose;
39 :
40 :
41 : /* Tokenize STRING using the set of delimiters in DELIM. Leading
42 : * spaces and tabs are removed from all tokens. The caller must free
43 : * the result.
44 : *
45 : * Returns: A malloced and NULL delimited array with the tokens. On
46 : * memory error NULL is returned and ERRNO is set.
47 : */
48 : static char **
49 0 : strtokenize (const char *string, const char *delim)
50 : {
51 : const char *s;
52 : size_t fields;
53 : size_t bytes, n;
54 : char *buffer;
55 : char *p, *px, *pend;
56 : char **result;
57 :
58 : /* Count the number of fields. */
59 0 : for (fields = 1, s = strpbrk (string, delim); s; s = strpbrk (s + 1, delim))
60 0 : fields++;
61 0 : fields++; /* Add one for the terminating NULL. */
62 :
63 : /* Allocate an array for all fields, a terminating NULL, and space
64 : for a copy of the string. */
65 0 : bytes = fields * sizeof *result;
66 0 : if (bytes / sizeof *result != fields)
67 : {
68 0 : gpg_err_set_errno (ENOMEM);
69 0 : return NULL;
70 : }
71 0 : n = strlen (string) + 1;
72 0 : bytes += n;
73 0 : if (bytes < n)
74 : {
75 0 : gpg_err_set_errno (ENOMEM);
76 0 : return NULL;
77 : }
78 0 : result = malloc (bytes);
79 0 : if (!result)
80 0 : return NULL;
81 0 : buffer = (char*)(result + fields);
82 :
83 : /* Copy and parse the string. */
84 0 : strcpy (buffer, string);
85 0 : for (n = 0, p = buffer; (pend = strpbrk (p, delim)); p = pend + 1)
86 : {
87 0 : *pend = 0;
88 0 : while (*p == ' ' || *p == '\t')
89 0 : p++;
90 0 : for (px = pend - 1; px >= p && (*px == ' ' || *px == '\t'); px--)
91 0 : *px = 0;
92 0 : result[n++] = p;
93 : }
94 0 : while (*p == ' ' || *p == '\t')
95 0 : p++;
96 0 : for (px = p + strlen (p) - 1; px >= p && (*px == ' ' || *px == '\t'); px--)
97 0 : *px = 0;
98 0 : result[n++] = p;
99 0 : result[n] = NULL;
100 :
101 0 : assert ((char*)(result + n + 1) == buffer);
102 :
103 0 : return result;
104 : }
105 :
106 :
107 : static gpg_error_t
108 0 : status_cb (void *opaque, const char *keyword, const char *value)
109 : {
110 : (void)opaque;
111 0 : fprintf (stderr, "status_cb: %s %s\n", nonnull(keyword), nonnull(value));
112 0 : return 0;
113 : }
114 :
115 :
116 : static void
117 0 : progress_cb (void *opaque, const char *what, int type, int current, int total)
118 : {
119 : (void)opaque;
120 : (void)type;
121 :
122 0 : if (total)
123 0 : fprintf (stderr, "progress for '%s' %u%% (%d of %d)\n",
124 : nonnull (what),
125 0 : (unsigned)(((double)current / total) * 100), current, total);
126 : else
127 0 : fprintf (stderr, "progress for '%s' %d\n", nonnull(what), current);
128 0 : fflush (stderr);
129 0 : }
130 :
131 :
132 : static unsigned long
133 0 : parse_expire_string (const char *string)
134 : {
135 : unsigned long seconds;
136 :
137 0 : if (!string || !*string || !strcmp (string, "none")
138 0 : || !strcmp (string, "never") || !strcmp (string, "-"))
139 0 : seconds = 0;
140 0 : else if (strspn (string, "01234567890") == strlen (string))
141 0 : seconds = strtoul (string, NULL, 10);
142 : else
143 : {
144 0 : fprintf (stderr, PGM ": invalid value '%s'\n", string);
145 0 : exit (1);
146 : }
147 :
148 0 : return seconds;
149 : }
150 :
151 :
152 : /* Parse a usage string and return flags for gpgme_op_createkey. */
153 : static unsigned int
154 0 : parse_usage_string (const char *string)
155 : {
156 : gpg_error_t err;
157 0 : char **tokens = NULL;
158 : const char *s;
159 : int i;
160 0 : unsigned int flags = 0;
161 :
162 0 : tokens = strtokenize (string, " \t,");
163 0 : if (!tokens)
164 : {
165 0 : err = gpg_error_from_syserror ();
166 0 : fprintf (stderr, PGM": strtokenize failed: %s\n", gpg_strerror (err));
167 0 : exit (1);
168 : }
169 :
170 0 : for (i=0; (s = tokens[i]); i++)
171 : {
172 0 : if (!*s)
173 : ;
174 0 : else if (!strcmp (s, "default"))
175 : ;
176 0 : else if (!strcmp (s, "sign"))
177 0 : flags |= GPGME_CREATE_SIGN;
178 0 : else if (!strcmp (s, "encr"))
179 0 : flags |= GPGME_CREATE_ENCR;
180 0 : else if (!strcmp (s, "cert"))
181 0 : flags |= GPGME_CREATE_CERT;
182 0 : else if (!strcmp (s, "auth"))
183 0 : flags |= GPGME_CREATE_AUTH;
184 : else
185 : {
186 0 : free (tokens);
187 0 : fprintf (stderr, PGM": invalid value '%s': %s\n",
188 : string, "bad usage");
189 0 : exit (1);
190 : }
191 : }
192 :
193 0 : free (tokens);
194 0 : return flags;
195 : }
196 :
197 :
198 :
199 : static int
200 0 : show_usage (int ex)
201 : {
202 0 : fputs ("usage: " PGM " [options] ARGS\n"
203 : " args: USERID [ALGO [USAGE [EXPIRESECONDS]]]\n"
204 : " for addkey: FPR [ALGO [USAGE [EXPIRESECONDS]]]\n"
205 : " for adduid: FPR USERID\n"
206 : " for revuid: FPR USERID\n"
207 : " for set-primary: FPR USERID\n"
208 : "Options:\n"
209 : " --addkey add a subkey to the key with FPR\n"
210 : " --adduid add a user id to the key with FPR\n"
211 : " --revuid revoke a user id from the key with FPR\n"
212 : " --set-primary set the primary key flag on USERID\n"
213 : " --verbose run in verbose mode\n"
214 : " --status print status lines from the backend\n"
215 : " --progress print progress info\n"
216 : " --openpgp use the OpenPGP protocol (default)\n"
217 : " --cms use the CMS protocol\n"
218 : " --loopback use a loopback pinentry\n"
219 : " --unprotected do not use a passphrase\n"
220 : " --force do not check for a duplicated user id\n"
221 : , stderr);
222 0 : exit (ex);
223 : }
224 :
225 :
226 : int
227 0 : main (int argc, char **argv)
228 : {
229 0 : int last_argc = -1;
230 : gpgme_error_t err;
231 : gpgme_ctx_t ctx;
232 0 : gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP;
233 0 : int print_status = 0;
234 0 : int print_progress = 0;
235 0 : int use_loopback = 0;
236 0 : int addkey = 0;
237 0 : int adduid = 0;
238 0 : int revuid = 0;
239 0 : int setpri = 0;
240 : const char *userid;
241 0 : const char *algo = NULL;
242 0 : const char *newuserid = NULL;
243 0 : unsigned int flags = 0;
244 0 : unsigned long expire = 0;
245 : gpgme_genkey_result_t result;
246 :
247 0 : if (argc)
248 0 : { argc--; argv++; }
249 :
250 0 : while (argc && last_argc != argc )
251 : {
252 0 : last_argc = argc;
253 0 : if (!strcmp (*argv, "--"))
254 : {
255 0 : argc--; argv++;
256 0 : break;
257 : }
258 0 : else if (!strcmp (*argv, "--help"))
259 0 : show_usage (0);
260 0 : else if (!strcmp (*argv, "--addkey"))
261 : {
262 0 : addkey = 1;
263 0 : adduid = 0;
264 0 : revuid = 0;
265 0 : setpri = 0;
266 0 : argc--; argv++;
267 : }
268 0 : else if (!strcmp (*argv, "--adduid"))
269 : {
270 0 : addkey = 0;
271 0 : adduid = 1;
272 0 : revuid = 0;
273 0 : setpri = 0;
274 0 : argc--; argv++;
275 : }
276 0 : else if (!strcmp (*argv, "--revuid"))
277 : {
278 0 : addkey = 0;
279 0 : adduid = 0;
280 0 : revuid = 1;
281 0 : setpri = 0;
282 0 : argc--; argv++;
283 : }
284 0 : else if (!strcmp (*argv, "--set-primary"))
285 : {
286 0 : addkey = 0;
287 0 : adduid = 0;
288 0 : revuid = 0;
289 0 : setpri = 1;
290 0 : argc--; argv++;
291 : }
292 0 : else if (!strcmp (*argv, "--verbose"))
293 : {
294 0 : verbose = 1;
295 0 : argc--; argv++;
296 : }
297 0 : else if (!strcmp (*argv, "--status"))
298 : {
299 0 : print_status = 1;
300 0 : argc--; argv++;
301 : }
302 0 : else if (!strcmp (*argv, "--progress"))
303 : {
304 0 : print_progress = 1;
305 0 : argc--; argv++;
306 : }
307 0 : else if (!strcmp (*argv, "--openpgp"))
308 : {
309 0 : protocol = GPGME_PROTOCOL_OpenPGP;
310 0 : argc--; argv++;
311 : }
312 0 : else if (!strcmp (*argv, "--cms"))
313 : {
314 0 : protocol = GPGME_PROTOCOL_CMS;
315 0 : argc--; argv++;
316 : }
317 0 : else if (!strcmp (*argv, "--loopback"))
318 : {
319 0 : use_loopback = 1;
320 0 : argc--; argv++;
321 : }
322 0 : else if (!strcmp (*argv, "--unprotected"))
323 : {
324 0 : flags |= GPGME_CREATE_NOPASSWD;
325 0 : argc--; argv++;
326 : }
327 0 : else if (!strcmp (*argv, "--force"))
328 : {
329 0 : flags |= GPGME_CREATE_FORCE;
330 0 : argc--; argv++;
331 : }
332 0 : else if (!strncmp (*argv, "--", 2))
333 0 : show_usage (1);
334 : }
335 :
336 0 : if (adduid || revuid || setpri)
337 : {
338 0 : if (argc != 2)
339 0 : show_usage (1);
340 0 : userid = argv[0];
341 0 : newuserid = argv[1];
342 : }
343 : else
344 : {
345 0 : if (!argc || argc > 4)
346 0 : show_usage (1);
347 0 : userid = argv[0];
348 0 : if (argc > 1)
349 0 : algo = argv[1];
350 0 : if (argc > 2)
351 0 : flags |= parse_usage_string (argv[2]);
352 0 : if (argc > 3)
353 0 : expire = parse_expire_string (argv[3]);
354 : }
355 :
356 0 : init_gpgme (protocol);
357 :
358 0 : err = gpgme_new (&ctx);
359 0 : fail_if_err (err);
360 0 : gpgme_set_protocol (ctx, protocol);
361 0 : gpgme_set_armor (ctx, 1);
362 0 : if (print_status)
363 : {
364 0 : gpgme_set_status_cb (ctx, status_cb, NULL);
365 0 : gpgme_set_ctx_flag (ctx, "full-status", "1");
366 : }
367 0 : if (print_progress)
368 0 : gpgme_set_progress_cb (ctx, progress_cb, NULL);
369 0 : if (use_loopback)
370 : {
371 0 : gpgme_set_pinentry_mode (ctx, GPGME_PINENTRY_MODE_LOOPBACK);
372 0 : gpgme_set_passphrase_cb (ctx, passphrase_cb, NULL);
373 : }
374 :
375 0 : if (addkey || adduid || revuid || setpri)
376 0 : {
377 : gpgme_key_t akey;
378 :
379 0 : err = gpgme_get_key (ctx, userid, &akey, 1);
380 0 : if (err)
381 : {
382 0 : fprintf (stderr, PGM ": error getting secret key for '%s': %s\n",
383 : userid, gpg_strerror (err));
384 0 : exit (1);
385 : }
386 :
387 0 : if (addkey)
388 : {
389 0 : err = gpgme_op_createsubkey (ctx, akey, algo, 0, expire, flags);
390 0 : if (err)
391 : {
392 0 : fprintf (stderr, PGM ": gpgme_op_createsubkey failed: %s\n",
393 : gpg_strerror (err));
394 0 : exit (1);
395 : }
396 : }
397 0 : else if (adduid)
398 : {
399 0 : err = gpgme_op_adduid (ctx, akey, newuserid, flags);
400 0 : if (err)
401 : {
402 0 : fprintf (stderr, PGM ": gpgme_op_adduid failed: %s\n",
403 : gpg_strerror (err));
404 0 : exit (1);
405 : }
406 : }
407 0 : else if (revuid)
408 : {
409 0 : err = gpgme_op_revuid (ctx, akey, newuserid, flags);
410 0 : if (err)
411 : {
412 0 : fprintf (stderr, PGM ": gpgme_op_revuid failed: %s\n",
413 : gpg_strerror (err));
414 0 : exit (1);
415 : }
416 : }
417 0 : else if (setpri)
418 : {
419 0 : err = gpgme_op_set_uid_flag (ctx, akey, newuserid, "primary", NULL);
420 0 : if (err)
421 : {
422 0 : fprintf (stderr, PGM ": gpgme_op_set_uid_flag failed: %s\n",
423 : gpg_strerror (err));
424 0 : exit (1);
425 : }
426 : }
427 0 : gpgme_key_unref (akey);
428 : }
429 : else
430 : {
431 0 : err = gpgme_op_createkey (ctx, userid, algo, 0, expire, NULL, flags);
432 0 : if (err)
433 : {
434 0 : fprintf (stderr, PGM ": gpgme_op_createkey failed: %s\n",
435 : gpg_strerror (err));
436 0 : exit (1);
437 : }
438 : }
439 :
440 0 : if (!setpri)
441 : {
442 0 : result = gpgme_op_genkey_result (ctx);
443 0 : if (!result)
444 : {
445 0 : fprintf (stderr, PGM": gpgme_op_genkey_result returned NULL\n");
446 0 : exit (1);
447 : }
448 :
449 0 : printf ("Generated key: %s (%s)\n",
450 0 : result->fpr ? result->fpr : "none",
451 0 : result->primary ? (result->sub ? "primary, sub" : "primary")
452 0 : /**/ : (result->sub ? "sub" : "none"));
453 :
454 0 : if (result->fpr && strlen (result->fpr) < 40)
455 0 : fprintf (stderr, PGM": generated key has unexpected fingerprint\n");
456 0 : if (!result->primary)
457 0 : fprintf (stderr, PGM": primary key was not generated\n");
458 0 : if (!result->sub)
459 0 : fprintf (stderr, PGM": sub key was not generated\n");
460 0 : if (!result->uid)
461 0 : fprintf (stderr, PGM": uid was not generated\n");
462 : }
463 :
464 0 : gpgme_release (ctx);
465 0 : return 0;
466 : }
|