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