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