static void rndc_connected(isc_task_t *task, isc_event_t *event) { char socktext[ISC_SOCKADDR_FORMATSIZE]; isc_socketevent_t *sevent = (isc_socketevent_t *)event; isccc_sexpr_t *request = NULL; isccc_sexpr_t *data; isccc_time_t now; isccc_region_t message; isc_region_t r; isc_uint32_t len; isc_buffer_t b; isc_result_t result; connects--; if (sevent->result != ISC_R_SUCCESS) { isc_sockaddr_format(&serveraddrs[currentaddr], socktext, sizeof(socktext)); if (sevent->result != ISC_R_CANCELED && ++currentaddr < nserveraddrs) { notify("connection failed: %s: %s", socktext, isc_result_totext(sevent->result)); isc_socket_detach(&sock); isc_event_free(&event); rndc_startconnect(&serveraddrs[currentaddr], task); return; } else fatal("connect failed: %s: %s", socktext, isc_result_totext(sevent->result)); } isc_stdtime_get(&now); DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial, now, now + 60, &request)); data = isccc_alist_lookup(request, "_data"); if (data == NULL) fatal("_data section missing"); if (isccc_cc_definestring(data, "type", "null") == NULL) fatal("out of memory"); message.rstart = databuf + 4; message.rend = databuf + sizeof(databuf); DO("render message", isccc_cc_towire(request, &message, &secret)); len = sizeof(databuf) - REGION_SIZE(message); isc_buffer_init(&b, databuf, 4); isc_buffer_putuint32(&b, len - 4); r.length = len; r.base = databuf; isccc_ccmsg_init(mctx, sock, &ccmsg); isccc_ccmsg_setmaxsize(&ccmsg, 1024 * 1024); DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task, rndc_recvnonce, NULL)); recvs++; DO("send message", isc_socket_send(sock, &r, task, rndc_senddone, NULL)); sends++; isc_event_free(&event); }
static void buffer_putuint48(isc_buffer_t *b, isc_uint64_t val) { isc_uint16_t valhi; isc_uint32_t vallo; valhi = (isc_uint16_t)(val >> 32); vallo = (isc_uint32_t)(val & 0xFFFFFFFF); isc_buffer_putuint16(b, valhi); isc_buffer_putuint32(b, vallo); }
static void control_recvmessage(isc_task_t *task, isc_event_t *event) { controlconnection_t *conn; controllistener_t *listener; controlkey_t *key; isccc_sexpr_t *request = NULL; isccc_sexpr_t *response = NULL; isc_uint32_t algorithm; isccc_region_t secret; isc_stdtime_t now; isc_buffer_t b; isc_region_t r; isc_buffer_t *text; isc_result_t result; isc_result_t eresult; isccc_sexpr_t *_ctrl; isccc_time_t sent; isccc_time_t exp; isc_uint32_t nonce; isccc_sexpr_t *data; REQUIRE(event->ev_type == ISCCC_EVENT_CCMSG); conn = event->ev_arg; listener = conn->listener; algorithm = DST_ALG_UNKNOWN; secret.rstart = NULL; text = NULL; /* Is the server shutting down? */ if (listener->controls->shuttingdown) goto cleanup; if (conn->ccmsg.result != ISC_R_SUCCESS) { if (conn->ccmsg.result != ISC_R_CANCELED && conn->ccmsg.result != ISC_R_EOF) log_invalid(&conn->ccmsg, conn->ccmsg.result); goto cleanup; } request = NULL; for (key = ISC_LIST_HEAD(listener->keys); key != NULL; key = ISC_LIST_NEXT(key, link)) { isccc_region_t ccregion; ccregion.rstart = isc_buffer_base(&conn->ccmsg.buffer); ccregion.rend = isc_buffer_used(&conn->ccmsg.buffer); secret.rstart = isc_mem_get(listener->mctx, key->secret.length); if (secret.rstart == NULL) goto cleanup; memmove(secret.rstart, key->secret.base, key->secret.length); secret.rend = secret.rstart + key->secret.length; algorithm = key->algorithm; result = isccc_cc_fromwire(&ccregion, &request, algorithm, &secret); if (result == ISC_R_SUCCESS) break; isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret)); if (result != ISCCC_R_BADAUTH) { log_invalid(&conn->ccmsg, result); goto cleanup; } } if (key == NULL) { log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); goto cleanup; } /* We shouldn't be getting a reply. */ if (isccc_cc_isreply(request)) { log_invalid(&conn->ccmsg, ISC_R_FAILURE); goto cleanup_request; } isc_stdtime_get(&now); /* * Limit exposure to replay attacks. */ _ctrl = isccc_alist_lookup(request, "_ctrl"); if (_ctrl == NULL) { log_invalid(&conn->ccmsg, ISC_R_FAILURE); goto cleanup_request; } if (isccc_cc_lookupuint32(_ctrl, "_tim", &sent) == ISC_R_SUCCESS) { if ((sent + CLOCKSKEW) < now || (sent - CLOCKSKEW) > now) { log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW); goto cleanup_request; } } else { log_invalid(&conn->ccmsg, ISC_R_FAILURE); goto cleanup_request; } /* * Expire messages that are too old. */ if (isccc_cc_lookupuint32(_ctrl, "_exp", &exp) == ISC_R_SUCCESS && now > exp) { log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED); goto cleanup_request; } /* * Duplicate suppression (required for UDP). */ isccc_cc_cleansymtab(listener->controls->symtab, now); result = isccc_cc_checkdup(listener->controls->symtab, request, now); if (result != ISC_R_SUCCESS) { if (result == ISC_R_EXISTS) result = ISCCC_R_DUPLICATE; log_invalid(&conn->ccmsg, result); goto cleanup_request; } if (conn->nonce != 0 && (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS || conn->nonce != nonce)) { log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); goto cleanup_request; } result = isc_buffer_allocate(listener->mctx, &text, 2 * 2048); if (result != ISC_R_SUCCESS) goto cleanup_request; /* * Establish nonce. */ if (conn->nonce == 0) { while (conn->nonce == 0) isc_random_get(&conn->nonce); eresult = ISC_R_SUCCESS; } else eresult = ns_control_docommand(request, &text); result = isccc_cc_createresponse(request, now, now + 60, &response); if (result != ISC_R_SUCCESS) goto cleanup_request; data = isccc_alist_lookup(response, "_data"); if (data != NULL) { if (isccc_cc_defineuint32(data, "result", eresult) == NULL) goto cleanup_response; } if (eresult != ISC_R_SUCCESS) { if (data != NULL) { const char *estr = isc_result_totext(eresult); if (isccc_cc_definestring(data, "err", estr) == NULL) goto cleanup_response; } } if (isc_buffer_usedlength(text) > 0) { if (data != NULL) { char *str = (char *)isc_buffer_base(text); if (isccc_cc_definestring(data, "text", str) == NULL) goto cleanup_response; } } _ctrl = isccc_alist_lookup(response, "_ctrl"); if (_ctrl == NULL || isccc_cc_defineuint32(_ctrl, "_nonce", conn->nonce) == NULL) goto cleanup_response; if (conn->buffer == NULL) { result = isc_buffer_allocate(listener->mctx, &conn->buffer, 2 * 2048); if (result != ISC_R_SUCCESS) goto cleanup_response; } isc_buffer_clear(conn->buffer); /* Skip the length field (4 bytes) */ isc_buffer_add(conn->buffer, 4); result = isccc_cc_towire(response, &conn->buffer, algorithm, &secret); if (result != ISC_R_SUCCESS) goto cleanup_response; isc_buffer_init(&b, conn->buffer->base, 4); isc_buffer_putuint32(&b, conn->buffer->used - 4); r.base = conn->buffer->base; r.length = conn->buffer->used; result = isc_socket_send(conn->sock, &r, task, control_senddone, conn); if (result != ISC_R_SUCCESS) goto cleanup_response; conn->sending = ISC_TRUE; isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret)); isccc_sexpr_free(&request); isccc_sexpr_free(&response); isc_buffer_free(&text); return; cleanup_response: isccc_sexpr_free(&response); cleanup_request: isccc_sexpr_free(&request); isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret)); if (text != NULL) isc_buffer_free(&text); cleanup: isc_socket_detach(&conn->sock); isccc_ccmsg_invalidate(&conn->ccmsg); conn->ccmsg_valid = ISC_FALSE; maybe_free_connection(conn); maybe_free_listener(listener); }
isc_result_t dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx, isc_buffer_t *target, unsigned int options, unsigned int *countp) { dns_rdata_t rdata = DNS_RDATA_INIT; isc_result_t result; isc_region_t remaining, tavailable; isc_buffer_t source, savedbuffer, rdlen; dns_name_t name; dns_rdatatype_t type; unsigned int i, rcount, count; /* * Convert the negative caching rdataset 'rdataset' to wire format, * compressing names as specified in 'cctx', and storing the result in * 'target'. */ REQUIRE(rdataset != NULL); REQUIRE(rdataset->type == 0); REQUIRE((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0); savedbuffer = *target; count = 0; result = dns_rdataset_first(rdataset); while (result == ISC_R_SUCCESS) { dns_rdataset_current(rdataset, &rdata); isc_buffer_init(&source, rdata.data, rdata.length); isc_buffer_add(&source, rdata.length); dns_name_init(&name, NULL); isc_buffer_remainingregion(&source, &remaining); dns_name_fromregion(&name, &remaining); INSIST(remaining.length >= name.length); isc_buffer_forward(&source, name.length); remaining.length -= name.length; INSIST(remaining.length >= 5); type = isc_buffer_getuint16(&source); isc_buffer_forward(&source, 1); rcount = isc_buffer_getuint16(&source); for (i = 0; i < rcount; i++) { /* * Get the length of this rdata and set up an * rdata structure for it. */ isc_buffer_remainingregion(&source, &remaining); INSIST(remaining.length >= 2); dns_rdata_reset(&rdata); rdata.length = isc_buffer_getuint16(&source); isc_buffer_remainingregion(&source, &remaining); rdata.data = remaining.base; rdata.type = type; rdata.rdclass = rdataset->rdclass; INSIST(remaining.length >= rdata.length); isc_buffer_forward(&source, rdata.length); if ((options & DNS_NCACHETOWIRE_OMITDNSSEC) != 0 && dns_rdatatype_isdnssec(type)) continue; /* * Write the name. */ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14); result = dns_name_towire(&name, cctx, target); if (result != ISC_R_SUCCESS) goto rollback; /* * See if we have space for type, class, ttl, and * rdata length. Write the type, class, and ttl. */ isc_buffer_availableregion(target, &tavailable); if (tavailable.length < 10) { result = ISC_R_NOSPACE; goto rollback; } isc_buffer_putuint16(target, type); isc_buffer_putuint16(target, rdataset->rdclass); isc_buffer_putuint32(target, rdataset->ttl); /* * Save space for rdata length. */ rdlen = *target; isc_buffer_add(target, 2); /* * Write the rdata. */ result = dns_rdata_towire(&rdata, cctx, target); if (result != ISC_R_SUCCESS) goto rollback; /* * Set the rdata length field to the compressed * length. */ INSIST((target->used >= rdlen.used + 2) && (target->used - rdlen.used - 2 < 65536)); isc_buffer_putuint16(&rdlen, (isc_uint16_t)(target->used - rdlen.used - 2)); count++; } INSIST(isc_buffer_remaininglength(&source) == 0); result = dns_rdataset_next(rdataset); dns_rdata_reset(&rdata); } if (result != ISC_R_NOMORE) goto rollback; *countp = count; return (ISC_R_SUCCESS); rollback: INSIST(savedbuffer.used < 65536); dns_compress_rollback(cctx, (isc_uint16_t)savedbuffer.used); *countp = 0; *target = savedbuffer; return (result); }
static isc_result_t towiresorted(dns_rdataset_t *rdataset, const dns_name_t *owner_name, dns_compress_t *cctx, isc_buffer_t *target, dns_rdatasetorderfunc_t order, const void *order_arg, isc_boolean_t partial, unsigned int options, unsigned int *countp, void **state) { dns_rdata_t rdata = DNS_RDATA_INIT; isc_region_t r; isc_result_t result; unsigned int i, count = 0, added, choice; isc_buffer_t savedbuffer, rdlen, rrbuffer; unsigned int headlen; isc_boolean_t question = ISC_FALSE; isc_boolean_t shuffle = ISC_FALSE; dns_rdata_t *shuffled = NULL, shuffled_fixed[MAX_SHUFFLE]; struct towire_sort *sorted = NULL, sorted_fixed[MAX_SHUFFLE]; /* count processed auswer ips */ int answer_count = 0; int is_no_auth_answer = 0; /* tmp count for record */ int tmp_count = 0; UNUSED(state); /* * Convert 'rdataset' to wire format, compressing names as specified * in cctx, and storing the result in 'target'. */ REQUIRE(DNS_RDATASET_VALID(rdataset)); REQUIRE(countp != NULL); REQUIRE((order == NULL) == (order_arg == NULL)); REQUIRE(cctx != NULL && cctx->mctx != NULL); if ((rdataset->attributes & DNS_RDATASETATTR_QUESTION) != 0) { question = ISC_TRUE; count = 1; result = dns_rdataset_first(rdataset); INSIST(result == ISC_R_NOMORE); } else if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) { /* * This is a negative caching rdataset. */ unsigned int ncache_opts = 0; if ((options & DNS_RDATASETTOWIRE_OMITDNSSEC) != 0) ncache_opts |= DNS_NCACHETOWIRE_OMITDNSSEC; return (dns_ncache_towire(rdataset, cctx, target, ncache_opts, countp)); } else { count = (rdataset->methods->count)(rdataset); result = dns_rdataset_first(rdataset); if (result == ISC_R_NOMORE) return (ISC_R_SUCCESS); if (result != ISC_R_SUCCESS) return (result); } /* * Do we want to shuffle this answer? */ if (!question && count > 1 && (!WANT_FIXED(rdataset) || order != NULL) && rdataset->type != dns_rdatatype_rrsig) shuffle = ISC_TRUE; if (shuffle && count > MAX_SHUFFLE) { shuffled = isc_mem_get(cctx->mctx, count * sizeof(*shuffled)); sorted = isc_mem_get(cctx->mctx, count * sizeof(*sorted)); if (shuffled == NULL || sorted == NULL) shuffle = ISC_FALSE; } else { shuffled = shuffled_fixed; sorted = sorted_fixed; } if (shuffle) { /* * First we get handles to all of the rdata. */ i = 0; do { INSIST(i < count); dns_rdata_init(&shuffled[i]); dns_rdataset_current(rdataset, &shuffled[i]); i++; result = dns_rdataset_next(rdataset); } while (result == ISC_R_SUCCESS); if (result != ISC_R_NOMORE) goto cleanup; INSIST(i == count); /* * Now we shuffle. */ if (WANT_FIXED(rdataset)) { /* * 'Fixed' order. */ INSIST(order != NULL); for (i = 0; i < count; i++) { sorted[i].key = (*order)(&shuffled[i], order_arg); sorted[i].rdata = &shuffled[i]; } } else if (WANT_RANDOM(rdataset)) { /* * 'Random' order. */ for (i = 0; i < count; i++) { dns_rdata_t rdata; isc_uint32_t val; isc_random_get(&val); choice = i + (val % (count - i)); rdata = shuffled[i]; shuffled[i] = shuffled[choice]; shuffled[choice] = rdata; if (order != NULL) sorted[i].key = (*order)(&shuffled[i], order_arg); else sorted[i].key = 0; /* Unused */ sorted[i].rdata = &shuffled[i]; } } else { /* * "Cyclic" order. */ isc_uint32_t val; unsigned int j; val = rdataset->count; if (val == ISC_UINT32_MAX) isc_random_get(&val); j = val % count; for (i = 0; i < count; i++) { if (order != NULL) sorted[i].key = (*order)(&shuffled[j], order_arg); else sorted[i].key = 0; /* Unused */ sorted[i].rdata = &shuffled[j]; j++; if (j == count) j = 0; /* Wrap around. */ } } /* * Sorted order. */ if (order != NULL) qsort(sorted, count, sizeof(sorted[0]), towire_compare); } savedbuffer = *target; i = 0; added = 0; // save the count here tmp_count = count; do { /* * Copy out the name, type, class, ttl. */ /* mark if is answer data. */ is_no_auth_answer = 0; /* Answer from a non-authoritative server,we set the ttl fixed as 500. */ if(rdataset->attributes == DNS_RDATASETATTR_LOADORDER && rdataset->type == dns_rdatatype_a){ /* Answer from a non-authoritative server,we set the ttl fixed as 500. */ if(rdataset->trust == dns_trust_answer ){ is_no_auth_answer = 1; } /* for all multi-ip answer,if set DNS_ANSWER_ONLY_ONE_IP flag, return only 1 ip. */ if(answer_count == 0){ answer_count ++; }else{ if( DNS_ANSWER_ONLY_ONE_IP ){ //fprintf(stderr,"DNS_ANSWER_ONLY_ONE_IP set,skip. \n"); if (shuffle) { tmp_count--; if (i == tmp_count) result = ISC_R_NOMORE; else result = ISC_R_SUCCESS; } else { result = dns_rdataset_next(rdataset); } continue; } } } rrbuffer = *target; dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14); result = dns_name_towire(owner_name, cctx, target); if (result != ISC_R_SUCCESS) goto rollback; headlen = sizeof(dns_rdataclass_t) + sizeof(dns_rdatatype_t); if (!question) headlen += sizeof(dns_ttl_t) + 2; /* XXX 2 for rdata len */ isc_buffer_availableregion(target, &r); if (r.length < headlen) { result = ISC_R_NOSPACE; goto rollback; } isc_buffer_putuint16(target, rdataset->type); isc_buffer_putuint16(target, rdataset->rdclass); if (!question) { if( is_no_auth_answer ){ isc_buffer_putuint32(target, DNS_DEFAULT_TTL_FOR_NO_AUTH_NAME); }else{ isc_buffer_putuint32(target, rdataset->ttl); } //fprintf(stderr,"xxxxxxxxx:trust : %d artr: %d ttl:%d cover: %d type: %d count: %d class: %d\n", rdataset->trust,rdataset->attributes,rdataset->ttl, rdataset->covers,rdataset->type, rdataset->count, rdataset->rdclass); /* * Save space for rdlen. */ rdlen = *target; isc_buffer_add(target, 2); /* * Copy out the rdata */ if (shuffle) rdata = *(sorted[i].rdata); else { dns_rdata_reset(&rdata); dns_rdataset_current(rdataset, &rdata); } result = dns_rdata_towire(&rdata, cctx, target); if (result != ISC_R_SUCCESS) goto rollback; INSIST((target->used >= rdlen.used + 2) && (target->used - rdlen.used - 2 < 65536)); isc_buffer_putuint16(&rdlen, (isc_uint16_t)(target->used - rdlen.used - 2)); added++; } if (shuffle) { i++; if (i == tmp_count) result = ISC_R_NOMORE; else result = ISC_R_SUCCESS; } else { result = dns_rdataset_next(rdataset); } } while (result == ISC_R_SUCCESS); if (result != ISC_R_NOMORE) goto rollback; *countp += tmp_count; result = ISC_R_SUCCESS; goto cleanup; rollback: if (partial && result == ISC_R_NOSPACE) { INSIST(rrbuffer.used < 65536); dns_compress_rollback(cctx, (isc_uint16_t)rrbuffer.used); *countp += added; *target = rrbuffer; goto cleanup; } INSIST(savedbuffer.used < 65536); dns_compress_rollback(cctx, (isc_uint16_t)savedbuffer.used); *countp = 0; *target = savedbuffer; cleanup: if (sorted != NULL && sorted != sorted_fixed) isc_mem_put(cctx->mctx, sorted, count * sizeof(*sorted)); if (shuffled != NULL && shuffled != shuffled_fixed) isc_mem_put(cctx->mctx, shuffled, count * sizeof(*shuffled)); return (result); }
isc_result_t dns_tsig_sign(dns_message_t *msg) { dns_tsigkey_t *key; dns_rdata_any_tsig_t tsig, querytsig; unsigned char data[128]; isc_buffer_t databuf, sigbuf; isc_buffer_t *dynbuf; dns_name_t *owner; dns_rdata_t *rdata = NULL; dns_rdatalist_t *datalist; dns_rdataset_t *dataset; isc_region_t r; isc_stdtime_t now; isc_mem_t *mctx; dst_context_t *ctx = NULL; isc_result_t ret; unsigned char badtimedata[BADTIMELEN]; unsigned int sigsize = 0; isc_boolean_t response = is_response(msg); REQUIRE(msg != NULL); REQUIRE(VALID_TSIG_KEY(dns_message_gettsigkey(msg))); /* * If this is a response, there should be a query tsig. */ if (response && msg->querytsig == NULL) return (DNS_R_EXPECTEDTSIG); dynbuf = NULL; mctx = msg->mctx; key = dns_message_gettsigkey(msg); tsig.mctx = mctx; tsig.common.rdclass = dns_rdataclass_any; tsig.common.rdtype = dns_rdatatype_tsig; ISC_LINK_INIT(&tsig.common, link); dns_name_init(&tsig.algorithm, NULL); dns_name_clone(key->algorithm, &tsig.algorithm); isc_stdtime_get(&now); tsig.timesigned = now + msg->timeadjust; tsig.fudge = DNS_TSIG_FUDGE; tsig.originalid = msg->id; isc_buffer_init(&databuf, data, sizeof(data)); if (response) tsig.error = msg->querytsigstatus; else tsig.error = dns_rcode_noerror; if (tsig.error != dns_tsigerror_badtime) { tsig.otherlen = 0; tsig.other = NULL; } else { isc_buffer_t otherbuf; tsig.otherlen = BADTIMELEN; tsig.other = badtimedata; isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen); isc_buffer_putuint48(&otherbuf, tsig.timesigned); } if (key->key != NULL && tsig.error != dns_tsigerror_badsig) { unsigned char header[DNS_MESSAGE_HEADERLEN]; isc_buffer_t headerbuf; isc_uint16_t digestbits; ret = dst_context_create3(key->key, mctx, DNS_LOGCATEGORY_DNSSEC, ISC_TRUE, &ctx); if (ret != ISC_R_SUCCESS) return (ret); /* * If this is a response, digest the query signature. */ if (response) { dns_rdata_t querytsigrdata = DNS_RDATA_INIT; ret = dns_rdataset_first(msg->querytsig); if (ret != ISC_R_SUCCESS) goto cleanup_context; dns_rdataset_current(msg->querytsig, &querytsigrdata); ret = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_putuint16(&databuf, querytsig.siglen); if (isc_buffer_availablelength(&databuf) < querytsig.siglen) { ret = ISC_R_NOSPACE; goto cleanup_context; } isc_buffer_putmem(&databuf, querytsig.signature, querytsig.siglen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } #if defined(__clang__) && \ ( __clang_major__ < 3 || \ (__clang_major__ == 3 && __clang_minor__ < 2) || \ (__clang_major__ == 4 && __clang_minor__ < 2)) /* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */ else memset(&querytsig, 0, sizeof(querytsig)); #endif /* * Digest the header. */ isc_buffer_init(&headerbuf, header, sizeof(header)); dns_message_renderheader(msg, &headerbuf); isc_buffer_usedregion(&headerbuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the remainder of the message. */ isc_buffer_usedregion(msg->buffer, &r); isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (msg->tcp_continuation == 0) { /* * Digest the name, class, ttl, alg. */ dns_name_toregion(&key->name, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_clear(&databuf); isc_buffer_putuint16(&databuf, dns_rdataclass_any); isc_buffer_putuint32(&databuf, 0); /* ttl */ isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; dns_name_toregion(&tsig.algorithm, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } /* Digest the timesigned and fudge */ isc_buffer_clear(&databuf); if (tsig.error == dns_tsigerror_badtime) { INSIST(response); tsig.timesigned = querytsig.timesigned; } isc_buffer_putuint48(&databuf, tsig.timesigned); isc_buffer_putuint16(&databuf, tsig.fudge); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (msg->tcp_continuation == 0) { /* * Digest the error and other data length. */ isc_buffer_clear(&databuf); isc_buffer_putuint16(&databuf, tsig.error); isc_buffer_putuint16(&databuf, tsig.otherlen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest other data. */ if (tsig.otherlen > 0) { r.length = tsig.otherlen; r.base = tsig.other; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } } ret = dst_key_sigsize(key->key, &sigsize); if (ret != ISC_R_SUCCESS) goto cleanup_context; tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize); if (tsig.signature == NULL) { ret = ISC_R_NOMEMORY; goto cleanup_context; } isc_buffer_init(&sigbuf, tsig.signature, sigsize); ret = dst_context_sign(ctx, &sigbuf); if (ret != ISC_R_SUCCESS) goto cleanup_signature; dst_context_destroy(&ctx); digestbits = dst_key_getbits(key->key); if (digestbits != 0) { unsigned int bytes = (digestbits + 1) / 8; if (response && bytes < querytsig.siglen) bytes = querytsig.siglen; if (bytes > isc_buffer_usedlength(&sigbuf)) bytes = isc_buffer_usedlength(&sigbuf); tsig.siglen = bytes; } else tsig.siglen = isc_buffer_usedlength(&sigbuf); } else { tsig.siglen = 0; tsig.signature = NULL; } ret = dns_message_gettemprdata(msg, &rdata); if (ret != ISC_R_SUCCESS) goto cleanup_signature; ret = isc_buffer_allocate(msg->mctx, &dynbuf, 512); if (ret != ISC_R_SUCCESS) goto cleanup_rdata; ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &tsig, dynbuf); if (ret != ISC_R_SUCCESS) goto cleanup_dynbuf; dns_message_takebuffer(msg, &dynbuf); if (tsig.signature != NULL) { isc_mem_put(mctx, tsig.signature, sigsize); tsig.signature = NULL; } owner = NULL; ret = dns_message_gettempname(msg, &owner); if (ret != ISC_R_SUCCESS) goto cleanup_rdata; dns_name_init(owner, NULL); ret = dns_name_dup(&key->name, msg->mctx, owner); if (ret != ISC_R_SUCCESS) goto cleanup_owner; datalist = NULL; ret = dns_message_gettemprdatalist(msg, &datalist); if (ret != ISC_R_SUCCESS) goto cleanup_owner; dataset = NULL; ret = dns_message_gettemprdataset(msg, &dataset); if (ret != ISC_R_SUCCESS) goto cleanup_rdatalist; datalist->rdclass = dns_rdataclass_any; datalist->type = dns_rdatatype_tsig; datalist->covers = 0; datalist->ttl = 0; ISC_LIST_INIT(datalist->rdata); ISC_LIST_APPEND(datalist->rdata, rdata, link); RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset) == ISC_R_SUCCESS); msg->tsig = dataset; msg->tsigname = owner; /* Windows does not like the tsig name being compressed. */ msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; return (ISC_R_SUCCESS); cleanup_rdatalist: dns_message_puttemprdatalist(msg, &datalist); cleanup_owner: dns_message_puttempname(msg, &owner); goto cleanup_rdata; cleanup_dynbuf: isc_buffer_free(&dynbuf); cleanup_rdata: dns_message_puttemprdata(msg, &rdata); cleanup_signature: if (tsig.signature != NULL) isc_mem_put(mctx, tsig.signature, sigsize); cleanup_context: if (ctx != NULL) dst_context_destroy(&ctx); return (ret); }
isc_result_t dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, dns_tsig_keyring_t *ring1, dns_tsig_keyring_t *ring2) { dns_rdata_any_tsig_t tsig, querytsig; isc_region_t r, source_r, header_r, sig_r; isc_buffer_t databuf; unsigned char data[32]; dns_name_t *keyname; dns_rdata_t rdata = DNS_RDATA_INIT; isc_stdtime_t now; isc_result_t ret; dns_tsigkey_t *tsigkey; dst_key_t *key = NULL; unsigned char header[DNS_MESSAGE_HEADERLEN]; dst_context_t *ctx = NULL; isc_mem_t *mctx; isc_uint16_t addcount, id; unsigned int siglen; unsigned int alg; isc_boolean_t response; REQUIRE(source != NULL); REQUIRE(DNS_MESSAGE_VALID(msg)); tsigkey = dns_message_gettsigkey(msg); response = is_response(msg); REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey)); msg->verify_attempted = 1; if (msg->tcp_continuation) { if (tsigkey == NULL || msg->querytsig == NULL) return (DNS_R_UNEXPECTEDTSIG); return (tsig_verify_tcp(source, msg)); } /* * There should be a TSIG record... */ if (msg->tsig == NULL) return (DNS_R_EXPECTEDTSIG); /* * If this is a response and there's no key or query TSIG, there * shouldn't be one on the response. */ if (response && (tsigkey == NULL || msg->querytsig == NULL)) return (DNS_R_UNEXPECTEDTSIG); mctx = msg->mctx; /* * If we're here, we know the message is well formed and contains a * TSIG record. */ keyname = msg->tsigname; ret = dns_rdataset_first(msg->tsig); if (ret != ISC_R_SUCCESS) return (ret); dns_rdataset_current(msg->tsig, &rdata); ret = dns_rdata_tostruct(&rdata, &tsig, NULL); if (ret != ISC_R_SUCCESS) return (ret); dns_rdata_reset(&rdata); if (response) { ret = dns_rdataset_first(msg->querytsig); if (ret != ISC_R_SUCCESS) return (ret); dns_rdataset_current(msg->querytsig, &rdata); ret = dns_rdata_tostruct(&rdata, &querytsig, NULL); if (ret != ISC_R_SUCCESS) return (ret); } #if defined(__clang__) && \ ( __clang_major__ < 3 || \ (__clang_major__ == 3 && __clang_minor__ < 2) || \ (__clang_major__ == 4 && __clang_minor__ < 2)) /* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */ else memset(&querytsig, 0, sizeof(querytsig)); #endif /* * Do the key name and algorithm match that of the query? */ if (response && (!dns_name_equal(keyname, &tsigkey->name) || !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))) { msg->tsigstatus = dns_tsigerror_badkey; tsig_log(msg->tsigkey, 2, "key name and algorithm do not match"); return (DNS_R_TSIGVERIFYFAILURE); } /* * Get the current time. */ isc_stdtime_get(&now); /* * Find dns_tsigkey_t based on keyname. */ if (tsigkey == NULL) { ret = ISC_R_NOTFOUND; if (ring1 != NULL) ret = dns_tsigkey_find(&tsigkey, keyname, &tsig.algorithm, ring1); if (ret == ISC_R_NOTFOUND && ring2 != NULL) ret = dns_tsigkey_find(&tsigkey, keyname, &tsig.algorithm, ring2); if (ret != ISC_R_SUCCESS) { msg->tsigstatus = dns_tsigerror_badkey; ret = dns_tsigkey_create(keyname, &tsig.algorithm, NULL, 0, ISC_FALSE, NULL, now, now, mctx, NULL, &msg->tsigkey); if (ret != ISC_R_SUCCESS) return (ret); tsig_log(msg->tsigkey, 2, "unknown key"); return (DNS_R_TSIGVERIFYFAILURE); } msg->tsigkey = tsigkey; } 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); ret = dst_key_sigsize(key, &siglen); if (ret != ISC_R_SUCCESS) 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); if (tsig.siglen > siglen) { 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_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) { sig_r.base = tsig.signature; sig_r.length = tsig.siglen; ret = dst_context_create3(key, mctx, DNS_LOGCATEGORY_DNSSEC, ISC_FALSE, &ctx); if (ret != ISC_R_SUCCESS) return (ret); if (response) { isc_buffer_init(&databuf, data, sizeof(data)); isc_buffer_putuint16(&databuf, querytsig.siglen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (querytsig.siglen > 0) { r.length = querytsig.siglen; r.base = querytsig.signature; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } } /* * Extract the header. */ isc_buffer_usedregion(source, &r); memmove(header, r.base, DNS_MESSAGE_HEADERLEN); isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); /* * Decrement the additional field counter. */ memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2); addcount = htons((isc_uint16_t)(ntohs(addcount) - 1)); memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2); /* * Put in the original id. */ id = htons(tsig.originalid); memmove(&header[0], &id, 2); /* * Digest the modified header. */ header_r.base = (unsigned char *) header; header_r.length = DNS_MESSAGE_HEADERLEN; ret = dst_context_adddata(ctx, &header_r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest all non-TSIG records. */ isc_buffer_usedregion(source, &source_r); r.base = source_r.base + DNS_MESSAGE_HEADERLEN; r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the key name. */ dns_name_toregion(&tsigkey->name, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_init(&databuf, data, sizeof(data)); isc_buffer_putuint16(&databuf, tsig.common.rdclass); isc_buffer_putuint32(&databuf, msg->tsig->ttl); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the key algorithm. */ dns_name_toregion(tsigkey->algorithm, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_clear(&databuf); isc_buffer_putuint48(&databuf, tsig.timesigned); isc_buffer_putuint16(&databuf, tsig.fudge); isc_buffer_putuint16(&databuf, tsig.error); isc_buffer_putuint16(&databuf, tsig.otherlen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (tsig.otherlen > 0) { r.base = tsig.other; r.length = tsig.otherlen; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } 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) 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; if (tsig.error != dns_rcode_noerror) { if (tsig.error == dns_tsigerror_badtime) return (DNS_R_CLOCKSKEW); else return (DNS_R_TSIGERRORSET); } msg->verified_sig = 1; return (ISC_R_SUCCESS); cleanup_context: if (ctx != NULL) dst_context_destroy(&ctx); return (ret); }
static void rndc_recvnonce(isc_task_t *task, isc_event_t *event) { isccc_sexpr_t *response = NULL; isccc_sexpr_t *_ctrl; isccc_region_t source; isc_result_t result; isc_uint32_t nonce; isccc_sexpr_t *request = NULL; isccc_time_t now; isc_region_t r; isccc_sexpr_t *data; isccc_region_t message; isc_uint32_t len; isc_buffer_t b; recvs--; if (ccmsg.result == ISC_R_EOF) fatal("connection to remote host closed\n" "This may indicate that the remote server is using " "an older version of \n" "the command protocol, this host is not authorized " "to connect,\nor the key is invalid."); if (ccmsg.result != ISC_R_SUCCESS) fatal("recv failed: %s", isc_result_totext(ccmsg.result)); source.rstart = isc_buffer_base(&ccmsg.buffer); source.rend = isc_buffer_used(&ccmsg.buffer); DO("parse message", isccc_cc_fromwire(&source, &response, &secret)); _ctrl = isccc_alist_lookup(response, "_ctrl"); if (_ctrl == NULL) fatal("_ctrl section missing"); nonce = 0; if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS) nonce = 0; isc_stdtime_get(&now); DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial, now, now + 60, &request)); data = isccc_alist_lookup(request, "_data"); if (data == NULL) fatal("_data section missing"); if (isccc_cc_definestring(data, "type", args) == NULL) fatal("out of memory"); if (nonce != 0) { _ctrl = isccc_alist_lookup(request, "_ctrl"); if (_ctrl == NULL) fatal("_ctrl section missing"); if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL) fatal("out of memory"); } message.rstart = databuf + 4; message.rend = databuf + sizeof(databuf); DO("render message", isccc_cc_towire(request, &message, &secret)); len = sizeof(databuf) - REGION_SIZE(message); isc_buffer_init(&b, databuf, 4); isc_buffer_putuint32(&b, len - 4); r.length = len; r.base = databuf; isccc_ccmsg_cancelread(&ccmsg); DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task, rndc_recvdone, NULL)); recvs++; DO("send message", isc_socket_send(sock, &r, task, rndc_senddone, NULL)); sends++; isc_event_free(&event); isccc_sexpr_free(&response); return; }
isc_result_t dns_tsig_sign(dns_message_t *msg) { dns_tsigkey_t *key; dns_rdata_any_tsig_t tsig, querytsig; unsigned char data[128]; isc_buffer_t databuf, sigbuf; isc_buffer_t *dynbuf; dns_name_t *owner; dns_rdata_t *rdata; dns_rdatalist_t *datalist; dns_rdataset_t *dataset; isc_region_t r; isc_stdtime_t now; isc_mem_t *mctx; dst_context_t *ctx = NULL; isc_result_t ret; unsigned char badtimedata[BADTIMELEN]; unsigned int sigsize = 0; REQUIRE(msg != NULL); REQUIRE(VALID_TSIG_KEY(dns_message_gettsigkey(msg))); /* * If this is a response, there should be a query tsig. */ if (is_response(msg) && msg->querytsig == NULL) return (DNS_R_EXPECTEDTSIG); dynbuf = NULL; mctx = msg->mctx; key = dns_message_gettsigkey(msg); tsig.mctx = mctx; tsig.common.rdclass = dns_rdataclass_any; tsig.common.rdtype = dns_rdatatype_tsig; ISC_LINK_INIT(&tsig.common, link); dns_name_init(&tsig.algorithm, NULL); dns_name_clone(key->algorithm, &tsig.algorithm); isc_stdtime_get(&now); tsig.timesigned = now + msg->timeadjust; tsig.fudge = DNS_TSIG_FUDGE; tsig.originalid = msg->id; isc_buffer_init(&databuf, data, sizeof(data)); if (is_response(msg)) tsig.error = msg->querytsigstatus; else tsig.error = dns_rcode_noerror; if (tsig.error != dns_tsigerror_badtime) { tsig.otherlen = 0; tsig.other = NULL; } else { isc_buffer_t otherbuf; tsig.otherlen = BADTIMELEN; tsig.other = badtimedata; isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen); buffer_putuint48(&otherbuf, tsig.timesigned); } if (key->key != NULL && tsig.error != dns_tsigerror_badsig) { unsigned char header[DNS_MESSAGE_HEADERLEN]; isc_buffer_t headerbuf; ret = dst_context_create(key->key, mctx, &ctx); if (ret != ISC_R_SUCCESS) return (ret); /* * If this is a response, digest the query signature. */ if (is_response(msg)) { dns_rdata_t querytsigrdata = DNS_RDATA_INIT; ret = dns_rdataset_first(msg->querytsig); if (ret != ISC_R_SUCCESS) goto cleanup_context; dns_rdataset_current(msg->querytsig, &querytsigrdata); ret = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_putuint16(&databuf, querytsig.siglen); if (isc_buffer_availablelength(&databuf) < querytsig.siglen) { ret = ISC_R_NOSPACE; goto cleanup_context; } isc_buffer_putmem(&databuf, querytsig.signature, querytsig.siglen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } /* * Digest the header. */ isc_buffer_init(&headerbuf, header, sizeof(header)); dns_message_renderheader(msg, &headerbuf); isc_buffer_usedregion(&headerbuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the remainder of the message. */ isc_buffer_usedregion(msg->buffer, &r); isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (msg->tcp_continuation == 0) { /* * Digest the name, class, ttl, alg. */ dns_name_toregion(&key->name, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_clear(&databuf); isc_buffer_putuint16(&databuf, dns_rdataclass_any); isc_buffer_putuint32(&databuf, 0); /* ttl */ isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; dns_name_toregion(&tsig.algorithm, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } /* Digest the timesigned and fudge */ isc_buffer_clear(&databuf); if (tsig.error == dns_tsigerror_badtime) tsig.timesigned = querytsig.timesigned; buffer_putuint48(&databuf, tsig.timesigned); isc_buffer_putuint16(&databuf, tsig.fudge); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (msg->tcp_continuation == 0) { /* * Digest the error and other data length. */ isc_buffer_clear(&databuf); isc_buffer_putuint16(&databuf, tsig.error); isc_buffer_putuint16(&databuf, tsig.otherlen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the error and other data. */ if (tsig.otherlen > 0) { r.length = tsig.otherlen; r.base = tsig.other; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } } ret = dst_key_sigsize(key->key, &sigsize); if (ret != ISC_R_SUCCESS) goto cleanup_context; tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize); if (tsig.signature == NULL) { ret = ISC_R_NOMEMORY; goto cleanup_context; } isc_buffer_init(&sigbuf, tsig.signature, sigsize); ret = dst_context_sign(ctx, &sigbuf); if (ret != ISC_R_SUCCESS) goto cleanup_signature; dst_context_destroy(&ctx); tsig.siglen = isc_buffer_usedlength(&sigbuf); } else { tsig.siglen = 0; tsig.signature = NULL; } rdata = NULL; ret = dns_message_gettemprdata(msg, &rdata); if (ret != ISC_R_SUCCESS) goto cleanup_signature; ret = isc_buffer_allocate(msg->mctx, &dynbuf, 512); if (ret != ISC_R_SUCCESS) goto cleanup_signature; ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &tsig, dynbuf); if (ret != ISC_R_SUCCESS) goto cleanup_dynbuf; dns_message_takebuffer(msg, &dynbuf); if (tsig.signature != NULL) { isc_mem_put(mctx, tsig.signature, sigsize); tsig.signature = NULL; } owner = NULL; ret = dns_message_gettempname(msg, &owner); if (ret != ISC_R_SUCCESS) goto cleanup_dynbuf; dns_name_init(owner, NULL); ret = dns_name_dup(&key->name, msg->mctx, owner); if (ret != ISC_R_SUCCESS) goto cleanup_owner; datalist = NULL; ret = dns_message_gettemprdatalist(msg, &datalist); if (ret != ISC_R_SUCCESS) goto cleanup_owner; datalist->rdclass = dns_rdataclass_any; datalist->type = dns_rdatatype_tsig; datalist->covers = 0; datalist->ttl = 0; ISC_LIST_INIT(datalist->rdata); ISC_LIST_APPEND(datalist->rdata, rdata, link); dataset = NULL; ret = dns_message_gettemprdataset(msg, &dataset); if (ret != ISC_R_SUCCESS) goto cleanup_owner; dns_rdataset_init(dataset); RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset) == ISC_R_SUCCESS); msg->tsig = dataset; msg->tsigname = owner; return (ISC_R_SUCCESS); cleanup_owner: if (owner != NULL) dns_message_puttempname(msg, &owner); cleanup_dynbuf: if (dynbuf != NULL) isc_buffer_free(&dynbuf); cleanup_signature: if (tsig.signature != NULL) isc_mem_put(mctx, tsig.signature, sigsize); cleanup_context: if (ctx != NULL) dst_context_destroy(&ctx); return (ret); }
isc_result_t dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, isc_boolean_t ignoretime, isc_mem_t *mctx, dns_rdata_t *sigrdata, dns_name_t *wild) { dns_rdata_rrsig_t sig; dns_fixedname_t fnewname; isc_region_t r; isc_buffer_t envbuf; dns_rdata_t *rdatas; int nrdatas, i; isc_stdtime_t now; isc_result_t ret; unsigned char data[300]; dst_context_t *ctx = NULL; int labels = 0; isc_uint32_t flags; REQUIRE(name != NULL); REQUIRE(set != NULL); REQUIRE(key != NULL); REQUIRE(mctx != NULL); REQUIRE(sigrdata != NULL && sigrdata->type == dns_rdatatype_rrsig); ret = dns_rdata_tostruct(sigrdata, &sig, NULL); if (ret != ISC_R_SUCCESS) return (ret); if (isc_serial_lt(sig.timeexpire, sig.timesigned)) return (DNS_R_SIGINVALID); if (!ignoretime) { isc_stdtime_get(&now); /* * Is SIG temporally valid? */ if (isc_serial_lt((isc_uint32_t)now, sig.timesigned)) return (DNS_R_SIGFUTURE); else if (isc_serial_lt(sig.timeexpire, (isc_uint32_t)now)) return (DNS_R_SIGEXPIRED); } /* * Is the key allowed to sign data? */ flags = dst_key_flags(key); if (flags & DNS_KEYTYPE_NOAUTH) return (DNS_R_KEYUNAUTHORIZED); if ((flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE) return (DNS_R_KEYUNAUTHORIZED); ret = dst_context_create(key, mctx, &ctx); if (ret != ISC_R_SUCCESS) goto cleanup_struct; /* * Digest the SIG rdata (not including the signature). */ ret = digest_sig(ctx, sigrdata, &sig); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * If the name is an expanded wildcard, use the wildcard name. */ dns_fixedname_init(&fnewname); labels = dns_name_countlabels(name) - 1; RUNTIME_CHECK(dns_name_downcase(name, dns_fixedname_name(&fnewname), NULL) == ISC_R_SUCCESS); if (labels - sig.labels > 0) dns_name_split(dns_fixedname_name(&fnewname), sig.labels + 1, NULL, dns_fixedname_name(&fnewname)); dns_name_toregion(dns_fixedname_name(&fnewname), &r); /* * Create an envelope for each rdata: <name|type|class|ttl>. */ isc_buffer_init(&envbuf, data, sizeof(data)); if (labels - sig.labels > 0) { isc_buffer_putuint8(&envbuf, 1); isc_buffer_putuint8(&envbuf, '*'); memcpy(data + 2, r.base, r.length); } else memcpy(data, r.base, r.length); isc_buffer_add(&envbuf, r.length); isc_buffer_putuint16(&envbuf, set->type); isc_buffer_putuint16(&envbuf, set->rdclass); isc_buffer_putuint32(&envbuf, sig.originalttl); ret = rdataset_to_sortedarray(set, mctx, &rdatas, &nrdatas); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_usedregion(&envbuf, &r); for (i = 0; i < nrdatas; i++) { isc_uint16_t len; isc_buffer_t lenbuf; isc_region_t lenr; /* * Skip duplicates. */ if (i > 0 && dns_rdata_compare(&rdatas[i], &rdatas[i-1]) == 0) continue; /* * Digest the envelope. */ ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_array; /* * Digest the rdata length. */ isc_buffer_init(&lenbuf, &len, sizeof(len)); INSIST(rdatas[i].length < 65536); isc_buffer_putuint16(&lenbuf, (isc_uint16_t)rdatas[i].length); isc_buffer_usedregion(&lenbuf, &lenr); /* * Digest the rdata. */ ret = dst_context_adddata(ctx, &lenr); if (ret != ISC_R_SUCCESS) goto cleanup_array; ret = dns_rdata_digest(&rdatas[i], digest_callback, ctx); if (ret != ISC_R_SUCCESS) goto cleanup_array; } r.base = sig.signature; r.length = sig.siglen; ret = dst_context_verify(ctx, &r); if (ret == DST_R_VERIFYFAILURE) ret = DNS_R_SIGINVALID; cleanup_array: isc_mem_put(mctx, rdatas, nrdatas * sizeof(dns_rdata_t)); cleanup_context: dst_context_destroy(&ctx); cleanup_struct: dns_rdata_freestruct(&sig); if (ret == ISC_R_SUCCESS && labels - sig.labels > 0) { if (wild != NULL) RUNTIME_CHECK(dns_name_concatenate(dns_wildcardname, dns_fixedname_name(&fnewname), wild, NULL) == ISC_R_SUCCESS); ret = DNS_R_FROMWILDCARD; } return (ret); }
isc_result_t dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, isc_stdtime_t *inception, isc_stdtime_t *expire, isc_mem_t *mctx, isc_buffer_t *buffer, dns_rdata_t *sigrdata) { dns_rdata_rrsig_t sig; dns_rdata_t tmpsigrdata; dns_rdata_t *rdatas; int nrdatas, i; isc_buffer_t sigbuf, envbuf; isc_region_t r; dst_context_t *ctx = NULL; isc_result_t ret; isc_buffer_t *databuf = NULL; char data[256 + 8]; isc_uint32_t flags; unsigned int sigsize; dns_fixedname_t fnewname; REQUIRE(name != NULL); REQUIRE(dns_name_countlabels(name) <= 255); REQUIRE(set != NULL); REQUIRE(key != NULL); REQUIRE(inception != NULL); REQUIRE(expire != NULL); REQUIRE(mctx != NULL); REQUIRE(sigrdata != NULL); if (*inception >= *expire) return (DNS_R_INVALIDTIME); /* * Is the key allowed to sign data? */ flags = dst_key_flags(key); if (flags & DNS_KEYTYPE_NOAUTH) return (DNS_R_KEYUNAUTHORIZED); if ((flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE) return (DNS_R_KEYUNAUTHORIZED); sig.mctx = mctx; sig.common.rdclass = set->rdclass; sig.common.rdtype = dns_rdatatype_rrsig; ISC_LINK_INIT(&sig.common, link); dns_name_init(&sig.signer, NULL); dns_name_clone(dst_key_name(key), &sig.signer); sig.covered = set->type; sig.algorithm = dst_key_alg(key); sig.labels = dns_name_countlabels(name) - 1; if (dns_name_iswildcard(name)) sig.labels--; sig.originalttl = set->ttl; sig.timesigned = *inception; sig.timeexpire = *expire; sig.keyid = dst_key_id(key); ret = dst_key_sigsize(key, &sigsize); if (ret != ISC_R_SUCCESS) return (ret); sig.siglen = sigsize; /* * The actual contents of sig.signature are not important yet, since * they're not used in digest_sig(). */ sig.signature = isc_mem_get(mctx, sig.siglen); if (sig.signature == NULL) return (ISC_R_NOMEMORY); ret = isc_buffer_allocate(mctx, &databuf, sigsize + 256 + 18); if (ret != ISC_R_SUCCESS) goto cleanup_signature; dns_rdata_init(&tmpsigrdata); ret = dns_rdata_fromstruct(&tmpsigrdata, sig.common.rdclass, sig.common.rdtype, &sig, databuf); if (ret != ISC_R_SUCCESS) goto cleanup_databuf; ret = dst_context_create(key, mctx, &ctx); if (ret != ISC_R_SUCCESS) goto cleanup_databuf; /* * Digest the SIG rdata. */ ret = digest_sig(ctx, &tmpsigrdata, &sig); if (ret != ISC_R_SUCCESS) goto cleanup_context; dns_fixedname_init(&fnewname); RUNTIME_CHECK(dns_name_downcase(name, dns_fixedname_name(&fnewname), NULL) == ISC_R_SUCCESS); dns_name_toregion(dns_fixedname_name(&fnewname), &r); /* * Create an envelope for each rdata: <name|type|class|ttl>. */ isc_buffer_init(&envbuf, data, sizeof(data)); memcpy(data, r.base, r.length); isc_buffer_add(&envbuf, r.length); isc_buffer_putuint16(&envbuf, set->type); isc_buffer_putuint16(&envbuf, set->rdclass); isc_buffer_putuint32(&envbuf, set->ttl); ret = rdataset_to_sortedarray(set, mctx, &rdatas, &nrdatas); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_usedregion(&envbuf, &r); for (i = 0; i < nrdatas; i++) { isc_uint16_t len; isc_buffer_t lenbuf; isc_region_t lenr; /* * Skip duplicates. */ if (i > 0 && dns_rdata_compare(&rdatas[i], &rdatas[i-1]) == 0) continue; /* * Digest the envelope. */ ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_array; /* * Digest the length of the rdata. */ isc_buffer_init(&lenbuf, &len, sizeof(len)); INSIST(rdatas[i].length < 65536); isc_buffer_putuint16(&lenbuf, (isc_uint16_t)rdatas[i].length); isc_buffer_usedregion(&lenbuf, &lenr); ret = dst_context_adddata(ctx, &lenr); if (ret != ISC_R_SUCCESS) goto cleanup_array; /* * Digest the rdata. */ ret = dns_rdata_digest(&rdatas[i], digest_callback, ctx); if (ret != ISC_R_SUCCESS) goto cleanup_array; } isc_buffer_init(&sigbuf, sig.signature, sig.siglen); ret = dst_context_sign(ctx, &sigbuf); if (ret != ISC_R_SUCCESS) goto cleanup_array; isc_buffer_usedregion(&sigbuf, &r); if (r.length != sig.siglen) { ret = ISC_R_NOSPACE; goto cleanup_array; } memcpy(sig.signature, r.base, sig.siglen); ret = dns_rdata_fromstruct(sigrdata, sig.common.rdclass, sig.common.rdtype, &sig, buffer); cleanup_array: isc_mem_put(mctx, rdatas, nrdatas * sizeof(dns_rdata_t)); cleanup_context: dst_context_destroy(&ctx); cleanup_databuf: isc_buffer_free(&databuf); cleanup_signature: isc_mem_put(mctx, sig.signature, sig.siglen); return (ret); }
/* * Perform an update-policy rule check against an external application * over a socket. * * This currently only supports local: for unix domain datagram sockets. * * Note that by using a datagram socket and creating a new socket each * time we avoid the need for locking and allow for parallel access to * the authorization server. */ isc_boolean_t dns_ssu_external_match(dns_name_t *identity, dns_name_t *signer, dns_name_t *name, isc_netaddr_t *tcpaddr, dns_rdatatype_t type, const dst_key_t *key, isc_mem_t *mctx) { char b_identity[DNS_NAME_FORMATSIZE]; char b_signer[DNS_NAME_FORMATSIZE]; char b_name[DNS_NAME_FORMATSIZE]; char b_addr[ISC_NETADDR_FORMATSIZE]; char b_type[DNS_RDATATYPE_FORMATSIZE]; char b_key[DST_KEY_FORMATSIZE]; isc_buffer_t *tkey_token = NULL; int fd; const char *sock_path; unsigned int req_len; isc_region_t token_region; unsigned char *data; isc_buffer_t buf; isc_uint32_t token_len = 0; isc_uint32_t reply; ssize_t ret; /* The identity contains local:/path/to/socket */ dns_name_format(identity, b_identity, sizeof(b_identity)); /* For now only local: is supported */ if (strncmp(b_identity, "local:", 6) != 0) { ssu_e_log(3, "ssu_external: invalid socket path '%s'", b_identity); return (ISC_FALSE); } sock_path = &b_identity[6]; fd = ux_socket_connect(sock_path); if (fd == -1) return (ISC_FALSE); if (key != NULL) { dst_key_format(key, b_key, sizeof(b_key)); tkey_token = dst_key_tkeytoken(key); } else b_key[0] = 0; if (tkey_token != NULL) { isc_buffer_region(tkey_token, &token_region); token_len = token_region.length; } /* Format the request elements */ if (signer != NULL) dns_name_format(signer, b_signer, sizeof(b_signer)); else b_signer[0] = 0; dns_name_format(name, b_name, sizeof(b_name)); if (tcpaddr != NULL) isc_netaddr_format(tcpaddr, b_addr, sizeof(b_addr)); else b_addr[0] = 0; dns_rdatatype_format(type, b_type, sizeof(b_type)); /* Work out how big the request will be */ req_len = sizeof(isc_uint32_t) + /* Format version */ sizeof(isc_uint32_t) + /* Length */ strlen(b_signer) + 1 + /* Signer */ strlen(b_name) + 1 + /* Name */ strlen(b_addr) + 1 + /* Address */ strlen(b_type) + 1 + /* Type */ strlen(b_key) + 1 + /* Key */ sizeof(isc_uint32_t) + /* tkey_token length */ token_len; /* tkey_token */ /* format the buffer */ data = isc_mem_allocate(mctx, req_len); if (data == NULL) { close(fd); return (ISC_FALSE); } isc_buffer_init(&buf, data, req_len); isc_buffer_putuint32(&buf, SSU_EXTERNAL_VERSION); isc_buffer_putuint32(&buf, req_len); /* Strings must be null-terminated */ isc_buffer_putstr(&buf, b_signer); isc_buffer_putuint8(&buf, 0); isc_buffer_putstr(&buf, b_name); isc_buffer_putuint8(&buf, 0); isc_buffer_putstr(&buf, b_addr); isc_buffer_putuint8(&buf, 0); isc_buffer_putstr(&buf, b_type); isc_buffer_putuint8(&buf, 0); isc_buffer_putstr(&buf, b_key); isc_buffer_putuint8(&buf, 0); isc_buffer_putuint32(&buf, token_len); if (tkey_token && token_len != 0) isc_buffer_putmem(&buf, token_region.base, token_len); ENSURE(isc_buffer_availablelength(&buf) == 0); /* Send the request */ ret = write(fd, data, req_len); isc_mem_free(mctx, data); if (ret != (ssize_t) req_len) { char strbuf[ISC_STRERRORSIZE]; isc__strerror(errno, strbuf, sizeof(strbuf)); ssu_e_log(3, "ssu_external: unable to send request - %s", strbuf); close(fd); return (ISC_FALSE); } /* Receive the reply */ ret = read(fd, &reply, sizeof(isc_uint32_t)); if (ret != (ssize_t) sizeof(isc_uint32_t)) { char strbuf[ISC_STRERRORSIZE]; isc__strerror(errno, strbuf, sizeof(strbuf)); ssu_e_log(3, "ssu_external: unable to receive reply - %s", strbuf); close(fd); return (ISC_FALSE); } close(fd); reply = ntohl(reply); if (reply == 0) { ssu_e_log(3, "ssu_external: denied external auth for '%s'", b_name); return (ISC_FALSE); } else if (reply == 1) { ssu_e_log(3, "ssu_external: allowed external auth for '%s'", b_name); return (ISC_TRUE); } ssu_e_log(3, "ssu_external: invalid reply 0x%08x", reply); return (ISC_FALSE); }
static void rndc_recvnonce(isc_task_t *task, isc_event_t *event) { isccc_sexpr_t *response = NULL; isccc_sexpr_t *_ctrl; isccc_region_t source; isc_result_t result; isc_uint32_t nonce; isccc_sexpr_t *request = NULL; isccc_time_t now; isc_region_t r; isccc_sexpr_t *data; isc_buffer_t b; recvs--; if (ccmsg.result == ISC_R_EOF) fatal("connection to remote host closed\n" "This may indicate that\n" "* the remote server is using an older version of" " the command protocol,\n" "* this host is not authorized to connect,\n" "* the clocks are not synchronized,\n" "* the key signing algorithm is incorrect, or\n" "* the key is invalid."); if (ccmsg.result != ISC_R_SUCCESS) fatal("recv failed: %s", isc_result_totext(ccmsg.result)); source.rstart = isc_buffer_base(&ccmsg.buffer); source.rend = isc_buffer_used(&ccmsg.buffer); DO("parse message", isccc_cc_fromwire(&source, &response, algorithm, &secret)); _ctrl = isccc_alist_lookup(response, "_ctrl"); if (!isccc_alist_alistp(_ctrl)) fatal("bad or missing ctrl section in response"); nonce = 0; if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS) nonce = 0; isc_stdtime_get(&now); DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial, now, now + 60, &request)); data = isccc_alist_lookup(request, "_data"); if (data == NULL) fatal("_data section missing"); if (isccc_cc_definestring(data, "type", args) == NULL) fatal("out of memory"); if (nonce != 0) { _ctrl = isccc_alist_lookup(request, "_ctrl"); if (_ctrl == NULL) fatal("_ctrl section missing"); if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL) fatal("out of memory"); } isc_buffer_clear(databuf); /* Skip the length field (4 bytes) */ isc_buffer_add(databuf, 4); DO("render message", isccc_cc_towire(request, &databuf, algorithm, &secret)); isc_buffer_init(&b, databuf->base, 4); isc_buffer_putuint32(&b, databuf->used - 4); r.base = databuf->base; r.length = databuf->used; isccc_ccmsg_cancelread(&ccmsg); DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task, rndc_recvdone, NULL)); recvs++; DO("send message", isc_socket_send(sock, &r, task, rndc_senddone, NULL)); sends++; isc_event_free(&event); isccc_sexpr_free(&response); return; }