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