2 * Copyright (C) 2004-2009, 2011-2015 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2002 Internet Software Consortium.
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.
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.
19 * $Id: gssapi_link.c,v 1.17 2011/03/28 05:32:16 marka Exp $
26 #include <isc/base64.h>
27 #include <isc/buffer.h>
29 #include <isc/print.h>
30 #include <isc/string.h>
33 #include <dst/result.h>
35 #include "dst_internal.h"
36 #include "dst_parse.h"
38 #include <dst/gssapi.h>
40 #define INITIAL_BUFFER_SIZE 1024
41 #define BUFFER_EXTRA 1024
43 #define REGION_TO_GBUFFER(r, gb) \
45 (gb).length = (r).length; \
46 (gb).value = (r).base; \
49 #define GBUFFER_TO_REGION(gb, r) \
51 (r).length = (unsigned int)(gb).length; \
52 (r).base = (gb).value; \
56 struct dst_gssapi_signverifyctx {
61 * Allocate a temporary "context" for use in gathering data for signing
65 gssapi_create_signverify_ctx(dst_key_t *key, dst_context_t *dctx) {
66 dst_gssapi_signverifyctx_t *ctx;
71 ctx = isc_mem_get(dctx->mctx, sizeof(dst_gssapi_signverifyctx_t));
73 return (ISC_R_NOMEMORY);
75 result = isc_buffer_allocate(dctx->mctx, &ctx->buffer,
77 if (result != ISC_R_SUCCESS) {
78 isc_mem_put(dctx->mctx, ctx, sizeof(dst_gssapi_signverifyctx_t));
82 dctx->ctxdata.gssctx = ctx;
84 return (ISC_R_SUCCESS);
88 * Destroy the temporary sign/verify context.
91 gssapi_destroy_signverify_ctx(dst_context_t *dctx) {
92 dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
95 if (ctx->buffer != NULL)
96 isc_buffer_free(&ctx->buffer);
97 isc_mem_put(dctx->mctx, ctx, sizeof(dst_gssapi_signverifyctx_t));
98 dctx->ctxdata.gssctx = NULL;
103 * Add data to our running buffer of data we will be signing or verifying.
104 * This code will see if the new data will fit in our existing buffer, and
105 * copy it in if it will. If not, it will attempt to allocate a larger
106 * buffer and copy old+new into it, and free the old buffer.
109 gssapi_adddata(dst_context_t *dctx, const isc_region_t *data) {
110 dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
111 isc_buffer_t *newbuffer = NULL;
116 result = isc_buffer_copyregion(ctx->buffer, data);
117 if (result == ISC_R_SUCCESS)
118 return (ISC_R_SUCCESS);
120 length = isc_buffer_length(ctx->buffer) + data->length + BUFFER_EXTRA;
122 result = isc_buffer_allocate(dctx->mctx, &newbuffer, length);
123 if (result != ISC_R_SUCCESS)
126 isc_buffer_usedregion(ctx->buffer, &r);
127 (void)isc_buffer_copyregion(newbuffer, &r);
128 (void)isc_buffer_copyregion(newbuffer, data);
130 isc_buffer_free(&ctx->buffer);
131 ctx->buffer = newbuffer;
133 return (ISC_R_SUCCESS);
140 gssapi_sign(dst_context_t *dctx, isc_buffer_t *sig) {
141 dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
142 isc_region_t message;
143 gss_buffer_desc gmessage, gsig;
144 OM_uint32 minor, gret;
145 gss_ctx_id_t gssctx = dctx->key->keydata.gssctx;
149 * Convert the data we wish to sign into a structure gssapi can
152 isc_buffer_usedregion(ctx->buffer, &message);
153 REGION_TO_GBUFFER(message, gmessage);
156 * Generate the signature.
158 gret = gss_get_mic(&minor, gssctx, GSS_C_QOP_DEFAULT, &gmessage,
162 * If it did not complete, we log the result and return a generic
165 if (gret != GSS_S_COMPLETE) {
166 gss_log(3, "GSS sign error: %s",
167 gss_error_tostring(gret, minor, buf, sizeof(buf)));
168 return (ISC_R_FAILURE);
172 * If it will not fit in our allocated buffer, return that we need
175 if (gsig.length > isc_buffer_availablelength(sig)) {
176 gss_release_buffer(&minor, &gsig);
177 return (ISC_R_NOSPACE);
181 * Copy the output into our buffer space, and release the gssapi
184 isc_buffer_putmem(sig, gsig.value, (unsigned int)gsig.length);
185 if (gsig.length != 0U)
186 gss_release_buffer(&minor, &gsig);
188 return (ISC_R_SUCCESS);
195 gssapi_verify(dst_context_t *dctx, const isc_region_t *sig) {
196 dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
197 isc_region_t message, r;
198 gss_buffer_desc gmessage, gsig;
199 OM_uint32 minor, gret;
200 gss_ctx_id_t gssctx = dctx->key->keydata.gssctx;
205 * Convert the data we wish to sign into a structure gssapi can
208 isc_buffer_usedregion(ctx->buffer, &message);
209 REGION_TO_GBUFFER(message, gmessage);
213 * It seem that gss_verify_mic() modifies the signature buffer,
214 * at least on Heimdal's implementation. Copy it here to an allocated
217 buf = isc_mem_allocate(dst__memory_pool, sig->length);
219 return (ISC_R_FAILURE);
220 memmove(buf, sig->base, sig->length);
222 r.length = sig->length;
223 REGION_TO_GBUFFER(r, gsig);
228 gret = gss_verify_mic(&minor, gssctx, &gmessage, &gsig, NULL);
230 isc_mem_free(dst__memory_pool, buf);
233 * Convert return codes into something useful to us.
235 if (gret != GSS_S_COMPLETE) {
236 gss_log(3, "GSS verify error: %s",
237 gss_error_tostring(gret, minor, err, sizeof(err)));
238 if (gret == GSS_S_DEFECTIVE_TOKEN ||
239 gret == GSS_S_BAD_SIG ||
240 gret == GSS_S_DUPLICATE_TOKEN ||
241 gret == GSS_S_OLD_TOKEN ||
242 gret == GSS_S_UNSEQ_TOKEN ||
243 gret == GSS_S_GAP_TOKEN ||
244 gret == GSS_S_CONTEXT_EXPIRED ||
245 gret == GSS_S_NO_CONTEXT ||
246 gret == GSS_S_FAILURE)
247 return(DST_R_VERIFYFAILURE);
249 return (ISC_R_FAILURE);
252 return (ISC_R_SUCCESS);
256 gssapi_compare(const dst_key_t *key1, const dst_key_t *key2) {
257 gss_ctx_id_t gsskey1 = key1->keydata.gssctx;
258 gss_ctx_id_t gsskey2 = key2->keydata.gssctx;
261 return (ISC_TF(gsskey1 == gsskey2));
265 gssapi_generate(dst_key_t *key, int unused, void (*callback)(int)) {
271 return (ISC_R_FAILURE);
275 gssapi_isprivate(const dst_key_t *key) {
281 gssapi_destroy(dst_key_t *key) {
282 REQUIRE(key != NULL);
283 dst_gssapi_deletectx(key->mctx, &key->keydata.gssctx);
284 key->keydata.gssctx = NULL;
288 gssapi_restore(dst_key_t *key, const char *keystr) {
289 OM_uint32 major, minor;
291 isc_buffer_t *b = NULL;
293 gss_buffer_desc gssbuffer;
296 len = strlen(keystr);
298 return (ISC_R_BADBASE64);
302 result = isc_buffer_allocate(key->mctx, &b, len);
303 if (result != ISC_R_SUCCESS)
306 result = isc_base64_decodestring(keystr, b);
307 if (result != ISC_R_SUCCESS) {
312 isc_buffer_remainingregion(b, &r);
313 REGION_TO_GBUFFER(r, gssbuffer);
314 major = gss_import_sec_context(&minor, &gssbuffer,
315 &key->keydata.gssctx);
316 if (major != GSS_S_COMPLETE) {
318 return (ISC_R_FAILURE);
322 return (ISC_R_SUCCESS);
326 gssapi_dump(dst_key_t *key, isc_mem_t *mctx, char **buffer, int *length) {
327 OM_uint32 major, minor;
328 gss_buffer_desc gssbuffer;
335 major = gss_export_sec_context(&minor, &key->keydata.gssctx,
337 if (major != GSS_S_COMPLETE) {
338 fprintf(stderr, "gss_export_sec_context -> %d, %d\n",
340 return (ISC_R_FAILURE);
342 if (gssbuffer.length == 0U)
343 return (ISC_R_FAILURE);
344 len = ((gssbuffer.length + 2)/3) * 4;
345 buf = isc_mem_get(mctx, len);
347 gss_release_buffer(&minor, &gssbuffer);
348 return (ISC_R_NOMEMORY);
350 isc_buffer_init(&b, buf, (unsigned int)len);
351 GBUFFER_TO_REGION(gssbuffer, r);
352 result = isc_base64_totext(&r, 0, "", &b);
353 RUNTIME_CHECK(result == ISC_R_SUCCESS);
354 gss_release_buffer(&minor, &gssbuffer);
357 return (ISC_R_SUCCESS);
360 static dst_func_t gssapi_functions = {
361 gssapi_create_signverify_ctx,
362 gssapi_destroy_signverify_ctx,
366 NULL, /*%< verify2 */
367 NULL, /*%< computesecret */
369 NULL, /*%< paramcompare */
374 NULL, /*%< fromdns */
377 NULL, /*%< cleanup */
378 NULL, /*%< fromlabel */
384 dst__gssapi_init(dst_func_t **funcp) {
385 REQUIRE(funcp != NULL);
387 *funcp = &gssapi_functions;
388 return (ISC_R_SUCCESS);
392 int gssapi_link_unneeded = 1;