/src/krb5/src/lib/gssapi/mechglue/g_glue.c
Line | Count | Source |
1 | | /* #pragma ident "@(#)g_glue.c 1.25 04/02/23 SMI" */ |
2 | | |
3 | | /* |
4 | | * Copyright 1996 by Sun Microsystems, Inc. |
5 | | * |
6 | | * Permission to use, copy, modify, distribute, and sell this software |
7 | | * and its documentation for any purpose is hereby granted without fee, |
8 | | * provided that the above copyright notice appears in all copies and |
9 | | * that both that copyright notice and this permission notice appear in |
10 | | * supporting documentation, and that the name of Sun Microsystems not be used |
11 | | * in advertising or publicity pertaining to distribution of the software |
12 | | * without specific, written prior permission. Sun Microsystems makes no |
13 | | * representations about the suitability of this software for any |
14 | | * purpose. It is provided "as is" without express or implied warranty. |
15 | | * |
16 | | * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
17 | | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
18 | | * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
19 | | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF |
20 | | * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR |
21 | | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
22 | | * PERFORMANCE OF THIS SOFTWARE. |
23 | | */ |
24 | | |
25 | | #include "mglueP.h" |
26 | | #include "k5-der.h" |
27 | | #include <stdio.h> |
28 | | #ifdef HAVE_STDLIB_H |
29 | | #include <stdlib.h> |
30 | | #endif |
31 | | #include <string.h> |
32 | | #include <errno.h> |
33 | | |
34 | | #define MSO_BIT (8*(sizeof (int) - 1)) /* Most significant octet bit */ |
35 | | |
36 | | extern gss_mechanism *gssint_mechs_array; |
37 | | |
38 | | /* |
39 | | * This file contains the support routines for the glue layer. |
40 | | */ |
41 | | |
42 | | /* |
43 | | * The following mechanisms do not always identify themselves |
44 | | * per the GSS-API specification, when interoperating with MS |
45 | | * peers. We include the OIDs here so we do not have to ilnk |
46 | | * with the mechanism. |
47 | | */ |
48 | | static gss_OID_desc gss_ntlm_mechanism_oid_desc = |
49 | | {10, (void *)"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"}; |
50 | | static gss_OID_desc gss_spnego_mechanism_oid_desc = |
51 | | {6, (void *)"\x2b\x06\x01\x05\x05\x02"}; |
52 | | static gss_OID_desc gss_krb5_mechanism_oid_desc = |
53 | | {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"}; |
54 | | |
55 | 5.92k | #define NTLMSSP_SIGNATURE "NTLMSSP" |
56 | | |
57 | | OM_uint32 |
58 | | gssint_get_mech_type(gss_OID OID, gss_buffer_t token) |
59 | 2.09k | { |
60 | 2.09k | struct k5input in; |
61 | 2.09k | size_t tlen; |
62 | | |
63 | | /* Check for interoperability exceptions */ |
64 | 2.09k | if (token->length >= sizeof(NTLMSSP_SIGNATURE) && |
65 | 1.91k | memcmp(token->value, NTLMSSP_SIGNATURE, |
66 | 1.91k | sizeof(NTLMSSP_SIGNATURE)) == 0) { |
67 | 1 | *OID = gss_ntlm_mechanism_oid_desc; |
68 | 2.09k | } else if (token->length != 0 && |
69 | 2.09k | ((char *)token->value)[0] == 0x6E) { |
70 | | /* Could be a raw AP-REQ (check for APPLICATION tag) */ |
71 | 5 | *OID = gss_krb5_mechanism_oid_desc; |
72 | 2.09k | } else if (token->length == 0) { |
73 | 0 | *OID = gss_spnego_mechanism_oid_desc; |
74 | 2.09k | } else { |
75 | 2.09k | k5_input_init(&in, token->value, token->length); |
76 | 2.09k | return (g_get_token_header(&in, OID, &tlen) ? GSS_S_COMPLETE : |
77 | 2.09k | GSS_S_DEFECTIVE_TOKEN); |
78 | 2.09k | } |
79 | | |
80 | 6 | return (GSS_S_COMPLETE); |
81 | 2.09k | } |
82 | | |
83 | | static OM_uint32 |
84 | | import_internal_attributes(OM_uint32 *minor, |
85 | | gss_mechanism dmech, |
86 | | gss_union_name_t sname, |
87 | | gss_name_t dname) |
88 | 0 | { |
89 | 0 | OM_uint32 major, tmpMinor; |
90 | 0 | gss_mechanism smech; |
91 | 0 | gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET; |
92 | 0 | size_t i; |
93 | |
|
94 | 0 | if (sname->mech_name == GSS_C_NO_NAME) |
95 | 0 | return (GSS_S_UNAVAILABLE); |
96 | | |
97 | 0 | smech = gssint_get_mechanism (sname->mech_type); |
98 | 0 | if (smech == NULL) |
99 | 0 | return (GSS_S_BAD_MECH); |
100 | | |
101 | 0 | if (smech->gss_inquire_name == NULL || |
102 | 0 | smech->gss_get_name_attribute == NULL) |
103 | 0 | return (GSS_S_UNAVAILABLE); |
104 | | |
105 | 0 | if (dmech->gss_set_name_attribute == NULL) |
106 | 0 | return (GSS_S_UNAVAILABLE); |
107 | | |
108 | 0 | major = smech->gss_inquire_name(minor, sname->mech_name, |
109 | 0 | NULL, NULL, &attrs); |
110 | 0 | if (GSS_ERROR(major) || attrs == GSS_C_NO_BUFFER_SET) { |
111 | 0 | gss_release_buffer_set(&tmpMinor, &attrs); |
112 | 0 | return (major); |
113 | 0 | } |
114 | | |
115 | 0 | for (i = 0; i < attrs->count; i++) { |
116 | 0 | int more = -1; |
117 | |
|
118 | 0 | while (more != 0) { |
119 | 0 | gss_buffer_desc value, display_value; |
120 | 0 | int authenticated, complete; |
121 | |
|
122 | 0 | major = smech->gss_get_name_attribute(minor, sname->mech_name, |
123 | 0 | &attrs->elements[i], |
124 | 0 | &authenticated, &complete, |
125 | 0 | &value, &display_value, |
126 | 0 | &more); |
127 | 0 | if (GSS_ERROR(major)) |
128 | 0 | continue; |
129 | | |
130 | 0 | if (authenticated) { |
131 | 0 | dmech->gss_set_name_attribute(minor, dname, complete, |
132 | 0 | &attrs->elements[i], &value); |
133 | 0 | } |
134 | |
|
135 | 0 | gss_release_buffer(&tmpMinor, &value); |
136 | 0 | gss_release_buffer(&tmpMinor, &display_value); |
137 | 0 | } |
138 | 0 | } |
139 | |
|
140 | 0 | gss_release_buffer_set(&tmpMinor, &attrs); |
141 | |
|
142 | 0 | return (GSS_S_COMPLETE); |
143 | 0 | } |
144 | | |
145 | | /* |
146 | | * Internal routines to get and release an internal mechanism name |
147 | | */ |
148 | | |
149 | | OM_uint32 |
150 | | gssint_import_internal_name(OM_uint32 *minor_status, gss_OID mech_type, |
151 | | gss_union_name_t union_name, |
152 | | gss_name_t *internal_name) |
153 | 0 | { |
154 | 0 | OM_uint32 status, tmpMinor; |
155 | 0 | gss_mechanism mech; |
156 | 0 | gss_OID public_mech; |
157 | |
|
158 | 0 | mech = gssint_get_mechanism (mech_type); |
159 | 0 | if (mech == NULL) |
160 | 0 | return (GSS_S_BAD_MECH); |
161 | | |
162 | | /* |
163 | | * If we are importing a name for the same mechanism, and the |
164 | | * mechanism implements gss_duplicate_name, then use that. |
165 | | */ |
166 | 0 | if (union_name->mech_type != GSS_C_NO_OID && |
167 | 0 | union_name->mech_name != GSS_C_NO_NAME && |
168 | 0 | g_OID_equal(union_name->mech_type, mech_type) && |
169 | 0 | mech->gss_duplicate_name != NULL) { |
170 | 0 | status = mech->gss_duplicate_name(minor_status, |
171 | 0 | union_name->mech_name, |
172 | 0 | internal_name); |
173 | 0 | if (status != GSS_S_UNAVAILABLE) { |
174 | 0 | if (status != GSS_S_COMPLETE) |
175 | 0 | map_error(minor_status, mech); |
176 | 0 | return (status); |
177 | 0 | } |
178 | 0 | } |
179 | | |
180 | 0 | if (mech->gssspi_import_name_by_mech) { |
181 | 0 | public_mech = gssint_get_public_oid(mech_type); |
182 | 0 | status = mech->gssspi_import_name_by_mech(minor_status, public_mech, |
183 | 0 | union_name->external_name, |
184 | 0 | union_name->name_type, |
185 | 0 | internal_name); |
186 | 0 | } else if (mech->gss_import_name) { |
187 | 0 | status = mech->gss_import_name(minor_status, union_name->external_name, |
188 | 0 | union_name->name_type, internal_name); |
189 | 0 | } else { |
190 | 0 | return (GSS_S_UNAVAILABLE); |
191 | 0 | } |
192 | | |
193 | 0 | if (status == GSS_S_COMPLETE) { |
194 | | /* Attempt to round-trip attributes */ |
195 | 0 | (void) import_internal_attributes(&tmpMinor, mech, |
196 | 0 | union_name, *internal_name); |
197 | 0 | } else { |
198 | 0 | map_error(minor_status, mech); |
199 | 0 | } |
200 | |
|
201 | 0 | return (status); |
202 | 0 | } |
203 | | |
204 | | OM_uint32 |
205 | | gssint_export_internal_name(OM_uint32 *minor_status, const gss_OID mech_type, |
206 | | const gss_name_t internal_name, |
207 | | gss_buffer_t name_buf) |
208 | 0 | { |
209 | 0 | OM_uint32 status; |
210 | 0 | gss_mechanism mech; |
211 | 0 | gss_buffer_desc dispName; |
212 | 0 | gss_OID nameOid; |
213 | 0 | int mech_der_len = 0; |
214 | 0 | struct k5buf buf; |
215 | |
|
216 | 0 | mech = gssint_get_mechanism(mech_type); |
217 | 0 | if (!mech) |
218 | 0 | return (GSS_S_BAD_MECH); |
219 | | |
220 | 0 | if (mech->gss_export_name) { |
221 | 0 | status = mech->gss_export_name(minor_status, |
222 | 0 | internal_name, |
223 | 0 | name_buf); |
224 | 0 | if (status != GSS_S_COMPLETE) |
225 | 0 | map_error(minor_status, mech); |
226 | 0 | return status; |
227 | 0 | } |
228 | | |
229 | | /* |
230 | | * if we are here it is because the mechanism does not provide |
231 | | * a gss_export_name so we will use our implementation. We |
232 | | * do required that the mechanism define a gss_display_name. |
233 | | */ |
234 | 0 | if (!mech->gss_display_name) |
235 | 0 | return (GSS_S_UNAVAILABLE); |
236 | | |
237 | | /* |
238 | | * NOTE: RFC2743 (section 3.2) governs the format of the outer |
239 | | * wrapper of exported names; the mechanisms' specs govern |
240 | | * the format of the inner portion of the exported name |
241 | | * and, for some (e.g., RFC1964, the Kerberos V mech), a |
242 | | * generic default as implemented here will do. |
243 | | * |
244 | | * The outer wrapper of an exported MN is: 2-octet tok Id |
245 | | * (0x0401) + 2-octet network-byte order mech OID length + mech |
246 | | * oid (in DER format, including DER tag and DER length) + |
247 | | * 4-octet network-byte order length of inner portion + inner |
248 | | * portion. |
249 | | * |
250 | | * For the Kerberos V mechanism the inner portion of an exported |
251 | | * MN is the display name string and ignores the name type OID |
252 | | * altogether. And we hope this will be so for any future |
253 | | * mechanisms also, so that factoring name export/import out of |
254 | | * the mech and into libgss pays off. |
255 | | */ |
256 | 0 | if ((status = mech->gss_display_name(minor_status, |
257 | 0 | internal_name, |
258 | 0 | &dispName, |
259 | 0 | &nameOid)) |
260 | 0 | != GSS_S_COMPLETE) { |
261 | 0 | map_error(minor_status, mech); |
262 | 0 | return (status); |
263 | 0 | } |
264 | | |
265 | | /* Allocate space and prepare a buffer. */ |
266 | 0 | mech_der_len = k5_der_value_len(mech_type->length); |
267 | 0 | name_buf->length = 2 + 2 + mech_der_len + 4 + dispName.length; |
268 | 0 | name_buf->value = gssalloc_malloc(name_buf->length); |
269 | 0 | if (name_buf->value == NULL) { |
270 | 0 | name_buf->length = 0; |
271 | 0 | (void) gss_release_buffer(&status, &dispName); |
272 | 0 | return (GSS_S_FAILURE); |
273 | 0 | } |
274 | 0 | k5_buf_init_fixed(&buf, name_buf->value, name_buf->length); |
275 | | |
276 | | /* Assemble the name. */ |
277 | 0 | k5_buf_add_len(&buf, "\x04\x01", 2); |
278 | 0 | k5_buf_add_uint16_be(&buf, mech_der_len); |
279 | 0 | k5_der_add_value(&buf, 0x06, mech_type->elements, mech_type->length); |
280 | 0 | k5_buf_add_uint32_be(&buf, dispName.length); |
281 | 0 | k5_buf_add_len(&buf, dispName.value, dispName.length); |
282 | 0 | assert(buf.len == name_buf->length); |
283 | | |
284 | | /* release the buffer obtained from gss_display_name */ |
285 | 0 | (void) gss_release_buffer(minor_status, &dispName); |
286 | 0 | return (GSS_S_COMPLETE); |
287 | 0 | } /* gssint_export_internal_name */ |
288 | | |
289 | | OM_uint32 |
290 | | gssint_display_internal_name(OM_uint32 *minor_status, gss_OID mech_type, |
291 | | gss_name_t internal_name, |
292 | | gss_buffer_t external_name, gss_OID *name_type) |
293 | 0 | { |
294 | 0 | OM_uint32 status; |
295 | 0 | gss_mechanism mech; |
296 | |
|
297 | 0 | mech = gssint_get_mechanism (mech_type); |
298 | 0 | if (mech) { |
299 | 0 | if (mech->gss_display_name) { |
300 | 0 | status = mech->gss_display_name ( |
301 | 0 | minor_status, |
302 | 0 | internal_name, |
303 | 0 | external_name, |
304 | 0 | name_type); |
305 | 0 | if (status != GSS_S_COMPLETE) |
306 | 0 | map_error(minor_status, mech); |
307 | 0 | } else |
308 | 0 | status = GSS_S_UNAVAILABLE; |
309 | |
|
310 | 0 | return (status); |
311 | 0 | } |
312 | | |
313 | 0 | return (GSS_S_BAD_MECH); |
314 | 0 | } |
315 | | |
316 | | OM_uint32 |
317 | | gssint_release_internal_name(OM_uint32 *minor_status, gss_OID mech_type, |
318 | | gss_name_t *internal_name) |
319 | 0 | { |
320 | 0 | OM_uint32 status; |
321 | 0 | gss_mechanism mech; |
322 | |
|
323 | 0 | mech = gssint_get_mechanism (mech_type); |
324 | 0 | if (mech) { |
325 | 0 | if (mech->gss_release_name) { |
326 | 0 | status = mech->gss_release_name ( |
327 | 0 | minor_status, |
328 | 0 | internal_name); |
329 | 0 | if (status != GSS_S_COMPLETE) |
330 | 0 | map_error(minor_status, mech); |
331 | 0 | } else |
332 | 0 | status = GSS_S_UNAVAILABLE; |
333 | |
|
334 | 0 | return (status); |
335 | 0 | } |
336 | | |
337 | 0 | return (GSS_S_BAD_MECH); |
338 | 0 | } |
339 | | |
340 | | OM_uint32 |
341 | | gssint_delete_internal_sec_context(OM_uint32 *minor_status, gss_OID mech_type, |
342 | | gss_ctx_id_t *internal_ctx, |
343 | | gss_buffer_t output_token) |
344 | 0 | { |
345 | 0 | OM_uint32 status; |
346 | 0 | gss_mechanism mech; |
347 | |
|
348 | 0 | mech = gssint_get_mechanism (mech_type); |
349 | 0 | if (mech) { |
350 | 0 | if (mech->gss_delete_sec_context) |
351 | 0 | status = mech->gss_delete_sec_context (minor_status, |
352 | 0 | internal_ctx, |
353 | 0 | output_token); |
354 | 0 | else |
355 | 0 | status = GSS_S_UNAVAILABLE; |
356 | |
|
357 | 0 | return (status); |
358 | 0 | } |
359 | | |
360 | 0 | return (GSS_S_BAD_MECH); |
361 | 0 | } |
362 | | |
363 | | /* |
364 | | * This function converts an internal gssapi name to a union gssapi |
365 | | * name. Note that internal_name should be considered "consumed" by |
366 | | * this call, whether or not we return an error. |
367 | | */ |
368 | | OM_uint32 |
369 | | gssint_convert_name_to_union_name(OM_uint32 *minor_status, gss_mechanism mech, |
370 | | gss_name_t internal_name, |
371 | | gss_name_t *external_name) |
372 | 0 | { |
373 | 0 | OM_uint32 major_status,tmp; |
374 | 0 | gss_union_name_t union_name; |
375 | |
|
376 | 0 | union_name = (gss_union_name_t) malloc (sizeof(gss_union_name_desc)); |
377 | 0 | if (!union_name) { |
378 | 0 | major_status = GSS_S_FAILURE; |
379 | 0 | *minor_status = ENOMEM; |
380 | 0 | map_errcode(minor_status); |
381 | 0 | goto allocation_failure; |
382 | 0 | } |
383 | 0 | union_name->mech_type = 0; |
384 | 0 | union_name->mech_name = internal_name; |
385 | 0 | union_name->name_type = 0; |
386 | 0 | union_name->external_name = 0; |
387 | |
|
388 | 0 | major_status = generic_gss_copy_oid(minor_status, &mech->mech_type, |
389 | 0 | &union_name->mech_type); |
390 | 0 | if (major_status != GSS_S_COMPLETE) { |
391 | 0 | map_errcode(minor_status); |
392 | 0 | goto allocation_failure; |
393 | 0 | } |
394 | | |
395 | 0 | union_name->external_name = |
396 | 0 | (gss_buffer_t) malloc(sizeof(gss_buffer_desc)); |
397 | 0 | if (!union_name->external_name) { |
398 | 0 | major_status = GSS_S_FAILURE; |
399 | 0 | goto allocation_failure; |
400 | 0 | } |
401 | 0 | union_name->external_name->length = 0; |
402 | 0 | union_name->external_name->value = NULL; |
403 | |
|
404 | 0 | major_status = mech->gss_display_name(minor_status, |
405 | 0 | internal_name, |
406 | 0 | union_name->external_name, |
407 | 0 | &union_name->name_type); |
408 | 0 | if (major_status != GSS_S_COMPLETE) { |
409 | 0 | map_error(minor_status, mech); |
410 | 0 | goto allocation_failure; |
411 | 0 | } |
412 | | |
413 | 0 | union_name->loopback = union_name; |
414 | 0 | *external_name = /*(gss_name_t) CHECK */union_name; |
415 | 0 | return (GSS_S_COMPLETE); |
416 | | |
417 | 0 | allocation_failure: |
418 | 0 | if (union_name) { |
419 | 0 | if (union_name->external_name) { |
420 | 0 | if (union_name->external_name->value) |
421 | 0 | free(union_name->external_name->value); |
422 | 0 | free(union_name->external_name); |
423 | 0 | } |
424 | 0 | if (union_name->name_type) |
425 | 0 | gss_release_oid(&tmp, &union_name->name_type); |
426 | 0 | if (union_name->mech_type) |
427 | 0 | gss_release_oid(&tmp, &union_name->mech_type); |
428 | 0 | free(union_name); |
429 | 0 | } |
430 | | /* |
431 | | * do as the top comment says - since we are now owners of |
432 | | * internal_name, we must clean it up |
433 | | */ |
434 | 0 | if (internal_name) |
435 | 0 | (void) gssint_release_internal_name(&tmp, &mech->mech_type, |
436 | 0 | &internal_name); |
437 | 0 | return (major_status); |
438 | 0 | } |
439 | | |
440 | | /* |
441 | | * Glue routine for returning the mechanism-specific credential from a |
442 | | * external union credential. |
443 | | */ |
444 | | gss_cred_id_t |
445 | | gssint_get_mechanism_cred(gss_union_cred_t union_cred, gss_OID mech_type) |
446 | 42 | { |
447 | 42 | int i; |
448 | | |
449 | 42 | if (union_cred == GSS_C_NO_CREDENTIAL) |
450 | 0 | return GSS_C_NO_CREDENTIAL; |
451 | | |
452 | 42 | for (i=0; i < union_cred->count; i++) { |
453 | 0 | if (g_OID_equal(mech_type, &union_cred->mechs_array[i])) |
454 | 0 | return union_cred->cred_array[i]; |
455 | 0 | } |
456 | 42 | return GSS_C_NO_CREDENTIAL; |
457 | 42 | } |
458 | | |
459 | | /* |
460 | | * Routine to create and copy the gss_buffer_desc structure. |
461 | | * Both space for the structure and the data is allocated. |
462 | | */ |
463 | | OM_uint32 |
464 | | gssint_create_copy_buffer(const gss_buffer_t srcBuf, gss_buffer_t *destBuf, |
465 | | int addNullChar) |
466 | 0 | { |
467 | 0 | gss_buffer_t aBuf; |
468 | 0 | unsigned int len; |
469 | |
|
470 | 0 | if (destBuf == NULL) |
471 | 0 | return (GSS_S_CALL_INACCESSIBLE_WRITE); |
472 | | |
473 | 0 | *destBuf = 0; |
474 | |
|
475 | 0 | aBuf = (gss_buffer_t)malloc(sizeof (gss_buffer_desc)); |
476 | 0 | if (!aBuf) |
477 | 0 | return (GSS_S_FAILURE); |
478 | | |
479 | 0 | if (addNullChar) |
480 | 0 | len = srcBuf->length + 1; |
481 | 0 | else |
482 | 0 | len = srcBuf->length; |
483 | |
|
484 | 0 | if (!(aBuf->value = (void*)gssalloc_malloc(len))) { |
485 | 0 | free(aBuf); |
486 | 0 | return (GSS_S_FAILURE); |
487 | 0 | } |
488 | | |
489 | | |
490 | 0 | (void) memcpy(aBuf->value, srcBuf->value, srcBuf->length); |
491 | 0 | aBuf->length = srcBuf->length; |
492 | 0 | *destBuf = aBuf; |
493 | | |
494 | | /* optionally add a NULL character */ |
495 | 0 | if (addNullChar) |
496 | 0 | ((char *)aBuf->value)[aBuf->length] = '\0'; |
497 | |
|
498 | 0 | return (GSS_S_COMPLETE); |
499 | 0 | } /* ****** gssint_create_copy_buffer ****** */ |
500 | | |
501 | | OM_uint32 |
502 | | gssint_create_union_context(OM_uint32 *minor, gss_const_OID mech_oid, |
503 | | gss_union_ctx_id_t *ctx_out) |
504 | 1.66k | { |
505 | 1.66k | OM_uint32 status; |
506 | 1.66k | gss_union_ctx_id_t ctx; |
507 | | |
508 | 1.66k | *ctx_out = NULL; |
509 | | |
510 | 1.66k | ctx = calloc(1, sizeof(*ctx)); |
511 | 1.66k | if (ctx == NULL) { |
512 | 0 | *minor = ENOMEM; |
513 | 0 | return GSS_S_FAILURE; |
514 | 0 | } |
515 | | |
516 | 1.66k | status = generic_gss_copy_oid(minor, mech_oid, &ctx->mech_type); |
517 | 1.66k | if (status != GSS_S_COMPLETE) { |
518 | 0 | free(ctx); |
519 | 0 | return status; |
520 | 0 | } |
521 | | |
522 | 1.66k | ctx->loopback = ctx; |
523 | 1.66k | ctx->internal_ctx_id = GSS_C_NO_CONTEXT; |
524 | | |
525 | 1.66k | *ctx_out = ctx; |
526 | 1.66k | return GSS_S_COMPLETE; |
527 | 1.66k | } |