]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
s4 dns: Negotiate GSSAPI-based TKEYs
authorKai Blin <kai@samba.org>
Thu, 30 Aug 2012 07:04:07 +0000 (09:04 +0200)
committerKai Blin <kai@samba.org>
Fri, 31 Aug 2012 08:38:35 +0000 (10:38 +0200)
Autobuild-User(master): Kai Blin <kai@samba.org>
Autobuild-Date(master): Fri Aug 31 10:38:35 CEST 2012 on sn-devel-104

source4/dns_server/dns_query.c
source4/dns_server/dns_server.c
source4/dns_server/dns_server.h
source4/dns_server/wscript_build

index 5978fe959792307afdff5032e043178023766423..e9c3a24b561c9aed005fc043d04f76d4783d26a7 100644 (file)
 #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;
 
index 3592258a8b25bfbdc7d5fa0c098f0c11f0bb4e76..70fb6a262dd99cfa9528ec20ac5c7aa276e576a7 100644 (file)
@@ -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) {
index f871544ddd690d1c8c63aae2de217a8794e0dbac..c2fe6cf9e8f9e18e9a2c9e2eddbf25c552390887 100644 (file)
@@ -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 {
index c541d08d557c0b519ee89af78f6bb239282575df..8cb23ee938efdc3ee596ef755e466e86834bb496 100644 (file)
@@ -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()