xref: /minix/external/bsd/bind/dist/lib/dns/gssapi_link.c (revision bb9622b5)
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