diff options
Diffstat (limited to 'lib/dns/gssapi_link.c')
-rw-r--r-- | lib/dns/gssapi_link.c | 388 |
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 */ |