pull up r23832 from trunk
[krb5.git] / src / lib / gssapi / spnego / spnego_mech.c
1 /*
2  * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
3  * All rights reserved.
4  *
5  * Export of this software from the United States of America may
6  *   require a specific license from the United States Government.
7  *   It is the responsibility of any person or organization contemplating
8  *   export to obtain such a license before exporting.
9  *
10  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
11  * distribute this software and its documentation for any purpose and
12  * without fee is hereby granted, provided that the above copyright
13  * notice appear in all copies and that both that copyright notice and
14  * this permission notice appear in supporting documentation, and that
15  * the name of M.I.T. not be used in advertising or publicity pertaining
16  * to distribution of the software without specific, written prior
17  * permission.  Furthermore if you modify this software you must label
18  * your software as modified software and not distribute it in such a
19  * fashion that it might be confused with the original M.I.T. software.
20  * M.I.T. makes no representations about the suitability of
21  * this software for any purpose.  It is provided "as is" without express
22  * or implied warranty.
23  *
24  */
25
26 /*
27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  *
30  * A module that implements the spnego security mechanism.
31  * It is used to negotiate the security mechanism between
32  * peers using the GSS-API.
33  *
34  */
35 /*
36  * Copyright (c) 2006-2008, Novell, Inc.
37  * All rights reserved.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions are met:
41  *
42  *   * Redistributions of source code must retain the above copyright notice,
43  *       this list of conditions and the following disclaimer.
44  *   * Redistributions in binary form must reproduce the above copyright
45  *       notice, this list of conditions and the following disclaimer in the
46  *       documentation and/or other materials provided with the distribution.
47  *   * The copyright holder's name is not used to endorse or promote products
48  *       derived from this software without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
51  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
54  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
59  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
60  * POSSIBILITY OF SUCH DAMAGE.
61  */
62 /* #pragma ident        "@(#)spnego_mech.c      1.7     04/09/28 SMI" */
63
64 #include        <sys/param.h>
65 #include        <unistd.h>
66 #include        <assert.h>
67 #include        <stdio.h>
68 #include        <stdlib.h>
69 #include        <string.h>
70 #include        <k5-int.h>
71 #include        <krb5.h>
72 #include        <mglueP.h>
73 #include        "gssapiP_spnego.h"
74 #include        <gssapi_err_generic.h>
75
76
77 #undef g_token_size
78 #undef g_verify_token_header
79 #undef g_make_token_header
80
81 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
82 typedef const gss_OID_desc *gss_OID_const;
83
84 /* der routines defined in libgss */
85 extern unsigned int gssint_der_length_size(OM_uint32);
86 extern int gssint_get_der_length(unsigned char **, OM_uint32, unsigned int*);
87 extern int gssint_put_der_length(OM_uint32, unsigned char **, unsigned int);
88
89
90 /* private routines for spnego_mechanism */
91 static spnego_token_t make_spnego_token(char *);
92 static gss_buffer_desc make_err_msg(char *);
93 static int g_token_size(gss_OID_const, unsigned int);
94 static int g_make_token_header(gss_OID_const, unsigned int,
95                                unsigned char **, unsigned int);
96 static int g_verify_token_header(gss_OID_const, unsigned int *,
97                                  unsigned char **,
98                                  int, unsigned int);
99 static int g_verify_neg_token_init(unsigned char **, unsigned int);
100 static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
101 static gss_buffer_t get_input_token(unsigned char **, unsigned int);
102 static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
103 static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
104 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
105         gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
106 static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
107                                       gss_cred_usage_t, gss_OID_set *);
108 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
109 static void check_spnego_options(spnego_gss_ctx_id_t);
110 static spnego_gss_ctx_id_t create_spnego_ctx(void);
111 static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
112 static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
113 static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);
114 static int put_negResult(unsigned char **, OM_uint32, unsigned int);
115
116 static OM_uint32
117 process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
118             gss_buffer_t *, OM_uint32 *, send_token_flag *);
119 static OM_uint32
120 handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
121            gss_buffer_t *, OM_uint32 *, send_token_flag *);
122
123 static OM_uint32
124 init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
125              gss_OID_set *, send_token_flag *);
126 static OM_uint32
127 init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
128               gss_buffer_t *, gss_buffer_t *,
129               OM_uint32 *, send_token_flag *);
130 static OM_uint32
131 init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
132               gss_buffer_t *, gss_buffer_t *,
133               OM_uint32 *, send_token_flag *);
134 static OM_uint32
135 init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
136                   gss_OID, gss_buffer_t *, gss_buffer_t *,
137                   OM_uint32 *, send_token_flag *);
138 static OM_uint32
139 init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
140                    gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
141                    gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
142                    OM_uint32 *, send_token_flag *);
143
144 static OM_uint32
145 acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
146             spnego_gss_cred_id_t, gss_buffer_t *,
147             gss_buffer_t *, OM_uint32 *, send_token_flag *);
148 static OM_uint32
149 acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
150              gss_buffer_t *, gss_buffer_t *,
151              OM_uint32 *, send_token_flag *);
152 static OM_uint32
153 acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
154                 OM_uint32 *, send_token_flag *);
155 static OM_uint32
156 acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
157                  gss_buffer_t, gss_OID *, gss_buffer_t,
158                  OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
159                  OM_uint32 *, send_token_flag *);
160
161 static gss_OID
162 negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
163                 OM_uint32 *);
164 static int
165 g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
166
167 static int
168 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
169                         int,
170                         gss_buffer_t,
171                         OM_uint32, gss_buffer_t, send_token_flag,
172                         gss_buffer_t);
173 static int
174 make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
175                         gss_buffer_t, send_token_flag,
176                         gss_buffer_t);
177
178 static OM_uint32
179 get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
180                  gss_OID_set *, OM_uint32 *, gss_buffer_t *,
181                  gss_buffer_t *);
182 static OM_uint32
183 get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
184                  OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
185
186 static int
187 is_kerb_mech(gss_OID oid);
188
189 /* SPNEGO oid structure */
190 static const gss_OID_desc spnego_oids[] = {
191         {SPNEGO_OID_LENGTH, SPNEGO_OID},
192 };
193
194 const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
195 static const gss_OID_set_desc spnego_oidsets[] = {
196         {1, (gss_OID) spnego_oids+0},
197 };
198 const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
199
200 static int make_NegHints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *);
201 static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
202 static OM_uint32
203 acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, spnego_gss_cred_id_t,
204               gss_buffer_t *, OM_uint32 *, send_token_flag *);
205
206 /*
207  * The Mech OID for SPNEGO:
208  * { iso(1) org(3) dod(6) internet(1) security(5)
209  *  mechanism(5) spnego(2) }
210  */
211 static struct gss_config spnego_mechanism =
212 {
213         {SPNEGO_OID_LENGTH, SPNEGO_OID},
214         NULL,
215         spnego_gss_acquire_cred,
216         spnego_gss_release_cred,
217         spnego_gss_init_sec_context,
218 #ifndef LEAN_CLIENT
219         spnego_gss_accept_sec_context,
220 #else
221         NULL,
222 #endif  /* LEAN_CLIENT */
223         NULL,                           /* gss_process_context_token */
224         spnego_gss_delete_sec_context,  /* gss_delete_sec_context */
225         spnego_gss_context_time,        /* gss_context_time */
226         spnego_gss_get_mic,             /* gss_get_mic */
227         spnego_gss_verify_mic,          /* gss_verify_mic */
228         spnego_gss_wrap,                /* gss_wrap */
229         spnego_gss_unwrap,              /* gss_unwrap */
230         spnego_gss_display_status,
231         NULL,                           /* gss_indicate_mechs */
232         spnego_gss_compare_name,
233         spnego_gss_display_name,
234         spnego_gss_import_name,
235         spnego_gss_release_name,
236         spnego_gss_inquire_cred,        /* gss_inquire_cred */
237         NULL,                           /* gss_add_cred */
238 #ifndef LEAN_CLIENT
239         spnego_gss_export_sec_context,          /* gss_export_sec_context */
240         spnego_gss_import_sec_context,          /* gss_import_sec_context */
241 #else
242         NULL,                           /* gss_export_sec_context */
243         NULL,                           /* gss_import_sec_context */
244 #endif /* LEAN_CLIENT */
245         NULL,                           /* gss_inquire_cred_by_mech */
246         spnego_gss_inquire_names_for_mech,
247         spnego_gss_inquire_context,     /* gss_inquire_context */
248         NULL,                           /* gss_internal_release_oid */
249         spnego_gss_wrap_size_limit,     /* gss_wrap_size_limit */
250         NULL,                           /* gss_export_name */
251         NULL,                           /* gss_store_cred */
252         spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
253         spnego_gss_inquire_cred_by_oid, /* gss_inquire_cred_by_oid */
254         spnego_gss_set_sec_context_option, /* gss_set_sec_context_option */
255         spnego_gss_set_cred_option,     /* gssspi_set_cred_option */
256         NULL,                           /* gssspi_mech_invoke */
257         spnego_gss_wrap_aead,
258         spnego_gss_unwrap_aead,
259         spnego_gss_wrap_iov,
260         spnego_gss_unwrap_iov,
261         spnego_gss_wrap_iov_length,
262         spnego_gss_complete_auth_token,
263         spnego_gss_acquire_cred_impersonate_name,
264         NULL,                           /* gss_add_cred_impersonate_name */
265         spnego_gss_display_name_ext,
266         spnego_gss_inquire_name,
267         spnego_gss_get_name_attribute,
268         spnego_gss_set_name_attribute,
269         spnego_gss_delete_name_attribute,
270         spnego_gss_export_name_composite,
271         spnego_gss_map_name_to_any,
272         spnego_gss_release_any_name_mapping,
273         spnego_gss_pseudo_random,
274         spnego_gss_set_neg_mechs,
275 };
276
277 #ifdef _GSS_STATIC_LINK
278 #include "mglueP.h"
279
280 static int gss_spnegomechglue_init(void)
281 {
282         struct gss_mech_config mech_spnego;
283
284         memset(&mech_spnego, 0, sizeof(mech_spnego));
285         mech_spnego.mech = &spnego_mechanism;
286         mech_spnego.mechNameStr = "spnego";
287         mech_spnego.mech_type = GSS_C_NO_OID;
288
289         return gssint_register_mechinfo(&mech_spnego);
290 }
291 #else
292 gss_mechanism KRB5_CALLCONV
293 gss_mech_initialize(void)
294 {
295         return (&spnego_mechanism);
296 }
297
298 MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
299 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
300 int gss_krb5int_lib_init(void);
301 #endif /* _GSS_STATIC_LINK */
302
303 int gss_spnegoint_lib_init(void)
304 {
305 #ifdef _GSS_STATIC_LINK
306         return gss_spnegomechglue_init();
307 #else
308         return 0;
309 #endif
310 }
311
312 void gss_spnegoint_lib_fini(void)
313 {
314 }
315
316 /*ARGSUSED*/
317 OM_uint32
318 spnego_gss_acquire_cred(OM_uint32 *minor_status,
319                         gss_name_t desired_name,
320                         OM_uint32 time_req,
321                         gss_OID_set desired_mechs,
322                         gss_cred_usage_t cred_usage,
323                         gss_cred_id_t *output_cred_handle,
324                         gss_OID_set *actual_mechs,
325                         OM_uint32 *time_rec)
326 {
327         OM_uint32 status;
328         gss_OID_set amechs;
329         gss_cred_id_t mcred = NULL;
330         spnego_gss_cred_id_t spcred = NULL;
331         dsyslog("Entering spnego_gss_acquire_cred\n");
332
333         if (actual_mechs)
334                 *actual_mechs = NULL;
335
336         if (time_rec)
337                 *time_rec = 0;
338
339         spcred = malloc(sizeof(spnego_gss_cred_id_rec));
340         if (spcred == NULL) {
341                 *minor_status = ENOMEM;
342                 return (GSS_S_FAILURE);
343         }
344         spcred->neg_mechs = GSS_C_NULL_OID_SET;
345
346         /*
347          * If the user did not specify a list of mechs,
348          * use get_available_mechs to collect a list of
349          * mechs for which creds are available.
350          */
351         if (desired_mechs == GSS_C_NULL_OID_SET) {
352                 status = get_available_mechs(minor_status,
353                                 desired_name, cred_usage,
354                                 &mcred, &amechs);
355         } else {
356                 /*
357                  * The caller gave a specific list of mechanisms,
358                  * so just get whatever creds are available.
359                  * gss_acquire_creds will return the subset of mechs for
360                  * which the given 'output_cred_handle' is valid.
361                  */
362                 status = gss_acquire_cred(minor_status,
363                                 desired_name, time_req,
364                                 desired_mechs, cred_usage,
365                                 &mcred, &amechs, time_rec);
366         }
367
368         if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
369                 (void) gssint_copy_oid_set(minor_status, amechs, actual_mechs);
370         }
371         (void) gss_release_oid_set(minor_status, &amechs);
372
373         if (status == GSS_S_COMPLETE) {
374                 spcred->mcred = mcred;
375                 *output_cred_handle = (gss_cred_id_t)spcred;
376         } else {
377                 free(spcred);
378                 *output_cred_handle = GSS_C_NO_CREDENTIAL;
379         }
380
381         dsyslog("Leaving spnego_gss_acquire_cred\n");
382         return (status);
383 }
384
385 /*ARGSUSED*/
386 OM_uint32
387 spnego_gss_release_cred(OM_uint32 *minor_status,
388                         gss_cred_id_t *cred_handle)
389 {
390         spnego_gss_cred_id_t spcred = NULL;
391
392         dsyslog("Entering spnego_gss_release_cred\n");
393
394         if (minor_status == NULL || cred_handle == NULL)
395                 return (GSS_S_CALL_INACCESSIBLE_WRITE);
396
397         *minor_status = 0;
398
399         if (*cred_handle == GSS_C_NO_CREDENTIAL)
400                 return (GSS_S_COMPLETE);
401
402         spcred = (spnego_gss_cred_id_t)*cred_handle;
403         *cred_handle = GSS_C_NO_CREDENTIAL;
404         gss_release_oid_set(minor_status, &spcred->neg_mechs);
405         gss_release_cred(minor_status, &spcred->mcred);
406         free(spcred);
407
408         dsyslog("Leaving spnego_gss_release_cred\n");
409         return (GSS_S_COMPLETE);
410 }
411
412 static void
413 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
414 {
415         spnego_ctx->optionStr = gssint_get_modOptions(
416                 (const gss_OID)&spnego_oids[0]);
417 }
418
419 static spnego_gss_ctx_id_t
420 create_spnego_ctx(void)
421 {
422         spnego_gss_ctx_id_t spnego_ctx = NULL;
423         spnego_ctx = (spnego_gss_ctx_id_t)
424                 malloc(sizeof (spnego_gss_ctx_id_rec));
425
426         if (spnego_ctx == NULL) {
427                 return (NULL);
428         }
429
430         spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
431         spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
432         spnego_ctx->internal_mech = NULL;
433         spnego_ctx->optionStr = NULL;
434         spnego_ctx->DER_mechTypes.length = 0;
435         spnego_ctx->DER_mechTypes.value = NULL;
436         spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
437         spnego_ctx->mic_reqd = 0;
438         spnego_ctx->mic_sent = 0;
439         spnego_ctx->mic_rcvd = 0;
440         spnego_ctx->mech_complete = 0;
441         spnego_ctx->nego_done = 0;
442         spnego_ctx->internal_name = GSS_C_NO_NAME;
443         spnego_ctx->actual_mech = GSS_C_NO_OID;
444
445         check_spnego_options(spnego_ctx);
446
447         return (spnego_ctx);
448 }
449
450 /*
451  * Both initiator and acceptor call here to verify and/or create mechListMIC,
452  * and to consistency-check the MIC state.  handle_mic is invoked only if the
453  * negotiated mech has completed and supports MICs.
454  */
455 static OM_uint32
456 handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
457            int send_mechtok, spnego_gss_ctx_id_t sc,
458            gss_buffer_t *mic_out,
459            OM_uint32 *negState, send_token_flag *tokflag)
460 {
461         OM_uint32 ret;
462
463         ret = GSS_S_FAILURE;
464         *mic_out = GSS_C_NO_BUFFER;
465         if (mic_in != GSS_C_NO_BUFFER) {
466                 if (sc->mic_rcvd) {
467                         /* Reject MIC if we've already received a MIC. */
468                         *negState = REJECT;
469                         *tokflag = ERROR_TOKEN_SEND;
470                         return GSS_S_DEFECTIVE_TOKEN;
471                 }
472         } else if (sc->mic_reqd && !send_mechtok) {
473                 /*
474                  * If the peer sends the final mechanism token, it
475                  * must send the MIC with that token if the
476                  * negotiation requires MICs.
477                  */
478                 *negState = REJECT;
479                 *tokflag = ERROR_TOKEN_SEND;
480                 return GSS_S_DEFECTIVE_TOKEN;
481         }
482         ret = process_mic(minor_status, mic_in, sc, mic_out,
483                           negState, tokflag);
484         if (ret != GSS_S_COMPLETE) {
485                 return ret;
486         }
487         if (sc->mic_reqd) {
488                 assert(sc->mic_sent || sc->mic_rcvd);
489         }
490         if (sc->mic_sent && sc->mic_rcvd) {
491                 ret = GSS_S_COMPLETE;
492                 *negState = ACCEPT_COMPLETE;
493                 if (*mic_out == GSS_C_NO_BUFFER) {
494                         /*
495                          * We sent a MIC on the previous pass; we
496                          * shouldn't be sending a mechanism token.
497                          */
498                         assert(!send_mechtok);
499                         *tokflag = NO_TOKEN_SEND;
500                 } else {
501                         *tokflag = CONT_TOKEN_SEND;
502                 }
503         } else if (sc->mic_reqd) {
504                 *negState = ACCEPT_INCOMPLETE;
505                 ret = GSS_S_CONTINUE_NEEDED;
506         } else if (*negState == ACCEPT_COMPLETE) {
507                 ret = GSS_S_COMPLETE;
508         } else {
509                 ret = GSS_S_CONTINUE_NEEDED;
510         }
511         return ret;
512 }
513
514 /*
515  * Perform the actual verification and/or generation of mechListMIC.
516  */
517 static OM_uint32
518 process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
519             spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
520             OM_uint32 *negState, send_token_flag *tokflag)
521 {
522         OM_uint32 ret, tmpmin;
523         gss_qop_t qop_state;
524         gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
525
526         ret = GSS_S_FAILURE;
527         if (mic_in != GSS_C_NO_BUFFER) {
528                 ret = gss_verify_mic(minor_status, sc->ctx_handle,
529                                      &sc->DER_mechTypes,
530                                      mic_in, &qop_state);
531                 if (ret != GSS_S_COMPLETE) {
532                         *negState = REJECT;
533                         *tokflag = ERROR_TOKEN_SEND;
534                         return ret;
535                 }
536                 /* If we got a MIC, we must send a MIC. */
537                 sc->mic_reqd = 1;
538                 sc->mic_rcvd = 1;
539         }
540         if (sc->mic_reqd && !sc->mic_sent) {
541                 ret = gss_get_mic(minor_status, sc->ctx_handle,
542                                   GSS_C_QOP_DEFAULT,
543                                   &sc->DER_mechTypes,
544                                   &tmpmic);
545                 if (ret != GSS_S_COMPLETE) {
546                         gss_release_buffer(&tmpmin, &tmpmic);
547                         *tokflag = NO_TOKEN_SEND;
548                         return ret;
549                 }
550                 *mic_out = malloc(sizeof(gss_buffer_desc));
551                 if (*mic_out == GSS_C_NO_BUFFER) {
552                         gss_release_buffer(&tmpmin, &tmpmic);
553                         *tokflag = NO_TOKEN_SEND;
554                         return GSS_S_FAILURE;
555                 }
556                 **mic_out = tmpmic;
557                 sc->mic_sent = 1;
558         }
559         return GSS_S_COMPLETE;
560 }
561
562 /*
563  * Initial call to spnego_gss_init_sec_context().
564  */
565 static OM_uint32
566 init_ctx_new(OM_uint32 *minor_status,
567              spnego_gss_cred_id_t spcred,
568              gss_ctx_id_t *ctx,
569              gss_OID_set *mechSet,
570              send_token_flag *tokflag)
571 {
572         OM_uint32 ret, tmpmin;
573         spnego_gss_ctx_id_t sc = NULL;
574
575         /* determine negotiation mech set */
576         ret = get_negotiable_mechs(minor_status, spcred, GSS_C_INITIATE,
577                                    mechSet);
578         if (ret != GSS_S_COMPLETE)
579                 return ret;
580
581         sc = create_spnego_ctx();
582         if (sc == NULL)
583                 return GSS_S_FAILURE;
584
585         /*
586          * need to pull the first mech from mechSet to do first
587          * gss_init_sec_context()
588          */
589         ret = generic_gss_copy_oid(minor_status, (*mechSet)->elements,
590                                    &sc->internal_mech);
591         if (ret != GSS_S_COMPLETE) {
592             map_errcode(minor_status);
593             goto cleanup;
594         }
595
596         if (put_mech_set(*mechSet, &sc->DER_mechTypes) < 0) {
597                 generic_gss_release_oid(&tmpmin, &sc->internal_mech);
598                 ret = GSS_S_FAILURE;
599                 goto cleanup;
600         }
601         /*
602          * The actual context is not yet determined, set the output
603          * context handle to refer to the spnego context itself.
604          */
605         sc->ctx_handle = GSS_C_NO_CONTEXT;
606         *ctx = (gss_ctx_id_t)sc;
607         *tokflag = INIT_TOKEN_SEND;
608         ret = GSS_S_CONTINUE_NEEDED;
609
610 cleanup:
611         gss_release_oid_set(&tmpmin, mechSet);
612         return ret;
613 }
614
615 /*
616  * Called by second and later calls to spnego_gss_init_sec_context()
617  * to decode reply and update state.
618  */
619 static OM_uint32
620 init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
621               gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
622               OM_uint32 *negState, send_token_flag *tokflag)
623 {
624         OM_uint32 ret, tmpmin, acc_negState;
625         unsigned char *ptr;
626         spnego_gss_ctx_id_t sc;
627         gss_OID supportedMech = GSS_C_NO_OID;
628
629         sc = (spnego_gss_ctx_id_t)*ctx;
630         *negState = REJECT;
631         *tokflag = ERROR_TOKEN_SEND;
632
633         ptr = buf->value;
634         ret = get_negTokenResp(minor_status, ptr, buf->length,
635                                &acc_negState, &supportedMech,
636                                responseToken, mechListMIC);
637         if (ret != GSS_S_COMPLETE)
638                 goto cleanup;
639         if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
640             supportedMech == GSS_C_NO_OID &&
641             *responseToken == GSS_C_NO_BUFFER &&
642             *mechListMIC == GSS_C_NO_BUFFER) {
643                 /* Reject "empty" token. */
644                 ret = GSS_S_DEFECTIVE_TOKEN;
645         }
646         if (acc_negState == REJECT) {
647                 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
648                 map_errcode(minor_status);
649                 *tokflag = NO_TOKEN_SEND;
650                 ret = GSS_S_FAILURE;
651                 goto cleanup;
652         }
653         /*
654          * nego_done is false for the first call to init_ctx_cont()
655          */
656         if (!sc->nego_done) {
657                 ret = init_ctx_nego(minor_status, sc,
658                                     acc_negState,
659                                     supportedMech, responseToken,
660                                     mechListMIC,
661                                     negState, tokflag);
662         } else if ((!sc->mech_complete && *responseToken == GSS_C_NO_BUFFER) ||
663                    (sc->mech_complete && *responseToken != GSS_C_NO_BUFFER)) {
664                 /* Missing or spurious token from acceptor. */
665                 ret = GSS_S_DEFECTIVE_TOKEN;
666         } else if (!sc->mech_complete ||
667                    (sc->mic_reqd &&
668                     (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
669                 /* Not obviously done; we may decide we're done later in
670                  * init_ctx_call_init or handle_mic. */
671                 *negState = ACCEPT_INCOMPLETE;
672                 *tokflag = CONT_TOKEN_SEND;
673                 ret = GSS_S_CONTINUE_NEEDED;
674         } else {
675                 /* mech finished on last pass and no MIC required, so done. */
676                 *negState = ACCEPT_COMPLETE;
677                 *tokflag = NO_TOKEN_SEND;
678                 ret = GSS_S_COMPLETE;
679         }
680 cleanup:
681         if (supportedMech != GSS_C_NO_OID)
682                 generic_gss_release_oid(&tmpmin, &supportedMech);
683         return ret;
684 }
685
686 /*
687  * Consistency checking and mechanism negotiation handling for second
688  * call of spnego_gss_init_sec_context().  Call init_ctx_reselect() to
689  * update internal state if acceptor has counter-proposed.
690  */
691 static OM_uint32
692 init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
693               OM_uint32 acc_negState, gss_OID supportedMech,
694               gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
695               OM_uint32 *negState, send_token_flag *tokflag)
696 {
697         OM_uint32 ret;
698
699         *negState = REJECT;
700         *tokflag = ERROR_TOKEN_SEND;
701         ret = GSS_S_DEFECTIVE_TOKEN;
702         /*
703          * Both supportedMech and negState must be present in first
704          * acceptor token.
705          */
706         if (supportedMech == GSS_C_NO_OID) {
707                 *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
708                 map_errcode(minor_status);
709                 return GSS_S_DEFECTIVE_TOKEN;
710         }
711         if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
712                 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
713                 map_errcode(minor_status);
714                 return GSS_S_DEFECTIVE_TOKEN;
715         }
716
717         /*
718          * If the mechanism we sent is not the mechanism returned from
719          * the server, we need to handle the server's counter
720          * proposal.  There is a bug in SAMBA servers that always send
721          * the old Kerberos mech OID, even though we sent the new one.
722          * So we will treat all the Kerberos mech OIDS as the same.
723          */
724         if (!(is_kerb_mech(supportedMech) &&
725               is_kerb_mech(sc->internal_mech)) &&
726             !g_OID_equal(supportedMech, sc->internal_mech)) {
727                 ret = init_ctx_reselect(minor_status, sc,
728                                         acc_negState, supportedMech,
729                                         responseToken, mechListMIC,
730                                         negState, tokflag);
731
732         } else if (*responseToken == GSS_C_NO_BUFFER) {
733                 if (sc->mech_complete) {
734                         /*
735                          * Mech completed on first call to its
736                          * init_sec_context().  Acceptor sends no mech
737                          * token.
738                          */
739                         *negState = ACCEPT_COMPLETE;
740                         *tokflag = NO_TOKEN_SEND;
741                         ret = GSS_S_COMPLETE;
742                 } else {
743                         /*
744                          * Reject missing mech token when optimistic
745                          * mech selected.
746                          */
747                         *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
748                         map_errcode(minor_status);
749                         ret = GSS_S_DEFECTIVE_TOKEN;
750                 }
751         } else if (sc->mech_complete) {
752                 /* Reject spurious mech token. */
753                 ret = GSS_S_DEFECTIVE_TOKEN;
754         } else {
755                 *negState = ACCEPT_INCOMPLETE;
756                 *tokflag = CONT_TOKEN_SEND;
757                 ret = GSS_S_CONTINUE_NEEDED;
758         }
759         sc->nego_done = 1;
760         return ret;
761 }
762
763 /*
764  * Handle acceptor's counter-proposal of an alternative mechanism.
765  */
766 static OM_uint32
767 init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
768                   OM_uint32 acc_negState, gss_OID supportedMech,
769                   gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
770                   OM_uint32 *negState, send_token_flag *tokflag)
771 {
772         OM_uint32 ret, tmpmin;
773
774         generic_gss_release_oid(&tmpmin, &sc->internal_mech);
775         gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
776                                GSS_C_NO_BUFFER);
777
778         ret = generic_gss_copy_oid(minor_status, supportedMech,
779                                    &sc->internal_mech);
780         if (ret != GSS_S_COMPLETE) {
781                 map_errcode(minor_status);
782                 sc->internal_mech = GSS_C_NO_OID;
783                 *tokflag = NO_TOKEN_SEND;
784                 return ret;
785         }
786         if (*responseToken != GSS_C_NO_BUFFER) {
787                 /* Reject spurious mech token. */
788                 return GSS_S_DEFECTIVE_TOKEN;
789         }
790         /*
791          * Windows 2003 and earlier don't correctly send a
792          * negState of request-mic when counter-proposing a
793          * mechanism.  They probably don't handle mechListMICs
794          * properly either.
795          */
796         if (acc_negState != REQUEST_MIC)
797                 return GSS_S_DEFECTIVE_TOKEN;
798
799         sc->mech_complete = 0;
800         sc->mic_reqd = 1;
801         *negState = REQUEST_MIC;
802         *tokflag = CONT_TOKEN_SEND;
803         return GSS_S_CONTINUE_NEEDED;
804 }
805
806 /*
807  * Wrap call to mechanism gss_init_sec_context() and update state
808  * accordingly.
809  */
810 static OM_uint32
811 init_ctx_call_init(OM_uint32 *minor_status,
812                    spnego_gss_ctx_id_t sc,
813                    spnego_gss_cred_id_t spcred,
814                    gss_name_t target_name,
815                    OM_uint32 req_flags,
816                    OM_uint32 time_req,
817                    gss_buffer_t mechtok_in,
818                    gss_OID *actual_mech,
819                    gss_buffer_t mechtok_out,
820                    OM_uint32 *ret_flags,
821                    OM_uint32 *time_rec,
822                    OM_uint32 *negState,
823                    send_token_flag *send_token)
824 {
825         OM_uint32 ret;
826         gss_cred_id_t mcred;
827
828         mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
829         ret = gss_init_sec_context(minor_status,
830                                    mcred,
831                                    &sc->ctx_handle,
832                                    target_name,
833                                    sc->internal_mech,
834                                    (req_flags | GSS_C_INTEG_FLAG),
835                                    time_req,
836                                    GSS_C_NO_CHANNEL_BINDINGS,
837                                    mechtok_in,
838                                    &sc->actual_mech,
839                                    mechtok_out,
840                                    &sc->ctx_flags,
841                                    time_rec);
842         if (ret == GSS_S_COMPLETE) {
843                 sc->mech_complete = 1;
844                 if (ret_flags != NULL)
845                         *ret_flags = sc->ctx_flags;
846                 /*
847                  * If this isn't the first time we've been called,
848                  * we're done unless a MIC needs to be
849                  * generated/handled.
850                  */
851                 if (*send_token == CONT_TOKEN_SEND &&
852                     mechtok_out->length == 0 &&
853                     (!sc->mic_reqd ||
854                      !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
855
856                         *negState = ACCEPT_COMPLETE;
857                         ret = GSS_S_COMPLETE;
858                         if (mechtok_out->length == 0) {
859                                 *send_token = NO_TOKEN_SEND;
860                         }
861                 } else {
862                         *negState = ACCEPT_INCOMPLETE;
863                         ret = GSS_S_CONTINUE_NEEDED;
864                 }
865         } else if (ret != GSS_S_CONTINUE_NEEDED) {
866                 if (*send_token == INIT_TOKEN_SEND) {
867                         /* Don't output token on error if first call. */
868                         *send_token = NO_TOKEN_SEND;
869                 } else {
870                         *send_token = ERROR_TOKEN_SEND;
871                 }
872                 *negState = REJECT;
873         }
874         return ret;
875 }
876
877 /*ARGSUSED*/
878 OM_uint32
879 spnego_gss_init_sec_context(
880                         OM_uint32 *minor_status,
881                         gss_cred_id_t claimant_cred_handle,
882                         gss_ctx_id_t *context_handle,
883                         gss_name_t target_name,
884                         gss_OID mech_type,
885                         OM_uint32 req_flags,
886                         OM_uint32 time_req,
887                         gss_channel_bindings_t input_chan_bindings,
888                         gss_buffer_t input_token,
889                         gss_OID *actual_mech,
890                         gss_buffer_t output_token,
891                         OM_uint32 *ret_flags,
892                         OM_uint32 *time_rec)
893 {
894         /*
895          * send_token is used to indicate in later steps
896          * what type of token, if any should be sent or processed.
897          * NO_TOKEN_SEND = no token should be sent
898          * INIT_TOKEN_SEND = initial token will be sent
899          * CONT_TOKEN_SEND = continuing tokens to be sent
900          * CHECK_MIC = no token to be sent, but have a MIC to check.
901          */
902         send_token_flag send_token = NO_TOKEN_SEND;
903         OM_uint32 tmpmin, ret, negState;
904         gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
905         gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
906         gss_OID_set mechSet = GSS_C_NO_OID_SET;
907         spnego_gss_cred_id_t spcred = NULL;
908         spnego_gss_ctx_id_t spnego_ctx = NULL;
909
910         dsyslog("Entering init_sec_context\n");
911
912         mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
913         negState = REJECT;
914
915         if (minor_status != NULL)
916                 *minor_status = 0;
917         if (output_token != GSS_C_NO_BUFFER) {
918                 output_token->length = 0;
919                 output_token->value = NULL;
920         }
921         if (minor_status == NULL ||
922             output_token == GSS_C_NO_BUFFER ||
923             context_handle == NULL)
924                 return GSS_S_CALL_INACCESSIBLE_WRITE;
925
926         if (actual_mech != NULL)
927                 *actual_mech = GSS_C_NO_OID;
928
929         spcred = (spnego_gss_cred_id_t)claimant_cred_handle;
930         if (*context_handle == GSS_C_NO_CONTEXT) {
931                 ret = init_ctx_new(minor_status, spcred,
932                                    context_handle, &mechSet, &send_token);
933                 if (ret != GSS_S_CONTINUE_NEEDED) {
934                         goto cleanup;
935                 }
936         } else {
937                 ret = init_ctx_cont(minor_status, context_handle,
938                                     input_token, &mechtok_in,
939                                     &mechListMIC_in, &negState, &send_token);
940                 if (HARD_ERROR(ret)) {
941                         goto cleanup;
942                 }
943         }
944         spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
945         if (!spnego_ctx->mech_complete) {
946                 ret = init_ctx_call_init(
947                         minor_status, spnego_ctx, spcred,
948                         target_name, req_flags,
949                         time_req, mechtok_in,
950                         actual_mech, &mechtok_out,
951                         ret_flags, time_rec,
952                         &negState, &send_token);
953         }
954         /* create mic/check mic */
955         if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
956             (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
957
958                 ret = handle_mic(minor_status,
959                                  mechListMIC_in,
960                                  (mechtok_out.length != 0),
961                                  spnego_ctx, &mechListMIC_out,
962                                  &negState, &send_token);
963         }
964 cleanup:
965         if (send_token == INIT_TOKEN_SEND) {
966                 if (make_spnego_tokenInit_msg(spnego_ctx,
967                                               0,
968                                               mechListMIC_out,
969                                               req_flags,
970                                               &mechtok_out, send_token,
971                                               output_token) < 0) {
972                         ret = GSS_S_FAILURE;
973                 }
974         } else if (send_token != NO_TOKEN_SEND) {
975                 if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
976                                               &mechtok_out, mechListMIC_out,
977                                               send_token,
978                                               output_token) < 0) {
979                         ret = GSS_S_FAILURE;
980                 }
981         }
982         gss_release_buffer(&tmpmin, &mechtok_out);
983         if (ret == GSS_S_COMPLETE) {
984                 /*
985                  * Now, switch the output context to refer to the
986                  * negotiated mechanism's context.
987                  */
988                 *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
989                 if (actual_mech != NULL)
990                         *actual_mech = spnego_ctx->actual_mech;
991                 if (ret_flags != NULL)
992                         *ret_flags = spnego_ctx->ctx_flags;
993                 release_spnego_ctx(&spnego_ctx);
994         } else if (ret != GSS_S_CONTINUE_NEEDED) {
995                 if (spnego_ctx != NULL) {
996                         gss_delete_sec_context(&tmpmin,
997                                                &spnego_ctx->ctx_handle,
998                                                GSS_C_NO_BUFFER);
999                         release_spnego_ctx(&spnego_ctx);
1000                 }
1001                 *context_handle = GSS_C_NO_CONTEXT;
1002         }
1003         if (mechtok_in != GSS_C_NO_BUFFER) {
1004                 gss_release_buffer(&tmpmin, mechtok_in);
1005                 free(mechtok_in);
1006         }
1007         if (mechListMIC_in != GSS_C_NO_BUFFER) {
1008                 gss_release_buffer(&tmpmin, mechListMIC_in);
1009                 free(mechListMIC_in);
1010         }
1011         if (mechListMIC_out != GSS_C_NO_BUFFER) {
1012                 gss_release_buffer(&tmpmin, mechListMIC_out);
1013                 free(mechListMIC_out);
1014         }
1015         if (mechSet != GSS_C_NO_OID_SET) {
1016                 gss_release_oid_set(&tmpmin, &mechSet);
1017         }
1018         return ret;
1019 } /* init_sec_context */
1020
1021 /* We don't want to import KRB5 headers here */
1022 static const gss_OID_desc gss_mech_krb5_oid =
1023         { 9, "\052\206\110\206\367\022\001\002\002" };
1024 static const gss_OID_desc gss_mech_krb5_wrong_oid =
1025         { 9, "\052\206\110\202\367\022\001\002\002" };
1026
1027 /*
1028  * verify that the input token length is not 0. If it is, just return.
1029  * If the token length is greater than 0, der encode as a sequence
1030  * and place in buf_out, advancing buf_out.
1031  */
1032
1033 static int
1034 put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
1035               unsigned int buflen)
1036 {
1037         int ret;
1038
1039         /* if token length is 0, we do not want to send */
1040         if (input_token->length == 0)
1041                 return (0);
1042
1043         if (input_token->length > buflen)
1044                 return (-1);
1045
1046         *(*buf_out)++ = SEQUENCE;
1047         if ((ret = gssint_put_der_length(input_token->length, buf_out,
1048                             input_token->length)))
1049                 return (ret);
1050         TWRITE_STR(*buf_out, input_token->value, input_token->length);
1051         return (0);
1052 }
1053
1054 /*
1055  * NegHints ::= SEQUENCE {
1056  *    hintName       [0]  GeneralString      OPTIONAL,
1057  *    hintAddress    [1]  OCTET STRING       OPTIONAL
1058  * }
1059  */
1060
1061 #define HOST_PREFIX     "host@"
1062 #define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
1063
1064 static int
1065 make_NegHints(OM_uint32 *minor_status,
1066               spnego_gss_cred_id_t spcred, gss_buffer_t *outbuf)
1067 {
1068         gss_buffer_desc hintNameBuf;
1069         gss_name_t hintName = GSS_C_NO_NAME;
1070         gss_name_t hintKerberosName;
1071         gss_OID hintNameType;
1072         OM_uint32 major_status;
1073         OM_uint32 minor;
1074         unsigned int tlen = 0;
1075         unsigned int hintNameSize = 0;
1076         unsigned int negHintsSize = 0;
1077         unsigned char *ptr;
1078         unsigned char *t;
1079
1080         *outbuf = GSS_C_NO_BUFFER;
1081
1082         if (spcred != NULL) {
1083                 major_status = gss_inquire_cred(minor_status,
1084                                                 spcred->mcred,
1085                                                 &hintName,
1086                                                 NULL,
1087                                                 NULL,
1088                                                 NULL);
1089                 if (major_status != GSS_S_COMPLETE)
1090                         return (major_status);
1091         }
1092
1093         if (hintName == GSS_C_NO_NAME) {
1094                 krb5_error_code code;
1095                 krb5int_access kaccess;
1096                 char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX;
1097
1098                 code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION);
1099                 if (code != 0) {
1100                         *minor_status = code;
1101                         return (GSS_S_FAILURE);
1102                 }
1103
1104                 /* this breaks mutual authentication but Samba relies on it */
1105                 code = (*kaccess.clean_hostname)(NULL, NULL,
1106                                                  &hostname[HOST_PREFIX_LEN],
1107                                                  MAXHOSTNAMELEN);
1108                 if (code != 0) {
1109                         *minor_status = code;
1110                         return (GSS_S_FAILURE);
1111                 }
1112
1113                 hintNameBuf.value = hostname;
1114                 hintNameBuf.length = strlen(hostname);
1115
1116                 major_status = gss_import_name(minor_status,
1117                                                &hintNameBuf,
1118                                                GSS_C_NT_HOSTBASED_SERVICE,
1119                                                &hintName);
1120                 if (major_status != GSS_S_COMPLETE) {
1121                         return (major_status);
1122                 }
1123         }
1124
1125         hintNameBuf.value = NULL;
1126         hintNameBuf.length = 0;
1127
1128         major_status = gss_canonicalize_name(minor_status,
1129                                              hintName,
1130                                              (gss_OID)&gss_mech_krb5_oid,
1131                                              &hintKerberosName);
1132         if (major_status != GSS_S_COMPLETE) {
1133                 gss_release_name(&minor, &hintName);
1134                 return (major_status);
1135         }
1136         gss_release_name(&minor, &hintName);
1137
1138         major_status = gss_display_name(minor_status,
1139                                         hintKerberosName,
1140                                         &hintNameBuf,
1141                                         &hintNameType);
1142         if (major_status != GSS_S_COMPLETE) {
1143                 gss_release_name(&minor, &hintName);
1144                 return (major_status);
1145         }
1146         gss_release_name(&minor, &hintKerberosName);
1147
1148         /*
1149          * Now encode the name hint into a NegHints ASN.1 type
1150          */
1151         major_status = GSS_S_FAILURE;
1152
1153         /* Length of DER encoded GeneralString */
1154         tlen = 1 + gssint_der_length_size(hintNameBuf.length) +
1155                 hintNameBuf.length;
1156         hintNameSize = tlen;
1157
1158         /* Length of DER encoded hintName */
1159         tlen += 1 + gssint_der_length_size(hintNameSize);
1160         negHintsSize = tlen;
1161
1162         t = (unsigned char *)malloc(tlen);
1163         if (t == NULL) {
1164                 *minor_status = ENOMEM;
1165                 goto errout;
1166         }
1167
1168         ptr = t;
1169
1170         *ptr++ = CONTEXT | 0x00; /* hintName identifier */
1171         if (gssint_put_der_length(hintNameSize,
1172                                   &ptr, tlen - (int)(ptr-t)))
1173                 goto errout;
1174
1175         *ptr++ = GENERAL_STRING;
1176         if (gssint_put_der_length(hintNameBuf.length,
1177                                   &ptr, tlen - (int)(ptr-t)))
1178                 goto errout;
1179
1180         memcpy(ptr, hintNameBuf.value, hintNameBuf.length);
1181         ptr += hintNameBuf.length;
1182
1183         *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
1184         if (*outbuf == NULL) {
1185                 *minor_status = ENOMEM;
1186                 goto errout;
1187         }
1188         (*outbuf)->value = (void *)t;
1189         (*outbuf)->length = ptr - t;
1190
1191         t = NULL; /* don't free */
1192
1193         *minor_status = 0;
1194         major_status = GSS_S_COMPLETE;
1195
1196 errout:
1197         if (t != NULL) {
1198                 free(t);
1199         }
1200
1201         gss_release_buffer(&minor, &hintNameBuf);
1202
1203         return (major_status);
1204 }
1205
1206 static OM_uint32
1207 acc_ctx_hints(OM_uint32 *minor_status,
1208               gss_ctx_id_t *ctx,
1209               spnego_gss_cred_id_t spcred,
1210               gss_buffer_t *mechListMIC,
1211               OM_uint32 *negState,
1212               send_token_flag *return_token)
1213 {
1214         OM_uint32 tmpmin, ret;
1215         gss_OID_set supported_mechSet;
1216         spnego_gss_ctx_id_t sc = NULL;
1217
1218         *mechListMIC = GSS_C_NO_BUFFER;
1219         supported_mechSet = GSS_C_NO_OID_SET;
1220         *return_token = ERROR_TOKEN_SEND;
1221         *negState = REJECT;
1222         *minor_status = 0;
1223
1224         *ctx = GSS_C_NO_CONTEXT;
1225         ret = GSS_S_DEFECTIVE_TOKEN;
1226
1227         ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
1228                                    &supported_mechSet);
1229         if (ret != GSS_S_COMPLETE) {
1230                 *return_token = NO_TOKEN_SEND;
1231                 goto cleanup;
1232         }
1233
1234         ret = make_NegHints(minor_status, spcred, mechListMIC);
1235         if (ret != GSS_S_COMPLETE) {
1236                 *return_token = NO_TOKEN_SEND;
1237                 goto cleanup;
1238         }
1239
1240         /*
1241          * Select the best match between the list of mechs
1242          * that the initiator requested and the list that
1243          * the acceptor will support.
1244          */
1245         sc = create_spnego_ctx();
1246         if (sc == NULL) {
1247                 ret = GSS_S_FAILURE;
1248                 *return_token = NO_TOKEN_SEND;
1249                 goto cleanup;
1250         }
1251         if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
1252                 ret = GSS_S_FAILURE;
1253                 *return_token = NO_TOKEN_SEND;
1254                 goto cleanup;
1255         }
1256         sc->internal_mech = GSS_C_NO_OID;
1257
1258         *negState = ACCEPT_INCOMPLETE;
1259         *return_token = INIT_TOKEN_SEND;
1260         sc->firstpass = 1;
1261         *ctx = (gss_ctx_id_t)sc;
1262         ret = GSS_S_COMPLETE;
1263
1264 cleanup:
1265         gss_release_oid_set(&tmpmin, &supported_mechSet);
1266
1267         return ret;
1268 }
1269
1270 /*
1271  * Set negState to REJECT if the token is defective, else
1272  * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
1273  * preferred mechanism is supported.
1274  */
1275 static OM_uint32
1276 acc_ctx_new(OM_uint32 *minor_status,
1277             gss_buffer_t buf,
1278             gss_ctx_id_t *ctx,
1279             spnego_gss_cred_id_t spcred,
1280             gss_buffer_t *mechToken,
1281             gss_buffer_t *mechListMIC,
1282             OM_uint32 *negState,
1283             send_token_flag *return_token)
1284 {
1285         OM_uint32 tmpmin, ret, req_flags;
1286         gss_OID_set supported_mechSet, mechTypes;
1287         gss_buffer_desc der_mechTypes;
1288         gss_OID mech_wanted;
1289         spnego_gss_ctx_id_t sc = NULL;
1290
1291         *ctx = GSS_C_NO_CONTEXT;
1292
1293         ret = GSS_S_DEFECTIVE_TOKEN;
1294         der_mechTypes.length = 0;
1295         der_mechTypes.value = NULL;
1296         *mechToken = *mechListMIC = GSS_C_NO_BUFFER;
1297         supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
1298         *return_token = ERROR_TOKEN_SEND;
1299         *negState = REJECT;
1300         *minor_status = 0;
1301
1302         ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
1303                                &mechTypes, &req_flags,
1304                                mechToken, mechListMIC);
1305         if (ret != GSS_S_COMPLETE) {
1306                 goto cleanup;
1307         }
1308         ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
1309                                    &supported_mechSet);
1310         if (ret != GSS_S_COMPLETE) {
1311                 *return_token = NO_TOKEN_SEND;
1312                 goto cleanup;
1313         }
1314         /*
1315          * Select the best match between the list of mechs
1316          * that the initiator requested and the list that
1317          * the acceptor will support.
1318          */
1319         mech_wanted = negotiate_mech_type(minor_status,
1320                                           supported_mechSet,
1321                                           mechTypes,
1322                                           negState);
1323         if (*negState == REJECT) {
1324                 ret = GSS_S_BAD_MECH;
1325                 goto cleanup;
1326         }
1327         sc = (spnego_gss_ctx_id_t)*ctx;
1328         if (sc != NULL) {
1329                 gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
1330                 assert(mech_wanted != GSS_C_NO_OID);
1331         } else
1332                 sc = create_spnego_ctx();
1333         if (sc == NULL) {
1334                 ret = GSS_S_FAILURE;
1335                 *return_token = NO_TOKEN_SEND;
1336                 generic_gss_release_oid(&tmpmin, &mech_wanted);
1337                 goto cleanup;
1338         }
1339         sc->internal_mech = mech_wanted;
1340         sc->DER_mechTypes = der_mechTypes;
1341         der_mechTypes.length = 0;
1342         der_mechTypes.value = NULL;
1343
1344         if (*negState == REQUEST_MIC)
1345                 sc->mic_reqd = 1;
1346
1347         *return_token = INIT_TOKEN_SEND;
1348         sc->firstpass = 1;
1349         *ctx = (gss_ctx_id_t)sc;
1350         ret = GSS_S_COMPLETE;
1351 cleanup:
1352         gss_release_oid_set(&tmpmin, &mechTypes);
1353         gss_release_oid_set(&tmpmin, &supported_mechSet);
1354         if (der_mechTypes.length != 0)
1355                 gss_release_buffer(&tmpmin, &der_mechTypes);
1356
1357         return ret;
1358 }
1359
1360 static OM_uint32
1361 acc_ctx_cont(OM_uint32 *minstat,
1362              gss_buffer_t buf,
1363              gss_ctx_id_t *ctx,
1364              gss_buffer_t *responseToken,
1365              gss_buffer_t *mechListMIC,
1366              OM_uint32 *negState,
1367              send_token_flag *return_token)
1368 {
1369         OM_uint32 ret, tmpmin;
1370         gss_OID supportedMech;
1371         spnego_gss_ctx_id_t sc;
1372         unsigned int len;
1373         unsigned char *ptr, *bufstart;
1374
1375         sc = (spnego_gss_ctx_id_t)*ctx;
1376         ret = GSS_S_DEFECTIVE_TOKEN;
1377         *negState = REJECT;
1378         *minstat = 0;
1379         supportedMech = GSS_C_NO_OID;
1380         *return_token = ERROR_TOKEN_SEND;
1381         *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1382
1383         ptr = bufstart = buf->value;
1384 #define REMAIN (buf->length - (ptr - bufstart))
1385         if (REMAIN > INT_MAX)
1386                 return GSS_S_DEFECTIVE_TOKEN;
1387
1388         /*
1389          * Attempt to work with old Sun SPNEGO.
1390          */
1391         if (*ptr == HEADER_ID) {
1392                 ret = g_verify_token_header(gss_mech_spnego,
1393                                             &len, &ptr, 0, REMAIN);
1394                 if (ret) {
1395                         *minstat = ret;
1396                         return GSS_S_DEFECTIVE_TOKEN;
1397                 }
1398         }
1399         if (*ptr != (CONTEXT | 0x01)) {
1400                 return GSS_S_DEFECTIVE_TOKEN;
1401         }
1402         ret = get_negTokenResp(minstat, ptr, REMAIN,
1403                                negState, &supportedMech,
1404                                responseToken, mechListMIC);
1405         if (ret != GSS_S_COMPLETE)
1406                 goto cleanup;
1407
1408         if (*responseToken == GSS_C_NO_BUFFER &&
1409             *mechListMIC == GSS_C_NO_BUFFER) {
1410
1411                 ret = GSS_S_DEFECTIVE_TOKEN;
1412                 goto cleanup;
1413         }
1414         if (supportedMech != GSS_C_NO_OID) {
1415                 ret = GSS_S_DEFECTIVE_TOKEN;
1416                 goto cleanup;
1417         }
1418         sc->firstpass = 0;
1419         *negState = ACCEPT_INCOMPLETE;
1420         *return_token = CONT_TOKEN_SEND;
1421 cleanup:
1422         if (supportedMech != GSS_C_NO_OID) {
1423                 generic_gss_release_oid(&tmpmin, &supportedMech);
1424         }
1425         return ret;
1426 #undef REMAIN
1427 }
1428
1429 /*
1430  * Verify that mech OID is either exactly the same as the negotiated
1431  * mech OID, or is a mech OID supported by the negotiated mech.  MS
1432  * implementations can list a most preferred mech using an incorrect
1433  * krb5 OID while emitting a krb5 initiator mech token having the
1434  * correct krb5 mech OID.
1435  */
1436 static OM_uint32
1437 acc_ctx_vfy_oid(OM_uint32 *minor_status,
1438                 spnego_gss_ctx_id_t sc, gss_OID mechoid,
1439                 OM_uint32 *negState, send_token_flag *tokflag)
1440 {
1441         OM_uint32 ret, tmpmin;
1442         gss_mechanism mech = NULL;
1443         gss_OID_set mech_set = GSS_C_NO_OID_SET;
1444         int present = 0;
1445
1446         if (g_OID_equal(sc->internal_mech, mechoid))
1447                 return GSS_S_COMPLETE;
1448
1449         mech = gssint_get_mechanism(sc->internal_mech);
1450         if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1451                 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1452                 map_errcode(minor_status);
1453                 *negState = REJECT;
1454                 *tokflag = ERROR_TOKEN_SEND;
1455                 return GSS_S_BAD_MECH;
1456         }
1457         ret = mech->gss_indicate_mechs(minor_status, &mech_set);
1458         if (ret != GSS_S_COMPLETE) {
1459                 *tokflag = NO_TOKEN_SEND;
1460                 map_error(minor_status, mech);
1461                 goto cleanup;
1462         }
1463         ret = gss_test_oid_set_member(minor_status, mechoid,
1464                                       mech_set, &present);
1465         if (ret != GSS_S_COMPLETE)
1466                 goto cleanup;
1467         if (!present) {
1468                 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1469                 map_errcode(minor_status);
1470                 *negState = REJECT;
1471                 *tokflag = ERROR_TOKEN_SEND;
1472                 ret = GSS_S_BAD_MECH;
1473         }
1474 cleanup:
1475         gss_release_oid_set(&tmpmin, &mech_set);
1476         return ret;
1477 }
1478 #ifndef LEAN_CLIENT
1479 /*
1480  * Wrap call to gss_accept_sec_context() and update state
1481  * accordingly.
1482  */
1483 static OM_uint32
1484 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1485                  spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
1486                  gss_OID *mech_type, gss_buffer_t mechtok_out,
1487                  OM_uint32 *ret_flags, OM_uint32 *time_rec,
1488                  gss_cred_id_t *delegated_cred_handle,
1489                  OM_uint32 *negState, send_token_flag *tokflag)
1490 {
1491         OM_uint32 ret;
1492         gss_OID_desc mechoid;
1493         gss_cred_id_t mcred;
1494
1495         if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
1496                 /*
1497                  * mechoid is an alias; don't free it.
1498                  */
1499                 ret = gssint_get_mech_type(&mechoid, mechtok_in);
1500                 if (ret != GSS_S_COMPLETE) {
1501                         *tokflag = NO_TOKEN_SEND;
1502                         return ret;
1503                 }
1504                 ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1505                                       negState, tokflag);
1506                 if (ret != GSS_S_COMPLETE)
1507                         return ret;
1508         }
1509
1510         mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
1511         ret = gss_accept_sec_context(minor_status,
1512                                      &sc->ctx_handle,
1513                                      mcred,
1514                                      mechtok_in,
1515                                      GSS_C_NO_CHANNEL_BINDINGS,
1516                                      &sc->internal_name,
1517                                      mech_type,
1518                                      mechtok_out,
1519                                      &sc->ctx_flags,
1520                                      time_rec,
1521                                      delegated_cred_handle);
1522         if (ret == GSS_S_COMPLETE) {
1523 #ifdef MS_BUG_TEST
1524                 /*
1525                  * Force MIC to be not required even if we previously
1526                  * requested a MIC.
1527                  */
1528                 char *envstr = getenv("MS_FORCE_NO_MIC");
1529
1530                 if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1531                     !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1532                     sc->mic_reqd) {
1533
1534                         sc->mic_reqd = 0;
1535                 }
1536 #endif
1537                 sc->mech_complete = 1;
1538                 if (ret_flags != NULL)
1539                         *ret_flags = sc->ctx_flags;
1540
1541                 if (!sc->mic_reqd ||
1542                     !(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1543                         /* No MIC exchange required, so we're done. */
1544                         *negState = ACCEPT_COMPLETE;
1545                         ret = GSS_S_COMPLETE;
1546                 } else {
1547                         /* handle_mic will decide if we're done. */
1548                         ret = GSS_S_CONTINUE_NEEDED;
1549                 }
1550         } else if (ret != GSS_S_CONTINUE_NEEDED) {
1551                 *negState = REJECT;
1552                 *tokflag = ERROR_TOKEN_SEND;
1553         }
1554         return ret;
1555 }
1556
1557 /*ARGSUSED*/
1558 OM_uint32
1559 spnego_gss_accept_sec_context(
1560                             OM_uint32 *minor_status,
1561                             gss_ctx_id_t *context_handle,
1562                             gss_cred_id_t verifier_cred_handle,
1563                             gss_buffer_t input_token,
1564                             gss_channel_bindings_t input_chan_bindings,
1565                             gss_name_t *src_name,
1566                             gss_OID *mech_type,
1567                             gss_buffer_t output_token,
1568                             OM_uint32 *ret_flags,
1569                             OM_uint32 *time_rec,
1570                             gss_cred_id_t *delegated_cred_handle)
1571 {
1572         OM_uint32 ret, tmpmin, negState;
1573         send_token_flag return_token;
1574         gss_buffer_t mechtok_in, mic_in, mic_out;
1575         gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1576         spnego_gss_ctx_id_t sc = NULL;
1577         spnego_gss_cred_id_t spcred = NULL;
1578         OM_uint32 mechstat = GSS_S_FAILURE;
1579         int sendTokenInit = 0, tmpret;
1580
1581         mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1582
1583         if (minor_status != NULL)
1584                 *minor_status = 0;
1585         if (output_token != GSS_C_NO_BUFFER) {
1586                 output_token->length = 0;
1587                 output_token->value = NULL;
1588         }
1589
1590         if (minor_status == NULL ||
1591             output_token == GSS_C_NO_BUFFER ||
1592             context_handle == NULL)
1593                 return GSS_S_CALL_INACCESSIBLE_WRITE;
1594
1595         if (input_token == GSS_C_NO_BUFFER)
1596                 return GSS_S_CALL_INACCESSIBLE_READ;
1597
1598         sc = (spnego_gss_ctx_id_t)*context_handle;
1599         spcred = (spnego_gss_cred_id_t)verifier_cred_handle;
1600         if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1601                 if (src_name != NULL)
1602                         *src_name = GSS_C_NO_NAME;
1603                 if (mech_type != NULL)
1604                         *mech_type = GSS_C_NO_OID;
1605                 if (time_rec != NULL)
1606                         *time_rec = 0;
1607                 if (ret_flags != NULL)
1608                         *ret_flags = 0;
1609                 if (delegated_cred_handle != NULL)
1610                         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1611                 if (input_token->length == 0) {
1612                         ret = acc_ctx_hints(minor_status,
1613                                             context_handle, spcred,
1614                                             &mic_out,
1615                                             &negState,
1616                                             &return_token);
1617                         if (ret != GSS_S_COMPLETE)
1618                                 goto cleanup;
1619                         sendTokenInit = 1;
1620                         ret = GSS_S_CONTINUE_NEEDED;
1621                 } else {
1622                         /* Can set negState to REQUEST_MIC */
1623                         ret = acc_ctx_new(minor_status, input_token,
1624                                           context_handle, spcred,
1625                                           &mechtok_in, &mic_in,
1626                                           &negState, &return_token);
1627                         if (ret != GSS_S_COMPLETE)
1628                                 goto cleanup;
1629                         ret = GSS_S_CONTINUE_NEEDED;
1630                 }
1631         } else {
1632                 /* Can set negState to ACCEPT_INCOMPLETE */
1633                 ret = acc_ctx_cont(minor_status, input_token,
1634                                    context_handle, &mechtok_in,
1635                                    &mic_in, &negState, &return_token);
1636                 if (ret != GSS_S_COMPLETE)
1637                         goto cleanup;
1638                 ret = GSS_S_CONTINUE_NEEDED;
1639         }
1640         sc = (spnego_gss_ctx_id_t)*context_handle;
1641         /*
1642          * Handle mechtok_in and mic_in only if they are
1643          * present in input_token.  If neither is present, whether
1644          * this is an error depends on whether this is the first
1645          * round-trip.  RET is set to a default value according to
1646          * whether it is the first round-trip.
1647          */
1648         mechstat = GSS_S_FAILURE;
1649         if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1650                 ret = acc_ctx_call_acc(minor_status, sc, spcred,
1651                                        mechtok_in, mech_type, &mechtok_out,
1652                                        ret_flags, time_rec,
1653                                        delegated_cred_handle,
1654                                        &negState, &return_token);
1655         } else if (negState == REQUEST_MIC) {
1656                 mechstat = GSS_S_CONTINUE_NEEDED;
1657         }
1658         if (!HARD_ERROR(ret) && sc->mech_complete &&
1659             (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1660
1661                 ret = handle_mic(minor_status, mic_in,
1662                                  (mechtok_out.length != 0),
1663                                  sc, &mic_out,
1664                                  &negState, &return_token);
1665         }
1666 cleanup:
1667         if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1668                 assert(sc != NULL);
1669                 tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1670                                                    GSS_C_NO_BUFFER,
1671                                                    return_token, output_token);
1672                 if (tmpret < 0)
1673                         ret = GSS_S_FAILURE;
1674         } else if (return_token != NO_TOKEN_SEND &&
1675                    return_token != CHECK_MIC) {
1676                 tmpret = make_spnego_tokenTarg_msg(negState,
1677                                                    sc ? sc->internal_mech :
1678                                                    GSS_C_NO_OID,
1679                                                    &mechtok_out, mic_out,
1680                                                    return_token,
1681                                                    output_token);
1682                 if (tmpret < 0)
1683                         ret = GSS_S_FAILURE;
1684         }
1685         if (ret == GSS_S_COMPLETE) {
1686                 *context_handle = (gss_ctx_id_t)sc->ctx_handle;
1687                 if (sc->internal_name != GSS_C_NO_NAME &&
1688                     src_name != NULL) {
1689                         *src_name = sc->internal_name;
1690                         sc->internal_name = GSS_C_NO_NAME;
1691                 }
1692                 release_spnego_ctx(&sc);
1693         } else if (ret != GSS_S_CONTINUE_NEEDED) {
1694                 if (sc != NULL) {
1695                         gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
1696                                                GSS_C_NO_BUFFER);
1697                         release_spnego_ctx(&sc);
1698                 }
1699                 *context_handle = GSS_C_NO_CONTEXT;
1700         }
1701         gss_release_buffer(&tmpmin, &mechtok_out);
1702         if (mechtok_in != GSS_C_NO_BUFFER) {
1703                 gss_release_buffer(&tmpmin, mechtok_in);
1704                 free(mechtok_in);
1705         }
1706         if (mic_in != GSS_C_NO_BUFFER) {
1707                 gss_release_buffer(&tmpmin, mic_in);
1708                 free(mic_in);
1709         }
1710         if (mic_out != GSS_C_NO_BUFFER) {
1711                 gss_release_buffer(&tmpmin, mic_out);
1712                 free(mic_out);
1713         }
1714         return ret;
1715 }
1716 #endif /*  LEAN_CLIENT */
1717
1718
1719 /*ARGSUSED*/
1720 OM_uint32
1721 spnego_gss_display_status(
1722                 OM_uint32 *minor_status,
1723                 OM_uint32 status_value,
1724                 int status_type,
1725                 gss_OID mech_type,
1726                 OM_uint32 *message_context,
1727                 gss_buffer_t status_string)
1728 {
1729         dsyslog("Entering display_status\n");
1730
1731         *message_context = 0;
1732         switch (status_value) {
1733             case ERR_SPNEGO_NO_MECHS_AVAILABLE:
1734                 /* CSTYLED */
1735                 *status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate");
1736                 break;
1737             case ERR_SPNEGO_NO_CREDS_ACQUIRED:
1738                 /* CSTYLED */
1739                 *status_string = make_err_msg("SPNEGO failed to acquire creds");
1740                 break;
1741             case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
1742                 /* CSTYLED */
1743                 *status_string = make_err_msg("SPNEGO acceptor did not select a mechanism");
1744                 break;
1745             case ERR_SPNEGO_NEGOTIATION_FAILED:
1746                 /* CSTYLED */
1747                 *status_string = make_err_msg("SPNEGO failed to negotiate a mechanism");
1748                 break;
1749             case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
1750                 /* CSTYLED */
1751                 *status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
1752                 break;
1753             default:
1754                 status_string->length = 0;
1755                 status_string->value = "";
1756                 break;
1757         }
1758
1759         dsyslog("Leaving display_status\n");
1760         return (GSS_S_COMPLETE);
1761 }
1762
1763
1764 /*ARGSUSED*/
1765 OM_uint32
1766 spnego_gss_import_name(
1767                     OM_uint32 *minor_status,
1768                     gss_buffer_t input_name_buffer,
1769                     gss_OID input_name_type,
1770                     gss_name_t *output_name)
1771 {
1772         OM_uint32 status;
1773
1774         dsyslog("Entering import_name\n");
1775
1776         status = gss_import_name(minor_status, input_name_buffer,
1777                         input_name_type, output_name);
1778
1779         dsyslog("Leaving import_name\n");
1780         return (status);
1781 }
1782
1783 /*ARGSUSED*/
1784 OM_uint32
1785 spnego_gss_release_name(
1786                         OM_uint32 *minor_status,
1787                         gss_name_t *input_name)
1788 {
1789         OM_uint32 status;
1790
1791         dsyslog("Entering release_name\n");
1792
1793         status = gss_release_name(minor_status, input_name);
1794
1795         dsyslog("Leaving release_name\n");
1796         return (status);
1797 }
1798
1799 OM_uint32
1800 spnego_gss_inquire_cred(
1801                         OM_uint32 *minor_status,
1802                         gss_cred_id_t cred_handle,
1803                         gss_name_t *name,
1804                         OM_uint32 *lifetime,
1805                         int *cred_usage,
1806                         gss_OID_set *mechanisms)
1807 {
1808         OM_uint32 status;
1809         spnego_gss_cred_id_t spcred = NULL;
1810         gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
1811         OM_uint32 tmp_minor_status;
1812         OM_uint32 initiator_lifetime, acceptor_lifetime;
1813
1814         dsyslog("Entering inquire_cred\n");
1815
1816         /*
1817          * To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is
1818          * supplied we call gss_inquire_cred_by_mech() on the
1819          * first non-SPNEGO mechanism.
1820          */
1821         spcred = (spnego_gss_cred_id_t)cred_handle;
1822         if (spcred == NULL) {
1823                 status = get_available_mechs(minor_status,
1824                         GSS_C_NO_NAME,
1825                         GSS_C_BOTH,
1826                         &creds,
1827                         mechanisms);
1828                 if (status != GSS_S_COMPLETE) {
1829                         dsyslog("Leaving inquire_cred\n");
1830                         return (status);
1831                 }
1832
1833                 if ((*mechanisms)->count == 0) {
1834                         gss_release_cred(&tmp_minor_status, &creds);
1835                         gss_release_oid_set(&tmp_minor_status, mechanisms);
1836                         dsyslog("Leaving inquire_cred\n");
1837                         return (GSS_S_DEFECTIVE_CREDENTIAL);
1838                 }
1839
1840                 assert((*mechanisms)->elements != NULL);
1841
1842                 status = gss_inquire_cred_by_mech(minor_status,
1843                         creds,
1844                         &(*mechanisms)->elements[0],
1845                         name,
1846                         &initiator_lifetime,
1847                         &acceptor_lifetime,
1848                         cred_usage);
1849                 if (status != GSS_S_COMPLETE) {
1850                         gss_release_cred(&tmp_minor_status, &creds);
1851                         dsyslog("Leaving inquire_cred\n");
1852                         return (status);
1853                 }
1854
1855                 if (lifetime != NULL)
1856                         *lifetime = (*cred_usage == GSS_C_ACCEPT) ?
1857                                 acceptor_lifetime : initiator_lifetime;
1858
1859                 gss_release_cred(&tmp_minor_status, &creds);
1860         } else {
1861                 status = gss_inquire_cred(minor_status, spcred->mcred,
1862                                           name, lifetime,
1863                                           cred_usage, mechanisms);
1864         }
1865
1866         dsyslog("Leaving inquire_cred\n");
1867
1868         return (status);
1869 }
1870
1871 /*ARGSUSED*/
1872 OM_uint32
1873 spnego_gss_compare_name(
1874                         OM_uint32 *minor_status,
1875                         const gss_name_t name1,
1876                         const gss_name_t name2,
1877                         int *name_equal)
1878 {
1879         OM_uint32 status = GSS_S_COMPLETE;
1880         dsyslog("Entering compare_name\n");
1881
1882         status = gss_compare_name(minor_status, name1, name2, name_equal);
1883
1884         dsyslog("Leaving compare_name\n");
1885         return (status);
1886 }
1887
1888 /*ARGSUSED*/
1889 /*ARGSUSED*/
1890 OM_uint32
1891 spnego_gss_display_name(
1892                         OM_uint32 *minor_status,
1893                         gss_name_t input_name,
1894                         gss_buffer_t output_name_buffer,
1895                         gss_OID *output_name_type)
1896 {
1897         OM_uint32 status = GSS_S_COMPLETE;
1898         dsyslog("Entering display_name\n");
1899
1900         status = gss_display_name(minor_status, input_name,
1901                         output_name_buffer, output_name_type);
1902
1903         dsyslog("Leaving display_name\n");
1904         return (status);
1905 }
1906
1907
1908 /*ARGSUSED*/
1909 OM_uint32
1910 spnego_gss_inquire_names_for_mech(
1911                                 OM_uint32       *minor_status,
1912                                 gss_OID         mechanism,
1913                                 gss_OID_set     *name_types)
1914 {
1915         OM_uint32   major, minor;
1916
1917         dsyslog("Entering inquire_names_for_mech\n");
1918         /*
1919          * We only know how to handle our own mechanism.
1920          */
1921         if ((mechanism != GSS_C_NULL_OID) &&
1922             !g_OID_equal(gss_mech_spnego, mechanism)) {
1923                 *minor_status = 0;
1924                 return (GSS_S_FAILURE);
1925         }
1926
1927         major = gss_create_empty_oid_set(minor_status, name_types);
1928         if (major == GSS_S_COMPLETE) {
1929                 /* Now add our members. */
1930                 if (((major = gss_add_oid_set_member(minor_status,
1931                                 (gss_OID) GSS_C_NT_USER_NAME,
1932                                 name_types)) == GSS_S_COMPLETE) &&
1933                     ((major = gss_add_oid_set_member(minor_status,
1934                                 (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
1935                                 name_types)) == GSS_S_COMPLETE) &&
1936                     ((major = gss_add_oid_set_member(minor_status,
1937                                 (gss_OID) GSS_C_NT_STRING_UID_NAME,
1938                                 name_types)) == GSS_S_COMPLETE)) {
1939                         major = gss_add_oid_set_member(minor_status,
1940                                 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
1941                                 name_types);
1942                 }
1943
1944                 if (major != GSS_S_COMPLETE)
1945                         (void) gss_release_oid_set(&minor, name_types);
1946         }
1947
1948         dsyslog("Leaving inquire_names_for_mech\n");
1949         return (major);
1950 }
1951
1952 OM_uint32
1953 spnego_gss_unwrap(
1954                 OM_uint32 *minor_status,
1955                 gss_ctx_id_t context_handle,
1956                 gss_buffer_t input_message_buffer,
1957                 gss_buffer_t output_message_buffer,
1958                 int *conf_state,
1959                 gss_qop_t *qop_state)
1960 {
1961         OM_uint32 ret;
1962         ret = gss_unwrap(minor_status,
1963                         context_handle,
1964                         input_message_buffer,
1965                         output_message_buffer,
1966                         conf_state,
1967                         qop_state);
1968
1969         return (ret);
1970 }
1971
1972 OM_uint32
1973 spnego_gss_wrap(
1974                 OM_uint32 *minor_status,
1975                 gss_ctx_id_t context_handle,
1976                 int conf_req_flag,
1977                 gss_qop_t qop_req,
1978                 gss_buffer_t input_message_buffer,
1979                 int *conf_state,
1980                 gss_buffer_t output_message_buffer)
1981 {
1982         OM_uint32 ret;
1983         ret = gss_wrap(minor_status,
1984                     context_handle,
1985                     conf_req_flag,
1986                     qop_req,
1987                     input_message_buffer,
1988                     conf_state,
1989                     output_message_buffer);
1990
1991         return (ret);
1992 }
1993
1994 OM_uint32
1995 spnego_gss_process_context_token(
1996                                 OM_uint32       *minor_status,
1997                                 const gss_ctx_id_t context_handle,
1998                                 const gss_buffer_t token_buffer)
1999 {
2000         OM_uint32 ret;
2001         ret = gss_process_context_token(minor_status,
2002                                         context_handle,
2003                                         token_buffer);
2004
2005         return (ret);
2006 }
2007
2008 OM_uint32
2009 spnego_gss_delete_sec_context(
2010                             OM_uint32 *minor_status,
2011                             gss_ctx_id_t *context_handle,
2012                             gss_buffer_t output_token)
2013 {
2014         OM_uint32 ret = GSS_S_COMPLETE;
2015         spnego_gss_ctx_id_t *ctx =
2016                     (spnego_gss_ctx_id_t *)context_handle;
2017
2018         if (context_handle == NULL)
2019                 return (GSS_S_FAILURE);
2020
2021         /*
2022          * If this is still an SPNEGO mech, release it locally.
2023          */
2024         if (*ctx != NULL &&
2025             (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
2026                 (void) gss_delete_sec_context(minor_status,
2027                                     &(*ctx)->ctx_handle,
2028                                     output_token);
2029                 (void) release_spnego_ctx(ctx);
2030         } else {
2031                 ret = gss_delete_sec_context(minor_status,
2032                                     context_handle,
2033                                     output_token);
2034         }
2035
2036         return (ret);
2037 }
2038
2039 OM_uint32
2040 spnego_gss_context_time(
2041                         OM_uint32       *minor_status,
2042                         const gss_ctx_id_t context_handle,
2043                         OM_uint32       *time_rec)
2044 {
2045         OM_uint32 ret;
2046         ret = gss_context_time(minor_status,
2047                             context_handle,
2048                             time_rec);
2049         return (ret);
2050 }
2051 #ifndef LEAN_CLIENT
2052 OM_uint32
2053 spnego_gss_export_sec_context(
2054                             OM_uint32     *minor_status,
2055                             gss_ctx_id_t *context_handle,
2056                             gss_buffer_t interprocess_token)
2057 {
2058         OM_uint32 ret;
2059         ret = gss_export_sec_context(minor_status,
2060                                     context_handle,
2061                                     interprocess_token);
2062         return (ret);
2063 }
2064
2065 OM_uint32
2066 spnego_gss_import_sec_context(
2067         OM_uint32               *minor_status,
2068         const gss_buffer_t      interprocess_token,
2069         gss_ctx_id_t            *context_handle)
2070 {
2071         OM_uint32 ret;
2072         ret = gss_import_sec_context(minor_status,
2073                                     interprocess_token,
2074                                     context_handle);
2075         return (ret);
2076 }
2077 #endif /* LEAN_CLIENT */
2078
2079 OM_uint32
2080 spnego_gss_inquire_context(
2081                         OM_uint32       *minor_status,
2082                         const gss_ctx_id_t context_handle,
2083                         gss_name_t      *src_name,
2084                         gss_name_t      *targ_name,
2085                         OM_uint32       *lifetime_rec,
2086                         gss_OID         *mech_type,
2087                         OM_uint32       *ctx_flags,
2088                         int             *locally_initiated,
2089                         int             *opened)
2090 {
2091         OM_uint32 ret = GSS_S_COMPLETE;
2092
2093         ret = gss_inquire_context(minor_status,
2094                                 context_handle,
2095                                 src_name,
2096                                 targ_name,
2097                                 lifetime_rec,
2098                                 mech_type,
2099                                 ctx_flags,
2100                                 locally_initiated,
2101                                 opened);
2102
2103         return (ret);
2104 }
2105
2106 OM_uint32
2107 spnego_gss_wrap_size_limit(
2108         OM_uint32       *minor_status,
2109         const gss_ctx_id_t context_handle,
2110         int             conf_req_flag,
2111         gss_qop_t       qop_req,
2112         OM_uint32       req_output_size,
2113         OM_uint32       *max_input_size)
2114 {
2115         OM_uint32 ret;
2116         ret = gss_wrap_size_limit(minor_status,
2117                                 context_handle,
2118                                 conf_req_flag,
2119                                 qop_req,
2120                                 req_output_size,
2121                                 max_input_size);
2122         return (ret);
2123 }
2124
2125 OM_uint32
2126 spnego_gss_get_mic(
2127                 OM_uint32 *minor_status,
2128                 const gss_ctx_id_t context_handle,
2129                 gss_qop_t  qop_req,
2130                 const gss_buffer_t message_buffer,
2131                 gss_buffer_t message_token)
2132 {
2133         OM_uint32 ret;
2134         ret = gss_get_mic(minor_status,
2135                     context_handle,
2136                     qop_req,
2137                     message_buffer,
2138                     message_token);
2139         return (ret);
2140 }
2141
2142 OM_uint32
2143 spnego_gss_verify_mic(
2144                 OM_uint32 *minor_status,
2145                 const gss_ctx_id_t context_handle,
2146                 const gss_buffer_t msg_buffer,
2147                 const gss_buffer_t token_buffer,
2148                 gss_qop_t *qop_state)
2149 {
2150         OM_uint32 ret;
2151         ret = gss_verify_mic(minor_status,
2152                             context_handle,
2153                             msg_buffer,
2154                             token_buffer,
2155                             qop_state);
2156         return (ret);
2157 }
2158
2159 OM_uint32
2160 spnego_gss_inquire_sec_context_by_oid(
2161                 OM_uint32 *minor_status,
2162                 const gss_ctx_id_t context_handle,
2163                 const gss_OID desired_object,
2164                 gss_buffer_set_t *data_set)
2165 {
2166         OM_uint32 ret;
2167         ret = gss_inquire_sec_context_by_oid(minor_status,
2168                             context_handle,
2169                             desired_object,
2170                             data_set);
2171         return (ret);
2172 }
2173
2174 OM_uint32
2175 spnego_gss_inquire_cred_by_oid(
2176                 OM_uint32 *minor_status,
2177                 const gss_cred_id_t cred_handle,
2178                 const gss_OID desired_object,
2179                 gss_buffer_set_t *data_set)
2180 {
2181         OM_uint32 ret;
2182         spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2183         gss_cred_id_t mcred;
2184         mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2185         ret = gss_inquire_cred_by_oid(minor_status,
2186                                 mcred,
2187                                 desired_object,
2188                                 data_set);
2189         return (ret);
2190 }
2191
2192 OM_uint32
2193 spnego_gss_set_cred_option(
2194                 OM_uint32 *minor_status,
2195                 gss_cred_id_t cred_handle,
2196                 const gss_OID desired_object,
2197                 const gss_buffer_t value)
2198 {
2199         OM_uint32 ret;
2200         spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2201         gss_cred_id_t mcred;
2202         mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2203         ret = gssspi_set_cred_option(minor_status,
2204                                      mcred,
2205                                      desired_object,
2206                                      value);
2207         return (ret);
2208 }
2209
2210 OM_uint32
2211 spnego_gss_set_sec_context_option(
2212                 OM_uint32 *minor_status,
2213                 gss_ctx_id_t *context_handle,
2214                 const gss_OID desired_object,
2215                 const gss_buffer_t value)
2216 {
2217         OM_uint32 ret;
2218         ret = gss_set_sec_context_option(minor_status,
2219                             context_handle,
2220                             desired_object,
2221                             value);
2222         return (ret);
2223 }
2224
2225 OM_uint32
2226 spnego_gss_wrap_aead(OM_uint32 *minor_status,
2227                      gss_ctx_id_t context_handle,
2228                      int conf_req_flag,
2229                      gss_qop_t qop_req,
2230                      gss_buffer_t input_assoc_buffer,
2231                      gss_buffer_t input_payload_buffer,
2232                      int *conf_state,
2233                      gss_buffer_t output_message_buffer)
2234 {
2235         OM_uint32 ret;
2236         ret = gss_wrap_aead(minor_status,
2237                             context_handle,
2238                             conf_req_flag,
2239                             qop_req,
2240                             input_assoc_buffer,
2241                             input_payload_buffer,
2242                             conf_state,
2243                             output_message_buffer);
2244
2245         return (ret);
2246 }
2247
2248 OM_uint32
2249 spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2250                        gss_ctx_id_t context_handle,
2251                        gss_buffer_t input_message_buffer,
2252                        gss_buffer_t input_assoc_buffer,
2253                        gss_buffer_t output_payload_buffer,
2254                        int *conf_state,
2255                        gss_qop_t *qop_state)
2256 {
2257         OM_uint32 ret;
2258         ret = gss_unwrap_aead(minor_status,
2259                               context_handle,
2260                               input_message_buffer,
2261                               input_assoc_buffer,
2262                               output_payload_buffer,
2263                               conf_state,
2264                               qop_state);
2265         return (ret);
2266 }
2267
2268 OM_uint32
2269 spnego_gss_wrap_iov(OM_uint32 *minor_status,
2270                     gss_ctx_id_t context_handle,
2271                     int conf_req_flag,
2272                     gss_qop_t qop_req,
2273                     int *conf_state,
2274                     gss_iov_buffer_desc *iov,
2275                     int iov_count)
2276 {
2277         OM_uint32 ret;
2278         ret = gss_wrap_iov(minor_status,
2279                            context_handle,
2280                            conf_req_flag,
2281                            qop_req,
2282                            conf_state,
2283                            iov,
2284                            iov_count);
2285         return (ret);
2286 }
2287
2288 OM_uint32
2289 spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2290                       gss_ctx_id_t context_handle,
2291                       int *conf_state,
2292                       gss_qop_t *qop_state,
2293                       gss_iov_buffer_desc *iov,
2294                       int iov_count)
2295 {
2296         OM_uint32 ret;
2297         ret = gss_unwrap_iov(minor_status,
2298                              context_handle,
2299                              conf_state,
2300                              qop_state,
2301                              iov,
2302                              iov_count);
2303         return (ret);
2304 }
2305
2306 OM_uint32
2307 spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2308                            gss_ctx_id_t context_handle,
2309                            int conf_req_flag,
2310                            gss_qop_t qop_req,
2311                            int *conf_state,
2312                            gss_iov_buffer_desc *iov,
2313                            int iov_count)
2314 {
2315         OM_uint32 ret;
2316         ret = gss_wrap_iov_length(minor_status,
2317                                   context_handle,
2318                                   conf_req_flag,
2319                                   qop_req,
2320                                   conf_state,
2321                                   iov,
2322                                   iov_count);
2323         return (ret);
2324 }
2325
2326
2327 OM_uint32
2328 spnego_gss_complete_auth_token(
2329                 OM_uint32 *minor_status,
2330                 const gss_ctx_id_t context_handle,
2331                 gss_buffer_t input_message_buffer)
2332 {
2333         OM_uint32 ret;
2334         ret = gss_complete_auth_token(minor_status,
2335                                       context_handle,
2336                                       input_message_buffer);
2337         return (ret);
2338 }
2339
2340 OM_uint32
2341 spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
2342                                          const gss_cred_id_t impersonator_cred_handle,
2343                                          gss_name_t desired_name,
2344                                          OM_uint32 time_req,
2345                                          gss_OID_set desired_mechs,
2346                                          gss_cred_usage_t cred_usage,
2347                                          gss_cred_id_t *output_cred_handle,
2348                                          gss_OID_set *actual_mechs,
2349                                          OM_uint32 *time_rec)
2350 {
2351         OM_uint32 status;
2352         gss_OID_set amechs = GSS_C_NULL_OID_SET;
2353         spnego_gss_cred_id_t imp_spcred = NULL, out_spcred = NULL;
2354         gss_cred_id_t mcred;
2355
2356         dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n");
2357
2358         if (actual_mechs)
2359                 *actual_mechs = NULL;
2360
2361         if (time_rec)
2362                 *time_rec = 0;
2363
2364         if (desired_mechs == GSS_C_NO_OID_SET) {
2365                 status = gss_inquire_cred(minor_status,
2366                                           impersonator_cred_handle,
2367                                           NULL, NULL,
2368                                           NULL, &amechs);
2369                 if (status != GSS_S_COMPLETE)
2370                         return status;
2371
2372                 desired_mechs = amechs;
2373         }
2374
2375         imp_spcred = (spnego_gss_cred_id_t)impersonator_cred_handle;
2376         status = gss_acquire_cred_impersonate_name(minor_status,
2377                         imp_spcred ? imp_spcred->mcred : GSS_C_NO_CREDENTIAL,
2378                         desired_name, time_req,
2379                         desired_mechs, cred_usage,
2380                         &mcred, actual_mechs,
2381                         time_rec);
2382
2383         if (amechs != GSS_C_NULL_OID_SET)
2384                 (void) gss_release_oid_set(minor_status, &amechs);
2385
2386         out_spcred = malloc(sizeof(spnego_gss_cred_id_rec));
2387         if (out_spcred == NULL) {
2388                 gss_release_cred(minor_status, &mcred);
2389                 *minor_status = ENOMEM;
2390                 return (GSS_S_FAILURE);
2391         }
2392         out_spcred->mcred = mcred;
2393         out_spcred->neg_mechs = GSS_C_NULL_OID_SET;
2394         *output_cred_handle = (gss_cred_id_t)out_spcred;
2395
2396         dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n");
2397         return (status);
2398 }
2399
2400 OM_uint32
2401 spnego_gss_display_name_ext(OM_uint32 *minor_status,
2402                             gss_name_t name,
2403                             gss_OID display_as_name_type,
2404                             gss_buffer_t display_name)
2405 {
2406         OM_uint32 ret;
2407         ret = gss_display_name_ext(minor_status,
2408                                    name,
2409                                    display_as_name_type,
2410                                    display_name);
2411         return (ret);
2412 }
2413
2414
2415 OM_uint32
2416 spnego_gss_inquire_name(OM_uint32 *minor_status,
2417                         gss_name_t name,
2418                         int *name_is_MN,
2419                         gss_OID *MN_mech,
2420                         gss_buffer_set_t *attrs)
2421 {
2422         OM_uint32 ret;
2423         ret = gss_inquire_name(minor_status,
2424                                name,
2425                                name_is_MN,
2426                                MN_mech,
2427                                attrs);
2428         return (ret);
2429 }
2430
2431 OM_uint32
2432 spnego_gss_get_name_attribute(OM_uint32 *minor_status,
2433                               gss_name_t name,
2434                               gss_buffer_t attr,
2435                               int *authenticated,
2436                               int *complete,
2437                               gss_buffer_t value,
2438                               gss_buffer_t display_value,
2439                               int *more)
2440 {
2441         OM_uint32 ret;
2442         ret = gss_get_name_attribute(minor_status,
2443                                      name,
2444                                      attr,
2445                                      authenticated,
2446                                      complete,
2447                                      value,
2448                                      display_value,
2449                                      more);
2450         return (ret);
2451 }
2452
2453 OM_uint32
2454 spnego_gss_set_name_attribute(OM_uint32 *minor_status,
2455                               gss_name_t name,
2456                               int complete,
2457                               gss_buffer_t attr,
2458                               gss_buffer_t value)
2459 {
2460         OM_uint32 ret;
2461         ret = gss_set_name_attribute(minor_status,
2462                                      name,
2463                                      complete,
2464                                      attr,
2465                                      value);
2466         return (ret);
2467 }
2468
2469 OM_uint32
2470 spnego_gss_delete_name_attribute(OM_uint32 *minor_status,
2471                                  gss_name_t name,
2472                                  gss_buffer_t attr)
2473 {
2474         OM_uint32 ret;
2475         ret = gss_delete_name_attribute(minor_status,
2476                                         name,
2477                                         attr);
2478         return (ret);
2479 }
2480
2481 OM_uint32
2482 spnego_gss_export_name_composite(OM_uint32 *minor_status,
2483                                  gss_name_t name,
2484                                  gss_buffer_t exp_composite_name)
2485 {
2486         OM_uint32 ret;
2487         ret = gss_export_name_composite(minor_status,
2488                                         name,
2489                                         exp_composite_name);
2490         return (ret);
2491 }
2492
2493 OM_uint32
2494 spnego_gss_map_name_to_any(OM_uint32 *minor_status,
2495                            gss_name_t name,
2496                            int authenticated,
2497                            gss_buffer_t type_id,
2498                            gss_any_t *output)
2499 {
2500         OM_uint32 ret;
2501         ret = gss_map_name_to_any(minor_status,
2502                                   name,
2503                                   authenticated,
2504                                   type_id,
2505                                   output);
2506         return (ret);
2507 }
2508
2509 OM_uint32
2510 spnego_gss_release_any_name_mapping(OM_uint32 *minor_status,
2511                                     gss_name_t name,
2512                                     gss_buffer_t type_id,
2513                                     gss_any_t *input)
2514 {
2515         OM_uint32 ret;
2516         ret = gss_release_any_name_mapping(minor_status,
2517                                            name,
2518                                            type_id,
2519                                            input);
2520         return (ret);
2521 }
2522
2523 OM_uint32
2524 spnego_gss_pseudo_random(OM_uint32 *minor_status,
2525                          gss_ctx_id_t context,
2526                          int prf_key,
2527                          const gss_buffer_t prf_in,
2528                          ssize_t desired_output_len,
2529                          gss_buffer_t prf_out)
2530 {
2531         OM_uint32 ret;
2532         ret = gss_pseudo_random(minor_status,
2533                                 context,
2534                                 prf_key,
2535                                 prf_in,
2536                                 desired_output_len,
2537                                 prf_out);
2538         return (ret);
2539 }
2540
2541 OM_uint32
2542 spnego_gss_set_neg_mechs(OM_uint32 *minor_status,
2543                          gss_cred_id_t cred_handle,
2544                          const gss_OID_set mech_list)
2545 {
2546         OM_uint32 ret;
2547         spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2548
2549         /* Store mech_list in spcred for use in negotiation logic. */
2550         gss_release_oid_set(minor_status, &spcred->neg_mechs);
2551         ret = generic_gss_copy_oid_set(minor_status, mech_list,
2552                                        &spcred->neg_mechs);
2553         return (ret);
2554 }
2555
2556 /*
2557  * We will release everything but the ctx_handle so that it
2558  * can be passed back to init/accept context. This routine should
2559  * not be called until after the ctx_handle memory is assigned to
2560  * the supplied context handle from init/accept context.
2561  */
2562 static void
2563 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
2564 {
2565         spnego_gss_ctx_id_t context;
2566         OM_uint32 minor_stat;
2567         context = *ctx;
2568
2569         if (context != NULL) {
2570                 (void) gss_release_buffer(&minor_stat,
2571                                         &context->DER_mechTypes);
2572
2573                 (void) generic_gss_release_oid(&minor_stat,
2574                                 &context->internal_mech);
2575
2576                 (void) gss_release_name(&minor_stat, &context->internal_name);
2577
2578                 if (context->optionStr != NULL) {
2579                         free(context->optionStr);
2580                         context->optionStr = NULL;
2581                 }
2582                 free(context);
2583                 *ctx = NULL;
2584         }
2585 }
2586
2587 /*
2588  * Can't use gss_indicate_mechs by itself to get available mechs for
2589  * SPNEGO because it will also return the SPNEGO mech and we do not
2590  * want to consider SPNEGO as an available security mech for
2591  * negotiation. For this reason, get_available_mechs will return
2592  * all available mechs except SPNEGO.
2593  *
2594  * If a ptr to a creds list is given, this function will attempt
2595  * to acquire creds for the creds given and trim the list of
2596  * returned mechanisms to only those for which creds are valid.
2597  *
2598  */
2599 static OM_uint32
2600 get_available_mechs(OM_uint32 *minor_status,
2601         gss_name_t name, gss_cred_usage_t usage,
2602         gss_cred_id_t *creds, gss_OID_set *rmechs)
2603 {
2604         unsigned int    i;
2605         int             found = 0;
2606         OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
2607         gss_OID_set mechs, goodmechs;
2608
2609         major_status = gss_indicate_mechs(minor_status, &mechs);
2610
2611         if (major_status != GSS_S_COMPLETE) {
2612                 return (major_status);
2613         }
2614
2615         major_status = gss_create_empty_oid_set(minor_status, rmechs);
2616
2617         if (major_status != GSS_S_COMPLETE) {
2618                 (void) gss_release_oid_set(minor_status, &mechs);
2619                 return (major_status);
2620         }
2621
2622         for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
2623                 if ((mechs->elements[i].length
2624                     != spnego_mechanism.mech_type.length) ||
2625                     memcmp(mechs->elements[i].elements,
2626                         spnego_mechanism.mech_type.elements,
2627                         spnego_mechanism.mech_type.length)) {
2628
2629                         major_status = gss_add_oid_set_member(minor_status,
2630                                                               &mechs->elements[i],
2631                                                               rmechs);
2632                         if (major_status == GSS_S_COMPLETE)
2633                                 found++;
2634                 }
2635         }
2636
2637         /*
2638          * If the caller wanted a list of creds returned,
2639          * trim the list of mechanisms down to only those
2640          * for which the creds are valid.
2641          */
2642         if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
2643                 major_status = gss_acquire_cred(minor_status,
2644                                                 name, GSS_C_INDEFINITE,
2645                                                 *rmechs, usage, creds,
2646                                                 &goodmechs, NULL);
2647
2648                 /*
2649                  * Drop the old list in favor of the new
2650                  * "trimmed" list.
2651                  */
2652                 (void) gss_release_oid_set(&tmpmin, rmechs);
2653                 if (major_status == GSS_S_COMPLETE) {
2654                         (void) gssint_copy_oid_set(&tmpmin,
2655                                         goodmechs, rmechs);
2656                         (void) gss_release_oid_set(&tmpmin, &goodmechs);
2657                 }
2658         }
2659
2660         (void) gss_release_oid_set(&tmpmin, &mechs);
2661         if (found == 0 || major_status != GSS_S_COMPLETE) {
2662                 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2663                 map_errcode(minor_status);
2664                 if (major_status == GSS_S_COMPLETE)
2665                         major_status = GSS_S_FAILURE;
2666         }
2667
2668         return (major_status);
2669 }
2670
2671 /*
2672  * Return a list of mechanisms we are willing to negotiate for a credential,
2673  * taking into account the mech set provided with gss_set_neg_mechs if it
2674  * exists.
2675  */
2676 static OM_uint32
2677 get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_cred_id_t spcred,
2678                      gss_cred_usage_t usage, gss_OID_set *rmechs)
2679 {
2680         OM_uint32 ret, tmpmin;
2681         gss_cred_id_t creds = GSS_C_NO_CREDENTIAL, *credptr;
2682         gss_OID_set cred_mechs = GSS_C_NULL_OID_SET;
2683         gss_OID_set intersect_mechs = GSS_C_NULL_OID_SET;
2684         unsigned int i, j;
2685
2686         if (spcred == NULL) {
2687                 /*
2688                  * The default credentials were supplied.  Return a list of all
2689                  * available mechs except SPNEGO.  When initiating, trim this
2690                  * list to mechs we can acquire credentials for.
2691                  */
2692                 credptr = (usage == GSS_C_INITIATE) ? &creds : NULL;
2693                 ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage,
2694                                           credptr, rmechs);
2695                 gss_release_cred(&tmpmin, &creds);
2696                 return (ret);
2697         }
2698
2699         /* Get the list of mechs in the mechglue cred. */
2700         ret = gss_inquire_cred(minor_status, spcred->mcred, NULL, NULL, NULL,
2701                                &cred_mechs);
2702         if (ret != GSS_S_COMPLETE)
2703                 return (ret);
2704
2705         if (spcred->neg_mechs == GSS_C_NULL_OID_SET) {
2706                 /* gss_set_neg_mechs was never called; return cred_mechs. */
2707                 *rmechs = cred_mechs;
2708                 *minor_status = 0;
2709                 return (GSS_S_COMPLETE);
2710         }
2711
2712         /* Compute the intersection of cred_mechs and spcred->neg_mechs,
2713          * preserving the order in spcred->neg_mechs. */
2714         ret = gss_create_empty_oid_set(minor_status, &intersect_mechs);
2715         if (ret != GSS_S_COMPLETE) {
2716                 gss_release_oid_set(&tmpmin, &cred_mechs);
2717                 return (ret);
2718         }
2719
2720         for (i = 0; i < spcred->neg_mechs->count; i++) {
2721                 for (j = 0; j < cred_mechs->count; j++) {
2722                         if (!g_OID_equal(&spcred->neg_mechs->elements[i],
2723                                          &cred_mechs->elements[j]))
2724                                 break;
2725                 }
2726                 if (j == cred_mechs->count)
2727                         continue;
2728                 ret = gss_add_oid_set_member(minor_status,
2729                                              &spcred->neg_mechs->elements[i],
2730                                              &intersect_mechs);
2731                 if (ret != GSS_S_COMPLETE)
2732                         break;
2733         }
2734
2735         gss_release_oid_set(&tmpmin, &cred_mechs);
2736         if (intersect_mechs->count == 0 || ret != GSS_S_COMPLETE) {
2737                 gss_release_oid_set(&tmpmin, &intersect_mechs);
2738                 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2739                 map_errcode(minor_status);
2740                 return (GSS_S_FAILURE);
2741         }
2742
2743         *rmechs = intersect_mechs;
2744         *minor_status = 0;
2745         return (GSS_S_COMPLETE);
2746 }
2747
2748 /* following are token creation and reading routines */
2749
2750 /*
2751  * If buff_in is not pointing to a MECH_OID, then return NULL and do not
2752  * advance the buffer, otherwise, decode the mech_oid from the buffer and
2753  * place in gss_OID.
2754  */
2755 static gss_OID
2756 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
2757 {
2758         OM_uint32       status;
2759         gss_OID_desc    toid;
2760         gss_OID         mech_out = NULL;
2761         unsigned char           *start, *end;
2762
2763         if (length < 1 || **buff_in != MECH_OID)
2764                 return (NULL);
2765
2766         start = *buff_in;
2767         end = start + length;
2768
2769         (*buff_in)++;
2770         toid.length = *(*buff_in)++;
2771
2772         if ((*buff_in + toid.length) > end)
2773                 return (NULL);
2774
2775         toid.elements = *buff_in;
2776         *buff_in += toid.length;
2777
2778         status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
2779
2780         if (status != GSS_S_COMPLETE) {
2781                 map_errcode(minor_status);
2782                 mech_out = NULL;
2783         }
2784
2785         return (mech_out);
2786 }
2787
2788 /*
2789  * der encode the given mechanism oid into buf_out, advancing the
2790  * buffer pointer.
2791  */
2792
2793 static int
2794 put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
2795 {
2796         if (buflen < mech->length + 2)
2797                 return (-1);
2798         *(*buf_out)++ = MECH_OID;
2799         *(*buf_out)++ = (unsigned char) mech->length;
2800         memcpy(*buf_out, mech->elements, mech->length);
2801         *buf_out += mech->length;
2802         return (0);
2803 }
2804
2805 /*
2806  * verify that buff_in points to an octet string, if it does not,
2807  * return NULL and don't advance the pointer. If it is an octet string
2808  * decode buff_in into a gss_buffer_t and return it, advancing the
2809  * buffer pointer.
2810  */
2811 static gss_buffer_t
2812 get_input_token(unsigned char **buff_in, unsigned int buff_length)
2813 {
2814         gss_buffer_t input_token;
2815         unsigned int len;
2816
2817         if (g_get_tag_and_length(buff_in, OCTET_STRING, buff_length, &len) < 0)
2818                 return (NULL);
2819
2820         input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
2821         if (input_token == NULL)
2822                 return (NULL);
2823
2824         input_token->length = len;
2825         input_token->value = malloc(input_token->length);
2826
2827         if (input_token->value == NULL) {
2828                 free(input_token);
2829                 return (NULL);
2830         }
2831
2832         (void) memcpy(input_token->value, *buff_in, input_token->length);
2833         *buff_in += input_token->length;
2834         return (input_token);
2835 }
2836
2837 /*
2838  * verify that the input token length is not 0. If it is, just return.
2839  * If the token length is greater than 0, der encode as an octet string
2840  * and place in buf_out, advancing buf_out.
2841  */
2842
2843 static int
2844 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
2845                 unsigned int buflen)
2846 {
2847         int ret;
2848
2849         /* if token length is 0, we do not want to send */
2850         if (input_token->length == 0)
2851                 return (0);
2852
2853         if (input_token->length > buflen)
2854                 return (-1);
2855
2856         *(*buf_out)++ = OCTET_STRING;
2857         if ((ret = gssint_put_der_length(input_token->length, buf_out,
2858                             input_token->length)))
2859                 return (ret);
2860         TWRITE_STR(*buf_out, input_token->value, input_token->length);
2861         return (0);
2862 }
2863
2864 /*
2865  * verify that buff_in points to a sequence of der encoding. The mech
2866  * set is the only sequence of encoded object in the token, so if it is
2867  * a sequence of encoding, decode the mechset into a gss_OID_set and
2868  * return it, advancing the buffer pointer.
2869  */
2870 static gss_OID_set
2871 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
2872              unsigned int buff_length)
2873 {
2874         gss_OID_set returned_mechSet;
2875         OM_uint32 major_status;
2876         int length;
2877         unsigned int bytes;
2878         OM_uint32 set_length;
2879         unsigned char           *start;
2880         int i;
2881
2882         if (**buff_in != SEQUENCE_OF)
2883                 return (NULL);
2884
2885         start = *buff_in;
2886         (*buff_in)++;
2887
2888         length = gssint_get_der_length(buff_in, buff_length, &bytes);
2889         if (length < 0 || buff_length - bytes < (unsigned int)length)
2890                 return NULL;
2891
2892         major_status = gss_create_empty_oid_set(minor_status,
2893                                                 &returned_mechSet);
2894         if (major_status != GSS_S_COMPLETE)
2895                 return (NULL);
2896
2897         for (set_length = 0, i = 0; set_length < (unsigned int)length; i++) {
2898                 gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
2899                         buff_length - (*buff_in - start));
2900                 if (temp == NULL)
2901                         break;
2902
2903                 major_status = gss_add_oid_set_member(minor_status,
2904                                                       temp, &returned_mechSet);
2905                 if (major_status == GSS_S_COMPLETE) {
2906                         set_length += returned_mechSet->elements[i].length +2;
2907                         if (generic_gss_release_oid(minor_status, &temp))
2908                                 map_errcode(minor_status);
2909                 }
2910         }
2911
2912         return (returned_mechSet);
2913 }
2914
2915 /*
2916  * Encode mechSet into buf.
2917  */
2918 static int
2919 put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
2920 {
2921         unsigned char *ptr;
2922         unsigned int i;
2923         unsigned int tlen, ilen;
2924
2925         tlen = ilen = 0;
2926         for (i = 0; i < mechSet->count; i++) {
2927                 /*
2928                  * 0x06 [DER LEN] [OID]
2929                  */
2930                 ilen += 1 +
2931                         gssint_der_length_size(mechSet->elements[i].length) +
2932                         mechSet->elements[i].length;
2933         }
2934         /*
2935          * 0x30 [DER LEN]
2936          */
2937         tlen = 1 + gssint_der_length_size(ilen) + ilen;
2938         ptr = malloc(tlen);
2939         if (ptr == NULL)
2940                 return -1;
2941
2942         buf->value = ptr;
2943         buf->length = tlen;
2944 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
2945
2946         *ptr++ = SEQUENCE_OF;
2947         if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
2948                 return -1;
2949         for (i = 0; i < mechSet->count; i++) {
2950                 if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
2951                         return -1;
2952                 }
2953         }
2954         return 0;
2955 #undef REMAIN
2956 }
2957
2958 /*
2959  * Verify that buff_in is pointing to a BIT_STRING with the correct
2960  * length and padding for the req_flags. If it is, decode req_flags
2961  * and return them, otherwise, return NULL.
2962  */
2963 static OM_uint32
2964 get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
2965               OM_uint32 *req_flags)
2966 {
2967         unsigned int len;
2968
2969         if (**buff_in != (CONTEXT | 0x01))
2970                 return (0);
2971
2972         if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
2973                                 bodysize, &len) < 0)
2974                 return GSS_S_DEFECTIVE_TOKEN;
2975
2976         if (*(*buff_in)++ != BIT_STRING)
2977                 return GSS_S_DEFECTIVE_TOKEN;
2978
2979         if (*(*buff_in)++ != BIT_STRING_LENGTH)
2980                 return GSS_S_DEFECTIVE_TOKEN;
2981
2982         if (*(*buff_in)++ != BIT_STRING_PADDING)
2983                 return GSS_S_DEFECTIVE_TOKEN;
2984
2985         *req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
2986         return (0);
2987 }
2988
2989 static OM_uint32
2990 get_negTokenInit(OM_uint32 *minor_status,
2991                  gss_buffer_t buf,
2992                  gss_buffer_t der_mechSet,
2993                  gss_OID_set *mechSet,
2994                  OM_uint32 *req_flags,
2995                  gss_buffer_t *mechtok,
2996                  gss_buffer_t *mechListMIC)
2997 {
2998         OM_uint32 err;
2999         unsigned char *ptr, *bufstart;
3000         unsigned int len;
3001         gss_buffer_desc tmpbuf;
3002
3003         *minor_status = 0;
3004         der_mechSet->length = 0;
3005         der_mechSet->value = NULL;
3006         *mechSet = GSS_C_NO_OID_SET;
3007         *req_flags = 0;
3008         *mechtok = *mechListMIC = GSS_C_NO_BUFFER;
3009
3010         ptr = bufstart = buf->value;
3011         if ((buf->length - (ptr - bufstart)) > INT_MAX)
3012                 return GSS_S_FAILURE;
3013 #define REMAIN (buf->length - (ptr - bufstart))
3014
3015         err = g_verify_token_header(gss_mech_spnego,
3016                                     &len, &ptr, 0, REMAIN);
3017         if (err) {
3018                 *minor_status = err;
3019                 map_errcode(minor_status);
3020                 return GSS_S_FAILURE;
3021         }
3022         *minor_status = g_verify_neg_token_init(&ptr, REMAIN);
3023         if (*minor_status) {
3024                 map_errcode(minor_status);
3025                 return GSS_S_FAILURE;
3026         }
3027
3028         /* alias into input_token */
3029         tmpbuf.value = ptr;
3030         tmpbuf.length = REMAIN;
3031         *mechSet = get_mech_set(minor_status, &ptr, REMAIN);
3032         if (*mechSet == NULL)
3033                 return GSS_S_FAILURE;
3034
3035         tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
3036         der_mechSet->value = malloc(tmpbuf.length);
3037         if (der_mechSet->value == NULL)
3038                 return GSS_S_FAILURE;
3039         memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
3040         der_mechSet->length = tmpbuf.length;
3041
3042         err = get_req_flags(&ptr, REMAIN, req_flags);
3043         if (err != GSS_S_COMPLETE) {
3044                 return err;
3045         }
3046         if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
3047                                  REMAIN, &len) >= 0) {
3048                 *mechtok = get_input_token(&ptr, len);
3049                 if (*mechtok == GSS_C_NO_BUFFER) {
3050                         return GSS_S_FAILURE;
3051                 }
3052         }
3053         if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
3054                                  REMAIN, &len) >= 0) {
3055                 *mechListMIC = get_input_token(&ptr, len);
3056                 if (*mechListMIC == GSS_C_NO_BUFFER) {
3057                         return GSS_S_FAILURE;
3058                 }
3059         }
3060         return GSS_S_COMPLETE;
3061 #undef REMAIN
3062 }
3063
3064 static OM_uint32
3065 get_negTokenResp(OM_uint32 *minor_status,
3066                  unsigned char *buf, unsigned int buflen,
3067                  OM_uint32 *negState,
3068                  gss_OID *supportedMech,
3069                  gss_buffer_t *responseToken,
3070                  gss_buffer_t *mechListMIC)
3071 {
3072         unsigned char *ptr, *bufstart;
3073         unsigned int len;
3074         int tmplen;
3075         unsigned int tag, bytes;
3076
3077         *negState = ACCEPT_DEFECTIVE_TOKEN;
3078         *supportedMech = GSS_C_NO_OID;
3079         *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
3080         ptr = bufstart = buf;
3081 #define REMAIN (buflen - (ptr - bufstart))
3082
3083         if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
3084                 return GSS_S_DEFECTIVE_TOKEN;
3085         if (*ptr++ == SEQUENCE) {
3086                 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3087                 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3088                         return GSS_S_DEFECTIVE_TOKEN;
3089         }
3090         if (REMAIN < 1)
3091                 tag = 0;
3092         else
3093                 tag = *ptr++;
3094
3095         if (tag == CONTEXT) {
3096                 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3097                 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3098                         return GSS_S_DEFECTIVE_TOKEN;
3099
3100                 if (g_get_tag_and_length(&ptr, ENUMERATED,
3101                                          REMAIN, &len) < 0)
3102                         return GSS_S_DEFECTIVE_TOKEN;
3103
3104                 if (len != ENUMERATION_LENGTH)
3105                         return GSS_S_DEFECTIVE_TOKEN;
3106
3107                 if (REMAIN < 1)
3108                         return GSS_S_DEFECTIVE_TOKEN;
3109                 *negState = *ptr++;
3110
3111                 if (REMAIN < 1)
3112                         tag = 0;
3113                 else
3114                         tag = *ptr++;
3115         }
3116         if (tag == (CONTEXT | 0x01)) {
3117                 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3118                 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3119                         return GSS_S_DEFECTIVE_TOKEN;
3120
3121                 *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
3122                 if (*supportedMech == GSS_C_NO_OID)
3123                         return GSS_S_DEFECTIVE_TOKEN;
3124
3125                 if (REMAIN < 1)
3126                         tag = 0;
3127                 else
3128                         tag = *ptr++;
3129         }
3130         if (tag == (CONTEXT | 0x02)) {
3131                 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3132                 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3133                         return GSS_S_DEFECTIVE_TOKEN;
3134
3135                 *responseToken = get_input_token(&ptr, REMAIN);
3136                 if (*responseToken == GSS_C_NO_BUFFER)
3137                         return GSS_S_DEFECTIVE_TOKEN;
3138
3139                 if (REMAIN < 1)
3140                         tag = 0;
3141                 else
3142                         tag = *ptr++;
3143         }
3144         if (tag == (CONTEXT | 0x03)) {
3145                 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3146                 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3147                         return GSS_S_DEFECTIVE_TOKEN;
3148
3149                 *mechListMIC = get_input_token(&ptr, REMAIN);
3150                 if (*mechListMIC == GSS_C_NO_BUFFER)
3151                         return GSS_S_DEFECTIVE_TOKEN;
3152         }
3153         return GSS_S_COMPLETE;
3154 #undef REMAIN
3155 }
3156
3157 /*
3158  * der encode the passed negResults as an ENUMERATED type and
3159  * place it in buf_out, advancing the buffer.
3160  */
3161
3162 static int
3163 put_negResult(unsigned char **buf_out, OM_uint32 negResult,
3164               unsigned int buflen)
3165 {
3166         if (buflen < 3)
3167                 return (-1);
3168         *(*buf_out)++ = ENUMERATED;
3169         *(*buf_out)++ = ENUMERATION_LENGTH;
3170         *(*buf_out)++ = (unsigned char) negResult;
3171         return (0);
3172 }
3173
3174 /*
3175  * This routine compares the recieved mechset to the mechset that
3176  * this server can support. It looks sequentially through the mechset
3177  * and the first one that matches what the server can support is
3178  * chosen as the negotiated mechanism. If one is found, negResult
3179  * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3180  * it's not the first mech, otherwise we return NULL and negResult
3181  * is set to REJECT.
3182  *
3183  * NOTE: There is currently no way to specify a preference order of
3184  * mechanisms supported by the acceptor.
3185  */
3186 static gss_OID
3187 negotiate_mech_type(OM_uint32 *minor_status,
3188                     gss_OID_set supported_mechSet,
3189                     gss_OID_set mechset,
3190                     OM_uint32 *negResult)
3191 {
3192         gss_OID returned_mech;
3193         OM_uint32 status;
3194         int present;
3195         unsigned int i;
3196
3197         for (i = 0; i < mechset->count; i++) {
3198                 gss_OID mech_oid = &mechset->elements[i];
3199
3200                 /* Accept wrong mechanism OID from MS clients */
3201                 if (mech_oid->length == gss_mech_krb5_wrong_oid.length &&
3202                     memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0)
3203                         mech_oid = (gss_OID)&gss_mech_krb5_oid;;
3204
3205                 gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present);
3206                 if (!present)
3207                         continue;
3208
3209                 if (i == 0)
3210                         *negResult = ACCEPT_INCOMPLETE;
3211                 else
3212                         *negResult = REQUEST_MIC;
3213
3214                 status = generic_gss_copy_oid(minor_status,
3215                                               &mechset->elements[i],
3216                                               &returned_mech);
3217                 if (status != GSS_S_COMPLETE) {
3218                         *negResult = REJECT;
3219                         map_errcode(minor_status);
3220                         return (NULL);
3221                 }
3222                 return (returned_mech);
3223         }
3224         *negResult = REJECT;
3225         return (NULL);
3226 }
3227
3228 /*
3229  * the next two routines make a token buffer suitable for
3230  * spnego_gss_display_status. These currently take the string
3231  * in name and place it in the token. Eventually, if
3232  * spnego_gss_display_status returns valid error messages,
3233  * these routines will be changes to return the error string.
3234  */
3235 static spnego_token_t
3236 make_spnego_token(char *name)
3237 {
3238         return (spnego_token_t)strdup(name);
3239 }
3240
3241 static gss_buffer_desc
3242 make_err_msg(char *name)
3243 {
3244         gss_buffer_desc buffer;
3245
3246         if (name == NULL) {
3247                 buffer.length = 0;
3248                 buffer.value = NULL;
3249         } else {
3250                 buffer.length = strlen(name)+1;
3251                 buffer.value = make_spnego_token(name);
3252         }
3253
3254         return (buffer);
3255 }
3256
3257 /*
3258  * Create the client side spnego token passed back to gss_init_sec_context
3259  * and eventually up to the application program and over to the server.
3260  *
3261  * Use DER rules, definite length method per RFC 2478
3262  */
3263 static int
3264 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
3265                           int negHintsCompat,
3266                           gss_buffer_t mechListMIC, OM_uint32 req_flags,
3267                           gss_buffer_t data, send_token_flag sendtoken,
3268                           gss_buffer_t outbuf)
3269 {
3270         int ret = 0;
3271         unsigned int tlen, dataLen = 0;
3272         unsigned int negTokenInitSize = 0;
3273         unsigned int negTokenInitSeqSize = 0;
3274         unsigned int negTokenInitContSize = 0;
3275         unsigned int rspTokenSize = 0;
3276         unsigned int mechListTokenSize = 0;
3277         unsigned int micTokenSize = 0;
3278         unsigned char *t;
3279         unsigned char *ptr;
3280
3281         if (outbuf == GSS_C_NO_BUFFER)
3282                 return (-1);
3283
3284         outbuf->length = 0;
3285         outbuf->value = NULL;
3286
3287         /* calculate the data length */
3288
3289         /*
3290          * 0xa0 [DER LEN] [mechTypes]
3291          */
3292         mechListTokenSize = 1 +
3293                 gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
3294                 spnego_ctx->DER_mechTypes.length;
3295         dataLen += mechListTokenSize;
3296
3297         /*
3298          * If a token from gss_init_sec_context exists,
3299          * add the length of the token + the ASN.1 overhead
3300          */
3301         if (data != NULL) {
3302                 /*
3303                  * Encoded in final output as:
3304                  * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
3305                  * -----s--------|--------s2----------
3306                  */
3307                 rspTokenSize = 1 +
3308                         gssint_der_length_size(data->length) +
3309                         data->length;
3310                 dataLen += 1 + gssint_der_length_size(rspTokenSize) +
3311                         rspTokenSize;
3312         }
3313
3314         if (mechListMIC) {
3315                 /*
3316                  * Encoded in final output as:
3317                  * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
3318                  *      --s--     -----tlen------------
3319                  */
3320                 micTokenSize = 1 +
3321                         gssint_der_length_size(mechListMIC->length) +
3322                         mechListMIC->length;
3323                 dataLen += 1 +
3324                         gssint_der_length_size(micTokenSize) +
3325                         micTokenSize;
3326         }
3327
3328         /*
3329          * Add size of DER encoding
3330          * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
3331          *   0x30 [DER_LEN] [data]
3332          *
3333          */
3334         negTokenInitContSize = dataLen;
3335         negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
3336         dataLen = negTokenInitSeqSize;
3337
3338         /*
3339          * negTokenInitSize indicates the bytes needed to
3340          * hold the ASN.1 encoding of the entire NegTokenInit
3341          * SEQUENCE.
3342          * 0xa0 [DER_LEN] + data
3343          *
3344          */
3345         negTokenInitSize = 1 +
3346                 gssint_der_length_size(negTokenInitSeqSize) +
3347                 negTokenInitSeqSize;
3348
3349         tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
3350
3351         t = (unsigned char *) malloc(tlen);
3352
3353         if (t == NULL) {
3354                 return (-1);
3355         }
3356
3357         ptr = t;
3358
3359         /* create the message */
3360         if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
3361                             &ptr, tlen)))
3362                 goto errout;
3363
3364         *ptr++ = CONTEXT; /* NegotiationToken identifier */
3365         if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
3366                 goto errout;
3367
3368         *ptr++ = SEQUENCE;
3369         if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
3370                                          tlen - (int)(ptr-t))))
3371                 goto errout;
3372
3373         *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
3374         if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
3375                                          &ptr, tlen - (int)(ptr-t))))
3376                 goto errout;
3377
3378         /* We already encoded the MechSetList */
3379         (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
3380                       spnego_ctx->DER_mechTypes.length);
3381
3382         ptr += spnego_ctx->DER_mechTypes.length;
3383
3384         if (data != NULL) {
3385                 *ptr++ = CONTEXT | 0x02;
3386                 if ((ret = gssint_put_der_length(rspTokenSize,
3387                                 &ptr, tlen - (int)(ptr - t))))
3388                         goto errout;
3389
3390                 if ((ret = put_input_token(&ptr, data,
3391                         tlen - (int)(ptr - t))))
3392                         goto errout;
3393         }
3394
3395         if (mechListMIC != GSS_C_NO_BUFFER) {
3396                 *ptr++ = CONTEXT | 0x03;
3397                 if ((ret = gssint_put_der_length(micTokenSize,
3398                                 &ptr, tlen - (int)(ptr - t))))
3399                         goto errout;
3400
3401                 if (negHintsCompat) {
3402                         ret = put_neg_hints(&ptr, mechListMIC,
3403                                             tlen - (int)(ptr - t));
3404                         if (ret)
3405                                 goto errout;
3406                 } else if ((ret = put_input_token(&ptr, mechListMIC,
3407                                 tlen - (int)(ptr - t))))
3408                         goto errout;
3409         }
3410
3411 errout:
3412         if (ret != 0) {
3413                 if (t)
3414                         free(t);
3415                 t = NULL;
3416                 tlen = 0;
3417         }
3418         outbuf->length = tlen;
3419         outbuf->value = (void *) t;
3420
3421         return (ret);
3422 }
3423
3424 /*
3425  * create the server side spnego token passed back to
3426  * gss_accept_sec_context and eventually up to the application program
3427  * and over to the client.
3428  */
3429 static int
3430 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
3431                           gss_buffer_t data, gss_buffer_t mechListMIC,
3432                           send_token_flag sendtoken,
3433                           gss_buffer_t outbuf)
3434 {
3435         unsigned int tlen = 0;
3436         unsigned int ret = 0;
3437         unsigned int NegTokenTargSize = 0;
3438         unsigned int NegTokenSize = 0;
3439         unsigned int rspTokenSize = 0;
3440         unsigned int micTokenSize = 0;
3441         unsigned int dataLen = 0;
3442         unsigned char *t;
3443         unsigned char *ptr;
3444
3445         if (outbuf == GSS_C_NO_BUFFER)
3446                 return (GSS_S_DEFECTIVE_TOKEN);
3447         if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID)
3448             return (GSS_S_DEFECTIVE_TOKEN);
3449
3450         outbuf->length = 0;
3451         outbuf->value = NULL;
3452
3453         /*
3454          * ASN.1 encoding of the negResult
3455          * ENUMERATED type is 3 bytes
3456          *  ENUMERATED TAG, Length, Value,
3457          * Plus 2 bytes for the CONTEXT id and length.
3458          */
3459         dataLen = 5;
3460
3461         /*
3462          * calculate data length
3463          *
3464          * If this is the initial token, include length of
3465          * mech_type and the negotiation result fields.
3466          */
3467         if (sendtoken == INIT_TOKEN_SEND) {
3468                 int mechlistTokenSize;
3469                 /*
3470                  * 1 byte for the CONTEXT ID(0xa0),
3471                  * 1 byte for the OID ID(0x06)
3472                  * 1 byte for OID Length field
3473                  * Plus the rest... (OID Length, OID value)
3474                  */
3475                 mechlistTokenSize = 3 + mech_wanted->length +
3476                         gssint_der_length_size(mech_wanted->length);
3477
3478                 dataLen += mechlistTokenSize;
3479         }
3480         if (data != NULL && data->length > 0) {
3481                 /* Length of the inner token */
3482                 rspTokenSize = 1 + gssint_der_length_size(data->length) +
3483                         data->length;
3484
3485                 dataLen += rspTokenSize;
3486
3487                 /* Length of the outer token */
3488                 dataLen += 1 + gssint_der_length_size(rspTokenSize);
3489         }
3490         if (mechListMIC != NULL) {
3491
3492                 /* Length of the inner token */
3493                 micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
3494                         mechListMIC->length;
3495
3496                 dataLen += micTokenSize;
3497
3498                 /* Length of the outer token */
3499                 dataLen += 1 + gssint_der_length_size(micTokenSize);
3500         }
3501         /*
3502          * Add size of DER encoded:
3503          * NegTokenTarg [ SEQUENCE ] of
3504          *    NegResult[0] ENUMERATED {
3505          *      accept_completed(0),
3506          *      accept_incomplete(1),
3507          *      reject(2) }
3508          *    supportedMech [1] MechType OPTIONAL,
3509          *    responseToken [2] OCTET STRING OPTIONAL,
3510          *    mechListMIC   [3] OCTET STRING OPTIONAL
3511          *
3512          * size = data->length + MechListMic + SupportedMech len +
3513          *      Result Length + ASN.1 overhead
3514          */
3515         NegTokenTargSize = dataLen;
3516         dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
3517
3518         /*
3519          * NegotiationToken [ CHOICE ]{
3520          *    negTokenInit  [0]  NegTokenInit,
3521          *    negTokenTarg  [1]  NegTokenTarg }
3522          */
3523         NegTokenSize = dataLen;
3524         dataLen += 1 + gssint_der_length_size(NegTokenSize);
3525
3526         tlen = dataLen;
3527         t = (unsigned char *) malloc(tlen);
3528
3529         if (t == NULL) {
3530                 ret = GSS_S_DEFECTIVE_TOKEN;
3531                 goto errout;
3532         }
3533
3534         ptr = t;
3535
3536         /*
3537          * Indicate that we are sending CHOICE 1
3538          * (NegTokenTarg)
3539          */
3540         *ptr++ = CONTEXT | 0x01;
3541         if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
3542                 ret = GSS_S_DEFECTIVE_TOKEN;
3543                 goto errout;
3544         }
3545         *ptr++ = SEQUENCE;
3546         if (gssint_put_der_length(NegTokenTargSize, &ptr,
3547                                   tlen - (int)(ptr-t)) < 0) {
3548                 ret = GSS_S_DEFECTIVE_TOKEN;
3549                 goto errout;
3550         }
3551
3552         /*
3553          * First field of the NegTokenTarg SEQUENCE
3554          * is the ENUMERATED NegResult.
3555          */
3556         *ptr++ = CONTEXT;
3557         if (gssint_put_der_length(3, &ptr,
3558                                   tlen - (int)(ptr-t)) < 0) {
3559                 ret = GSS_S_DEFECTIVE_TOKEN;
3560                 goto errout;
3561         }
3562         if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
3563                 ret = GSS_S_DEFECTIVE_TOKEN;
3564                 goto errout;
3565         }
3566         if (sendtoken == INIT_TOKEN_SEND) {
3567                 /*
3568                  * Next, is the Supported MechType
3569                  */
3570                 *ptr++ = CONTEXT | 0x01;
3571                 if (gssint_put_der_length(mech_wanted->length + 2,
3572                                           &ptr,
3573                                           tlen - (int)(ptr - t)) < 0) {
3574                         ret = GSS_S_DEFECTIVE_TOKEN;
3575                         goto errout;
3576                 }
3577                 if (put_mech_oid(&ptr, mech_wanted,
3578                                  tlen - (int)(ptr - t)) < 0) {
3579                         ret = GSS_S_DEFECTIVE_TOKEN;
3580                         goto errout;
3581                 }
3582         }
3583         if (data != NULL && data->length > 0) {
3584                 *ptr++ = CONTEXT | 0x02;
3585                 if (gssint_put_der_length(rspTokenSize, &ptr,
3586                                           tlen - (int)(ptr - t)) < 0) {
3587                         ret = GSS_S_DEFECTIVE_TOKEN;
3588                         goto errout;
3589                 }
3590                 if (put_input_token(&ptr, data,
3591                                     tlen - (int)(ptr - t)) < 0) {
3592                         ret = GSS_S_DEFECTIVE_TOKEN;
3593                         goto errout;
3594                 }
3595         }
3596         if (mechListMIC != NULL) {
3597                 *ptr++ = CONTEXT | 0x03;
3598                 if (gssint_put_der_length(micTokenSize, &ptr,
3599                                           tlen - (int)(ptr - t)) < 0) {
3600                         ret = GSS_S_DEFECTIVE_TOKEN;
3601                         goto errout;
3602                 }
3603                 if (put_input_token(&ptr, mechListMIC,
3604                                     tlen - (int)(ptr - t)) < 0) {
3605                         ret = GSS_S_DEFECTIVE_TOKEN;
3606                         goto errout;
3607                 }
3608         }
3609         ret = GSS_S_COMPLETE;
3610 errout:
3611         if (ret != GSS_S_COMPLETE) {
3612                 if (t)
3613                         free(t);
3614         } else {
3615                 outbuf->length = ptr - t;
3616                 outbuf->value = (void *) t;
3617         }
3618
3619         return (ret);
3620 }
3621
3622 /* determine size of token */
3623 static int
3624 g_token_size(gss_OID_const mech, unsigned int body_size)
3625 {
3626         int hdrsize;
3627
3628         /*
3629          * Initialize the header size to the
3630          * MECH_OID byte + the bytes needed to indicate the
3631          * length of the OID + the OID itself.
3632          *
3633          * 0x06 [MECHLENFIELD] MECHDATA
3634          */
3635         hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3636
3637         /*
3638          * Now add the bytes needed for the initial header
3639          * token bytes:
3640          * 0x60 + [DER_LEN] + HDRSIZE
3641          */
3642         hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
3643
3644         return (hdrsize + body_size);
3645 }
3646
3647 /*
3648  * generate token header.
3649  *
3650  * Use DER Definite Length method per RFC2478
3651  * Use of indefinite length encoding will not be compatible
3652  * with Microsoft or others that actually follow the spec.
3653  */
3654 static int
3655 g_make_token_header(gss_OID_const mech,
3656                     unsigned int body_size,
3657                     unsigned char **buf,
3658                     unsigned int totallen)
3659 {
3660         int ret = 0;
3661         unsigned int hdrsize;
3662         unsigned char *p = *buf;
3663
3664         hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3665
3666         *(*buf)++ = HEADER_ID;
3667         if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
3668                 return (ret);
3669
3670         *(*buf)++ = MECH_OID;
3671         if ((ret = gssint_put_der_length(mech->length, buf,
3672                             totallen - (int)(p - *buf))))
3673                 return (ret);
3674         TWRITE_STR(*buf, mech->elements, mech->length);
3675         return (0);
3676 }
3677
3678 /*
3679  * NOTE: This checks that the length returned by
3680  * gssint_get_der_length() is not greater than the number of octets
3681  * remaining, even though gssint_get_der_length() already checks, in
3682  * theory.
3683  */
3684 static int
3685 g_get_tag_and_length(unsigned char **buf, int tag,
3686                      unsigned int buflen, unsigned int *outlen)
3687 {
3688         unsigned char *ptr = *buf;
3689         int ret = -1; /* pessimists, assume failure ! */
3690         unsigned int encoded_len;
3691         int tmplen = 0;
3692
3693         *outlen = 0;
3694         if (buflen > 1 && *ptr == tag) {
3695                 ptr++;
3696                 tmplen = gssint_get_der_length(&ptr, buflen - 1,
3697                                                 &encoded_len);
3698                 if (tmplen < 0) {
3699                         ret = -1;
3700                 } else if ((unsigned int)tmplen > buflen - (ptr - *buf)) {
3701                         ret = -1;
3702                 } else
3703                         ret = 0;
3704         }
3705         *outlen = tmplen;
3706         *buf = ptr;
3707         return (ret);
3708 }
3709
3710 static int
3711 g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
3712 {
3713         unsigned char *buf = *buf_in;
3714         unsigned char *endptr = buf + cur_size;
3715         unsigned int seqsize;
3716         int ret = 0;
3717         unsigned int bytes;
3718
3719         /*
3720          * Verify this is a NegotiationToken type token
3721          * - check for a0(context specific identifier)
3722          * - get length and verify that enoughd ata exists
3723          */
3724         if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
3725                 return (G_BAD_TOK_HEADER);
3726
3727         cur_size = seqsize; /* should indicate bytes remaining */
3728
3729         /*
3730          * Verify the next piece, it should identify this as
3731          * a strucure of type NegTokenInit.
3732          */
3733         if (*buf++ == SEQUENCE) {
3734                 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3735                         return (G_BAD_TOK_HEADER);
3736                 /*
3737                  * Make sure we have the entire buffer as described
3738                  */
3739                 if (buf + seqsize > endptr)
3740                         return (G_BAD_TOK_HEADER);
3741         } else {
3742                 return (G_BAD_TOK_HEADER);
3743         }
3744
3745         cur_size = seqsize; /* should indicate bytes remaining */
3746
3747         /*
3748          * Verify that the first blob is a sequence of mechTypes
3749          */
3750         if (*buf++ == CONTEXT) {
3751                 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3752                         return (G_BAD_TOK_HEADER);
3753                 /*
3754                  * Make sure we have the entire buffer as described
3755                  */
3756                 if (buf + bytes > endptr)
3757                         return (G_BAD_TOK_HEADER);
3758         } else {
3759                 return (G_BAD_TOK_HEADER);
3760         }
3761
3762         /*
3763          * At this point, *buf should be at the beginning of the
3764          * DER encoded list of mech types that are to be negotiated.
3765          */
3766         *buf_in = buf;
3767
3768         return (ret);
3769
3770 }
3771
3772 /* verify token header. */
3773 static int
3774 g_verify_token_header(gss_OID_const mech,
3775                     unsigned int *body_size,
3776                     unsigned char **buf_in,
3777                     int tok_type,
3778                     unsigned int toksize)
3779 {
3780         unsigned char *buf = *buf_in;
3781         int seqsize;
3782         gss_OID_desc toid;
3783         int ret = 0;
3784         unsigned int bytes;
3785
3786         if (toksize-- < 1)
3787                 return (G_BAD_TOK_HEADER);
3788
3789         if (*buf++ != HEADER_ID)
3790                 return (G_BAD_TOK_HEADER);
3791
3792         if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
3793                 return (G_BAD_TOK_HEADER);
3794
3795         if ((seqsize + bytes) != toksize)
3796                 return (G_BAD_TOK_HEADER);
3797
3798         if (toksize-- < 1)
3799                 return (G_BAD_TOK_HEADER);
3800
3801
3802         if (*buf++ != MECH_OID)
3803                 return (G_BAD_TOK_HEADER);
3804
3805         if (toksize-- < 1)
3806                 return (G_BAD_TOK_HEADER);
3807
3808         toid.length = *buf++;
3809
3810         if (toksize < toid.length)
3811                 return (G_BAD_TOK_HEADER);
3812         else
3813                 toksize -= toid.length;
3814
3815         toid.elements = buf;
3816         buf += toid.length;
3817
3818         if (!g_OID_equal(&toid, mech))
3819                 ret = G_WRONG_MECH;
3820
3821         /*
3822          * G_WRONG_MECH is not returned immediately because it's more important
3823          * to return G_BAD_TOK_HEADER if the token header is in fact bad
3824          */
3825         if (toksize < 2)
3826                 return (G_BAD_TOK_HEADER);
3827         else
3828                 toksize -= 2;
3829
3830         if (!ret) {
3831                 *buf_in = buf;
3832                 *body_size = toksize;
3833         }
3834
3835         return (ret);
3836 }
3837
3838 /*
3839  * Return non-zero if the oid is one of the kerberos mech oids,
3840  * otherwise return zero.
3841  *
3842  * N.B. There are 3 oids that represent the kerberos mech:
3843  * RFC-specified GSS_MECH_KRB5_OID,
3844  * Old pre-RFC   GSS_MECH_KRB5_OLD_OID,
3845  * Incorrect MS  GSS_MECH_KRB5_WRONG_OID
3846  */
3847
3848 static int
3849 is_kerb_mech(gss_OID oid)
3850 {
3851         int answer = 0;
3852         OM_uint32 minor;
3853         extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
3854
3855         (void) gss_test_oid_set_member(&minor,
3856                 oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
3857
3858         return (answer);
3859 }