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