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