1/* $NetBSD: dnssec-revoke.c,v 1.9 2024/02/21 22:51:03 christos Exp $ */ 2 3/* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16/*! \file */ 17 18#include <inttypes.h> 19#include <stdbool.h> 20#include <stdlib.h> 21#include <unistd.h> 22 23#include <isc/attributes.h> 24#include <isc/buffer.h> 25#include <isc/commandline.h> 26#include <isc/file.h> 27#include <isc/hash.h> 28#include <isc/mem.h> 29#include <isc/print.h> 30#include <isc/result.h> 31#include <isc/string.h> 32#include <isc/util.h> 33 34#include <dns/keyvalues.h> 35 36#include <dst/dst.h> 37 38#include "dnssectool.h" 39 40const char *program = "dnssec-revoke"; 41 42static isc_mem_t *mctx = NULL; 43 44noreturn static void 45usage(void); 46 47static void 48usage(void) { 49 fprintf(stderr, "Usage:\n"); 50 fprintf(stderr, " %s [options] keyfile\n\n", program); 51 fprintf(stderr, "Version: %s\n", PACKAGE_VERSION); 52 fprintf(stderr, " -E engine: specify OpenSSL engine\n"); 53 fprintf(stderr, " -f: force overwrite\n"); 54 fprintf(stderr, " -h: help\n"); 55 fprintf(stderr, " -K directory: use directory for key files\n"); 56 fprintf(stderr, " -r: remove old keyfiles after " 57 "creating revoked version\n"); 58 fprintf(stderr, " -v level: set level of verbosity\n"); 59 fprintf(stderr, " -V: print version information\n"); 60 fprintf(stderr, "Output:\n"); 61 fprintf(stderr, " K<name>+<alg>+<new id>.key, " 62 "K<name>+<alg>+<new id>.private\n"); 63 64 exit(-1); 65} 66 67int 68main(int argc, char **argv) { 69 isc_result_t result; 70 const char *engine = NULL; 71 char const *filename = NULL; 72 char *dir = NULL; 73 char newname[1024], oldname[1024]; 74 char keystr[DST_KEY_FORMATSIZE]; 75 char *endp; 76 int ch; 77 dst_key_t *key = NULL; 78 uint32_t flags; 79 isc_buffer_t buf; 80 bool force = false; 81 bool removefile = false; 82 bool id = false; 83 84 if (argc == 1) { 85 usage(); 86 } 87 88 isc_mem_create(&mctx); 89 90 isc_commandline_errprint = false; 91 92 while ((ch = isc_commandline_parse(argc, argv, "E:fK:rRhv:V")) != -1) { 93 switch (ch) { 94 case 'E': 95 engine = isc_commandline_argument; 96 break; 97 case 'f': 98 force = true; 99 break; 100 case 'K': 101 /* 102 * We don't have to copy it here, but do it to 103 * simplify cleanup later 104 */ 105 dir = isc_mem_strdup(mctx, isc_commandline_argument); 106 break; 107 case 'r': 108 removefile = true; 109 break; 110 case 'R': 111 id = true; 112 break; 113 case 'v': 114 verbose = strtol(isc_commandline_argument, &endp, 0); 115 if (*endp != '\0') { 116 fatal("-v must be followed by a number"); 117 } 118 break; 119 case '?': 120 if (isc_commandline_option != '?') { 121 fprintf(stderr, "%s: invalid argument -%c\n", 122 program, isc_commandline_option); 123 } 124 FALLTHROUGH; 125 case 'h': 126 /* Does not return. */ 127 usage(); 128 129 case 'V': 130 /* Does not return. */ 131 version(program); 132 133 default: 134 fprintf(stderr, "%s: unhandled option -%c\n", program, 135 isc_commandline_option); 136 exit(1); 137 } 138 } 139 140 if (argc < isc_commandline_index + 1 || 141 argv[isc_commandline_index] == NULL) 142 { 143 fatal("The key file name was not specified"); 144 } 145 if (argc > isc_commandline_index + 1) { 146 fatal("Extraneous arguments"); 147 } 148 149 if (dir != NULL) { 150 filename = argv[isc_commandline_index]; 151 } else { 152 result = isc_file_splitpath(mctx, argv[isc_commandline_index], 153 &dir, &filename); 154 if (result != ISC_R_SUCCESS) { 155 fatal("cannot process filename %s: %s", 156 argv[isc_commandline_index], 157 isc_result_totext(result)); 158 } 159 if (strcmp(dir, ".") == 0) { 160 isc_mem_free(mctx, dir); 161 dir = NULL; 162 } 163 } 164 165 result = dst_lib_init(mctx, engine); 166 if (result != ISC_R_SUCCESS) { 167 fatal("Could not initialize dst: %s", 168 isc_result_totext(result)); 169 } 170 171 result = dst_key_fromnamedfile( 172 filename, dir, DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, mctx, &key); 173 if (result != ISC_R_SUCCESS) { 174 fatal("Invalid keyfile name %s: %s", filename, 175 isc_result_totext(result)); 176 } 177 178 if (id) { 179 fprintf(stdout, "%u\n", dst_key_rid(key)); 180 goto cleanup; 181 } 182 dst_key_format(key, keystr, sizeof(keystr)); 183 184 if (verbose > 2) { 185 fprintf(stderr, "%s: %s\n", program, keystr); 186 } 187 188 if (force) { 189 set_keyversion(key); 190 } else { 191 check_keyversion(key, keystr); 192 } 193 194 flags = dst_key_flags(key); 195 if ((flags & DNS_KEYFLAG_REVOKE) == 0) { 196 isc_stdtime_t now; 197 198 if ((flags & DNS_KEYFLAG_KSK) == 0) { 199 fprintf(stderr, 200 "%s: warning: Key is not flagged " 201 "as a KSK. Revoking a ZSK is " 202 "legal, but undefined.\n", 203 program); 204 } 205 206 isc_stdtime_get(&now); 207 dst_key_settime(key, DST_TIME_REVOKE, now); 208 209 dst_key_setflags(key, flags | DNS_KEYFLAG_REVOKE); 210 211 isc_buffer_init(&buf, newname, sizeof(newname)); 212 dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf); 213 214 if (access(newname, F_OK) == 0 && !force) { 215 fatal("Key file %s already exists; " 216 "use -f to force overwrite", 217 newname); 218 } 219 220 result = dst_key_tofile(key, DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, 221 dir); 222 if (result != ISC_R_SUCCESS) { 223 dst_key_format(key, keystr, sizeof(keystr)); 224 fatal("Failed to write key %s: %s", keystr, 225 isc_result_totext(result)); 226 } 227 228 isc_buffer_clear(&buf); 229 dst_key_buildfilename(key, 0, dir, &buf); 230 printf("%s\n", newname); 231 232 /* 233 * Remove old key file, if told to (and if 234 * it isn't the same as the new file) 235 */ 236 if (removefile) { 237 isc_buffer_init(&buf, oldname, sizeof(oldname)); 238 dst_key_setflags(key, flags & ~DNS_KEYFLAG_REVOKE); 239 dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf); 240 if (strcmp(oldname, newname) == 0) { 241 goto cleanup; 242 } 243 (void)unlink(oldname); 244 isc_buffer_clear(&buf); 245 dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf); 246 (void)unlink(oldname); 247 } 248 } else { 249 dst_key_format(key, keystr, sizeof(keystr)); 250 fatal("Key %s is already revoked", keystr); 251 } 252 253cleanup: 254 dst_key_free(&key); 255 dst_lib_destroy(); 256 if (verbose > 10) { 257 isc_mem_stats(mctx, stdout); 258 } 259 if (dir != NULL) { 260 isc_mem_free(mctx, dir); 261 } 262 isc_mem_destroy(&mctx); 263 264 return (0); 265} 266