1/* 2 * Copyright (C) 2004-2009, 2011-2014 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2000-2002 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* 19 * $Id: gssapi_link.c,v 1.17 2011/03/28 05:32:16 marka Exp $ 20 */ 21 22#include <config.h> 23 24#ifdef GSSAPI 25 26#include <isc/base64.h> 27#include <isc/buffer.h> 28#include <isc/mem.h> 29#include <isc/string.h> 30#include <isc/util.h> 31 32#include <dst/result.h> 33 34#include "dst_internal.h" 35#include "dst_parse.h" 36 37#include <dst/gssapi.h> 38 39#define INITIAL_BUFFER_SIZE 1024 40#define BUFFER_EXTRA 1024 41 42#define REGION_TO_GBUFFER(r, gb) \ 43 do { \ 44 (gb).length = (r).length; \ 45 (gb).value = (r).base; \ 46 } while (0) 47 48#define GBUFFER_TO_REGION(gb, r) \ 49 do { \ 50 (r).length = (unsigned int)(gb).length; \ 51 (r).base = (gb).value; \ 52 } while (0) 53 54 55struct dst_gssapi_signverifyctx { 56 isc_buffer_t *buffer; 57}; 58 59/*% 60 * Allocate a temporary "context" for use in gathering data for signing 61 * or verifying. 62 */ 63static isc_result_t 64gssapi_create_signverify_ctx(dst_key_t *key, dst_context_t *dctx) { 65 dst_gssapi_signverifyctx_t *ctx; 66 isc_result_t result; 67 68 UNUSED(key); 69 70 ctx = isc_mem_get(dctx->mctx, sizeof(dst_gssapi_signverifyctx_t)); 71 if (ctx == NULL) 72 return (ISC_R_NOMEMORY); 73 ctx->buffer = NULL; 74 result = isc_buffer_allocate(dctx->mctx, &ctx->buffer, 75 INITIAL_BUFFER_SIZE); 76 if (result != ISC_R_SUCCESS) { 77 isc_mem_put(dctx->mctx, ctx, sizeof(dst_gssapi_signverifyctx_t)); 78 return (result); 79 } 80 81 dctx->ctxdata.gssctx = ctx; 82 83 return (ISC_R_SUCCESS); 84} 85 86/*% 87 * Destroy the temporary sign/verify context. 88 */ 89static void 90gssapi_destroy_signverify_ctx(dst_context_t *dctx) { 91 dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx; 92 93 if (ctx != NULL) { 94 if (ctx->buffer != NULL) 95 isc_buffer_free(&ctx->buffer); 96 isc_mem_put(dctx->mctx, ctx, sizeof(dst_gssapi_signverifyctx_t)); 97 dctx->ctxdata.gssctx = NULL; 98 } 99} 100 101/*% 102 * Add data to our running buffer of data we will be signing or verifying. 103 * This code will see if the new data will fit in our existing buffer, and 104 * copy it in if it will. If not, it will attempt to allocate a larger 105 * buffer and copy old+new into it, and free the old buffer. 106 */ 107static isc_result_t 108gssapi_adddata(dst_context_t *dctx, const isc_region_t *data) { 109 dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx; 110 isc_buffer_t *newbuffer = NULL; 111 isc_region_t r; 112 unsigned int length; 113 isc_result_t result; 114 115 result = isc_buffer_copyregion(ctx->buffer, data); 116 if (result == ISC_R_SUCCESS) 117 return (ISC_R_SUCCESS); 118 119 length = isc_buffer_length(ctx->buffer) + data->length + BUFFER_EXTRA; 120 121 result = isc_buffer_allocate(dctx->mctx, &newbuffer, length); 122 if (result != ISC_R_SUCCESS) 123 return (result); 124 125 isc_buffer_usedregion(ctx->buffer, &r); 126 (void)isc_buffer_copyregion(newbuffer, &r); 127 (void)isc_buffer_copyregion(newbuffer, data); 128 129 isc_buffer_free(&ctx->buffer); 130 ctx->buffer = newbuffer; 131 132 return (ISC_R_SUCCESS); 133} 134 135/*% 136 * Sign. 137 */ 138static isc_result_t 139gssapi_sign(dst_context_t *dctx, isc_buffer_t *sig) { 140 dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx; 141 isc_region_t message; 142 gss_buffer_desc gmessage, gsig; 143 OM_uint32 minor, gret; 144 gss_ctx_id_t gssctx = dctx->key->keydata.gssctx; 145 char buf[1024]; 146 147 /* 148 * Convert the data we wish to sign into a structure gssapi can 149 * understand. 150 */ 151 isc_buffer_usedregion(ctx->buffer, &message); 152 REGION_TO_GBUFFER(message, gmessage); 153 154 /* 155 * Generate the signature. 156 */ 157 gret = gss_get_mic(&minor, gssctx, GSS_C_QOP_DEFAULT, &gmessage, 158 &gsig); 159 160 /* 161 * If it did not complete, we log the result and return a generic 162 * failure code. 163 */ 164 if (gret != GSS_S_COMPLETE) { 165 gss_log(3, "GSS sign error: %s", 166 gss_error_tostring(gret, minor, buf, sizeof(buf))); 167 return (ISC_R_FAILURE); 168 } 169 170 /* 171 * If it will not fit in our allocated buffer, return that we need 172 * more space. 173 */ 174 if (gsig.length > isc_buffer_availablelength(sig)) { 175 gss_release_buffer(&minor, &gsig); 176 return (ISC_R_NOSPACE); 177 } 178 179 /* 180 * Copy the output into our buffer space, and release the gssapi 181 * allocated space. 182 */ 183 isc_buffer_putmem(sig, gsig.value, (unsigned int)gsig.length); 184 if (gsig.length != 0U) 185 gss_release_buffer(&minor, &gsig); 186 187 return (ISC_R_SUCCESS); 188} 189 190/*% 191 * Verify. 192 */ 193static isc_result_t 194gssapi_verify(dst_context_t *dctx, const isc_region_t *sig) { 195 dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx; 196 isc_region_t message, r; 197 gss_buffer_desc gmessage, gsig; 198 OM_uint32 minor, gret; 199 gss_ctx_id_t gssctx = dctx->key->keydata.gssctx; 200 unsigned char *buf; 201 char err[1024]; 202 203 /* 204 * Convert the data we wish to sign into a structure gssapi can 205 * understand. 206 */ 207 isc_buffer_usedregion(ctx->buffer, &message); 208 REGION_TO_GBUFFER(message, gmessage); 209 210 /* 211 * XXXMLG 212 * It seem that gss_verify_mic() modifies the signature buffer, 213 * at least on Heimdal's implementation. Copy it here to an allocated 214 * buffer. 215 */ 216 buf = isc_mem_allocate(dst__memory_pool, sig->length); 217 if (buf == NULL) 218 return (ISC_R_FAILURE); 219 memmove(buf, sig->base, sig->length); 220 r.base = buf; 221 r.length = sig->length; 222 REGION_TO_GBUFFER(r, gsig); 223 224 /* 225 * Verify the data. 226 */ 227 gret = gss_verify_mic(&minor, gssctx, &gmessage, &gsig, NULL); 228 229 isc_mem_free(dst__memory_pool, buf); 230 231 /* 232 * Convert return codes into something useful to us. 233 */ 234 if (gret != GSS_S_COMPLETE) { 235 gss_log(3, "GSS verify error: %s", 236 gss_error_tostring(gret, minor, err, sizeof(err))); 237 if (gret == GSS_S_DEFECTIVE_TOKEN || 238 gret == GSS_S_BAD_SIG || 239 gret == GSS_S_DUPLICATE_TOKEN || 240 gret == GSS_S_OLD_TOKEN || 241 gret == GSS_S_UNSEQ_TOKEN || 242 gret == GSS_S_GAP_TOKEN || 243 gret == GSS_S_CONTEXT_EXPIRED || 244 gret == GSS_S_NO_CONTEXT || 245 gret == GSS_S_FAILURE) 246 return(DST_R_VERIFYFAILURE); 247 else 248 return (ISC_R_FAILURE); 249 } 250 251 return (ISC_R_SUCCESS); 252} 253 254static isc_boolean_t 255gssapi_compare(const dst_key_t *key1, const dst_key_t *key2) { 256 gss_ctx_id_t gsskey1 = key1->keydata.gssctx; 257 gss_ctx_id_t gsskey2 = key2->keydata.gssctx; 258 259 /* No idea */ 260 return (ISC_TF(gsskey1 == gsskey2)); 261} 262 263static isc_result_t 264gssapi_generate(dst_key_t *key, int unused, void (*callback)(int)) { 265 UNUSED(key); 266 UNUSED(unused); 267 UNUSED(callback); 268 269 /* No idea */ 270 return (ISC_R_FAILURE); 271} 272 273static isc_boolean_t 274gssapi_isprivate(const dst_key_t *key) { 275 UNUSED(key); 276 return (ISC_TRUE); 277} 278 279static void 280gssapi_destroy(dst_key_t *key) { 281 REQUIRE(key != NULL); 282 dst_gssapi_deletectx(key->mctx, &key->keydata.gssctx); 283 key->keydata.gssctx = NULL; 284} 285 286static isc_result_t 287gssapi_restore(dst_key_t *key, const char *keystr) { 288 OM_uint32 major, minor; 289 unsigned int len; 290 isc_buffer_t *b = NULL; 291 isc_region_t r; 292 gss_buffer_desc gssbuffer; 293 isc_result_t result; 294 295 len = strlen(keystr); 296 if ((len % 4) != 0U) 297 return (ISC_R_BADBASE64); 298 299 len = (len / 4) * 3; 300 301 result = isc_buffer_allocate(key->mctx, &b, len); 302 if (result != ISC_R_SUCCESS) 303 return (result); 304 305 result = isc_base64_decodestring(keystr, b); 306 if (result != ISC_R_SUCCESS) { 307 isc_buffer_free(&b); 308 return (result); 309 } 310 311 isc_buffer_remainingregion(b, &r); 312 REGION_TO_GBUFFER(r, gssbuffer); 313 major = gss_import_sec_context(&minor, &gssbuffer, 314 &key->keydata.gssctx); 315 if (major != GSS_S_COMPLETE) { 316 isc_buffer_free(&b); 317 return (ISC_R_FAILURE); 318 } 319 320 isc_buffer_free(&b); 321 return (ISC_R_SUCCESS); 322} 323 324static isc_result_t 325gssapi_dump(dst_key_t *key, isc_mem_t *mctx, char **buffer, int *length) { 326 OM_uint32 major, minor; 327 gss_buffer_desc gssbuffer; 328 size_t len; 329 char *buf; 330 isc_buffer_t b; 331 isc_region_t r; 332 isc_result_t result; 333 334 major = gss_export_sec_context(&minor, &key->keydata.gssctx, 335 &gssbuffer); 336 if (major != GSS_S_COMPLETE) { 337 fprintf(stderr, "gss_export_sec_context -> %d, %d\n", 338 major, minor); 339 return (ISC_R_FAILURE); 340 } 341 if (gssbuffer.length == 0U) 342 return (ISC_R_FAILURE); 343 len = ((gssbuffer.length + 2)/3) * 4; 344 buf = isc_mem_get(mctx, len); 345 if (buf == NULL) { 346 gss_release_buffer(&minor, &gssbuffer); 347 return (ISC_R_NOMEMORY); 348 } 349 isc_buffer_init(&b, buf, (unsigned int)len); 350 GBUFFER_TO_REGION(gssbuffer, r); 351 result = isc_base64_totext(&r, 0, "", &b); 352 RUNTIME_CHECK(result == ISC_R_SUCCESS); 353 gss_release_buffer(&minor, &gssbuffer); 354 *buffer = buf; 355 *length = (int)len; 356 return (ISC_R_SUCCESS); 357} 358 359static dst_func_t gssapi_functions = { 360 gssapi_create_signverify_ctx, 361 gssapi_destroy_signverify_ctx, 362 gssapi_adddata, 363 gssapi_sign, 364 gssapi_verify, 365 NULL, /*%< verify2 */ 366 NULL, /*%< computesecret */ 367 gssapi_compare, 368 NULL, /*%< paramcompare */ 369 gssapi_generate, 370 gssapi_isprivate, 371 gssapi_destroy, 372 NULL, /*%< todns */ 373 NULL, /*%< fromdns */ 374 NULL, /*%< tofile */ 375 NULL, /*%< parse */ 376 NULL, /*%< cleanup */ 377 NULL, /*%< fromlabel */ 378 gssapi_dump, 379 gssapi_restore, 380}; 381 382isc_result_t 383dst__gssapi_init(dst_func_t **funcp) { 384 REQUIRE(funcp != NULL); 385 if (*funcp == NULL) 386 *funcp = &gssapi_functions; 387 return (ISC_R_SUCCESS); 388} 389 390#else 391int gssapi_link_unneeded = 1; 392#endif 393 394/*! \file */ 395