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