xref: /netbsd/external/mpl/bind/dist/lib/dns/gssapi_link.c (revision c0b5d9fb)
1 /*	$NetBSD: gssapi_link.c,v 1.9 2022/09/23 12:15:29 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 #ifdef GSSAPI
17 
18 #include <stdbool.h>
19 
20 #include <isc/base64.h>
21 #include <isc/buffer.h>
22 #include <isc/mem.h>
23 #include <isc/print.h>
24 #include <isc/string.h>
25 #include <isc/util.h>
26 
27 #include <dst/gssapi.h>
28 #include <dst/result.h>
29 
30 #include "dst_internal.h"
31 #include "dst_parse.h"
32 
33 #define INITIAL_BUFFER_SIZE 1024
34 #define BUFFER_EXTRA	    1024
35 
36 #define REGION_TO_GBUFFER(r, gb)          \
37 	do {                              \
38 		(gb).length = (r).length; \
39 		(gb).value = (r).base;    \
40 	} while (0)
41 
42 #define GBUFFER_TO_REGION(gb, r)                        \
43 	do {                                            \
44 		(r).length = (unsigned int)(gb).length; \
45 		(r).base = (gb).value;                  \
46 	} while (0)
47 
48 struct dst_gssapi_signverifyctx {
49 	isc_buffer_t *buffer;
50 };
51 
52 /*%
53  * Allocate a temporary "context" for use in gathering data for signing
54  * or verifying.
55  */
56 static isc_result_t
gssapi_create_signverify_ctx(dst_key_t * key,dst_context_t * dctx)57 gssapi_create_signverify_ctx(dst_key_t *key, dst_context_t *dctx) {
58 	dst_gssapi_signverifyctx_t *ctx;
59 
60 	UNUSED(key);
61 
62 	ctx = isc_mem_get(dctx->mctx, sizeof(dst_gssapi_signverifyctx_t));
63 	ctx->buffer = NULL;
64 	isc_buffer_allocate(dctx->mctx, &ctx->buffer, INITIAL_BUFFER_SIZE);
65 
66 	dctx->ctxdata.gssctx = ctx;
67 
68 	return (ISC_R_SUCCESS);
69 }
70 
71 /*%
72  * Destroy the temporary sign/verify context.
73  */
74 static void
gssapi_destroy_signverify_ctx(dst_context_t * dctx)75 gssapi_destroy_signverify_ctx(dst_context_t *dctx) {
76 	dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
77 
78 	if (ctx != NULL) {
79 		if (ctx->buffer != NULL) {
80 			isc_buffer_free(&ctx->buffer);
81 		}
82 		isc_mem_put(dctx->mctx, ctx,
83 			    sizeof(dst_gssapi_signverifyctx_t));
84 		dctx->ctxdata.gssctx = NULL;
85 	}
86 }
87 
88 /*%
89  * Add data to our running buffer of data we will be signing or verifying.
90  * This code will see if the new data will fit in our existing buffer, and
91  * copy it in if it will.  If not, it will attempt to allocate a larger
92  * buffer and copy old+new into it, and free the old buffer.
93  */
94 static isc_result_t
gssapi_adddata(dst_context_t * dctx,const isc_region_t * data)95 gssapi_adddata(dst_context_t *dctx, const isc_region_t *data) {
96 	dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
97 	isc_buffer_t *newbuffer = NULL;
98 	isc_region_t r;
99 	unsigned int length;
100 	isc_result_t result;
101 
102 	result = isc_buffer_copyregion(ctx->buffer, data);
103 	if (result == ISC_R_SUCCESS) {
104 		return (ISC_R_SUCCESS);
105 	}
106 
107 	length = isc_buffer_length(ctx->buffer) + data->length + BUFFER_EXTRA;
108 
109 	isc_buffer_allocate(dctx->mctx, &newbuffer, length);
110 
111 	isc_buffer_usedregion(ctx->buffer, &r);
112 	(void)isc_buffer_copyregion(newbuffer, &r);
113 	(void)isc_buffer_copyregion(newbuffer, data);
114 
115 	isc_buffer_free(&ctx->buffer);
116 	ctx->buffer = newbuffer;
117 
118 	return (ISC_R_SUCCESS);
119 }
120 
121 /*%
122  * Sign.
123  */
124 static isc_result_t
gssapi_sign(dst_context_t * dctx,isc_buffer_t * sig)125 gssapi_sign(dst_context_t *dctx, isc_buffer_t *sig) {
126 	dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
127 	isc_region_t message;
128 	gss_buffer_desc gmessage, gsig;
129 	OM_uint32 minor, gret;
130 	gss_ctx_id_t gssctx = dctx->key->keydata.gssctx;
131 	char buf[1024];
132 
133 	/*
134 	 * Convert the data we wish to sign into a structure gssapi can
135 	 * understand.
136 	 */
137 	isc_buffer_usedregion(ctx->buffer, &message);
138 	REGION_TO_GBUFFER(message, gmessage);
139 
140 	/*
141 	 * Generate the signature.
142 	 */
143 	gret = gss_get_mic(&minor, gssctx, GSS_C_QOP_DEFAULT, &gmessage, &gsig);
144 
145 	/*
146 	 * If it did not complete, we log the result and return a generic
147 	 * failure code.
148 	 */
149 	if (gret != GSS_S_COMPLETE) {
150 		gss_log(3, "GSS sign error: %s",
151 			gss_error_tostring(gret, minor, buf, sizeof(buf)));
152 		return (ISC_R_FAILURE);
153 	}
154 
155 	/*
156 	 * If it will not fit in our allocated buffer, return that we need
157 	 * more space.
158 	 */
159 	if (gsig.length > isc_buffer_availablelength(sig)) {
160 		gss_release_buffer(&minor, &gsig);
161 		return (ISC_R_NOSPACE);
162 	}
163 
164 	/*
165 	 * Copy the output into our buffer space, and release the gssapi
166 	 * allocated space.
167 	 */
168 	isc_buffer_putmem(sig, gsig.value, (unsigned int)gsig.length);
169 	if (gsig.length != 0U) {
170 		gss_release_buffer(&minor, &gsig);
171 	}
172 
173 	return (ISC_R_SUCCESS);
174 }
175 
176 /*%
177  * Verify.
178  */
179 static isc_result_t
gssapi_verify(dst_context_t * dctx,const isc_region_t * sig)180 gssapi_verify(dst_context_t *dctx, const isc_region_t *sig) {
181 	dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
182 	isc_region_t message;
183 	gss_buffer_desc gmessage, gsig;
184 	OM_uint32 minor, gret;
185 	gss_ctx_id_t gssctx = dctx->key->keydata.gssctx;
186 	char err[1024];
187 
188 	/*
189 	 * Convert the data we wish to sign into a structure gssapi can
190 	 * understand.
191 	 */
192 	isc_buffer_usedregion(ctx->buffer, &message);
193 	REGION_TO_GBUFFER(message, gmessage);
194 	REGION_TO_GBUFFER(*sig, gsig);
195 
196 	/*
197 	 * Verify the data.
198 	 */
199 	gret = gss_verify_mic(&minor, gssctx, &gmessage, &gsig, NULL);
200 
201 	/*
202 	 * Convert return codes into something useful to us.
203 	 */
204 	if (gret != GSS_S_COMPLETE) {
205 		gss_log(3, "GSS verify error: %s",
206 			gss_error_tostring(gret, minor, err, sizeof(err)));
207 		if (gret == GSS_S_DEFECTIVE_TOKEN || gret == GSS_S_BAD_SIG ||
208 		    gret == GSS_S_DUPLICATE_TOKEN || gret == GSS_S_OLD_TOKEN ||
209 		    gret == GSS_S_UNSEQ_TOKEN || gret == GSS_S_GAP_TOKEN ||
210 		    gret == GSS_S_CONTEXT_EXPIRED || gret == GSS_S_NO_CONTEXT ||
211 		    gret == GSS_S_FAILURE)
212 		{
213 			return (DST_R_VERIFYFAILURE);
214 		} else {
215 			return (ISC_R_FAILURE);
216 		}
217 	}
218 
219 	return (ISC_R_SUCCESS);
220 }
221 
222 static bool
gssapi_compare(const dst_key_t * key1,const dst_key_t * key2)223 gssapi_compare(const dst_key_t *key1, const dst_key_t *key2) {
224 	gss_ctx_id_t gsskey1 = key1->keydata.gssctx;
225 	gss_ctx_id_t gsskey2 = key2->keydata.gssctx;
226 
227 	/* No idea */
228 	return (gsskey1 == gsskey2);
229 }
230 
231 static isc_result_t
gssapi_generate(dst_key_t * key,int unused,void (* callback)(int))232 gssapi_generate(dst_key_t *key, int unused, void (*callback)(int)) {
233 	UNUSED(key);
234 	UNUSED(unused);
235 	UNUSED(callback);
236 
237 	/* No idea */
238 	return (ISC_R_FAILURE);
239 }
240 
241 static bool
gssapi_isprivate(const dst_key_t * key)242 gssapi_isprivate(const dst_key_t *key) {
243 	UNUSED(key);
244 	return (true);
245 }
246 
247 static void
gssapi_destroy(dst_key_t * key)248 gssapi_destroy(dst_key_t *key) {
249 	REQUIRE(key != NULL);
250 	dst_gssapi_deletectx(key->mctx, &key->keydata.gssctx);
251 	key->keydata.gssctx = NULL;
252 }
253 
254 static isc_result_t
gssapi_restore(dst_key_t * key,const char * keystr)255 gssapi_restore(dst_key_t *key, const char *keystr) {
256 	OM_uint32 major, minor;
257 	unsigned int len;
258 	isc_buffer_t *b = NULL;
259 	isc_region_t r;
260 	gss_buffer_desc gssbuffer;
261 	isc_result_t result;
262 
263 	len = strlen(keystr);
264 	if ((len % 4) != 0U) {
265 		return (ISC_R_BADBASE64);
266 	}
267 
268 	len = (len / 4) * 3;
269 
270 	isc_buffer_allocate(key->mctx, &b, len);
271 
272 	result = isc_base64_decodestring(keystr, b);
273 	if (result != ISC_R_SUCCESS) {
274 		isc_buffer_free(&b);
275 		return (result);
276 	}
277 
278 	isc_buffer_remainingregion(b, &r);
279 	REGION_TO_GBUFFER(r, gssbuffer);
280 	major = gss_import_sec_context(&minor, &gssbuffer,
281 				       (gss_ctx_id_t *)&key->keydata.gssctx);
282 	if (major != GSS_S_COMPLETE) {
283 		isc_buffer_free(&b);
284 		return (ISC_R_FAILURE);
285 	}
286 
287 	isc_buffer_free(&b);
288 	return (ISC_R_SUCCESS);
289 }
290 
291 static isc_result_t
gssapi_dump(dst_key_t * key,isc_mem_t * mctx,char ** buffer,int * length)292 gssapi_dump(dst_key_t *key, isc_mem_t *mctx, char **buffer, int *length) {
293 	OM_uint32 major, minor;
294 	gss_buffer_desc gssbuffer;
295 	size_t len;
296 	char *buf;
297 	isc_buffer_t b;
298 	isc_region_t r;
299 	isc_result_t result;
300 
301 	major = gss_export_sec_context(
302 		&minor, (gss_ctx_id_t *)&key->keydata.gssctx, &gssbuffer);
303 	if (major != GSS_S_COMPLETE) {
304 		fprintf(stderr, "gss_export_sec_context -> %u, %u\n", major,
305 			minor);
306 		return (ISC_R_FAILURE);
307 	}
308 	if (gssbuffer.length == 0U) {
309 		return (ISC_R_FAILURE);
310 	}
311 	len = ((gssbuffer.length + 2) / 3) * 4;
312 	buf = isc_mem_get(mctx, len);
313 	isc_buffer_init(&b, buf, (unsigned int)len);
314 	GBUFFER_TO_REGION(gssbuffer, r);
315 	result = isc_base64_totext(&r, 0, "", &b);
316 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
317 	gss_release_buffer(&minor, &gssbuffer);
318 	*buffer = buf;
319 	*length = (int)len;
320 	return (ISC_R_SUCCESS);
321 }
322 
323 static dst_func_t gssapi_functions = {
324 	gssapi_create_signverify_ctx,
325 	NULL, /*%< createctx2 */
326 	gssapi_destroy_signverify_ctx,
327 	gssapi_adddata,
328 	gssapi_sign,
329 	gssapi_verify,
330 	NULL, /*%< verify2 */
331 	NULL, /*%< computesecret */
332 	gssapi_compare,
333 	NULL, /*%< paramcompare */
334 	gssapi_generate,
335 	gssapi_isprivate,
336 	gssapi_destroy,
337 	NULL, /*%< todns */
338 	NULL, /*%< fromdns */
339 	NULL, /*%< tofile */
340 	NULL, /*%< parse */
341 	NULL, /*%< cleanup */
342 	NULL, /*%< fromlabel */
343 	gssapi_dump,
344 	gssapi_restore,
345 };
346 
347 isc_result_t
dst__gssapi_init(dst_func_t ** funcp)348 dst__gssapi_init(dst_func_t **funcp) {
349 	REQUIRE(funcp != NULL);
350 	if (*funcp == NULL) {
351 		*funcp = &gssapi_functions;
352 	}
353 	return (ISC_R_SUCCESS);
354 }
355 
356 #else  /* ifdef GSSAPI */
357 int gssapi_link_unneeded = 1;
358 #endif /* ifdef GSSAPI */
359 
360 /*! \file */
361