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