Line | Count | Source (jump to first uncovered line) |
1 | | /* revoke.c - Create recovation certificates. |
2 | | * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, |
3 | | * 2004 Free Software Foundation, Inc. |
4 | | * |
5 | | * This file is part of GnuPG. |
6 | | * |
7 | | * GnuPG is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 3 of the License, or |
10 | | * (at your option) any later version. |
11 | | * |
12 | | * GnuPG is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, see <https://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include <config.h> |
22 | | #include <stdio.h> |
23 | | #include <stdlib.h> |
24 | | #include <string.h> |
25 | | #include <errno.h> |
26 | | #include <ctype.h> |
27 | | |
28 | | #include "gpg.h" |
29 | | #include "options.h" |
30 | | #include "packet.h" |
31 | | #include "../common/status.h" |
32 | | #include "keydb.h" |
33 | | #include "../common/util.h" |
34 | | #include "main.h" |
35 | | #include "../common/ttyio.h" |
36 | | #include "../common/i18n.h" |
37 | | #include "call-agent.h" |
38 | | |
39 | | struct revocation_reason_info { |
40 | | int code; |
41 | | char *desc; |
42 | | }; |
43 | | |
44 | | |
45 | | int |
46 | | revocation_reason_build_cb( PKT_signature *sig, void *opaque ) |
47 | 0 | { |
48 | 0 | struct revocation_reason_info *reason = opaque; |
49 | 0 | char *ud = NULL; |
50 | 0 | byte *buffer; |
51 | 0 | size_t buflen = 1; |
52 | |
|
53 | 0 | if(!reason) |
54 | 0 | return 0; |
55 | | |
56 | 0 | if( reason->desc ) { |
57 | 0 | ud = native_to_utf8( reason->desc ); |
58 | 0 | buflen += strlen(ud); |
59 | 0 | } |
60 | 0 | buffer = xmalloc( buflen ); |
61 | 0 | *buffer = reason->code; |
62 | 0 | if( ud ) { |
63 | 0 | memcpy(buffer+1, ud, strlen(ud) ); |
64 | 0 | xfree( ud ); |
65 | 0 | } |
66 | |
|
67 | 0 | build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen ); |
68 | 0 | xfree( buffer ); |
69 | 0 | return 0; |
70 | 0 | } |
71 | | |
72 | | /* Outputs a minimal pk (as defined by 2440) from a keyblock. A |
73 | | minimal pk consists of the public key packet and a user ID. We try |
74 | | and pick a user ID that has a uid signature, and include it if |
75 | | possible. */ |
76 | | static int |
77 | | export_minimal_pk(IOBUF out,KBNODE keyblock, |
78 | | PKT_signature *revsig,PKT_signature *revkey) |
79 | 0 | { |
80 | 0 | KBNODE node; |
81 | 0 | PACKET pkt; |
82 | 0 | PKT_user_id *uid=NULL; |
83 | 0 | PKT_signature *selfsig=NULL; |
84 | 0 | u32 keyid[2]; |
85 | 0 | int rc; |
86 | |
|
87 | 0 | node=find_kbnode(keyblock,PKT_PUBLIC_KEY); |
88 | 0 | if(!node) |
89 | 0 | { |
90 | 0 | log_error("key incomplete\n"); |
91 | 0 | return GPG_ERR_GENERAL; |
92 | 0 | } |
93 | | |
94 | 0 | keyid_from_pk(node->pkt->pkt.public_key,keyid); |
95 | |
|
96 | 0 | pkt=*node->pkt; |
97 | 0 | rc=build_packet(out,&pkt); |
98 | 0 | if(rc) |
99 | 0 | { |
100 | 0 | log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); |
101 | 0 | return rc; |
102 | 0 | } |
103 | | |
104 | 0 | init_packet(&pkt); |
105 | 0 | pkt.pkttype=PKT_SIGNATURE; |
106 | | |
107 | | /* the revocation itself, if any. 2440 likes this to come first. */ |
108 | 0 | if(revsig) |
109 | 0 | { |
110 | 0 | pkt.pkt.signature=revsig; |
111 | 0 | rc=build_packet(out,&pkt); |
112 | 0 | if(rc) |
113 | 0 | { |
114 | 0 | log_error("build_packet failed: %s\n", gpg_strerror (rc) ); |
115 | 0 | return rc; |
116 | 0 | } |
117 | 0 | } |
118 | | |
119 | | /* If a revkey in a 1F sig is present, include it too */ |
120 | 0 | if(revkey) |
121 | 0 | { |
122 | 0 | pkt.pkt.signature=revkey; |
123 | 0 | rc=build_packet(out,&pkt); |
124 | 0 | if(rc) |
125 | 0 | { |
126 | 0 | log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); |
127 | 0 | return rc; |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | 0 | while(!selfsig) |
132 | 0 | { |
133 | 0 | KBNODE signode; |
134 | |
|
135 | 0 | node=find_next_kbnode(node,PKT_USER_ID); |
136 | 0 | if(!node) |
137 | 0 | { |
138 | | /* We're out of user IDs - none were self-signed. */ |
139 | 0 | if(uid) |
140 | 0 | break; |
141 | 0 | else |
142 | 0 | { |
143 | 0 | log_error(_("key %s has no user IDs\n"),keystr(keyid)); |
144 | 0 | return GPG_ERR_GENERAL; |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | 0 | if(node->pkt->pkt.user_id->attrib_data) |
149 | 0 | continue; |
150 | | |
151 | 0 | uid=node->pkt->pkt.user_id; |
152 | 0 | signode=node; |
153 | |
|
154 | 0 | while((signode=find_next_kbnode(signode,PKT_SIGNATURE))) |
155 | 0 | { |
156 | 0 | if(keyid[0]==signode->pkt->pkt.signature->keyid[0] && |
157 | 0 | keyid[1]==signode->pkt->pkt.signature->keyid[1] && |
158 | 0 | IS_UID_SIG(signode->pkt->pkt.signature)) |
159 | 0 | { |
160 | 0 | selfsig=signode->pkt->pkt.signature; |
161 | 0 | break; |
162 | 0 | } |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | 0 | pkt.pkttype=PKT_USER_ID; |
167 | 0 | pkt.pkt.user_id=uid; |
168 | |
|
169 | 0 | rc=build_packet(out,&pkt); |
170 | 0 | if(rc) |
171 | 0 | { |
172 | 0 | log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); |
173 | 0 | return rc; |
174 | 0 | } |
175 | | |
176 | 0 | if(selfsig) |
177 | 0 | { |
178 | 0 | pkt.pkttype=PKT_SIGNATURE; |
179 | 0 | pkt.pkt.signature=selfsig; |
180 | |
|
181 | 0 | rc=build_packet(out,&pkt); |
182 | 0 | if(rc) |
183 | 0 | { |
184 | 0 | log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); |
185 | 0 | return rc; |
186 | 0 | } |
187 | 0 | } |
188 | | |
189 | 0 | return 0; |
190 | 0 | } |
191 | | |
192 | | /**************** |
193 | | * Generate a revocation certificate for UNAME via a designated revoker |
194 | | */ |
195 | | int |
196 | | gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr) |
197 | 0 | { |
198 | 0 | int rc = 0; |
199 | 0 | armor_filter_context_t *afx; |
200 | 0 | PKT_public_key *pk = NULL; |
201 | 0 | PKT_public_key *pk2 = NULL; |
202 | 0 | PKT_signature *sig = NULL; |
203 | 0 | IOBUF out = NULL; |
204 | 0 | struct revocation_reason_info *reason = NULL; |
205 | 0 | KEYDB_HANDLE kdbhd; |
206 | 0 | KEYDB_SEARCH_DESC desc; |
207 | 0 | KBNODE keyblock=NULL,node; |
208 | 0 | u32 keyid[2]; |
209 | 0 | int i,any=0; |
210 | 0 | SK_LIST sk_list=NULL; |
211 | |
|
212 | 0 | if( opt.batch ) |
213 | 0 | { |
214 | 0 | log_error(_("can't do this in batch mode\n")); |
215 | 0 | return GPG_ERR_GENERAL; |
216 | 0 | } |
217 | | |
218 | 0 | afx = new_armor_context (); |
219 | |
|
220 | 0 | kdbhd = keydb_new (ctrl); |
221 | 0 | if (!kdbhd) |
222 | 0 | { |
223 | 0 | rc = gpg_error_from_syserror (); |
224 | 0 | goto leave; |
225 | 0 | } |
226 | 0 | rc = classify_user_id (uname, &desc, 1); |
227 | 0 | if (!rc) |
228 | 0 | rc = keydb_search (kdbhd, &desc, 1, NULL); |
229 | 0 | if (rc) { |
230 | 0 | log_error (_("key \"%s\" not found: %s\n"),uname, gpg_strerror (rc)); |
231 | 0 | goto leave; |
232 | 0 | } |
233 | | |
234 | 0 | rc = keydb_get_keyblock (kdbhd, &keyblock ); |
235 | 0 | if( rc ) { |
236 | 0 | log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); |
237 | 0 | goto leave; |
238 | 0 | } |
239 | | |
240 | | /* To parse the revkeys */ |
241 | 0 | merge_keys_and_selfsig (ctrl, keyblock); |
242 | | |
243 | | /* get the key from the keyblock */ |
244 | 0 | node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); |
245 | 0 | if( !node ) |
246 | 0 | BUG (); |
247 | | |
248 | 0 | pk=node->pkt->pkt.public_key; |
249 | |
|
250 | 0 | keyid_from_pk(pk,keyid); |
251 | |
|
252 | 0 | if(locusr) |
253 | 0 | { |
254 | 0 | rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_CERT); |
255 | 0 | if(rc) |
256 | 0 | goto leave; |
257 | 0 | } |
258 | | |
259 | | /* Are we a designated revoker for this key? */ |
260 | | |
261 | 0 | if(!pk->revkey && pk->numrevkeys) |
262 | 0 | BUG(); |
263 | | |
264 | 0 | for(i=0;i<pk->numrevkeys;i++) |
265 | 0 | { |
266 | 0 | SK_LIST list; |
267 | |
|
268 | 0 | free_public_key (pk2); |
269 | 0 | pk2 = NULL; |
270 | |
|
271 | 0 | if(sk_list) |
272 | 0 | { |
273 | 0 | for(list=sk_list;list;list=list->next) |
274 | 0 | { |
275 | 0 | byte fpr[MAX_FINGERPRINT_LEN]; |
276 | 0 | size_t fprlen; |
277 | |
|
278 | 0 | fingerprint_from_pk (list->pk, fpr, &fprlen); |
279 | | |
280 | | /* Don't get involved with keys that don't have a v4 |
281 | | * or v5 fingerprint */ |
282 | 0 | if (fprlen != 20 && fprlen != 32) |
283 | 0 | continue; |
284 | | |
285 | 0 | if (!memcmp(fpr,pk->revkey[i].fpr, fprlen)) |
286 | 0 | break; |
287 | 0 | } |
288 | |
|
289 | 0 | if (list) |
290 | 0 | pk2 = copy_public_key (NULL, list->pk); |
291 | 0 | else |
292 | 0 | continue; |
293 | 0 | } |
294 | 0 | else |
295 | 0 | { |
296 | 0 | pk2 = xmalloc_clear (sizeof *pk2); |
297 | 0 | rc = get_pubkey_byfprint (ctrl, pk2, NULL, |
298 | 0 | pk->revkey[i].fpr, pk->revkey[i].fprlen); |
299 | 0 | } |
300 | | |
301 | | /* We have the revocation key. */ |
302 | 0 | if(!rc) |
303 | 0 | { |
304 | 0 | PKT_signature *revkey = NULL; |
305 | |
|
306 | 0 | any = 1; |
307 | |
|
308 | 0 | print_key_info (ctrl, NULL, 0, pk, 0); |
309 | 0 | tty_printf ("\n"); |
310 | |
|
311 | 0 | tty_printf (_("To be revoked by:\n")); |
312 | 0 | print_key_info (ctrl, NULL, 0, pk2, 1); |
313 | |
|
314 | 0 | if(pk->revkey[i].class&0x40) |
315 | 0 | tty_printf(_("(This is a sensitive revocation key)\n")); |
316 | 0 | tty_printf("\n"); |
317 | |
|
318 | 0 | if (!agent_probe_secret_key (ctrl, pk2)) |
319 | 0 | { |
320 | 0 | tty_printf (_("Secret key is not available.\n")); |
321 | 0 | continue; |
322 | 0 | } |
323 | | |
324 | 0 | if( !cpr_get_answer_is_yes("gen_desig_revoke.okay", |
325 | 0 | _("Create a designated revocation certificate for this key? (y/N) "))) |
326 | 0 | continue; |
327 | | |
328 | | /* get the reason for the revocation (this is always v4) */ |
329 | 0 | reason = ask_revocation_reason( 1, 0, 1 ); |
330 | 0 | if( !reason ) |
331 | 0 | continue; |
332 | | |
333 | 0 | if( !opt.armor ) |
334 | 0 | tty_printf(_("ASCII armored output forced.\n")); |
335 | |
|
336 | 0 | if( (rc = open_outfile (-1, NULL, 0, 1, &out )) ) |
337 | 0 | goto leave; |
338 | | |
339 | 0 | afx->what = 1; |
340 | 0 | afx->hdrlines = "Comment: A designated revocation certificate" |
341 | 0 | " should follow\n"; |
342 | 0 | push_armor_filter (afx, out); |
343 | | |
344 | | /* create it */ |
345 | 0 | rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk2, 0x20, |
346 | 0 | 0, 0, |
347 | 0 | revocation_reason_build_cb, reason, |
348 | 0 | NULL); |
349 | 0 | if( rc ) { |
350 | 0 | log_error(_("make_keysig_packet failed: %s\n"), gpg_strerror (rc)); |
351 | 0 | goto leave; |
352 | 0 | } |
353 | | |
354 | | /* Spit out a minimal pk as well, since otherwise there is |
355 | | no way to know which key to attach this revocation to. |
356 | | Also include the direct key signature that contains |
357 | | this revocation key. We're allowed to include |
358 | | sensitive revocation keys along with a revocation, as |
359 | | this may be the only time the recipient has seen it. |
360 | | Note that this means that if we have multiple different |
361 | | sensitive revocation keys in a given direct key |
362 | | signature, we're going to include them all here. This |
363 | | is annoying, but the good outweighs the bad, since |
364 | | without including this a sensitive revoker can't really |
365 | | do their job. People should not include multiple |
366 | | sensitive revocation keys in one signature: 2440 says |
367 | | "Note that it may be appropriate to isolate this |
368 | | subpacket within a separate signature so that it is not |
369 | | combined with other subpackets that need to be |
370 | | exported." -dms */ |
371 | | |
372 | 0 | while(!revkey) |
373 | 0 | { |
374 | 0 | KBNODE signode; |
375 | |
|
376 | 0 | signode=find_next_kbnode(node,PKT_SIGNATURE); |
377 | 0 | if(!signode) |
378 | 0 | break; |
379 | | |
380 | 0 | node=signode; |
381 | |
|
382 | 0 | if(keyid[0]==signode->pkt->pkt.signature->keyid[0] && |
383 | 0 | keyid[1]==signode->pkt->pkt.signature->keyid[1] && |
384 | 0 | IS_KEY_SIG(signode->pkt->pkt.signature)) |
385 | 0 | { |
386 | 0 | int j; |
387 | |
|
388 | 0 | for(j=0;j<signode->pkt->pkt.signature->numrevkeys;j++) |
389 | 0 | { |
390 | 0 | if (pk->revkey[i].class |
391 | 0 | == signode->pkt->pkt.signature->revkey[j].class |
392 | 0 | && pk->revkey[i].algid |
393 | 0 | == signode->pkt->pkt.signature->revkey[j].algid |
394 | 0 | && pk->revkey[i].fprlen |
395 | 0 | == signode->pkt->pkt.signature->revkey[j].fprlen |
396 | 0 | && !memcmp |
397 | 0 | (pk->revkey[i].fpr, |
398 | 0 | signode->pkt->pkt.signature->revkey[j].fpr, |
399 | 0 | pk->revkey[i].fprlen)) |
400 | 0 | { |
401 | 0 | revkey = signode->pkt->pkt.signature; |
402 | 0 | break; |
403 | 0 | } |
404 | 0 | } |
405 | 0 | } |
406 | 0 | } |
407 | |
|
408 | 0 | if(!revkey) |
409 | 0 | BUG(); |
410 | | |
411 | 0 | rc=export_minimal_pk(out,keyblock,sig,revkey); |
412 | 0 | if(rc) |
413 | 0 | goto leave; |
414 | | |
415 | | /* and issue a usage notice */ |
416 | 0 | tty_printf(_("Revocation certificate created.\n")); |
417 | 0 | break; |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | 0 | if(!any) |
422 | 0 | log_error(_("no revocation keys found for \"%s\"\n"),uname); |
423 | |
|
424 | 0 | leave: |
425 | 0 | free_public_key (pk); |
426 | 0 | free_public_key (pk2); |
427 | 0 | if( sig ) |
428 | 0 | free_seckey_enc( sig ); |
429 | |
|
430 | 0 | release_sk_list(sk_list); |
431 | |
|
432 | 0 | if( rc ) |
433 | 0 | iobuf_cancel(out); |
434 | 0 | else |
435 | 0 | iobuf_close(out); |
436 | 0 | release_revocation_reason_info( reason ); |
437 | 0 | release_armor_context (afx); |
438 | 0 | keydb_release (kdbhd); |
439 | 0 | return rc; |
440 | 0 | } |
441 | | |
442 | | |
443 | | /* Common core to create the revocation. FILENAME may be NULL to write |
444 | | to stdout or the filename given by --output. REASON describes the |
445 | | revocation reason. PSK is the public primary key - we expect that |
446 | | a corresponding secret key is available. KEYBLOCK is the entire |
447 | | KEYBLOCK which is used in PGP mode to write a minimal key and not |
448 | | just the naked revocation signature; it may be NULL. If LEADINTEXT |
449 | | is not NULL, it is written right before the (armored) output.*/ |
450 | | static int |
451 | | create_revocation (ctrl_t ctrl, |
452 | | const char *filename, |
453 | | struct revocation_reason_info *reason, |
454 | | PKT_public_key *psk, |
455 | | kbnode_t keyblock, |
456 | | const char *leadintext, int suffix, |
457 | | const char *cache_nonce) |
458 | 0 | { |
459 | 0 | int rc; |
460 | 0 | iobuf_t out = NULL; |
461 | 0 | armor_filter_context_t *afx; |
462 | 0 | PKT_signature *sig = NULL; |
463 | 0 | PACKET pkt; |
464 | |
|
465 | 0 | afx = new_armor_context (); |
466 | |
|
467 | 0 | if ((rc = open_outfile (-1, filename, suffix, 1, &out))) |
468 | 0 | goto leave; |
469 | | |
470 | 0 | if (leadintext ) |
471 | 0 | iobuf_writestr (out, leadintext); |
472 | |
|
473 | 0 | afx->what = 1; |
474 | 0 | afx->hdrlines = "Comment: This is a revocation certificate\n"; |
475 | 0 | push_armor_filter (afx, out); |
476 | |
|
477 | 0 | rc = make_keysig_packet (ctrl, &sig, psk, NULL, NULL, psk, 0x20, |
478 | 0 | 0, 0, |
479 | 0 | revocation_reason_build_cb, reason, cache_nonce); |
480 | 0 | if (rc) |
481 | 0 | { |
482 | 0 | log_error (_("make_keysig_packet failed: %s\n"), gpg_strerror (rc)); |
483 | 0 | goto leave; |
484 | 0 | } |
485 | | |
486 | 0 | if (keyblock && (PGP7 || PGP8)) |
487 | 0 | { |
488 | | /* Use a minimal pk for PGPx mode, since PGP can't import bare |
489 | | revocation certificates. */ |
490 | 0 | rc = export_minimal_pk (out, keyblock, sig, NULL); |
491 | 0 | if (rc) |
492 | 0 | goto leave; |
493 | 0 | } |
494 | 0 | else |
495 | 0 | { |
496 | 0 | init_packet (&pkt); |
497 | 0 | pkt.pkttype = PKT_SIGNATURE; |
498 | 0 | pkt.pkt.signature = sig; |
499 | |
|
500 | 0 | rc = build_packet (out, &pkt); |
501 | 0 | if (rc) |
502 | 0 | { |
503 | 0 | log_error (_("build_packet failed: %s\n"), gpg_strerror (rc)); |
504 | 0 | goto leave; |
505 | 0 | } |
506 | 0 | } |
507 | | |
508 | 0 | leave: |
509 | 0 | if (sig) |
510 | 0 | free_seckey_enc (sig); |
511 | 0 | if (rc) |
512 | 0 | iobuf_cancel (out); |
513 | 0 | else |
514 | 0 | iobuf_close (out); |
515 | 0 | release_armor_context (afx); |
516 | 0 | return rc; |
517 | 0 | } |
518 | | |
519 | | |
520 | | /* This function is used to generate a standard revocation certificate |
521 | | by gpg's interactive key generation function. The certificate is |
522 | | stored at a dedicated place in a slightly modified form to avoid an |
523 | | accidental import. PSK is the primary key; a corresponding secret |
524 | | key must be available. CACHE_NONCE is optional but can be used to |
525 | | help gpg-agent to avoid an extra passphrase prompt. */ |
526 | | int |
527 | | gen_standard_revoke (ctrl_t ctrl, PKT_public_key *psk, const char *cache_nonce) |
528 | 0 | { |
529 | 0 | int rc; |
530 | 0 | estream_t memfp; |
531 | 0 | struct revocation_reason_info reason; |
532 | 0 | char *dir, *tmpstr, *fname; |
533 | 0 | void *leadin; |
534 | 0 | size_t len; |
535 | 0 | u32 keyid[2]; |
536 | 0 | int kl; |
537 | 0 | char *orig_codeset; |
538 | 0 | char *old_outfile; |
539 | |
|
540 | 0 | dir = get_openpgp_revocdir (gnupg_homedir ()); |
541 | 0 | tmpstr = hexfingerprint (psk, NULL, 0); |
542 | 0 | if (!tmpstr) |
543 | 0 | { |
544 | 0 | rc = gpg_error_from_syserror (); |
545 | 0 | xfree (dir); |
546 | 0 | return rc; |
547 | 0 | } |
548 | 0 | fname = strconcat (dir, DIRSEP_S, tmpstr, NULL); |
549 | 0 | if (!fname) |
550 | 0 | { |
551 | 0 | rc = gpg_error_from_syserror (); |
552 | 0 | xfree (tmpstr); |
553 | 0 | xfree (dir); |
554 | 0 | return rc; |
555 | 0 | } |
556 | 0 | xfree (tmpstr); |
557 | 0 | xfree (dir); |
558 | |
|
559 | 0 | keyid_from_pk (psk, keyid); |
560 | |
|
561 | 0 | memfp = es_fopenmem (0, "r+"); |
562 | 0 | if (!memfp) |
563 | 0 | log_fatal ("error creating memory stream\n"); |
564 | | |
565 | 0 | orig_codeset = i18n_switchto_utf8 (); |
566 | |
|
567 | 0 | es_fprintf (memfp, "%s\n\n", |
568 | 0 | _("This is a revocation certificate for the OpenPGP key:")); |
569 | |
|
570 | 0 | print_key_line (ctrl, memfp, psk, 0); |
571 | |
|
572 | 0 | if (opt.keyid_format != KF_NONE) |
573 | 0 | print_fingerprint (ctrl, memfp, psk, 3); |
574 | |
|
575 | 0 | kl = opt.keyid_format == KF_NONE? 0 : keystrlen (); |
576 | |
|
577 | 0 | tmpstr = get_user_id (ctrl, keyid, &len, NULL); |
578 | 0 | es_fprintf (memfp, "uid%*s%.*s\n\n", |
579 | 0 | kl + 10, "", |
580 | 0 | (int)len, tmpstr); |
581 | 0 | xfree (tmpstr); |
582 | |
|
583 | 0 | es_fprintf (memfp, "%s\n\n%s\n\n%s\n\n:", |
584 | 0 | _("A revocation certificate is a kind of \"kill switch\" to publicly\n" |
585 | 0 | "declare that a key shall not anymore be used. It is not possible\n" |
586 | 0 | "to retract such a revocation certificate once it has been published."), |
587 | 0 | _("Use it to revoke this key in case of a compromise or loss of\n" |
588 | 0 | "the secret key. However, if the secret key is still accessible,\n" |
589 | 0 | "it is better to generate a new revocation certificate and give\n" |
590 | 0 | "a reason for the revocation. For details see the description of\n" |
591 | 0 | "of the gpg command \"--generate-revocation\" in the " |
592 | 0 | "GnuPG manual."), |
593 | 0 | _("To avoid an accidental use of this file, a colon has been inserted\n" |
594 | 0 | "before the 5 dashes below. Remove this colon with a text editor\n" |
595 | 0 | "before importing and publishing this revocation certificate.")); |
596 | |
|
597 | 0 | es_putc (0, memfp); |
598 | |
|
599 | 0 | i18n_switchback (orig_codeset); |
600 | |
|
601 | 0 | if (es_fclose_snatch (memfp, &leadin, NULL)) |
602 | 0 | log_fatal ("error snatching memory stream\n"); |
603 | | |
604 | 0 | reason.code = 0x00; /* No particular reason. */ |
605 | 0 | reason.desc = NULL; |
606 | 0 | old_outfile = opt.outfile; |
607 | 0 | opt.outfile = NULL; |
608 | 0 | rc = create_revocation (ctrl, |
609 | 0 | fname, &reason, psk, NULL, leadin, 3, cache_nonce); |
610 | 0 | opt.outfile = old_outfile; |
611 | 0 | if (!rc && !opt.quiet) |
612 | 0 | log_info (_("revocation certificate stored as '%s.rev'\n"), fname); |
613 | |
|
614 | 0 | xfree (leadin); |
615 | 0 | xfree (fname); |
616 | |
|
617 | 0 | return rc; |
618 | 0 | } |
619 | | |
620 | | |
621 | | |
622 | | /**************** |
623 | | * Generate a revocation certificate for UNAME |
624 | | */ |
625 | | int |
626 | | gen_revoke (ctrl_t ctrl, const char *uname) |
627 | 0 | { |
628 | 0 | int rc = 0; |
629 | 0 | PKT_public_key *psk; |
630 | 0 | u32 keyid[2]; |
631 | 0 | kbnode_t keyblock = NULL; |
632 | 0 | kbnode_t node; |
633 | 0 | KEYDB_HANDLE kdbhd; |
634 | 0 | struct revocation_reason_info *reason = NULL; |
635 | 0 | KEYDB_SEARCH_DESC desc; |
636 | |
|
637 | 0 | if( opt.batch ) |
638 | 0 | { |
639 | 0 | log_error(_("can't do this in batch mode\n")); |
640 | 0 | return GPG_ERR_GENERAL; |
641 | 0 | } |
642 | | |
643 | | /* Search the userid; we don't want the whole getkey stuff here. */ |
644 | 0 | kdbhd = keydb_new (ctrl); |
645 | 0 | if (!kdbhd) |
646 | 0 | { |
647 | 0 | rc = gpg_error_from_syserror (); |
648 | 0 | goto leave; |
649 | 0 | } |
650 | 0 | rc = classify_user_id (uname, &desc, 1); |
651 | 0 | if (!rc) |
652 | 0 | rc = keydb_search (kdbhd, &desc, 1, NULL); |
653 | 0 | if (rc) |
654 | 0 | { |
655 | 0 | if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) |
656 | 0 | log_error (_("secret key \"%s\" not found\n"), uname); |
657 | 0 | else |
658 | 0 | log_error (_("secret key \"%s\" not found: %s\n"), |
659 | 0 | uname, gpg_strerror (rc)); |
660 | 0 | goto leave; |
661 | 0 | } |
662 | | |
663 | 0 | rc = keydb_get_keyblock (kdbhd, &keyblock ); |
664 | 0 | if (rc) |
665 | 0 | { |
666 | 0 | log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); |
667 | 0 | goto leave; |
668 | 0 | } |
669 | | |
670 | 0 | rc = keydb_search (kdbhd, &desc, 1, NULL); |
671 | 0 | if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) |
672 | 0 | { |
673 | | /* Not ambiguous. */ |
674 | 0 | } |
675 | 0 | else if (rc == 0) |
676 | 0 | { |
677 | | /* Ambiguous. */ |
678 | | /* TRANSLATORS: The %s prints a key specification which |
679 | | for example has been given at the command line. Several lines |
680 | | lines with secret key infos are printed after this message. */ |
681 | 0 | log_error (_("'%s' matches multiple secret keys:\n"), uname); |
682 | |
|
683 | 0 | print_key_info_log (ctrl, GPGRT_LOGLVL_ERROR, 2, |
684 | 0 | keyblock->pkt->pkt.public_key, 1); |
685 | 0 | release_kbnode (keyblock); |
686 | |
|
687 | 0 | rc = keydb_get_keyblock (kdbhd, &keyblock); |
688 | 0 | while (! rc) |
689 | 0 | { |
690 | 0 | print_key_info_log (ctrl, GPGRT_LOGLVL_INFO, 2, |
691 | 0 | keyblock->pkt->pkt.public_key, 1); |
692 | 0 | release_kbnode (keyblock); |
693 | 0 | keyblock = NULL; |
694 | |
|
695 | 0 | rc = keydb_search (kdbhd, &desc, 1, NULL); |
696 | 0 | if (! rc) |
697 | 0 | rc = keydb_get_keyblock (kdbhd, &keyblock); |
698 | 0 | } |
699 | |
|
700 | 0 | rc = GPG_ERR_AMBIGUOUS_NAME; |
701 | |
|
702 | 0 | goto leave; |
703 | 0 | } |
704 | 0 | else |
705 | 0 | { |
706 | 0 | log_error (_("error searching the keyring: %s\n"), gpg_strerror (rc)); |
707 | 0 | goto leave; |
708 | 0 | } |
709 | | |
710 | | /* Get the keyid from the keyblock. */ |
711 | 0 | node = find_kbnode (keyblock, PKT_PUBLIC_KEY); |
712 | 0 | if (!node) |
713 | 0 | BUG (); |
714 | | |
715 | 0 | psk = node->pkt->pkt.public_key; |
716 | 0 | if (!agent_probe_secret_key (NULL, psk)) |
717 | 0 | { |
718 | 0 | rc = gpg_error (GPG_ERR_NO_SECKEY); |
719 | 0 | log_error (_("secret key \"%s\" not found: %s\n"), |
720 | 0 | uname, gpg_strerror (rc)); |
721 | 0 | goto leave; |
722 | 0 | } |
723 | | |
724 | 0 | keyid_from_pk (psk, keyid ); |
725 | 0 | print_key_info (ctrl, NULL, 0, psk, 1); |
726 | |
|
727 | 0 | tty_printf("\n"); |
728 | 0 | if (!cpr_get_answer_is_yes ("gen_revoke.okay", |
729 | 0 | _("Create a revocation certificate for this key? (y/N) "))) |
730 | 0 | { |
731 | 0 | rc = 0; |
732 | 0 | goto leave; |
733 | 0 | } |
734 | | |
735 | | /* Get the reason for the revocation. */ |
736 | 0 | reason = ask_revocation_reason (1, 0, 1); |
737 | 0 | if (!reason) |
738 | 0 | { |
739 | | /* User decided to cancel. */ |
740 | 0 | rc = 0; |
741 | 0 | goto leave; |
742 | 0 | } |
743 | | |
744 | 0 | if (!opt.armor) |
745 | 0 | tty_printf (_("ASCII armored output forced.\n")); |
746 | |
|
747 | 0 | rc = create_revocation (ctrl, NULL, reason, psk, keyblock, NULL, 0, NULL); |
748 | 0 | if (rc) |
749 | 0 | goto leave; |
750 | | |
751 | | /* and issue a usage notice */ |
752 | 0 | tty_printf (_( |
753 | 0 | "Revocation certificate created.\n\n" |
754 | 0 | "Please move it to a medium which you can hide away; if Mallory gets\n" |
755 | 0 | "access to this certificate he can use it to make your key unusable.\n" |
756 | 0 | "It is smart to print this certificate and store it away, just in case\n" |
757 | 0 | "your media become unreadable. But have some caution: The print system of\n" |
758 | 0 | "your machine might store the data and make it available to others!\n")); |
759 | |
|
760 | 0 | leave: |
761 | 0 | release_kbnode (keyblock); |
762 | 0 | keydb_release (kdbhd); |
763 | 0 | release_revocation_reason_info( reason ); |
764 | 0 | return rc; |
765 | 0 | } |
766 | | |
767 | | |
768 | | |
769 | | struct revocation_reason_info * |
770 | | ask_revocation_reason( int key_rev, int cert_rev, int hint ) |
771 | 0 | { |
772 | 0 | int code=-1; |
773 | 0 | char *description = NULL; |
774 | 0 | struct revocation_reason_info *reason; |
775 | 0 | const char *text_0 = _("No reason specified"); |
776 | 0 | const char *text_1 = _("Key has been compromised"); |
777 | 0 | const char *text_2 = _("Key is superseded"); |
778 | 0 | const char *text_3 = _("Key is no longer used"); |
779 | 0 | const char *text_4 = _("User ID is no longer valid"); |
780 | 0 | const char *code_text = NULL; |
781 | |
|
782 | 0 | do { |
783 | 0 | code=-1; |
784 | 0 | xfree(description); |
785 | 0 | description = NULL; |
786 | |
|
787 | 0 | tty_printf(_("Please select the reason for the revocation:\n")); |
788 | 0 | tty_printf( " 0 = %s\n", text_0 ); |
789 | 0 | if( key_rev ) |
790 | 0 | tty_printf(" 1 = %s\n", text_1 ); |
791 | 0 | if( key_rev ) |
792 | 0 | tty_printf(" 2 = %s\n", text_2 ); |
793 | 0 | if( key_rev ) |
794 | 0 | tty_printf(" 3 = %s\n", text_3 ); |
795 | 0 | if( cert_rev ) |
796 | 0 | tty_printf(" 4 = %s\n", text_4 ); |
797 | 0 | tty_printf( " Q = %s\n", _("Cancel") ); |
798 | 0 | if( hint ) |
799 | 0 | tty_printf(_("(Probably you want to select %d here)\n"), hint ); |
800 | |
|
801 | 0 | while(code==-1) { |
802 | 0 | int n; |
803 | 0 | char *answer = cpr_get("ask_revocation_reason.code", |
804 | 0 | _("Your decision? ")); |
805 | 0 | trim_spaces( answer ); |
806 | 0 | cpr_kill_prompt(); |
807 | 0 | if( *answer == 'q' || *answer == 'Q') |
808 | 0 | { |
809 | 0 | xfree (answer); |
810 | 0 | return NULL; /* cancel */ |
811 | 0 | } |
812 | 0 | if( hint && !*answer ) |
813 | 0 | n = hint; |
814 | 0 | else if(!digitp( answer ) ) |
815 | 0 | n = -1; |
816 | 0 | else |
817 | 0 | n = atoi(answer); |
818 | 0 | xfree(answer); |
819 | 0 | if( n == 0 ) { |
820 | 0 | code = 0x00; /* no particular reason */ |
821 | 0 | code_text = text_0; |
822 | 0 | } |
823 | 0 | else if( key_rev && n == 1 ) { |
824 | 0 | code = 0x02; /* key has been compromised */ |
825 | 0 | code_text = text_1; |
826 | 0 | } |
827 | 0 | else if( key_rev && n == 2 ) { |
828 | 0 | code = 0x01; /* key is superseded */ |
829 | 0 | code_text = text_2; |
830 | 0 | } |
831 | 0 | else if( key_rev && n == 3 ) { |
832 | 0 | code = 0x03; /* key is no longer used */ |
833 | 0 | code_text = text_3; |
834 | 0 | } |
835 | 0 | else if( cert_rev && n == 4 ) { |
836 | 0 | code = 0x20; /* uid is no longer valid */ |
837 | 0 | code_text = text_4; |
838 | 0 | } |
839 | 0 | else |
840 | 0 | tty_printf(_("Invalid selection.\n")); |
841 | 0 | } |
842 | | |
843 | 0 | tty_printf(_("Enter an optional description; " |
844 | 0 | "end it with an empty line:\n") ); |
845 | 0 | for(;;) { |
846 | 0 | char *answer = cpr_get("ask_revocation_reason.text", "> " ); |
847 | 0 | trim_trailing_ws( answer, strlen(answer) ); |
848 | 0 | cpr_kill_prompt(); |
849 | 0 | if( !*answer ) { |
850 | 0 | xfree(answer); |
851 | 0 | break; |
852 | 0 | } |
853 | | |
854 | 0 | { |
855 | 0 | char *p = make_printable_string( answer, strlen(answer), 0 ); |
856 | 0 | xfree(answer); |
857 | 0 | answer = p; |
858 | 0 | } |
859 | |
|
860 | 0 | if( !description ) |
861 | 0 | description = xstrdup(answer); |
862 | 0 | else { |
863 | 0 | char *p = xmalloc( strlen(description) + strlen(answer) + 2 ); |
864 | 0 | strcpy(stpcpy(stpcpy( p, description),"\n"),answer); |
865 | 0 | xfree(description); |
866 | 0 | description = p; |
867 | 0 | } |
868 | 0 | xfree(answer); |
869 | 0 | } |
870 | |
|
871 | 0 | tty_printf(_("Reason for revocation: %s\n"), code_text ); |
872 | 0 | if( !description ) |
873 | 0 | tty_printf(_("(No description given)\n") ); |
874 | 0 | else |
875 | 0 | tty_printf("%s\n", description ); |
876 | |
|
877 | 0 | } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay", |
878 | 0 | _("Is this okay? (y/N) ")) ); |
879 | | |
880 | 0 | reason = xmalloc( sizeof *reason ); |
881 | 0 | reason->code = code; |
882 | 0 | reason->desc = description; |
883 | 0 | return reason; |
884 | 0 | } |
885 | | |
886 | | struct revocation_reason_info * |
887 | | get_default_uid_revocation_reason(void) |
888 | 0 | { |
889 | 0 | struct revocation_reason_info *reason; |
890 | 0 | reason = xmalloc( sizeof *reason ); |
891 | 0 | reason->code = 0x20; /* uid is no longer valid */ |
892 | 0 | reason->desc = strdup(""); /* no text */ |
893 | 0 | return reason; |
894 | 0 | } |
895 | | |
896 | | struct revocation_reason_info * |
897 | | get_default_sig_revocation_reason(void) |
898 | 0 | { |
899 | 0 | struct revocation_reason_info *reason; |
900 | 0 | reason = xmalloc( sizeof *reason ); |
901 | 0 | reason->code = 0; /* No specific reason given. */ |
902 | 0 | reason->desc = strdup(""); /* no text */ |
903 | 0 | return reason; |
904 | 0 | } |
905 | | |
906 | | void |
907 | | release_revocation_reason_info( struct revocation_reason_info *reason ) |
908 | 0 | { |
909 | 0 | if( reason ) { |
910 | 0 | xfree( reason->desc ); |
911 | 0 | xfree( reason ); |
912 | 0 | } |
913 | 0 | } |