1/*	$NetBSD: gssapi_link.c,v 1.1 2024/02/18 20:57:31 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
48struct 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 */
56static isc_result_t
57gssapi_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 */
74static void
75gssapi_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 */
94static isc_result_t
95gssapi_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 */
124static isc_result_t
125gssapi_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 */
179static isc_result_t
180gssapi_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
222static bool
223gssapi_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
231static isc_result_t
232gssapi_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
241static bool
242gssapi_isprivate(const dst_key_t *key) {
243	UNUSED(key);
244	return (true);
245}
246
247static void
248gssapi_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
254static isc_result_t
255gssapi_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
291static isc_result_t
292gssapi_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
323static 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
347isc_result_t
348dst__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 */
357int gssapi_link_unneeded = 1;
358#endif /* ifdef GSSAPI */
359
360/*! \file */
361