crypto verify routines.

git-svn-id: file:///svn/unbound/trunk@520 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-08-15 08:39:00 +00:00
parent c2b1ad1418
commit f06b1e8ef3
4 changed files with 260 additions and 8 deletions

View file

@ -392,6 +392,7 @@ AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=pathname],
AC_SUBST(RUNTIME_PATH)
fi
AC_CHECK_HEADERS([openssl/ssl.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_HEADERS([openssl/err.h],,, [AC_INCLUDES_DEFAULT])
# check for thread library.
AC_ARG_WITH(pthreads, AC_HELP_STRING([--with-pthreads],
@ -568,6 +569,10 @@ AH_BOTTOM([
#include <getopt.h>
#endif
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_ATTR_FORMAT
# define ATTR_FORMAT(archetype, string_index, first_to_check) \
__attribute__ ((format (archetype, string_index, first_to_check)))

View file

@ -125,6 +125,7 @@ daemon_init()
return NULL;
signal_handling_record();
checklock_start();
ERR_load_crypto_strings();
daemon->need_to_exit = 0;
daemon->num_modules = 0;
if(!(daemon->env = (struct module_env*)calloc(1,
@ -463,5 +464,6 @@ daemon_delete(struct daemon* daemon)
free(daemon->pidfile);
free(daemon->env);
free(daemon);
ERR_free_strings();
checklock_stop();
}

View file

@ -1,3 +1,6 @@
15 August 2007: Wouter
- crypto calls to verify signatures.
14 August 2007: Wouter
- default outgoing ports changed to avoid port 2049 by default.
This port is widely blocked by firewalls.

View file

@ -145,6 +145,23 @@ dnskey_get_algo(struct ub_packed_rrset_key* k, size_t idx)
return (int)rdata[2+3];
}
/** get public key rdata field from a dnskey RR and do some checks */
static void
dnskey_get_pubkey(struct ub_packed_rrset_key* k, size_t idx,
unsigned char** pk, size_t* pklen)
{
uint8_t* rdata;
size_t len;
rrset_get_rdata(k, idx, &rdata, &len);
if(len < 2+5) {
*pk = NULL;
*pklen = 0;
return;
}
*pk = (unsigned char*)rdata+2+4;
*pklen = len-2-4;
}
int
ds_get_key_algo(struct ub_packed_rrset_key* k, size_t idx)
{
@ -1004,19 +1021,236 @@ check_dates(struct val_env* ve, uint8_t* expi_p, uint8_t* incep_p)
}
/**
* Output a libcrypto openssl error to the logfile.
* @param str: string to add to it.
* @param e: the error to output, error number from ERR_get_error().
*/
static void
log_crypto_error(const char* str, unsigned long e)
{
char buf[128];
/* or use ERR_error_string if ERR_error_string_n is not avail TODO */
ERR_error_string_n(e, buf, sizeof(buf));
/* buf now contains */
/* error:[error code]:[library name]:[function name]:[reason string] */
log_err("%s crypto %s", str, buf);
}
/**
* Convert DSA RRsig sigblock to a DSA_SIG structure.
* @param sig: sigblock field of RRSIG
* @param siglen: length of sig.
* @return DSA_SIG or NULL
*/
static DSA_SIG*
dsa_rrsig_to_dsa_sig(unsigned char* sig, unsigned int siglen)
{
uint8_t t;
BIGNUM *R, *S;
DSA_SIG *dsasig;
/* extract the R and S field from the sig buffer */
if(siglen < 1 + SHA_DIGEST_LENGTH*2) {
verbose(VERB_ALGO, "verify: short DSA RRSIG");
return NULL;
}
t = sig[0];
R = BN_new();
if(!R) {
log_err("verify: alloc failure");
return NULL;
}
S = BN_new();
if(!S) {
BN_free(R);
log_err("verify: alloc failure");
return NULL;
}
if(!BN_bin2bn(sig + 1, SHA_DIGEST_LENGTH, R)) {
log_err("verify: bignum failure");
BN_free(R);
BN_free(S);
return NULL;
}
if(!BN_bin2bn(sig + 21, SHA_DIGEST_LENGTH, S)) {
log_err("verify: bignum failure");
BN_free(R);
BN_free(S);
return NULL;
}
dsasig = DSA_SIG_new();
if(!dsasig) {
log_err("verify: alloc failure");
BN_free(R);
BN_free(S);
return NULL;
}
dsasig->r = R;
dsasig->s = S;
return dsasig;
}
/**
* Convert DSA signature to a DER signature.
* @param sig: signature, used to read, then replaced with malloced
* DER signature on success.
* @param siglen: read to get input len, then updated to new length.
* @return false on (alloc) error.
*/
static int
dsa_convert_to_der(unsigned char** sig, unsigned int* siglen)
{
DSA_SIG* dsasig;
int res;
dsasig = dsa_rrsig_to_dsa_sig(*sig, *siglen);
if(!dsasig) {
return 0;
}
*sig = NULL; /* needs libcrypto >= 0.9.7 */
res = i2d_DSA_SIG(dsasig, sig);
if(res < 0) {
log_crypto_error("verify: bad dsa sig ",
ERR_get_error());
DSA_SIG_free(dsasig);
return 0;
}
DSA_SIG_free(dsasig);
*siglen = (unsigned int)res;
return 1;
}
/**
* Setup key and digest for verification. Adjust sig if necessary.
*
* @param algo: key algorithm
* @param evp_key: EVP PKEY public key to update.
* @param digest_type: digest type to use
* @param key: key to setup for.
* @param keylen: length of key.
* @param sig: sig to update if necessary.
* @param siglen: length of sig.
* @return false on failure.
*/
static int
setup_key_digest(int algo, EVP_PKEY* evp_key, const EVP_MD** digest_type,
unsigned char* key, size_t keylen,
unsigned char** sig, unsigned int* siglen)
{
switch(algo) {
case LDNS_DSA:
case LDNS_DSA_NSEC3:
EVP_PKEY_assign_DSA(evp_key,
ldns_key_buf2dsa_raw(key, keylen));
*digest_type = EVP_dss1();
if(!dsa_convert_to_der(sig, siglen))
return 0;
break;
case LDNS_RSASHA1:
case LDNS_RSASHA1_NSEC3:
EVP_PKEY_assign_RSA(evp_key,
ldns_key_buf2rsa_raw(key, keylen));
*digest_type = EVP_sha1();
break;
case LDNS_RSAMD5:
EVP_PKEY_assign_RSA(evp_key,
ldns_key_buf2rsa_raw(key, keylen));
*digest_type = EVP_md5();
break;
default:
verbose(VERB_ALGO, "verify: unknown algorithm %d",
algo);
return 0;
}
return 1;
}
/**
* Desetup what setup_key_digest setup.
* @param algo: key algorithm
* @param sig: signature.
*/
static void
desetup_key_digest(int algo, unsigned char* sig)
{
switch(algo) {
case LDNS_DSA:
case LDNS_DSA_NSEC3:
/* free converted signature */
free(sig);
break;
}
}
/**
* Check a canonical sig+rrset and signature against a dnskey
* @param buf: buffer with data to verify, the first rrsig part and the
* canonicalized rrset.
* @param algo: DNSKEY algorithm.
* @param sigblock: signature rdata field from RRSIG
* @param sigblock_len: length of sigblock data.
* @param key: public key data from DNSKEY RR.
* @param keylen: length of keydata.
* @return secure if verification succeeded, bogus on crypto failure,
* unchecked on format errors and alloc failures.
*/
static enum sec_status
verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock,
unsigned int sigblock_len, unsigned char* key, unsigned int keylen)
{
const EVP_MD *digest_type;
EVP_MD_CTX ctx;
int res;
EVP_PKEY *evp_key = EVP_PKEY_new();
if(!evp_key) {
log_err("verify: malloc failure in crypto");
return sec_status_unchecked;
}
if(!setup_key_digest(algo, evp_key, &digest_type, key, keylen,
&sigblock, &sigblock_len)) {
EVP_PKEY_free(evp_key);
return sec_status_bogus;
}
/* do the signature cryptography work */
EVP_MD_CTX_init(&ctx);
EVP_VerifyInit(&ctx, digest_type);
EVP_VerifyUpdate(&ctx, ldns_buffer_begin(buf), ldns_buffer_limit(buf));
res = EVP_VerifyFinal(&ctx, sigblock, sigblock_len, evp_key);
EVP_MD_CTX_cleanup(&ctx);
EVP_PKEY_free(evp_key);
desetup_key_digest(algo, sigblock);
if(res == 1) {
return sec_status_secure;
} else if(res == 0) {
return sec_status_bogus;
}
log_crypto_error("verify:", ERR_get_error());
return sec_status_unchecked;
}
enum sec_status
dnskey_verify_rrset_sig(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
size_t dnskey_idx, size_t sig_idx)
{
uint8_t* sig; /* rdata */
uint8_t* sig; /* RRSIG rdata */
size_t siglen;
size_t rrnum = rrset_get_count(rrset);
uint8_t* signer;
uint8_t* signer; /* rrsig signer name */
size_t signer_len;
uint8_t* sigblock; /* signature rdata field */
size_t sigblock_len;
uint16_t ktag;
unsigned char* sigblock; /* signature rdata field */
unsigned int sigblock_len;
uint16_t ktag; /* DNSKEY key tag */
unsigned char* key; /* public key rdata field */
unsigned int keylen;
rrset_get_rdata(rrset, rrnum + sig_idx, &sig, &siglen);
/* min length of rdatalen, fixed rrsig, root signer, 1 byte sig */
if(siglen < 2+20) {
@ -1036,12 +1270,12 @@ dnskey_verify_rrset_sig(struct module_env* env, struct val_env* ve,
verbose(VERB_ALGO, "verify: malformed signer name");
return sec_status_bogus; /* signer name invalid */
}
sigblock = signer+signer_len;
sigblock = (unsigned char*)signer+signer_len;
if(siglen < 2+18+signer_len+1) {
verbose(VERB_ALGO, "verify: too short, no signature data");
return sec_status_bogus; /* sig rdf is < 1 byte */
}
sigblock_len = siglen - 2 - 18 - signer_len;
sigblock_len = (unsigned int)(siglen - 2 - 18 - signer_len);
/* verify key dname == sig signer name */
if(query_dname_compare(signer, dnskey->rk.dname) != 0) {
@ -1086,6 +1320,14 @@ dnskey_verify_rrset_sig(struct module_env* env, struct val_env* ve,
return sec_status_unchecked;
}
/* check that dnskey is available */
dnskey_get_pubkey(dnskey, dnskey_idx, &key, &keylen);
if(!key) {
verbose(VERB_ALGO, "verify: short DNSKEY RR");
return sec_status_unchecked;
}
/* verify */
return sec_status_unchecked;
return verify_canonrrset(env->scratch_buffer, (int)sig[2],
sigblock, sigblock_len, key, keylen);
}