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