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