summaryrefslogtreecommitdiffstats
path: root/lib/dns/gssapi_link.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns/gssapi_link.c')
-rw-r--r--lib/dns/gssapi_link.c388
1 files changed, 388 insertions, 0 deletions
diff --git a/lib/dns/gssapi_link.c b/lib/dns/gssapi_link.c
new file mode 100644
index 0000000..82eac95
--- /dev/null
+++ b/lib/dns/gssapi_link.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#ifdef GSSAPI
+
+#include <stdbool.h>
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+
+#include <dst/gssapi.h>
+
+#define INITIAL_BUFFER_SIZE 1024
+#define BUFFER_EXTRA 1024
+
+#define REGION_TO_GBUFFER(r, gb) \
+ do { \
+ (gb).length = (r).length; \
+ (gb).value = (r).base; \
+ } while (0)
+
+#define GBUFFER_TO_REGION(gb, r) \
+ do { \
+ (r).length = (unsigned int)(gb).length; \
+ (r).base = (gb).value; \
+ } while (0)
+
+
+struct dst_gssapi_signverifyctx {
+ isc_buffer_t *buffer;
+};
+
+/*%
+ * Allocate a temporary "context" for use in gathering data for signing
+ * or verifying.
+ */
+static isc_result_t
+gssapi_create_signverify_ctx(dst_key_t *key, dst_context_t *dctx) {
+ dst_gssapi_signverifyctx_t *ctx;
+ isc_result_t result;
+
+ UNUSED(key);
+
+ ctx = isc_mem_get(dctx->mctx, sizeof(dst_gssapi_signverifyctx_t));
+ if (ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ctx->buffer = NULL;
+ result = isc_buffer_allocate(dctx->mctx, &ctx->buffer,
+ INITIAL_BUFFER_SIZE);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(dctx->mctx, ctx, sizeof(dst_gssapi_signverifyctx_t));
+ return (result);
+ }
+
+ dctx->ctxdata.gssctx = ctx;
+
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Destroy the temporary sign/verify context.
+ */
+static void
+gssapi_destroy_signverify_ctx(dst_context_t *dctx) {
+ dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
+
+ if (ctx != NULL) {
+ if (ctx->buffer != NULL)
+ isc_buffer_free(&ctx->buffer);
+ isc_mem_put(dctx->mctx, ctx, sizeof(dst_gssapi_signverifyctx_t));
+ dctx->ctxdata.gssctx = NULL;
+ }
+}
+
+/*%
+ * Add data to our running buffer of data we will be signing or verifying.
+ * This code will see if the new data will fit in our existing buffer, and
+ * copy it in if it will. If not, it will attempt to allocate a larger
+ * buffer and copy old+new into it, and free the old buffer.
+ */
+static isc_result_t
+gssapi_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
+ isc_buffer_t *newbuffer = NULL;
+ isc_region_t r;
+ unsigned int length;
+ isc_result_t result;
+
+ result = isc_buffer_copyregion(ctx->buffer, data);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ length = isc_buffer_length(ctx->buffer) + data->length + BUFFER_EXTRA;
+
+ result = isc_buffer_allocate(dctx->mctx, &newbuffer, length);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isc_buffer_usedregion(ctx->buffer, &r);
+ (void)isc_buffer_copyregion(newbuffer, &r);
+ (void)isc_buffer_copyregion(newbuffer, data);
+
+ isc_buffer_free(&ctx->buffer);
+ ctx->buffer = newbuffer;
+
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Sign.
+ */
+static isc_result_t
+gssapi_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
+ isc_region_t message;
+ gss_buffer_desc gmessage, gsig;
+ OM_uint32 minor, gret;
+ gss_ctx_id_t gssctx = dctx->key->keydata.gssctx;
+ char buf[1024];
+
+ /*
+ * Convert the data we wish to sign into a structure gssapi can
+ * understand.
+ */
+ isc_buffer_usedregion(ctx->buffer, &message);
+ REGION_TO_GBUFFER(message, gmessage);
+
+ /*
+ * Generate the signature.
+ */
+ gret = gss_get_mic(&minor, gssctx, GSS_C_QOP_DEFAULT, &gmessage,
+ &gsig);
+
+ /*
+ * If it did not complete, we log the result and return a generic
+ * failure code.
+ */
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "GSS sign error: %s",
+ gss_error_tostring(gret, minor, buf, sizeof(buf)));
+ return (ISC_R_FAILURE);
+ }
+
+ /*
+ * If it will not fit in our allocated buffer, return that we need
+ * more space.
+ */
+ if (gsig.length > isc_buffer_availablelength(sig)) {
+ gss_release_buffer(&minor, &gsig);
+ return (ISC_R_NOSPACE);
+ }
+
+ /*
+ * Copy the output into our buffer space, and release the gssapi
+ * allocated space.
+ */
+ isc_buffer_putmem(sig, gsig.value, (unsigned int)gsig.length);
+ if (gsig.length != 0U)
+ gss_release_buffer(&minor, &gsig);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Verify.
+ */
+static isc_result_t
+gssapi_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
+ isc_region_t message, r;
+ gss_buffer_desc gmessage, gsig;
+ OM_uint32 minor, gret;
+ gss_ctx_id_t gssctx = dctx->key->keydata.gssctx;
+ unsigned char *buf;
+ char err[1024];
+
+ /*
+ * Convert the data we wish to sign into a structure gssapi can
+ * understand.
+ */
+ isc_buffer_usedregion(ctx->buffer, &message);
+ REGION_TO_GBUFFER(message, gmessage);
+
+ /*
+ * XXXMLG
+ * It seem that gss_verify_mic() modifies the signature buffer,
+ * at least on Heimdal's implementation. Copy it here to an allocated
+ * buffer.
+ */
+ buf = isc_mem_allocate(dst__memory_pool, sig->length);
+ if (buf == NULL)
+ return (ISC_R_FAILURE);
+ memmove(buf, sig->base, sig->length);
+ r.base = buf;
+ r.length = sig->length;
+ REGION_TO_GBUFFER(r, gsig);
+
+ /*
+ * Verify the data.
+ */
+ gret = gss_verify_mic(&minor, gssctx, &gmessage, &gsig, NULL);
+
+ isc_mem_free(dst__memory_pool, buf);
+
+ /*
+ * Convert return codes into something useful to us.
+ */
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "GSS verify error: %s",
+ gss_error_tostring(gret, minor, err, sizeof(err)));
+ if (gret == GSS_S_DEFECTIVE_TOKEN ||
+ gret == GSS_S_BAD_SIG ||
+ gret == GSS_S_DUPLICATE_TOKEN ||
+ gret == GSS_S_OLD_TOKEN ||
+ gret == GSS_S_UNSEQ_TOKEN ||
+ gret == GSS_S_GAP_TOKEN ||
+ gret == GSS_S_CONTEXT_EXPIRED ||
+ gret == GSS_S_NO_CONTEXT ||
+ gret == GSS_S_FAILURE)
+ return(DST_R_VERIFYFAILURE);
+ else
+ return (ISC_R_FAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static bool
+gssapi_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ gss_ctx_id_t gsskey1 = key1->keydata.gssctx;
+ gss_ctx_id_t gsskey2 = key2->keydata.gssctx;
+
+ /* No idea */
+ return (gsskey1 == gsskey2);
+}
+
+static isc_result_t
+gssapi_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+ UNUSED(key);
+ UNUSED(unused);
+ UNUSED(callback);
+
+ /* No idea */
+ return (ISC_R_FAILURE);
+}
+
+static bool
+gssapi_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (true);
+}
+
+static void
+gssapi_destroy(dst_key_t *key) {
+ REQUIRE(key != NULL);
+ dst_gssapi_deletectx(key->mctx, &key->keydata.gssctx);
+ key->keydata.gssctx = NULL;
+}
+
+static isc_result_t
+gssapi_restore(dst_key_t *key, const char *keystr) {
+ OM_uint32 major, minor;
+ unsigned int len;
+ isc_buffer_t *b = NULL;
+ isc_region_t r;
+ gss_buffer_desc gssbuffer;
+ isc_result_t result;
+
+ len = strlen(keystr);
+ if ((len % 4) != 0U)
+ return (ISC_R_BADBASE64);
+
+ len = (len / 4) * 3;
+
+ result = isc_buffer_allocate(key->mctx, &b, len);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = isc_base64_decodestring(keystr, b);
+ if (result != ISC_R_SUCCESS) {
+ isc_buffer_free(&b);
+ return (result);
+ }
+
+ isc_buffer_remainingregion(b, &r);
+ REGION_TO_GBUFFER(r, gssbuffer);
+ major = gss_import_sec_context(&minor, &gssbuffer,
+ &key->keydata.gssctx);
+ if (major != GSS_S_COMPLETE) {
+ isc_buffer_free(&b);
+ return (ISC_R_FAILURE);
+ }
+
+ isc_buffer_free(&b);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+gssapi_dump(dst_key_t *key, isc_mem_t *mctx, char **buffer, int *length) {
+ OM_uint32 major, minor;
+ gss_buffer_desc gssbuffer;
+ size_t len;
+ char *buf;
+ isc_buffer_t b;
+ isc_region_t r;
+ isc_result_t result;
+
+ major = gss_export_sec_context(&minor, &key->keydata.gssctx,
+ &gssbuffer);
+ if (major != GSS_S_COMPLETE) {
+ fprintf(stderr, "gss_export_sec_context -> %d, %d\n",
+ major, minor);
+ return (ISC_R_FAILURE);
+ }
+ if (gssbuffer.length == 0U)
+ return (ISC_R_FAILURE);
+ len = ((gssbuffer.length + 2)/3) * 4;
+ buf = isc_mem_get(mctx, len);
+ if (buf == NULL) {
+ gss_release_buffer(&minor, &gssbuffer);
+ return (ISC_R_NOMEMORY);
+ }
+ isc_buffer_init(&b, buf, (unsigned int)len);
+ GBUFFER_TO_REGION(gssbuffer, r);
+ result = isc_base64_totext(&r, 0, "", &b);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ gss_release_buffer(&minor, &gssbuffer);
+ *buffer = buf;
+ *length = (int)len;
+ return (ISC_R_SUCCESS);
+}
+
+static dst_func_t gssapi_functions = {
+ gssapi_create_signverify_ctx,
+ NULL, /*%< createctx2 */
+ gssapi_destroy_signverify_ctx,
+ gssapi_adddata,
+ gssapi_sign,
+ gssapi_verify,
+ NULL, /*%< verify2 */
+ NULL, /*%< computesecret */
+ gssapi_compare,
+ NULL, /*%< paramcompare */
+ gssapi_generate,
+ gssapi_isprivate,
+ gssapi_destroy,
+ NULL, /*%< todns */
+ NULL, /*%< fromdns */
+ NULL, /*%< tofile */
+ NULL, /*%< parse */
+ NULL, /*%< cleanup */
+ NULL, /*%< fromlabel */
+ gssapi_dump,
+ gssapi_restore,
+};
+
+isc_result_t
+dst__gssapi_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &gssapi_functions;
+ return (ISC_R_SUCCESS);
+}
+
+#else
+int gssapi_link_unneeded = 1;
+#endif
+
+/*! \file */