You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
497 lines
14 KiB
497 lines
14 KiB
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c |
|
index 00a0080..336c4da 100644 |
|
--- a/lib/dns/dnssec.c |
|
+++ b/lib/dns/dnssec.c |
|
@@ -982,6 +982,8 @@ dns_dnssec_verifymessage(isc_buffer_t *source, dns_message_t *msg, |
|
mctx = msg->mctx; |
|
|
|
msg->verify_attempted = 1; |
|
+ msg->verified_sig = 0; |
|
+ msg->sig0status = dns_tsigerror_badsig; |
|
|
|
if (is_response(msg)) { |
|
if (msg->query.base == NULL) |
|
@@ -1076,6 +1078,7 @@ dns_dnssec_verifymessage(isc_buffer_t *source, dns_message_t *msg, |
|
} |
|
|
|
msg->verified_sig = 1; |
|
+ msg->sig0status = dns_rcode_noerror; |
|
|
|
dst_context_destroy(&ctx); |
|
dns_rdata_freestruct(&sig); |
|
diff --git a/lib/dns/message.c b/lib/dns/message.c |
|
index 1417067..0621175 100644 |
|
--- a/lib/dns/message.c |
|
+++ b/lib/dns/message.c |
|
@@ -3052,12 +3052,19 @@ dns_message_signer(dns_message_t *msg, dns_name_t *signer) { |
|
|
|
result = dns_rdata_tostruct(&rdata, &tsig, NULL); |
|
INSIST(result == ISC_R_SUCCESS); |
|
- if (msg->tsigstatus != dns_rcode_noerror) |
|
+ if (msg->verified_sig && |
|
+ msg->tsigstatus == dns_rcode_noerror && |
|
+ tsig.error == dns_rcode_noerror) |
|
+ { |
|
+ result = ISC_R_SUCCESS; |
|
+ } else if ((!msg->verified_sig) || |
|
+ (msg->tsigstatus != dns_rcode_noerror)) |
|
+ { |
|
result = DNS_R_TSIGVERIFYFAILURE; |
|
- else if (tsig.error != dns_rcode_noerror) |
|
+ } else { |
|
+ INSIST(tsig.error != dns_rcode_noerror); |
|
result = DNS_R_TSIGERRORSET; |
|
- else |
|
- result = ISC_R_SUCCESS; |
|
+ } |
|
dns_rdata_freestruct(&tsig); |
|
|
|
if (msg->tsigkey == NULL) { |
|
diff --git a/lib/dns/tsig.c b/lib/dns/tsig.c |
|
index 3239bff..7b91d1e 100644 |
|
--- a/lib/dns/tsig.c |
|
+++ b/lib/dns/tsig.c |
|
@@ -941,11 +941,20 @@ dns_tsig_sign(dns_message_t *msg) { |
|
isc_buffer_putuint48(&otherbuf, tsig.timesigned); |
|
} |
|
|
|
- if (key->key != NULL && tsig.error != dns_tsigerror_badsig) { |
|
+ if ((key->key != NULL) && |
|
+ (tsig.error != dns_tsigerror_badsig) && |
|
+ (tsig.error != dns_tsigerror_badkey)) |
|
+ { |
|
unsigned char header[DNS_MESSAGE_HEADERLEN]; |
|
isc_buffer_t headerbuf; |
|
isc_uint16_t digestbits; |
|
|
|
+ /* |
|
+ * If it is a response, we assume that the request MAC |
|
+ * has validated at this point. This is why we include a |
|
+ * MAC length > 0 in the reply. |
|
+ */ |
|
+ |
|
ret = dst_context_create3(key->key, mctx, |
|
DNS_LOGCATEGORY_DNSSEC, |
|
ISC_TRUE, &ctx); |
|
@@ -953,7 +962,7 @@ dns_tsig_sign(dns_message_t *msg) { |
|
return (ret); |
|
|
|
/* |
|
- * If this is a response, digest the query signature. |
|
+ * If this is a response, digest the request's MAC. |
|
*/ |
|
if (response) { |
|
dns_rdata_t querytsigrdata = DNS_RDATA_INIT; |
|
@@ -1083,6 +1092,17 @@ dns_tsig_sign(dns_message_t *msg) { |
|
dst_context_destroy(&ctx); |
|
digestbits = dst_key_getbits(key->key); |
|
if (digestbits != 0) { |
|
+ /* |
|
+ * XXXRAY: Is this correct? What is the |
|
+ * expected behavior when digestbits is not an |
|
+ * integral multiple of 8? It looks like bytes |
|
+ * should either be (digestbits/8) or |
|
+ * (digestbits+7)/8. |
|
+ * |
|
+ * In any case, for current algorithms, |
|
+ * digestbits are an integral multiple of 8, so |
|
+ * it has the same effect as (digestbits/8). |
|
+ */ |
|
unsigned int bytes = (digestbits + 1) / 8; |
|
if (response && bytes < querytsig.siglen) |
|
bytes = querytsig.siglen; |
|
@@ -1196,6 +1216,8 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, |
|
REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey)); |
|
|
|
msg->verify_attempted = 1; |
|
+ msg->verified_sig = 0; |
|
+ msg->tsigstatus = dns_tsigerror_badsig; |
|
|
|
if (msg->tcp_continuation) { |
|
if (tsigkey == NULL || msg->querytsig == NULL) |
|
@@ -1294,19 +1316,6 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, |
|
key = tsigkey->key; |
|
|
|
/* |
|
- * Is the time ok? |
|
- */ |
|
- if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) { |
|
- msg->tsigstatus = dns_tsigerror_badtime; |
|
- tsig_log(msg->tsigkey, 2, "signature has expired"); |
|
- return (DNS_R_CLOCKSKEW); |
|
- } else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) { |
|
- msg->tsigstatus = dns_tsigerror_badtime; |
|
- tsig_log(msg->tsigkey, 2, "signature is in the future"); |
|
- return (DNS_R_CLOCKSKEW); |
|
- } |
|
- |
|
- /* |
|
* Check digest length. |
|
*/ |
|
alg = dst_key_alg(key); |
|
@@ -1315,31 +1324,19 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, |
|
return (ret); |
|
if (alg == DST_ALG_HMACMD5 || alg == DST_ALG_HMACSHA1 || |
|
alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 || |
|
- alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512) { |
|
- isc_uint16_t digestbits = dst_key_getbits(key); |
|
+ alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512) |
|
+ { |
|
if (tsig.siglen > siglen) { |
|
- tsig_log(msg->tsigkey, 2, "signature length to big"); |
|
+ tsig_log(msg->tsigkey, 2, "signature length too big"); |
|
return (DNS_R_FORMERR); |
|
} |
|
if (tsig.siglen > 0 && |
|
- (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2))) { |
|
+ (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2))) |
|
+ { |
|
tsig_log(msg->tsigkey, 2, |
|
"signature length below minimum"); |
|
return (DNS_R_FORMERR); |
|
} |
|
- if (tsig.siglen > 0 && digestbits != 0 && |
|
- tsig.siglen < ((digestbits + 1) / 8)) { |
|
- msg->tsigstatus = dns_tsigerror_badtrunc; |
|
- tsig_log(msg->tsigkey, 2, |
|
- "truncated signature length too small"); |
|
- return (DNS_R_TSIGVERIFYFAILURE); |
|
- } |
|
- if (tsig.siglen > 0 && digestbits == 0 && |
|
- tsig.siglen < siglen) { |
|
- msg->tsigstatus = dns_tsigerror_badtrunc; |
|
- tsig_log(msg->tsigkey, 2, "signature length too small"); |
|
- return (DNS_R_TSIGVERIFYFAILURE); |
|
- } |
|
} |
|
|
|
if (tsig.siglen > 0) { |
|
@@ -1451,34 +1448,92 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, |
|
|
|
ret = dst_context_verify(ctx, &sig_r); |
|
if (ret == DST_R_VERIFYFAILURE) { |
|
- msg->tsigstatus = dns_tsigerror_badsig; |
|
ret = DNS_R_TSIGVERIFYFAILURE; |
|
tsig_log(msg->tsigkey, 2, |
|
"signature failed to verify(1)"); |
|
goto cleanup_context; |
|
- } else if (ret != ISC_R_SUCCESS) |
|
+ } else if (ret != ISC_R_SUCCESS) { |
|
goto cleanup_context; |
|
- |
|
- dst_context_destroy(&ctx); |
|
+ } |
|
} else if (tsig.error != dns_tsigerror_badsig && |
|
tsig.error != dns_tsigerror_badkey) { |
|
- msg->tsigstatus = dns_tsigerror_badsig; |
|
tsig_log(msg->tsigkey, 2, "signature was empty"); |
|
return (DNS_R_TSIGVERIFYFAILURE); |
|
} |
|
|
|
- msg->tsigstatus = dns_rcode_noerror; |
|
+ /* |
|
+ * Here at this point, the MAC has been verified. Even if any of |
|
+ * the following code returns a TSIG error, the reply will be |
|
+ * signed and WILL always include the request MAC in the digest |
|
+ * computation. |
|
+ */ |
|
+ |
|
+ /* |
|
+ * Is the time ok? |
|
+ */ |
|
+ if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) { |
|
+ msg->tsigstatus = dns_tsigerror_badtime; |
|
+ tsig_log(msg->tsigkey, 2, "signature has expired"); |
|
+ ret = DNS_R_CLOCKSKEW; |
|
+ goto cleanup_context; |
|
+ } else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) { |
|
+ msg->tsigstatus = dns_tsigerror_badtime; |
|
+ tsig_log(msg->tsigkey, 2, "signature is in the future"); |
|
+ ret = DNS_R_CLOCKSKEW; |
|
+ goto cleanup_context; |
|
+ } |
|
+ |
|
+ if ( |
|
+#ifndef PK11_MD5_DISABLE |
|
+ alg == DST_ALG_HMACMD5 || |
|
+#endif |
|
+ alg == DST_ALG_HMACSHA1 || |
|
+ alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 || |
|
+ alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512) |
|
+ { |
|
+ isc_uint16_t digestbits = dst_key_getbits(key); |
|
+ |
|
+ /* |
|
+ * XXXRAY: Is this correct? What is the expected |
|
+ * behavior when digestbits is not an integral multiple |
|
+ * of 8? It looks like bytes should either be |
|
+ * (digestbits/8) or (digestbits+7)/8. |
|
+ * |
|
+ * In any case, for current algorithms, digestbits are |
|
+ * an integral multiple of 8, so it has the same effect |
|
+ * as (digestbits/8). |
|
+ */ |
|
+ if (tsig.siglen > 0 && digestbits != 0 && |
|
+ tsig.siglen < ((digestbits + 1) / 8)) |
|
+ { |
|
+ msg->tsigstatus = dns_tsigerror_badtrunc; |
|
+ tsig_log(msg->tsigkey, 2, |
|
+ "truncated signature length too small"); |
|
+ ret = DNS_R_TSIGVERIFYFAILURE; |
|
+ goto cleanup_context; |
|
+ } |
|
+ if (tsig.siglen > 0 && digestbits == 0 && |
|
+ tsig.siglen < siglen) |
|
+ { |
|
+ msg->tsigstatus = dns_tsigerror_badtrunc; |
|
+ tsig_log(msg->tsigkey, 2, "signature length too small"); |
|
+ ret = DNS_R_TSIGVERIFYFAILURE; |
|
+ goto cleanup_context; |
|
+ } |
|
+ } |
|
|
|
if (tsig.error != dns_rcode_noerror) { |
|
+ msg->tsigstatus = tsig.error; |
|
if (tsig.error == dns_tsigerror_badtime) |
|
- return (DNS_R_CLOCKSKEW); |
|
+ ret = DNS_R_CLOCKSKEW; |
|
else |
|
- return (DNS_R_TSIGERRORSET); |
|
+ ret = DNS_R_TSIGERRORSET; |
|
+ goto cleanup_context; |
|
} |
|
|
|
+ msg->tsigstatus = dns_rcode_noerror; |
|
msg->verified_sig = 1; |
|
- |
|
- return (ISC_R_SUCCESS); |
|
+ ret = ISC_R_SUCCESS; |
|
|
|
cleanup_context: |
|
if (ctx != NULL) |
|
@@ -1503,6 +1558,8 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) { |
|
isc_uint16_t addcount, id; |
|
isc_boolean_t has_tsig = ISC_FALSE; |
|
isc_mem_t *mctx; |
|
+ unsigned int siglen; |
|
+ unsigned int alg; |
|
|
|
REQUIRE(source != NULL); |
|
REQUIRE(msg != NULL); |
|
@@ -1510,12 +1567,16 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) { |
|
REQUIRE(msg->tcp_continuation == 1); |
|
REQUIRE(msg->querytsig != NULL); |
|
|
|
+ msg->verified_sig = 0; |
|
+ msg->tsigstatus = dns_tsigerror_badsig; |
|
+ |
|
if (!is_response(msg)) |
|
return (DNS_R_EXPECTEDRESPONSE); |
|
|
|
mctx = msg->mctx; |
|
|
|
tsigkey = dns_message_gettsigkey(msg); |
|
+ key = tsigkey->key; |
|
|
|
/* |
|
* Extract and parse the previous TSIG |
|
@@ -1548,7 +1609,8 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) { |
|
* Do the key name and algorithm match that of the query? |
|
*/ |
|
if (!dns_name_equal(keyname, &tsigkey->name) || |
|
- !dns_name_equal(&tsig.algorithm, &querytsig.algorithm)) { |
|
+ !dns_name_equal(&tsig.algorithm, &querytsig.algorithm)) |
|
+ { |
|
msg->tsigstatus = dns_tsigerror_badkey; |
|
ret = DNS_R_TSIGVERIFYFAILURE; |
|
tsig_log(msg->tsigkey, 2, |
|
@@ -1557,27 +1619,40 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) { |
|
} |
|
|
|
/* |
|
- * Is the time ok? |
|
+ * Check digest length. |
|
*/ |
|
- isc_stdtime_get(&now); |
|
- |
|
- if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) { |
|
- msg->tsigstatus = dns_tsigerror_badtime; |
|
- tsig_log(msg->tsigkey, 2, "signature has expired"); |
|
- ret = DNS_R_CLOCKSKEW; |
|
- goto cleanup_querystruct; |
|
- } else if (now + msg->timeadjust < |
|
- tsig.timesigned - tsig.fudge) { |
|
- msg->tsigstatus = dns_tsigerror_badtime; |
|
- tsig_log(msg->tsigkey, 2, |
|
- "signature is in the future"); |
|
- ret = DNS_R_CLOCKSKEW; |
|
+ alg = dst_key_alg(key); |
|
+ ret = dst_key_sigsize(key, &siglen); |
|
+ if (ret != ISC_R_SUCCESS) |
|
goto cleanup_querystruct; |
|
+ if ( |
|
+#ifndef PK11_MD5_DISABLE |
|
+ alg == DST_ALG_HMACMD5 || |
|
+#endif |
|
+ alg == DST_ALG_HMACSHA1 || |
|
+ alg == DST_ALG_HMACSHA224 || |
|
+ alg == DST_ALG_HMACSHA256 || |
|
+ alg == DST_ALG_HMACSHA384 || |
|
+ alg == DST_ALG_HMACSHA512) |
|
+ { |
|
+ if (tsig.siglen > siglen) { |
|
+ tsig_log(tsigkey, 2, |
|
+ "signature length too big"); |
|
+ ret = DNS_R_FORMERR; |
|
+ goto cleanup_querystruct; |
|
+ } |
|
+ if (tsig.siglen > 0 && |
|
+ (tsig.siglen < 10 || |
|
+ tsig.siglen < ((siglen + 1) / 2))) |
|
+ { |
|
+ tsig_log(tsigkey, 2, |
|
+ "signature length below minimum"); |
|
+ ret = DNS_R_FORMERR; |
|
+ goto cleanup_querystruct; |
|
+ } |
|
} |
|
} |
|
|
|
- key = tsigkey->key; |
|
- |
|
if (msg->tsigctx == NULL) { |
|
ret = dst_context_create3(key, mctx, |
|
DNS_LOGCATEGORY_DNSSEC, |
|
@@ -1670,10 +1745,12 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) { |
|
sig_r.length = tsig.siglen; |
|
if (tsig.siglen == 0) { |
|
if (tsig.error != dns_rcode_noerror) { |
|
- if (tsig.error == dns_tsigerror_badtime) |
|
+ msg->tsigstatus = tsig.error; |
|
+ if (tsig.error == dns_tsigerror_badtime) { |
|
ret = DNS_R_CLOCKSKEW; |
|
- else |
|
+ } else { |
|
ret = DNS_R_TSIGERRORSET; |
|
+ } |
|
} else { |
|
tsig_log(msg->tsigkey, 2, |
|
"signature is empty"); |
|
@@ -1684,29 +1761,111 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) { |
|
|
|
ret = dst_context_verify(msg->tsigctx, &sig_r); |
|
if (ret == DST_R_VERIFYFAILURE) { |
|
- msg->tsigstatus = dns_tsigerror_badsig; |
|
tsig_log(msg->tsigkey, 2, |
|
"signature failed to verify(2)"); |
|
ret = DNS_R_TSIGVERIFYFAILURE; |
|
goto cleanup_context; |
|
+ } else if (ret != ISC_R_SUCCESS) { |
|
+ goto cleanup_context; |
|
} |
|
- else if (ret != ISC_R_SUCCESS) |
|
+ |
|
+ /* |
|
+ * Here at this point, the MAC has been verified. Even |
|
+ * if any of the following code returns a TSIG error, |
|
+ * the reply will be signed and WILL always include the |
|
+ * request MAC in the digest computation. |
|
+ */ |
|
+ |
|
+ /* |
|
+ * Is the time ok? |
|
+ */ |
|
+ isc_stdtime_get(&now); |
|
+ |
|
+ if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) { |
|
+ msg->tsigstatus = dns_tsigerror_badtime; |
|
+ tsig_log(msg->tsigkey, 2, "signature has expired"); |
|
+ ret = DNS_R_CLOCKSKEW; |
|
+ goto cleanup_context; |
|
+ } else if (now + msg->timeadjust < |
|
+ tsig.timesigned - tsig.fudge) |
|
+ { |
|
+ msg->tsigstatus = dns_tsigerror_badtime; |
|
+ tsig_log(msg->tsigkey, 2, |
|
+ "signature is in the future"); |
|
+ ret = DNS_R_CLOCKSKEW; |
|
goto cleanup_context; |
|
+ } |
|
|
|
- dst_context_destroy(&msg->tsigctx); |
|
+ alg = dst_key_alg(key); |
|
+ ret = dst_key_sigsize(key, &siglen); |
|
+ if (ret != ISC_R_SUCCESS) |
|
+ goto cleanup_context; |
|
+ if ( |
|
+#ifndef PK11_MD5_DISABLE |
|
+ alg == DST_ALG_HMACMD5 || |
|
+#endif |
|
+ alg == DST_ALG_HMACSHA1 || |
|
+ alg == DST_ALG_HMACSHA224 || |
|
+ alg == DST_ALG_HMACSHA256 || |
|
+ alg == DST_ALG_HMACSHA384 || |
|
+ alg == DST_ALG_HMACSHA512) |
|
+ { |
|
+ isc_uint16_t digestbits = dst_key_getbits(key); |
|
+ |
|
+ /* |
|
+ * XXXRAY: Is this correct? What is the |
|
+ * expected behavior when digestbits is not an |
|
+ * integral multiple of 8? It looks like bytes |
|
+ * should either be (digestbits/8) or |
|
+ * (digestbits+7)/8. |
|
+ * |
|
+ * In any case, for current algorithms, |
|
+ * digestbits are an integral multiple of 8, so |
|
+ * it has the same effect as (digestbits/8). |
|
+ */ |
|
+ if (tsig.siglen > 0 && digestbits != 0 && |
|
+ tsig.siglen < ((digestbits + 1) / 8)) |
|
+ { |
|
+ msg->tsigstatus = dns_tsigerror_badtrunc; |
|
+ tsig_log(msg->tsigkey, 2, |
|
+ "truncated signature length " |
|
+ "too small"); |
|
+ ret = DNS_R_TSIGVERIFYFAILURE; |
|
+ goto cleanup_context; |
|
+ } |
|
+ if (tsig.siglen > 0 && digestbits == 0 && |
|
+ tsig.siglen < siglen) |
|
+ { |
|
+ msg->tsigstatus = dns_tsigerror_badtrunc; |
|
+ tsig_log(msg->tsigkey, 2, |
|
+ "signature length too small"); |
|
+ ret = DNS_R_TSIGVERIFYFAILURE; |
|
+ goto cleanup_context; |
|
+ } |
|
+ } |
|
+ |
|
+ if (tsig.error != dns_rcode_noerror) { |
|
+ msg->tsigstatus = tsig.error; |
|
+ if (tsig.error == dns_tsigerror_badtime) |
|
+ ret = DNS_R_CLOCKSKEW; |
|
+ else |
|
+ ret = DNS_R_TSIGERRORSET; |
|
+ goto cleanup_context; |
|
+ } |
|
} |
|
|
|
msg->tsigstatus = dns_rcode_noerror; |
|
- return (ISC_R_SUCCESS); |
|
+ msg->verified_sig = 1; |
|
+ ret = ISC_R_SUCCESS; |
|
|
|
cleanup_context: |
|
- dst_context_destroy(&msg->tsigctx); |
|
+ if (msg->tsigctx != NULL) |
|
+ dst_context_destroy(&msg->tsigctx); |
|
|
|
cleanup_querystruct: |
|
dns_rdata_freestruct(&querytsig); |
|
|
|
return (ret); |
|
- |
|
} |
|
|
|
isc_result_t
|
|
|