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