Home | History | Annotate | Line # | Download | only in dns
gssapi_link.c revision 1.1.4.2
      1 /*	$NetBSD: gssapi_link.c,v 1.1.4.2 2024/02/29 11:38:39 martin Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 #ifdef GSSAPI
     17 
     18 #include <stdbool.h>
     19 
     20 #include <isc/base64.h>
     21 #include <isc/buffer.h>
     22 #include <isc/mem.h>
     23 #include <isc/print.h>
     24 #include <isc/string.h>
     25 #include <isc/util.h>
     26 
     27 #include <dst/gssapi.h>
     28 #include <dst/result.h>
     29 
     30 #include "dst_internal.h"
     31 #include "dst_parse.h"
     32 
     33 #define INITIAL_BUFFER_SIZE 1024
     34 #define BUFFER_EXTRA	    1024
     35 
     36 #define REGION_TO_GBUFFER(r, gb)          \
     37 	do {                              \
     38 		(gb).length = (r).length; \
     39 		(gb).value = (r).base;    \
     40 	} while (0)
     41 
     42 #define GBUFFER_TO_REGION(gb, r)                        \
     43 	do {                                            \
     44 		(r).length = (unsigned int)(gb).length; \
     45 		(r).base = (gb).value;                  \
     46 	} while (0)
     47 
     48 struct dst_gssapi_signverifyctx {
     49 	isc_buffer_t *buffer;
     50 };
     51 
     52 /*%
     53  * Allocate a temporary "context" for use in gathering data for signing
     54  * or verifying.
     55  */
     56 static isc_result_t
     57 gssapi_create_signverify_ctx(dst_key_t *key, dst_context_t *dctx) {
     58 	dst_gssapi_signverifyctx_t *ctx;
     59 
     60 	UNUSED(key);
     61 
     62 	ctx = isc_mem_get(dctx->mctx, sizeof(dst_gssapi_signverifyctx_t));
     63 	ctx->buffer = NULL;
     64 	isc_buffer_allocate(dctx->mctx, &ctx->buffer, INITIAL_BUFFER_SIZE);
     65 
     66 	dctx->ctxdata.gssctx = ctx;
     67 
     68 	return (ISC_R_SUCCESS);
     69 }
     70 
     71 /*%
     72  * Destroy the temporary sign/verify context.
     73  */
     74 static void
     75 gssapi_destroy_signverify_ctx(dst_context_t *dctx) {
     76 	dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
     77 
     78 	if (ctx != NULL) {
     79 		if (ctx->buffer != NULL) {
     80 			isc_buffer_free(&ctx->buffer);
     81 		}
     82 		isc_mem_put(dctx->mctx, ctx,
     83 			    sizeof(dst_gssapi_signverifyctx_t));
     84 		dctx->ctxdata.gssctx = NULL;
     85 	}
     86 }
     87 
     88 /*%
     89  * Add data to our running buffer of data we will be signing or verifying.
     90  * This code will see if the new data will fit in our existing buffer, and
     91  * copy it in if it will.  If not, it will attempt to allocate a larger
     92  * buffer and copy old+new into it, and free the old buffer.
     93  */
     94 static isc_result_t
     95 gssapi_adddata(dst_context_t *dctx, const isc_region_t *data) {
     96 	dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
     97 	isc_buffer_t *newbuffer = NULL;
     98 	isc_region_t r;
     99 	unsigned int length;
    100 	isc_result_t result;
    101 
    102 	result = isc_buffer_copyregion(ctx->buffer, data);
    103 	if (result == ISC_R_SUCCESS) {
    104 		return (ISC_R_SUCCESS);
    105 	}
    106 
    107 	length = isc_buffer_length(ctx->buffer) + data->length + BUFFER_EXTRA;
    108 
    109 	isc_buffer_allocate(dctx->mctx, &newbuffer, length);
    110 
    111 	isc_buffer_usedregion(ctx->buffer, &r);
    112 	(void)isc_buffer_copyregion(newbuffer, &r);
    113 	(void)isc_buffer_copyregion(newbuffer, data);
    114 
    115 	isc_buffer_free(&ctx->buffer);
    116 	ctx->buffer = newbuffer;
    117 
    118 	return (ISC_R_SUCCESS);
    119 }
    120 
    121 /*%
    122  * Sign.
    123  */
    124 static isc_result_t
    125 gssapi_sign(dst_context_t *dctx, isc_buffer_t *sig) {
    126 	dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
    127 	isc_region_t message;
    128 	gss_buffer_desc gmessage, gsig;
    129 	OM_uint32 minor, gret;
    130 	gss_ctx_id_t gssctx = dctx->key->keydata.gssctx;
    131 	char buf[1024];
    132 
    133 	/*
    134 	 * Convert the data we wish to sign into a structure gssapi can
    135 	 * understand.
    136 	 */
    137 	isc_buffer_usedregion(ctx->buffer, &message);
    138 	REGION_TO_GBUFFER(message, gmessage);
    139 
    140 	/*
    141 	 * Generate the signature.
    142 	 */
    143 	gret = gss_get_mic(&minor, gssctx, GSS_C_QOP_DEFAULT, &gmessage, &gsig);
    144 
    145 	/*
    146 	 * If it did not complete, we log the result and return a generic
    147 	 * failure code.
    148 	 */
    149 	if (gret != GSS_S_COMPLETE) {
    150 		gss_log(3, "GSS sign error: %s",
    151 			gss_error_tostring(gret, minor, buf, sizeof(buf)));
    152 		return (ISC_R_FAILURE);
    153 	}
    154 
    155 	/*
    156 	 * If it will not fit in our allocated buffer, return that we need
    157 	 * more space.
    158 	 */
    159 	if (gsig.length > isc_buffer_availablelength(sig)) {
    160 		gss_release_buffer(&minor, &gsig);
    161 		return (ISC_R_NOSPACE);
    162 	}
    163 
    164 	/*
    165 	 * Copy the output into our buffer space, and release the gssapi
    166 	 * allocated space.
    167 	 */
    168 	isc_buffer_putmem(sig, gsig.value, (unsigned int)gsig.length);
    169 	if (gsig.length != 0U) {
    170 		gss_release_buffer(&minor, &gsig);
    171 	}
    172 
    173 	return (ISC_R_SUCCESS);
    174 }
    175 
    176 /*%
    177  * Verify.
    178  */
    179 static isc_result_t
    180 gssapi_verify(dst_context_t *dctx, const isc_region_t *sig) {
    181 	dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
    182 	isc_region_t message;
    183 	gss_buffer_desc gmessage, gsig;
    184 	OM_uint32 minor, gret;
    185 	gss_ctx_id_t gssctx = dctx->key->keydata.gssctx;
    186 	char err[1024];
    187 
    188 	/*
    189 	 * Convert the data we wish to sign into a structure gssapi can
    190 	 * understand.
    191 	 */
    192 	isc_buffer_usedregion(ctx->buffer, &message);
    193 	REGION_TO_GBUFFER(message, gmessage);
    194 	REGION_TO_GBUFFER(*sig, gsig);
    195 
    196 	/*
    197 	 * Verify the data.
    198 	 */
    199 	gret = gss_verify_mic(&minor, gssctx, &gmessage, &gsig, NULL);
    200 
    201 	/*
    202 	 * Convert return codes into something useful to us.
    203 	 */
    204 	if (gret != GSS_S_COMPLETE) {
    205 		gss_log(3, "GSS verify error: %s",
    206 			gss_error_tostring(gret, minor, err, sizeof(err)));
    207 		if (gret == GSS_S_DEFECTIVE_TOKEN || gret == GSS_S_BAD_SIG ||
    208 		    gret == GSS_S_DUPLICATE_TOKEN || gret == GSS_S_OLD_TOKEN ||
    209 		    gret == GSS_S_UNSEQ_TOKEN || gret == GSS_S_GAP_TOKEN ||
    210 		    gret == GSS_S_CONTEXT_EXPIRED || gret == GSS_S_NO_CONTEXT ||
    211 		    gret == GSS_S_FAILURE)
    212 		{
    213 			return (DST_R_VERIFYFAILURE);
    214 		} else {
    215 			return (ISC_R_FAILURE);
    216 		}
    217 	}
    218 
    219 	return (ISC_R_SUCCESS);
    220 }
    221 
    222 static bool
    223 gssapi_compare(const dst_key_t *key1, const dst_key_t *key2) {
    224 	gss_ctx_id_t gsskey1 = key1->keydata.gssctx;
    225 	gss_ctx_id_t gsskey2 = key2->keydata.gssctx;
    226 
    227 	/* No idea */
    228 	return (gsskey1 == gsskey2);
    229 }
    230 
    231 static isc_result_t
    232 gssapi_generate(dst_key_t *key, int unused, void (*callback)(int)) {
    233 	UNUSED(key);
    234 	UNUSED(unused);
    235 	UNUSED(callback);
    236 
    237 	/* No idea */
    238 	return (ISC_R_FAILURE);
    239 }
    240 
    241 static bool
    242 gssapi_isprivate(const dst_key_t *key) {
    243 	UNUSED(key);
    244 	return (true);
    245 }
    246 
    247 static void
    248 gssapi_destroy(dst_key_t *key) {
    249 	REQUIRE(key != NULL);
    250 	dst_gssapi_deletectx(key->mctx, &key->keydata.gssctx);
    251 	key->keydata.gssctx = NULL;
    252 }
    253 
    254 static isc_result_t
    255 gssapi_restore(dst_key_t *key, const char *keystr) {
    256 	OM_uint32 major, minor;
    257 	unsigned int len;
    258 	isc_buffer_t *b = NULL;
    259 	isc_region_t r;
    260 	gss_buffer_desc gssbuffer;
    261 	isc_result_t result;
    262 
    263 	len = strlen(keystr);
    264 	if ((len % 4) != 0U) {
    265 		return (ISC_R_BADBASE64);
    266 	}
    267 
    268 	len = (len / 4) * 3;
    269 
    270 	isc_buffer_allocate(key->mctx, &b, len);
    271 
    272 	result = isc_base64_decodestring(keystr, b);
    273 	if (result != ISC_R_SUCCESS) {
    274 		isc_buffer_free(&b);
    275 		return (result);
    276 	}
    277 
    278 	isc_buffer_remainingregion(b, &r);
    279 	REGION_TO_GBUFFER(r, gssbuffer);
    280 	major = gss_import_sec_context(&minor, &gssbuffer,
    281 				       (gss_ctx_id_t *)&key->keydata.gssctx);
    282 	if (major != GSS_S_COMPLETE) {
    283 		isc_buffer_free(&b);
    284 		return (ISC_R_FAILURE);
    285 	}
    286 
    287 	isc_buffer_free(&b);
    288 	return (ISC_R_SUCCESS);
    289 }
    290 
    291 static isc_result_t
    292 gssapi_dump(dst_key_t *key, isc_mem_t *mctx, char **buffer, int *length) {
    293 	OM_uint32 major, minor;
    294 	gss_buffer_desc gssbuffer;
    295 	size_t len;
    296 	char *buf;
    297 	isc_buffer_t b;
    298 	isc_region_t r;
    299 	isc_result_t result;
    300 
    301 	major = gss_export_sec_context(
    302 		&minor, (gss_ctx_id_t *)&key->keydata.gssctx, &gssbuffer);
    303 	if (major != GSS_S_COMPLETE) {
    304 		fprintf(stderr, "gss_export_sec_context -> %u, %u\n", major,
    305 			minor);
    306 		return (ISC_R_FAILURE);
    307 	}
    308 	if (gssbuffer.length == 0U) {
    309 		return (ISC_R_FAILURE);
    310 	}
    311 	len = ((gssbuffer.length + 2) / 3) * 4;
    312 	buf = isc_mem_get(mctx, len);
    313 	isc_buffer_init(&b, buf, (unsigned int)len);
    314 	GBUFFER_TO_REGION(gssbuffer, r);
    315 	result = isc_base64_totext(&r, 0, "", &b);
    316 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    317 	gss_release_buffer(&minor, &gssbuffer);
    318 	*buffer = buf;
    319 	*length = (int)len;
    320 	return (ISC_R_SUCCESS);
    321 }
    322 
    323 static dst_func_t gssapi_functions = {
    324 	gssapi_create_signverify_ctx,
    325 	NULL, /*%< createctx2 */
    326 	gssapi_destroy_signverify_ctx,
    327 	gssapi_adddata,
    328 	gssapi_sign,
    329 	gssapi_verify,
    330 	NULL, /*%< verify2 */
    331 	NULL, /*%< computesecret */
    332 	gssapi_compare,
    333 	NULL, /*%< paramcompare */
    334 	gssapi_generate,
    335 	gssapi_isprivate,
    336 	gssapi_destroy,
    337 	NULL, /*%< todns */
    338 	NULL, /*%< fromdns */
    339 	NULL, /*%< tofile */
    340 	NULL, /*%< parse */
    341 	NULL, /*%< cleanup */
    342 	NULL, /*%< fromlabel */
    343 	gssapi_dump,
    344 	gssapi_restore,
    345 };
    346 
    347 isc_result_t
    348 dst__gssapi_init(dst_func_t **funcp) {
    349 	REQUIRE(funcp != NULL);
    350 	if (*funcp == NULL) {
    351 		*funcp = &gssapi_functions;
    352 	}
    353 	return (ISC_R_SUCCESS);
    354 }
    355 
    356 #else  /* ifdef GSSAPI */
    357 int gssapi_link_unneeded = 1;
    358 #endif /* ifdef GSSAPI */
    359 
    360 /*! \file */
    361