summaryrefslogtreecommitdiff
path: root/source4/dns_server
diff options
context:
space:
mode:
authorKai Blin <kai@samba.org>2012-08-30 09:04:07 +0200
committerKai Blin <kai@samba.org>2012-08-31 10:38:35 +0200
commit7204dc9708e5f5164dcd9b7cc3fcb2ea27dcd62e (patch)
tree9d0bdd38eb1a19b57e2c0cc10d6685b4ff7b10b2 /source4/dns_server
parentd2c0387d66038fb474daa1507923c2138a6e584f (diff)
downloadsamba-7204dc9708e5f5164dcd9b7cc3fcb2ea27dcd62e.tar.gz
s4 dns: Negotiate GSSAPI-based TKEYs
Autobuild-User(master): Kai Blin <kai@samba.org> Autobuild-Date(master): Fri Aug 31 10:38:35 CEST 2012 on sn-devel-104
Diffstat (limited to 'source4/dns_server')
-rw-r--r--source4/dns_server/dns_query.c224
-rw-r--r--source4/dns_server/dns_server.c18
-rw-r--r--source4/dns_server/dns_server.h11
-rw-r--r--source4/dns_server/wscript_build2
4 files changed, 254 insertions, 1 deletions
diff --git a/source4/dns_server/dns_query.c b/source4/dns_server/dns_query.c
index 5978fe95979..e9c3a24b561 100644
--- a/source4/dns_server/dns_query.c
+++ b/source4/dns_server/dns_query.c
@@ -33,6 +33,10 @@
#include "libcli/dns/libdns.h"
#include "lib/util/util_net.h"
#include "lib/util/tevent_werror.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "lib/util/dlinklist.h"
static WERROR create_response_rr(const struct dns_name_question *question,
const struct dnsp_DnssrvRpcRecord *rec,
@@ -317,6 +321,214 @@ static WERROR handle_question(struct dns_server *dns,
return WERR_OK;
}
+static NTSTATUS create_new_tkey(TALLOC_CTX *mem_ctx,
+ struct dns_server *dns,
+ struct dns_server_tkey **tkey,
+ const char* name)
+{
+ NTSTATUS status;
+ struct dns_server_tkey *k = talloc_zero(mem_ctx, struct dns_server_tkey);
+
+ if (k == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ k->name = talloc_strdup(mem_ctx, name);
+
+ if (k->name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = samba_server_gensec_start(k,
+ dns->task->event_ctx,
+ dns->task->msg_ctx,
+ dns->task->lp_ctx,
+ dns->server_credentials,
+ "dns",
+ &k->gensec);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
+ *tkey = NULL;
+ return status;
+ }
+
+ gensec_want_feature(k->gensec, GENSEC_FEATURE_SIGN);
+
+ status = gensec_start_mech_by_oid(k->gensec, GENSEC_OID_SPNEGO);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC server code: %s\n",
+ nt_errstr(status)));
+ *tkey = NULL;
+ return status;
+ }
+
+ *tkey = k;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS accept_gss_ticket(TALLOC_CTX *mem_ctx,
+ struct dns_server *dns,
+ struct dns_server_tkey *tkey,
+ const DATA_BLOB *key,
+ DATA_BLOB *reply,
+ uint16_t *dns_auth_error)
+{
+ NTSTATUS status;
+
+ status = gensec_update(tkey->gensec, mem_ctx, dns->task->event_ctx,
+ *key, reply);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_MORE_PROCESSING_REQUIRED, status)) {
+ *dns_auth_error = DNS_RCODE_OK;
+ return status;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+
+ status = gensec_session_info(tkey->gensec, tkey, &tkey->session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ *dns_auth_error = DNS_RCODE_BADKEY;
+ return status;
+ }
+ *dns_auth_error = DNS_RCODE_OK;
+ }
+
+ return status;
+}
+
+static struct dns_server_tkey *find_tkey(struct dns_server *dns,
+ const char *name)
+{
+ struct dns_server_tkey *tkey = NULL;
+
+ for (tkey = dns->tkeys; tkey != NULL; tkey = tkey->next) {
+ if (dns_name_equal(name, tkey->name)) {
+ break;
+ }
+ }
+
+ return tkey;
+}
+
+static WERROR handle_tkey(struct dns_server *dns,
+ TALLOC_CTX *mem_ctx,
+ const struct dns_name_packet *in,
+ struct dns_res_rec **answers,
+ uint16_t *ancount)
+{
+ struct dns_res_rec *in_tkey = NULL;
+ struct dns_res_rec *ret_tkey;
+ uint16_t i;
+
+ for (i = 0; i < in->arcount; i++) {
+ if (in->additional[i].rr_type == DNS_QTYPE_TKEY) {
+ in_tkey = &in->additional[i];
+ break;
+ }
+ }
+
+ /* If this is a TKEY query, it should have a TKEY RR.
+ * Behaviour is not really specified in RFC 2930 or RFC 3645, but
+ * FORMAT_ERROR seems to be what BIND uses .*/
+ if (in_tkey == NULL) {
+ return DNS_ERR(FORMAT_ERROR);
+ }
+
+ ret_tkey = talloc_zero(mem_ctx, struct dns_res_rec);
+ if (ret_tkey == NULL) {
+ return WERR_NOMEM;
+ }
+
+ ret_tkey->name = talloc_strdup(ret_tkey, in_tkey->name);
+ if (ret_tkey->name == NULL) {
+ return WERR_NOMEM;
+ }
+
+ ret_tkey->rr_type = DNS_QTYPE_TKEY;
+ ret_tkey->rr_class = DNS_QCLASS_ANY;
+ ret_tkey->length = UINT16_MAX;
+
+ ret_tkey->rdata.tkey_record.algorithm = talloc_strdup(ret_tkey, ret_tkey->name);
+ if (ret_tkey->rdata.tkey_record.algorithm == NULL) {
+ return WERR_NOMEM;
+ }
+
+ ret_tkey->rdata.tkey_record.inception = in_tkey->rdata.tkey_record.inception;
+ ret_tkey->rdata.tkey_record.expiration = in_tkey->rdata.tkey_record.expiration;
+ ret_tkey->rdata.tkey_record.mode = in_tkey->rdata.tkey_record.mode;
+
+ switch (in_tkey->rdata.tkey_record.mode) {
+ case DNS_TKEY_MODE_DH:
+ /* FIXME: According to RFC 2930, we MUST support this, but we don't.
+ * Still, claim it's a bad key instead of a bad mode */
+ ret_tkey->rdata.tkey_record.error = DNS_RCODE_BADKEY;
+ break;
+ case DNS_TKEY_MODE_GSSAPI: {
+ NTSTATUS status;
+ struct dns_server_tkey *tkey;
+ DATA_BLOB key;
+ DATA_BLOB reply;
+
+ tkey = find_tkey(dns, in->questions[0].name);
+ if (tkey != NULL && tkey->complete) {
+ /* TODO: check if the key is still valid */
+ DEBUG(1, ("Rejecting tkey negotiation for already established key\n"));
+ ret_tkey->rdata.tkey_record.error = DNS_RCODE_BADNAME;
+ break;
+ }
+
+ if (tkey == NULL) {
+ status = create_new_tkey(dns, dns, &tkey,
+ in->questions[0].name);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret_tkey->rdata.tkey_record.error = DNS_RCODE_BADKEY;
+ return ntstatus_to_werror(status);
+ }
+
+ DLIST_ADD_END(dns->tkeys, tkey, NULL);
+ }
+
+ key.data = in_tkey->rdata.tkey_record.key_data;
+ key.length = in_tkey->rdata.tkey_record.key_size;
+
+ status = accept_gss_ticket(ret_tkey, dns, tkey, &key, &reply,
+ &ret_tkey->rdata.tkey_record.error);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("More processing required\n"));
+ ret_tkey->rdata.tkey_record.error = DNS_RCODE_BADKEY;
+ } else if (NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Tkey handshake completed\n"));
+ } else {
+ DEBUG(0, ("GSS key negotiation returned %s\n", nt_errstr(status)));
+ ret_tkey->rdata.tkey_record.error = DNS_RCODE_BADKEY;
+ }
+
+ break;
+ }
+ case DNS_TKEY_MODE_DELETE:
+ /* TODO: implement me */
+ DEBUG(1, ("Should delete tkey here\n"));
+ ret_tkey->rdata.tkey_record.error = DNS_RCODE_OK;
+ break;
+ case DNS_TKEY_MODE_NULL:
+ case DNS_TKEY_MODE_SERVER:
+ case DNS_TKEY_MODE_CLIENT:
+ case DNS_TKEY_MODE_LAST:
+ /* We don't have to implement these, return a mode error */
+ ret_tkey->rdata.tkey_record.error = DNS_RCODE_BADMODE;
+ break;
+ default:
+ DEBUG(1, ("Unsupported TKEY mode %d\n",
+ in_tkey->rdata.tkey_record.mode));
+ }
+
+ *answers = ret_tkey;
+ *ancount = 1;
+
+ return WERR_OK;
+}
+
struct dns_server_process_query_state {
struct dns_res_rec *answers;
uint16_t ancount;
@@ -352,6 +564,18 @@ struct tevent_req *dns_server_process_query_send(
return tevent_req_post(req, ev);
}
+ if (in->questions[0].question_type == DNS_QTYPE_TKEY) {
+ WERROR err;
+
+ err = handle_tkey(dns, state, in, &state->answers,
+ &state->ancount);
+ if (tevent_req_werror(req, err)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
if (dns_authorative_for_zone(dns, in->questions[0].name)) {
WERROR err;
diff --git a/source4/dns_server/dns_server.c b/source4/dns_server/dns_server.c
index 3592258a8b2..70fb6a262dd 100644
--- a/source4/dns_server/dns_server.c
+++ b/source4/dns_server/dns_server.c
@@ -43,6 +43,8 @@
#include "auth/session.h"
#include "lib/util/dlinklist.h"
#include "lib/util/tevent_werror.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
NTSTATUS server_service_dns_init(void);
@@ -720,6 +722,22 @@ static void dns_task_init(struct task_server *task)
dns->task = task;
+ dns->server_credentials = cli_credentials_init(dns);
+ if (!dns->server_credentials) {
+ task_server_terminate(task, "Failed to init server credentials\n", true);
+ return;
+ }
+
+ cli_credentials_set_conf(dns->server_credentials, task->lp_ctx);
+ status = cli_credentials_set_machine_account(dns->server_credentials, task->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task,
+ talloc_asprintf(task, "Failed to obtain server credentials, perhaps a standalone server?: %s\n",
+ nt_errstr(status)),
+ true);
+ return;
+ }
+
dns->samdb = samdb_connect(dns, dns->task->event_ctx, dns->task->lp_ctx,
system_session(dns->task->lp_ctx), 0);
if (!dns->samdb) {
diff --git a/source4/dns_server/dns_server.h b/source4/dns_server/dns_server.h
index f871544ddd6..c2fe6cf9e8f 100644
--- a/source4/dns_server/dns_server.h
+++ b/source4/dns_server/dns_server.h
@@ -33,10 +33,21 @@ struct dns_server_zone {
struct ldb_dn *dn;
};
+struct dns_server_tkey {
+ struct dns_server_tkey *prev, *next;
+ const char *name;
+ enum dns_tkey_mode mode;
+ struct auth_session_info *session_info;
+ struct gensec_security *gensec;
+ bool complete;
+};
+
struct dns_server {
struct task_server *task;
struct ldb_context *samdb;
struct dns_server_zone *zones;
+ struct dns_server_tkey *tkeys;
+ struct cli_credentials *server_credentials;
};
struct dns_request_state {
diff --git a/source4/dns_server/wscript_build b/source4/dns_server/wscript_build
index c541d08d557..8cb23ee938e 100644
--- a/source4/dns_server/wscript_build
+++ b/source4/dns_server/wscript_build
@@ -4,7 +4,7 @@ bld.SAMBA_MODULE('service_dns',
source='dns_server.c dns_query.c dns_update.c dns_utils.c',
subsystem='service',
init_function='server_service_dns_init',
- deps='samba-hostconfig LIBTSOCKET LIBSAMBA_TSOCKET ldbsamba clidns',
+ deps='samba-hostconfig LIBTSOCKET LIBSAMBA_TSOCKET ldbsamba clidns gensec auth',
local_include=False,
internal_module=False,
enabled=bld.AD_DC_BUILD_IS_ENABLED()