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