* keyedit.c (keyedit_menu, menu_addrevoker): Allow specifying "sensitive"
[gnupg.git] / g10 / revoke.c
1 /* revoke.c
2  * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
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 "options.h"
30 #include "packet.h"
31 #include "errors.h"
32 #include "keydb.h"
33 #include "memory.h"
34 #include "util.h"
35 #include "main.h"
36 #include "ttyio.h"
37 #include "status.h"
38 #include "i18n.h"
39
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 = m_alloc( buflen );
63     *buffer = reason->code;
64     if( ud ) {
65         memcpy(buffer+1, ud, strlen(ud) );
66         m_free( ud );
67     }
68
69     build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen );
70     m_free( buffer );
71     return 0;
72 }
73
74
75
76 /****************
77  * Generate a revocation certificate for UNAME via a designated revoker
78  */
79 int
80 gen_desig_revoke( const char *uname )
81 {
82     int rc = 0;
83     armor_filter_context_t afx;
84     PACKET pkt;
85     PKT_public_key *pk = NULL;
86     PKT_secret_key *sk = NULL;
87     PKT_signature *sig = NULL;
88     IOBUF out = NULL;
89     struct revocation_reason_info *reason = NULL;
90     KEYDB_HANDLE kdbhd;
91     KEYDB_SEARCH_DESC desc;
92     KBNODE keyblock=NULL,node;
93     u32 keyid[2];
94     int i,any=0;
95
96     if( opt.batch ) {
97         log_error(_("sorry, can't do this in batch mode\n"));
98         return G10ERR_GENERAL;
99     }
100
101     memset( &afx, 0, sizeof afx);
102
103     kdbhd = keydb_new (0);
104     classify_user_id (uname, &desc);
105     rc = desc.mode? keydb_search (kdbhd, &desc, 1) : G10ERR_INV_USER_ID;
106     if (rc) {
107         log_error (_("key `%s' not found: %s\n"),uname, g10_errstr (rc));
108         goto leave;
109     }
110
111     rc = keydb_get_keyblock (kdbhd, &keyblock );
112     if( rc ) {
113         log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
114         goto leave;
115     }
116
117     /* To parse the revkeys */
118     merge_keys_and_selfsig(keyblock);
119
120     /* get the key from the keyblock */
121     node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
122     if( !node ) 
123       BUG ();
124
125     pk=node->pkt->pkt.public_key;
126
127     keyid_from_pk(pk,keyid);
128
129     /* Are we a designated revoker for this key? */
130
131     if(!pk->revkey && pk->numrevkeys)
132       BUG();
133
134     for(i=0;i<pk->numrevkeys;i++)
135       {
136         if(sk)
137           free_secret_key(sk);
138
139         sk=m_alloc_clear(sizeof(*sk));
140
141         rc=get_seckey_byfprint(sk,pk->revkey[i].fpr,MAX_FINGERPRINT_LEN);
142
143         /* We have the revocation key */
144         if(!rc)
145           {
146             size_t n;
147             char *p;
148             u32 sk_keyid[2];
149             PKT_user_id *uid=NULL;
150             PKT_signature *selfsig=NULL,*revsig=NULL;
151
152             any=1;
153             keyid_from_sk(sk,sk_keyid);
154
155             tty_printf("\npub  %4u%c/%08lX %s   ",
156                        nbits_from_pk( pk ),
157                        pubkey_letter( pk->pubkey_algo ),
158                        (ulong)keyid[1], datestr_from_pk(pk) );
159
160             p = get_user_id( keyid, &n );
161             tty_print_utf8_string( p, n );
162             m_free(p);
163             tty_printf("\n\n");
164
165             tty_printf(_("To be revoked by:\n"));
166
167             tty_printf("\nsec  %4u%c/%08lX %s   ",
168                        nbits_from_sk( sk ),
169                        pubkey_letter( sk->pubkey_algo ),
170                        (ulong)sk_keyid[1], datestr_from_sk(sk) );
171
172             p = get_user_id( sk_keyid, &n );
173             tty_print_utf8_string( p, n );
174             m_free(p);
175             tty_printf("\n");
176             if(pk->revkey[i].class&0x40)
177               tty_printf(_("(This is a sensitive revocation key)\n"));
178             tty_printf("\n");
179
180             if( !cpr_get_answer_is_yes("gen_desig_revoke.okay",
181                        _("Create a revocation certificate for this key? ")) )
182               continue;
183
184             /* get the reason for the revocation (this is always v4) */
185             reason = ask_revocation_reason( 1, 0, 1 );
186             if( !reason )
187               continue;
188
189             rc = check_secret_key( sk, 0 );
190             if( rc )
191               continue;
192
193             if( !opt.armor )
194               tty_printf(_("ASCII armored output forced.\n"));
195
196             if( (rc = open_outfile( NULL, 0, &out )) )
197               goto leave;
198
199             afx.what = 1;
200             afx.hdrlines = "Comment: A revocation certificate should follow\n";
201             iobuf_push_filter( out, armor_filter, &afx );
202
203             /* create it */
204             rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0,
205                                      0, 0, 0,
206                                      revocation_reason_build_cb, reason );
207             if( rc ) {
208               log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc));
209               goto leave;
210             }
211
212             /* Spit out a minimal pk as well, since otherwise there is
213                no way to know which key to attach this revocation
214                to. */
215
216             node=find_kbnode(keyblock,PKT_PUBLIC_KEY);
217             if(!node)
218               {
219                 rc=G10ERR_GENERAL;
220                 log_error(_("key %08lX incomplete\n"),(ulong)keyid[1]);
221                 goto leave;
222               }
223
224             pkt = *node->pkt;
225             rc=build_packet(out,&pkt);
226             if( rc ) {
227               log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
228               goto leave;
229             }
230
231             /* Include the direct key signature that contains this
232                revocation key.  We're allowed to include sensitive
233                revocation keys along with a revocation, and this may
234                be the only time the recipient has seen it. */
235             while(!revsig)
236               {
237                 KBNODE signode;
238
239                 signode=find_next_kbnode(node,PKT_SIGNATURE);
240                 if(!signode)
241                   break;
242
243                 node=signode;
244
245                 if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
246                    keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
247                    IS_KEY_SIG(signode->pkt->pkt.signature))
248                   {
249                     int j;
250
251                     for(j=0;j<signode->pkt->pkt.signature->numrevkeys;j++)
252                       {
253                         if(pk->revkey[i].class==
254                            signode->pkt->pkt.signature->revkey[j]->class &&
255                            pk->revkey[i].algid==
256                            signode->pkt->pkt.signature->revkey[j]->algid &&
257                            memcmp(pk->revkey[i].fpr,
258                                   signode->pkt->pkt.signature->revkey[j]->fpr,
259                                   MAX_FINGERPRINT_LEN)==0)
260                           {
261                             revsig=signode->pkt->pkt.signature;
262                             break;
263                           }
264                       }
265                   }
266               }
267
268             if(revsig)
269               {
270                 pkt.pkttype = PKT_SIGNATURE;
271                 pkt.pkt.signature = revsig;
272
273                 rc = build_packet( out, &pkt );
274                 if( rc ) {
275                   log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
276                   goto leave;
277                 }
278               }
279             else
280               BUG();
281
282             init_packet( &pkt );
283             pkt.pkttype = PKT_SIGNATURE;
284             pkt.pkt.signature = sig;
285
286             rc = build_packet( out, &pkt );
287             if( rc ) {
288               log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
289               goto leave;
290             }
291
292             while(!selfsig)
293               {
294                 KBNODE signode;
295
296                 node=find_next_kbnode(node,PKT_USER_ID);
297                 if(!node)
298                   {
299                     /* We're out of user IDs - none were
300                        self-signed. */
301                     if(uid)
302                       break;
303                     else
304                       {
305                         rc=G10ERR_GENERAL;
306                         log_error(_("key %08lX incomplete\n"),(ulong)keyid[1]);
307                         goto leave;
308                       }
309                   }
310
311                 if(node->pkt->pkt.user_id->attrib_data)
312                   continue;
313
314                 uid=node->pkt->pkt.user_id;
315                 signode=node;
316
317                 while((signode=find_next_kbnode(signode,PKT_SIGNATURE)))
318                   {
319                     if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
320                        keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
321                        IS_UID_SIG(signode->pkt->pkt.signature))
322                       {
323                         selfsig=signode->pkt->pkt.signature;
324                         break;
325                       }
326                   }
327               }
328
329             pkt.pkttype = PKT_USER_ID;
330             pkt.pkt.user_id = uid;
331
332             rc = build_packet( out, &pkt );
333             if( rc ) {
334               log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
335               goto leave;
336             }
337
338             if(selfsig)
339               {
340                 pkt.pkttype = PKT_SIGNATURE;
341                 pkt.pkt.signature = selfsig;
342
343                 rc = build_packet( out, &pkt );
344                 if( rc ) {
345                   log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
346                   goto leave;
347                 }
348               }
349
350             /* and issue a usage notice */
351             tty_printf(_("Revocation certificate created.\n"));
352             break;
353           }
354       }
355
356     if(!any)
357       log_error(_("no revocation keys found for `%s'\n"),uname);
358
359   leave:
360     if( pk )
361         free_public_key( pk );
362     if( sk )
363         free_secret_key( sk );
364     if( sig )
365         free_seckey_enc( sig );
366
367     if( rc )
368         iobuf_cancel(out);
369     else
370         iobuf_close(out);
371     release_revocation_reason_info( reason );
372     return rc;
373 }
374
375
376 /****************
377  * Generate a revocation certificate for UNAME
378  */
379 int
380 gen_revoke( const char *uname )
381 {
382     int rc = 0;
383     armor_filter_context_t afx;
384     PACKET pkt;
385     PKT_secret_key *sk; /* used as pointer into a kbnode */
386     PKT_public_key *pk = NULL;
387     PKT_signature *sig = NULL;
388     u32 sk_keyid[2];
389     IOBUF out = NULL;
390     KBNODE keyblock = NULL;
391     KBNODE node;
392     KEYDB_HANDLE kdbhd;
393     struct revocation_reason_info *reason = NULL;
394     KEYDB_SEARCH_DESC desc;
395
396     if( opt.batch ) {
397         log_error(_("sorry, can't do this in batch mode\n"));
398         return G10ERR_GENERAL;
399     }
400
401     memset( &afx, 0, sizeof afx);
402     init_packet( &pkt );
403
404     /* search the userid: 
405      * We don't want the whole getkey stuff here but the entire keyblock
406      */
407     kdbhd = keydb_new (1);
408     classify_user_id (uname, &desc);
409     rc = desc.mode? keydb_search (kdbhd, &desc, 1) : G10ERR_INV_USER_ID;
410     if (rc) {
411         log_error (_("secret key `%s' not found: %s\n"),
412                    uname, g10_errstr (rc));
413         goto leave;
414     }
415
416     rc = keydb_get_keyblock (kdbhd, &keyblock );
417     if( rc ) {
418         log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
419         goto leave;
420     }
421
422     /* get the keyid from the keyblock */
423     node = find_kbnode( keyblock, PKT_SECRET_KEY );
424     if( !node ) 
425         BUG ();
426
427     /* fixme: should make a function out of this stuff,
428      * it's used all over the source */
429     sk = node->pkt->pkt.secret_key;
430     keyid_from_sk( sk, sk_keyid );
431     tty_printf("\nsec  %4u%c/%08lX %s   ",
432               nbits_from_sk( sk ),
433               pubkey_letter( sk->pubkey_algo ),
434               (ulong)sk_keyid[1], datestr_from_sk(sk) );
435     {
436         size_t n;
437         char *p = get_user_id( sk_keyid, &n );
438         tty_print_utf8_string( p, n );
439         m_free(p);
440         tty_printf("\n");
441     }
442     pk = m_alloc_clear( sizeof *pk );
443
444     /* FIXME: We should get the public key direct from the secret one */
445     rc = get_pubkey( pk, sk_keyid );
446     if( rc ) {
447         log_error(_("no corresponding public key: %s\n"), g10_errstr(rc) );
448         goto leave;
449     }
450     if( cmp_public_secret_key( pk, sk ) ) {
451         log_error(_("public key does not match secret key!\n") );
452         rc = G10ERR_GENERAL;
453         goto leave;
454     }
455
456     tty_printf("\n");
457     if( !cpr_get_answer_is_yes("gen_revoke.okay",
458                         _("Create a revocation certificate for this key? ")) ){
459         rc = 0;
460         goto leave;
461     }
462
463     if(sk->version>=4 || opt.force_v4_certs) {
464       /* get the reason for the revocation */
465       reason = ask_revocation_reason( 1, 0, 1 );
466       if( !reason ) { /* user decided to cancel */
467         rc = 0;
468         goto leave;
469       }
470     }
471
472     switch( is_secret_key_protected( sk ) ) {
473       case -1:
474         log_error(_("unknown protection algorithm\n"));
475         rc = G10ERR_PUBKEY_ALGO;
476         break;
477       case 0:
478         tty_printf(_("NOTE: This key is not protected!\n"));
479         break;
480       default:
481         rc = check_secret_key( sk, 0 );
482         break;
483     }
484     if( rc )
485         goto leave;
486
487
488     if( !opt.armor )
489         tty_printf(_("ASCII armored output forced.\n"));
490
491     if( (rc = open_outfile( NULL, 0, &out )) )
492         goto leave;
493
494     afx.what = 1;
495     afx.hdrlines = "Comment: A revocation certificate should follow\n";
496     iobuf_push_filter( out, armor_filter, &afx );
497
498     /* create it */
499     rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0,
500                              opt.force_v4_certs?4:0, 0, 0,
501                              revocation_reason_build_cb, reason );
502     if( rc ) {
503         log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc));
504         goto leave;
505     }
506     init_packet( &pkt );
507     pkt.pkttype = PKT_SIGNATURE;
508     pkt.pkt.signature = sig;
509
510     rc = build_packet( out, &pkt );
511     if( rc ) {
512         log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
513         goto leave;
514     }
515
516     /* and issue a usage notice */
517     tty_printf(_("Revocation certificate created.\n\n"
518 "Please move it to a medium which you can hide away; if Mallory gets\n"
519 "access to this certificate he can use it to make your key unusable.\n"
520 "It is smart to print this certificate and store it away, just in case\n"
521 "your media become unreadable.  But have some caution:  The print system of\n"
522 "your machine might store the data and make it available to others!\n"));
523
524
525
526   leave:
527     if( pk )
528         free_public_key( pk );
529     if( sig )
530         free_seckey_enc( sig );
531     release_kbnode( keyblock );
532     keydb_release (kdbhd);
533     if( rc )
534         iobuf_cancel(out);
535     else
536         iobuf_close(out);
537     release_revocation_reason_info( reason );
538     return rc;
539 }
540
541
542
543 struct revocation_reason_info *
544 ask_revocation_reason( int key_rev, int cert_rev, int hint )
545 {
546     int code=-1;
547     char *description = NULL;
548     struct revocation_reason_info *reason;
549     const char *text_0 = _("No reason specified");
550     const char *text_1 = _("Key has been compromised");
551     const char *text_2 = _("Key is superseded");
552     const char *text_3 = _("Key is no longer used");
553     const char *text_4 = _("User ID is no longer valid");
554     const char *code_text = NULL;
555
556     do {
557         m_free(description);
558         description = NULL;
559
560         tty_printf(_("Please select the reason for the revocation:\n"));
561         tty_printf(    "  0 = %s\n", text_0 );
562         if( key_rev )
563             tty_printf("  1 = %s\n", text_1 );
564         if( key_rev )
565             tty_printf("  2 = %s\n", text_2 );
566         if( key_rev )
567             tty_printf("  3 = %s\n", text_3 );
568         if( cert_rev )
569             tty_printf("  4 = %s\n", text_4 );
570         tty_printf(    "  Q = %s\n", _("Cancel") );
571         if( hint )
572             tty_printf(_("(Probably you want to select %d here)\n"), hint );
573
574         while(code==-1) {
575             int n;
576             char *answer = cpr_get("ask_revocation_reason.code",
577                                                 _("Your decision? "));
578             trim_spaces( answer );
579             cpr_kill_prompt();
580             if( *answer == 'q' || *answer == 'Q')
581               return NULL; /* cancel */
582             if( hint && !*answer )
583                 n = hint;
584             else if(!isdigit( *answer ) )
585                 n = -1;
586             else
587                 n = atoi(answer);
588             m_free(answer);
589             if( n == 0 ) {
590                 code = 0x00; /* no particular reason */
591                 code_text = text_0;
592             }
593             else if( key_rev && n == 1 ) {
594                 code = 0x02; /* key has been compromised */
595                 code_text = text_1;
596             }
597             else if( key_rev && n == 2 ) {
598                 code = 0x01; /* key is superseded */
599                 code_text = text_2;
600             }
601             else if( key_rev && n == 3 ) {
602                 code = 0x03; /* key is no longer used */
603                 code_text = text_3;
604             }
605             else if( cert_rev && n == 4 ) {
606                 code = 0x20; /* uid is no longer valid */
607                 code_text = text_4;
608             }
609             else
610                 tty_printf(_("Invalid selection.\n"));
611         }
612
613         tty_printf(_("Enter an optional description; "
614                      "end it with an empty line:\n") );
615         for(;;) {
616             char *answer = cpr_get("ask_revocation_reason.text", "> " );
617             trim_trailing_ws( answer, strlen(answer) );
618             cpr_kill_prompt();
619             if( !*answer ) {
620                 m_free(answer);
621                 break;
622             }
623
624             {
625                 char *p = make_printable_string( answer, strlen(answer), 0 );
626                 m_free(answer);
627                 answer = p;
628             }
629
630             if( !description )
631                 description = m_strdup(answer);
632             else {
633                 char *p = m_alloc( strlen(description) + strlen(answer) + 2 );
634                 strcpy(stpcpy(stpcpy( p, description),"\n"),answer);
635                 m_free(description);
636                 description = p;
637             }
638             m_free(answer);
639         }
640
641         tty_printf(_("Reason for revocation: %s\n"), code_text );
642         if( !description )
643             tty_printf(_("(No description given)\n") );
644         else
645             tty_printf("%s\n", description );
646
647     } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay",
648                                             _("Is this okay? "))  );
649
650     reason = m_alloc( sizeof *reason );
651     reason->code = code;
652     reason->desc = description;
653     return reason;
654 }
655
656 void
657 release_revocation_reason_info( struct revocation_reason_info *reason )
658 {
659     if( reason ) {
660         m_free( reason->desc );
661         m_free( reason );
662     }
663 }