1*bcda20f6Schristos /* $NetBSD: dst_parse.c,v 1.12 2025/01/26 16:25:22 christos Exp $ */ 2d68c78b8Schristos 3d68c78b8Schristos /* 48596601aSchristos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 58596601aSchristos * 68596601aSchristos * SPDX-License-Identifier: MPL-2.0 AND ISC 7d68c78b8Schristos * 8d68c78b8Schristos * This Source Code Form is subject to the terms of the Mozilla Public 9d68c78b8Schristos * License, v. 2.0. If a copy of the MPL was not distributed with this 10fce770bdSchristos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11d68c78b8Schristos * 12d68c78b8Schristos * See the COPYRIGHT file distributed with this work for additional 13d68c78b8Schristos * information regarding copyright ownership. 148596601aSchristos */ 158596601aSchristos 168596601aSchristos /* 178596601aSchristos * Copyright (C) Network Associates, Inc. 18d68c78b8Schristos * 19d68c78b8Schristos * Permission to use, copy, modify, and/or distribute this software for any 20d68c78b8Schristos * purpose with or without fee is hereby granted, provided that the above 21d68c78b8Schristos * copyright notice and this permission notice appear in all copies. 22d68c78b8Schristos * 23d68c78b8Schristos * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS 24d68c78b8Schristos * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 25d68c78b8Schristos * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE 26d68c78b8Schristos * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 27d68c78b8Schristos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 28d68c78b8Schristos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 29d68c78b8Schristos * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 30d68c78b8Schristos */ 31d68c78b8Schristos 325606745fSchristos #include "dst_parse.h" 33d4a20c3eSchristos #include <inttypes.h> 34d4a20c3eSchristos #include <stdbool.h> 35bb5aa156Schristos #include <unistd.h> 36d4a20c3eSchristos 37d68c78b8Schristos #include <isc/base64.h> 38d68c78b8Schristos #include <isc/dir.h> 39d68c78b8Schristos #include <isc/file.h> 40d68c78b8Schristos #include <isc/lex.h> 41d68c78b8Schristos #include <isc/mem.h> 42d68c78b8Schristos #include <isc/stdtime.h> 43d68c78b8Schristos #include <isc/string.h> 44d68c78b8Schristos #include <isc/util.h> 45d68c78b8Schristos 46d68c78b8Schristos #include <dns/log.h> 475606745fSchristos #include <dns/time.h> 48d68c78b8Schristos 495606745fSchristos #include "dst_internal.h" 50bb5aa156Schristos #include "isc/result.h" 51d68c78b8Schristos 52d68c78b8Schristos #define DST_AS_STR(t) ((t).value.as_textregion.base) 53d68c78b8Schristos 54d68c78b8Schristos #define PRIVATE_KEY_STR "Private-key-format:" 55d68c78b8Schristos #define ALGORITHM_STR "Algorithm:" 56d68c78b8Schristos 57d68c78b8Schristos #define TIMING_NTAGS (DST_MAX_TIMES + 1) 58d68c78b8Schristos static const char *timetags[TIMING_NTAGS] = { 595606745fSchristos "Created:", "Publish:", "Activate:", "Revoke:", 605606745fSchristos "Inactive:", "Delete:", "DSPublish:", "SyncPublish:", 615606745fSchristos "SyncDelete:", NULL, NULL, NULL, 625606745fSchristos NULL 63d68c78b8Schristos }; 64d68c78b8Schristos 65d68c78b8Schristos #define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1) 66d68c78b8Schristos static const char *numerictags[NUMERIC_NTAGS] = { 6709f4e0c3Schristos "Predecessor:", "Successor:", "MaxTTL:", "RollPeriod:", NULL, NULL, NULL 68d68c78b8Schristos }; 69d68c78b8Schristos 70d68c78b8Schristos struct parse_map { 71d68c78b8Schristos const int value; 72d68c78b8Schristos const char *tag; 73d68c78b8Schristos }; 74d68c78b8Schristos 755606745fSchristos static struct parse_map map[] = { { TAG_RSA_MODULUS, "Modulus:" }, 76d68c78b8Schristos { TAG_RSA_PUBLICEXPONENT, "PublicExponent:" }, 775606745fSchristos { TAG_RSA_PRIVATEEXPONENT, "PrivateExponent" 785606745fSchristos ":" }, 79d68c78b8Schristos { TAG_RSA_PRIME1, "Prime1:" }, 80d68c78b8Schristos { TAG_RSA_PRIME2, "Prime2:" }, 81d68c78b8Schristos { TAG_RSA_EXPONENT1, "Exponent1:" }, 82d68c78b8Schristos { TAG_RSA_EXPONENT2, "Exponent2:" }, 83d68c78b8Schristos { TAG_RSA_COEFFICIENT, "Coefficient:" }, 84d68c78b8Schristos { TAG_RSA_ENGINE, "Engine:" }, 85d68c78b8Schristos { TAG_RSA_LABEL, "Label:" }, 86d68c78b8Schristos 87d68c78b8Schristos { TAG_ECDSA_PRIVATEKEY, "PrivateKey:" }, 88d68c78b8Schristos { TAG_ECDSA_ENGINE, "Engine:" }, 89d68c78b8Schristos { TAG_ECDSA_LABEL, "Label:" }, 90d68c78b8Schristos 91d68c78b8Schristos { TAG_EDDSA_PRIVATEKEY, "PrivateKey:" }, 92d68c78b8Schristos { TAG_EDDSA_ENGINE, "Engine:" }, 93d68c78b8Schristos { TAG_EDDSA_LABEL, "Label:" }, 94d68c78b8Schristos 95d68c78b8Schristos { TAG_HMACMD5_KEY, "Key:" }, 96d68c78b8Schristos { TAG_HMACMD5_BITS, "Bits:" }, 97d68c78b8Schristos 98d68c78b8Schristos { TAG_HMACSHA1_KEY, "Key:" }, 99d68c78b8Schristos { TAG_HMACSHA1_BITS, "Bits:" }, 100d68c78b8Schristos 101d68c78b8Schristos { TAG_HMACSHA224_KEY, "Key:" }, 102d68c78b8Schristos { TAG_HMACSHA224_BITS, "Bits:" }, 103d68c78b8Schristos 104d68c78b8Schristos { TAG_HMACSHA256_KEY, "Key:" }, 105d68c78b8Schristos { TAG_HMACSHA256_BITS, "Bits:" }, 106d68c78b8Schristos 107d68c78b8Schristos { TAG_HMACSHA384_KEY, "Key:" }, 108d68c78b8Schristos { TAG_HMACSHA384_BITS, "Bits:" }, 109d68c78b8Schristos 110d68c78b8Schristos { TAG_HMACSHA512_KEY, "Key:" }, 111d68c78b8Schristos { TAG_HMACSHA512_BITS, "Bits:" }, 112d68c78b8Schristos 1135606745fSchristos { 0, NULL } }; 114d68c78b8Schristos 115d68c78b8Schristos static int 116d68c78b8Schristos find_value(const char *s, const unsigned int alg) { 117d68c78b8Schristos int i; 118d68c78b8Schristos 119d68c78b8Schristos for (i = 0; map[i].tag != NULL; i++) { 120d68c78b8Schristos if (strcasecmp(s, map[i].tag) == 0 && 121903adeddSchristos (TAG_ALG(map[i].value) == alg)) 122903adeddSchristos { 123*bcda20f6Schristos return map[i].value; 124d68c78b8Schristos } 1255606745fSchristos } 126*bcda20f6Schristos return -1; 127d68c78b8Schristos } 128d68c78b8Schristos 129d68c78b8Schristos static const char * 130d68c78b8Schristos find_tag(const int value) { 131d68c78b8Schristos int i; 132d68c78b8Schristos 133d68c78b8Schristos for (i = 0;; i++) { 1345606745fSchristos if (map[i].tag == NULL) { 135*bcda20f6Schristos return NULL; 1365606745fSchristos } else if (value == map[i].value) { 137*bcda20f6Schristos return map[i].tag; 138d68c78b8Schristos } 139d68c78b8Schristos } 1405606745fSchristos } 141d68c78b8Schristos 142d68c78b8Schristos static int 143d68c78b8Schristos find_metadata(const char *s, const char *tags[], int ntags) { 144d68c78b8Schristos int i; 145d68c78b8Schristos 146d68c78b8Schristos for (i = 0; i < ntags; i++) { 1475606745fSchristos if (tags[i] != NULL && strcasecmp(s, tags[i]) == 0) { 148*bcda20f6Schristos return i; 149d68c78b8Schristos } 1505606745fSchristos } 151d68c78b8Schristos 152*bcda20f6Schristos return -1; 153d68c78b8Schristos } 154d68c78b8Schristos 155d68c78b8Schristos static int 156d68c78b8Schristos find_timedata(const char *s) { 157*bcda20f6Schristos return find_metadata(s, timetags, TIMING_NTAGS); 158d68c78b8Schristos } 159d68c78b8Schristos 160d68c78b8Schristos static int 161d68c78b8Schristos find_numericdata(const char *s) { 162*bcda20f6Schristos return find_metadata(s, numerictags, NUMERIC_NTAGS); 163d68c78b8Schristos } 164d68c78b8Schristos 165d68c78b8Schristos static int 166d4a20c3eSchristos check_rsa(const dst_private_t *priv, bool external) { 167d68c78b8Schristos int i, j; 168d4a20c3eSchristos bool have[RSA_NTAGS]; 169d4a20c3eSchristos bool ok; 170d68c78b8Schristos unsigned int mask; 171d68c78b8Schristos 1725606745fSchristos if (external) { 173*bcda20f6Schristos return (priv->nelements == 0) ? 0 : -1; 1745606745fSchristos } 175d68c78b8Schristos 1765606745fSchristos for (i = 0; i < RSA_NTAGS; i++) { 177d4a20c3eSchristos have[i] = false; 1785606745fSchristos } 179d68c78b8Schristos 180d68c78b8Schristos for (j = 0; j < priv->nelements; j++) { 1815606745fSchristos for (i = 0; i < RSA_NTAGS; i++) { 1825606745fSchristos if (priv->elements[j].tag == TAG(DST_ALG_RSA, i)) { 183d68c78b8Schristos break; 1845606745fSchristos } 1855606745fSchristos } 1865606745fSchristos if (i == RSA_NTAGS) { 187*bcda20f6Schristos return -1; 1885606745fSchristos } 189d4a20c3eSchristos have[i] = true; 190d68c78b8Schristos } 191d68c78b8Schristos 192d68c78b8Schristos mask = (1ULL << TAG_SHIFT) - 1; 193d68c78b8Schristos 194*bcda20f6Schristos if (have[TAG_RSA_LABEL & mask]) { 195d68c78b8Schristos ok = have[TAG_RSA_MODULUS & mask] && 196*bcda20f6Schristos have[TAG_RSA_PUBLICEXPONENT & mask]; 1975606745fSchristos } else { 198d68c78b8Schristos ok = have[TAG_RSA_MODULUS & mask] && 199d68c78b8Schristos have[TAG_RSA_PUBLICEXPONENT & mask] && 200d68c78b8Schristos have[TAG_RSA_PRIVATEEXPONENT & mask] && 201d68c78b8Schristos have[TAG_RSA_PRIME1 & mask] && 202d68c78b8Schristos have[TAG_RSA_PRIME2 & mask] && 203d68c78b8Schristos have[TAG_RSA_EXPONENT1 & mask] && 204d68c78b8Schristos have[TAG_RSA_EXPONENT2 & mask] && 205d68c78b8Schristos have[TAG_RSA_COEFFICIENT & mask]; 2065606745fSchristos } 207*bcda20f6Schristos return ok ? 0 : -1; 208d68c78b8Schristos } 209d68c78b8Schristos 210d68c78b8Schristos static int 211d4a20c3eSchristos check_ecdsa(const dst_private_t *priv, bool external) { 212d68c78b8Schristos int i, j; 213d4a20c3eSchristos bool have[ECDSA_NTAGS]; 214d4a20c3eSchristos bool ok; 215d68c78b8Schristos unsigned int mask; 216d68c78b8Schristos 2175606745fSchristos if (external) { 218*bcda20f6Schristos return (priv->nelements == 0) ? 0 : -1; 2195606745fSchristos } 220d68c78b8Schristos 2215606745fSchristos for (i = 0; i < ECDSA_NTAGS; i++) { 222d4a20c3eSchristos have[i] = false; 2235606745fSchristos } 224d68c78b8Schristos for (j = 0; j < priv->nelements; j++) { 2255606745fSchristos for (i = 0; i < ECDSA_NTAGS; i++) { 2265606745fSchristos if (priv->elements[j].tag == TAG(DST_ALG_ECDSA256, i)) { 227d68c78b8Schristos break; 2285606745fSchristos } 2295606745fSchristos } 2305606745fSchristos if (i == ECDSA_NTAGS) { 231*bcda20f6Schristos return -1; 2325606745fSchristos } 233d4a20c3eSchristos have[i] = true; 234d68c78b8Schristos } 235d68c78b8Schristos 236d68c78b8Schristos mask = (1ULL << TAG_SHIFT) - 1; 237d68c78b8Schristos 238*bcda20f6Schristos ok = have[TAG_ECDSA_LABEL & mask] || have[TAG_ECDSA_PRIVATEKEY & mask]; 239*bcda20f6Schristos 240*bcda20f6Schristos return ok ? 0 : -1; 241d68c78b8Schristos } 242d68c78b8Schristos 243d68c78b8Schristos static int 244d4a20c3eSchristos check_eddsa(const dst_private_t *priv, bool external) { 245d68c78b8Schristos int i, j; 246d4a20c3eSchristos bool have[EDDSA_NTAGS]; 247d4a20c3eSchristos bool ok; 248d68c78b8Schristos unsigned int mask; 249d68c78b8Schristos 2505606745fSchristos if (external) { 251*bcda20f6Schristos return (priv->nelements == 0) ? 0 : -1; 2525606745fSchristos } 253d68c78b8Schristos 2545606745fSchristos for (i = 0; i < EDDSA_NTAGS; i++) { 255d4a20c3eSchristos have[i] = false; 2565606745fSchristos } 257d68c78b8Schristos for (j = 0; j < priv->nelements; j++) { 2585606745fSchristos for (i = 0; i < EDDSA_NTAGS; i++) { 2595606745fSchristos if (priv->elements[j].tag == TAG(DST_ALG_ED25519, i)) { 260d68c78b8Schristos break; 2615606745fSchristos } 2625606745fSchristos } 2635606745fSchristos if (i == EDDSA_NTAGS) { 264*bcda20f6Schristos return -1; 2655606745fSchristos } 266d4a20c3eSchristos have[i] = true; 267d68c78b8Schristos } 268d68c78b8Schristos 269d68c78b8Schristos mask = (1ULL << TAG_SHIFT) - 1; 270d68c78b8Schristos 271*bcda20f6Schristos ok = have[TAG_EDDSA_LABEL & mask] || have[TAG_EDDSA_PRIVATEKEY & mask]; 272*bcda20f6Schristos 273*bcda20f6Schristos return ok ? 0 : -1; 274d68c78b8Schristos } 275d68c78b8Schristos 276d68c78b8Schristos static int 277d4a20c3eSchristos check_hmac_md5(const dst_private_t *priv, bool old) { 278d68c78b8Schristos int i, j; 279d68c78b8Schristos 280d68c78b8Schristos if (priv->nelements != HMACMD5_NTAGS) { 281d68c78b8Schristos /* 282d68c78b8Schristos * If this is a good old format and we are accepting 283d68c78b8Schristos * the old format return success. 284d68c78b8Schristos */ 285d68c78b8Schristos if (old && priv->nelements == OLD_HMACMD5_NTAGS && 286d68c78b8Schristos priv->elements[0].tag == TAG_HMACMD5_KEY) 2875606745fSchristos { 288*bcda20f6Schristos return 0; 2895606745fSchristos } 290*bcda20f6Schristos return -1; 291d68c78b8Schristos } 292d68c78b8Schristos /* 293d68c78b8Schristos * We must be new format at this point. 294d68c78b8Schristos */ 295d68c78b8Schristos for (i = 0; i < HMACMD5_NTAGS; i++) { 2965606745fSchristos for (j = 0; j < priv->nelements; j++) { 2975606745fSchristos if (priv->elements[j].tag == TAG(DST_ALG_HMACMD5, i)) { 298d68c78b8Schristos break; 2995606745fSchristos } 3005606745fSchristos } 3015606745fSchristos if (j == priv->nelements) { 302*bcda20f6Schristos return -1; 303d68c78b8Schristos } 3045606745fSchristos } 305*bcda20f6Schristos return 0; 306d68c78b8Schristos } 307d68c78b8Schristos 308d68c78b8Schristos static int 309d68c78b8Schristos check_hmac_sha(const dst_private_t *priv, unsigned int ntags, 3105606745fSchristos unsigned int alg) { 311d68c78b8Schristos unsigned int i, j; 3125606745fSchristos if (priv->nelements != ntags) { 313*bcda20f6Schristos return -1; 3145606745fSchristos } 315d68c78b8Schristos for (i = 0; i < ntags; i++) { 3165606745fSchristos for (j = 0; j < priv->nelements; j++) { 3175606745fSchristos if (priv->elements[j].tag == TAG(alg, i)) { 318d68c78b8Schristos break; 3195606745fSchristos } 3205606745fSchristos } 3215606745fSchristos if (j == priv->nelements) { 322*bcda20f6Schristos return -1; 323d68c78b8Schristos } 3245606745fSchristos } 325*bcda20f6Schristos return 0; 326d68c78b8Schristos } 327d68c78b8Schristos 328d68c78b8Schristos static int 3295606745fSchristos check_data(const dst_private_t *priv, const unsigned int alg, bool old, 3305606745fSchristos bool external) { 331d68c78b8Schristos switch (alg) { 3325e267ba4Schristos case DST_ALG_RSA: 333d68c78b8Schristos case DST_ALG_RSASHA1: 334d68c78b8Schristos case DST_ALG_NSEC3RSASHA1: 335d68c78b8Schristos case DST_ALG_RSASHA256: 336d68c78b8Schristos case DST_ALG_RSASHA512: 337*bcda20f6Schristos return check_rsa(priv, external); 338d68c78b8Schristos case DST_ALG_ECDSA256: 339d68c78b8Schristos case DST_ALG_ECDSA384: 340*bcda20f6Schristos return check_ecdsa(priv, external); 341d68c78b8Schristos case DST_ALG_ED25519: 342d68c78b8Schristos case DST_ALG_ED448: 343*bcda20f6Schristos return check_eddsa(priv, external); 344d68c78b8Schristos case DST_ALG_HMACMD5: 345*bcda20f6Schristos return check_hmac_md5(priv, old); 346d68c78b8Schristos case DST_ALG_HMACSHA1: 347*bcda20f6Schristos return check_hmac_sha(priv, HMACSHA1_NTAGS, alg); 348d68c78b8Schristos case DST_ALG_HMACSHA224: 349*bcda20f6Schristos return check_hmac_sha(priv, HMACSHA224_NTAGS, alg); 350d68c78b8Schristos case DST_ALG_HMACSHA256: 351*bcda20f6Schristos return check_hmac_sha(priv, HMACSHA256_NTAGS, alg); 352d68c78b8Schristos case DST_ALG_HMACSHA384: 353*bcda20f6Schristos return check_hmac_sha(priv, HMACSHA384_NTAGS, alg); 354d68c78b8Schristos case DST_ALG_HMACSHA512: 355*bcda20f6Schristos return check_hmac_sha(priv, HMACSHA512_NTAGS, alg); 356d68c78b8Schristos default: 357*bcda20f6Schristos return DST_R_UNSUPPORTEDALG; 358d68c78b8Schristos } 359d68c78b8Schristos } 360d68c78b8Schristos 361d68c78b8Schristos void 362d68c78b8Schristos dst__privstruct_free(dst_private_t *priv, isc_mem_t *mctx) { 363d68c78b8Schristos int i; 364d68c78b8Schristos 3655606745fSchristos if (priv == NULL) { 366d68c78b8Schristos return; 3675606745fSchristos } 368d68c78b8Schristos for (i = 0; i < priv->nelements; i++) { 3695606745fSchristos if (priv->elements[i].data == NULL) { 370d68c78b8Schristos continue; 3715606745fSchristos } 372d68c78b8Schristos memset(priv->elements[i].data, 0, MAXFIELDSIZE); 373d68c78b8Schristos isc_mem_put(mctx, priv->elements[i].data, MAXFIELDSIZE); 374d68c78b8Schristos } 375d68c78b8Schristos priv->nelements = 0; 376d68c78b8Schristos } 377d68c78b8Schristos 378d68c78b8Schristos isc_result_t 379d68c78b8Schristos dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex, 3805606745fSchristos isc_mem_t *mctx, dst_private_t *priv) { 381d68c78b8Schristos int n = 0, major, minor, check; 382d68c78b8Schristos isc_buffer_t b; 383d68c78b8Schristos isc_token_t token; 384d68c78b8Schristos unsigned char *data = NULL; 385d68c78b8Schristos unsigned int opt = ISC_LEXOPT_EOL; 386d68c78b8Schristos isc_stdtime_t when; 387d68c78b8Schristos isc_result_t ret; 388d4a20c3eSchristos bool external = false; 389d68c78b8Schristos 390d68c78b8Schristos REQUIRE(priv != NULL); 391d68c78b8Schristos 392d68c78b8Schristos priv->nelements = 0; 393d68c78b8Schristos memset(priv->elements, 0, sizeof(priv->elements)); 394d68c78b8Schristos 395d68c78b8Schristos #define NEXTTOKEN(lex, opt, token) \ 396d68c78b8Schristos do { \ 397d68c78b8Schristos ret = isc_lex_gettoken(lex, opt, token); \ 398d68c78b8Schristos if (ret != ISC_R_SUCCESS) \ 399d68c78b8Schristos goto fail; \ 40053cc4e50Srillig } while (0) 401d68c78b8Schristos 402d68c78b8Schristos #define READLINE(lex, opt, token) \ 403d68c78b8Schristos do { \ 404d68c78b8Schristos ret = isc_lex_gettoken(lex, opt, token); \ 405d68c78b8Schristos if (ret == ISC_R_EOF) \ 406d68c78b8Schristos break; \ 407d68c78b8Schristos else if (ret != ISC_R_SUCCESS) \ 408d68c78b8Schristos goto fail; \ 409d68c78b8Schristos } while ((*token).type != isc_tokentype_eol) 410d68c78b8Schristos 411d68c78b8Schristos /* 412d68c78b8Schristos * Read the description line. 413d68c78b8Schristos */ 414d68c78b8Schristos NEXTTOKEN(lex, opt, &token); 415d68c78b8Schristos if (token.type != isc_tokentype_string || 416d68c78b8Schristos strcmp(DST_AS_STR(token), PRIVATE_KEY_STR) != 0) 417d68c78b8Schristos { 418d68c78b8Schristos ret = DST_R_INVALIDPRIVATEKEY; 419d68c78b8Schristos goto fail; 420d68c78b8Schristos } 421d68c78b8Schristos 422d68c78b8Schristos NEXTTOKEN(lex, opt, &token); 4235606745fSchristos if (token.type != isc_tokentype_string || (DST_AS_STR(token))[0] != 'v') 424d68c78b8Schristos { 425d68c78b8Schristos ret = DST_R_INVALIDPRIVATEKEY; 426d68c78b8Schristos goto fail; 427d68c78b8Schristos } 4285606745fSchristos if (sscanf(DST_AS_STR(token), "v%d.%d", &major, &minor) != 2) { 429d68c78b8Schristos ret = DST_R_INVALIDPRIVATEKEY; 430d68c78b8Schristos goto fail; 431d68c78b8Schristos } 432d68c78b8Schristos 433d68c78b8Schristos if (major > DST_MAJOR_VERSION) { 434d68c78b8Schristos ret = DST_R_INVALIDPRIVATEKEY; 435d68c78b8Schristos goto fail; 436d68c78b8Schristos } 437d68c78b8Schristos 438d68c78b8Schristos /* 439d68c78b8Schristos * Store the private key format version number 440d68c78b8Schristos */ 441d68c78b8Schristos dst_key_setprivateformat(key, major, minor); 442d68c78b8Schristos 443d68c78b8Schristos READLINE(lex, opt, &token); 444d68c78b8Schristos 445d68c78b8Schristos /* 446d68c78b8Schristos * Read the algorithm line. 447d68c78b8Schristos */ 448d68c78b8Schristos NEXTTOKEN(lex, opt, &token); 449d68c78b8Schristos if (token.type != isc_tokentype_string || 450d68c78b8Schristos strcmp(DST_AS_STR(token), ALGORITHM_STR) != 0) 451d68c78b8Schristos { 452d68c78b8Schristos ret = DST_R_INVALIDPRIVATEKEY; 453d68c78b8Schristos goto fail; 454d68c78b8Schristos } 455d68c78b8Schristos 456d68c78b8Schristos NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token); 457d68c78b8Schristos if (token.type != isc_tokentype_number || 458d68c78b8Schristos token.value.as_ulong != (unsigned long)dst_key_alg(key)) 459d68c78b8Schristos { 460d68c78b8Schristos ret = DST_R_INVALIDPRIVATEKEY; 461d68c78b8Schristos goto fail; 462d68c78b8Schristos } 463d68c78b8Schristos 464d68c78b8Schristos READLINE(lex, opt, &token); 465d68c78b8Schristos 466d68c78b8Schristos /* 467d68c78b8Schristos * Read the key data. 468d68c78b8Schristos */ 469d68c78b8Schristos for (n = 0; n < MAXFIELDS; n++) { 470d68c78b8Schristos int tag; 471d68c78b8Schristos isc_region_t r; 472d68c78b8Schristos do { 473d68c78b8Schristos ret = isc_lex_gettoken(lex, opt, &token); 4745606745fSchristos if (ret == ISC_R_EOF) { 475d68c78b8Schristos goto done; 4765606745fSchristos } 4775606745fSchristos if (ret != ISC_R_SUCCESS) { 478d68c78b8Schristos goto fail; 4795606745fSchristos } 480d68c78b8Schristos } while (token.type == isc_tokentype_eol); 481d68c78b8Schristos 482d68c78b8Schristos if (token.type != isc_tokentype_string) { 483d68c78b8Schristos ret = DST_R_INVALIDPRIVATEKEY; 484d68c78b8Schristos goto fail; 485d68c78b8Schristos } 486d68c78b8Schristos 487d68c78b8Schristos if (strcmp(DST_AS_STR(token), "External:") == 0) { 488d4a20c3eSchristos external = true; 489d68c78b8Schristos goto next; 490d68c78b8Schristos } 491d68c78b8Schristos 492d68c78b8Schristos /* Numeric metadata */ 493d68c78b8Schristos tag = find_numericdata(DST_AS_STR(token)); 494d68c78b8Schristos if (tag >= 0) { 495d68c78b8Schristos INSIST(tag < NUMERIC_NTAGS); 496d68c78b8Schristos 497d68c78b8Schristos NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token); 498d68c78b8Schristos if (token.type != isc_tokentype_number) { 499d68c78b8Schristos ret = DST_R_INVALIDPRIVATEKEY; 500d68c78b8Schristos goto fail; 501d68c78b8Schristos } 502d68c78b8Schristos 503d68c78b8Schristos dst_key_setnum(key, tag, token.value.as_ulong); 504d68c78b8Schristos goto next; 505d68c78b8Schristos } 506d68c78b8Schristos 507d68c78b8Schristos /* Timing metadata */ 508d68c78b8Schristos tag = find_timedata(DST_AS_STR(token)); 509d68c78b8Schristos if (tag >= 0) { 510d68c78b8Schristos INSIST(tag < TIMING_NTAGS); 511d68c78b8Schristos 512d68c78b8Schristos NEXTTOKEN(lex, opt, &token); 513d68c78b8Schristos if (token.type != isc_tokentype_string) { 514d68c78b8Schristos ret = DST_R_INVALIDPRIVATEKEY; 515d68c78b8Schristos goto fail; 516d68c78b8Schristos } 517d68c78b8Schristos 518d68c78b8Schristos ret = dns_time32_fromtext(DST_AS_STR(token), &when); 5195606745fSchristos if (ret != ISC_R_SUCCESS) { 520d68c78b8Schristos goto fail; 5215606745fSchristos } 522d68c78b8Schristos 523d68c78b8Schristos dst_key_settime(key, tag, when); 524d68c78b8Schristos 525d68c78b8Schristos goto next; 526d68c78b8Schristos } 527d68c78b8Schristos 528d68c78b8Schristos /* Key data */ 529d68c78b8Schristos tag = find_value(DST_AS_STR(token), alg); 5305606745fSchristos if (tag < 0 && minor > DST_MINOR_VERSION) { 531d68c78b8Schristos goto next; 5325606745fSchristos } else if (tag < 0) { 533d68c78b8Schristos ret = DST_R_INVALIDPRIVATEKEY; 534d68c78b8Schristos goto fail; 535d68c78b8Schristos } 536d68c78b8Schristos 537d68c78b8Schristos priv->elements[n].tag = tag; 538d68c78b8Schristos 5395606745fSchristos data = isc_mem_get(mctx, MAXFIELDSIZE); 540d68c78b8Schristos 541d68c78b8Schristos isc_buffer_init(&b, data, MAXFIELDSIZE); 542d68c78b8Schristos ret = isc_base64_tobuffer(lex, &b, -1); 5435606745fSchristos if (ret != ISC_R_SUCCESS) { 544d68c78b8Schristos goto fail; 5455606745fSchristos } 546d68c78b8Schristos 547d68c78b8Schristos isc_buffer_usedregion(&b, &r); 548d68c78b8Schristos priv->elements[n].length = r.length; 549d68c78b8Schristos priv->elements[n].data = r.base; 550d68c78b8Schristos priv->nelements++; 551d68c78b8Schristos 552d68c78b8Schristos next: 553d68c78b8Schristos READLINE(lex, opt, &token); 554d68c78b8Schristos data = NULL; 555d68c78b8Schristos } 556d68c78b8Schristos 557d68c78b8Schristos done: 558d68c78b8Schristos if (external && priv->nelements != 0) { 559d68c78b8Schristos ret = DST_R_INVALIDPRIVATEKEY; 560d68c78b8Schristos goto fail; 561d68c78b8Schristos } 562d68c78b8Schristos 563d4a20c3eSchristos check = check_data(priv, alg, true, external); 564d68c78b8Schristos if (check < 0) { 565d68c78b8Schristos ret = DST_R_INVALIDPRIVATEKEY; 566d68c78b8Schristos goto fail; 567d68c78b8Schristos } else if (check != ISC_R_SUCCESS) { 568d68c78b8Schristos ret = check; 569d68c78b8Schristos goto fail; 570d68c78b8Schristos } 571d68c78b8Schristos 572d68c78b8Schristos key->external = external; 573d68c78b8Schristos 574*bcda20f6Schristos return ISC_R_SUCCESS; 575d68c78b8Schristos 576d68c78b8Schristos fail: 577d68c78b8Schristos dst__privstruct_free(priv, mctx); 5785606745fSchristos if (data != NULL) { 579d68c78b8Schristos isc_mem_put(mctx, data, MAXFIELDSIZE); 5805606745fSchristos } 581d68c78b8Schristos 582*bcda20f6Schristos return ret; 583d68c78b8Schristos } 584d68c78b8Schristos 585d68c78b8Schristos isc_result_t 586d68c78b8Schristos dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv, 5875606745fSchristos const char *directory) { 588d68c78b8Schristos FILE *fp; 589d68c78b8Schristos isc_result_t result; 590d4a20c3eSchristos char filename[NAME_MAX]; 591bb5aa156Schristos char tmpname[NAME_MAX]; 592d68c78b8Schristos char buffer[MAXFIELDSIZE * 2]; 593d68c78b8Schristos isc_stdtime_t when; 594d4a20c3eSchristos uint32_t value; 595d68c78b8Schristos isc_buffer_t b; 596bb5aa156Schristos isc_buffer_t fileb; 597bb5aa156Schristos isc_buffer_t tmpb; 598d68c78b8Schristos isc_region_t r; 599d68c78b8Schristos int major, minor; 600d68c78b8Schristos mode_t mode; 601d68c78b8Schristos int i, ret; 602d68c78b8Schristos 603d68c78b8Schristos REQUIRE(priv != NULL); 604d68c78b8Schristos 605d4a20c3eSchristos ret = check_data(priv, dst_key_alg(key), false, key->external); 6065606745fSchristos if (ret < 0) { 607*bcda20f6Schristos return DST_R_INVALIDPRIVATEKEY; 6085606745fSchristos } else if (ret != ISC_R_SUCCESS) { 609*bcda20f6Schristos return ret; 6105606745fSchristos } 611d68c78b8Schristos 612bb5aa156Schristos isc_buffer_init(&fileb, filename, sizeof(filename)); 613bb5aa156Schristos result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, 614bb5aa156Schristos &fileb); 6155606745fSchristos if (result != ISC_R_SUCCESS) { 616*bcda20f6Schristos return result; 6175606745fSchristos } 618d68c78b8Schristos 619d68c78b8Schristos result = isc_file_mode(filename, &mode); 620bb5aa156Schristos if (result == ISC_R_SUCCESS && mode != (S_IRUSR | S_IWUSR)) { 621d68c78b8Schristos /* File exists; warn that we are changing its permissions */ 622d68c78b8Schristos int level; 623d68c78b8Schristos 624d68c78b8Schristos level = ISC_LOG_WARNING; 625d68c78b8Schristos isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 626d68c78b8Schristos DNS_LOGMODULE_DNSSEC, level, 627d68c78b8Schristos "Permissions on the file %s " 628d68c78b8Schristos "have changed from 0%o to 0600 as " 629d68c78b8Schristos "a result of this operation.", 630d68c78b8Schristos filename, (unsigned int)mode); 631d68c78b8Schristos } 632d68c78b8Schristos 633bb5aa156Schristos isc_buffer_init(&tmpb, tmpname, sizeof(tmpname)); 634bb5aa156Schristos result = dst_key_buildfilename(key, DST_TYPE_TEMPLATE, directory, 635bb5aa156Schristos &tmpb); 636bb5aa156Schristos if (result != ISC_R_SUCCESS) { 637*bcda20f6Schristos return result; 6385606745fSchristos } 639d68c78b8Schristos 640bb5aa156Schristos fp = dst_key_open(tmpname, S_IRUSR | S_IWUSR); 641bb5aa156Schristos if (fp == NULL) { 642*bcda20f6Schristos return DST_R_WRITEERROR; 643bb5aa156Schristos } 644d68c78b8Schristos 645d68c78b8Schristos dst_key_getprivateformat(key, &major, &minor); 646d68c78b8Schristos if (major == 0 && minor == 0) { 647d68c78b8Schristos major = DST_MAJOR_VERSION; 648d68c78b8Schristos minor = DST_MINOR_VERSION; 649d68c78b8Schristos } 650d68c78b8Schristos 651d68c78b8Schristos /* XXXDCL return value should be checked for full filesystem */ 652d68c78b8Schristos fprintf(fp, "%s v%d.%d\n", PRIVATE_KEY_STR, major, minor); 653d68c78b8Schristos 654d68c78b8Schristos fprintf(fp, "%s %u ", ALGORITHM_STR, dst_key_alg(key)); 655d68c78b8Schristos 656d68c78b8Schristos switch (dst_key_alg(key)) { 657d68c78b8Schristos case DST_ALG_RSASHA1: 658d68c78b8Schristos fprintf(fp, "(RSASHA1)\n"); 659d68c78b8Schristos break; 660d68c78b8Schristos case DST_ALG_NSEC3RSASHA1: 661d68c78b8Schristos fprintf(fp, "(NSEC3RSASHA1)\n"); 662d68c78b8Schristos break; 663d68c78b8Schristos case DST_ALG_RSASHA256: 664d68c78b8Schristos fprintf(fp, "(RSASHA256)\n"); 665d68c78b8Schristos break; 666d68c78b8Schristos case DST_ALG_RSASHA512: 667d68c78b8Schristos fprintf(fp, "(RSASHA512)\n"); 668d68c78b8Schristos break; 669d68c78b8Schristos case DST_ALG_ECDSA256: 670d68c78b8Schristos fprintf(fp, "(ECDSAP256SHA256)\n"); 671d68c78b8Schristos break; 672d68c78b8Schristos case DST_ALG_ECDSA384: 673d68c78b8Schristos fprintf(fp, "(ECDSAP384SHA384)\n"); 674d68c78b8Schristos break; 675d68c78b8Schristos case DST_ALG_ED25519: 676d68c78b8Schristos fprintf(fp, "(ED25519)\n"); 677d68c78b8Schristos break; 678d68c78b8Schristos case DST_ALG_ED448: 679d68c78b8Schristos fprintf(fp, "(ED448)\n"); 680d68c78b8Schristos break; 681d68c78b8Schristos case DST_ALG_HMACMD5: 682d68c78b8Schristos fprintf(fp, "(HMAC_MD5)\n"); 683d68c78b8Schristos break; 684d68c78b8Schristos case DST_ALG_HMACSHA1: 685d68c78b8Schristos fprintf(fp, "(HMAC_SHA1)\n"); 686d68c78b8Schristos break; 687d68c78b8Schristos case DST_ALG_HMACSHA224: 688d68c78b8Schristos fprintf(fp, "(HMAC_SHA224)\n"); 689d68c78b8Schristos break; 690d68c78b8Schristos case DST_ALG_HMACSHA256: 691d68c78b8Schristos fprintf(fp, "(HMAC_SHA256)\n"); 692d68c78b8Schristos break; 693d68c78b8Schristos case DST_ALG_HMACSHA384: 694d68c78b8Schristos fprintf(fp, "(HMAC_SHA384)\n"); 695d68c78b8Schristos break; 696d68c78b8Schristos case DST_ALG_HMACSHA512: 697d68c78b8Schristos fprintf(fp, "(HMAC_SHA512)\n"); 698d68c78b8Schristos break; 699d68c78b8Schristos default: 700d68c78b8Schristos fprintf(fp, "(?)\n"); 701d68c78b8Schristos break; 702d68c78b8Schristos } 703d68c78b8Schristos 704d68c78b8Schristos for (i = 0; i < priv->nelements; i++) { 705d68c78b8Schristos const char *s; 706d68c78b8Schristos 707d68c78b8Schristos s = find_tag(priv->elements[i].tag); 708d68c78b8Schristos 709d68c78b8Schristos r.base = priv->elements[i].data; 710d68c78b8Schristos r.length = priv->elements[i].length; 711d68c78b8Schristos isc_buffer_init(&b, buffer, sizeof(buffer)); 712d68c78b8Schristos result = isc_base64_totext(&r, sizeof(buffer), "", &b); 713d68c78b8Schristos if (result != ISC_R_SUCCESS) { 714*bcda20f6Schristos return dst_key_cleanup(tmpname, fp); 715d68c78b8Schristos } 716d68c78b8Schristos isc_buffer_usedregion(&b, &r); 717d68c78b8Schristos 718d68c78b8Schristos fprintf(fp, "%s %.*s\n", s, (int)r.length, r.base); 719d68c78b8Schristos } 720d68c78b8Schristos 7215606745fSchristos if (key->external) { 722d68c78b8Schristos fprintf(fp, "External:\n"); 7235606745fSchristos } 724d68c78b8Schristos 725d68c78b8Schristos /* Add the metadata tags */ 726d68c78b8Schristos if (major > 1 || (major == 1 && minor >= 3)) { 727d68c78b8Schristos for (i = 0; i < NUMERIC_NTAGS; i++) { 728d68c78b8Schristos result = dst_key_getnum(key, i, &value); 7295606745fSchristos if (result != ISC_R_SUCCESS) { 730d68c78b8Schristos continue; 7315606745fSchristos } 7325606745fSchristos if (numerictags[i] != NULL) { 733d68c78b8Schristos fprintf(fp, "%s %u\n", numerictags[i], value); 734d68c78b8Schristos } 7355606745fSchristos } 736d68c78b8Schristos for (i = 0; i < TIMING_NTAGS; i++) { 737d68c78b8Schristos result = dst_key_gettime(key, i, &when); 7385606745fSchristos if (result != ISC_R_SUCCESS) { 739d68c78b8Schristos continue; 7405606745fSchristos } 741d68c78b8Schristos 742d68c78b8Schristos isc_buffer_init(&b, buffer, sizeof(buffer)); 743d68c78b8Schristos result = dns_time32_totext(when, &b); 744d68c78b8Schristos if (result != ISC_R_SUCCESS) { 745*bcda20f6Schristos return dst_key_cleanup(tmpname, fp); 746d68c78b8Schristos } 747d68c78b8Schristos 748d68c78b8Schristos isc_buffer_usedregion(&b, &r); 749d68c78b8Schristos 7505606745fSchristos if (timetags[i] != NULL) { 7515606745fSchristos fprintf(fp, "%s %.*s\n", timetags[i], 7525606745fSchristos (int)r.length, r.base); 7535606745fSchristos } 754d68c78b8Schristos } 755d68c78b8Schristos } 756d68c78b8Schristos 757bb5aa156Schristos result = dst_key_close(tmpname, fp, filename); 758*bcda20f6Schristos return result; 759d68c78b8Schristos } 760d68c78b8Schristos 761d68c78b8Schristos /*! \file */ 762