1/* $NetBSD: gssapi_link.c,v 1.1 2024/02/18 20:57:31 christos 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 48struct 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 */ 56static isc_result_t 57gssapi_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 */ 74static void 75gssapi_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 */ 94static isc_result_t 95gssapi_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 */ 124static isc_result_t 125gssapi_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 */ 179static isc_result_t 180gssapi_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 222static bool 223gssapi_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 231static isc_result_t 232gssapi_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 241static bool 242gssapi_isprivate(const dst_key_t *key) { 243 UNUSED(key); 244 return (true); 245} 246 247static void 248gssapi_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 254static isc_result_t 255gssapi_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 291static isc_result_t 292gssapi_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 323static 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 347isc_result_t 348dst__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 */ 357int gssapi_link_unneeded = 1; 358#endif /* ifdef GSSAPI */ 359 360/*! \file */ 361