New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
resolve: add support for RFC 8080 #7600
Conversation
src/resolve/resolved-dns-dnssec.c
Outdated
} else | ||
fclose(f); | ||
|
||
switch (rrsig->rrsig.algorithm) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The control flow within dnssec_verify_rrset
is somewhat complicated. I would appreciate if someone verify that it's correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks really good. Just some minor fixups
return -EIO; | ||
f = open_memstream(&sig_data, &sig_size); | ||
if (!f) | ||
return -ENOMEM; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, given that this is somewhat performance-sensitive I figure it would be low-hanging but worthy to call __fsetlocking(f, FSETLOCKING_BYCALLER) on the memstream, to turn off all locking on it. For just concatenating a few bytes we really shouldn't pay the hefty locking price stdio makes us pay normally
src/resolve/resolved-dns-dnssec.c
Outdated
if (ferror(f)) { | ||
fclose(f); | ||
free(sig_data); | ||
return -ENOMEM; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use fflush_and_check() like everywhere else for this
src/resolve/resolved-dns-dnssec.c
Outdated
@@ -613,6 +707,9 @@ int dnssec_verify_rrset( | |||
gcry_md_hd_t md = NULL; | |||
int r, md_algorithm; | |||
size_t k, n = 0; | |||
FILE *f = NULL; | |||
size_t sig_size = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use cleanup_free and cleanup_fclose on these and avoid manual freeing/closing
src/resolve/resolved-dns-dnssec.c
Outdated
@@ -445,9 +530,18 @@ static void md_add_uint16(gcry_md_hd_t md, uint16_t v) { | |||
gcry_md_write(md, &v, sizeof(v)); | |||
} | |||
|
|||
static void md_add_uint32(gcry_md_hd_t md, uint32_t v) { | |||
static size_t fwrite_uint8(uint8_t v, FILE *fp) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you swap the two args please? libc is weird that fputc() take the argument first, and the object to operate on second, but we really shouldn't replicate that here...
src/resolve/resolved-dns-dnssec.c
Outdated
return fwrite(&v, sizeof(v), 1, fp); | ||
} | ||
|
||
static size_t fwrite_uint16(uint16_t v, FILE *fp) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as above
src/resolve/resolved-dns-dnssec.c
Outdated
return fwrite(&v, sizeof(v), 1, fp); | ||
} | ||
|
||
static size_t fwrite_uint32(uint32_t v, FILE *fp) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as above
src/resolve/resolved-dns-rr.h
Outdated
@@ -57,6 +57,8 @@ enum { | |||
DNSSEC_ALGORITHM_ECC_GOST = 12, /* RFC 5933 */ | |||
DNSSEC_ALGORITHM_ECDSAP256SHA256 = 13, /* RFC 6605 */ | |||
DNSSEC_ALGORITHM_ECDSAP384SHA384 = 14, /* RFC 6605 */ | |||
DNSSEC_ALGORITHM_ED25519 = 15, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add the RFC number as comment for this too!
I did incorporate the comments. |
@@ -613,6 +711,9 @@ int dnssec_verify_rrset( | |||
gcry_md_hd_t md = NULL; | |||
int r, md_algorithm; | |||
size_t k, n = 0; | |||
size_t sig_size = 0; | |||
_cleanup_free_ char *sig_data = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
free
has to be called after fclose
. The GCC documentation for the cleanup
attribute does not state in which order the functions are called. I only found a Google+ post by Lennart Poettering that claims that they are called “in order”, presumably in the reverse order of declaration. I would like to avoid that we depend on undefined behaviour as the wrong order caused a memory corruption when I tested it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, we do that all over the place already by ordering the declarations properly, so by doing this you are not going to break anything new...
Moreover the gcc C cleanup stuff is pretty much identical to the C++ destructor logic, and exposes the same semantics. And for C++ the standard is very explicit about the destruction order, see https://stackoverflow.com/a/2254306
src/resolve/resolved-dns-dnssec.c
Outdated
@@ -445,9 +534,18 @@ static void md_add_uint16(gcry_md_hd_t md, uint16_t v) { | |||
gcry_md_write(md, &v, sizeof(v)); | |||
} | |||
|
|||
static void md_add_uint32(gcry_md_hd_t md, uint32_t v) { | |||
static size_t fwrite_uint8(FILE *fp, uint8_t v) { | |||
return fwrite(&v, sizeof(v), 1, fp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, thinking about this, these function prototypes are actually weird since they are declared size_t, but will only return 0 or 1... (that's because fwrite() returns the "nmemb" paramater on success, not the first. It's kinda strange to return a "size_t" if we never actually return anything non-binary there... dunno.
Given that we don't care about the return value, and these functions are static anyway, I am pretty sure it would make more senseto just return void here...
src/resolve/resolved-dns-dnssec.c
Outdated
hash = gcry_md_read(md, 0); | ||
if (!hash) { | ||
r = -EIO; | ||
if (fflush_and_check(f)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r = fflush_and_check(f);
if (r < 0)
return r;
I incorporated the comments. |
src/resolve/resolved-dns-dnssec.c
Outdated
r = -EIO; | ||
r = fflush_and_check(f); | ||
if (r < 0) | ||
return -ENOMEM; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not return r
?
Patch looks good to me, but the semaphore CI does not: does that work correctly locally for you? (maybe semaphore's gcrypt is too old for this. Might be worth skipping the test if gcrypt doesn't know the EC algs yet)
|
I successfully built systemd on Debian Stretch. It includes Libgcrypt 1.7.6 and |
hmm, best would be to just add some versioning printing code to the test case, and then push it to see what version it is. That's how I'd do this myself at least... |
Semaphore CI uses Libgcrypt 1.5.3. Ed25519 support was introduced 1.5.4. If you install the |
@martinpitt any chance you can update the libgrcypt package for the semaphore CI? @ott I think it would make sense to change the test case for now to simply skip testing this bit if gcrypt doesn't support the algorithm. That way, we can merge your patch now, and as soon as @martinpitt does the CI update we'll test it on semaphore too. Note that semaphore isn't out only CI, we also test on current development ubuntu (which should hvae the new gcrypt) , hence it's entirely OK if one CI only tests a subset of what there is to test.. if you change the test as suggested, please change it so that the availability of the algorithm is tested itself, rather than a version check, to cover for backporting distros... |
hmm, i see you just updated the patch already. test version check doesn't look too bad, hence i figure it's ok to leave it as is. |
RFC 8080 describes how to use EdDSA keys and signatures in DNSSEC. It uses the curves Ed25519 and Ed448. Libgcrypt 1.8.1 does not support Ed448, so only the Ed25519 is supported at the moment. Once Libgcrypt supports Ed448, support for it can be trivially added to resolve.
I included a check that checks that Libgcrypt is at least version 1.6.0 before it enables EdDSA support. |
No, semaphore uses version 1.6.1 (in Ubuntu 14.04), so in theory that should satisfy the check |
You can see in build 6 of Semaphore CI of this pull request that it uses Libgcrypt 1.5.6. I successfully built and tested systemd with Libgcrypt 1.6.0. The configuration was as follows:
The build on Semaphore CI is also successful now. |
CI failures are unrelated (KVM and dpkg locking issues) |
looks good. merging |
It is possible that Libgcrypt was compiled without EdDSA support. But it can be also compiled without RSA, ECDSA or SHA support and we don't check for that. We could test that via |
RFC 8080 describes how to use EdDSA keys and signatures in DNSSEC. It
uses the curves Ed25519 and Ed448. Libgcrypt 1.8.1 does not support
Ed448, so only the Ed25519 is supported at the moment. Once Libgcrypt
supports Ed448, support for it can be trivially added to resolve.