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