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