xref: /netbsd-src/external/mpl/bind/dist/lib/dns/gssapi_link.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
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