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