4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
25 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
30 #pragma ident "%Z%%M% %I% %E% SMI"
32 #include "dh_gssapi.h"
36 #include <sys/types.h>
37 #include <sys/param.h>
42 get_der_length(unsigned char **, unsigned int, unsigned int *);
45 der_length_size(unsigned int);
48 put_der_length(unsigned int, unsigned char **, unsigned int);
50 /* Diffie-Hellman ONC RPC netname name type */
51 static gss_OID_desc __DH_GSS_C_NT_NETNAME_desc
=
52 { 9, "\053\006\004\001\052\002\032\001\001" };
54 const gss_OID_desc
* const __DH_GSS_C_NT_NETNAME
= &__DH_GSS_C_NT_NETNAME_desc
;
56 #define OID_MAX_NAME_ENTRIES 32
59 * __dh_gss_compare_name: Diffie-Hellman machanism support for
60 * gss_compare_name. Given two gss_name_ts that are presumed to
61 * be rpc netnames set the *equal parameter to true if they are
62 * the same, else set it to false.
66 __dh_gss_compare_name(void *ctx
, /* Per mechanism context (not used) */
67 OM_uint32
*minor
, /* Mechanism status */
68 gss_name_t name1
, /* First name to compare */
69 gss_name_t name2
, /* Second name to compare */
70 int *equal
/* The result */)
74 if (minor
== 0 || equal
== 0)
75 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
79 if (name1
== 0 || name2
== 0) {
80 *minor
= DH_BADARG_FAILURE
;
81 return (GSS_S_BAD_NAME
| GSS_S_CALL_INACCESSIBLE_READ
);
84 *equal
= (strcmp((char *)name1
, (char *)name2
) == 0);
86 return (GSS_S_COMPLETE
);
90 * __dh_gss_display_name: Supports gss_display_name for Diffie-Hellman
91 * mechanism. This takes a gss internal name and converts it to
92 * a counted string suitable for display.
95 __dh_gss_display_name(void * ctx
, /* Per mechanism context (not used) */
96 OM_uint32
* minor
, /* Mechanism status */
97 gss_name_t name
, /* Diffie-Hellman internal name */
98 gss_buffer_t output
, /* Were the printable name goes */
99 gss_OID
*name_type
/* Name type of the internal name */)
101 _NOTE(ARGUNUSED(ctx
))
103 if (minor
== 0 || output
== 0)
104 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
107 return (GSS_S_CALL_INACCESSIBLE_READ
| GSS_S_BAD_NAME
);
112 output
->value
= (void *)strdup((char *)name
);
113 if (output
->value
== NULL
) {
114 *minor
= DH_NOMEM_FAILURE
;
115 return (GSS_S_FAILURE
);
117 output
->length
= strlen((char *)name
) + 1;
120 * Note: we no longer copy the name type OID. The current draft of
121 * the standard specifies:
123 * "The returned gss_OID will be a pointer into static stoarge
124 * and should be treated as read-only by the caller (in particular,
125 * it does not need to be freed)."
128 * if ((*minor = __OID_copy(name_type, __DH_GSS_C_NT_NETNAME))
130 * free(output->value);
131 * output->value = NULL;
132 * return (GSS_S_FAILURE);
138 *name_type
= (gss_OID
) __DH_GSS_C_NT_NETNAME
;
140 return (GSS_S_COMPLETE
);
144 * Routine that takes a netname as a character string and assigns it
145 * to a an gss_name_t pointed to by output.
148 do_netname_nametype(OM_uint32
*minor
, char *input
, gss_name_t
*output
)
150 if (__dh_validate_principal(input
) != DH_SUCCESS
)
151 return (GSS_S_BAD_NAME
);
154 *output
= (gss_name_t
)strdup((char *)input
);
156 if (*output
== NULL
) {
157 *minor
= DH_NOMEM_FAILURE
;
158 return (GSS_S_FAILURE
);
161 return (GSS_S_COMPLETE
);
165 * do_uid_nametype converts a uid to a gss_name_t pointed to by output
168 do_uid_nametype(OM_uint32
*minor
, uid_t uid
, gss_name_t
*output
)
170 char netname
[MAXNETNAMELEN
+1];
172 if (!user2netname(netname
, uid
, NULL
)) {
173 *minor
= DH_NETNAME_FAILURE
;
174 return (GSS_S_FAILURE
);
176 return (do_netname_nametype(minor
, netname
, output
));
180 * do_username_nametype converts a username to a gss_name_t pointed to by
183 * A username will be represented by the following:
184 * name[/node][@security-domain]
186 * Then optional security-domain will represent secure rpc domain if
187 * present. If not present the local domain will be used. name is the
188 * user name as found in the unix password file. If name is root and
189 * node is present, then node will represent the host. If the host is
190 * a qualified name we assume that it is a DNS name and will only return
191 * the first commponnet since we want host name that are relative to
192 * the security domain (secure rpc domain).
196 do_username_nametype(OM_uint32
*minor
, char *uname
, gss_name_t
*output
)
198 char netname
[MAXNETNAMELEN
+1];
199 char *user
, *node
, *domain
;
201 struct passwd
*result
;
204 /* Set outputs to sane values */
209 /* See if we have a name */
211 *minor
= DH_NO_SUCH_USER
;
212 return (GSS_S_FAILURE
);
215 /* copy the name so that we can do surgery on it */
216 user
= strdup(uname
);
218 *minor
= DH_NOMEM_FAILURE
;
219 return (GSS_S_FAILURE
);
223 /* Look for optional node part */
224 node
= strchr(user
, '/');
227 * user is now just the user portion and node
228 * points to the start of the node part.
232 /* Now see if there is a domain */
233 domain
= strchr(node
, '@');
236 /* Check for a domain */
237 domain
= strchr(user
, '@');
239 /* Set domain to the beginning of the domain part if pressent */
244 * See if the node part is important. If the user is root get
245 * the host from the node. If node is not present we assume
246 * we're the local host.
248 if (strcmp(user
, "root") == 0) {
252 * We only want the host part of a qualfied host name. We
253 * assume the domain part of a hostname is a DNS domain,
254 * not an rpc domain. The rpc domain can be specified
255 * in the optional security domain part.
258 dot
= strchr(node
, '.');
263 * If node is null, assume local host. If domain is
264 * null assume local domain. See host2netname(3N)
266 if (!host2netname(netname
, node
, domain
)) {
267 *minor
= DH_NETNAME_FAILURE
;
269 return (GSS_S_FAILURE
);
272 return (do_netname_nametype(minor
, netname
, output
));
276 * We use getpwnam_r to convert the name to uid. Note it is
277 * important to use getpwnam_r to preserve MT safty.
279 getpwnam_r(user
, &pwd
, buff
, sizeof (buff
), &result
);
281 *minor
= DH_NO_SUCH_USER
;
283 return (GSS_S_FAILURE
);
286 /* If domain is null assume local domain. See user2netname(3N) */
287 if (!user2netname(netname
, pwd
.pw_uid
, domain
)) {
288 *minor
= DH_NETNAME_FAILURE
;
290 return (GSS_S_FAILURE
);
293 return (do_netname_nametype(minor
, netname
, output
));
297 * do_hostbase_nametype convert a hostbase service name of the form
300 * For Diffie-Hellman we assume that the service is running with the
301 * credtials of the machine, i.e., as root.
304 do_hostbase_nametype(OM_uint32
*minor
, char *input
, gss_name_t
*output
)
306 /* Get the nostname */
307 char *host
= strchr(input
, '@');
308 char netname
[MAXNETNAMELEN
+1];
311 /* If no host return bad name */
313 return (GSS_S_BAD_NAME
);
315 /* Advance pass the "@" sign */
318 /* Convert the hostname to its netname */
319 if (!host2netname(netname
, host
, NULL
)) {
320 *minor
= DH_NETNAME_FAILURE
;
321 return (GSS_S_FAILURE
);
324 /* Internalize the netname to output */
325 return (do_netname_nametype(minor
, netname
, output
));
329 * do_exported_netname: Convert an exported Diffie-Hellman name
330 * to a Diffie-Hellman internal name.
333 do_exported_netname(dh_context_t ctx
, /* Diffie-Hellman mech context */
334 OM_uint32
*minor
, /* Mech status */
335 gss_buffer_t input
, /* The export name to convert */
336 gss_name_t
*output
/* The converted internal name */)
338 /* All export names must start with this */
339 const char tokid
[] = "\x04\x01";
340 const int tokid_len
= 2;
341 const int OIDlen_len
= 2;
342 const int namelen_len
= 4;
343 unsigned char *p
= (unsigned char *)input
->value
;
344 OM_uint32 len
= input
->length
;
346 OM_uint32 oidlen
; /* includes object tag len & DER len bytes */
351 *minor
= DH_BADARG_FAILURE
;
353 /* The len must be at least this big */
354 if (len
< tokid_len
+ OIDlen_len
+ namelen_len
)
355 return (GSS_S_DEFECTIVE_TOKEN
);
357 /* Export names must start with the token id of 0x04 0x01 */
358 if (memcmp(p
, tokid
, tokid_len
) != 0)
359 return (GSS_S_DEFECTIVE_TOKEN
);
362 /* Decode the Mechanism oid */
363 oidlen
= (*p
++ << 8) & 0xff00;
364 oidlen
|= *p
++ & 0xff;
366 /* Check that we actually have the mechanism oid elements */
367 if (len
< tokid_len
+ OIDlen_len
+ oidlen
+ namelen_len
)
368 return (GSS_S_DEFECTIVE_TOKEN
);
370 /* Compare that the input is for this mechanism */
372 return (GSS_S_DEFECTIVE_TOKEN
);
373 currlen
= len
- (tokid_len
+ OIDlen_len
+ oidlen
+ namelen_len
);
374 if ((mechoidlen
= get_der_length(&p
, currlen
, &bytes
)) < 0)
375 return (GSS_S_DEFECTIVE_TOKEN
);
376 if (mechoidlen
!= ctx
->mech
->length
)
377 return (GSS_S_DEFECTIVE_TOKEN
);
378 if (memcmp(p
, ctx
->mech
->elements
, mechoidlen
) != 0)
379 return (GSS_S_DEFECTIVE_TOKEN
);
382 /* Grab the length of the mechanism specific name per RFC 2078 */
383 namelen
= (*p
++ << 24) & 0xff000000;
384 namelen
|= (*p
++ << 16) & 0xff0000;
385 namelen
|= (*p
++ << 8) & 0xff00;
386 namelen
|= *p
++ & 0xff;
388 /* This should alway be false */
389 if (len
< tokid_len
+ OIDlen_len
+ oidlen
+ namelen_len
+ namelen
)
390 return (GSS_S_DEFECTIVE_TOKEN
);
392 /* Make sure the bytes for the netname oid length are available */
393 if (namelen
< OIDlen_len
)
394 return (GSS_S_DEFECTIVE_TOKEN
);
396 /* Get the netname oid length */
397 oidlen
= (*p
++ << 8) & 0xff00;
398 oidlen
= *p
++ & 0xff;
400 /* See if we have the elements of the netname oid */
401 if (namelen
< OIDlen_len
+ oidlen
)
402 return (GSS_S_DEFECTIVE_TOKEN
);
404 /* Check that the oid is really a netname */
405 if (oidlen
!= __DH_GSS_C_NT_NETNAME
->length
)
406 return (GSS_S_DEFECTIVE_TOKEN
);
407 if (memcmp(p
, __DH_GSS_C_NT_NETNAME
->elements
,
408 __DH_GSS_C_NT_NETNAME
->length
) != 0)
409 return (GSS_S_DEFECTIVE_TOKEN
);
411 /* p now points to the netname wich is null terminated */
415 * How the netname is encoded in an export name type for
416 * this mechanism. See _dh_gss_export_name below.
419 if (namelen
!= OIDlen_len
+ oidlen
+ strlen((char *)p
) + 1)
420 return (GSS_S_DEFECTIVE_TOKEN
);
422 /* Grab the netname */
423 *output
= (gss_name_t
)strdup((char *)p
);
426 return (GSS_S_COMPLETE
);
429 *minor
= DH_NOMEM_FAILURE
;
430 return (GSS_S_FAILURE
);
434 * __dh_gss_import_name: Diffie-Hellman entry point for gss_import_name.
435 * Given an input name of a specified name type, convert this to a
436 * Diffie-Hellman internal name (netname).
438 * The idea here is simply compare the name_type supplied with each
439 * name type that we know how to deal with. If we have a match we call
440 * the appropriate support routine form above. If we done't have a match
441 * we return GSS_S_BAD_NAMETYPE
444 __dh_gss_import_name(void *ctx
, /* Per mechanism context */
445 OM_uint32
*minor
, /* Mechanism status */
446 gss_buffer_t input
, /* The name to convert */
447 gss_OID name_type
, /* of this name_type */
448 gss_name_t
*output
/* The converted name */)
453 if (minor
== NULL
|| output
== NULL
)
454 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
456 if (input
== NULL
|| input
->value
== NULL
)
457 return (GSS_S_BAD_NAME
| GSS_S_CALL_INACCESSIBLE_READ
);
458 if (name_type
== GSS_C_NO_OID
)
459 return (GSS_S_BAD_NAMETYPE
);
463 *output
= GSS_C_NO_NAME
;
465 /* UID in machine format */
466 if (__OID_equal(name_type
, GSS_C_NT_MACHINE_UID_NAME
)) {
468 if (input
->length
!= sizeof (uid_t
))
469 return (GSS_S_BAD_NAME
);
470 uid
= *(uid_t
*)input
->value
;
471 /* Should we assume that the id is network byte order ??? */
472 /* uid = htonl(uid); No, this should be the local orfering */
473 return (do_uid_nametype(minor
, uid
, output
));
475 /* Name that was exported with __dh_gss_export_name */
476 } else if (__OID_equal(name_type
, GSS_C_NT_EXPORT_NAME
)) {
477 stat
= do_exported_netname((dh_context_t
)ctx
, minor
,
482 /* Null ternamte name so we can manipulate as a c-style string */
483 name
= malloc(input
->length
+1);
485 *minor
= DH_NOMEM_FAILURE
;
486 return (GSS_S_FAILURE
);
488 memcpy(name
, input
->value
, input
->length
);
489 name
[input
->length
] = '\0';
492 /* Diffie-Hellman (ONC RPC netname) */
493 if (__OID_equal(name_type
, __DH_GSS_C_NT_NETNAME
)) {
494 stat
= do_netname_nametype(minor
, name
, output
);
497 /* Host based service name (service@hostname) */
498 } else if (__OID_equal(name_type
, GSS_C_NT_HOSTBASED_SERVICE
)) {
499 stat
= do_hostbase_nametype(minor
, name
, output
);
502 /* Thus local OS user name */
503 } else if (__OID_equal(name_type
, GSS_C_NT_USER_NAME
)) {
504 stat
= do_username_nametype(minor
, name
, output
);
507 /* The os user id writen as a string */
508 } else if (__OID_equal(name_type
, GSS_C_NT_STRING_UID_NAME
)) {
510 /* Convert the name to a uid */
511 uid_t uid
= (uid_t
)strtol(name
, &p
, 0);
514 return (GSS_S_BAD_NAME
);
515 return (do_uid_nametype(minor
, uid
, output
));
519 return (GSS_S_BAD_NAMETYPE
);
524 * __dh_gss_release_name: DH entry point for gss_release_name.
525 * Release an internal DH name.
528 __dh_gss_release_name(void *ctx
, OM_uint32
*minor
, gss_name_t
*name
)
530 _NOTE(ARGUNUSED(ctx
))
532 if (minor
== 0 || name
== 0)
533 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
538 *name
= GSS_C_NO_NAME
;
540 return (GSS_S_COMPLETE
);
543 /* Lock for initializing oid_name_tab */
544 static mutex_t name_tab_lock
= DEFAULTMUTEX
;
546 /* Table of name types that this mechanism understands */
547 static const gss_OID_desc
* oid_name_tab
[OID_MAX_NAME_ENTRIES
];
550 * __dh_gss_inquire_names_for_mech: DH entry point for
551 * gss_inquire_names_for_mech.
553 * Return a set of OID name types that a mechanism can understand
556 __dh_gss_inquire_names_for_mech(void *ctx
, OM_uint32
*minor
,
557 gss_OID mech
, gss_OID_set
*names
)
559 _NOTE(ARGUNUSED(ctx
,mech
))
561 /* See if we need to initialize the table */
562 if (oid_name_tab
[0] == 0) {
563 mutex_lock(&name_tab_lock
);
564 /* If nobody sneaked in, initialize the table */
565 if (oid_name_tab
[0] == 0) {
566 oid_name_tab
[0] = __DH_GSS_C_NT_NETNAME
;
567 oid_name_tab
[1] = GSS_C_NT_HOSTBASED_SERVICE
;
568 oid_name_tab
[2] = GSS_C_NT_USER_NAME
;
569 oid_name_tab
[3] = GSS_C_NT_MACHINE_UID_NAME
;
570 oid_name_tab
[4] = GSS_C_NT_STRING_UID_NAME
;
571 oid_name_tab
[5] = GSS_C_NT_EXPORT_NAME
;
572 /* oid_name_tab[6] = GSS_C_NT_ANONYMOUS_NAME; */
574 mutex_unlock(&name_tab_lock
);
577 /* Return the set of OIDS from the table */
578 if ((*minor
= __OID_copy_set_from_array(names
,
579 oid_name_tab
, 6)) != DH_SUCCESS
)
580 return (GSS_S_FAILURE
);
582 return (GSS_S_COMPLETE
);
587 * Private libgss entry point to convert a principal name to uid.
590 __dh_pname_to_uid(void *ctx
, /* DH mech context (not used) */
591 OM_uint32
*minor
, /* Mech status */
592 const gss_name_t pname
, /* principal */
593 uid_t
*uid
/* where to put the uid */)
595 _NOTE(ARGUNUSED(ctx
))
600 /* Convert the principal name to a netname */
601 char *netname
= (char *)pname
;
602 char host_netname
[MAXNETNAMELEN
+1];
605 return (GSS_S_BAD_NAME
| GSS_S_CALL_INACCESSIBLE_READ
);
606 if (minor
== 0 || uid
== 0)
607 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
612 /* First try to convert as a user */
613 if (netname2user(netname
, uid
, &gid
, &glen
, glist
))
614 return (GSS_S_COMPLETE
);
615 /* Get this hosts netname */
616 else if (host2netname(host_netname
, NULL
, NULL
)) {
618 * If the netname is this host's netname then we're root
621 if (strncmp(netname
, host_netname
, MAXNETNAMELEN
) == 0)
623 return (GSS_S_COMPLETE
);
626 /* We could not get a netname */
627 *minor
= DH_NETNAME_FAILURE
;
628 return (GSS_S_FAILURE
);
632 * __dh_gss_export_name: Diffie-Hellman support for gss_export_name.
633 * Given a Diffie-Hellman internal name return the GSS exported format.
636 __dh_gss_export_name(void *ctx
, /* Per mechanism context */
637 OM_uint32
*minor
, /* Mechanism status */
638 const gss_name_t input_name
, /* The name to export */
639 gss_buffer_t exported_name
/* Exported name goes here */)
641 /* input_name is dh principal name */
642 dh_principal pname
= (dh_principal
)input_name
;
643 dh_context_t dc
= (dh_context_t
)ctx
;
644 /* Magic for exported blobs */
645 const char tokid
[] = "\x04\x01";
646 const int tokid_len
= 2;
647 const int OIDlen_len
= 2; /* Why did they do this? */
648 const int namelen_len
= 4;
649 const int mechoid_tag_len
= 1;
654 OM_uint32 oid_der_len
= 0;
656 if (minor
== 0 || exported_name
== GSS_C_NO_BUFFER
)
657 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
658 if (input_name
== GSS_C_NO_NAME
)
659 return (GSS_S_CALL_INACCESSIBLE_READ
);
661 /* Set sane outputs */
663 exported_name
->length
= 0;
664 exported_name
->value
= NULL
;
666 /* Determine the length of the name */
667 namelen
= OIDlen_len
+ __DH_GSS_C_NT_NETNAME
->length
669 oid_der_len
= der_length_size(dc
->mech
->length
);
670 /* Find the total length */
671 len
= tokid_len
+ OIDlen_len
+ mechoid_tag_len
+ oid_der_len
672 + dc
->mech
->length
+ namelen_len
+ namelen
;
674 /* Allocate the blob */
675 p
= New(unsigned char, len
);
677 *minor
= DH_NOMEM_FAILURE
;
678 return (GSS_S_FAILURE
);
680 /* Set the blob to the exported name */
681 exported_name
->length
= len
;
682 exported_name
->value
= p
;
684 /* Start with some magic */
685 memcpy(p
, tokid
, tokid_len
);
689 * The spec only allows two bytes for the oid length.
690 * We are assuming here that the correct encodeing is MSB first as
691 * was done in libgss.
694 *p
++ = ((mechoid_tag_len
+ oid_der_len
+ dc
->mech
->length
)
696 *p
++ = ((mechoid_tag_len
+ oid_der_len
+ dc
->mech
->length
)
699 /* Now the mechanism OID DER Encoding */
700 *p
++ = 0x06; /* Universal Tag for OID */
701 currlen
= len
- tokid_len
- OIDlen_len
- mechoid_tag_len
;
702 if (!put_der_length(dc
->mech
->length
, &p
, currlen
) == 0) {
703 return (GSS_S_FAILURE
);
706 /* Now the mechanism OID elements */
707 memcpy(p
, dc
->mech
->elements
, dc
->mech
->length
);
708 p
+= dc
->mech
->length
;
710 /* The name length most MSB first */
711 *p
++ = (namelen
& 0xff000000) >> 24;
712 *p
++ = (namelen
& 0x00ff0000) >> 16;
713 *p
++ = (namelen
& 0x0000ff00) >> 8;
714 *p
++ = (namelen
& 0x000000ff);
717 * We'll now encode the netname oid. Again we'll just use 2 bytes.
718 * This is the same encoding that the libgss implementor uses, so
719 * we'll just follow along.
722 *p
++ = (__DH_GSS_C_NT_NETNAME
->length
& 0xff00) >> 8;
723 *p
++ = (__DH_GSS_C_NT_NETNAME
->length
&0x00ff);
725 /* The netname oid values */
726 memcpy(p
, __DH_GSS_C_NT_NETNAME
->elements
,
727 __DH_GSS_C_NT_NETNAME
->length
);
729 p
+= __DH_GSS_C_NT_NETNAME
->length
;
731 /* Now we copy the netname including the null byte to be safe */
732 memcpy(p
, pname
, strlen(pname
) + 1);
734 return (GSS_S_COMPLETE
);
738 * Support routine for __dh_internal_release_oid. Return True if
739 * the supplied OID points to the reference OID or if the elements
740 * of the reference OID are the same as the supplied OID. In the
741 * latter case, just free the OID container and set the pointer to it
742 * to GSS_C_NO_OID. Otherwise return false
745 release_oid(const gss_OID_desc
* const ref
, gss_OID
*oid
)
753 * If some on create a shallow copy free, the structure point to
754 * id and set the pointer to it to GSS_C_NO_OID
756 if (id
->elements
== ref
->elements
) {
766 * __dh_gss_internal_release_oid: DH support for the gss_internal_relaese_oid
767 * entry. Check that the refence to an oid is one of our mechanisms static
768 * OIDS. If it is return true indicating to libgss that we have handled the
769 * release of that OID. Otherwise we return false and let libgss deal with it.
771 * The only OIDS we know are the calling mechanism found in the context
772 * and the shared DH_GSS_C_NT_NETNAME name type
775 __dh_gss_internal_release_oid(void *ctx
, OM_uint32
*minor
, gss_OID
*oid
)
777 dh_context_t dhcxt
= (dh_context_t
)ctx
;
780 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
784 if (oid
== NULL
|| *oid
== NULL
)
785 return (GSS_S_COMPLETE
);
787 if (release_oid(dhcxt
->mech
, oid
))
788 return (GSS_S_COMPLETE
);
790 if (release_oid(__DH_GSS_C_NT_NETNAME
, oid
))
791 return (GSS_S_COMPLETE
);
793 return (GSS_S_FAILURE
);