chiark / gitweb /
Replace use of variable-length-arrays.
[gnupg2.git] / g10 / revoke.c
1 /* revoke.c - Create recovation certificates.
2  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
3  *               2004 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <https://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <ctype.h>
27
28 #include "gpg.h"
29 #include "options.h"
30 #include "packet.h"
31 #include "status.h"
32 #include "keydb.h"
33 #include "util.h"
34 #include "main.h"
35 #include "ttyio.h"
36 #include "status.h"
37 #include "i18n.h"
38 #include "call-agent.h"
39
40 struct revocation_reason_info {
41     int code;
42     char *desc;
43 };
44
45
46 int
47 revocation_reason_build_cb( PKT_signature *sig, void *opaque )
48 {
49     struct revocation_reason_info *reason = opaque;
50     char *ud = NULL;
51     byte *buffer;
52     size_t buflen = 1;
53
54     if(!reason)
55       return 0;
56
57     if( reason->desc ) {
58         ud = native_to_utf8( reason->desc );
59         buflen += strlen(ud);
60     }
61     buffer = xmalloc( buflen );
62     *buffer = reason->code;
63     if( ud ) {
64         memcpy(buffer+1, ud, strlen(ud) );
65         xfree( ud );
66     }
67
68     build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen );
69     xfree( buffer );
70     return 0;
71 }
72
73 /* Outputs a minimal pk (as defined by 2440) from a keyblock.  A
74    minimal pk consists of the public key packet and a user ID.  We try
75    and pick a user ID that has a uid signature, and include it if
76    possible. */
77 static int
78 export_minimal_pk(IOBUF out,KBNODE keyblock,
79                   PKT_signature *revsig,PKT_signature *revkey)
80 {
81   KBNODE node;
82   PACKET pkt;
83   PKT_user_id *uid=NULL;
84   PKT_signature *selfsig=NULL;
85   u32 keyid[2];
86   int rc;
87
88   node=find_kbnode(keyblock,PKT_PUBLIC_KEY);
89   if(!node)
90     {
91       log_error("key incomplete\n");
92       return GPG_ERR_GENERAL;
93     }
94
95   keyid_from_pk(node->pkt->pkt.public_key,keyid);
96
97   pkt=*node->pkt;
98   rc=build_packet(out,&pkt);
99   if(rc)
100     {
101       log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) );
102       return rc;
103     }
104
105   init_packet(&pkt);
106   pkt.pkttype=PKT_SIGNATURE;
107
108   /* the revocation itself, if any.  2440 likes this to come first. */
109   if(revsig)
110     {
111       pkt.pkt.signature=revsig;
112       rc=build_packet(out,&pkt);
113       if(rc)
114         {
115           log_error("build_packet failed: %s\n", gpg_strerror (rc) );
116           return rc;
117         }
118     }
119
120   /* If a revkey in a 1F sig is present, include it too */
121   if(revkey)
122     {
123       pkt.pkt.signature=revkey;
124       rc=build_packet(out,&pkt);
125       if(rc)
126         {
127           log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) );
128           return rc;
129         }
130     }
131
132   while(!selfsig)
133     {
134       KBNODE signode;
135
136       node=find_next_kbnode(node,PKT_USER_ID);
137       if(!node)
138         {
139           /* We're out of user IDs - none were self-signed. */
140           if(uid)
141             break;
142           else
143             {
144               log_error(_("key %s has no user IDs\n"),keystr(keyid));
145               return GPG_ERR_GENERAL;
146             }
147         }
148
149       if(node->pkt->pkt.user_id->attrib_data)
150         continue;
151
152       uid=node->pkt->pkt.user_id;
153       signode=node;
154
155       while((signode=find_next_kbnode(signode,PKT_SIGNATURE)))
156         {
157           if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
158              keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
159              IS_UID_SIG(signode->pkt->pkt.signature))
160             {
161               selfsig=signode->pkt->pkt.signature;
162               break;
163             }
164         }
165     }
166
167   pkt.pkttype=PKT_USER_ID;
168   pkt.pkt.user_id=uid;
169
170   rc=build_packet(out,&pkt);
171   if(rc)
172     {
173       log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) );
174       return rc;
175     }
176
177   if(selfsig)
178     {
179       pkt.pkttype=PKT_SIGNATURE;
180       pkt.pkt.signature=selfsig;
181
182       rc=build_packet(out,&pkt);
183       if(rc)
184         {
185           log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) );
186           return rc;
187         }
188     }
189
190   return 0;
191 }
192
193 /****************
194  * Generate a revocation certificate for UNAME via a designated revoker
195  */
196 int
197 gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr)
198 {
199     int rc = 0;
200     armor_filter_context_t *afx;
201     PKT_public_key *pk = NULL;
202     PKT_public_key *pk2 = NULL;
203     PKT_signature *sig = NULL;
204     IOBUF out = NULL;
205     struct revocation_reason_info *reason = NULL;
206     KEYDB_HANDLE kdbhd;
207     KEYDB_SEARCH_DESC desc;
208     KBNODE keyblock=NULL,node;
209     u32 keyid[2];
210     int i,any=0;
211     SK_LIST sk_list=NULL;
212
213     if( opt.batch )
214       {
215         log_error(_("can't do this in batch mode\n"));
216         return GPG_ERR_GENERAL;
217       }
218
219     afx = new_armor_context ();
220
221     kdbhd = keydb_new ();
222     if (!kdbhd)
223       {
224         rc = gpg_error_from_syserror ();
225         goto leave;
226       }
227     rc = classify_user_id (uname, &desc, 1);
228     if (!rc)
229       rc = keydb_search (kdbhd, &desc, 1, NULL);
230     if (rc) {
231         log_error (_("key \"%s\" not found: %s\n"),uname, gpg_strerror (rc));
232         goto leave;
233     }
234
235     rc = keydb_get_keyblock (kdbhd, &keyblock );
236     if( rc ) {
237         log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) );
238         goto leave;
239     }
240
241     /* To parse the revkeys */
242     merge_keys_and_selfsig(keyblock);
243
244     /* get the key from the keyblock */
245     node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
246     if( !node )
247       BUG ();
248
249     pk=node->pkt->pkt.public_key;
250
251     keyid_from_pk(pk,keyid);
252
253     if(locusr)
254       {
255         rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_CERT);
256         if(rc)
257           goto leave;
258       }
259
260     /* Are we a designated revoker for this key? */
261
262     if(!pk->revkey && pk->numrevkeys)
263       BUG();
264
265     for(i=0;i<pk->numrevkeys;i++)
266       {
267         SK_LIST list;
268
269         free_public_key (pk2);
270         pk2 = NULL;
271
272         if(sk_list)
273           {
274             for(list=sk_list;list;list=list->next)
275               {
276                 byte fpr[MAX_FINGERPRINT_LEN];
277                 size_t fprlen;
278
279                 fingerprint_from_pk (list->pk, fpr, &fprlen);
280
281                 /* Don't get involved with keys that don't have 160
282                    bit fingerprints */
283                 if(fprlen!=20)
284                   continue;
285
286                 if(memcmp(fpr,pk->revkey[i].fpr,20)==0)
287                   break;
288               }
289
290             if (list)
291               pk2 = copy_public_key (NULL, list->pk);
292             else
293               continue;
294           }
295         else
296           {
297             pk2 = xmalloc_clear (sizeof *pk2);
298             rc = get_pubkey_byfprint (pk2, NULL,
299                                       pk->revkey[i].fpr, MAX_FINGERPRINT_LEN);
300           }
301
302         /* We have the revocation key.  */
303         if(!rc)
304           {
305             PKT_signature *revkey = NULL;
306
307             any = 1;
308
309             print_pubkey_info (NULL, pk);
310             tty_printf ("\n");
311
312             tty_printf (_("To be revoked by:\n"));
313             print_seckey_info (pk2);
314
315             if(pk->revkey[i].class&0x40)
316               tty_printf(_("(This is a sensitive revocation key)\n"));
317             tty_printf("\n");
318
319             rc = agent_probe_secret_key (ctrl, pk2);
320             if (rc)
321               {
322                 tty_printf (_("Secret key is not available.\n"));
323                 continue;
324               }
325
326             if( !cpr_get_answer_is_yes("gen_desig_revoke.okay",
327          _("Create a designated revocation certificate for this key? (y/N) ")))
328               continue;
329
330             /* get the reason for the revocation (this is always v4) */
331             reason = ask_revocation_reason( 1, 0, 1 );
332             if( !reason )
333               continue;
334
335             if( !opt.armor )
336               tty_printf(_("ASCII armored output forced.\n"));
337
338             if( (rc = open_outfile (-1, NULL, 0, 1, &out )) )
339               goto leave;
340
341             afx->what = 1;
342             afx->hdrlines = "Comment: A designated revocation certificate"
343               " should follow\n";
344             push_armor_filter (afx, out);
345
346             /* create it */
347             rc = make_keysig_packet( &sig, pk, NULL, NULL, pk2, 0x20, 0,
348                                      0, 0,
349                                      revocation_reason_build_cb, reason,
350                                      NULL);
351             if( rc ) {
352               log_error(_("make_keysig_packet failed: %s\n"), gpg_strerror (rc));
353               goto leave;
354             }
355
356             /* Spit out a minimal pk as well, since otherwise there is
357                no way to know which key to attach this revocation to.
358                Also include the direct key signature that contains
359                this revocation key.  We're allowed to include
360                sensitive revocation keys along with a revocation, as
361                this may be the only time the recipient has seen it.
362                Note that this means that if we have multiple different
363                sensitive revocation keys in a given direct key
364                signature, we're going to include them all here.  This
365                is annoying, but the good outweighs the bad, since
366                without including this a sensitive revoker can't really
367                do their job.  People should not include multiple
368                sensitive revocation keys in one signature: 2440 says
369                "Note that it may be appropriate to isolate this
370                subpacket within a separate signature so that it is not
371                combined with other subpackets that need to be
372                exported." -dms */
373
374             while(!revkey)
375               {
376                 KBNODE signode;
377
378                 signode=find_next_kbnode(node,PKT_SIGNATURE);
379                 if(!signode)
380                   break;
381
382                 node=signode;
383
384                 if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
385                    keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
386                    IS_KEY_SIG(signode->pkt->pkt.signature))
387                   {
388                     int j;
389
390                     for(j=0;j<signode->pkt->pkt.signature->numrevkeys;j++)
391                       {
392                         if(pk->revkey[i].class==
393                            signode->pkt->pkt.signature->revkey[j].class &&
394                            pk->revkey[i].algid==
395                            signode->pkt->pkt.signature->revkey[j].algid &&
396                            memcmp(pk->revkey[i].fpr,
397                                   signode->pkt->pkt.signature->revkey[j].fpr,
398                                   MAX_FINGERPRINT_LEN)==0)
399                           {
400                             revkey=signode->pkt->pkt.signature;
401                             break;
402                           }
403                       }
404                   }
405               }
406
407             if(!revkey)
408               BUG();
409
410             rc=export_minimal_pk(out,keyblock,sig,revkey);
411             if(rc)
412               goto leave;
413
414             /* and issue a usage notice */
415             tty_printf(_("Revocation certificate created.\n"));
416             break;
417           }
418       }
419
420     if(!any)
421       log_error(_("no revocation keys found for \"%s\"\n"),uname);
422
423   leave:
424     free_public_key (pk);
425     free_public_key (pk2);
426     if( sig )
427         free_seckey_enc( sig );
428
429     release_sk_list(sk_list);
430
431     if( rc )
432         iobuf_cancel(out);
433     else
434         iobuf_close(out);
435     release_revocation_reason_info( reason );
436     release_armor_context (afx);
437     return rc;
438 }
439
440
441 /* Common core to create the revocation. FILENAME may be NULL to write
442    to stdout or the filename given by --output.  REASON describes the
443    revocation reason.  PSK is the public primary key - we expect that
444    a corresponding secret key is available.  KEYBLOCK is the entire
445    KEYBLOCK which is used in PGP mode to write a a minimal key and not
446    just the naked revocation signature; it may be NULL.  If LEADINTEXT
447    is not NULL, it is written right before the (armored) output.*/
448 static int
449 create_revocation (const char *filename,
450                    struct revocation_reason_info *reason,
451                    PKT_public_key *psk,
452                    kbnode_t keyblock,
453                    const char *leadintext, int suffix,
454                    const char *cache_nonce)
455 {
456   int rc;
457   iobuf_t out = NULL;
458   armor_filter_context_t *afx;
459   PKT_signature *sig = NULL;
460   PACKET pkt;
461
462   afx = new_armor_context ();
463
464   if ((rc = open_outfile (-1, filename, suffix, 1, &out)))
465     goto leave;
466
467   if (leadintext )
468     iobuf_writestr (out, leadintext);
469
470   afx->what = 1;
471   afx->hdrlines = "Comment: This is a revocation certificate\n";
472   push_armor_filter (afx, out);
473
474   rc = make_keysig_packet (&sig, psk, NULL, NULL, psk, 0x20, 0,
475                            0, 0,
476                            revocation_reason_build_cb, reason, cache_nonce);
477   if (rc)
478     {
479       log_error (_("make_keysig_packet failed: %s\n"), gpg_strerror (rc));
480       goto leave;
481     }
482
483   if (keyblock && (PGP6 || PGP7 || PGP8))
484     {
485       /* Use a minimal pk for PGPx mode, since PGP can't import bare
486          revocation certificates. */
487       rc = export_minimal_pk (out, keyblock, sig, NULL);
488       if (rc)
489         goto leave;
490     }
491   else
492     {
493       init_packet (&pkt);
494       pkt.pkttype = PKT_SIGNATURE;
495       pkt.pkt.signature = sig;
496
497       rc = build_packet (out, &pkt);
498       if (rc)
499         {
500           log_error (_("build_packet failed: %s\n"), gpg_strerror (rc));
501           goto leave;
502         }
503     }
504
505  leave:
506   if (sig)
507     free_seckey_enc (sig);
508   if (rc)
509     iobuf_cancel (out);
510   else
511     iobuf_close (out);
512   release_armor_context (afx);
513   return rc;
514 }
515
516
517 /* This function is used to generate a standard revocation certificate
518    by gpg's interactive key generation function.  The certificate is
519    stored at a dedicated place in a slightly modified form to avoid an
520    accidental import.  PSK is the primary key; a corresponding secret
521    key must be available.  CACHE_NONCE is optional but can be used to
522    help gpg-agent to avoid an extra passphrase prompt. */
523 int
524 gen_standard_revoke (PKT_public_key *psk, const char *cache_nonce)
525 {
526   int rc;
527   estream_t memfp;
528   struct revocation_reason_info reason;
529   char *dir, *tmpstr, *fname;
530   void *leadin;
531   size_t len;
532   u32 keyid[2];
533   int kl;
534   char *orig_codeset;
535
536   dir = get_openpgp_revocdir (gnupg_homedir ());
537   tmpstr = hexfingerprint (psk, NULL, 0);
538   fname = xstrconcat (dir, DIRSEP_S, tmpstr, NULL);
539   xfree (tmpstr);
540   xfree (dir);
541
542   keyid_from_pk (psk, keyid);
543
544   memfp = es_fopenmem (0, "r+");
545   if (!memfp)
546     log_fatal ("error creating memory stream\n");
547
548   orig_codeset = i18n_switchto_utf8 ();
549
550   es_fprintf (memfp, "%s\n\n",
551               _("This is a revocation certificate for the OpenPGP key:"));
552
553   print_key_line (memfp, psk, 0);
554
555   if (opt.keyid_format != KF_NONE)
556     print_fingerprint (memfp, psk, 3);
557
558   kl = opt.keyid_format == KF_NONE? 0 : keystrlen ();
559
560   tmpstr = get_user_id (keyid, &len);
561   es_fprintf (memfp, "uid%*s%.*s\n\n",
562               kl + 10, "",
563               (int)len, tmpstr);
564   xfree (tmpstr);
565
566   es_fprintf (memfp, "%s\n\n%s\n\n%s\n\n:",
567      _("A revocation certificate is a kind of \"kill switch\" to publicly\n"
568        "declare that a key shall not anymore be used.  It is not possible\n"
569        "to retract such a revocation certificate once it has been published."),
570      _("Use it to revoke this key in case of a compromise or loss of\n"
571        "the secret key.  However, if the secret key is still accessible,\n"
572        "it is better to generate a new revocation certificate and give\n"
573        "a reason for the revocation.  For details see the description of\n"
574        "of the gpg command \"--generate-revocation\" in the "
575        "GnuPG manual."),
576      _("To avoid an accidental use of this file, a colon has been inserted\n"
577        "before the 5 dashes below.  Remove this colon with a text editor\n"
578        "before importing and publishing this revocation certificate."));
579
580   es_putc (0, memfp);
581
582   i18n_switchback (orig_codeset);
583
584   if (es_fclose_snatch (memfp, &leadin, NULL))
585     log_fatal ("error snatching memory stream\n");
586
587   reason.code = 0x00; /* No particular reason.  */
588   reason.desc = NULL;
589   rc = create_revocation (fname, &reason, psk, NULL, leadin, 3, cache_nonce);
590   if (!rc && !opt.quiet)
591     log_info (_("revocation certificate stored as '%s.rev'\n"), fname);
592
593   xfree (leadin);
594   xfree (fname);
595
596   return rc;
597 }
598
599
600
601 /****************
602  * Generate a revocation certificate for UNAME
603  */
604 int
605 gen_revoke (const char *uname)
606 {
607   int rc = 0;
608   PKT_public_key *psk;
609   u32 keyid[2];
610   kbnode_t keyblock = NULL;
611   kbnode_t node;
612   KEYDB_HANDLE kdbhd;
613   struct revocation_reason_info *reason = NULL;
614   KEYDB_SEARCH_DESC desc;
615
616   if( opt.batch )
617     {
618       log_error(_("can't do this in batch mode\n"));
619       return GPG_ERR_GENERAL;
620     }
621
622   /* Search the userid; we don't want the whole getkey stuff here.  */
623   kdbhd = keydb_new ();
624   if (!kdbhd)
625     {
626       rc = gpg_error_from_syserror ();
627       goto leave;
628     }
629   rc = classify_user_id (uname, &desc, 1);
630   if (!rc)
631     rc = keydb_search (kdbhd, &desc, 1, NULL);
632   if (rc)
633     {
634       if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
635         log_error (_("secret key \"%s\" not found\n"), uname);
636       else
637         log_error (_("secret key \"%s\" not found: %s\n"),
638                    uname, gpg_strerror (rc));
639       goto leave;
640     }
641
642   rc = keydb_get_keyblock (kdbhd, &keyblock );
643   if (rc)
644     {
645       log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) );
646       goto leave;
647     }
648
649   rc = keydb_search (kdbhd, &desc, 1, NULL);
650   if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
651     /* Not ambiguous.  */
652     {
653     }
654   else if (rc == 0)
655     /* Ambiguous.  */
656     {
657       char *info;
658
659       /* TRANSLATORS: The %s prints a key specification which
660          for example has been given at the command line.  Several lines
661          lines with secret key infos are printed after this message.  */
662       log_error (_("'%s' matches multiple secret keys:\n"), uname);
663
664       info = format_seckey_info (keyblock->pkt->pkt.public_key);
665       log_error ("  %s\n", info);
666       xfree (info);
667       release_kbnode (keyblock);
668
669       rc = keydb_get_keyblock (kdbhd, &keyblock);
670       while (! rc)
671         {
672           info = format_seckey_info (keyblock->pkt->pkt.public_key);
673           log_info ("  %s\n", info);
674           xfree (info);
675           release_kbnode (keyblock);
676           keyblock = NULL;
677
678           rc = keydb_search (kdbhd, &desc, 1, NULL);
679           if (! rc)
680             rc = keydb_get_keyblock (kdbhd, &keyblock);
681         }
682
683       rc = GPG_ERR_AMBIGUOUS_NAME;
684
685       goto leave;
686     }
687   else
688     {
689       log_error (_("error searching the keyring: %s\n"), gpg_strerror (rc));
690       goto leave;
691     }
692
693   /* Get the keyid from the keyblock.  */
694   node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
695   if (!node)
696     BUG ();
697
698   psk = node->pkt->pkt.public_key;
699   rc = agent_probe_secret_key (NULL, psk);
700   if (rc)
701     {
702       log_error (_("secret key \"%s\" not found: %s\n"),
703                  uname, gpg_strerror (rc));
704       goto leave;
705     }
706
707   keyid_from_pk (psk, keyid );
708   print_seckey_info (psk);
709
710   tty_printf("\n");
711   if (!cpr_get_answer_is_yes ("gen_revoke.okay",
712                 _("Create a revocation certificate for this key? (y/N) ")))
713     {
714       rc = 0;
715       goto leave;
716     }
717
718   /* Get the reason for the revocation.  */
719   reason = ask_revocation_reason (1, 0, 1);
720   if (!reason)
721     {
722       /* User decided to cancel.  */
723       rc = 0;
724       goto leave;
725     }
726
727   if (!opt.armor)
728     tty_printf (_("ASCII armored output forced.\n"));
729
730   rc = create_revocation (NULL, reason, psk, keyblock, NULL, 0, NULL);
731   if (rc)
732     goto leave;
733
734   /* and issue a usage notice */
735   tty_printf (_(
736 "Revocation certificate created.\n\n"
737 "Please move it to a medium which you can hide away; if Mallory gets\n"
738 "access to this certificate he can use it to make your key unusable.\n"
739 "It is smart to print this certificate and store it away, just in case\n"
740 "your media become unreadable.  But have some caution:  The print system of\n"
741 "your machine might store the data and make it available to others!\n"));
742
743  leave:
744   release_kbnode (keyblock);
745   keydb_release (kdbhd);
746   release_revocation_reason_info( reason );
747   return rc;
748 }
749
750
751
752 struct revocation_reason_info *
753 ask_revocation_reason( int key_rev, int cert_rev, int hint )
754 {
755     int code=-1;
756     char *description = NULL;
757     struct revocation_reason_info *reason;
758     const char *text_0 = _("No reason specified");
759     const char *text_1 = _("Key has been compromised");
760     const char *text_2 = _("Key is superseded");
761     const char *text_3 = _("Key is no longer used");
762     const char *text_4 = _("User ID is no longer valid");
763     const char *code_text = NULL;
764
765     do {
766         code=-1;
767         xfree(description);
768         description = NULL;
769
770         tty_printf(_("Please select the reason for the revocation:\n"));
771         tty_printf(    "  0 = %s\n", text_0 );
772         if( key_rev )
773             tty_printf("  1 = %s\n", text_1 );
774         if( key_rev )
775             tty_printf("  2 = %s\n", text_2 );
776         if( key_rev )
777             tty_printf("  3 = %s\n", text_3 );
778         if( cert_rev )
779             tty_printf("  4 = %s\n", text_4 );
780         tty_printf(    "  Q = %s\n", _("Cancel") );
781         if( hint )
782             tty_printf(_("(Probably you want to select %d here)\n"), hint );
783
784         while(code==-1) {
785             int n;
786             char *answer = cpr_get("ask_revocation_reason.code",
787                                                 _("Your decision? "));
788             trim_spaces( answer );
789             cpr_kill_prompt();
790             if( *answer == 'q' || *answer == 'Q')
791               return NULL; /* cancel */
792             if( hint && !*answer )
793                 n = hint;
794             else if(!digitp( answer ) )
795                 n = -1;
796             else
797                 n = atoi(answer);
798             xfree(answer);
799             if( n == 0 ) {
800                 code = 0x00; /* no particular reason */
801                 code_text = text_0;
802             }
803             else if( key_rev && n == 1 ) {
804                 code = 0x02; /* key has been compromised */
805                 code_text = text_1;
806             }
807             else if( key_rev && n == 2 ) {
808                 code = 0x01; /* key is superseded */
809                 code_text = text_2;
810             }
811             else if( key_rev && n == 3 ) {
812                 code = 0x03; /* key is no longer used */
813                 code_text = text_3;
814             }
815             else if( cert_rev && n == 4 ) {
816                 code = 0x20; /* uid is no longer valid */
817                 code_text = text_4;
818             }
819             else
820                 tty_printf(_("Invalid selection.\n"));
821         }
822
823         tty_printf(_("Enter an optional description; "
824                      "end it with an empty line:\n") );
825         for(;;) {
826             char *answer = cpr_get("ask_revocation_reason.text", "> " );
827             trim_trailing_ws( answer, strlen(answer) );
828             cpr_kill_prompt();
829             if( !*answer ) {
830                 xfree(answer);
831                 break;
832             }
833
834             {
835                 char *p = make_printable_string( answer, strlen(answer), 0 );
836                 xfree(answer);
837                 answer = p;
838             }
839
840             if( !description )
841                 description = xstrdup(answer);
842             else {
843                 char *p = xmalloc( strlen(description) + strlen(answer) + 2 );
844                 strcpy(stpcpy(stpcpy( p, description),"\n"),answer);
845                 xfree(description);
846                 description = p;
847             }
848             xfree(answer);
849         }
850
851         tty_printf(_("Reason for revocation: %s\n"), code_text );
852         if( !description )
853             tty_printf(_("(No description given)\n") );
854         else
855             tty_printf("%s\n", description );
856
857     } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay",
858                                             _("Is this okay? (y/N) "))  );
859
860     reason = xmalloc( sizeof *reason );
861     reason->code = code;
862     reason->desc = description;
863     return reason;
864 }
865
866 struct revocation_reason_info *
867 get_default_uid_revocation_reason(void)
868 {
869   struct revocation_reason_info *reason;
870   reason = xmalloc( sizeof *reason );
871   reason->code = 0x20; /* uid is no longer valid */
872   reason->desc = strdup(""); /* no text */
873   return reason;
874 }
875
876 void
877 release_revocation_reason_info( struct revocation_reason_info *reason )
878 {
879     if( reason ) {
880         xfree( reason->desc );
881         xfree( reason );
882     }
883 }