See ChangeLog: Fri Oct 6 14:29:16 CEST 2000 Werner Koch
[gnupg.git] / g10 / revoke.c
1 /* revoke.c
2  *      Copyright (C) 1998, 1999, 2000 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 <gcrypt.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->desc ) {
56         ud = native_to_utf8( reason->desc );
57         buflen += strlen(ud);
58     }
59     buffer = gcry_xmalloc( buflen );
60     *buffer = reason->code;
61     if( ud ) {
62         memcpy(buffer+1, ud, strlen(ud) );
63         gcry_free( ud );
64     }
65
66     build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen );
67     gcry_free( buffer );
68     return 0;
69 }
70
71
72
73 /****************
74  * Generate a revocation certificate for UNAME
75  */
76 int
77 gen_revoke( const char *uname )
78 {
79     int rc = 0;
80     armor_filter_context_t afx;
81     compress_filter_context_t zfx;
82     PACKET pkt;
83     PKT_secret_key *sk; /* used as pointer into a kbnode */
84     PKT_public_key *pk = NULL;
85     PKT_signature *sig = NULL;
86     u32 sk_keyid[2];
87     IOBUF out = NULL;
88     KBNODE keyblock = NULL;
89     KBNODE node;
90     struct revocation_reason_info *reason = NULL;
91
92     if( opt.batch ) {
93         log_error(_("sorry, can't do this in batch mode\n"));
94         return GPGERR_GENERAL;
95     }
96
97
98     memset( &afx, 0, sizeof afx);
99     memset( &zfx, 0, sizeof zfx);
100     init_packet( &pkt );
101
102
103     /* search the userid */
104     rc = find_secret_keyblock_byname( &keyblock, uname );
105     if( rc ) {
106         log_error(_("secret key for user `%s' not found: %s\n"),
107                   uname, gpg_errstr(rc) );
108         goto leave;
109     }
110
111     /* get the keyid from the keyblock */
112     node = find_kbnode( keyblock, PKT_SECRET_KEY );
113     if( !node ) { /* maybe better to use log_bug ? */
114         log_error(_("Oops; secret key not found anymore!\n"));
115         rc = GPGERR_GENERAL;
116         goto leave;
117     }
118
119     /* fixme: should make a function out of this stuff,
120      * it's used all over the source */
121     sk = node->pkt->pkt.secret_key;
122     keyid_from_sk( sk, sk_keyid );
123     tty_printf("\nsec  %4u%c/%08lX %s   ",
124               nbits_from_sk( sk ),
125               pubkey_letter( sk->pubkey_algo ),
126               sk_keyid[1], datestr_from_sk(sk) );
127     {
128         size_t n;
129         char *p = get_user_id( sk_keyid, &n );
130         tty_print_utf8_string( p, n );
131         gcry_free(p);
132         tty_printf("\n");
133     }
134     pk = gcry_xcalloc( 1, sizeof *pk );
135     rc = get_pubkey( pk, sk_keyid );
136     if( rc ) {
137         log_error(_("no corresponding public key: %s\n"), gpg_errstr(rc) );
138         goto leave;
139     }
140     if( cmp_public_secret_key( pk, sk ) ) {
141         log_error(_("public key does not match secret key!\n") );
142         rc = GPGERR_GENERAL;
143         goto leave;
144     }
145
146     tty_printf("\n");
147     if( !cpr_get_answer_is_yes("gen_revoke.okay",
148                         _("Create a revocation certificate for this key? ")) ){
149         rc = 0;
150         goto leave;
151     }
152
153     /* get the reason for the revocation */
154     reason = ask_revocation_reason( 1, 0, 1 );
155     if( !reason ) { /* user decided to cancel */
156         rc = 0;
157         goto leave;
158     }
159
160     switch( is_secret_key_protected( sk ) ) {
161       case -1:
162         log_error(_("unknown protection algorithm\n"));
163         rc = GPGERR_PUBKEY_ALGO;
164         break;
165       case 0:
166         tty_printf(_("NOTE: This key is not protected!\n"));
167         break;
168       default:
169         rc = check_secret_key( sk, 0 );
170         break;
171     }
172     if( rc )
173         goto leave;
174
175
176     if( !opt.armor )
177         tty_printf(_("ASCII armored output forced.\n"));
178
179     if( (rc = open_outfile( NULL, 0, &out )) )
180         goto leave;
181
182     afx.what = 1;
183     afx.hdrlines = "Comment: A revocation certificate should follow\n";
184     iobuf_push_filter( out, armor_filter, &afx );
185
186     /* create it */
187     rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0,
188                                                   revocation_reason_build_cb,
189                                                   reason );
190     if( rc ) {
191         log_error(_("make_keysig_packet failed: %s\n"), gpg_errstr(rc));
192         goto leave;
193     }
194     init_packet( &pkt );
195     pkt.pkttype = PKT_SIGNATURE;
196     pkt.pkt.signature = sig;
197
198     rc = build_packet( out, &pkt );
199     if( rc ) {
200         log_error(_("build_packet failed: %s\n"), gpg_errstr(rc) );
201         goto leave;
202     }
203
204     /* and issue a usage notice */
205     tty_printf(_("Revocation certificate created.\n\n"
206 "Please move it to a medium which you can hide away; if Mallory gets\n"
207 "access to this certificate he can use it to make your key unusable.\n"
208 "It is smart to print this certificate and store it away, just in case\n"
209 "your media become unreadable.  But have some caution:  The print system of\n"
210 "your machine might store the data and make it available to others!\n"));
211
212
213
214   leave:
215     if( pk )
216         free_public_key( pk );
217     if( sig )
218         free_seckey_enc( sig );
219     release_kbnode( keyblock );
220     if( rc )
221         iobuf_cancel(out);
222     else
223         iobuf_close(out);
224     release_revocation_reason_info( reason );
225     return rc;
226 }
227
228
229
230 struct revocation_reason_info *
231 ask_revocation_reason( int key_rev, int cert_rev, int hint )
232 {
233     int code;
234     char *description = NULL;
235     struct revocation_reason_info *reason;
236     const char *text_1 = _("Key has been compromised");
237     const char *text_2 = _("Key is superseded");
238     const char *text_3 = _("Key is no longer used");
239     const char *text_4 = _("User ID is non longer valid");
240     const char *code_text = NULL;
241
242     do {
243         gcry_free(description);
244         description = NULL;
245
246         tty_printf(_("Please select the reason for the revocation:\n"));
247         if( key_rev )
248             tty_printf("  1 = %s\n", text_1 );
249         if( key_rev )
250             tty_printf("  2 = %s\n", text_2 );
251         if( key_rev )
252             tty_printf("  3 = %s\n", text_3 );
253         if( cert_rev )
254             tty_printf("  4 = %s\n", text_4 );
255         tty_printf(    "  0 = %s\n", _("Cancel") );
256         if( hint )
257             tty_printf(_("(Probably you want to select %d here)\n"), hint );
258
259         for(code = 0; !code;) {
260             int n;
261             char *answer = cpr_get("ask_revocation_reason.code",
262                                                 _("Your decision? "));
263             trim_spaces( answer );
264             cpr_kill_prompt();
265             if( *answer == 'q' || *answer == 'Q' )
266                 n = 0;
267             else if( !isdigit( *answer ) )
268                 n = -1;
269             else if( hint && !*answer )
270                 n = hint;
271             else
272                 n = atoi(answer);
273             gcry_free(answer);
274             if( !n )
275                 return NULL; /* cancel */
276             else if( key_rev && n == 1 ) {
277                 code = 0x02; /* key has  been compromised */
278                 code_text = text_1;
279             }
280             else if( key_rev && n == 2 ) {
281                 code = 0x01; /* key is superseded */
282                 code_text = text_2;
283             }
284             else if( key_rev && n == 3 ) {
285                 code = 0x03; /* key is no longer used */
286                 code_text = text_3;
287             }
288             else if( cert_rev && n == 4 ) {
289                 code = 0x20; /* uid is non longer valid */
290                 code_text = text_4;
291             }
292             else
293                 tty_printf(_("Invalid selection.\n"));
294         }
295
296         tty_printf(_("Enter an optional description; "
297                      "end it with an empty line:\n") );
298         for(;;) {
299             char *answer = cpr_get("ask_revocation_reason.text", "> " );
300             trim_trailing_ws( answer, strlen(answer) );
301             cpr_kill_prompt();
302             if( !*answer ) {
303                 gcry_free(answer);
304                 break;
305             }
306
307             {
308                 char *p = make_printable_string( answer, strlen(answer), 0 );
309                 gcry_free(answer);
310                 answer = p;
311             }
312
313             if( !description )
314                 description = gcry_xstrdup(answer);
315             else {
316                 char *p = gcry_xmalloc( strlen(description) + strlen(answer) + 2 );
317                 strcpy(stpcpy(stpcpy( p, description),"\n"),answer);
318                 gcry_free(description);
319                 description = p;
320             }
321             gcry_free(answer);
322         }
323
324         tty_printf(_("Reason for revocation: %s\n"), code_text );
325         if( !description )
326             tty_printf(_("(No description given)\n") );
327         else
328             tty_printf("%s\n", description );
329
330     } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay",
331                                             _("Is this okay? "))  );
332
333     reason = gcry_xmalloc( sizeof *reason );
334     reason->code = code;
335     reason->desc = description;
336     return reason;
337 }
338
339 void
340 release_revocation_reason_info( struct revocation_reason_info *reason )
341 {
342     if( reason ) {
343         gcry_free( reason->desc );
344         gcry_free( reason );
345     }
346 }
347