Forgot to commit this:
[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.  Note that
235                this means that if we have multiple different sensitive
236                revocation keys in a given direct key signature, we're
237                going to include them all here.  This is annoying, but
238                the good outweighs the bad, since without including
239                this a sensitive revoker can't really do their job.
240                People should not include multiple sensitive revocation
241                keys in one signature: 2440 says "Note that it may be
242                appropriate to isolate this subpacket within a separate
243                signature so that it is not combined with other
244                subpackets that need to be exported." -dms */
245
246             while(!revsig)
247               {
248                 KBNODE signode;
249
250                 signode=find_next_kbnode(node,PKT_SIGNATURE);
251                 if(!signode)
252                   break;
253
254                 node=signode;
255
256                 if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
257                    keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
258                    IS_KEY_SIG(signode->pkt->pkt.signature))
259                   {
260                     int j;
261
262                     for(j=0;j<signode->pkt->pkt.signature->numrevkeys;j++)
263                       {
264                         if(pk->revkey[i].class==
265                            signode->pkt->pkt.signature->revkey[j]->class &&
266                            pk->revkey[i].algid==
267                            signode->pkt->pkt.signature->revkey[j]->algid &&
268                            memcmp(pk->revkey[i].fpr,
269                                   signode->pkt->pkt.signature->revkey[j]->fpr,
270                                   MAX_FINGERPRINT_LEN)==0)
271                           {
272                             revsig=signode->pkt->pkt.signature;
273                             break;
274                           }
275                       }
276                   }
277               }
278
279             if(revsig)
280               {
281                 pkt.pkttype = PKT_SIGNATURE;
282                 pkt.pkt.signature = revsig;
283
284                 rc = build_packet( out, &pkt );
285                 if( rc ) {
286                   log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
287                   goto leave;
288                 }
289               }
290             else
291               BUG();
292
293             init_packet( &pkt );
294             pkt.pkttype = PKT_SIGNATURE;
295             pkt.pkt.signature = sig;
296
297             rc = build_packet( out, &pkt );
298             if( rc ) {
299               log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
300               goto leave;
301             }
302
303             while(!selfsig)
304               {
305                 KBNODE signode;
306
307                 node=find_next_kbnode(node,PKT_USER_ID);
308                 if(!node)
309                   {
310                     /* We're out of user IDs - none were
311                        self-signed. */
312                     if(uid)
313                       break;
314                     else
315                       {
316                         rc=G10ERR_GENERAL;
317                         log_error(_("key %08lX incomplete\n"),(ulong)keyid[1]);
318                         goto leave;
319                       }
320                   }
321
322                 if(node->pkt->pkt.user_id->attrib_data)
323                   continue;
324
325                 uid=node->pkt->pkt.user_id;
326                 signode=node;
327
328                 while((signode=find_next_kbnode(signode,PKT_SIGNATURE)))
329                   {
330                     if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
331                        keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
332                        IS_UID_SIG(signode->pkt->pkt.signature))
333                       {
334                         selfsig=signode->pkt->pkt.signature;
335                         break;
336                       }
337                   }
338               }
339
340             pkt.pkttype = PKT_USER_ID;
341             pkt.pkt.user_id = uid;
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             if(selfsig)
350               {
351                 pkt.pkttype = PKT_SIGNATURE;
352                 pkt.pkt.signature = selfsig;
353
354                 rc = build_packet( out, &pkt );
355                 if( rc ) {
356                   log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
357                   goto leave;
358                 }
359               }
360
361             /* and issue a usage notice */
362             tty_printf(_("Revocation certificate created.\n"));
363             break;
364           }
365       }
366
367     if(!any)
368       log_error(_("no revocation keys found for `%s'\n"),uname);
369
370   leave:
371     if( pk )
372         free_public_key( pk );
373     if( sk )
374         free_secret_key( sk );
375     if( sig )
376         free_seckey_enc( sig );
377
378     if( rc )
379         iobuf_cancel(out);
380     else
381         iobuf_close(out);
382     release_revocation_reason_info( reason );
383     return rc;
384 }
385
386
387 /****************
388  * Generate a revocation certificate for UNAME
389  */
390 int
391 gen_revoke( const char *uname )
392 {
393     int rc = 0;
394     armor_filter_context_t afx;
395     PACKET pkt;
396     PKT_secret_key *sk; /* used as pointer into a kbnode */
397     PKT_public_key *pk = NULL;
398     PKT_signature *sig = NULL;
399     u32 sk_keyid[2];
400     IOBUF out = NULL;
401     KBNODE keyblock = NULL;
402     KBNODE node;
403     KEYDB_HANDLE kdbhd;
404     struct revocation_reason_info *reason = NULL;
405     KEYDB_SEARCH_DESC desc;
406
407     if( opt.batch ) {
408         log_error(_("sorry, can't do this in batch mode\n"));
409         return G10ERR_GENERAL;
410     }
411
412     memset( &afx, 0, sizeof afx);
413     init_packet( &pkt );
414
415     /* search the userid: 
416      * We don't want the whole getkey stuff here but the entire keyblock
417      */
418     kdbhd = keydb_new (1);
419     classify_user_id (uname, &desc);
420     rc = desc.mode? keydb_search (kdbhd, &desc, 1) : G10ERR_INV_USER_ID;
421     if (rc) {
422         log_error (_("secret key `%s' not found: %s\n"),
423                    uname, g10_errstr (rc));
424         goto leave;
425     }
426
427     rc = keydb_get_keyblock (kdbhd, &keyblock );
428     if( rc ) {
429         log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
430         goto leave;
431     }
432
433     /* get the keyid from the keyblock */
434     node = find_kbnode( keyblock, PKT_SECRET_KEY );
435     if( !node ) 
436         BUG ();
437
438     /* fixme: should make a function out of this stuff,
439      * it's used all over the source */
440     sk = node->pkt->pkt.secret_key;
441     keyid_from_sk( sk, sk_keyid );
442     tty_printf("\nsec  %4u%c/%08lX %s   ",
443               nbits_from_sk( sk ),
444               pubkey_letter( sk->pubkey_algo ),
445               (ulong)sk_keyid[1], datestr_from_sk(sk) );
446     {
447         size_t n;
448         char *p = get_user_id( sk_keyid, &n );
449         tty_print_utf8_string( p, n );
450         m_free(p);
451         tty_printf("\n");
452     }
453     pk = m_alloc_clear( sizeof *pk );
454
455     /* FIXME: We should get the public key direct from the secret one */
456     rc = get_pubkey( pk, sk_keyid );
457     if( rc ) {
458         log_error(_("no corresponding public key: %s\n"), g10_errstr(rc) );
459         goto leave;
460     }
461     if( cmp_public_secret_key( pk, sk ) ) {
462         log_error(_("public key does not match secret key!\n") );
463         rc = G10ERR_GENERAL;
464         goto leave;
465     }
466
467     tty_printf("\n");
468     if( !cpr_get_answer_is_yes("gen_revoke.okay",
469                         _("Create a revocation certificate for this key? ")) ){
470         rc = 0;
471         goto leave;
472     }
473
474     if(sk->version>=4 || opt.force_v4_certs) {
475       /* get the reason for the revocation */
476       reason = ask_revocation_reason( 1, 0, 1 );
477       if( !reason ) { /* user decided to cancel */
478         rc = 0;
479         goto leave;
480       }
481     }
482
483     switch( is_secret_key_protected( sk ) ) {
484       case -1:
485         log_error(_("unknown protection algorithm\n"));
486         rc = G10ERR_PUBKEY_ALGO;
487         break;
488       case 0:
489         tty_printf(_("NOTE: This key is not protected!\n"));
490         break;
491       default:
492         rc = check_secret_key( sk, 0 );
493         break;
494     }
495     if( rc )
496         goto leave;
497
498
499     if( !opt.armor )
500         tty_printf(_("ASCII armored output forced.\n"));
501
502     if( (rc = open_outfile( NULL, 0, &out )) )
503         goto leave;
504
505     afx.what = 1;
506     afx.hdrlines = "Comment: A revocation certificate should follow\n";
507     iobuf_push_filter( out, armor_filter, &afx );
508
509     /* create it */
510     rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0,
511                              opt.force_v4_certs?4:0, 0, 0,
512                              revocation_reason_build_cb, reason );
513     if( rc ) {
514         log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc));
515         goto leave;
516     }
517     init_packet( &pkt );
518     pkt.pkttype = PKT_SIGNATURE;
519     pkt.pkt.signature = sig;
520
521     rc = build_packet( out, &pkt );
522     if( rc ) {
523         log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
524         goto leave;
525     }
526
527     /* and issue a usage notice */
528     tty_printf(_("Revocation certificate created.\n\n"
529 "Please move it to a medium which you can hide away; if Mallory gets\n"
530 "access to this certificate he can use it to make your key unusable.\n"
531 "It is smart to print this certificate and store it away, just in case\n"
532 "your media become unreadable.  But have some caution:  The print system of\n"
533 "your machine might store the data and make it available to others!\n"));
534
535
536
537   leave:
538     if( pk )
539         free_public_key( pk );
540     if( sig )
541         free_seckey_enc( sig );
542     release_kbnode( keyblock );
543     keydb_release (kdbhd);
544     if( rc )
545         iobuf_cancel(out);
546     else
547         iobuf_close(out);
548     release_revocation_reason_info( reason );
549     return rc;
550 }
551
552
553
554 struct revocation_reason_info *
555 ask_revocation_reason( int key_rev, int cert_rev, int hint )
556 {
557     int code=-1;
558     char *description = NULL;
559     struct revocation_reason_info *reason;
560     const char *text_0 = _("No reason specified");
561     const char *text_1 = _("Key has been compromised");
562     const char *text_2 = _("Key is superseded");
563     const char *text_3 = _("Key is no longer used");
564     const char *text_4 = _("User ID is no longer valid");
565     const char *code_text = NULL;
566
567     do {
568         m_free(description);
569         description = NULL;
570
571         tty_printf(_("Please select the reason for the revocation:\n"));
572         tty_printf(    "  0 = %s\n", text_0 );
573         if( key_rev )
574             tty_printf("  1 = %s\n", text_1 );
575         if( key_rev )
576             tty_printf("  2 = %s\n", text_2 );
577         if( key_rev )
578             tty_printf("  3 = %s\n", text_3 );
579         if( cert_rev )
580             tty_printf("  4 = %s\n", text_4 );
581         tty_printf(    "  Q = %s\n", _("Cancel") );
582         if( hint )
583             tty_printf(_("(Probably you want to select %d here)\n"), hint );
584
585         while(code==-1) {
586             int n;
587             char *answer = cpr_get("ask_revocation_reason.code",
588                                                 _("Your decision? "));
589             trim_spaces( answer );
590             cpr_kill_prompt();
591             if( *answer == 'q' || *answer == 'Q')
592               return NULL; /* cancel */
593             if( hint && !*answer )
594                 n = hint;
595             else if(!isdigit( *answer ) )
596                 n = -1;
597             else
598                 n = atoi(answer);
599             m_free(answer);
600             if( n == 0 ) {
601                 code = 0x00; /* no particular reason */
602                 code_text = text_0;
603             }
604             else if( key_rev && n == 1 ) {
605                 code = 0x02; /* key has been compromised */
606                 code_text = text_1;
607             }
608             else if( key_rev && n == 2 ) {
609                 code = 0x01; /* key is superseded */
610                 code_text = text_2;
611             }
612             else if( key_rev && n == 3 ) {
613                 code = 0x03; /* key is no longer used */
614                 code_text = text_3;
615             }
616             else if( cert_rev && n == 4 ) {
617                 code = 0x20; /* uid is no longer valid */
618                 code_text = text_4;
619             }
620             else
621                 tty_printf(_("Invalid selection.\n"));
622         }
623
624         tty_printf(_("Enter an optional description; "
625                      "end it with an empty line:\n") );
626         for(;;) {
627             char *answer = cpr_get("ask_revocation_reason.text", "> " );
628             trim_trailing_ws( answer, strlen(answer) );
629             cpr_kill_prompt();
630             if( !*answer ) {
631                 m_free(answer);
632                 break;
633             }
634
635             {
636                 char *p = make_printable_string( answer, strlen(answer), 0 );
637                 m_free(answer);
638                 answer = p;
639             }
640
641             if( !description )
642                 description = m_strdup(answer);
643             else {
644                 char *p = m_alloc( strlen(description) + strlen(answer) + 2 );
645                 strcpy(stpcpy(stpcpy( p, description),"\n"),answer);
646                 m_free(description);
647                 description = p;
648             }
649             m_free(answer);
650         }
651
652         tty_printf(_("Reason for revocation: %s\n"), code_text );
653         if( !description )
654             tty_printf(_("(No description given)\n") );
655         else
656             tty_printf("%s\n", description );
657
658     } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay",
659                                             _("Is this okay? "))  );
660
661     reason = m_alloc( sizeof *reason );
662     reason->code = code;
663     reason->desc = description;
664     return reason;
665 }
666
667 void
668 release_revocation_reason_info( struct revocation_reason_info *reason )
669 {
670     if( reason ) {
671         m_free( reason->desc );
672         m_free( reason );
673     }
674 }