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