[DNSSEC] [OpenSSL] Make it possible to use key-by-reference to sign zones
Description
Bind being packaged in ALTLinux is configured with openssl
, but without any pkcs11
options (uses defaults).
Bind version:
named -version
BIND 9.11.10 (Extended Support Version) <id:9390ecc>
But, to be integrated into FreeIPA (with support for DNSSEC) some sort of pkcs11 stuff is required. This project utilizes SoftHSM to store signing keys within a token and OpenDNSSEC (with custom services) for keeping track of DNSSEC keys.
There are several pkcs11 ops:
-
creation of a key pair of files that referencing a key object stored in a cryptographic HSM. This is being done by
dnssec-keyfromlabel
. -
signing zones inline.
Partially, this could be achieved by OpenSSL engine pkcs11
( engine plugin for the OpenSSL library allows accessing PKCS#11 modules in a semi-transparent way) https://github.com/OpenSC/libp11
For example,
using a specific OpenSSL engine pkcs11
:
dnssec-keyfromlabel -E pkcs11 -a RSASHA1 -l "token=dns;object=robie-rsa-zsk1;pin-source=/test/pin" example.test
This works as expected. Nice.
But manual signing fails.
Without key name provided:
dnssec-signzone -E pkcs11 -v 10 -a -P -g -r /dev/random -o example.test example.test
dnssec-signzone: using 8 cpus
C_Initialize
IN: pInitArgs = NULL
C_Initialize = CKR_OK
C_GetSlotList
IN: tokenPresent = CK_FALSE
IN: pulCount = 0x7FFF87EF4B28 = 15880584
OUT: pSlotList = (0) NO-VALUES
C_GetSlotList = CKR_OK
dnssec-signzone: debug 1: delete_node(): 0x7fadb4072010 example.test (bucket 4)
dnssec-signzone: debug 1: calling free_rbtdb(.)
dnssec-signzone: debug 1: done free_rbtdb(.)
dnssec-signzone: no existing signatures for example.test/NSEC
dnssec-signzone: example.test/NSEC:
dnssec-signzone: no existing signatures for example.test/DNSKEY
dnssec-signzone: example.test/DNSKEY:
dnssec-signzone: no existing signatures for example.test/SOA
dnssec-signzone: example.test/SOA:
dnssec-signzone: no existing signatures for example.test/NS
dnssec-signzone: example.test/NS:
dnssec-signzone: no existing signatures for txt.example.test/NSEC
dnssec-signzone: txt.example.test/NSEC:
dnssec-signzone: no existing signatures for txt.example.test/TXT
dnssec-signzone: txt.example.test/TXT:
dnssec-signzone: no existing signatures for ns.example.test/NSEC
dnssec-signzone: ns.example.test/NSEC:
dnssec-signzone: no existing signatures for ns.example.test/A
dnssec-signzone: ns.example.test/A:
example.test.signed
dnssec-signzone: debug 1: calling free_rbtdb(example.test)
dnssec-signzone: debug 1: done free_rbtdb(example.test)
grep RRSIG example.test.signed
3600 NSEC ns.example.test. NS SOA RRSIG NSEC DNSKEY
3600 NSEC example.test. TXT RRSIG NSEC
3600 NSEC txt.example.test. A RRSIG NSEC
And with explicit key name:
dnssec-signzone -E pkcs11 -v 10 -a -P -g -r /dev/random -o example.test example.test Kexample.test.+005+38895
dnssec-signzone: using 8 cpus
C_Initialize
IN: pInitArgs = NULL
C_Initialize = CKR_OK
C_GetSlotList
IN: tokenPresent = CK_FALSE
IN: pulCount = 0x7FFD8C748688 = 10563256
OUT: pSlotList = (0) NO-VALUES
C_GetSlotList = CKR_OK
dnssec-signzone: fatal: cannot sign zone with non-private dnskey Kexample.test.+005+38895
2579 if (result != ISC_R_SUCCESS)
(gdb)
2583 if (!dns_name_equal(gorigin, dst_key_name(newkey)))
(gdb)
2586 if (!dst_key_isprivate(newkey))
(gdb)
2587 fatal("cannot sign zone with non-private dnskey %s",
(gdb) p *newkey
$2 = {magic = 1146311755, refs = {refs = 1}, key_name = 0x7ffff75ea1f0, key_size = 2048,
key_proto = 3, key_alg = 5, key_flags = 256, key_id = 38895, key_rid = 39023, key_bits = 0,
key_class = 1, key_ttl = 0, mctx = 0x41d920, engine = 0x7ffff75f7008 "pkcs11",
label = 0x7ffff75e9180 "pkcs11:token=dns;object=robie-rsa-zsk1;pin-source=/test/pin",
keydata = {generic = 0x4a3838, gssctx = 0x4a3838, rsa = 0x4a3838, dsa = 0x4a3838,
dh = 0x4a3838, pkey = 0x4a3838, hmacmd5 = 0x4a3838, hmacsha1 = 0x4a3838,
hmacsha224 = 0x4a3838, hmacsha256 = 0x4a3838, hmacsha384 = 0x4a3838,
hmacsha512 = 0x4a3838}, times = {1568496680, 1568496680, 1568496680, 0, 0, 0, 0, 0, 0},
timeset = {true, true, true, false, false, false, false, false, false}, nums = {0, 0, 0,
0}, numset = {false, false, false, false}, inactive = false, external = false,
fmt_major = 1, fmt_minor = 3, func = 0x7ffff7fc1f00 <opensslrsa_functions>,
key_tkeytoken = 0x0}
(gdb) p *newkey->keydata->pkey->pkey->rsa
$4 = {pad = 0, version = 0, meth = 0x4546a8, engine = 0x0, n = 0x4a3f48, e = 0x4a4298,
d = 0x0, p = 0x0, q = 0x0, dmp1 = 0x0, dmq1 = 0x0, iqmp = 0x0, prime_infos = 0x0,
pss = 0x0, ex_data = {sk = 0x4a34b8}, references = 1, flags = 6, _method_mod_n = 0x0,
_method_mod_p = 0x0, _method_mod_q = 0x0, bignum_data = 0x0, blinding = 0x0,
mt_blinding = 0x0, lock = 0x4a33e8}
(gdb) bt
#0 loadexplicitkeys (keyfiles=keyfiles@entry=0x7fffffffec90, n=n@entry=1,
setksk=setksk@entry=false) at ./dnssec-signzone.c:2587
#1 0x0000000000407025 in main (argc=1, argv=0x7fffffffec90) at ./dnssec-signzone.c:3649
PKCS11 Flags for private key: CKA_PRIVATE; CKA_EXTRACTABLE; CKA_SENSITIVE;
Actually, BIND loads private key: https://gitlab.isc.org/isc-projects/bind9/blob/v9_11/lib/dns/opensslrsa_link.c#L1553
if (label != NULL) {
#if !defined(OPENSSL_NO_ENGINE)
if (engine == NULL)
DST_RET(DST_R_NOENGINE);
ep = dst__openssl_getengine(engine);
if (ep == NULL)
DST_RET(DST_R_NOENGINE);
pkey = ENGINE_load_private_key(ep, label, NULL, NULL);
But later fails on checking this key: https://gitlab.isc.org/isc-projects/bind9/blob/v9_11/lib/dns/opensslrsa_link.c#L1146
static bool
opensslrsa_isprivate(const dst_key_t *key) {
const BIGNUM *d = NULL;
#if USE_EVP
RSA *rsa = EVP_PKEY_get1_RSA(key->keydata.pkey);
INSIST(rsa != NULL);
RSA_free(rsa);
/* key->keydata.pkey still has a reference so rsa is still valid. */
#else
RSA *rsa = key->keydata.rsa;
#endif
if (rsa != NULL && RSA_test_flags(rsa, RSA_FLAG_EXT_PKEY) != 0)
return (true);
RSA_get0_key(rsa, NULL, NULL, &d);
return (rsa != NULL && d != NULL);
}
As we can see the presence of RSA_FLAG_EXT_PKEY
is checked first and then d
(private exponent).
For example, RSA_FLAG_EXT_PKEY
RSA flag is set in BIND PKCS11 patch for OpenSSL:
https://gitlab.isc.org/isc-projects/bind9/blob/v9_11/bin/pkcs11/openssl-1.0.2h-patch#L6939
According to OpenSSL RSA:
This flag means the private key operations will be handled by rsa_mod_exp
and that they do not depend on the private key components being present:
for example a key stored in external hardware. Without this flag
bn_mod_exp gets called when private key components are absent.
So, signing doesn't happen in the case where the private key is a reference to HSM object because of the missing
RSA_FLAG_EXT_PKEY
, which should be set by used OpenSSL engine (pkcs11
).
Known solution
For example, Fedora provides two Binds (with native pkcs11 or with OpenSSL). But in my opinion, it makes things harder. It's preferred to have one build on a system.
Request
Make it possible to use key-by-reference to sign zones in OpenSSL clause.