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