/* * Check if any clock skew policy matches */ static void get_clock_skew(val_context_t *ctx, u_char *name_n, int *skew, u_int32_t *ttl_x) { policy_entry_t *cs_pol, *cs_cur; u_char *p; size_t name_len; if (ctx == NULL || name_n == NULL || skew == NULL || ttl_x == NULL) { val_log(ctx, LOG_DEBUG, "get_clock_skew(): Cannot check for clock skew policy, bad args"); return; } RETRIEVE_POLICY(ctx, P_CLOCK_SKEW, cs_pol); if (cs_pol) { name_len = wire_name_length(name_n); for (cs_cur = cs_pol; cs_cur && (wire_name_length(cs_cur->zone_n) > name_len); cs_cur = cs_cur->next); /* * for all zones which are shorter or as long, do a strstr */ /* * Because of the ordering, the longest match is found first */ for (; cs_cur; cs_cur = cs_cur->next) { int root_zone = 0; if (!namecmp(cs_cur->zone_n, (const u_char *) "")) root_zone = 1; else { /* * Find the last occurrence of cs_cur->zone_n in name_n */ p = name_n; while (p && (*p != '\0')) { if (!namecmp(p, cs_cur->zone_n)) break; p = p + *p + 1; } } if (root_zone || (!namecmp(p, cs_cur->zone_n))) { val_log(ctx, LOG_DEBUG, "get_clock_skew(): Found clock skew policy"); if (cs_cur->pol) { *skew = ((struct clock_skew_policy *)(cs_cur->pol))->clock_skew; if (cs_cur->exp_ttl > 0) *ttl_x = cs_cur->exp_ttl; return; } } } } val_log(ctx, LOG_DEBUG, "get_clock_skew(): No clock skew policy found"); *skew = 0; }
static void endless_loop(void) { val_context_t *context; /* * signal handlers to exit gracefully */ #ifdef SIGTERM signal(SIGTERM, sig_shutdown); #endif #ifdef SIGINT signal(SIGINT, sig_shutdown); #endif /* * open a port and process incoming packets */ port_setup(1153); if (VAL_NO_ERROR != val_create_context(NULL, &context)) { val_log(NULL, LOG_ERR, "Cannot create validator context. Exiting."); return; } while (!done) { wait_for_packet(); process_packet(context); } val_free_context(context); val_free_validator_state(); }
void val_log_rrsig_rdata(const val_context_t * ctx, int level, const char *prefix, val_rrsig_rdata_t * rdata) { char ctime_buf1[1028], ctime_buf2[1028]; char buf[1028]; struct timeval tv_sig1, tv_sig2; if (rdata) { if (!prefix) prefix = ""; memset(&tv_sig1, 0, sizeof(tv_sig1)); memset(&tv_sig2, 0, sizeof(tv_sig2)); tv_sig1.tv_sec = rdata->sig_expr; tv_sig2.tv_sec = rdata->sig_incp; GET_TIME_BUF((const time_t *)(&tv_sig1.tv_sec), ctime_buf1); GET_TIME_BUF((const time_t *)(&tv_sig2.tv_sec), ctime_buf2); val_log(ctx, level, "%s Type=%d Algo=%d[%s] Labels=%d OrgTTL=%d " "SigExp=%s SigIncp=%s KeyTag=%d[0x %04x] Signer=%s Sig=%s", prefix, rdata->algorithm, get_algorithm_string(rdata->algorithm), rdata->labels, rdata->orig_ttl, ctime_buf1, ctime_buf2, rdata->key_tag, rdata->key_tag, rdata->signer_name, get_base64_string(rdata->signature, rdata->signature_len, buf, 1024)); } }
/* * wrapper around the DS comparison function */ int ds_hash_is_equal(val_context_t *ctx, u_char ds_hashtype, u_char * ds_hash, size_t ds_hash_len, u_char * name_n, struct rrset_rr *dnskey, val_astatus_t * ds_status) { if ((dnskey == NULL) || (ds_hash == NULL) || (name_n == NULL)) { val_log(ctx, LOG_INFO, "ds_hash_is_equal(): Cannot compare DS data - invalid content"); return 0; } /* * Only SHA-1 is understood */ if (ds_hashtype == ALG_DS_HASH_SHA1) { return ds_sha_hash_is_equal(name_n, dnskey->rr_rdata, (size_t)dnskey->rr_rdata_length, ds_hash, ds_hash_len); } #ifdef HAVE_SHA_2 else if (ds_hashtype == ALG_DS_HASH_SHA256) { return ds_sha256_hash_is_equal(name_n, dnskey->rr_rdata, (size_t)dnskey->rr_rdata_length, ds_hash, ds_hash_len); } else if (ds_hashtype == ALG_DS_HASH_SHA384) { return ds_sha384_hash_is_equal(name_n, dnskey->rr_rdata, (size_t)dnskey->rr_rdata_length, ds_hash, ds_hash_len); } #endif /* else */ *ds_status = VAL_AC_ALGORITHM_NOT_SUPPORTED; val_log(ctx, LOG_INFO, "ds_hash_is_equal(): Unsupported DS hash algorithm"); return 0; }
void rsamd5_sigverify(val_context_t * ctx, const u_char *data, size_t data_len, const val_dnskey_rdata_t * dnskey, const val_rrsig_rdata_t * rrsig, val_astatus_t * key_status, val_astatus_t * sig_status) { char buf[1028]; size_t buflen = 1024; RSA *rsa = NULL; u_char md5_hash[MD5_DIGEST_LENGTH]; val_log(ctx, LOG_DEBUG, "rsamd5_sigverify(): parsing the public key..."); if ((rsa = RSA_new()) == NULL) { val_log(ctx, LOG_INFO, "rsamd5_sigverify(): could not allocate rsa structure."); *key_status = VAL_AC_INVALID_KEY; return; }; if (rsamd5_parse_public_key(dnskey->public_key, dnskey->public_key_len, rsa) != VAL_NO_ERROR) { val_log(ctx, LOG_INFO, "rsamd5_sigverify(): Error in parsing public key."); RSA_free(rsa); *key_status = VAL_AC_INVALID_KEY; return; } memset(md5_hash, 0, MD5_DIGEST_LENGTH); MD5(data, data_len, (u_char *) md5_hash); val_log(ctx, LOG_DEBUG, "rsamd5_sigverify(): MD5 hash = %s", get_hex_string(md5_hash, MD5_DIGEST_LENGTH, buf, buflen)); val_log(ctx, LOG_DEBUG, "rsamd5_sigverify(): verifying RSA signature..."); if (RSA_verify(NID_md5, (u_char *) md5_hash, MD5_DIGEST_LENGTH, rrsig->signature, rrsig->signature_len, rsa) == 1) { val_log(ctx, LOG_INFO, "rsamd5_sigverify(): returned SUCCESS"); RSA_free(rsa); *sig_status = VAL_AC_RRSIG_VERIFIED; } else { val_log(ctx, LOG_INFO, "rsamd5_sigverify(): returned FAILURE"); RSA_free(rsa); *sig_status = VAL_AC_RRSIG_VERIFY_FAILED; } return; }
static int _callback(void *callback_data, int eai_retval, struct addrinfo *res, val_status_t val_status) { struct getaddr_s *gas = (struct getaddr_s*)callback_data; *gas->retval = eai_retval; *gas->ainfo = res; *gas->vstatus = val_status; gas->done = 1; val_log(NULL, LOG_DEBUG, "_callback %p %d %p %d\n", callback_data, eai_retval, res, val_status); return 0; /* OK */ }
void val_log_dnskey_rdata(val_context_t * ctx, int level, const char *prefix, val_dnskey_rdata_t * rdata) { char buf[1028]; if (rdata) { if (!prefix) prefix = ""; val_log(ctx, level, "%s Flags=%d Prot=%d Algo=%d[%s] KeyTag=%d[0x %04x] PK=%s", prefix, rdata->flags, rdata->protocol, rdata->algorithm, get_algorithm_string(rdata->algorithm), rdata->key_tag, rdata->key_tag, get_base64_string(rdata->public_key, rdata->public_key_len, buf, 1024)); } }
void val_log_val_rrset_pfx(const val_context_t * ctx, int level, const char *pfx, struct val_rrset_rec *val_rrset_rec) { char buf1[2049], buf2[2049]; if (!val_rrset_rec) return; val_log(ctx, level, "%srrs->val_rrset_name=%s rrs->val_rrset_type=%s " "rrs->val_rrset_class=%s rrs->val_rrset_ttl=%d " "rrs->val_rrset_section=%s\nrrs->val_rrset_data=%s\n" "rrs->val_rrset_sig=%s", pfx ? pfx : "", val_rrset_rec->val_rrset_name, p_type(val_rrset_rec->val_rrset_type), p_class(val_rrset_rec->val_rrset_class), val_rrset_rec->val_rrset_ttl, p_section(val_rrset_rec->val_rrset_section - 1, !ns_o_update), get_rr_string(val_rrset_rec->val_rrset_data, buf1, 2048), get_rr_string(val_rrset_rec->val_rrset_sig, buf2, 2048)); }
static int _danecallback(void *callback_data, int dane_rc, struct val_danestatus **res) { struct dane_cb *dcb = (struct dane_cb *)callback_data; *dcb->retval = dane_rc; if (res != NULL) { *dcb->danestatus = *res; *res = NULL; } else *dcb->danestatus = NULL; dcb->done = 1; val_log(NULL, LOG_DEBUG, "_danecallback %p %d %p\n", callback_data, dane_rc, res); return 0; /* OK */ }
int val_get_rrset(val_context_t *context, const char *name, int class_h, int type_h, u_int32_t flags, struct val_answer_chain **answers) { struct val_result_chain *results = NULL; int retval = VAL_NO_ERROR; val_context_t *ctx = NULL; if (name == NULL || answers == NULL) { return VAL_BAD_ARGUMENT; } ctx = val_create_or_refresh_context(context);/* does CTX_LOCK_POL_SH */ if (ctx == NULL) return VAL_INTERNAL_ERROR; if ((retval = val_resolve_and_check(ctx, name, class_h, type_h, flags, &results)) != VAL_NO_ERROR) { val_log(ctx, LOG_INFO, "get_addrinfo_from_dns(): val_resolve_and_check failed - %s", p_val_err(retval)); goto err; } retval = val_get_answer_from_result(ctx, name, class_h, type_h, &results, answers, 0); err: CTX_UNLOCK_POL(ctx); return retval; }
void dsasha1_sigverify(val_context_t * ctx, const u_char *data, size_t data_len, const val_dnskey_rdata_t * dnskey, const val_rrsig_rdata_t * rrsig, val_astatus_t * key_status, val_astatus_t * sig_status) { char buf[1028]; size_t buflen = 1024; DSA *dsa = NULL; u_char sha1_hash[SHA_DIGEST_LENGTH]; u_char sig_asn1[2+2*(3+SHA_DIGEST_LENGTH)]; val_log(ctx, LOG_DEBUG, "dsasha1_sigverify(): parsing the public key..."); if ((dsa = DSA_new()) == NULL) { val_log(ctx, LOG_INFO, "dsasha1_sigverify(): could not allocate dsa structure."); *key_status = VAL_AC_INVALID_KEY; return; }; if (dsasha1_parse_public_key (dnskey->public_key, dnskey->public_key_len, dsa) != VAL_NO_ERROR) { val_log(ctx, LOG_INFO, "dsasha1_sigverify(): Error in parsing public key."); DSA_free(dsa); *key_status = VAL_AC_INVALID_KEY; return; } memset(sha1_hash, 0, SHA_DIGEST_LENGTH); SHA1(data, data_len, sha1_hash); val_log(ctx, LOG_DEBUG, "dsasha1_sigverify(): SHA-1 hash = %s", get_hex_string(sha1_hash, SHA_DIGEST_LENGTH, buf, buflen)); val_log(ctx, LOG_DEBUG, "dsasha1_sigverify(): verifying DSA signature..."); /* * Fix: courtesy tom.fowler * First convert the signature into its DER representation * 0x30, 0x2E, - ASN1 sequence * 0x02, 0x15, - ASN integer, length 21 bytes * 0x00, <R bytes> - 1 + 20 bytes per 2536 * 0x02, 0x15, - ASN integer * 0x00, <S bytes> - 1 + 20 bytes per 2536 */ if (rrsig->signature_len < (1 + 2*SHA_DIGEST_LENGTH)) { /* dont have enough data */ val_log(ctx, LOG_INFO, "dsasha1_sigverify(): Error parsing DSA rrsig."); DSA_free(dsa); *sig_status = VAL_AC_INVALID_RRSIG; return; } memcpy(sig_asn1, "\x30\x2E\x02\x15\x00", 5); memcpy(sig_asn1+5, rrsig->signature+1, SHA_DIGEST_LENGTH); memcpy(sig_asn1+5+SHA_DIGEST_LENGTH, "\x02\x15\x00", 3); memcpy(sig_asn1+5+SHA_DIGEST_LENGTH+3, rrsig->signature+1+SHA_DIGEST_LENGTH, SHA_DIGEST_LENGTH); if (DSA_verify (NID_sha1, (u_char *) sha1_hash, SHA_DIGEST_LENGTH, sig_asn1, sizeof(sig_asn1), dsa) == 1) { val_log(ctx, LOG_INFO, "dsasha1_sigverify(): returned SUCCESS"); DSA_free(dsa); *sig_status = VAL_AC_RRSIG_VERIFIED; } else { val_log(ctx, LOG_INFO, "dsasha1_sigverify(): returned FAILURE"); DSA_free(dsa); *sig_status = VAL_AC_RRSIG_VERIFY_FAILED; } return; }
void val_log_authentication_chain(const val_context_t * ctx, int level, const char * name_p, int class_h, int type_h, struct val_result_chain *results) { struct val_result_chain *next_result; int real_type_h; int real_class_h; if (results == NULL) { return; } for (next_result = results; next_result; next_result = next_result->val_rc_next) { struct val_authentication_chain *next_as; int i; /* Display the correct owner name, class,type for the record */ if (next_result->val_rc_rrset) { real_type_h = next_result->val_rc_rrset->val_rrset_type; real_class_h = next_result->val_rc_rrset->val_rrset_class; } else { real_type_h = type_h; real_class_h = class_h; } if (val_isvalidated(next_result->val_rc_status)) { val_log(ctx, level, "Validation result for {%s, %s(%d), %s(%d)}: %s:%d (Validated)", name_p, p_class(real_class_h), real_class_h, p_type(real_type_h), real_type_h, p_val_status(next_result->val_rc_status), next_result->val_rc_status); } else if (val_istrusted(next_result->val_rc_status)) { val_log(ctx, level, "Validation result for {%s, %s(%d), %s(%d)}: %s:%d (Trusted but not Validated)", name_p, p_class(real_class_h), real_class_h, p_type(real_type_h), real_type_h, p_val_status(next_result->val_rc_status), next_result->val_rc_status); } else { val_log(ctx, level, "Validation result for {%s, %s(%d), %s(%d)}: %s:%d (Untrusted)", name_p, p_class(real_class_h), real_class_h, p_type(real_type_h), real_type_h, p_val_status(next_result->val_rc_status), next_result->val_rc_status); } for (next_as = next_result->val_rc_answer; next_as; next_as = next_as->val_ac_trust) { if (next_as->val_ac_rrset == NULL) { val_log(ctx, level, " Assertion status = %s:%d", p_ac_status(next_as->val_ac_status), next_as->val_ac_status); } else { const char *t_name; t_name = next_as->val_ac_rrset->val_rrset_name; if (t_name == NULL) t_name = (const char *) "NULL_DATA"; val_log_assertion_pfx(ctx, level, " ", t_name, next_as); // val_log_val_rrset_pfx(ctx, level, " ", // next_as->val_ac_rrset); } } for (i = 0; i < next_result->val_rc_proof_count; i++) { val_log(ctx, level, " Proof of non-existence [%d of %d]", i+1, next_result->val_rc_proof_count); for (next_as = next_result->val_rc_proofs[i]; next_as; next_as = next_as->val_ac_trust) { if (next_as->val_ac_rrset == NULL) { val_log(ctx, level, " Assertion status = %s:%d", p_ac_status(next_as->val_ac_status), next_as->val_ac_status); } else { const char *t_name; t_name = next_as->val_ac_rrset->val_rrset_name; if (t_name == NULL) t_name = (const char *) "NULL_DATA"; val_log_assertion_pfx(ctx, level, " ", t_name, next_as); } } } } }
void val_log_assertion_pfx(const val_context_t * ctx, int level, const char *prefix, const char * name_pr, struct val_authentication_chain *next_as) { char name_buf[INET6_ADDRSTRLEN + 1]; const char *serv_pr; int tag = 0; int class_h; int type_h; struct val_rr_rec *data; struct sockaddr *serv; val_astatus_t status; struct val_rr_rec *curkey; #undef VAL_LOG_SIG #ifdef VAL_LOG_SIG struct val_rr_rec *sig; struct val_rr_rec *cursig; #endif if (next_as == NULL) return; class_h = next_as->val_ac_rrset->val_rrset_class; type_h = next_as->val_ac_rrset->val_rrset_type; data = next_as->val_ac_rrset->val_rrset_data; #ifdef VAL_LOG_SIG sig = next_as->val_ac_rrset->val_rrset_sig; #endif serv = next_as->val_ac_rrset->val_rrset_server; status = next_as->val_ac_status; if (NULL == prefix) prefix = ""; if (serv) serv_pr = ((serv_pr = val_get_ns_string(serv, name_buf, sizeof(name_buf))) == NULL) ? "VAL_CACHE" : serv_pr; else serv_pr = "NULL"; if (type_h == ns_t_dnskey) { for (curkey = data; curkey; curkey = curkey->rr_next) { if ((curkey->rr_status == VAL_AC_VERIFIED_LINK) || (curkey->rr_status == VAL_AC_TRUST_POINT) || (curkey->rr_status == VAL_AC_UNKNOWN_ALGORITHM_LINK)) { /* * Extract the key tag */ val_dnskey_rdata_t dnskey; if (VAL_NO_ERROR != val_parse_dnskey_rdata(curkey->rr_rdata, curkey->rr_rdata_length, &dnskey)) { val_log(ctx, LOG_INFO, "val_log_assertion_pfx(): Cannot parse DNSKEY data"); } else { tag = dnskey.key_tag; if (dnskey.public_key) FREE(dnskey.public_key); } break; } } } if (tag != 0) { val_log(ctx, level, "%sname=%s class=%s type=%s[tag=%d] from-server=%s " "status=%s:%d", prefix, name_pr, p_class(class_h), p_type(type_h), tag, serv_pr, p_ac_status(status), status); } else { val_log(ctx, level, "%sname=%s class=%s type=%s from-server=%s status=%s:%d", prefix, name_pr, p_class(class_h), p_type(type_h), serv_pr, p_ac_status(status), status); } #ifdef VAL_LOG_SIG for (cursig = sig; cursig; cursig = cursig->rr_next) { char incpTime[1028]; char exprTime[1028]; struct timeval tv_sig; val_rrsig_rdata_t rrsig; val_parse_rrsig_rdata(cursig->rr_rdata, cursig->rr_rdata_length, &rrsig); memset(&tv_sig, 0, sizeof(tv_sig)); tv_sig.tv_sec = rrsig.sig_incp; GET_TIME_BUF((const time_t *)(&tv_sig.tv_sec), incpTime); memset(&tv_sig, 0, sizeof(tv_sig)); tv_sig.tv_sec = rrsig.sig_expr; GET_TIME_BUF((const time_t *)(&tv_sig.tv_sec), exprTime); val_log(ctx, level, "%s ->tag=%d status=%s sig-incep=%s sig-expr=%s", prefix, rrsig.key_tag, p_ac_status(cursig->rr_status), incpTime, exprTime); } #endif #ifdef VAL_LOG_SIG struct val_rr_rec *rr; struct val_rr_rec *sig = next_as->val_ac_rrset->val_rrset_sig; for (rr = data; rr; rr = rr->rr_next) { val_log(ctx, level, " data_status=%s:%d", p_ac_status(rr->rr_status), rr->rr_status); } for (rr = sig; rr; rr = rr->rr_next) { val_log(ctx, level, " sig_status=%s:%d", p_ac_status(rr->rr_status), rr->rr_status); } #endif }
/* * Function: val_gethostbyname2_r * * Purpose: A validating DNSSEC-aware version of the reentrant gethostbyname2_r * function. This function supports both IPv4 and IPv6 addresses. * * Parameters: * ctx -- The validation context. Can be NULL for default value. * name -- The domain name or IP address in string format. * af -- Address family AF_INET or AF_INET6 * ret -- Pointer to a hostent variable to store the return value. * This parameter must not be NULL. * buf -- Pointer to a buffer to store auxiliary data. This * parameter must not be NULL. * buflen -- Length of the buffer 'buf'. * result -- Pointer to a variable of type (struct hostent *). This * parameter must not be NULL. *result will contain NULL on * failure and will point to the 'ret' parameter on success. * h_errnop -- Pointer to an integer variable to return the h_errno error * code. This parameter must not be NULL. * val_status -- A pointer to a val_status_t variable to hold the * returned validation-status value. This parameter * must not be NULL. * If successful, *val_status will contain a success * code. If there is a failure, *val_status will contain * the validator error code. To test whether the returned * error code represents a trustworthy status, the caller * can use the val_istrusted() function. * * Return value: 0 on success, and a non-zero error-code on failure. * * See also: val_gethostbyname2(), val_gethostbyname_r(), val_istrusted() */ int val_gethostbyname2_r(val_context_t * context, const char *name, int af, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop, val_status_t * val_status) { struct sockaddr_in sa; #if defined( WIN32 ) size_t addrlen4 = sizeof(struct sockaddr_in); #endif #ifdef VAL_IPV6 struct sockaddr_in6 sa6; #if defined( WIN32 ) size_t addrlen6 = sizeof(struct sockaddr_in6); #endif #endif int offset = 0; val_status_t local_ans_status = VAL_OOB_ANSWER; int trusted = 0; int retval; struct val_result_chain *results = NULL; u_int16_t type; val_context_t *ctx = NULL; *val_status = VAL_DONT_KNOW; if (!name || !ret || !h_errnop || !val_status || !result || !buf) { goto err; } ctx = val_create_or_refresh_context(context); /* does CTX_LOCK_POL_SH */ if (ctx == NULL) goto err; if (VAL_NO_ERROR == val_is_local_trusted(ctx, &trusted)) { if (trusted) { local_ans_status = VAL_TRUSTED_ANSWER; } } /* * Check if the address-family is AF_INET and the address is an IPv4 address */ if ((af == AF_INET) && (INET_PTON(AF_INET, name, ((struct sockaddr *)&sa), &addrlen4) > 0)) { memset(ret, 0, sizeof(struct hostent)); // Name ret->h_name = bufalloc(buf, buflen, &offset, strlen(name) + 1); if (ret->h_name == NULL) { goto err; } memcpy(ret->h_name, name, strlen(name) + 1); // Alias ret->h_aliases = (char **) bufalloc(buf, buflen, &offset, sizeof(char *)); if (ret->h_aliases == NULL) { goto err; } ret->h_aliases[0] = 0; // Address ret->h_addrtype = AF_INET; ret->h_length = sizeof(struct in_addr); ret->h_addr_list = (char **) bufalloc(buf, buflen, &offset, 2 * sizeof(char *)); if (ret->h_addr_list == NULL) { goto err; } ret->h_addr_list[0] = (char *) bufalloc(buf, buflen, &offset, sizeof(struct in_addr)); if (ret->h_addr_list[0] == NULL) { goto err; } memcpy(ret->h_addr_list[0], &sa.sin_addr, sizeof(struct in_addr)); ret->h_addr_list[1] = 0; *val_status = VAL_TRUSTED_ANSWER; *h_errnop = NETDB_SUCCESS; *result = ret; } #ifdef VAL_IPV6 /* * Check if the address-family is AF_INET6 and the address is an IPv6 address */ else if ((af == AF_INET6) && (INET_PTON(AF_INET6, name, ((struct sockaddr *)&sa6), &addrlen6) > 0)) { memset(ret, 0, sizeof(struct hostent)); // Name ret->h_name = bufalloc(buf, buflen, &offset, strlen(name) + 1); if (ret->h_name == NULL) { goto err; } memcpy(ret->h_name, name, strlen(name) + 1); // Alias ret->h_aliases = (char **) bufalloc(buf, buflen, &offset, sizeof(char *)); if (ret->h_aliases == NULL) { goto err; } ret->h_aliases[0] = 0; // Address ret->h_addrtype = AF_INET6; ret->h_length = sizeof(struct in6_addr); ret->h_addr_list = (char **) bufalloc(buf, buflen, &offset, 2 * sizeof(char *)); if (ret->h_addr_list == NULL) { goto err; } ret->h_addr_list[0] = (char *) bufalloc(buf, buflen, &offset, sizeof(struct in6_addr)); if (ret->h_addr_list[0] == NULL) { goto err; } memcpy(ret->h_addr_list[0], &sa6.sin6_addr, sizeof(struct in6_addr)); ret->h_addr_list[1] = 0; *val_status = VAL_TRUSTED_ANSWER; *h_errnop = NETDB_SUCCESS; *result = ret; } #endif else if (NULL != (*result = get_hostent_from_etc_hosts(ctx, name, af, ret, buf, buflen, &offset))) { /* * First check the ETC_HOSTS file * XXX: TODO check the order in the ETC_HOST_CONF file */ *val_status = local_ans_status; *h_errnop = NETDB_SUCCESS; } else { #ifdef VAL_IPV6 if (af == AF_INET6) type = ns_t_aaaa; else #endif type = ns_t_a; /* * Query the validator */ if (VAL_NO_ERROR == (retval = val_resolve_and_check(ctx, name, ns_c_in, type, 0, &results))) { /* * Convert the validator result into hostent */ *result = get_hostent_from_response(ctx, af, ret, results, h_errnop, buf, buflen, &offset, val_status); } else { val_log(ctx, LOG_ERR, "val_gethostbyname2_r(): val_resolve_and_check failed - %s", p_val_err(retval)); } if (*result == NULL) { goto err; } else { val_free_result_chain(results); results = NULL; *h_errnop = NETDB_SUCCESS; } } val_log(ctx, LOG_DEBUG, "val_gethostbyname2_r returned success, herrno = %d, val_status = %s", *h_errnop, val_status? p_val_status(*val_status) : NULL); CTX_UNLOCK_POL(ctx); return 0; err: if (result) { *result = NULL; } if (h_errnop) *h_errnop = NO_RECOVERY; if (ctx) { val_log(ctx, LOG_DEBUG, "val_gethostbyname2_r returned failure, herrno = %d, val_status = %s", *h_errnop, val_status? p_val_status(*val_status) : NULL); CTX_UNLOCK_POL(ctx); } return (NO_RECOVERY); }
/* * A thread-safe, re-entrant version of val_gethostbyaddr */ int val_gethostbyaddr_r(val_context_t * context, const char *addr, int len, int type, struct hostent *ret, char *buf, int buflen, struct hostent **result, int *h_errnop, val_status_t * val_status) { char domain_string[NS_MAXDNAME]; int ret_status = 0, bufused = 0; struct val_answer_chain *val_res = NULL; struct val_answer_chain *res; int retval; val_context_t *ctx = NULL; /* * check misc parameters exist */ if (!addr || !ret || !buf || (buflen <= 0) || !result || !h_errnop || !val_status) { if (h_errnop) *h_errnop = NO_RECOVERY; return (NO_RECOVERY); } /* * default the input parameters */ *result = NULL; ret->h_name = NULL; ret->h_aliases = NULL; ret->h_addr_list = NULL; *h_errnop = 0; *val_status = VAL_UNTRUSTED_ANSWER; /* * get the address values, only support IPv4 and IPv6 */ if (AF_INET == type && len >= sizeof(struct in_addr)) { ret->h_addrtype = type; ret->h_length = sizeof(struct in_addr); } #ifdef VAL_IPV6 else if (AF_INET6 == type && len >= sizeof(struct in6_addr)) { ret->h_addrtype = type; ret->h_length = sizeof(struct in6_addr); } #endif else { *h_errnop = NO_RECOVERY; return (NO_RECOVERY); } memset(domain_string, 0, sizeof(domain_string)); if (0 != (ret_status = address_to_reverse_domain(addr, type, domain_string, sizeof(domain_string))) ) { *h_errnop = ret_status; return ret_status; } /* * if there is memory, add the address to hostent's address list */ if ((buflen > bufused) && ((buflen - bufused) >= (ret->h_length + (sizeof(char *) * 2)))) { ret->h_addr_list = (char **) (buf + bufused); bufused = bufused + (sizeof(char *) * 2); ret->h_addr_list[0] = buf + bufused; ret->h_addr_list[1] = NULL; bufused = bufused + ret->h_length; memcpy(ret->h_addr_list[0], addr, ret->h_length); } else { /* no memory, fail */ *h_errnop = NO_RECOVERY; return (NO_RECOVERY); } ctx = val_create_or_refresh_context(context); /* does CTX_LOCK_POL_SH */ if (ctx == NULL) { *h_errnop = NO_RECOVERY; return (NO_RECOVERY); } if (VAL_NO_ERROR != (retval = val_get_rrset(ctx, /* val_context_t * */ domain_string, /* domain name */ ns_c_in, /* const u_int16_t q_class */ ns_t_ptr, /* const u_int16_t type */ 0, &val_res))) { /* struct val_answer_chain **results */ val_log(ctx, LOG_ERR, "val_gethostbyaddr_r(): val_get_rrset failed - %s", p_val_err(retval)); CTX_UNLOCK_POL(ctx); *h_errnop = NO_RECOVERY; return NO_RECOVERY; } CTX_UNLOCK_POL(ctx); if (!val_res) { *h_errnop = NO_RECOVERY; return NO_RECOVERY; } for (res = val_res; res; res=res->val_ans_next) { struct rr_rec *rr = res->val_ans; if (rr) { struct rr_rec *rr_it = NULL; int count = 0; int aliases_sz = 0; /* * if the buffer has enough room add the first host address */ if (rr->rr_length < (buflen - bufused - 1)) { /* * setup hostent */ ret->h_name = buf + bufused; ns_name_ntop(rr->rr_data, ret->h_name, (buflen - bufused)); bufused = bufused + strlen(ret->h_name) + 1; rr_it = rr->rr_next; /* * are there other hostnames? */ if (rr_it) { /* * calculate the amount of memory we need for aliases. */ do { count++; aliases_sz = aliases_sz + rr_it->rr_length + 1; } while (NULL != (rr_it = rr_it->rr_next)); /* * check that we have the space in the buffer */ if (buflen >= (bufused + (sizeof(char *) * (count + 1)) + aliases_sz)) { /* * assign the string pointer array */ ret->h_aliases = (char **) (buf + bufused); bufused = bufused + (sizeof(char *) * (count + 1)); /* * assign the strings */ rr_it = rr->rr_next; count = 0; do { ret->h_aliases[count] = buf + bufused; ns_name_ntop(rr_it->rr_data, ret->h_aliases[count], (buflen - bufused)); bufused = bufused + strlen(ret->h_aliases[count]) + 1; count++; } while (NULL != (rr_it = rr_it->rr_next)); /* * mark end of array */ ret->h_aliases[count] = NULL; } /* * else we didn't have enough memory for the aliases. They * will be ignored with only one hostname returned */ } /* else there are no other hostnames/aliases */ } else { /* else there is not enough room for even one host name, fail */ ret->h_name = NULL; *h_errnop = NO_RECOVERY; return NO_RECOVERY; } break; } else if (val_does_not_exist(res->val_ans_status)) { if ((res->val_ans_status == VAL_NONEXISTENT_TYPE) || (res->val_ans_status == VAL_NONEXISTENT_TYPE_NOCHAIN)) { *h_errnop = NO_DATA; } else if ((res->val_ans_status == VAL_NONEXISTENT_NAME) || (res->val_ans_status == VAL_NONEXISTENT_NAME_NOCHAIN)) { *h_errnop = HOST_NOT_FOUND; } *result = ret; return *h_errnop; } } if (!res) { /* no rrset, but a succesful return from the query?, fail */ ret->h_name = NULL; *h_errnop = NO_RECOVERY; return NO_RECOVERY; } /* set the value of merged trusted and validated status values */ if (val_isvalidated(res->val_ans_status)) *val_status = VAL_VALIDATED_ANSWER; else if (val_istrusted(res->val_ans_status)) *val_status = VAL_TRUSTED_ANSWER; /* * no error, set result */ *result = ret; return *h_errnop; } /* val_getthostbyaddr_r */
/* * Verify a signature, given the data and the dnskey */ static int val_sigverify(val_context_t * ctx, int is_a_wildcard, const u_char *data, size_t data_len, const val_dnskey_rdata_t * dnskey, const val_rrsig_rdata_t * rrsig, val_astatus_t * dnskey_status, val_astatus_t * sig_status, int clock_skew) { struct timeval tv; struct timeval tv_sig; /** Inputs to this function have already been NULL-checked **/ /* * Check if the dnskey is a zone key */ if ((dnskey->flags & ZONE_KEY_FLAG) == 0) { val_log(ctx, LOG_INFO, "val_sigverify(): DNSKEY with tag=%d is not a zone key", dnskey->key_tag); *dnskey_status = VAL_AC_INVALID_KEY; return 0; } /* * Check dnskey protocol value */ if (dnskey->protocol != 3) { val_log(ctx, LOG_INFO, "val_sigverify(): Invalid protocol field in DNSKEY with tag=%d: %d", dnskey->protocol, dnskey->key_tag); *dnskey_status = VAL_AC_UNKNOWN_DNSKEY_PROTOCOL; return 0; } /* * Match dnskey and rrsig algorithms */ if (dnskey->algorithm != rrsig->algorithm) { val_log(ctx, LOG_INFO, "val_sigverify(): Algorithm mismatch between DNSKEY (%d) and RRSIG (%d) records.", dnskey->algorithm, rrsig->algorithm); *sig_status = VAL_AC_RRSIG_ALGORITHM_MISMATCH; return 0; } if (clock_skew >= 0) { /* * Check signature inception and expiration times */ gettimeofday(&tv, NULL); if (tv.tv_sec < rrsig->sig_incp) { if (tv.tv_sec < rrsig->sig_incp - clock_skew) { char currTime[1028]; char incpTime[1028]; memset(&tv_sig, 0, sizeof(tv_sig)); tv_sig.tv_sec = rrsig->sig_incp; GET_TIME_BUF((const time_t *)(&tv.tv_sec), currTime); GET_TIME_BUF((const time_t *)(&tv_sig.tv_sec), incpTime); val_log(ctx, LOG_INFO, "val_sigverify(): Signature not yet valid. Current time (%s) is less than signature inception time (%s).", currTime, incpTime); *sig_status = VAL_AC_RRSIG_NOTYETACTIVE; return 0; } else { val_log(ctx, LOG_DEBUG, "val_sigverify(): Signature not yet valid, but within acceptable skew."); } } if (tv.tv_sec > rrsig->sig_expr) { if (tv.tv_sec > rrsig->sig_expr + clock_skew) { char currTime[1028]; char exprTime[1028]; memset(&tv_sig, 0, sizeof(tv_sig)); tv_sig.tv_sec = rrsig->sig_expr; memset(currTime, 0, sizeof(currTime)); memset(exprTime, 0, sizeof(exprTime)); GET_TIME_BUF((const time_t *)(&tv.tv_sec), currTime); GET_TIME_BUF((const time_t *)(&tv_sig.tv_sec), exprTime); val_log(ctx, LOG_INFO, "val_sigverify(): Signature expired. Current time (%s) is greater than signature expiration time (%s).", currTime, exprTime); *sig_status = VAL_AC_RRSIG_EXPIRED; return 0; } else { val_log(ctx, LOG_DEBUG, "val_sigverify(): Signature expired, but within acceptable skew."); } } } else { val_log(ctx, LOG_DEBUG, "val_sigverify(): Not checking inception and expiration times on signatures."); } switch (rrsig->algorithm) { case ALG_RSAMD5: rsamd5_sigverify(ctx, data, data_len, dnskey, rrsig, dnskey_status, sig_status); break; #ifdef LIBVAL_NSEC3 case ALG_NSEC3_DSASHA1: #endif case ALG_DSASHA1: dsasha1_sigverify(ctx, data, data_len, dnskey, rrsig, dnskey_status, sig_status); break; #ifdef LIBVAL_NSEC3 case ALG_NSEC3_RSASHA1: #endif case ALG_RSASHA1: #ifdef HAVE_SHA_2 case ALG_RSASHA256: case ALG_RSASHA512: #endif rsasha_sigverify(ctx, data, data_len, dnskey, rrsig, dnskey_status, sig_status); break; #if defined(HAVE_SHA_2) && defined(HAVE_OPENSSL_ECDSA_H) case ALG_ECDSAP256SHA256: case ALG_ECDSAP384SHA384: ecdsa_sigverify(ctx, data, data_len, dnskey, rrsig, dnskey_status, sig_status); break; #endif default: val_log(ctx, LOG_INFO, "val_sigverify(): Unsupported algorithm %d.", rrsig->algorithm); *sig_status = VAL_AC_ALGORITHM_NOT_SUPPORTED; *dnskey_status = VAL_AC_ALGORITHM_NOT_SUPPORTED; break; } if (*sig_status == VAL_AC_RRSIG_VERIFIED) { if (is_a_wildcard) { val_log(ctx, LOG_DEBUG, "val_sigverify(): Verified RRSIG is for a wildcard"); if (clock_skew > 0) *sig_status = VAL_AC_WCARD_VERIFIED_SKEW; else *sig_status = VAL_AC_WCARD_VERIFIED; } else { if (clock_skew > 0) *sig_status = VAL_AC_RRSIG_VERIFIED_SKEW; } return 1; } return 0; }
void verify_next_assertion(val_context_t * ctx, struct val_digested_auth_chain *as, struct val_digested_auth_chain *the_trust, u_int32_t flags) { struct rrset_rec *the_set; struct rrset_rr *the_sig; u_char *signby_name_n; u_int16_t signby_footprint_n; val_dnskey_rdata_t dnskey; int is_a_wildcard; struct rrset_rr *nextrr; struct rrset_rr *keyrr; u_int16_t tag_h; char name_p[NS_MAXDNAME]; if ((as == NULL) || (as->val_ac_rrset.ac_data == NULL) || (the_trust == NULL)) { val_log(ctx, LOG_INFO, "verify_next_assertion(): Cannot verify assertion - no data"); return; } the_set = as->val_ac_rrset.ac_data; dnskey.public_key = NULL; if (-1 == ns_name_ntop(the_set->rrs_name_n, name_p, sizeof(name_p))) snprintf(name_p, sizeof(name_p), "unknown/error"); if (the_set->rrs_sig == NULL) { val_log(ctx, LOG_INFO, "verify_next_assertion(): RRSIG is missing"); as->val_ac_status = VAL_AC_RRSIG_MISSING; return; } if (the_set->rrs_type_h != ns_t_dnskey) { /* * trust path contains the key */ if (the_trust->val_ac_rrset.ac_data == NULL) { val_log(ctx, LOG_INFO, "verify_next_assertion(): Key is empty"); as->val_ac_status = VAL_AC_DNSKEY_MISSING; return; } keyrr = the_trust->val_ac_rrset.ac_data->rrs_data; } else { /* * data itself contains the key */ if (the_set->rrs_data == NULL) { val_log(ctx, LOG_INFO, "verify_next_assertion(): Key is empty"); as->val_ac_status = VAL_AC_DNSKEY_MISSING; return; } keyrr = the_set->rrs_data; } for (the_sig = the_set->rrs_sig; the_sig; the_sig = the_sig->rr_next) { /* * do wildcard processing */ if (!check_label_count(the_set, the_sig, &is_a_wildcard)) { SET_STATUS(as->val_ac_status, the_sig, VAL_AC_WRONG_LABEL_COUNT); val_log(ctx, LOG_INFO, "verify_next_assertion(): Incorrect RRSIG label count"); continue; } /* * for each sig, identify key, */ if (VAL_NO_ERROR != identify_key_from_sig(the_sig, &signby_name_n, &signby_footprint_n)) { SET_STATUS(as->val_ac_status, the_sig, VAL_AC_INVALID_RRSIG); val_log(ctx, LOG_INFO, "verify_next_assertion(): Cannot extract key footprint from RRSIG"); continue; } tag_h = ntohs(signby_footprint_n); for (nextrr = keyrr; nextrr; nextrr = nextrr->rr_next) { int is_verified = 0; if (VAL_NO_ERROR != val_parse_dnskey_rdata(nextrr->rr_rdata, nextrr->rr_rdata_length, &dnskey)) { val_log(ctx, LOG_INFO, "verify_next_assertion(): Cannot parse DNSKEY data"); nextrr->rr_status = VAL_AC_INVALID_KEY; continue; } dnskey.next = NULL; if (dnskey.key_tag != tag_h) { if (dnskey.public_key != NULL) { FREE(dnskey.public_key); dnskey.public_key = NULL; } continue; } val_log(ctx, LOG_DEBUG, "verify_next_assertion(): Found potential matching DNSKEY for RRSIG"); /* * check the signature */ is_verified = do_verify(ctx, signby_name_n, &nextrr->rr_status, &the_sig->rr_status, the_set, the_sig, &dnskey, is_a_wildcard, flags); /* * There might be multiple keys with the same key tag; set this as * the signing key only if we dont have other status for this key */ SET_STATUS(as->val_ac_status, the_sig, the_sig->rr_status); if (nextrr->rr_status == VAL_AC_UNSET) { nextrr->rr_status = VAL_AC_SIGNING_KEY; } if (is_verified) { val_log(ctx, LOG_INFO, "verify_next_assertion(): Verified a RRSIG for %s (%s) using a DNSKEY (%d)", name_p, p_type(the_set->rrs_type_h), dnskey.key_tag); if ( as->val_ac_status == VAL_AC_TRUST || nextrr->rr_status == VAL_AC_TRUST_POINT) { /* we've verified a trust anchor */ as->val_ac_status = VAL_AC_TRUST; val_log(ctx, LOG_INFO, "verify_next_assertion(): verification traces back to trust anchor"); if (dnskey.public_key != NULL) { FREE(dnskey.public_key); dnskey.public_key = NULL; } return; } /* Check if we're trying to verify some key in the authentication chain */ if ( the_set->rrs_type_h == ns_t_dnskey && as != the_trust) { /* Check if we have reached our trust key */ /* * If this record contains a DNSKEY, check if the DS record contains this key * DNSKEYs cannot be wildcard expanded, so VAL_AC_WCARD_VERIFIED does not * count as a good sig * Create the link even if the DNSKEY algorithm is unknown since this * may be the provably insecure case */ /* * follow the trust path */ struct rrset_rr *dsrec = the_trust->val_ac_rrset.ac_data->rrs_data; while (dsrec) { val_ds_rdata_t ds; ds.d_hash = NULL; int retval = val_parse_ds_rdata(dsrec->rr_rdata, dsrec->rr_rdata_length, &ds); if(retval == VAL_NOT_IMPLEMENTED) { val_log(ctx, LOG_INFO, "verify_next_assertion(): DS hash not supported"); dsrec->rr_status = VAL_AC_ALGORITHM_NOT_SUPPORTED; } else if (retval != VAL_NO_ERROR) { val_log(ctx, LOG_INFO, "verify_next_assertion(): DS parse error"); dsrec->rr_status = VAL_AC_INVALID_DS; } else if (DNSKEY_MATCHES_DS(ctx, &dnskey, &ds, the_set->rrs_name_n, nextrr, &dsrec->rr_status)) { val_log(ctx, LOG_DEBUG, "verify_next_assertion(): DNSKEY tag (%d) matches DS tag (%d)", (&dnskey)->key_tag, (&ds)->d_keytag); /* * the first match is enough */ nextrr->rr_status = VAL_AC_VERIFIED_LINK; FREE(ds.d_hash); ds.d_hash = NULL; if (dnskey.public_key) { FREE(dnskey.public_key); dnskey.public_key = NULL; } val_log(ctx, LOG_INFO, "verify_next_assertion(): Key links upward"); return; } else { /* * Didn't find a valid entry in the DS record set * Not necessarily a problem, since there is no requirement that a DS be present * If none match, then we set the status accordingly. See below. */ nextrr->rr_status = VAL_AC_DS_NOMATCH; } if (ds.d_hash != NULL) FREE(ds.d_hash); dsrec = dsrec->rr_next; } } } if (dnskey.public_key != NULL) { FREE(dnskey.public_key); } dnskey.public_key = NULL; } val_log(ctx, LOG_INFO, "verify_next_assertion(): Could not link this RRSIG to a DNSKEY"); SET_STATUS(as->val_ac_status, the_sig, VAL_AC_DNSKEY_NOMATCH); } /* * If we reach here and we're a keyset, we either didn't verify the keyset or * didn't verify the link from the key to the DS */ if (the_set->rrs_type_h == ns_t_dnskey){ as->val_ac_status = VAL_AC_NO_LINK; } }
/* * Function: get_hostent_from_etc_hosts * * Purpose: Read the ETC_HOSTS file and check if it contains the given name. * Return the result in a hostent structure. * * Parameters: * ctx -- The validation context. * name -- The domain name or IP address in string form. * af -- The address family: AF_INET or AF_INET6. * ret -- Pointer to a hostent structure to return the result. * This parameter must not be NULL. * buf -- A buffer to store auxiliary data. This parameter must not be NULL. * buflen -- Length of the buffer 'buf'. * offset -- Pointer to an integer variable that contains the offset in the buffer * 'buf', where data can be written. When this function writes any data * in the auxiliary data, the offset is incremented accordingly. This * parameter must not be NULL. * * Return value: Returns NULL on failure and 'ret' on success. * * See also: get_hostent_from_response() */ static struct hostent * get_hostent_from_etc_hosts(val_context_t * ctx, const char *name, int af, struct hostent *ret, char *buf, int buflen, int *offset) { int orig_offset = 0; struct hosts *hs = NULL; struct hosts *h_prev = NULL; if ((ret == NULL) || (buf == NULL) || (offset == NULL) || (*offset < 0)) { return NULL; } /* * Parse the /etc/hosts file */ hs = parse_etc_hosts(name); orig_offset = *offset; memset(ret, 0, sizeof(struct hostent)); /* * XXX: todo -- can hs have more than one element ? */ while (hs) { struct sockaddr_in sa; #if defined( WIN32 ) size_t addrlen4 = sizeof(struct sockaddr_in); #endif #ifdef VAL_IPV6 struct sockaddr_in6 sa6; #if defined( WIN32 ) size_t addrlen6 = sizeof(struct sockaddr_in6); #endif #endif char addr_buf[INET6_ADDRSTRLEN]; int i, alias_count; int len = 0; const char *addr = NULL; size_t buflen = INET6_ADDRSTRLEN; if ((af == AF_INET) && (INET_PTON(AF_INET, hs->address, ((struct sockaddr *)&sa), &addrlen4) > 0)) { INET_NTOP(AF_INET, (&sa), sizeof(sa), addr_buf, buflen, addr); val_log(ctx, LOG_DEBUG, "get_hostent_from_etc_hosts(): type of address is IPv4"); val_log(ctx, LOG_DEBUG, "get_hostent_from_etc_hosts(): Address is: %s", addr ); } #ifdef VAL_IPV6 else if ((af == AF_INET6) && (INET_PTON(AF_INET6, hs->address, ((struct sockaddr *)&sa6), &addrlen6) > 0)) { INET_NTOP(AF_INET6, (&sa6), sizeof(sa6), addr_buf, buflen, addr); val_log(ctx, LOG_DEBUG, "get_hostent_from_etc_hosts(): type of address is IPv6"); val_log(ctx, LOG_DEBUG, "get_hostent_from_etc_hosts(): Address is: %s", addr ); } #endif else { /* * not a valid address ... skip this line */ val_log(ctx, LOG_WARNING, "get_hostent_from_etc_hosts(): error in address format: %s", hs->address); h_prev = hs; hs = hs->next; FREE_HOSTS(h_prev); continue; } // Name len = (hs->canonical_hostname == NULL) ? 0 : strlen(hs->canonical_hostname); if (hs->canonical_hostname) { ret->h_name = (char *) bufalloc(buf, buflen, offset, len + 1); if (ret->h_name == NULL) { goto err; } memcpy(ret->h_name, hs->canonical_hostname, len + 1); } else { ret->h_name = NULL; } // Aliases alias_count = 0; while (hs->aliases[alias_count]) { alias_count++; } alias_count++; ret->h_aliases = (char **) bufalloc(buf, buflen, offset, alias_count * sizeof(char *)); if (ret->h_aliases == NULL) { goto err; } for (i = 0; i < alias_count; i++) { len = (hs->aliases[i] == NULL) ? 0 : strlen(hs->aliases[i]); if (hs->aliases[i]) { ret->h_aliases[i] = (char *) bufalloc(buf, buflen, offset, len + 1); if (ret->h_aliases[i] == NULL) { goto err; } memcpy(ret->h_aliases[i], hs->aliases[i], len + 1); } else { ret->h_aliases[i] = NULL; } } // Addresses ret->h_addr_list = (char **) bufalloc(buf, buflen, offset, 2 * sizeof(char *)); if ((ret->h_addr_list == NULL) || ((af != AF_INET) && (af != AF_INET6))) { goto err; } if (af == AF_INET) { ret->h_addrtype = AF_INET; ret->h_length = sizeof(struct in_addr); ret->h_addr_list[0] = (char *) bufalloc(buf, buflen, offset, sizeof(struct in_addr)); if (ret->h_addr_list[0] == NULL) { goto err; } memcpy(ret->h_addr_list[0], &sa.sin_addr, sizeof(struct in_addr)); ret->h_addr_list[1] = 0; } #ifdef VAL_IPV6 else if (af == AF_INET6) { ret->h_addrtype = AF_INET6; ret->h_length = sizeof(struct in6_addr); ret->h_addr_list[0] = (char *) bufalloc(buf, buflen, offset, sizeof(struct in6_addr)); if (ret->h_addr_list[0] == NULL) { goto err; } memcpy(ret->h_addr_list[0], &sa6.sin6_addr, sizeof(struct in6_addr)); ret->h_addr_list[1] = 0; } #endif /* * clean up host list */ while (hs) { h_prev = hs; hs = hs->next; FREE_HOSTS(h_prev); } return ret; } return NULL; err: /* * clean up host list */ while (hs) { h_prev = hs; hs = hs->next; FREE_HOSTS(h_prev); } *offset = orig_offset; return NULL; } /* get_hostent_from_etc_hosts() */
/* * helper function for a set of verify-related operations */ static int do_verify(val_context_t * ctx, u_char *zone_n, val_astatus_t * dnskey_status, val_astatus_t * sig_status, struct rrset_rec *the_set, struct rrset_rr *the_sig, val_dnskey_rdata_t * the_key, int is_a_wildcard, u_int32_t flags) { /* * Use the crypto routines to verify the signature */ u_char *ver_field; size_t ver_length; int ret_val; val_rrsig_rdata_t rrsig_rdata; int clock_skew = 0; u_int32_t ttl_x = 0; int retval = 0; /* * Wildcard expansions for DNSKEYs and DSs are not permitted */ if (is_a_wildcard && ((the_set->rrs_type_h == ns_t_ds) || (the_set->rrs_type_h == ns_t_dnskey))) { val_log(ctx, LOG_INFO, "do_verify(): Invalid DNSKEY or DS record - cannot be wildcard expanded"); *dnskey_status = VAL_AC_INVALID_KEY; return 0; } if ((ret_val = make_sigfield(&ver_field, &ver_length, the_set, the_sig, is_a_wildcard)) != VAL_NO_ERROR || ver_field == NULL || ver_length == 0) { val_log(ctx, LOG_INFO, "do_verify(): Could not construct signature field for verification: %s", p_val_err(ret_val)); *sig_status = VAL_AC_INVALID_RRSIG; return 0; } /* * Find the signature - no memory is malloc'ed for this operation */ if (VAL_NO_ERROR != val_parse_rrsig_rdata(the_sig->rr_rdata, the_sig->rr_rdata_length, &rrsig_rdata)) { if (ver_field) FREE(ver_field); val_log(ctx, LOG_INFO, "do_verify(): Could not parse signature field"); *sig_status = VAL_AC_INVALID_RRSIG; return 0; } rrsig_rdata.next = NULL; if (flags & VAL_QUERY_IGNORE_SKEW) { clock_skew = -1; val_log(ctx, LOG_DEBUG, "do_verify(): Ignoring clock skew"); } else { get_clock_skew(ctx, zone_n, &clock_skew, &ttl_x); /* the state is valid for only as long as the policy validity period */ SET_MIN_TTL(the_set->rrs_ttl_x, ttl_x); } /* * Perform the verification */ retval = val_sigverify(ctx, is_a_wildcard, ver_field, ver_length, the_key, &rrsig_rdata, dnskey_status, sig_status, clock_skew); if (rrsig_rdata.signature != NULL) { FREE(rrsig_rdata.signature); rrsig_rdata.signature = NULL; } FREE(ver_field); return retval; }
static int process_packet(val_context_t *context) { HEADER *query_header, *response_header; u_char *pos; int q_name_len, rc; u_int16_t type_h, class_h; struct sockaddr from; socklen_t from_len; u_char query[4096], response[4096]; int query_size, response_size; /* * get a packet */ from_len = sizeof(from); memset(&from, 0x0, sizeof(from)); do { rc = recvfrom(listen_fd, query, sizeof(query), 0, &from, &from_len); if (rc < 0 && errno != EINTR) { // xxx-rks: log err msg break; } } while (rc < 0); if (rc < 0) return rc; query_size = rc; if (query_size < (sizeof(HEADER) + 1)) return -1; query_header = (HEADER *) query; /* * get query name */ pos = &query[sizeof(HEADER)]; q_name_len = wire_name_length(pos); pos += q_name_len; /* * get class and type */ VAL_GET16(type_h, pos); VAL_GET16(class_h, pos); response_size = sizeof(response); get_results(context, "test", (char *)&query[sizeof(HEADER)], (int)class_h, (int)type_h, response, &response_size, 0); /* * check to see if we need a dummy response */ val_log(NULL, LOG_DEBUG, "XXX-RKS: handle no response"); if (0 == response_size) { // no response; generate dummy/nxdomain response? return 1; } response_header = (HEADER*)response; response_header->id = query_header->id; /* * send response */ do { rc = sendto(listen_fd, response, response_size, 0, &from, sizeof(from)); if (rc < 0 && errno != EINTR) { // xxx-rks: log err msg break; } } while (rc < 0); if (rc > 0) { val_log(NULL, LOG_DEBUG, "sent %d bytes", rc); } return 0; /* no error */ }
/* * Function: get_hostent_from_response * * Purpose: Converts the linked list of val_result_chain structures obtained * as a result from the validator into a hostent structure. * * Parameters: * ctx -- The validation context. * af -- The address family: AF_INET or AF_INET6. * ret -- Pointer to a hostent structure to return the result. * This parameter must not be NULL. * results -- Pointer to a linked list of val_result_chain structures. * h_errnop -- Pointer to an integer variable to store the h_errno value. * buf -- A buffer to store auxiliary data. This parameter must not be NULL. * buflen -- Length of the buffer 'buf'. * offset -- Pointer to an integer variable that contains the offset in the buffer * 'buf', where data can be written. When this function writes any data * in the auxiliary data, the offset is incremented accordingly. This * parameter must not be NULL. * * Return value: Returns NULL on failure and 'ret' on success. * * See also: get_hostent_from_etc_hosts() */ static struct hostent * get_hostent_from_response(val_context_t * ctx, int af, struct hostent *ret, struct val_result_chain *results, int *h_errnop, char *buf, int buflen, int *offset, val_status_t * val_status) { int alias_count = 0; int alias_index = 0; int addr_count = 0; int addr_index = 0; int orig_offset = 0; struct val_result_chain *res; int validated = 1; int trusted = 1; struct val_rrset_rec *rrset; char *alias_target = NULL; /* * Check parameter sanity */ if (!results || !h_errnop || !buf || !offset || !ret || !val_status) { return NULL; } *val_status = VAL_DONT_KNOW; *h_errnop = 0; orig_offset = *offset; memset(ret, 0, sizeof(struct hostent)); /* * Count the number of aliases and addresses in the result */ for (res = results; res != NULL; res = res->val_rc_next) { rrset = res->val_rc_rrset; if (res->val_rc_alias && rrset) { val_log(ctx, LOG_DEBUG, "get_hostent_from_response(): type of record = CNAME"); alias_count++; continue; } // Get a count of aliases and addresses if (rrset) { struct val_rr_rec *rr = rrset->val_rrset_data; while (rr) { if ((af == AF_INET) && (rrset->val_rrset_type == ns_t_a)) { val_log(ctx, LOG_DEBUG, "get_hostent_from_response(): type of record = A"); addr_count++; } else if ((af == AF_INET6) && (rrset->val_rrset_type == ns_t_aaaa)) { val_log(ctx, LOG_DEBUG, "get_hostent_from_response(): type of record = AAAA"); addr_count++; } rr = rr->rr_next; } } } ret->h_aliases = (char **) bufalloc(buf, buflen, offset, (alias_count + 1) * sizeof(char *)); if (ret->h_aliases == NULL) { goto err; } ret->h_aliases[alias_count] = NULL; ret->h_addr_list = (char **) bufalloc(buf, buflen, offset, (addr_count + 1) * sizeof(char *)); if (ret->h_addr_list == NULL) { goto err; } ret->h_addr_list[addr_count] = NULL; alias_index = alias_count - 1; if (results == NULL) { *val_status = VAL_UNTRUSTED_ANSWER; *h_errnop = HOST_NOT_FOUND; goto err; } /* * Process the result */ for (res = results; res != NULL; res = res->val_rc_next) { rrset = res->val_rc_rrset; if (!(validated && val_isvalidated(res->val_rc_status))) validated = 0; if (!(trusted && val_istrusted(res->val_rc_status))) trusted = 0; /* save the non-existence state */ if (val_does_not_exist(res->val_rc_status)) { *val_status = res->val_rc_status; if (res->val_rc_status == VAL_NONEXISTENT_NAME || res->val_rc_status == VAL_NONEXISTENT_NAME_NOCHAIN) { *h_errnop = HOST_NOT_FOUND; } else { *h_errnop = NO_DATA; } break; } if (res->val_rc_alias && rrset) { // Handle CNAME RRs if (alias_index >= 0) { ret->h_aliases[alias_index] = (char *) bufalloc(buf, buflen, offset, (strlen(rrset->val_rrset_name) + 1) * sizeof(char)); if (ret->h_aliases[alias_index] == NULL) { goto err; } memcpy(ret->h_aliases[alias_index], rrset->val_rrset_name, strlen(rrset->val_rrset_name) + 1); alias_index--; } /* save the alias target for later use */ alias_target = res->val_rc_alias; } else if (rrset) { if (((af == AF_INET) && (rrset->val_rrset_type == ns_t_a)) || ((af == AF_INET6) && (rrset->val_rrset_type == ns_t_aaaa))) { struct val_rr_rec *rr = rrset->val_rrset_data; if (!ret->h_name) { ret->h_name = (char *) bufalloc(buf, buflen, offset, (strlen(rrset->val_rrset_name) + 1) * sizeof(char)); if (ret->h_name == NULL) { goto err; } memcpy(ret->h_name, rrset->val_rrset_name, strlen(rrset->val_rrset_name) + 1); } while (rr) { // Handle A and AAAA RRs ret->h_length = rr->rr_rdata_length; ret->h_addrtype = af; ret->h_addr_list[addr_index] = (char *) bufalloc(buf, buflen, offset, rr->rr_rdata_length * sizeof(char)); if (ret->h_addr_list[addr_index] == NULL) { goto err; } memcpy(ret->h_addr_list[addr_index], rr->rr_rdata, rr->rr_rdata_length); addr_index++; rr = rr->rr_next; } } } } /* pick up official name from the alias target */ if (!ret->h_name && alias_target) { ret->h_name = (char *) bufalloc(buf, buflen, offset, (strlen(alias_target) + 1) * sizeof(char)); if (ret->h_name == NULL) { goto err; } memcpy(ret->h_name, alias_target, strlen(alias_target) + 1); } if (addr_count > 0) { *h_errnop = NETDB_SUCCESS; if (validated) *val_status = VAL_VALIDATED_ANSWER; else if (trusted) *val_status = VAL_TRUSTED_ANSWER; else *val_status = VAL_UNTRUSTED_ANSWER; } else if (alias_count == 0) { goto err; } else if (*h_errnop == 0) { /* missing a proof of non-existence for alias */ *val_status = VAL_UNTRUSTED_ANSWER; *h_errnop = NO_DATA; } return ret; err: *offset = orig_offset; return NULL; } /* get_hostent_from_response() */
void ecdsa_sigverify(val_context_t * ctx, const u_char *data, size_t data_len, const val_dnskey_rdata_t * dnskey, const val_rrsig_rdata_t * rrsig, val_astatus_t * key_status, val_astatus_t * sig_status) { char buf[1028]; size_t buflen = 1024; u_char sha_hash[MAX_DIGEST_LENGTH]; EC_KEY *eckey = NULL; BIGNUM *bn_x = NULL; BIGNUM *bn_y = NULL; ECDSA_SIG *ecdsa_sig; size_t hashlen = 0; ecdsa_sig = ECDSA_SIG_new(); memset(sha_hash, 0, sizeof(sha_hash)); val_log(ctx, LOG_DEBUG, "ecdsa_sigverify(): parsing the public key..."); if (rrsig->algorithm == ALG_ECDSAP256SHA256) { hashlen = SHA256_DIGEST_LENGTH; SHA256(data, data_len, sha_hash); eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); /* P-256 */ } else if (rrsig->algorithm == ALG_ECDSAP384SHA384) { hashlen = SHA384_DIGEST_LENGTH; SHA384(data, data_len, sha_hash); eckey = EC_KEY_new_by_curve_name(NID_secp384r1); /* P-384 */ } if (eckey == NULL) { val_log(ctx, LOG_INFO, "ecdsa_sigverify(): could not create key for ECDSA group."); *key_status = VAL_AC_INVALID_KEY; goto err; }; /* * contruct an EC_POINT from the "Q" field in the * dnskey->public_key, dnskey->public_key_len */ if (dnskey->public_key_len != 2*hashlen) { val_log(ctx, LOG_INFO, "ecdsa_sigverify(): dnskey length does not match expected size."); *key_status = VAL_AC_INVALID_KEY; goto err; } bn_x = BN_bin2bn(dnskey->public_key, hashlen, NULL); bn_y = BN_bin2bn(&dnskey->public_key[hashlen], hashlen, NULL); if (1 != EC_KEY_set_public_key_affine_coordinates(eckey, bn_x, bn_y)) { val_log(ctx, LOG_INFO, "ecdsa_sigverify(): Error associating ECSA structure with key."); *key_status = VAL_AC_INVALID_KEY; goto err; } val_log(ctx, LOG_DEBUG, "ecdsa_sigverify(): SHA hash = %s", get_hex_string(sha_hash, hashlen, buf, buflen)); val_log(ctx, LOG_DEBUG, "ecdsa_sigverify(): verifying ECDSA signature..."); /* * contruct ECDSA signature from the "r" and "s" fileds in * rrsig->signature, rrsig->signature_len */ if (rrsig->signature_len != 2*hashlen) { val_log(ctx, LOG_INFO, "ecdsa_sigverify(): Signature length does not match expected size."); *sig_status = VAL_AC_RRSIG_VERIFY_FAILED; goto err; } ECDSA_SIG_set0(ecdsa_sig, BN_bin2bn(rrsig->signature, hashlen, NULL), BN_bin2bn(&rrsig->signature[hashlen], hashlen, NULL)); if (ECDSA_do_verify(sha_hash, hashlen, ecdsa_sig, eckey) == 1) { val_log(ctx, LOG_INFO, "ecdsa_sigverify(): returned SUCCESS"); *sig_status = VAL_AC_RRSIG_VERIFIED; } else { val_log(ctx, LOG_INFO, "ecdsa_sigverify(): returned FAILURE"); *sig_status = VAL_AC_RRSIG_VERIFY_FAILED; } /* Free all structures allocated */ err: if (ecdsa_sig) ECDSA_SIG_free(ecdsa_sig); if (bn_x) BN_free(bn_x); if (bn_y) BN_free(bn_y); if (eckey) EC_KEY_free(eckey); return; }
void rsasha_sigverify(val_context_t * ctx, const u_char *data, size_t data_len, const val_dnskey_rdata_t * dnskey, const val_rrsig_rdata_t * rrsig, val_astatus_t * key_status, val_astatus_t * sig_status) { char buf[1028]; size_t buflen = 1024; RSA *rsa = NULL; u_char sha_hash[MAX_DIGEST_LENGTH]; size_t hashlen = 0; int nid = 0; val_log(ctx, LOG_DEBUG, "rsasha_sigverify(): parsing the public key..."); if ((rsa = RSA_new()) == NULL) { val_log(ctx, LOG_INFO, "rsasha_sigverify(): could not allocate rsa structure."); *key_status = VAL_AC_INVALID_KEY; return; }; if (rsa_parse_public_key (dnskey->public_key, (size_t)dnskey->public_key_len, rsa) != VAL_NO_ERROR) { val_log(ctx, LOG_INFO, "rsasha_sigverify(): Error in parsing public key."); RSA_free(rsa); *key_status = VAL_AC_INVALID_KEY; return; } memset(sha_hash, 0, sizeof(sha_hash)); if (rrsig->algorithm == ALG_RSASHA1 #ifdef LIBVAL_NSEC3 || rrsig->algorithm == ALG_NSEC3_RSASHA1 #endif ) { SHA1(data, data_len, sha_hash); hashlen = SHA_DIGEST_LENGTH; nid = NID_sha1; } else if (rrsig->algorithm == ALG_RSASHA256) { SHA256(data, data_len, sha_hash); hashlen = SHA256_DIGEST_LENGTH; nid = NID_sha256; } else if (rrsig->algorithm == ALG_RSASHA512) { SHA512(data, data_len, sha_hash); hashlen = SHA512_DIGEST_LENGTH; nid = NID_sha512; } else { val_log(ctx, LOG_INFO, "rsasha_sigverify(): Unkown algorithm."); RSA_free(rsa); *key_status = VAL_AC_INVALID_KEY; return; } val_log(ctx, LOG_DEBUG, "rsasha_sigverify(): SHA hash = %s", get_hex_string(sha_hash, hashlen, buf, buflen)); val_log(ctx, LOG_DEBUG, "rsasha_sigverify(): verifying RSA signature..."); if (RSA_verify (nid, sha_hash, hashlen, rrsig->signature, rrsig->signature_len, rsa) == 1) { val_log(ctx, LOG_INFO, "rsasha_sigverify(): returned SUCCESS"); RSA_free(rsa); *sig_status = VAL_AC_RRSIG_VERIFIED; } else { val_log(ctx, LOG_INFO, "rsasha_sigverify(): returned FAILURE"); RSA_free(rsa); *sig_status = VAL_AC_RRSIG_VERIFY_FAILED; } return; }