]> CyberLeo.Net >> Repos - FreeBSD/releng/8.0.git/blob - contrib/bind9/lib/dns/spnego.c
Adjust to reflect 8.0-RELEASE.
[FreeBSD/releng/8.0.git] / contrib / bind9 / lib / dns / spnego.c
1 /*
2  * Copyright (C) 2006-2009  Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 /* $Id: spnego.c,v 1.8.118.2 2009/01/18 23:47:40 tbox Exp $ */
18
19 /*! \file
20  * \brief
21  * Portable SPNEGO implementation.
22  *
23  * This is part of a portable implementation of the SPNEGO protocol
24  * (RFCs 2478 and 4178).  This implementation uses the RFC 4178 ASN.1
25  * module but is not a full implementation of the RFC 4178 protocol;
26  * at the moment, we only support GSS-TSIG with Kerberos
27  * authentication, so we only need enough of the SPNEGO protocol to
28  * support that.
29  *
30  * The files that make up this portable SPNEGO implementation are:
31  * \li  spnego.c        (this file)
32  * \li  spnego.h        (API SPNEGO exports to the rest of lib/dns)
33  * \li  spnego.asn1     (SPNEGO ASN.1 module)
34  * \li  spnego_asn1.c   (routines generated from spngo.asn1)
35  * \li  spnego_asn1.pl  (perl script to generate spnego_asn1.c)
36  *
37  * Everything but the functions exported in spnego.h is static, to
38  * avoid possible conflicts with other libraries (particularly Heimdal,
39  * since much of this code comes from Heimdal by way of mod_auth_kerb).
40  *
41  * spnego_asn1.c is shipped as part of lib/dns because generating it
42  * requires both Perl and the Heimdal ASN.1 compiler.  See
43  * spnego_asn1.pl for further details.  We've tried to eliminate all
44  * compiler warnings from the generated code, but you may see a few
45  * when using a compiler version we haven't tested yet.
46  */
47
48 /*
49  * Portions of this code were derived from mod_auth_kerb and Heimdal.
50  * These packages are available from:
51  *
52  *   http://modauthkerb.sourceforge.net/
53  *   http://www.pdc.kth.se/heimdal/
54  *
55  * and were released under the following licenses:
56  *
57  * ----------------------------------------------------------------
58  *
59  * Copyright (c) 2004 Masarykova universita
60  * (Masaryk University, Brno, Czech Republic)
61  * All rights reserved.
62  *
63  * Redistribution and use in source and binary forms, with or without
64  * modification, are permitted provided that the following conditions are met:
65  *
66  * 1. Redistributions of source code must retain the above copyright notice,
67  *    this list of conditions and the following disclaimer.
68  *
69  * 2. Redistributions in binary form must reproduce the above copyright
70  *    notice, this list of conditions and the following disclaimer in the
71  *    documentation and/or other materials provided with the distribution.
72  *
73  * 3. Neither the name of the University nor the names of its contributors may
74  *    be used to endorse or promote products derived from this software
75  *    without specific prior written permission.
76  *
77  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
78  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
81  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
82  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
83  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
84  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
85  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
86  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
87  * POSSIBILITY OF SUCH DAMAGE.
88  *
89  * ----------------------------------------------------------------
90  *
91  * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
92  * (Royal Institute of Technology, Stockholm, Sweden).
93  * All rights reserved.
94  *
95  * Redistribution and use in source and binary forms, with or without
96  * modification, are permitted provided that the following conditions
97  * are met:
98  *
99  * 1. Redistributions of source code must retain the above copyright
100  *    notice, this list of conditions and the following disclaimer.
101  *
102  * 2. Redistributions in binary form must reproduce the above copyright
103  *    notice, this list of conditions and the following disclaimer in the
104  *    documentation and/or other materials provided with the distribution.
105  *
106  * 3. Neither the name of the Institute nor the names of its contributors
107  *    may be used to endorse or promote products derived from this software
108  *    without specific prior written permission.
109  *
110  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
111  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
112  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
113  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
114  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
115  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
116  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
117  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
118  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
119  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
120  * SUCH DAMAGE.
121  */
122
123 /*
124  * XXXSRA We should omit this file entirely in Makefile.in via autoconf,
125  * but this will keep it from generating errors until that's written.
126  */
127
128 #ifdef GSSAPI
129
130 /*
131  * XXXSRA Some of the following files are almost certainly unnecessary,
132  * but using this list (borrowed from gssapictx.c) gets rid of some
133  * whacky compilation errors when building with MSVC and should be
134  * harmless in any case.
135  */
136
137 #include <config.h>
138
139 #include <stdlib.h>
140 #include <errno.h>
141
142 #include <isc/buffer.h>
143 #include <isc/dir.h>
144 #include <isc/entropy.h>
145 #include <isc/lex.h>
146 #include <isc/mem.h>
147 #include <isc/once.h>
148 #include <isc/random.h>
149 #include <isc/string.h>
150 #include <isc/time.h>
151 #include <isc/util.h>
152
153 #include <dns/fixedname.h>
154 #include <dns/name.h>
155 #include <dns/rdata.h>
156 #include <dns/rdataclass.h>
157 #include <dns/result.h>
158 #include <dns/types.h>
159 #include <dns/keyvalues.h>
160 #include <dns/log.h>
161
162 #include <dst/gssapi.h>
163 #include <dst/result.h>
164
165 #include "dst_internal.h"
166
167 /*
168  * The API we export
169  */
170 #include "spnego.h"
171
172 /* asn1_err.h */
173 /* Generated from ../../../lib/asn1/asn1_err.et */
174
175 typedef enum asn1_error_number {
176         ASN1_BAD_TIMEFORMAT = 1859794432,
177         ASN1_MISSING_FIELD = 1859794433,
178         ASN1_MISPLACED_FIELD = 1859794434,
179         ASN1_TYPE_MISMATCH = 1859794435,
180         ASN1_OVERFLOW = 1859794436,
181         ASN1_OVERRUN = 1859794437,
182         ASN1_BAD_ID = 1859794438,
183         ASN1_BAD_LENGTH = 1859794439,
184         ASN1_BAD_FORMAT = 1859794440,
185         ASN1_PARSE_ERROR = 1859794441
186 } asn1_error_number;
187
188 #define ERROR_TABLE_BASE_asn1 1859794432
189
190 #define __asn1_common_definitions__
191
192 typedef struct octet_string {
193         size_t length;
194         void *data;
195 } octet_string;
196
197 typedef char *general_string;
198
199 typedef char *utf8_string;
200
201 typedef struct oid {
202         size_t length;
203         unsigned *components;
204 } oid;
205
206 /* der.h */
207
208 typedef enum {
209         ASN1_C_UNIV = 0, ASN1_C_APPL = 1,
210         ASN1_C_CONTEXT = 2, ASN1_C_PRIVATE = 3
211 } Der_class;
212
213 typedef enum {
214         PRIM = 0, CONS = 1
215 } Der_type;
216
217 /* Universal tags */
218
219 enum {
220         UT_Boolean = 1,
221         UT_Integer = 2,
222         UT_BitString = 3,
223         UT_OctetString = 4,
224         UT_Null = 5,
225         UT_OID = 6,
226         UT_Enumerated = 10,
227         UT_Sequence = 16,
228         UT_Set = 17,
229         UT_PrintableString = 19,
230         UT_IA5String = 22,
231         UT_UTCTime = 23,
232         UT_GeneralizedTime = 24,
233         UT_VisibleString = 26,
234         UT_GeneralString = 27
235 };
236
237 #define ASN1_INDEFINITE 0xdce0deed
238
239 static int
240 der_get_length(const unsigned char *p, size_t len,
241                size_t * val, size_t * size);
242
243 static int
244 der_get_octet_string(const unsigned char *p, size_t len,
245                      octet_string * data, size_t * size);
246 static int
247 der_get_oid(const unsigned char *p, size_t len,
248             oid * data, size_t * size);
249 static int
250 der_get_tag(const unsigned char *p, size_t len,
251             Der_class * class, Der_type * type,
252             int *tag, size_t * size);
253
254 static int
255 der_match_tag(const unsigned char *p, size_t len,
256               Der_class class, Der_type type,
257               int tag, size_t * size);
258 static int
259 der_match_tag_and_length(const unsigned char *p, size_t len,
260                          Der_class class, Der_type type, int tag,
261                          size_t * length_ret, size_t * size);
262
263 static int
264 decode_oid(const unsigned char *p, size_t len,
265            oid * k, size_t * size);
266
267 static int
268 decode_enumerated(const unsigned char *p, size_t len,
269                   unsigned *num, size_t *size);
270
271 static int
272 decode_octet_string(const unsigned char *, size_t, octet_string *, size_t *);
273
274 static int
275 der_put_int(unsigned char *p, size_t len, int val, size_t *);
276
277 static int
278 der_put_length(unsigned char *p, size_t len, size_t val, size_t *);
279
280 static int
281 der_put_octet_string(unsigned char *p, size_t len,
282                      const octet_string * data, size_t *);
283 static int
284 der_put_oid(unsigned char *p, size_t len,
285             const oid * data, size_t * size);
286 static int
287 der_put_tag(unsigned char *p, size_t len, Der_class class, Der_type type,
288             int tag, size_t *);
289 static int
290 der_put_length_and_tag(unsigned char *, size_t, size_t,
291                        Der_class, Der_type, int, size_t *);
292
293 static int
294 encode_enumerated(unsigned char *p, size_t len,
295                   const unsigned *data, size_t *);
296
297 static int
298 encode_octet_string(unsigned char *p, size_t len,
299                     const octet_string * k, size_t *);
300 static int
301 encode_oid(unsigned char *p, size_t len,
302            const oid * k, size_t *);
303
304 static void
305 free_octet_string(octet_string * k);
306
307 static void
308 free_oid  (oid * k);
309
310 static size_t
311 length_len(size_t len);
312
313 static int
314 fix_dce(size_t reallen, size_t * len);
315
316 /*
317  * Include stuff generated by the ASN.1 compiler.
318  */
319
320 #include "spnego_asn1.c"
321
322 static unsigned char gss_krb5_mech_oid_bytes[] = {
323         0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
324 };
325
326 static gss_OID_desc gss_krb5_mech_oid_desc = {
327         sizeof(gss_krb5_mech_oid_bytes),
328         gss_krb5_mech_oid_bytes
329 };
330
331 static gss_OID GSS_KRB5_MECH = &gss_krb5_mech_oid_desc;
332
333 static unsigned char gss_mskrb5_mech_oid_bytes[] = {
334         0x2a, 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02
335 };
336
337 static gss_OID_desc gss_mskrb5_mech_oid_desc = {
338         sizeof(gss_mskrb5_mech_oid_bytes),
339         gss_mskrb5_mech_oid_bytes
340 };
341
342 static gss_OID GSS_MSKRB5_MECH = &gss_mskrb5_mech_oid_desc;
343
344 static unsigned char gss_spnego_mech_oid_bytes[] = {
345         0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
346 };
347
348 static gss_OID_desc gss_spnego_mech_oid_desc = {
349         sizeof(gss_spnego_mech_oid_bytes),
350         gss_spnego_mech_oid_bytes
351 };
352
353 static gss_OID GSS_SPNEGO_MECH = &gss_spnego_mech_oid_desc;
354
355 /* spnegokrb5_locl.h */
356
357 static OM_uint32
358 gssapi_spnego_encapsulate(OM_uint32 *,
359                           unsigned char *,
360                           size_t,
361                           gss_buffer_t,
362                           const gss_OID);
363
364 static OM_uint32
365 gssapi_spnego_decapsulate(OM_uint32 *,
366                           gss_buffer_t,
367                           unsigned char **,
368                           size_t *,
369                           const gss_OID);
370
371 /* mod_auth_kerb.c */
372
373 static int
374 cmp_gss_type(gss_buffer_t token, gss_OID oid)
375 {
376         unsigned char *p;
377         size_t len;
378
379         if (token->length == 0)
380                 return (GSS_S_DEFECTIVE_TOKEN);
381
382         p = token->value;
383         if (*p++ != 0x60)
384                 return (GSS_S_DEFECTIVE_TOKEN);
385         len = *p++;
386         if (len & 0x80) {
387                 if ((len & 0x7f) > 4)
388                         return (GSS_S_DEFECTIVE_TOKEN);
389                 p += len & 0x7f;
390         }
391         if (*p++ != 0x06)
392                 return (GSS_S_DEFECTIVE_TOKEN);
393
394         if (((OM_uint32) *p++) != oid->length)
395                 return (GSS_S_DEFECTIVE_TOKEN);
396
397         return (memcmp(p, oid->elements, oid->length));
398 }
399
400 /* accept_sec_context.c */
401 /*
402  * SPNEGO wrapper for Kerberos5 GSS-API kouril@ics.muni.cz, 2003 (mostly
403  * based on Heimdal code)
404  */
405
406 static OM_uint32
407 code_NegTokenArg(OM_uint32 * minor_status,
408                  const NegTokenResp * resp,
409                  unsigned char **outbuf,
410                  size_t * outbuf_size)
411 {
412         OM_uint32 ret;
413         u_char *buf;
414         size_t buf_size, buf_len;
415
416         buf_size = 1024;
417         buf = malloc(buf_size);
418         if (buf == NULL) {
419                 *minor_status = ENOMEM;
420                 return (GSS_S_FAILURE);
421         }
422         do {
423                 ret = encode_NegTokenResp(buf + buf_size - 1,
424                                           buf_size,
425                                           resp, &buf_len);
426                 if (ret == 0) {
427                         size_t tmp;
428
429                         ret = der_put_length_and_tag(buf + buf_size - buf_len - 1,
430                                                      buf_size - buf_len,
431                                                      buf_len,
432                                                      ASN1_C_CONTEXT,
433                                                      CONS,
434                                                      1,
435                                                      &tmp);
436                         if (ret == 0)
437                                 buf_len += tmp;
438                 }
439                 if (ret) {
440                         if (ret == ASN1_OVERFLOW) {
441                                 u_char *tmp;
442
443                                 buf_size *= 2;
444                                 tmp = realloc(buf, buf_size);
445                                 if (tmp == NULL) {
446                                         *minor_status = ENOMEM;
447                                         free(buf);
448                                         return (GSS_S_FAILURE);
449                                 }
450                                 buf = tmp;
451                         } else {
452                                 *minor_status = ret;
453                                 free(buf);
454                                 return (GSS_S_FAILURE);
455                         }
456                 }
457         } while (ret == ASN1_OVERFLOW);
458
459         *outbuf = malloc(buf_len);
460         if (*outbuf == NULL) {
461                 *minor_status = ENOMEM;
462                 free(buf);
463                 return (GSS_S_FAILURE);
464         }
465         memcpy(*outbuf, buf + buf_size - buf_len, buf_len);
466         *outbuf_size = buf_len;
467
468         free(buf);
469
470         return (GSS_S_COMPLETE);
471 }
472
473 static OM_uint32
474 send_reject(OM_uint32 * minor_status,
475             gss_buffer_t output_token)
476 {
477         NegTokenResp resp;
478         OM_uint32 ret;
479
480         resp.negState = malloc(sizeof(*resp.negState));
481         if (resp.negState == NULL) {
482                 *minor_status = ENOMEM;
483                 return (GSS_S_FAILURE);
484         }
485         *(resp.negState) = reject;
486
487         resp.supportedMech = NULL;
488         resp.responseToken = NULL;
489         resp.mechListMIC = NULL;
490
491         ret = code_NegTokenArg(minor_status, &resp,
492                                (unsigned char **)&output_token->value,
493                                &output_token->length);
494         free_NegTokenResp(&resp);
495         if (ret)
496                 return (ret);
497
498         return (GSS_S_BAD_MECH);
499 }
500
501 static OM_uint32
502 send_accept(OM_uint32 * minor_status,
503             gss_buffer_t output_token,
504             gss_buffer_t mech_token,
505             const gss_OID pref)
506 {
507         NegTokenResp resp;
508         OM_uint32 ret;
509
510         memset(&resp, 0, sizeof(resp));
511         resp.negState = malloc(sizeof(*resp.negState));
512         if (resp.negState == NULL) {
513                 *minor_status = ENOMEM;
514                 return (GSS_S_FAILURE);
515         }
516         *(resp.negState) = accept_completed;
517
518         resp.supportedMech = malloc(sizeof(*resp.supportedMech));
519         if (resp.supportedMech == NULL) {
520                 free_NegTokenResp(&resp);
521                 *minor_status = ENOMEM;
522                 return (GSS_S_FAILURE);
523         }
524         ret = der_get_oid(pref->elements,
525                           pref->length,
526                           resp.supportedMech,
527                           NULL);
528         if (ret) {
529                 free_NegTokenResp(&resp);
530                 *minor_status = ENOMEM;
531                 return (GSS_S_FAILURE);
532         }
533         if (mech_token != NULL && mech_token->length != 0) {
534                 resp.responseToken = malloc(sizeof(*resp.responseToken));
535                 if (resp.responseToken == NULL) {
536                         free_NegTokenResp(&resp);
537                         *minor_status = ENOMEM;
538                         return (GSS_S_FAILURE);
539                 }
540                 resp.responseToken->length = mech_token->length;
541                 resp.responseToken->data = mech_token->value;
542         }
543
544         ret = code_NegTokenArg(minor_status, &resp,
545                                (unsigned char **)&output_token->value,
546                                &output_token->length);
547         if (resp.responseToken != NULL) {
548                 free(resp.responseToken);
549                 resp.responseToken = NULL;
550         }
551         free_NegTokenResp(&resp);
552         if (ret)
553                 return (ret);
554
555         return (GSS_S_COMPLETE);
556 }
557
558 OM_uint32
559 gss_accept_sec_context_spnego(OM_uint32 *minor_status,
560                               gss_ctx_id_t *context_handle,
561                               const gss_cred_id_t acceptor_cred_handle,
562                               const gss_buffer_t input_token_buffer,
563                               const gss_channel_bindings_t input_chan_bindings,
564                               gss_name_t *src_name,
565                               gss_OID *mech_type,
566                               gss_buffer_t output_token,
567                               OM_uint32 *ret_flags,
568                               OM_uint32 *time_rec,
569                               gss_cred_id_t *delegated_cred_handle)
570 {
571         NegTokenInit init_token;
572         OM_uint32 major_status;
573         OM_uint32 minor_status2;
574         gss_buffer_desc ibuf, obuf;
575         gss_buffer_t ot = NULL;
576         gss_OID pref = GSS_KRB5_MECH;
577         unsigned char *buf;
578         size_t buf_size;
579         size_t len, taglen, ni_len;
580         int found = 0;
581         int ret;
582         unsigned i;
583
584         /*
585          * Before doing anything else, see whether this is a SPNEGO
586          * PDU.  If not, dispatch to the GSSAPI library and get out.
587          */
588
589         if (cmp_gss_type(input_token_buffer, GSS_SPNEGO_MECH))
590                 return (gss_accept_sec_context(minor_status,
591                                                context_handle,
592                                                acceptor_cred_handle,
593                                                input_token_buffer,
594                                                input_chan_bindings,
595                                                src_name,
596                                                mech_type,
597                                                output_token,
598                                                ret_flags,
599                                                time_rec,
600                                                delegated_cred_handle));
601
602         /*
603          * If we get here, it's SPNEGO.
604          */
605
606         memset(&init_token, 0, sizeof(init_token));
607
608         ret = gssapi_spnego_decapsulate(minor_status, input_token_buffer,
609                                         &buf, &buf_size, GSS_SPNEGO_MECH);
610         if (ret)
611                 return (ret);
612
613         ret = der_match_tag_and_length(buf, buf_size, ASN1_C_CONTEXT, CONS,
614                                        0, &len, &taglen);
615         if (ret)
616                 return (ret);
617
618         ret = decode_NegTokenInit(buf + taglen, len, &init_token, &ni_len);
619         if (ret) {
620                 *minor_status = EINVAL; /* XXX */
621                 return (GSS_S_DEFECTIVE_TOKEN);
622         }
623
624         for (i = 0; !found && i < init_token.mechTypes.len; ++i) {
625                 char mechbuf[17];
626                 size_t mech_len;
627
628                 ret = der_put_oid(mechbuf + sizeof(mechbuf) - 1,
629                                   sizeof(mechbuf),
630                                   &init_token.mechTypes.val[i],
631                                   &mech_len);
632                 if (ret)
633                         return (GSS_S_DEFECTIVE_TOKEN);
634                 if (mech_len == GSS_KRB5_MECH->length &&
635                     memcmp(GSS_KRB5_MECH->elements,
636                            mechbuf + sizeof(mechbuf) - mech_len,
637                            mech_len) == 0) {
638                         found = 1;
639                         break;
640                 }
641                 if (mech_len == GSS_MSKRB5_MECH->length &&
642                     memcmp(GSS_MSKRB5_MECH->elements,
643                            mechbuf + sizeof(mechbuf) - mech_len,
644                            mech_len) == 0) {
645                         found = 1;
646                         if (i == 0)
647                                 pref = GSS_MSKRB5_MECH;
648                         break;
649                 }
650         }
651
652         if (!found)
653                 return (send_reject(minor_status, output_token));
654
655         if (i == 0 && init_token.mechToken != NULL) {
656                 ibuf.length = init_token.mechToken->length;
657                 ibuf.value = init_token.mechToken->data;
658
659                 major_status = gss_accept_sec_context(minor_status,
660                                                       context_handle,
661                                                       acceptor_cred_handle,
662                                                       &ibuf,
663                                                       input_chan_bindings,
664                                                       src_name,
665                                                       mech_type,
666                                                       &obuf,
667                                                       ret_flags,
668                                                       time_rec,
669                                                       delegated_cred_handle);
670                 if (GSS_ERROR(major_status)) {
671                         send_reject(&minor_status2, output_token);
672                         return (major_status);
673                 }
674                 ot = &obuf;
675         }
676         ret = send_accept(&minor_status2, output_token, ot, pref);
677         if (ot != NULL && ot->length != 0)
678                 gss_release_buffer(&minor_status2, ot);
679
680         return (ret);
681 }
682
683 /* decapsulate.c */
684
685 static OM_uint32
686 gssapi_verify_mech_header(u_char ** str,
687                           size_t total_len,
688                           const gss_OID mech)
689 {
690         size_t len, len_len, mech_len, foo;
691         int e;
692         u_char *p = *str;
693
694         if (total_len < 1)
695                 return (GSS_S_DEFECTIVE_TOKEN);
696         if (*p++ != 0x60)
697                 return (GSS_S_DEFECTIVE_TOKEN);
698         e = der_get_length(p, total_len - 1, &len, &len_len);
699         if (e || 1 + len_len + len != total_len)
700                 return (GSS_S_DEFECTIVE_TOKEN);
701         p += len_len;
702         if (*p++ != 0x06)
703                 return (GSS_S_DEFECTIVE_TOKEN);
704         e = der_get_length(p, total_len - 1 - len_len - 1,
705                            &mech_len, &foo);
706         if (e)
707                 return (GSS_S_DEFECTIVE_TOKEN);
708         p += foo;
709         if (mech_len != mech->length)
710                 return (GSS_S_BAD_MECH);
711         if (memcmp(p, mech->elements, mech->length) != 0)
712                 return (GSS_S_BAD_MECH);
713         p += mech_len;
714         *str = p;
715         return (GSS_S_COMPLETE);
716 }
717
718 /*
719  * Remove the GSS-API wrapping from `in_token' giving `buf and buf_size' Does
720  * not copy data, so just free `in_token'.
721  */
722
723 static OM_uint32
724 gssapi_spnego_decapsulate(OM_uint32 *minor_status,
725                           gss_buffer_t input_token_buffer,
726                           unsigned char **buf,
727                           size_t *buf_len,
728                           const gss_OID mech)
729 {
730         u_char *p;
731         OM_uint32 ret;
732
733         p = input_token_buffer->value;
734         ret = gssapi_verify_mech_header(&p,
735                                         input_token_buffer->length,
736                                         mech);
737         if (ret) {
738                 *minor_status = ret;
739                 return (GSS_S_FAILURE);
740         }
741         *buf_len = input_token_buffer->length -
742                 (p - (u_char *) input_token_buffer->value);
743         *buf = p;
744         return (GSS_S_COMPLETE);
745 }
746
747 /* der_free.c */
748
749 static void
750 free_octet_string(octet_string *k)
751 {
752         free(k->data);
753         k->data = NULL;
754 }
755
756 static void
757 free_oid(oid *k)
758 {
759         free(k->components);
760         k->components = NULL;
761 }
762
763 /* der_get.c */
764
765 /*
766  * All decoding functions take a pointer `p' to first position in which to
767  * read, from the left, `len' which means the maximum number of characters we
768  * are able to read, `ret' were the value will be returned and `size' where
769  * the number of used bytes is stored. Either 0 or an error code is returned.
770  */
771
772 static int
773 der_get_unsigned(const unsigned char *p, size_t len,
774                  unsigned *ret, size_t *size)
775 {
776         unsigned val = 0;
777         size_t oldlen = len;
778
779         while (len--)
780                 val = val * 256 + *p++;
781         *ret = val;
782         if (size)
783                 *size = oldlen;
784         return (0);
785 }
786
787 static int
788 der_get_int(const unsigned char *p, size_t len,
789             int *ret, size_t *size)
790 {
791         int val = 0;
792         size_t oldlen = len;
793
794         if (len > 0) {
795                 val = (signed char)*p++;
796                 while (--len)
797                         val = val * 256 + *p++;
798         }
799         *ret = val;
800         if (size)
801                 *size = oldlen;
802         return (0);
803 }
804
805 static int
806 der_get_length(const unsigned char *p, size_t len,
807                size_t *val, size_t *size)
808 {
809         size_t v;
810
811         if (len <= 0)
812                 return (ASN1_OVERRUN);
813         --len;
814         v = *p++;
815         if (v < 128) {
816                 *val = v;
817                 if (size)
818                         *size = 1;
819         } else {
820                 int e;
821                 size_t l;
822                 unsigned tmp;
823
824                 if (v == 0x80) {
825                         *val = ASN1_INDEFINITE;
826                         if (size)
827                                 *size = 1;
828                         return (0);
829                 }
830                 v &= 0x7F;
831                 if (len < v)
832                         return (ASN1_OVERRUN);
833                 e = der_get_unsigned(p, v, &tmp, &l);
834                 if (e)
835                         return (e);
836                 *val = tmp;
837                 if (size)
838                         *size = l + 1;
839         }
840         return (0);
841 }
842
843 static int
844 der_get_octet_string(const unsigned char *p, size_t len,
845                      octet_string *data, size_t *size)
846 {
847         data->length = len;
848         data->data = malloc(len);
849         if (data->data == NULL && data->length != 0)
850                 return (ENOMEM);
851         memcpy(data->data, p, len);
852         if (size)
853                 *size = len;
854         return (0);
855 }
856
857 static int
858 der_get_oid(const unsigned char *p, size_t len,
859             oid *data, size_t *size)
860 {
861         int n;
862         size_t oldlen = len;
863
864         if (len < 1)
865                 return (ASN1_OVERRUN);
866
867         data->components = malloc(len * sizeof(*data->components));
868         if (data->components == NULL && len != 0)
869                 return (ENOMEM);
870         data->components[0] = (*p) / 40;
871         data->components[1] = (*p) % 40;
872         --len;
873         ++p;
874         for (n = 2; len > 0; ++n) {
875                 unsigned u = 0;
876
877                 do {
878                         --len;
879                         u = u * 128 + (*p++ % 128);
880                 } while (len > 0 && p[-1] & 0x80);
881                 data->components[n] = u;
882         }
883         if (p[-1] & 0x80) {
884                 free_oid(data);
885                 return (ASN1_OVERRUN);
886         }
887         data->length = n;
888         if (size)
889                 *size = oldlen;
890         return (0);
891 }
892
893 static int
894 der_get_tag(const unsigned char *p, size_t len,
895             Der_class *class, Der_type *type,
896             int *tag, size_t *size)
897 {
898         if (len < 1)
899                 return (ASN1_OVERRUN);
900         *class = (Der_class) (((*p) >> 6) & 0x03);
901         *type = (Der_type) (((*p) >> 5) & 0x01);
902         *tag = (*p) & 0x1F;
903         if (size)
904                 *size = 1;
905         return (0);
906 }
907
908 static int
909 der_match_tag(const unsigned char *p, size_t len,
910               Der_class class, Der_type type,
911               int tag, size_t *size)
912 {
913         size_t l;
914         Der_class thisclass;
915         Der_type thistype;
916         int thistag;
917         int e;
918
919         e = der_get_tag(p, len, &thisclass, &thistype, &thistag, &l);
920         if (e)
921                 return (e);
922         if (class != thisclass || type != thistype)
923                 return (ASN1_BAD_ID);
924         if (tag > thistag)
925                 return (ASN1_MISPLACED_FIELD);
926         if (tag < thistag)
927                 return (ASN1_MISSING_FIELD);
928         if (size)
929                 *size = l;
930         return (0);
931 }
932
933 static int
934 der_match_tag_and_length(const unsigned char *p, size_t len,
935                          Der_class class, Der_type type, int tag,
936                          size_t *length_ret, size_t *size)
937 {
938         size_t l, ret = 0;
939         int e;
940
941         e = der_match_tag(p, len, class, type, tag, &l);
942         if (e)
943                 return (e);
944         p += l;
945         len -= l;
946         ret += l;
947         e = der_get_length(p, len, length_ret, &l);
948         if (e)
949                 return (e);
950         p += l;
951         len -= l;
952         ret += l;
953         if (size)
954                 *size = ret;
955         return (0);
956 }
957
958 static int
959 decode_enumerated(const unsigned char *p, size_t len,
960                   unsigned *num, size_t *size)
961 {
962         size_t ret = 0;
963         size_t l, reallen;
964         int e;
965
966         e = der_match_tag(p, len, ASN1_C_UNIV, PRIM, UT_Enumerated, &l);
967         if (e)
968                 return (e);
969         p += l;
970         len -= l;
971         ret += l;
972         e = der_get_length(p, len, &reallen, &l);
973         if (e)
974                 return (e);
975         p += l;
976         len -= l;
977         ret += l;
978         e = der_get_int(p, reallen, num, &l);
979         if (e)
980                 return (e);
981         p += l;
982         len -= l;
983         ret += l;
984         if (size)
985                 *size = ret;
986         return (0);
987 }
988
989 static int
990 decode_octet_string(const unsigned char *p, size_t len,
991                     octet_string *k, size_t *size)
992 {
993         size_t ret = 0;
994         size_t l;
995         int e;
996         size_t slen;
997
998         e = der_match_tag(p, len, ASN1_C_UNIV, PRIM, UT_OctetString, &l);
999         if (e)
1000                 return (e);
1001         p += l;
1002         len -= l;
1003         ret += l;
1004
1005         e = der_get_length(p, len, &slen, &l);
1006         if (e)
1007                 return (e);
1008         p += l;
1009         len -= l;
1010         ret += l;
1011         if (len < slen)
1012                 return (ASN1_OVERRUN);
1013
1014         e = der_get_octet_string(p, slen, k, &l);
1015         if (e)
1016                 return (e);
1017         p += l;
1018         len -= l;
1019         ret += l;
1020         if (size)
1021                 *size = ret;
1022         return (0);
1023 }
1024
1025 static int
1026 decode_oid(const unsigned char *p, size_t len,
1027            oid *k, size_t *size)
1028 {
1029         size_t ret = 0;
1030         size_t l;
1031         int e;
1032         size_t slen;
1033
1034         e = der_match_tag(p, len, ASN1_C_UNIV, PRIM, UT_OID, &l);
1035         if (e)
1036                 return (e);
1037         p += l;
1038         len -= l;
1039         ret += l;
1040
1041         e = der_get_length(p, len, &slen, &l);
1042         if (e)
1043                 return (e);
1044         p += l;
1045         len -= l;
1046         ret += l;
1047         if (len < slen)
1048                 return (ASN1_OVERRUN);
1049
1050         e = der_get_oid(p, slen, k, &l);
1051         if (e)
1052                 return (e);
1053         p += l;
1054         len -= l;
1055         ret += l;
1056         if (size)
1057                 *size = ret;
1058         return (0);
1059 }
1060
1061 static int
1062 fix_dce(size_t reallen, size_t *len)
1063 {
1064         if (reallen == ASN1_INDEFINITE)
1065                 return (1);
1066         if (*len < reallen)
1067                 return (-1);
1068         *len = reallen;
1069         return (0);
1070 }
1071
1072 /* der_length.c */
1073
1074 static size_t
1075 len_unsigned(unsigned val)
1076 {
1077         size_t ret = 0;
1078
1079         do {
1080                 ++ret;
1081                 val /= 256;
1082         } while (val);
1083         return (ret);
1084 }
1085
1086 static size_t
1087 length_len(size_t len)
1088 {
1089         if (len < 128)
1090                 return (1);
1091         else
1092                 return (len_unsigned(len) + 1);
1093 }
1094
1095
1096 /* der_put.c */
1097
1098 /*
1099  * All encoding functions take a pointer `p' to first position in which to
1100  * write, from the right, `len' which means the maximum number of characters
1101  * we are able to write.  The function returns the number of characters
1102  * written in `size' (if non-NULL). The return value is 0 or an error.
1103  */
1104
1105 static int
1106 der_put_unsigned(unsigned char *p, size_t len, unsigned val, size_t *size)
1107 {
1108         unsigned char *base = p;
1109
1110         if (val) {
1111                 while (len > 0 && val) {
1112                         *p-- = val % 256;
1113                         val /= 256;
1114                         --len;
1115                 }
1116                 if (val != 0)
1117                         return (ASN1_OVERFLOW);
1118                 else {
1119                         *size = base - p;
1120                         return (0);
1121                 }
1122         } else if (len < 1)
1123                 return (ASN1_OVERFLOW);
1124         else {
1125                 *p = 0;
1126                 *size = 1;
1127                 return (0);
1128         }
1129 }
1130
1131 static int
1132 der_put_int(unsigned char *p, size_t len, int val, size_t *size)
1133 {
1134         unsigned char *base = p;
1135
1136         if (val >= 0) {
1137                 do {
1138                         if (len < 1)
1139                                 return (ASN1_OVERFLOW);
1140                         *p-- = val % 256;
1141                         len--;
1142                         val /= 256;
1143                 } while (val);
1144                 if (p[1] >= 128) {
1145                         if (len < 1)
1146                                 return (ASN1_OVERFLOW);
1147                         *p-- = 0;
1148                         len--;
1149                 }
1150         } else {
1151                 val = ~val;
1152                 do {
1153                         if (len < 1)
1154                                 return (ASN1_OVERFLOW);
1155                         *p-- = ~(val % 256);
1156                         len--;
1157                         val /= 256;
1158                 } while (val);
1159                 if (p[1] < 128) {
1160                         if (len < 1)
1161                                 return (ASN1_OVERFLOW);
1162                         *p-- = 0xff;
1163                         len--;
1164                 }
1165         }
1166         *size = base - p;
1167         return (0);
1168 }
1169
1170 static int
1171 der_put_length(unsigned char *p, size_t len, size_t val, size_t *size)
1172 {
1173         if (len < 1)
1174                 return (ASN1_OVERFLOW);
1175         if (val < 128) {
1176                 *p = val;
1177                 *size = 1;
1178                 return (0);
1179         } else {
1180                 size_t l;
1181                 int e;
1182
1183                 e = der_put_unsigned(p, len - 1, val, &l);
1184                 if (e)
1185                         return (e);
1186                 p -= l;
1187                 *p = 0x80 | l;
1188                 *size = l + 1;
1189                 return (0);
1190         }
1191 }
1192
1193 static int
1194 der_put_octet_string(unsigned char *p, size_t len,
1195                      const octet_string *data, size_t *size)
1196 {
1197         if (len < data->length)
1198                 return (ASN1_OVERFLOW);
1199         p -= data->length;
1200         len -= data->length;
1201         memcpy(p + 1, data->data, data->length);
1202         *size = data->length;
1203         return (0);
1204 }
1205
1206 static int
1207 der_put_oid(unsigned char *p, size_t len,
1208             const oid *data, size_t *size)
1209 {
1210         unsigned char *base = p;
1211         int n;
1212
1213         for (n = data->length - 1; n >= 2; --n) {
1214                 unsigned        u = data->components[n];
1215
1216                 if (len < 1)
1217                         return (ASN1_OVERFLOW);
1218                 *p-- = u % 128;
1219                 u /= 128;
1220                 --len;
1221                 while (u > 0) {
1222                         if (len < 1)
1223                                 return (ASN1_OVERFLOW);
1224                         *p-- = 128 + u % 128;
1225                         u /= 128;
1226                         --len;
1227                 }
1228         }
1229         if (len < 1)
1230                 return (ASN1_OVERFLOW);
1231         *p-- = 40 * data->components[0] + data->components[1];
1232         *size = base - p;
1233         return (0);
1234 }
1235
1236 static int
1237 der_put_tag(unsigned char *p, size_t len, Der_class class, Der_type type,
1238             int tag, size_t *size)
1239 {
1240         if (len < 1)
1241                 return (ASN1_OVERFLOW);
1242         *p = (class << 6) | (type << 5) | tag;  /* XXX */
1243         *size = 1;
1244         return (0);
1245 }
1246
1247 static int
1248 der_put_length_and_tag(unsigned char *p, size_t len, size_t len_val,
1249                        Der_class class, Der_type type, int tag, size_t *size)
1250 {
1251         size_t ret = 0;
1252         size_t l;
1253         int e;
1254
1255         e = der_put_length(p, len, len_val, &l);
1256         if (e)
1257                 return (e);
1258         p -= l;
1259         len -= l;
1260         ret += l;
1261         e = der_put_tag(p, len, class, type, tag, &l);
1262         if (e)
1263                 return (e);
1264         p -= l;
1265         len -= l;
1266         ret += l;
1267         *size = ret;
1268         return (0);
1269 }
1270
1271 static int
1272 encode_enumerated(unsigned char *p, size_t len, const unsigned *data,
1273                   size_t *size)
1274 {
1275         unsigned num = *data;
1276         size_t ret = 0;
1277         size_t l;
1278         int e;
1279
1280         e = der_put_int(p, len, num, &l);
1281         if (e)
1282                 return (e);
1283         p -= l;
1284         len -= l;
1285         ret += l;
1286         e = der_put_length_and_tag(p, len, l, ASN1_C_UNIV, PRIM, UT_Enumerated, &l);
1287         if (e)
1288                 return (e);
1289         p -= l;
1290         len -= l;
1291         ret += l;
1292         *size = ret;
1293         return (0);
1294 }
1295
1296 static int
1297 encode_octet_string(unsigned char *p, size_t len,
1298                     const octet_string *k, size_t *size)
1299 {
1300         size_t ret = 0;
1301         size_t l;
1302         int e;
1303
1304         e = der_put_octet_string(p, len, k, &l);
1305         if (e)
1306                 return (e);
1307         p -= l;
1308         len -= l;
1309         ret += l;
1310         e = der_put_length_and_tag(p, len, l, ASN1_C_UNIV, PRIM, UT_OctetString, &l);
1311         if (e)
1312                 return (e);
1313         p -= l;
1314         len -= l;
1315         ret += l;
1316         *size = ret;
1317         return (0);
1318 }
1319
1320 static int
1321 encode_oid(unsigned char *p, size_t len,
1322            const oid *k, size_t *size)
1323 {
1324         size_t ret = 0;
1325         size_t l;
1326         int e;
1327
1328         e = der_put_oid(p, len, k, &l);
1329         if (e)
1330                 return (e);
1331         p -= l;
1332         len -= l;
1333         ret += l;
1334         e = der_put_length_and_tag(p, len, l, ASN1_C_UNIV, PRIM, UT_OID, &l);
1335         if (e)
1336                 return (e);
1337         p -= l;
1338         len -= l;
1339         ret += l;
1340         *size = ret;
1341         return (0);
1342 }
1343
1344
1345 /* encapsulate.c */
1346
1347 static void
1348 gssapi_encap_length(size_t data_len,
1349                     size_t *len,
1350                     size_t *total_len,
1351                     const gss_OID mech)
1352 {
1353         size_t len_len;
1354
1355         *len = 1 + 1 + mech->length + data_len;
1356
1357         len_len = length_len(*len);
1358
1359         *total_len = 1 + len_len + *len;
1360 }
1361
1362 static u_char *
1363 gssapi_mech_make_header(u_char *p,
1364                         size_t len,
1365                         const gss_OID mech)
1366 {
1367         int e;
1368         size_t len_len, foo;
1369
1370         *p++ = 0x60;
1371         len_len = length_len(len);
1372         e = der_put_length(p + len_len - 1, len_len, len, &foo);
1373         if (e || foo != len_len)
1374                 return (NULL);
1375         p += len_len;
1376         *p++ = 0x06;
1377         *p++ = mech->length;
1378         memcpy(p, mech->elements, mech->length);
1379         p += mech->length;
1380         return (p);
1381 }
1382
1383 /*
1384  * Give it a krb5_data and it will encapsulate with extra GSS-API wrappings.
1385  */
1386
1387 static OM_uint32
1388 gssapi_spnego_encapsulate(OM_uint32 * minor_status,
1389                           unsigned char *buf,
1390                           size_t buf_size,
1391                           gss_buffer_t output_token,
1392                           const gss_OID mech)
1393 {
1394         size_t len, outer_len;
1395         u_char *p;
1396
1397         gssapi_encap_length(buf_size, &len, &outer_len, mech);
1398
1399         output_token->length = outer_len;
1400         output_token->value = malloc(outer_len);
1401         if (output_token->value == NULL) {
1402                 *minor_status = ENOMEM;
1403                 return (GSS_S_FAILURE);
1404         }
1405         p = gssapi_mech_make_header(output_token->value, len, mech);
1406         if (p == NULL) {
1407                 if (output_token->length != 0)
1408                         gss_release_buffer(minor_status, output_token);
1409                 return (GSS_S_FAILURE);
1410         }
1411         memcpy(p, buf, buf_size);
1412         return (GSS_S_COMPLETE);
1413 }
1414
1415 /* init_sec_context.c */
1416 /*
1417  * SPNEGO wrapper for Kerberos5 GSS-API kouril@ics.muni.cz, 2003 (mostly
1418  * based on Heimdal code)
1419  */
1420
1421 static int
1422 add_mech(MechTypeList * mech_list, gss_OID mech)
1423 {
1424         MechType *tmp;
1425         int ret;
1426
1427         tmp = realloc(mech_list->val, (mech_list->len + 1) * sizeof(*tmp));
1428         if (tmp == NULL)
1429                 return (ENOMEM);
1430         mech_list->val = tmp;
1431
1432         ret = der_get_oid(mech->elements, mech->length,
1433                           &mech_list->val[mech_list->len], NULL);
1434         if (ret)
1435                 return (ret);
1436
1437         mech_list->len++;
1438         return (0);
1439 }
1440
1441 /*
1442  * return the length of the mechanism in token or -1
1443  * (which implies that the token was bad - GSS_S_DEFECTIVE_TOKEN
1444  */
1445
1446 static ssize_t
1447 gssapi_krb5_get_mech(const u_char *ptr,
1448                      size_t total_len,
1449                      const u_char **mech_ret)
1450 {
1451         size_t len, len_len, mech_len, foo;
1452         const u_char *p = ptr;
1453         int e;
1454
1455         if (total_len < 1)
1456                 return (-1);
1457         if (*p++ != 0x60)
1458                 return (-1);
1459         e = der_get_length (p, total_len - 1, &len, &len_len);
1460         if (e || 1 + len_len + len != total_len)
1461                 return (-1);
1462         p += len_len;
1463         if (*p++ != 0x06)
1464                 return (-1);
1465         e = der_get_length (p, total_len - 1 - len_len - 1,
1466                             &mech_len, &foo);
1467         if (e)
1468                 return (-1);
1469         p += foo;
1470         *mech_ret = p;
1471         return (mech_len);
1472 }
1473
1474 static OM_uint32
1475 spnego_initial(OM_uint32 *minor_status,
1476                const gss_cred_id_t initiator_cred_handle,
1477                gss_ctx_id_t *context_handle,
1478                const gss_name_t target_name,
1479                const gss_OID mech_type,
1480                OM_uint32 req_flags,
1481                OM_uint32 time_req,
1482                const gss_channel_bindings_t input_chan_bindings,
1483                const gss_buffer_t input_token,
1484                gss_OID *actual_mech_type,
1485                gss_buffer_t output_token,
1486                OM_uint32 *ret_flags,
1487                OM_uint32 *time_rec)
1488 {
1489         NegTokenInit token_init;
1490         OM_uint32 major_status, minor_status2;
1491         gss_buffer_desc krb5_output_token = GSS_C_EMPTY_BUFFER;
1492         unsigned char *buf = NULL;
1493         size_t buf_size;
1494         size_t len;
1495         int ret;
1496
1497         (void)mech_type;
1498
1499         memset(&token_init, 0, sizeof(token_init));
1500
1501         ret = add_mech(&token_init.mechTypes, GSS_KRB5_MECH);
1502         if (ret) {
1503                 *minor_status = ret;
1504                 ret = GSS_S_FAILURE;
1505                 goto end;
1506         }
1507
1508         major_status = gss_init_sec_context(minor_status,
1509                                             initiator_cred_handle,
1510                                             context_handle,
1511                                             target_name,
1512                                             GSS_KRB5_MECH,
1513                                             req_flags,
1514                                             time_req,
1515                                             input_chan_bindings,
1516                                             input_token,
1517                                             actual_mech_type,
1518                                             &krb5_output_token,
1519                                             ret_flags,
1520                                             time_rec);
1521         if (GSS_ERROR(major_status)) {
1522                 ret = major_status;
1523                 goto end;
1524         }
1525         if (krb5_output_token.length > 0) {
1526                 token_init.mechToken = malloc(sizeof(*token_init.mechToken));
1527                 if (token_init.mechToken == NULL) {
1528                         *minor_status = ENOMEM;
1529                         ret = GSS_S_FAILURE;
1530                         goto end;
1531                 }
1532                 token_init.mechToken->data = krb5_output_token.value;
1533                 token_init.mechToken->length = krb5_output_token.length;
1534         }
1535         /*
1536          * The MS implementation of SPNEGO seems to not like the mechListMIC
1537          * field, so we omit it (it's optional anyway)
1538          */
1539
1540         buf_size = 1024;
1541         buf = malloc(buf_size);
1542
1543         do {
1544                 ret = encode_NegTokenInit(buf + buf_size - 1,
1545                                           buf_size,
1546                                           &token_init, &len);
1547                 if (ret == 0) {
1548                         size_t tmp;
1549
1550                         ret = der_put_length_and_tag(buf + buf_size - len - 1,
1551                                                      buf_size - len,
1552                                                      len,
1553                                                      ASN1_C_CONTEXT,
1554                                                      CONS,
1555                                                      0,
1556                                                      &tmp);
1557                         if (ret == 0)
1558                                 len += tmp;
1559                 }
1560                 if (ret) {
1561                         if (ret == ASN1_OVERFLOW) {
1562                                 u_char *tmp;
1563
1564                                 buf_size *= 2;
1565                                 tmp = realloc(buf, buf_size);
1566                                 if (tmp == NULL) {
1567                                         *minor_status = ENOMEM;
1568                                         ret = GSS_S_FAILURE;
1569                                         goto end;
1570                                 }
1571                                 buf = tmp;
1572                         } else {
1573                                 *minor_status = ret;
1574                                 ret = GSS_S_FAILURE;
1575                                 goto end;
1576                         }
1577                 }
1578         } while (ret == ASN1_OVERFLOW);
1579
1580         ret = gssapi_spnego_encapsulate(minor_status,
1581                                         buf + buf_size - len, len,
1582                                         output_token, GSS_SPNEGO_MECH);
1583         if (ret == GSS_S_COMPLETE)
1584                 ret = major_status;
1585
1586 end:
1587         if (token_init.mechToken != NULL) {
1588                 free(token_init.mechToken);
1589                 token_init.mechToken = NULL;
1590         }
1591         free_NegTokenInit(&token_init);
1592         if (krb5_output_token.length != 0)
1593                 gss_release_buffer(&minor_status2, &krb5_output_token);
1594         if (buf)
1595                 free(buf);
1596
1597         return (ret);
1598 }
1599
1600 static OM_uint32
1601 spnego_reply(OM_uint32 *minor_status,
1602              const gss_cred_id_t initiator_cred_handle,
1603              gss_ctx_id_t *context_handle,
1604              const gss_name_t target_name,
1605              const gss_OID mech_type,
1606              OM_uint32 req_flags,
1607              OM_uint32 time_req,
1608              const gss_channel_bindings_t input_chan_bindings,
1609              const gss_buffer_t input_token,
1610              gss_OID *actual_mech_type,
1611              gss_buffer_t output_token,
1612              OM_uint32 *ret_flags,
1613              OM_uint32 *time_rec)
1614 {
1615         OM_uint32 ret;
1616         NegTokenResp resp;
1617         unsigned char *buf;
1618         size_t buf_size;
1619         u_char oidbuf[17];
1620         size_t oidlen;
1621         gss_buffer_desc sub_token;
1622         ssize_t mech_len;
1623         const u_char *p;
1624         size_t len, taglen;
1625
1626         (void)mech_type;
1627
1628         output_token->length = 0;
1629         output_token->value  = NULL;
1630
1631         /*
1632          * SPNEGO doesn't include gss wrapping on SubsequentContextToken
1633          * like the Kerberos 5 mech does. But lets check for it anyway.
1634          */
1635
1636         mech_len = gssapi_krb5_get_mech(input_token->value,
1637                                         input_token->length,
1638                                         &p);
1639
1640         if (mech_len < 0) {
1641                 buf = input_token->value;
1642                 buf_size = input_token->length;
1643         } else if ((size_t)mech_len == GSS_KRB5_MECH->length &&
1644                    memcmp(GSS_KRB5_MECH->elements, p, mech_len) == 0)
1645                 return (gss_init_sec_context(minor_status,
1646                                              initiator_cred_handle,
1647                                              context_handle,
1648                                              target_name,
1649                                              GSS_KRB5_MECH,
1650                                              req_flags,
1651                                              time_req,
1652                                              input_chan_bindings,
1653                                              input_token,
1654                                              actual_mech_type,
1655                                              output_token,
1656                                              ret_flags,
1657                                              time_rec));
1658         else if ((size_t)mech_len == GSS_SPNEGO_MECH->length &&
1659                  memcmp(GSS_SPNEGO_MECH->elements, p, mech_len) == 0) {
1660                 ret = gssapi_spnego_decapsulate(minor_status,
1661                                                 input_token,
1662                                                 &buf,
1663                                                 &buf_size,
1664                                                 GSS_SPNEGO_MECH);
1665                 if (ret)
1666                         return (ret);
1667         } else
1668                 return (GSS_S_BAD_MECH);
1669
1670         ret = der_match_tag_and_length(buf, buf_size,
1671                                        ASN1_C_CONTEXT, CONS, 1, &len, &taglen);
1672         if (ret)
1673                 return (ret);
1674
1675         if(len > buf_size - taglen)
1676                 return (ASN1_OVERRUN);
1677
1678         ret = decode_NegTokenResp(buf + taglen, len, &resp, NULL);
1679         if (ret) {
1680                 *minor_status = ENOMEM;
1681                 return (GSS_S_FAILURE);
1682         }
1683
1684         if (resp.negState == NULL ||
1685             *(resp.negState) == reject ||
1686             resp.supportedMech == NULL) {
1687                 free_NegTokenResp(&resp);
1688                 return (GSS_S_BAD_MECH);
1689         }
1690
1691         ret = der_put_oid(oidbuf + sizeof(oidbuf) - 1,
1692                           sizeof(oidbuf),
1693                           resp.supportedMech,
1694                           &oidlen);
1695         if (ret || oidlen != GSS_KRB5_MECH->length ||
1696             memcmp(oidbuf + sizeof(oidbuf) - oidlen,
1697                    GSS_KRB5_MECH->elements,
1698                    oidlen) != 0) {
1699                 free_NegTokenResp(&resp);
1700                 return GSS_S_BAD_MECH;
1701         }
1702
1703         if (resp.responseToken != NULL) {
1704                 sub_token.length = resp.responseToken->length;
1705                 sub_token.value  = resp.responseToken->data;
1706         } else {
1707                 sub_token.length = 0;
1708                 sub_token.value  = NULL;
1709         }
1710
1711         ret = gss_init_sec_context(minor_status,
1712                                    initiator_cred_handle,
1713                                    context_handle,
1714                                    target_name,
1715                                    GSS_KRB5_MECH,
1716                                    req_flags,
1717                                    time_req,
1718                                    input_chan_bindings,
1719                                    &sub_token,
1720                                    actual_mech_type,
1721                                    output_token,
1722                                    ret_flags,
1723                                    time_rec);
1724         if (ret) {
1725                 free_NegTokenResp(&resp);
1726                 return (ret);
1727         }
1728
1729         /*
1730          * XXXSRA I don't think this limited implementation ever needs
1731          * to check the MIC -- our preferred mechanism (Kerberos)
1732          * authenticates its own messages and is the only mechanism
1733          * we'll accept, so if the mechanism negotiation completes
1734          * successfully, we don't need the MIC.  See RFC 4178.
1735          */
1736
1737         free_NegTokenResp(&resp);
1738         return (ret);
1739 }
1740
1741
1742
1743 OM_uint32
1744 gss_init_sec_context_spnego(OM_uint32 *minor_status,
1745                             const gss_cred_id_t initiator_cred_handle,
1746                             gss_ctx_id_t *context_handle,
1747                             const gss_name_t target_name,
1748                             const gss_OID mech_type,
1749                             OM_uint32 req_flags,
1750                             OM_uint32 time_req,
1751                             const gss_channel_bindings_t input_chan_bindings,
1752                             const gss_buffer_t input_token,
1753                             gss_OID *actual_mech_type,
1754                             gss_buffer_t output_token,
1755                             OM_uint32 *ret_flags,
1756                             OM_uint32 *time_rec)
1757 {
1758         /* Dirty trick to suppress compiler warnings */
1759
1760         /* Figure out whether we're starting over or processing a reply */
1761
1762         if (input_token == GSS_C_NO_BUFFER || input_token->length == 0)
1763                 return (spnego_initial(minor_status,
1764                                        initiator_cred_handle,
1765                                        context_handle,
1766                                        target_name,
1767                                        mech_type,
1768                                        req_flags,
1769                                        time_req,
1770                                        input_chan_bindings,
1771                                        input_token,
1772                                        actual_mech_type,
1773                                        output_token,
1774                                        ret_flags,
1775                                        time_rec));
1776         else
1777                 return (spnego_reply(minor_status,
1778                                      initiator_cred_handle,
1779                                      context_handle,
1780                                      target_name,
1781                                      mech_type,
1782                                      req_flags,
1783                                      time_req,
1784                                      input_chan_bindings,
1785                                      input_token,
1786                                      actual_mech_type,
1787                                      output_token,
1788                                      ret_flags,
1789                                      time_rec));
1790 }
1791
1792 #endif /* GSSAPI */