Merge branch 'vendor/FILE'
[dragonfly.git] / contrib / bind / lib / dns / gssapictx.c
1 /*
2  * Copyright (C) 2004-2008  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000, 2001  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 /* $Id: gssapictx.c,v 1.8.128.4 2008/04/03 06:08:26 tbox Exp $ */
19
20 #include <config.h>
21
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <isc/buffer.h>
26 #include <isc/dir.h>
27 #include <isc/entropy.h>
28 #include <isc/lex.h>
29 #include <isc/mem.h>
30 #include <isc/once.h>
31 #include <isc/print.h>
32 #include <isc/random.h>
33 #include <isc/string.h>
34 #include <isc/time.h>
35 #include <isc/util.h>
36
37 #include <dns/fixedname.h>
38 #include <dns/name.h>
39 #include <dns/rdata.h>
40 #include <dns/rdataclass.h>
41 #include <dns/result.h>
42 #include <dns/types.h>
43 #include <dns/keyvalues.h>
44 #include <dns/log.h>
45
46 #include <dst/gssapi.h>
47 #include <dst/result.h>
48
49 #include "dst_internal.h"
50
51 /*
52  * If we're using our own SPNEGO implementation (see configure.in),
53  * pull it in now.  Otherwise, we just use whatever GSSAPI supplies.
54  */
55 #if defined(GSSAPI) && defined(USE_ISC_SPNEGO)
56 #include "spnego.h"
57 #define gss_accept_sec_context  gss_accept_sec_context_spnego
58 #define gss_init_sec_context    gss_init_sec_context_spnego
59 #endif
60
61 /*
62  * Solaris8 apparently needs an explicit OID set, and Solaris10 needs
63  * one for anything but Kerberos.  Supplying an explicit OID set
64  * doesn't appear to hurt anything in other implementations, so we
65  * always use one.  If we're not using our own SPNEGO implementation,
66  * we include SPNEGO's OID.
67  */
68 #if defined(GSSAPI)
69
70 static unsigned char krb5_mech_oid_bytes[] = {
71         0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
72 };
73
74 #ifndef USE_ISC_SPNEGO
75 static unsigned char spnego_mech_oid_bytes[] = {
76         0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
77 };
78 #endif
79
80 static gss_OID_desc mech_oid_set_array[] = {
81         { sizeof(krb5_mech_oid_bytes), krb5_mech_oid_bytes },
82 #ifndef USE_ISC_SPNEGO
83         { sizeof(spnego_mech_oid_bytes), spnego_mech_oid_bytes },
84 #endif
85 };
86
87 static gss_OID_set_desc mech_oid_set = {
88         sizeof(mech_oid_set_array) / sizeof(*mech_oid_set_array),
89         mech_oid_set_array
90 };
91
92 #endif
93
94 #define REGION_TO_GBUFFER(r, gb) \
95         do { \
96                 (gb).length = (r).length; \
97                 (gb).value = (r).base; \
98         } while (0)
99
100 #define GBUFFER_TO_REGION(gb, r) \
101         do { \
102                 (r).length = (gb).length; \
103                 (r).base = (gb).value; \
104         } while (0)
105
106
107 #define RETERR(x) do { \
108         result = (x); \
109         if (result != ISC_R_SUCCESS) \
110                 goto out; \
111         } while (0)
112
113 #ifdef GSSAPI
114 static inline void
115 name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer,
116                 gss_buffer_desc *gbuffer)
117 {
118         dns_name_t tname, *namep;
119         isc_region_t r;
120         isc_result_t result;
121
122         if (!dns_name_isabsolute(name))
123                 namep = name;
124         else
125         {
126                 unsigned int labels;
127                 dns_name_init(&tname, NULL);
128                 labels = dns_name_countlabels(name);
129                 dns_name_getlabelsequence(name, 0, labels - 1, &tname);
130                 namep = &tname;
131         }
132
133         result = dns_name_totext(namep, ISC_FALSE, buffer);
134         isc_buffer_putuint8(buffer, 0);
135         isc_buffer_usedregion(buffer, &r);
136         REGION_TO_GBUFFER(r, *gbuffer);
137 }
138
139 static void
140 log_cred(const gss_cred_id_t cred) {
141         OM_uint32 gret, minor, lifetime;
142         gss_name_t gname;
143         gss_buffer_desc gbuffer;
144         gss_cred_usage_t usage;
145         const char *usage_text;
146         char buf[1024];
147
148         gret = gss_inquire_cred(&minor, cred, &gname, &lifetime, &usage, NULL);
149         if (gret != GSS_S_COMPLETE) {
150                 gss_log(3, "failed gss_inquire_cred: %s",
151                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
152                 return;
153         }
154
155         gret = gss_display_name(&minor, gname, &gbuffer, NULL);
156         if (gret != GSS_S_COMPLETE)
157                 gss_log(3, "failed gss_display_name: %s",
158                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
159         else {
160                 switch (usage) {
161                 case GSS_C_BOTH:
162                         usage_text = "GSS_C_BOTH";
163                         break;
164                 case GSS_C_INITIATE:
165                         usage_text = "GSS_C_INITIATE";
166                         break;
167                 case GSS_C_ACCEPT:
168                         usage_text = "GSS_C_ACCEPT";
169                         break;
170                 default:
171                         usage_text = "???";
172                 }
173                 gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value,
174                         usage_text, (unsigned long)lifetime);
175         }
176
177         if (gret == GSS_S_COMPLETE) {
178                 if (gbuffer.length != 0) {
179                         gret = gss_release_buffer(&minor, &gbuffer);
180                         if (gret != GSS_S_COMPLETE)
181                                 gss_log(3, "failed gss_release_buffer: %s",
182                                         gss_error_tostring(gret, minor, buf,
183                                                            sizeof(buf)));
184                 }
185         }
186
187         gret = gss_release_name(&minor, &gname);
188         if (gret != GSS_S_COMPLETE)
189                 gss_log(3, "failed gss_release_name: %s",
190                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
191 }
192 #endif
193
194 isc_result_t
195 dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate,
196                        gss_cred_id_t *cred)
197 {
198 #ifdef GSSAPI
199         isc_buffer_t namebuf;
200         gss_name_t gname;
201         gss_buffer_desc gnamebuf;
202         unsigned char array[DNS_NAME_MAXTEXT + 1];
203         OM_uint32 gret, minor;
204         gss_OID_set mechs;
205         OM_uint32 lifetime;
206         gss_cred_usage_t usage;
207         char buf[1024];
208
209         REQUIRE(cred != NULL && *cred == NULL);
210
211         /*
212          * XXXSRA In theory we could use GSS_C_NT_HOSTBASED_SERVICE
213          * here when we're in the acceptor role, which would let us
214          * default the hostname and use a compiled in default service
215          * name of "DNS", giving one less thing to configure in
216          * named.conf.  Unfortunately, this creates a circular
217          * dependency due to DNS-based realm lookup in at least one
218          * GSSAPI implementation (Heimdal).  Oh well.
219          */
220         if (name != NULL) {
221                 isc_buffer_init(&namebuf, array, sizeof(array));
222                 name_to_gbuffer(name, &namebuf, &gnamebuf);
223                 gret = gss_import_name(&minor, &gnamebuf,
224                                        GSS_C_NO_OID, &gname);
225                 if (gret != GSS_S_COMPLETE) {
226                         gss_log(3, "failed gss_import_name: %s",
227                                 gss_error_tostring(gret, minor, buf,
228                                                    sizeof(buf)));
229                         return (ISC_R_FAILURE);
230                 }
231         } else
232                 gname = NULL;
233
234         /* Get the credentials. */
235         if (gname != NULL)
236                 gss_log(3, "acquiring credentials for %s",
237                         (char *)gnamebuf.value);
238         else {
239                 /* XXXDCL does this even make any sense? */
240                 gss_log(3, "acquiring credentials for ?");
241         }
242
243         if (initiate)
244                 usage = GSS_C_INITIATE;
245         else
246                 usage = GSS_C_ACCEPT;
247
248         gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE,
249                                 &mech_oid_set,
250                                 usage, cred, &mechs, &lifetime);
251
252         if (gret != GSS_S_COMPLETE) {
253                 gss_log(3, "failed to acquire %s credentials for %s: %s",
254                         initiate ? "initiate" : "accept",
255                         (char *)gnamebuf.value,
256                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
257                 return (ISC_R_FAILURE);
258         }
259
260         gss_log(4, "acquired %s credentials for %s",
261                 initiate ? "initiate" : "accept",
262                 (char *)gnamebuf.value);
263
264         log_cred(*cred);
265
266         return (ISC_R_SUCCESS);
267 #else
268         UNUSED(name);
269         UNUSED(initiate);
270         UNUSED(cred);
271
272         return (ISC_R_NOTIMPLEMENTED);
273 #endif
274 }
275
276 isc_boolean_t
277 dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name,
278                                     dns_name_t *realm)
279 {
280 #ifdef GSSAPI
281         char sbuf[DNS_NAME_FORMATSIZE];
282         char nbuf[DNS_NAME_FORMATSIZE];
283         char rbuf[DNS_NAME_FORMATSIZE];
284         char *sname;
285         char *rname;
286
287         /*
288          * It is far, far easier to write the names we are looking at into
289          * a string, and do string operations on them.
290          */
291         dns_name_format(signer, sbuf, sizeof(sbuf));
292         if (name != NULL)
293                 dns_name_format(name, nbuf, sizeof(nbuf));
294         dns_name_format(realm, rbuf, sizeof(rbuf));
295
296         /*
297          * Find the realm portion.  This is the part after the @.  If it
298          * does not exist, we don't have something we like, so we fail our
299          * compare.
300          */
301         rname = strstr(sbuf, "\\@");
302         if (rname == NULL)
303                 return (isc_boolean_false);
304         *rname = '\0';
305         rname += 2;
306
307         /*
308          * Find the host portion of the signer's name.  We do this by
309          * searching for the first / character.  We then check to make
310          * certain the instance name is "host"
311          *
312          * This will work for
313          *    host/example.com@EXAMPLE.COM
314          */
315         sname = strchr(sbuf, '/');
316         if (sname == NULL)
317                 return (isc_boolean_false);
318         *sname = '\0';
319         sname++;
320         if (strcmp(sbuf, "host") != 0)
321                 return (isc_boolean_false);
322
323         /*
324          * Now, we do a simple comparison between the name and the realm.
325          */
326         if (name != NULL) {
327                 if ((strcasecmp(sname, nbuf) == 0)
328                     && (strcmp(rname, rbuf) == 0))
329                         return (isc_boolean_true);
330         } else {
331                 if (strcmp(rname, rbuf) == 0)
332                         return (isc_boolean_true);
333         }
334
335         return (isc_boolean_false);
336 #else
337         UNUSED(signer);
338         UNUSED(name);
339         UNUSED(realm);
340         return (isc_boolean_false);
341 #endif
342 }
343
344 isc_boolean_t
345 dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name,
346                                   dns_name_t *realm)
347 {
348 #ifdef GSSAPI
349         char sbuf[DNS_NAME_FORMATSIZE];
350         char nbuf[DNS_NAME_FORMATSIZE];
351         char rbuf[DNS_NAME_FORMATSIZE];
352         char *sname;
353         char *nname;
354         char *rname;
355
356         /*
357          * It is far, far easier to write the names we are looking at into
358          * a string, and do string operations on them.
359          */
360         dns_name_format(signer, sbuf, sizeof(sbuf));
361         if (name != NULL)
362                 dns_name_format(name, nbuf, sizeof(nbuf));
363         dns_name_format(realm, rbuf, sizeof(rbuf));
364
365         /*
366          * Find the realm portion.  This is the part after the @.  If it
367          * does not exist, we don't have something we like, so we fail our
368          * compare.
369          */
370         rname = strstr(sbuf, "\\@");
371         if (rname == NULL)
372                 return (isc_boolean_false);
373         sname = strstr(sbuf, "\\$");
374         if (sname == NULL)
375                 return (isc_boolean_false);
376
377         /*
378          * Verify that the $ and @ follow one another.
379          */
380         if (rname - sname != 2)
381                 return (isc_boolean_false);
382
383         /*
384          * Find the host portion of the signer's name.  Zero out the $ so
385          * it terminates the signer's name, and skip past the @ for
386          * the realm.
387          *
388          * All service principals in Microsoft format seem to be in
389          *    machinename$@EXAMPLE.COM
390          * format.
391          */
392         *rname = '\0';
393         rname += 2;
394         *sname = '\0';
395         sname = sbuf;
396
397         /*
398          * Find the first . in the target name, and make it the end of
399          * the string.   The rest of the name has to match the realm.
400          */
401         if (name != NULL) {
402                 nname = strchr(nbuf, '.');
403                 if (nname == NULL)
404                         return (isc_boolean_false);
405                 *nname++ = '\0';
406         }
407
408         /*
409          * Now, we do a simple comparison between the name and the realm.
410          */
411         if (name != NULL) {
412                 if ((strcasecmp(sname, nbuf) == 0)
413                     && (strcmp(rname, rbuf) == 0)
414                     && (strcasecmp(nname, rbuf) == 0))
415                         return (isc_boolean_true);
416         } else {
417                 if (strcmp(rname, rbuf) == 0)
418                         return (isc_boolean_true);
419         }
420
421
422         return (isc_boolean_false);
423 #else
424         UNUSED(signer);
425         UNUSED(name);
426         UNUSED(realm);
427         return (isc_boolean_false);
428 #endif
429 }
430
431 isc_result_t
432 dst_gssapi_releasecred(gss_cred_id_t *cred) {
433 #ifdef GSSAPI
434         OM_uint32 gret, minor;
435         char buf[1024];
436
437         REQUIRE(cred != NULL && *cred != NULL);
438
439         gret = gss_release_cred(&minor, cred);
440         if (gret != GSS_S_COMPLETE) {
441                 /* Log the error, but still free the credential's memory */
442                 gss_log(3, "failed releasing credential: %s",
443                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
444         }
445         *cred = NULL;
446
447         return(ISC_R_SUCCESS);
448 #else
449         UNUSED(cred);
450
451         return (ISC_R_NOTIMPLEMENTED);
452 #endif
453 }
454
455 isc_result_t
456 dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
457                    isc_buffer_t *outtoken, gss_ctx_id_t *gssctx)
458 {
459 #ifdef GSSAPI
460         isc_region_t r;
461         isc_buffer_t namebuf;
462         gss_name_t gname;
463         OM_uint32 gret, minor, ret_flags, flags;
464         gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER;
465         isc_result_t result;
466         gss_buffer_desc gnamebuf;
467         unsigned char array[DNS_NAME_MAXTEXT + 1];
468         char buf[1024];
469
470         /* Client must pass us a valid gss_ctx_id_t here */
471         REQUIRE(gssctx != NULL);
472
473         isc_buffer_init(&namebuf, array, sizeof(array));
474         name_to_gbuffer(name, &namebuf, &gnamebuf);
475
476         /* Get the name as a GSS name */
477         gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname);
478         if (gret != GSS_S_COMPLETE) {
479                 result = ISC_R_FAILURE;
480                 goto out;
481         }
482
483         if (intoken != NULL) {
484                 /* Don't call gss_release_buffer for gintoken! */
485                 REGION_TO_GBUFFER(*intoken, gintoken);
486                 gintokenp = &gintoken;
487         } else {
488                 gintokenp = NULL;
489         }
490
491         flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG |
492                 GSS_C_SEQUENCE_FLAG | GSS_C_INTEG_FLAG;
493
494         gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx,
495                                     gname, GSS_SPNEGO_MECHANISM, flags,
496                                     0, NULL, gintokenp,
497                                     NULL, &gouttoken, &ret_flags, NULL);
498
499         if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) {
500                 gss_log(3, "Failure initiating security context");
501                 gss_log(3, "%s", gss_error_tostring(gret, minor,
502                                                     buf, sizeof(buf)));
503                 result = ISC_R_FAILURE;
504                 goto out;
505         }
506
507         /*
508          * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags
509          * MUTUAL and INTEG flags, fail if either not set.
510          */
511
512         /*
513          * RFC 2744 states the a valid output token has a non-zero length.
514          */
515         if (gouttoken.length != 0) {
516                 GBUFFER_TO_REGION(gouttoken, r);
517                 RETERR(isc_buffer_copyregion(outtoken, &r));
518                 (void)gss_release_buffer(&minor, &gouttoken);
519         }
520         (void)gss_release_name(&minor, &gname);
521
522         if (gret == GSS_S_COMPLETE)
523                 result = ISC_R_SUCCESS;
524         else
525                 result = DNS_R_CONTINUE;
526
527  out:
528         return (result);
529 #else
530         UNUSED(name);
531         UNUSED(intoken);
532         UNUSED(outtoken);
533         UNUSED(gssctx);
534
535         return (ISC_R_NOTIMPLEMENTED);
536 #endif
537 }
538
539 isc_result_t
540 dst_gssapi_acceptctx(gss_cred_id_t cred,
541                      isc_region_t *intoken, isc_buffer_t **outtoken,
542                      gss_ctx_id_t *ctxout, dns_name_t *principal,
543                      isc_mem_t *mctx)
544 {
545 #ifdef GSSAPI
546         isc_region_t r;
547         isc_buffer_t namebuf;
548         gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken,
549                         gouttoken = GSS_C_EMPTY_BUFFER;
550         OM_uint32 gret, minor;
551         gss_ctx_id_t context = GSS_C_NO_CONTEXT;
552         gss_name_t gname = NULL;
553         isc_result_t result;
554         char buf[1024];
555
556         REQUIRE(outtoken != NULL && *outtoken == NULL);
557
558         log_cred(cred);
559
560         REGION_TO_GBUFFER(*intoken, gintoken);
561
562         if (*ctxout == NULL)
563                 context = GSS_C_NO_CONTEXT;
564         else
565                 context = *ctxout;
566
567         gret = gss_accept_sec_context(&minor, &context, cred, &gintoken,
568                                       GSS_C_NO_CHANNEL_BINDINGS, &gname,
569                                       NULL, &gouttoken, NULL, NULL, NULL);
570
571         result = ISC_R_FAILURE;
572
573         switch (gret) {
574         case GSS_S_COMPLETE:
575                 result = ISC_R_SUCCESS;
576                 break;
577         case GSS_S_CONTINUE_NEEDED:
578                 result = DNS_R_CONTINUE;
579                 break;
580         case GSS_S_DEFECTIVE_TOKEN:
581         case GSS_S_DEFECTIVE_CREDENTIAL:
582         case GSS_S_BAD_SIG:
583         case GSS_S_DUPLICATE_TOKEN:
584         case GSS_S_OLD_TOKEN:
585         case GSS_S_NO_CRED:
586         case GSS_S_CREDENTIALS_EXPIRED:
587         case GSS_S_BAD_BINDINGS:
588         case GSS_S_NO_CONTEXT:
589         case GSS_S_BAD_MECH:
590         case GSS_S_FAILURE:
591                 result = DNS_R_INVALIDTKEY;
592                 /* fall through */
593         default:
594                 gss_log(3, "failed gss_accept_sec_context: %s",
595                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
596                 return (result);
597         }
598
599         if (gouttoken.length > 0) {
600                 RETERR(isc_buffer_allocate(mctx, outtoken, gouttoken.length));
601                 GBUFFER_TO_REGION(gouttoken, r);
602                 RETERR(isc_buffer_copyregion(*outtoken, &r));
603                 (void)gss_release_buffer(&minor, &gouttoken);
604         }
605
606         if (gret == GSS_S_COMPLETE) {
607                 gret = gss_display_name(&minor, gname, &gnamebuf, NULL);
608                 if (gret != GSS_S_COMPLETE) {
609                         gss_log(3, "failed gss_display_name: %s",
610                                 gss_error_tostring(gret, minor,
611                                                    buf, sizeof(buf)));
612                         RETERR(ISC_R_FAILURE);
613                 }
614
615                 /*
616                  * Compensate for a bug in Solaris8's implementation
617                  * of gss_display_name().  Should be harmless in any
618                  * case, since principal names really should not
619                  * contain null characters.
620                  */
621                 if (gnamebuf.length > 0 &&
622                     ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0')
623                         gnamebuf.length--;
624
625                 gss_log(3, "gss-api source name (accept) is %.*s",
626                         (int)gnamebuf.length, (char *)gnamebuf.value);
627
628                 GBUFFER_TO_REGION(gnamebuf, r);
629                 isc_buffer_init(&namebuf, r.base, r.length);
630                 isc_buffer_add(&namebuf, r.length);
631
632                 RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname,
633                                          ISC_FALSE, NULL));
634
635                 if (gnamebuf.length != 0) {
636                         gret = gss_release_buffer(&minor, &gnamebuf);
637                         if (gret != GSS_S_COMPLETE)
638                                 gss_log(3, "failed gss_release_buffer: %s",
639                                         gss_error_tostring(gret, minor, buf,
640                                                            sizeof(buf)));
641                 }
642         }
643
644         *ctxout = context;
645
646  out:
647         if (gname != NULL) {
648                 gret = gss_release_name(&minor, &gname);
649                 if (gret != GSS_S_COMPLETE)
650                         gss_log(3, "failed gss_release_name: %s",
651                                 gss_error_tostring(gret, minor, buf,
652                                                    sizeof(buf)));
653         }
654
655         return (result);
656 #else
657         UNUSED(cred);
658         UNUSED(intoken);
659         UNUSED(outtoken);
660         UNUSED(ctxout);
661         UNUSED(principal);
662         UNUSED(mctx);
663
664         return (ISC_R_NOTIMPLEMENTED);
665 #endif
666 }
667
668 isc_result_t
669 dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx)
670 {
671 #ifdef GSSAPI
672         OM_uint32 gret, minor;
673         char buf[1024];
674
675         UNUSED(mctx);
676
677         REQUIRE(gssctx != NULL && *gssctx != NULL);
678
679         /* Delete the context from the GSS provider */
680         gret = gss_delete_sec_context(&minor, gssctx, GSS_C_NO_BUFFER);
681         if (gret != GSS_S_COMPLETE) {
682                 /* Log the error, but still free the context's memory */
683                 gss_log(3, "Failure deleting security context %s",
684                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
685         }
686         return(ISC_R_SUCCESS);
687 #else
688         UNUSED(mctx);
689         UNUSED(gssctx);
690         return (ISC_R_NOTIMPLEMENTED);
691 #endif
692 }
693
694 char *
695 gss_error_tostring(isc_uint32_t major, isc_uint32_t minor,
696                    char *buf, size_t buflen) {
697 #ifdef GSSAPI
698         gss_buffer_desc msg_minor = GSS_C_EMPTY_BUFFER,
699                         msg_major = GSS_C_EMPTY_BUFFER;
700         OM_uint32 msg_ctx, minor_stat;
701
702         /* Handle major status */
703         msg_ctx = 0;
704         (void)gss_display_status(&minor_stat, major, GSS_C_GSS_CODE,
705                                  GSS_C_NULL_OID, &msg_ctx, &msg_major);
706
707         /* Handle minor status */
708         msg_ctx = 0;
709         (void)gss_display_status(&minor_stat, minor, GSS_C_MECH_CODE,
710                                  GSS_C_NULL_OID, &msg_ctx, &msg_minor);
711
712         snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.",
713                 (char *)msg_major.value, (char *)msg_minor.value);
714
715         if (msg_major.length != 0)
716                 (void)gss_release_buffer(&minor_stat, &msg_major);
717         if (msg_minor.length != 0)
718                 (void)gss_release_buffer(&minor_stat, &msg_minor);
719         return(buf);
720 #else
721         snprintf(buf, buflen, "GSSAPI error: Major = %u, Minor = %u.",
722                  major, minor);
723
724         return (buf);
725 #endif
726 }
727
728 void
729 gss_log(int level, const char *fmt, ...) {
730         va_list ap;
731
732         va_start(ap, fmt);
733         isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL,
734                        DNS_LOGMODULE_TKEY, ISC_LOG_DEBUG(level), fmt, ap);
735         va_end(ap);
736 }
737
738 /*! \file */