* photoid.c (get_default_photo_command, show_photos): Honor
[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;
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\n");
176
177             if( !cpr_get_answer_is_yes("gen_desig_revoke.okay",
178                        _("Create a revocation certificate for this key? ")) )
179               continue;
180
181             /* get the reason for the revocation (this is always v4) */
182             reason = ask_revocation_reason( 1, 0, 1 );
183             if( !reason )
184               continue;
185
186             rc = check_secret_key( sk, 0 );
187             if( rc )
188               continue;
189
190             if( !opt.armor )
191               tty_printf(_("ASCII armored output forced.\n"));
192
193             if( (rc = open_outfile( NULL, 0, &out )) )
194               goto leave;
195
196             afx.what = 1;
197             afx.hdrlines = "Comment: A revocation certificate should follow\n";
198             iobuf_push_filter( out, armor_filter, &afx );
199
200             /* create it */
201             rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0,
202                                      0, 0, 0,
203                                      revocation_reason_build_cb, reason );
204             if( rc ) {
205               log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc));
206               goto leave;
207             }
208
209             /* Spit out a minimal pk as well, since otherwise there is
210                no way to know which key to attach this revocation
211                to. */
212
213             node=find_kbnode(keyblock,PKT_PUBLIC_KEY);
214             if(!node)
215               {
216                 rc=G10ERR_GENERAL;
217                 log_error(_("key %08lX incomplete\n"),(ulong)keyid[1]);
218                 goto leave;
219               }
220
221             pkt = *node->pkt;
222             rc=build_packet(out,&pkt);
223             if( rc ) {
224               log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
225               goto leave;
226             }
227
228             init_packet( &pkt );
229             pkt.pkttype = PKT_SIGNATURE;
230             pkt.pkt.signature = sig;
231
232             rc = build_packet( out, &pkt );
233             if( rc ) {
234               log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
235               goto leave;
236             }
237
238             while(!selfsig)
239               {
240                 KBNODE signode;
241
242                 node=find_next_kbnode(node,PKT_USER_ID);
243                 if(!node)
244                   {
245                     /* We're out of user IDs - none were
246                        self-signed. */
247                     if(uid)
248                       break;
249                     else
250                       {
251                         rc=G10ERR_GENERAL;
252                         log_error(_("key %08lX incomplete\n"),(ulong)keyid[1]);
253                         goto leave;
254                       }
255                   }
256
257                 if(node->pkt->pkt.user_id->attrib_data)
258                   continue;
259
260                 uid=node->pkt->pkt.user_id;
261                 signode=node;
262
263                 while((signode=find_next_kbnode(signode,PKT_SIGNATURE)))
264                   {
265                     if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
266                        keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
267                        IS_UID_SIG(signode->pkt->pkt.signature))
268                       {
269                         selfsig=signode->pkt->pkt.signature;
270                         break;
271                       }
272                   }
273               }
274
275             pkt.pkttype = PKT_USER_ID;
276             pkt.pkt.user_id = uid;
277
278             rc = build_packet( out, &pkt );
279             if( rc ) {
280               log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
281               goto leave;
282             }
283
284             if(selfsig)
285               {
286                 pkt.pkttype = PKT_SIGNATURE;
287                 pkt.pkt.signature = selfsig;
288
289                 rc = build_packet( out, &pkt );
290                 if( rc ) {
291                   log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
292                   goto leave;
293                 }
294               }
295
296             /* and issue a usage notice */
297             tty_printf(_("Revocation certificate created.\n"));
298             break;
299           }
300       }
301
302     if(!any)
303       log_error(_("no revocation keys found for `%s'\n"),uname);
304
305   leave:
306     if( pk )
307         free_public_key( pk );
308     if( sk )
309         free_secret_key( sk );
310     if( sig )
311         free_seckey_enc( sig );
312
313     if( rc )
314         iobuf_cancel(out);
315     else
316         iobuf_close(out);
317     release_revocation_reason_info( reason );
318     return rc;
319 }
320
321
322 /****************
323  * Generate a revocation certificate for UNAME
324  */
325 int
326 gen_revoke( const char *uname )
327 {
328     int rc = 0;
329     armor_filter_context_t afx;
330     PACKET pkt;
331     PKT_secret_key *sk; /* used as pointer into a kbnode */
332     PKT_public_key *pk = NULL;
333     PKT_signature *sig = NULL;
334     u32 sk_keyid[2];
335     IOBUF out = NULL;
336     KBNODE keyblock = NULL;
337     KBNODE node;
338     KEYDB_HANDLE kdbhd;
339     struct revocation_reason_info *reason = NULL;
340     KEYDB_SEARCH_DESC desc;
341
342     if( opt.batch ) {
343         log_error(_("sorry, can't do this in batch mode\n"));
344         return G10ERR_GENERAL;
345     }
346
347     memset( &afx, 0, sizeof afx);
348     init_packet( &pkt );
349
350     /* search the userid: 
351      * We don't want the whole getkey stuff here but the entire keyblock
352      */
353     kdbhd = keydb_new (1);
354     classify_user_id (uname, &desc);
355     rc = desc.mode? keydb_search (kdbhd, &desc, 1) : G10ERR_INV_USER_ID;
356     if (rc) {
357         log_error (_("secret key `%s' not found: %s\n"),
358                    uname, g10_errstr (rc));
359         goto leave;
360     }
361
362     rc = keydb_get_keyblock (kdbhd, &keyblock );
363     if( rc ) {
364         log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
365         goto leave;
366     }
367
368     /* get the keyid from the keyblock */
369     node = find_kbnode( keyblock, PKT_SECRET_KEY );
370     if( !node ) 
371         BUG ();
372
373     /* fixme: should make a function out of this stuff,
374      * it's used all over the source */
375     sk = node->pkt->pkt.secret_key;
376     keyid_from_sk( sk, sk_keyid );
377     tty_printf("\nsec  %4u%c/%08lX %s   ",
378               nbits_from_sk( sk ),
379               pubkey_letter( sk->pubkey_algo ),
380               (ulong)sk_keyid[1], datestr_from_sk(sk) );
381     {
382         size_t n;
383         char *p = get_user_id( sk_keyid, &n );
384         tty_print_utf8_string( p, n );
385         m_free(p);
386         tty_printf("\n");
387     }
388     pk = m_alloc_clear( sizeof *pk );
389
390     /* FIXME: We should get the public key direct from the secret one */
391     rc = get_pubkey( pk, sk_keyid );
392     if( rc ) {
393         log_error(_("no corresponding public key: %s\n"), g10_errstr(rc) );
394         goto leave;
395     }
396     if( cmp_public_secret_key( pk, sk ) ) {
397         log_error(_("public key does not match secret key!\n") );
398         rc = G10ERR_GENERAL;
399         goto leave;
400     }
401
402     tty_printf("\n");
403     if( !cpr_get_answer_is_yes("gen_revoke.okay",
404                         _("Create a revocation certificate for this key? ")) ){
405         rc = 0;
406         goto leave;
407     }
408
409     if(sk->version>=4 || opt.force_v4_certs) {
410       /* get the reason for the revocation */
411       reason = ask_revocation_reason( 1, 0, 1 );
412       if( !reason ) { /* user decided to cancel */
413         rc = 0;
414         goto leave;
415       }
416     }
417
418     switch( is_secret_key_protected( sk ) ) {
419       case -1:
420         log_error(_("unknown protection algorithm\n"));
421         rc = G10ERR_PUBKEY_ALGO;
422         break;
423       case 0:
424         tty_printf(_("NOTE: This key is not protected!\n"));
425         break;
426       default:
427         rc = check_secret_key( sk, 0 );
428         break;
429     }
430     if( rc )
431         goto leave;
432
433
434     if( !opt.armor )
435         tty_printf(_("ASCII armored output forced.\n"));
436
437     if( (rc = open_outfile( NULL, 0, &out )) )
438         goto leave;
439
440     afx.what = 1;
441     afx.hdrlines = "Comment: A revocation certificate should follow\n";
442     iobuf_push_filter( out, armor_filter, &afx );
443
444     /* create it */
445     rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0,
446                              opt.force_v4_certs?4:0, 0, 0,
447                              revocation_reason_build_cb, reason );
448     if( rc ) {
449         log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc));
450         goto leave;
451     }
452     init_packet( &pkt );
453     pkt.pkttype = PKT_SIGNATURE;
454     pkt.pkt.signature = sig;
455
456     rc = build_packet( out, &pkt );
457     if( rc ) {
458         log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
459         goto leave;
460     }
461
462     /* and issue a usage notice */
463     tty_printf(_("Revocation certificate created.\n\n"
464 "Please move it to a medium which you can hide away; if Mallory gets\n"
465 "access to this certificate he can use it to make your key unusable.\n"
466 "It is smart to print this certificate and store it away, just in case\n"
467 "your media become unreadable.  But have some caution:  The print system of\n"
468 "your machine might store the data and make it available to others!\n"));
469
470
471
472   leave:
473     if( pk )
474         free_public_key( pk );
475     if( sig )
476         free_seckey_enc( sig );
477     release_kbnode( keyblock );
478     keydb_release (kdbhd);
479     if( rc )
480         iobuf_cancel(out);
481     else
482         iobuf_close(out);
483     release_revocation_reason_info( reason );
484     return rc;
485 }
486
487
488
489 struct revocation_reason_info *
490 ask_revocation_reason( int key_rev, int cert_rev, int hint )
491 {
492     int code=-1;
493     char *description = NULL;
494     struct revocation_reason_info *reason;
495     const char *text_0 = _("No reason specified");
496     const char *text_1 = _("Key has been compromised");
497     const char *text_2 = _("Key is superseded");
498     const char *text_3 = _("Key is no longer used");
499     const char *text_4 = _("User ID is no longer valid");
500     const char *code_text = NULL;
501
502     do {
503         m_free(description);
504         description = NULL;
505
506         tty_printf(_("Please select the reason for the revocation:\n"));
507         tty_printf(    "  0 = %s\n", text_0 );
508         if( key_rev )
509             tty_printf("  1 = %s\n", text_1 );
510         if( key_rev )
511             tty_printf("  2 = %s\n", text_2 );
512         if( key_rev )
513             tty_printf("  3 = %s\n", text_3 );
514         if( cert_rev )
515             tty_printf("  4 = %s\n", text_4 );
516         tty_printf(    "  Q = %s\n", _("Cancel") );
517         if( hint )
518             tty_printf(_("(Probably you want to select %d here)\n"), hint );
519
520         while(code==-1) {
521             int n;
522             char *answer = cpr_get("ask_revocation_reason.code",
523                                                 _("Your decision? "));
524             trim_spaces( answer );
525             cpr_kill_prompt();
526             if( *answer == 'q' || *answer == 'Q')
527               return NULL; /* cancel */
528             if( hint && !*answer )
529                 n = hint;
530             else if(!isdigit( *answer ) )
531                 n = -1;
532             else
533                 n = atoi(answer);
534             m_free(answer);
535             if( n == 0 ) {
536                 code = 0x00; /* no particular reason */
537                 code_text = text_0;
538             }
539             else if( key_rev && n == 1 ) {
540                 code = 0x02; /* key has been compromised */
541                 code_text = text_1;
542             }
543             else if( key_rev && n == 2 ) {
544                 code = 0x01; /* key is superseded */
545                 code_text = text_2;
546             }
547             else if( key_rev && n == 3 ) {
548                 code = 0x03; /* key is no longer used */
549                 code_text = text_3;
550             }
551             else if( cert_rev && n == 4 ) {
552                 code = 0x20; /* uid is no longer valid */
553                 code_text = text_4;
554             }
555             else
556                 tty_printf(_("Invalid selection.\n"));
557         }
558
559         tty_printf(_("Enter an optional description; "
560                      "end it with an empty line:\n") );
561         for(;;) {
562             char *answer = cpr_get("ask_revocation_reason.text", "> " );
563             trim_trailing_ws( answer, strlen(answer) );
564             cpr_kill_prompt();
565             if( !*answer ) {
566                 m_free(answer);
567                 break;
568             }
569
570             {
571                 char *p = make_printable_string( answer, strlen(answer), 0 );
572                 m_free(answer);
573                 answer = p;
574             }
575
576             if( !description )
577                 description = m_strdup(answer);
578             else {
579                 char *p = m_alloc( strlen(description) + strlen(answer) + 2 );
580                 strcpy(stpcpy(stpcpy( p, description),"\n"),answer);
581                 m_free(description);
582                 description = p;
583             }
584             m_free(answer);
585         }
586
587         tty_printf(_("Reason for revocation: %s\n"), code_text );
588         if( !description )
589             tty_printf(_("(No description given)\n") );
590         else
591             tty_printf("%s\n", description );
592
593     } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay",
594                                             _("Is this okay? "))  );
595
596     reason = m_alloc( sizeof *reason );
597     reason->code = code;
598     reason->desc = description;
599     return reason;
600 }
601
602 void
603 release_revocation_reason_info( struct revocation_reason_info *reason )
604 {
605     if( reason ) {
606         m_free( reason->desc );
607         m_free( reason );
608     }
609 }