1193141Sdougb/*
2262706Serwin * Copyright (C) 2006-2014  Internet Systems Consortium, Inc. ("ISC")
3193141Sdougb *
4193141Sdougb * Permission to use, copy, modify, and/or distribute this software for any
5193141Sdougb * purpose with or without fee is hereby granted, provided that the above
6193141Sdougb * copyright notice and this permission notice appear in all copies.
7193141Sdougb *
8193141Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9193141Sdougb * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10193141Sdougb * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11193141Sdougb * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12193141Sdougb * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13193141Sdougb * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14193141Sdougb * PERFORMANCE OF THIS SOFTWARE.
15193141Sdougb */
16193141Sdougb
17234010Sdougb/* $Id$ */
18193141Sdougb
19193141Sdougb/*! \file
20193141Sdougb * \brief
21193141Sdougb * Portable SPNEGO implementation.
22193141Sdougb *
23193141Sdougb * This is part of a portable implementation of the SPNEGO protocol
24193141Sdougb * (RFCs 2478 and 4178).  This implementation uses the RFC 4178 ASN.1
25193141Sdougb * module but is not a full implementation of the RFC 4178 protocol;
26193141Sdougb * at the moment, we only support GSS-TSIG with Kerberos
27193141Sdougb * authentication, so we only need enough of the SPNEGO protocol to
28193141Sdougb * support that.
29193141Sdougb *
30193141Sdougb * The files that make up this portable SPNEGO implementation are:
31193141Sdougb * \li	spnego.c	(this file)
32193141Sdougb * \li	spnego.h	(API SPNEGO exports to the rest of lib/dns)
33193141Sdougb * \li	spnego.asn1	(SPNEGO ASN.1 module)
34193141Sdougb * \li	spnego_asn1.c	(routines generated from spngo.asn1)
35193141Sdougb * \li	spnego_asn1.pl	(perl script to generate spnego_asn1.c)
36193141Sdougb *
37193141Sdougb * Everything but the functions exported in spnego.h is static, to
38193141Sdougb * avoid possible conflicts with other libraries (particularly Heimdal,
39193141Sdougb * since much of this code comes from Heimdal by way of mod_auth_kerb).
40193141Sdougb *
41193141Sdougb * spnego_asn1.c is shipped as part of lib/dns because generating it
42193141Sdougb * requires both Perl and the Heimdal ASN.1 compiler.  See
43193141Sdougb * spnego_asn1.pl for further details.  We've tried to eliminate all
44193141Sdougb * compiler warnings from the generated code, but you may see a few
45193141Sdougb * when using a compiler version we haven't tested yet.
46193141Sdougb */
47193141Sdougb
48193141Sdougb/*
49193141Sdougb * Portions of this code were derived from mod_auth_kerb and Heimdal.
50193141Sdougb * These packages are available from:
51193141Sdougb *
52193141Sdougb *   http://modauthkerb.sourceforge.net/
53193141Sdougb *   http://www.pdc.kth.se/heimdal/
54193141Sdougb *
55193141Sdougb * and were released under the following licenses:
56193141Sdougb *
57193141Sdougb * ----------------------------------------------------------------
58193141Sdougb *
59193141Sdougb * Copyright (c) 2004 Masarykova universita
60193141Sdougb * (Masaryk University, Brno, Czech Republic)
61193141Sdougb * All rights reserved.
62193141Sdougb *
63193141Sdougb * Redistribution and use in source and binary forms, with or without
64193141Sdougb * modification, are permitted provided that the following conditions are met:
65193141Sdougb *
66193141Sdougb * 1. Redistributions of source code must retain the above copyright notice,
67193141Sdougb *    this list of conditions and the following disclaimer.
68193141Sdougb *
69193141Sdougb * 2. Redistributions in binary form must reproduce the above copyright
70193141Sdougb *    notice, this list of conditions and the following disclaimer in the
71193141Sdougb *    documentation and/or other materials provided with the distribution.
72193141Sdougb *
73193141Sdougb * 3. Neither the name of the University nor the names of its contributors may
74193141Sdougb *    be used to endorse or promote products derived from this software
75193141Sdougb *    without specific prior written permission.
76193141Sdougb *
77193141Sdougb * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
78193141Sdougb * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79193141Sdougb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80193141Sdougb * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
81193141Sdougb * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
82193141Sdougb * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
83193141Sdougb * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
84193141Sdougb * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
85193141Sdougb * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
86193141Sdougb * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
87193141Sdougb * POSSIBILITY OF SUCH DAMAGE.
88193141Sdougb *
89193141Sdougb * ----------------------------------------------------------------
90193141Sdougb *
91193141Sdougb * Copyright (c) 1997 - 2003 Kungliga Tekniska H�gskolan
92193141Sdougb * (Royal Institute of Technology, Stockholm, Sweden).
93193141Sdougb * All rights reserved.
94193141Sdougb *
95193141Sdougb * Redistribution and use in source and binary forms, with or without
96193141Sdougb * modification, are permitted provided that the following conditions
97193141Sdougb * are met:
98193141Sdougb *
99193141Sdougb * 1. Redistributions of source code must retain the above copyright
100193141Sdougb *    notice, this list of conditions and the following disclaimer.
101193141Sdougb *
102193141Sdougb * 2. Redistributions in binary form must reproduce the above copyright
103193141Sdougb *    notice, this list of conditions and the following disclaimer in the
104193141Sdougb *    documentation and/or other materials provided with the distribution.
105193141Sdougb *
106193141Sdougb * 3. Neither the name of the Institute nor the names of its contributors
107193141Sdougb *    may be used to endorse or promote products derived from this software
108193141Sdougb *    without specific prior written permission.
109193141Sdougb *
110193141Sdougb * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
111193141Sdougb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
112193141Sdougb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
113193141Sdougb * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
114193141Sdougb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
115193141Sdougb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
116193141Sdougb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
117193141Sdougb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
118193141Sdougb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
119193141Sdougb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
120193141Sdougb * SUCH DAMAGE.
121193141Sdougb */
122193141Sdougb
123193141Sdougb/*
124193141Sdougb * XXXSRA We should omit this file entirely in Makefile.in via autoconf,
125193141Sdougb * but this will keep it from generating errors until that's written.
126193141Sdougb */
127193141Sdougb
128193141Sdougb#ifdef GSSAPI
129193141Sdougb
130193141Sdougb/*
131193141Sdougb * XXXSRA Some of the following files are almost certainly unnecessary,
132193141Sdougb * but using this list (borrowed from gssapictx.c) gets rid of some
133193141Sdougb * whacky compilation errors when building with MSVC and should be
134193141Sdougb * harmless in any case.
135193141Sdougb */
136193141Sdougb
137193141Sdougb#include <config.h>
138193141Sdougb
139193141Sdougb#include <stdlib.h>
140193141Sdougb#include <errno.h>
141193141Sdougb
142193141Sdougb#include <isc/buffer.h>
143193141Sdougb#include <isc/dir.h>
144193141Sdougb#include <isc/entropy.h>
145193141Sdougb#include <isc/lex.h>
146193141Sdougb#include <isc/mem.h>
147193141Sdougb#include <isc/once.h>
148193141Sdougb#include <isc/random.h>
149193141Sdougb#include <isc/string.h>
150193141Sdougb#include <isc/time.h>
151193141Sdougb#include <isc/util.h>
152193141Sdougb
153193141Sdougb#include <dns/fixedname.h>
154193141Sdougb#include <dns/name.h>
155193141Sdougb#include <dns/rdata.h>
156193141Sdougb#include <dns/rdataclass.h>
157193141Sdougb#include <dns/result.h>
158193141Sdougb#include <dns/types.h>
159193141Sdougb#include <dns/keyvalues.h>
160193141Sdougb#include <dns/log.h>
161193141Sdougb
162193141Sdougb#include <dst/gssapi.h>
163193141Sdougb#include <dst/result.h>
164193141Sdougb
165193141Sdougb#include "dst_internal.h"
166193141Sdougb
167193141Sdougb/*
168193141Sdougb * The API we export
169193141Sdougb */
170193141Sdougb#include "spnego.h"
171193141Sdougb
172193141Sdougb/* asn1_err.h */
173193141Sdougb/* Generated from ../../../lib/asn1/asn1_err.et */
174193141Sdougb
175224092Sdougb#ifndef ERROR_TABLE_BASE_asn1
176224092Sdougb/* these may be brought in already via gssapi_krb5.h */
177193141Sdougbtypedef enum asn1_error_number {
178193141Sdougb	ASN1_BAD_TIMEFORMAT = 1859794432,
179193141Sdougb	ASN1_MISSING_FIELD = 1859794433,
180193141Sdougb	ASN1_MISPLACED_FIELD = 1859794434,
181193141Sdougb	ASN1_TYPE_MISMATCH = 1859794435,
182193141Sdougb	ASN1_OVERFLOW = 1859794436,
183193141Sdougb	ASN1_OVERRUN = 1859794437,
184193141Sdougb	ASN1_BAD_ID = 1859794438,
185193141Sdougb	ASN1_BAD_LENGTH = 1859794439,
186193141Sdougb	ASN1_BAD_FORMAT = 1859794440,
187193141Sdougb	ASN1_PARSE_ERROR = 1859794441
188193141Sdougb} asn1_error_number;
189193141Sdougb
190193141Sdougb#define ERROR_TABLE_BASE_asn1 1859794432
191224092Sdougb#endif
192193141Sdougb
193193141Sdougb#define __asn1_common_definitions__
194193141Sdougb
195193141Sdougbtypedef struct octet_string {
196193141Sdougb	size_t length;
197193141Sdougb	void *data;
198193141Sdougb} octet_string;
199193141Sdougb
200193141Sdougbtypedef char *general_string;
201193141Sdougb
202193141Sdougbtypedef char *utf8_string;
203193141Sdougb
204193141Sdougbtypedef struct oid {
205193141Sdougb	size_t length;
206193141Sdougb	unsigned *components;
207193141Sdougb} oid;
208193141Sdougb
209193141Sdougb/* der.h */
210193141Sdougb
211193141Sdougbtypedef enum {
212193141Sdougb	ASN1_C_UNIV = 0, ASN1_C_APPL = 1,
213193141Sdougb	ASN1_C_CONTEXT = 2, ASN1_C_PRIVATE = 3
214193141Sdougb} Der_class;
215193141Sdougb
216193141Sdougbtypedef enum {
217193141Sdougb	PRIM = 0, CONS = 1
218193141Sdougb} Der_type;
219193141Sdougb
220193141Sdougb/* Universal tags */
221193141Sdougb
222193141Sdougbenum {
223193141Sdougb	UT_Boolean = 1,
224193141Sdougb	UT_Integer = 2,
225193141Sdougb	UT_BitString = 3,
226193141Sdougb	UT_OctetString = 4,
227193141Sdougb	UT_Null = 5,
228193141Sdougb	UT_OID = 6,
229193141Sdougb	UT_Enumerated = 10,
230193141Sdougb	UT_Sequence = 16,
231193141Sdougb	UT_Set = 17,
232193141Sdougb	UT_PrintableString = 19,
233193141Sdougb	UT_IA5String = 22,
234193141Sdougb	UT_UTCTime = 23,
235193141Sdougb	UT_GeneralizedTime = 24,
236193141Sdougb	UT_VisibleString = 26,
237193141Sdougb	UT_GeneralString = 27
238193141Sdougb};
239193141Sdougb
240193141Sdougb#define ASN1_INDEFINITE 0xdce0deed
241193141Sdougb
242193141Sdougbstatic int
243193141Sdougbder_get_length(const unsigned char *p, size_t len,
244193141Sdougb	       size_t * val, size_t * size);
245193141Sdougb
246193141Sdougbstatic int
247193141Sdougbder_get_octet_string(const unsigned char *p, size_t len,
248193141Sdougb		     octet_string * data, size_t * size);
249193141Sdougbstatic int
250193141Sdougbder_get_oid(const unsigned char *p, size_t len,
251193141Sdougb	    oid * data, size_t * size);
252193141Sdougbstatic int
253193141Sdougbder_get_tag(const unsigned char *p, size_t len,
254193141Sdougb	    Der_class * class, Der_type * type,
255193141Sdougb	    int *tag, size_t * size);
256193141Sdougb
257193141Sdougbstatic int
258193141Sdougbder_match_tag(const unsigned char *p, size_t len,
259193141Sdougb	      Der_class class, Der_type type,
260193141Sdougb	      int tag, size_t * size);
261193141Sdougbstatic int
262193141Sdougbder_match_tag_and_length(const unsigned char *p, size_t len,
263193141Sdougb			 Der_class class, Der_type type, int tag,
264193141Sdougb			 size_t * length_ret, size_t * size);
265193141Sdougb
266193141Sdougbstatic int
267193141Sdougbdecode_oid(const unsigned char *p, size_t len,
268193141Sdougb	   oid * k, size_t * size);
269193141Sdougb
270193141Sdougbstatic int
271204619Sdougbdecode_enumerated(const unsigned char *p, size_t len, void *num, size_t *size);
272193141Sdougb
273193141Sdougbstatic int
274193141Sdougbdecode_octet_string(const unsigned char *, size_t, octet_string *, size_t *);
275193141Sdougb
276193141Sdougbstatic int
277193141Sdougbder_put_int(unsigned char *p, size_t len, int val, size_t *);
278193141Sdougb
279193141Sdougbstatic int
280193141Sdougbder_put_length(unsigned char *p, size_t len, size_t val, size_t *);
281193141Sdougb
282193141Sdougbstatic int
283193141Sdougbder_put_octet_string(unsigned char *p, size_t len,
284193141Sdougb		     const octet_string * data, size_t *);
285193141Sdougbstatic int
286193141Sdougbder_put_oid(unsigned char *p, size_t len,
287193141Sdougb	    const oid * data, size_t * size);
288193141Sdougbstatic int
289193141Sdougbder_put_tag(unsigned char *p, size_t len, Der_class class, Der_type type,
290193141Sdougb	    int tag, size_t *);
291193141Sdougbstatic int
292193141Sdougbder_put_length_and_tag(unsigned char *, size_t, size_t,
293193141Sdougb		       Der_class, Der_type, int, size_t *);
294193141Sdougb
295193141Sdougbstatic int
296204619Sdougbencode_enumerated(unsigned char *p, size_t len, const void *data, size_t *);
297193141Sdougb
298193141Sdougbstatic int
299193141Sdougbencode_octet_string(unsigned char *p, size_t len,
300193141Sdougb		    const octet_string * k, size_t *);
301193141Sdougbstatic int
302193141Sdougbencode_oid(unsigned char *p, size_t len,
303193141Sdougb	   const oid * k, size_t *);
304193141Sdougb
305193141Sdougbstatic void
306193141Sdougbfree_octet_string(octet_string * k);
307193141Sdougb
308193141Sdougbstatic void
309193141Sdougbfree_oid  (oid * k);
310193141Sdougb
311193141Sdougbstatic size_t
312193141Sdougblength_len(size_t len);
313193141Sdougb
314193141Sdougbstatic int
315193141Sdougbfix_dce(size_t reallen, size_t * len);
316193141Sdougb
317193141Sdougb/*
318193141Sdougb * Include stuff generated by the ASN.1 compiler.
319193141Sdougb */
320193141Sdougb
321193141Sdougb#include "spnego_asn1.c"
322193141Sdougb
323193141Sdougbstatic unsigned char gss_krb5_mech_oid_bytes[] = {
324193141Sdougb	0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
325193141Sdougb};
326193141Sdougb
327193141Sdougbstatic gss_OID_desc gss_krb5_mech_oid_desc = {
328193141Sdougb	sizeof(gss_krb5_mech_oid_bytes),
329193141Sdougb	gss_krb5_mech_oid_bytes
330193141Sdougb};
331193141Sdougb
332193141Sdougbstatic gss_OID GSS_KRB5_MECH = &gss_krb5_mech_oid_desc;
333193141Sdougb
334193141Sdougbstatic unsigned char gss_mskrb5_mech_oid_bytes[] = {
335193141Sdougb	0x2a, 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02
336193141Sdougb};
337193141Sdougb
338193141Sdougbstatic gss_OID_desc gss_mskrb5_mech_oid_desc = {
339193141Sdougb	sizeof(gss_mskrb5_mech_oid_bytes),
340193141Sdougb	gss_mskrb5_mech_oid_bytes
341193141Sdougb};
342193141Sdougb
343193141Sdougbstatic gss_OID GSS_MSKRB5_MECH = &gss_mskrb5_mech_oid_desc;
344193141Sdougb
345193141Sdougbstatic unsigned char gss_spnego_mech_oid_bytes[] = {
346193141Sdougb	0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
347193141Sdougb};
348193141Sdougb
349193141Sdougbstatic gss_OID_desc gss_spnego_mech_oid_desc = {
350193141Sdougb	sizeof(gss_spnego_mech_oid_bytes),
351193141Sdougb	gss_spnego_mech_oid_bytes
352193141Sdougb};
353193141Sdougb
354193141Sdougbstatic gss_OID GSS_SPNEGO_MECH = &gss_spnego_mech_oid_desc;
355193141Sdougb
356193141Sdougb/* spnegokrb5_locl.h */
357193141Sdougb
358193141Sdougbstatic OM_uint32
359193141Sdougbgssapi_spnego_encapsulate(OM_uint32 *,
360193141Sdougb			  unsigned char *,
361193141Sdougb			  size_t,
362193141Sdougb			  gss_buffer_t,
363193141Sdougb			  const gss_OID);
364193141Sdougb
365193141Sdougbstatic OM_uint32
366193141Sdougbgssapi_spnego_decapsulate(OM_uint32 *,
367193141Sdougb			  gss_buffer_t,
368193141Sdougb			  unsigned char **,
369193141Sdougb			  size_t *,
370193141Sdougb			  const gss_OID);
371193141Sdougb
372193141Sdougb/* mod_auth_kerb.c */
373193141Sdougb
374193141Sdougbstatic int
375193141Sdougbcmp_gss_type(gss_buffer_t token, gss_OID oid)
376193141Sdougb{
377193141Sdougb	unsigned char *p;
378193141Sdougb	size_t len;
379193141Sdougb
380225361Sdougb	if (token->length == 0U)
381193141Sdougb		return (GSS_S_DEFECTIVE_TOKEN);
382193141Sdougb
383193141Sdougb	p = token->value;
384193141Sdougb	if (*p++ != 0x60)
385193141Sdougb		return (GSS_S_DEFECTIVE_TOKEN);
386193141Sdougb	len = *p++;
387193141Sdougb	if (len & 0x80) {
388225361Sdougb		if ((len & 0x7f) > 4U)
389193141Sdougb			return (GSS_S_DEFECTIVE_TOKEN);
390193141Sdougb		p += len & 0x7f;
391193141Sdougb	}
392193141Sdougb	if (*p++ != 0x06)
393193141Sdougb		return (GSS_S_DEFECTIVE_TOKEN);
394193141Sdougb
395193141Sdougb	if (((OM_uint32) *p++) != oid->length)
396193141Sdougb		return (GSS_S_DEFECTIVE_TOKEN);
397193141Sdougb
398193141Sdougb	return (memcmp(p, oid->elements, oid->length));
399193141Sdougb}
400193141Sdougb
401193141Sdougb/* accept_sec_context.c */
402193141Sdougb/*
403193141Sdougb * SPNEGO wrapper for Kerberos5 GSS-API kouril@ics.muni.cz, 2003 (mostly
404193141Sdougb * based on Heimdal code)
405193141Sdougb */
406193141Sdougb
407193141Sdougbstatic OM_uint32
408193141Sdougbcode_NegTokenArg(OM_uint32 * minor_status,
409193141Sdougb		 const NegTokenResp * resp,
410193141Sdougb		 unsigned char **outbuf,
411193141Sdougb		 size_t * outbuf_size)
412193141Sdougb{
413193141Sdougb	OM_uint32 ret;
414193141Sdougb	u_char *buf;
415224092Sdougb	size_t buf_size, buf_len = 0;
416193141Sdougb
417193141Sdougb	buf_size = 1024;
418193141Sdougb	buf = malloc(buf_size);
419193141Sdougb	if (buf == NULL) {
420193141Sdougb		*minor_status = ENOMEM;
421193141Sdougb		return (GSS_S_FAILURE);
422193141Sdougb	}
423193141Sdougb	do {
424193141Sdougb		ret = encode_NegTokenResp(buf + buf_size - 1,
425193141Sdougb					  buf_size,
426193141Sdougb					  resp, &buf_len);
427193141Sdougb		if (ret == 0) {
428193141Sdougb			size_t tmp;
429193141Sdougb
430193141Sdougb			ret = der_put_length_and_tag(buf + buf_size - buf_len - 1,
431193141Sdougb						     buf_size - buf_len,
432193141Sdougb						     buf_len,
433193141Sdougb						     ASN1_C_CONTEXT,
434193141Sdougb						     CONS,
435193141Sdougb						     1,
436193141Sdougb						     &tmp);
437193141Sdougb			if (ret == 0)
438193141Sdougb				buf_len += tmp;
439193141Sdougb		}
440193141Sdougb		if (ret) {
441193141Sdougb			if (ret == ASN1_OVERFLOW) {
442193141Sdougb				u_char *tmp;
443193141Sdougb
444193141Sdougb				buf_size *= 2;
445193141Sdougb				tmp = realloc(buf, buf_size);
446193141Sdougb				if (tmp == NULL) {
447193141Sdougb					*minor_status = ENOMEM;
448193141Sdougb					free(buf);
449193141Sdougb					return (GSS_S_FAILURE);
450193141Sdougb				}
451193141Sdougb				buf = tmp;
452193141Sdougb			} else {
453193141Sdougb				*minor_status = ret;
454193141Sdougb				free(buf);
455193141Sdougb				return (GSS_S_FAILURE);
456193141Sdougb			}
457193141Sdougb		}
458193141Sdougb	} while (ret == ASN1_OVERFLOW);
459193141Sdougb
460193141Sdougb	*outbuf = malloc(buf_len);
461193141Sdougb	if (*outbuf == NULL) {
462193141Sdougb		*minor_status = ENOMEM;
463193141Sdougb		free(buf);
464193141Sdougb		return (GSS_S_FAILURE);
465193141Sdougb	}
466262706Serwin	memmove(*outbuf, buf + buf_size - buf_len, buf_len);
467193141Sdougb	*outbuf_size = buf_len;
468193141Sdougb
469193141Sdougb	free(buf);
470193141Sdougb
471193141Sdougb	return (GSS_S_COMPLETE);
472193141Sdougb}
473193141Sdougb
474193141Sdougbstatic OM_uint32
475193141Sdougbsend_reject(OM_uint32 * minor_status,
476193141Sdougb	    gss_buffer_t output_token)
477193141Sdougb{
478193141Sdougb	NegTokenResp resp;
479193141Sdougb	OM_uint32 ret;
480193141Sdougb
481193141Sdougb	resp.negState = malloc(sizeof(*resp.negState));
482193141Sdougb	if (resp.negState == NULL) {
483193141Sdougb		*minor_status = ENOMEM;
484193141Sdougb		return (GSS_S_FAILURE);
485193141Sdougb	}
486193141Sdougb	*(resp.negState) = reject;
487193141Sdougb
488193141Sdougb	resp.supportedMech = NULL;
489193141Sdougb	resp.responseToken = NULL;
490193141Sdougb	resp.mechListMIC = NULL;
491193141Sdougb
492193141Sdougb	ret = code_NegTokenArg(minor_status, &resp,
493193141Sdougb			       (unsigned char **)&output_token->value,
494193141Sdougb			       &output_token->length);
495193141Sdougb	free_NegTokenResp(&resp);
496193141Sdougb	if (ret)
497193141Sdougb		return (ret);
498193141Sdougb
499193141Sdougb	return (GSS_S_BAD_MECH);
500193141Sdougb}
501193141Sdougb
502193141Sdougbstatic OM_uint32
503193141Sdougbsend_accept(OM_uint32 * minor_status,
504193141Sdougb	    gss_buffer_t output_token,
505193141Sdougb	    gss_buffer_t mech_token,
506193141Sdougb	    const gss_OID pref)
507193141Sdougb{
508193141Sdougb	NegTokenResp resp;
509193141Sdougb	OM_uint32 ret;
510193141Sdougb
511193141Sdougb	memset(&resp, 0, sizeof(resp));
512193141Sdougb	resp.negState = malloc(sizeof(*resp.negState));
513193141Sdougb	if (resp.negState == NULL) {
514193141Sdougb		*minor_status = ENOMEM;
515193141Sdougb		return (GSS_S_FAILURE);
516193141Sdougb	}
517193141Sdougb	*(resp.negState) = accept_completed;
518193141Sdougb
519193141Sdougb	resp.supportedMech = malloc(sizeof(*resp.supportedMech));
520193141Sdougb	if (resp.supportedMech == NULL) {
521193141Sdougb		free_NegTokenResp(&resp);
522193141Sdougb		*minor_status = ENOMEM;
523193141Sdougb		return (GSS_S_FAILURE);
524193141Sdougb	}
525193141Sdougb	ret = der_get_oid(pref->elements,
526193141Sdougb			  pref->length,
527193141Sdougb			  resp.supportedMech,
528193141Sdougb			  NULL);
529193141Sdougb	if (ret) {
530193141Sdougb		free_NegTokenResp(&resp);
531193141Sdougb		*minor_status = ENOMEM;
532193141Sdougb		return (GSS_S_FAILURE);
533193141Sdougb	}
534225361Sdougb	if (mech_token != NULL && mech_token->length != 0U) {
535193141Sdougb		resp.responseToken = malloc(sizeof(*resp.responseToken));
536193141Sdougb		if (resp.responseToken == NULL) {
537193141Sdougb			free_NegTokenResp(&resp);
538193141Sdougb			*minor_status = ENOMEM;
539193141Sdougb			return (GSS_S_FAILURE);
540193141Sdougb		}
541193141Sdougb		resp.responseToken->length = mech_token->length;
542193141Sdougb		resp.responseToken->data = mech_token->value;
543193141Sdougb	}
544193141Sdougb
545193141Sdougb	ret = code_NegTokenArg(minor_status, &resp,
546193141Sdougb			       (unsigned char **)&output_token->value,
547193141Sdougb			       &output_token->length);
548193141Sdougb	if (resp.responseToken != NULL) {
549193141Sdougb		free(resp.responseToken);
550193141Sdougb		resp.responseToken = NULL;
551193141Sdougb	}
552193141Sdougb	free_NegTokenResp(&resp);
553193141Sdougb	if (ret)
554193141Sdougb		return (ret);
555193141Sdougb
556193141Sdougb	return (GSS_S_COMPLETE);
557193141Sdougb}
558193141Sdougb
559193141SdougbOM_uint32
560193141Sdougbgss_accept_sec_context_spnego(OM_uint32 *minor_status,
561193141Sdougb			      gss_ctx_id_t *context_handle,
562193141Sdougb			      const gss_cred_id_t acceptor_cred_handle,
563193141Sdougb			      const gss_buffer_t input_token_buffer,
564193141Sdougb			      const gss_channel_bindings_t input_chan_bindings,
565193141Sdougb			      gss_name_t *src_name,
566193141Sdougb			      gss_OID *mech_type,
567193141Sdougb			      gss_buffer_t output_token,
568193141Sdougb			      OM_uint32 *ret_flags,
569193141Sdougb			      OM_uint32 *time_rec,
570193141Sdougb			      gss_cred_id_t *delegated_cred_handle)
571193141Sdougb{
572193141Sdougb	NegTokenInit init_token;
573193141Sdougb	OM_uint32 major_status;
574193141Sdougb	OM_uint32 minor_status2;
575193141Sdougb	gss_buffer_desc ibuf, obuf;
576193141Sdougb	gss_buffer_t ot = NULL;
577193141Sdougb	gss_OID pref = GSS_KRB5_MECH;
578193141Sdougb	unsigned char *buf;
579193141Sdougb	size_t buf_size;
580193141Sdougb	size_t len, taglen, ni_len;
581193141Sdougb	int found = 0;
582193141Sdougb	int ret;
583193141Sdougb	unsigned i;
584193141Sdougb
585193141Sdougb	/*
586193141Sdougb	 * Before doing anything else, see whether this is a SPNEGO
587193141Sdougb	 * PDU.  If not, dispatch to the GSSAPI library and get out.
588193141Sdougb	 */
589193141Sdougb
590193141Sdougb	if (cmp_gss_type(input_token_buffer, GSS_SPNEGO_MECH))
591193141Sdougb		return (gss_accept_sec_context(minor_status,
592193141Sdougb					       context_handle,
593193141Sdougb					       acceptor_cred_handle,
594193141Sdougb					       input_token_buffer,
595193141Sdougb					       input_chan_bindings,
596193141Sdougb					       src_name,
597193141Sdougb					       mech_type,
598193141Sdougb					       output_token,
599193141Sdougb					       ret_flags,
600193141Sdougb					       time_rec,
601193141Sdougb					       delegated_cred_handle));
602193141Sdougb
603193141Sdougb	/*
604193141Sdougb	 * If we get here, it's SPNEGO.
605193141Sdougb	 */
606193141Sdougb
607193141Sdougb	memset(&init_token, 0, sizeof(init_token));
608193141Sdougb
609193141Sdougb	ret = gssapi_spnego_decapsulate(minor_status, input_token_buffer,
610193141Sdougb					&buf, &buf_size, GSS_SPNEGO_MECH);
611193141Sdougb	if (ret)
612193141Sdougb		return (ret);
613193141Sdougb
614193141Sdougb	ret = der_match_tag_and_length(buf, buf_size, ASN1_C_CONTEXT, CONS,
615193141Sdougb				       0, &len, &taglen);
616193141Sdougb	if (ret)
617193141Sdougb		return (ret);
618193141Sdougb
619193141Sdougb	ret = decode_NegTokenInit(buf + taglen, len, &init_token, &ni_len);
620193141Sdougb	if (ret) {
621193141Sdougb		*minor_status = EINVAL;	/* XXX */
622193141Sdougb		return (GSS_S_DEFECTIVE_TOKEN);
623193141Sdougb	}
624193141Sdougb
625193141Sdougb	for (i = 0; !found && i < init_token.mechTypes.len; ++i) {
626204619Sdougb		unsigned char mechbuf[17];
627193141Sdougb		size_t mech_len;
628193141Sdougb
629193141Sdougb		ret = der_put_oid(mechbuf + sizeof(mechbuf) - 1,
630193141Sdougb				  sizeof(mechbuf),
631193141Sdougb				  &init_token.mechTypes.val[i],
632193141Sdougb				  &mech_len);
633254402Serwin		if (ret) {
634254402Serwin			free_NegTokenInit(&init_token);
635193141Sdougb			return (GSS_S_DEFECTIVE_TOKEN);
636254402Serwin		}
637193141Sdougb		if (mech_len == GSS_KRB5_MECH->length &&
638193141Sdougb		    memcmp(GSS_KRB5_MECH->elements,
639193141Sdougb			   mechbuf + sizeof(mechbuf) - mech_len,
640193141Sdougb			   mech_len) == 0) {
641193141Sdougb			found = 1;
642193141Sdougb			break;
643193141Sdougb		}
644193141Sdougb		if (mech_len == GSS_MSKRB5_MECH->length &&
645193141Sdougb		    memcmp(GSS_MSKRB5_MECH->elements,
646193141Sdougb			   mechbuf + sizeof(mechbuf) - mech_len,
647193141Sdougb			   mech_len) == 0) {
648193141Sdougb			found = 1;
649193141Sdougb			if (i == 0)
650193141Sdougb				pref = GSS_MSKRB5_MECH;
651193141Sdougb			break;
652193141Sdougb		}
653193141Sdougb	}
654193141Sdougb
655254402Serwin	if (!found) {
656254402Serwin		free_NegTokenInit(&init_token);
657193141Sdougb		return (send_reject(minor_status, output_token));
658254402Serwin	}
659193141Sdougb
660193141Sdougb	if (i == 0 && init_token.mechToken != NULL) {
661193141Sdougb		ibuf.length = init_token.mechToken->length;
662193141Sdougb		ibuf.value = init_token.mechToken->data;
663193141Sdougb
664193141Sdougb		major_status = gss_accept_sec_context(minor_status,
665193141Sdougb						      context_handle,
666193141Sdougb						      acceptor_cred_handle,
667193141Sdougb						      &ibuf,
668193141Sdougb						      input_chan_bindings,
669193141Sdougb						      src_name,
670193141Sdougb						      mech_type,
671193141Sdougb						      &obuf,
672193141Sdougb						      ret_flags,
673193141Sdougb						      time_rec,
674193141Sdougb						      delegated_cred_handle);
675193141Sdougb		if (GSS_ERROR(major_status)) {
676254402Serwin			free_NegTokenInit(&init_token);
677193141Sdougb			send_reject(&minor_status2, output_token);
678193141Sdougb			return (major_status);
679193141Sdougb		}
680193141Sdougb		ot = &obuf;
681193141Sdougb	}
682193141Sdougb	ret = send_accept(&minor_status2, output_token, ot, pref);
683254402Serwin	free_NegTokenInit(&init_token);
684225361Sdougb	if (ot != NULL && ot->length != 0U)
685193141Sdougb		gss_release_buffer(&minor_status2, ot);
686193141Sdougb
687193141Sdougb	return (ret);
688193141Sdougb}
689193141Sdougb
690193141Sdougb/* decapsulate.c */
691193141Sdougb
692193141Sdougbstatic OM_uint32
693193141Sdougbgssapi_verify_mech_header(u_char ** str,
694193141Sdougb			  size_t total_len,
695193141Sdougb			  const gss_OID mech)
696193141Sdougb{
697193141Sdougb	size_t len, len_len, mech_len, foo;
698193141Sdougb	int e;
699193141Sdougb	u_char *p = *str;
700193141Sdougb
701225361Sdougb	if (total_len < 1U)
702193141Sdougb		return (GSS_S_DEFECTIVE_TOKEN);
703193141Sdougb	if (*p++ != 0x60)
704193141Sdougb		return (GSS_S_DEFECTIVE_TOKEN);
705193141Sdougb	e = der_get_length(p, total_len - 1, &len, &len_len);
706193141Sdougb	if (e || 1 + len_len + len != total_len)
707193141Sdougb		return (GSS_S_DEFECTIVE_TOKEN);
708193141Sdougb	p += len_len;
709193141Sdougb	if (*p++ != 0x06)
710193141Sdougb		return (GSS_S_DEFECTIVE_TOKEN);
711193141Sdougb	e = der_get_length(p, total_len - 1 - len_len - 1,
712193141Sdougb			   &mech_len, &foo);
713193141Sdougb	if (e)
714193141Sdougb		return (GSS_S_DEFECTIVE_TOKEN);
715193141Sdougb	p += foo;
716193141Sdougb	if (mech_len != mech->length)
717193141Sdougb		return (GSS_S_BAD_MECH);
718193141Sdougb	if (memcmp(p, mech->elements, mech->length) != 0)
719193141Sdougb		return (GSS_S_BAD_MECH);
720193141Sdougb	p += mech_len;
721193141Sdougb	*str = p;
722193141Sdougb	return (GSS_S_COMPLETE);
723193141Sdougb}
724193141Sdougb
725193141Sdougb/*
726193141Sdougb * Remove the GSS-API wrapping from `in_token' giving `buf and buf_size' Does
727193141Sdougb * not copy data, so just free `in_token'.
728193141Sdougb */
729193141Sdougb
730193141Sdougbstatic OM_uint32
731193141Sdougbgssapi_spnego_decapsulate(OM_uint32 *minor_status,
732193141Sdougb			  gss_buffer_t input_token_buffer,
733193141Sdougb			  unsigned char **buf,
734193141Sdougb			  size_t *buf_len,
735193141Sdougb			  const gss_OID mech)
736193141Sdougb{
737193141Sdougb	u_char *p;
738193141Sdougb	OM_uint32 ret;
739193141Sdougb
740193141Sdougb	p = input_token_buffer->value;
741193141Sdougb	ret = gssapi_verify_mech_header(&p,
742193141Sdougb					input_token_buffer->length,
743193141Sdougb					mech);
744193141Sdougb	if (ret) {
745193141Sdougb		*minor_status = ret;
746193141Sdougb		return (GSS_S_FAILURE);
747193141Sdougb	}
748193141Sdougb	*buf_len = input_token_buffer->length -
749193141Sdougb		(p - (u_char *) input_token_buffer->value);
750193141Sdougb	*buf = p;
751193141Sdougb	return (GSS_S_COMPLETE);
752193141Sdougb}
753193141Sdougb
754193141Sdougb/* der_free.c */
755193141Sdougb
756193141Sdougbstatic void
757193141Sdougbfree_octet_string(octet_string *k)
758193141Sdougb{
759193141Sdougb	free(k->data);
760193141Sdougb	k->data = NULL;
761193141Sdougb}
762193141Sdougb
763193141Sdougbstatic void
764193141Sdougbfree_oid(oid *k)
765193141Sdougb{
766193141Sdougb	free(k->components);
767193141Sdougb	k->components = NULL;
768193141Sdougb}
769193141Sdougb
770193141Sdougb/* der_get.c */
771193141Sdougb
772193141Sdougb/*
773193141Sdougb * All decoding functions take a pointer `p' to first position in which to
774193141Sdougb * read, from the left, `len' which means the maximum number of characters we
775193141Sdougb * are able to read, `ret' were the value will be returned and `size' where
776193141Sdougb * the number of used bytes is stored. Either 0 or an error code is returned.
777193141Sdougb */
778193141Sdougb
779193141Sdougbstatic int
780193141Sdougbder_get_unsigned(const unsigned char *p, size_t len,
781193141Sdougb		 unsigned *ret, size_t *size)
782193141Sdougb{
783193141Sdougb	unsigned val = 0;
784193141Sdougb	size_t oldlen = len;
785193141Sdougb
786193141Sdougb	while (len--)
787193141Sdougb		val = val * 256 + *p++;
788193141Sdougb	*ret = val;
789193141Sdougb	if (size)
790193141Sdougb		*size = oldlen;
791193141Sdougb	return (0);
792193141Sdougb}
793193141Sdougb
794193141Sdougbstatic int
795193141Sdougbder_get_int(const unsigned char *p, size_t len,
796193141Sdougb	    int *ret, size_t *size)
797193141Sdougb{
798193141Sdougb	int val = 0;
799193141Sdougb	size_t oldlen = len;
800193141Sdougb
801225361Sdougb	if (len > 0U) {
802193141Sdougb		val = (signed char)*p++;
803193141Sdougb		while (--len)
804193141Sdougb			val = val * 256 + *p++;
805193141Sdougb	}
806193141Sdougb	*ret = val;
807193141Sdougb	if (size)
808193141Sdougb		*size = oldlen;
809193141Sdougb	return (0);
810193141Sdougb}
811193141Sdougb
812193141Sdougbstatic int
813193141Sdougbder_get_length(const unsigned char *p, size_t len,
814193141Sdougb	       size_t *val, size_t *size)
815193141Sdougb{
816193141Sdougb	size_t v;
817193141Sdougb
818225361Sdougb	if (len <= 0U)
819193141Sdougb		return (ASN1_OVERRUN);
820193141Sdougb	--len;
821193141Sdougb	v = *p++;
822225361Sdougb	if (v < 128U) {
823193141Sdougb		*val = v;
824193141Sdougb		if (size)
825193141Sdougb			*size = 1;
826193141Sdougb	} else {
827193141Sdougb		int e;
828193141Sdougb		size_t l;
829193141Sdougb		unsigned tmp;
830193141Sdougb
831225361Sdougb		if (v == 0x80U) {
832193141Sdougb			*val = ASN1_INDEFINITE;
833193141Sdougb			if (size)
834193141Sdougb				*size = 1;
835193141Sdougb			return (0);
836193141Sdougb		}
837193141Sdougb		v &= 0x7F;
838193141Sdougb		if (len < v)
839193141Sdougb			return (ASN1_OVERRUN);
840193141Sdougb		e = der_get_unsigned(p, v, &tmp, &l);
841193141Sdougb		if (e)
842193141Sdougb			return (e);
843193141Sdougb		*val = tmp;
844193141Sdougb		if (size)
845193141Sdougb			*size = l + 1;
846193141Sdougb	}
847193141Sdougb	return (0);
848193141Sdougb}
849193141Sdougb
850193141Sdougbstatic int
851193141Sdougbder_get_octet_string(const unsigned char *p, size_t len,
852193141Sdougb		     octet_string *data, size_t *size)
853193141Sdougb{
854193141Sdougb	data->length = len;
855254402Serwin	if (len != 0U) {
856254402Serwin		data->data = malloc(len);
857254402Serwin		if (data->data == NULL)
858254402Serwin			return (ENOMEM);
859262706Serwin		memmove(data->data, p, len);
860254402Serwin	} else
861254402Serwin		data->data = NULL;
862193141Sdougb	if (size)
863193141Sdougb		*size = len;
864193141Sdougb	return (0);
865193141Sdougb}
866193141Sdougb
867193141Sdougbstatic int
868193141Sdougbder_get_oid(const unsigned char *p, size_t len,
869193141Sdougb	    oid *data, size_t *size)
870193141Sdougb{
871193141Sdougb	int n;
872193141Sdougb	size_t oldlen = len;
873193141Sdougb
874254402Serwin	data->components = NULL;
875254402Serwin	data->length = 0;
876225361Sdougb	if (len < 1U)
877193141Sdougb		return (ASN1_OVERRUN);
878193141Sdougb
879193141Sdougb	data->components = malloc(len * sizeof(*data->components));
880225361Sdougb	if (data->components == NULL && len != 0U)
881193141Sdougb		return (ENOMEM);
882193141Sdougb	data->components[0] = (*p) / 40;
883193141Sdougb	data->components[1] = (*p) % 40;
884193141Sdougb	--len;
885193141Sdougb	++p;
886225361Sdougb	for (n = 2; len > 0U; ++n) {
887193141Sdougb		unsigned u = 0;
888193141Sdougb
889193141Sdougb		do {
890193141Sdougb			--len;
891193141Sdougb			u = u * 128 + (*p++ % 128);
892225361Sdougb		} while (len > 0U && p[-1] & 0x80);
893193141Sdougb		data->components[n] = u;
894193141Sdougb	}
895193141Sdougb	if (p[-1] & 0x80) {
896193141Sdougb		free_oid(data);
897193141Sdougb		return (ASN1_OVERRUN);
898193141Sdougb	}
899193141Sdougb	data->length = n;
900193141Sdougb	if (size)
901193141Sdougb		*size = oldlen;
902193141Sdougb	return (0);
903193141Sdougb}
904193141Sdougb
905193141Sdougbstatic int
906193141Sdougbder_get_tag(const unsigned char *p, size_t len,
907193141Sdougb	    Der_class *class, Der_type *type,
908193141Sdougb	    int *tag, size_t *size)
909193141Sdougb{
910225361Sdougb	if (len < 1U)
911193141Sdougb		return (ASN1_OVERRUN);
912193141Sdougb	*class = (Der_class) (((*p) >> 6) & 0x03);
913193141Sdougb	*type = (Der_type) (((*p) >> 5) & 0x01);
914193141Sdougb	*tag = (*p) & 0x1F;
915193141Sdougb	if (size)
916193141Sdougb		*size = 1;
917193141Sdougb	return (0);
918193141Sdougb}
919193141Sdougb
920193141Sdougbstatic int
921193141Sdougbder_match_tag(const unsigned char *p, size_t len,
922193141Sdougb	      Der_class class, Der_type type,
923193141Sdougb	      int tag, size_t *size)
924193141Sdougb{
925193141Sdougb	size_t l;
926193141Sdougb	Der_class thisclass;
927193141Sdougb	Der_type thistype;
928193141Sdougb	int thistag;
929193141Sdougb	int e;
930193141Sdougb
931193141Sdougb	e = der_get_tag(p, len, &thisclass, &thistype, &thistag, &l);
932193141Sdougb	if (e)
933193141Sdougb		return (e);
934193141Sdougb	if (class != thisclass || type != thistype)
935193141Sdougb		return (ASN1_BAD_ID);
936193141Sdougb	if (tag > thistag)
937193141Sdougb		return (ASN1_MISPLACED_FIELD);
938193141Sdougb	if (tag < thistag)
939193141Sdougb		return (ASN1_MISSING_FIELD);
940193141Sdougb	if (size)
941193141Sdougb		*size = l;
942193141Sdougb	return (0);
943193141Sdougb}
944193141Sdougb
945193141Sdougbstatic int
946193141Sdougbder_match_tag_and_length(const unsigned char *p, size_t len,
947193141Sdougb			 Der_class class, Der_type type, int tag,
948193141Sdougb			 size_t *length_ret, size_t *size)
949193141Sdougb{
950193141Sdougb	size_t l, ret = 0;
951193141Sdougb	int e;
952193141Sdougb
953193141Sdougb	e = der_match_tag(p, len, class, type, tag, &l);
954193141Sdougb	if (e)
955193141Sdougb		return (e);
956193141Sdougb	p += l;
957193141Sdougb	len -= l;
958193141Sdougb	ret += l;
959193141Sdougb	e = der_get_length(p, len, length_ret, &l);
960193141Sdougb	if (e)
961193141Sdougb		return (e);
962234010Sdougb	/* p += l; */
963193141Sdougb	len -= l;
964234010Sdougb	POST(len);
965193141Sdougb	ret += l;
966193141Sdougb	if (size)
967193141Sdougb		*size = ret;
968193141Sdougb	return (0);
969193141Sdougb}
970193141Sdougb
971193141Sdougbstatic int
972204619Sdougbdecode_enumerated(const unsigned char *p, size_t len, void *num, size_t *size)
973193141Sdougb{
974193141Sdougb	size_t ret = 0;
975193141Sdougb	size_t l, reallen;
976193141Sdougb	int e;
977193141Sdougb
978193141Sdougb	e = der_match_tag(p, len, ASN1_C_UNIV, PRIM, UT_Enumerated, &l);
979193141Sdougb	if (e)
980193141Sdougb		return (e);
981193141Sdougb	p += l;
982193141Sdougb	len -= l;
983193141Sdougb	ret += l;
984193141Sdougb	e = der_get_length(p, len, &reallen, &l);
985193141Sdougb	if (e)
986193141Sdougb		return (e);
987193141Sdougb	p += l;
988193141Sdougb	len -= l;
989193141Sdougb	ret += l;
990193141Sdougb	e = der_get_int(p, reallen, num, &l);
991193141Sdougb	if (e)
992193141Sdougb		return (e);
993193141Sdougb	p += l;
994193141Sdougb	len -= l;
995234010Sdougb	POST(p); POST(len);
996193141Sdougb	ret += l;
997193141Sdougb	if (size)
998193141Sdougb		*size = ret;
999193141Sdougb	return (0);
1000193141Sdougb}
1001193141Sdougb
1002193141Sdougbstatic int
1003193141Sdougbdecode_octet_string(const unsigned char *p, size_t len,
1004193141Sdougb		    octet_string *k, size_t *size)
1005193141Sdougb{
1006193141Sdougb	size_t ret = 0;
1007193141Sdougb	size_t l;
1008193141Sdougb	int e;
1009193141Sdougb	size_t slen;
1010193141Sdougb
1011254402Serwin	k->data = NULL;
1012254402Serwin	k->length = 0;
1013254402Serwin
1014193141Sdougb	e = der_match_tag(p, len, ASN1_C_UNIV, PRIM, UT_OctetString, &l);
1015193141Sdougb	if (e)
1016193141Sdougb		return (e);
1017193141Sdougb	p += l;
1018193141Sdougb	len -= l;
1019193141Sdougb	ret += l;
1020193141Sdougb
1021193141Sdougb	e = der_get_length(p, len, &slen, &l);
1022193141Sdougb	if (e)
1023193141Sdougb		return (e);
1024193141Sdougb	p += l;
1025193141Sdougb	len -= l;
1026193141Sdougb	ret += l;
1027193141Sdougb	if (len < slen)
1028193141Sdougb		return (ASN1_OVERRUN);
1029193141Sdougb
1030193141Sdougb	e = der_get_octet_string(p, slen, k, &l);
1031193141Sdougb	if (e)
1032193141Sdougb		return (e);
1033193141Sdougb	p += l;
1034193141Sdougb	len -= l;
1035234010Sdougb	POST(p); POST(len);
1036193141Sdougb	ret += l;
1037193141Sdougb	if (size)
1038193141Sdougb		*size = ret;
1039193141Sdougb	return (0);
1040193141Sdougb}
1041193141Sdougb
1042193141Sdougbstatic int
1043193141Sdougbdecode_oid(const unsigned char *p, size_t len,
1044193141Sdougb	   oid *k, size_t *size)
1045193141Sdougb{
1046193141Sdougb	size_t ret = 0;
1047193141Sdougb	size_t l;
1048193141Sdougb	int e;
1049193141Sdougb	size_t slen;
1050193141Sdougb
1051193141Sdougb	e = der_match_tag(p, len, ASN1_C_UNIV, PRIM, UT_OID, &l);
1052193141Sdougb	if (e)
1053193141Sdougb		return (e);
1054193141Sdougb	p += l;
1055193141Sdougb	len -= l;
1056193141Sdougb	ret += l;
1057193141Sdougb
1058193141Sdougb	e = der_get_length(p, len, &slen, &l);
1059193141Sdougb	if (e)
1060193141Sdougb		return (e);
1061193141Sdougb	p += l;
1062193141Sdougb	len -= l;
1063193141Sdougb	ret += l;
1064193141Sdougb	if (len < slen)
1065193141Sdougb		return (ASN1_OVERRUN);
1066193141Sdougb
1067193141Sdougb	e = der_get_oid(p, slen, k, &l);
1068193141Sdougb	if (e)
1069193141Sdougb		return (e);
1070193141Sdougb	p += l;
1071193141Sdougb	len -= l;
1072234010Sdougb	POST(p); POST(len);
1073193141Sdougb	ret += l;
1074193141Sdougb	if (size)
1075193141Sdougb		*size = ret;
1076193141Sdougb	return (0);
1077193141Sdougb}
1078193141Sdougb
1079193141Sdougbstatic int
1080193141Sdougbfix_dce(size_t reallen, size_t *len)
1081193141Sdougb{
1082193141Sdougb	if (reallen == ASN1_INDEFINITE)
1083193141Sdougb		return (1);
1084193141Sdougb	if (*len < reallen)
1085193141Sdougb		return (-1);
1086193141Sdougb	*len = reallen;
1087193141Sdougb	return (0);
1088193141Sdougb}
1089193141Sdougb
1090193141Sdougb/* der_length.c */
1091193141Sdougb
1092193141Sdougbstatic size_t
1093193141Sdougblen_unsigned(unsigned val)
1094193141Sdougb{
1095193141Sdougb	size_t ret = 0;
1096193141Sdougb
1097193141Sdougb	do {
1098193141Sdougb		++ret;
1099193141Sdougb		val /= 256;
1100193141Sdougb	} while (val);
1101193141Sdougb	return (ret);
1102193141Sdougb}
1103193141Sdougb
1104193141Sdougbstatic size_t
1105193141Sdougblength_len(size_t len)
1106193141Sdougb{
1107225361Sdougb	if (len < 128U)
1108193141Sdougb		return (1);
1109193141Sdougb	else
1110262706Serwin		return (len_unsigned((unsigned int)len) + 1);
1111193141Sdougb}
1112193141Sdougb
1113193141Sdougb
1114193141Sdougb/* der_put.c */
1115193141Sdougb
1116193141Sdougb/*
1117193141Sdougb * All encoding functions take a pointer `p' to first position in which to
1118193141Sdougb * write, from the right, `len' which means the maximum number of characters
1119193141Sdougb * we are able to write.  The function returns the number of characters
1120193141Sdougb * written in `size' (if non-NULL). The return value is 0 or an error.
1121193141Sdougb */
1122193141Sdougb
1123193141Sdougbstatic int
1124193141Sdougbder_put_unsigned(unsigned char *p, size_t len, unsigned val, size_t *size)
1125193141Sdougb{
1126193141Sdougb	unsigned char *base = p;
1127193141Sdougb
1128193141Sdougb	if (val) {
1129225361Sdougb		while (len > 0U && val) {
1130193141Sdougb			*p-- = val % 256;
1131193141Sdougb			val /= 256;
1132193141Sdougb			--len;
1133193141Sdougb		}
1134193141Sdougb		if (val != 0)
1135193141Sdougb			return (ASN1_OVERFLOW);
1136193141Sdougb		else {
1137193141Sdougb			*size = base - p;
1138193141Sdougb			return (0);
1139193141Sdougb		}
1140225361Sdougb	} else if (len < 1U)
1141193141Sdougb		return (ASN1_OVERFLOW);
1142193141Sdougb	else {
1143193141Sdougb		*p = 0;
1144193141Sdougb		*size = 1;
1145193141Sdougb		return (0);
1146193141Sdougb	}
1147193141Sdougb}
1148193141Sdougb
1149193141Sdougbstatic int
1150193141Sdougbder_put_int(unsigned char *p, size_t len, int val, size_t *size)
1151193141Sdougb{
1152193141Sdougb	unsigned char *base = p;
1153193141Sdougb
1154193141Sdougb	if (val >= 0) {
1155193141Sdougb		do {
1156225361Sdougb			if (len < 1U)
1157193141Sdougb				return (ASN1_OVERFLOW);
1158193141Sdougb			*p-- = val % 256;
1159193141Sdougb			len--;
1160193141Sdougb			val /= 256;
1161193141Sdougb		} while (val);
1162193141Sdougb		if (p[1] >= 128) {
1163225361Sdougb			if (len < 1U)
1164193141Sdougb				return (ASN1_OVERFLOW);
1165193141Sdougb			*p-- = 0;
1166193141Sdougb			len--;
1167193141Sdougb		}
1168193141Sdougb	} else {
1169193141Sdougb		val = ~val;
1170193141Sdougb		do {
1171225361Sdougb			if (len < 1U)
1172193141Sdougb				return (ASN1_OVERFLOW);
1173193141Sdougb			*p-- = ~(val % 256);
1174193141Sdougb			len--;
1175193141Sdougb			val /= 256;
1176193141Sdougb		} while (val);
1177193141Sdougb		if (p[1] < 128) {
1178225361Sdougb			if (len < 1U)
1179193141Sdougb				return (ASN1_OVERFLOW);
1180193141Sdougb			*p-- = 0xff;
1181193141Sdougb			len--;
1182193141Sdougb		}
1183193141Sdougb	}
1184193141Sdougb	*size = base - p;
1185193141Sdougb	return (0);
1186193141Sdougb}
1187193141Sdougb
1188193141Sdougbstatic int
1189193141Sdougbder_put_length(unsigned char *p, size_t len, size_t val, size_t *size)
1190193141Sdougb{
1191225361Sdougb	if (len < 1U)
1192193141Sdougb		return (ASN1_OVERFLOW);
1193225361Sdougb	if (val < 128U) {
1194262706Serwin		*p = (unsigned char)val;
1195193141Sdougb		*size = 1;
1196193141Sdougb		return (0);
1197193141Sdougb	} else {
1198193141Sdougb		size_t l;
1199193141Sdougb		int e;
1200193141Sdougb
1201262706Serwin		e = der_put_unsigned(p, len - 1, (unsigned int)val, &l);
1202193141Sdougb		if (e)
1203193141Sdougb			return (e);
1204193141Sdougb		p -= l;
1205262706Serwin		*p = 0x80 | (unsigned char)l;
1206193141Sdougb		*size = l + 1;
1207193141Sdougb		return (0);
1208193141Sdougb	}
1209193141Sdougb}
1210193141Sdougb
1211193141Sdougbstatic int
1212193141Sdougbder_put_octet_string(unsigned char *p, size_t len,
1213193141Sdougb		     const octet_string *data, size_t *size)
1214193141Sdougb{
1215193141Sdougb	if (len < data->length)
1216193141Sdougb		return (ASN1_OVERFLOW);
1217193141Sdougb	p -= data->length;
1218193141Sdougb	len -= data->length;
1219234010Sdougb	POST(len);
1220262706Serwin	memmove(p + 1, data->data, data->length);
1221193141Sdougb	*size = data->length;
1222193141Sdougb	return (0);
1223193141Sdougb}
1224193141Sdougb
1225193141Sdougbstatic int
1226193141Sdougbder_put_oid(unsigned char *p, size_t len,
1227193141Sdougb	    const oid *data, size_t *size)
1228193141Sdougb{
1229193141Sdougb	unsigned char *base = p;
1230262706Serwin	size_t n;
1231193141Sdougb
1232262706Serwin	for (n = data->length; n >= 3u; --n) {
1233262706Serwin		unsigned	u = data->components[n - 1];
1234193141Sdougb
1235225361Sdougb		if (len < 1U)
1236193141Sdougb			return (ASN1_OVERFLOW);
1237193141Sdougb		*p-- = u % 128;
1238193141Sdougb		u /= 128;
1239193141Sdougb		--len;
1240193141Sdougb		while (u > 0) {
1241225361Sdougb			if (len < 1U)
1242193141Sdougb				return (ASN1_OVERFLOW);
1243193141Sdougb			*p-- = 128 + u % 128;
1244193141Sdougb			u /= 128;
1245193141Sdougb			--len;
1246193141Sdougb		}
1247193141Sdougb	}
1248225361Sdougb	if (len < 1U)
1249193141Sdougb		return (ASN1_OVERFLOW);
1250193141Sdougb	*p-- = 40 * data->components[0] + data->components[1];
1251193141Sdougb	*size = base - p;
1252193141Sdougb	return (0);
1253193141Sdougb}
1254193141Sdougb
1255193141Sdougbstatic int
1256193141Sdougbder_put_tag(unsigned char *p, size_t len, Der_class class, Der_type type,
1257193141Sdougb	    int tag, size_t *size)
1258193141Sdougb{
1259225361Sdougb	if (len < 1U)
1260193141Sdougb		return (ASN1_OVERFLOW);
1261193141Sdougb	*p = (class << 6) | (type << 5) | tag;	/* XXX */
1262193141Sdougb	*size = 1;
1263193141Sdougb	return (0);
1264193141Sdougb}
1265193141Sdougb
1266193141Sdougbstatic int
1267193141Sdougbder_put_length_and_tag(unsigned char *p, size_t len, size_t len_val,
1268193141Sdougb		       Der_class class, Der_type type, int tag, size_t *size)
1269193141Sdougb{
1270193141Sdougb	size_t ret = 0;
1271193141Sdougb	size_t l;
1272193141Sdougb	int e;
1273193141Sdougb
1274193141Sdougb	e = der_put_length(p, len, len_val, &l);
1275193141Sdougb	if (e)
1276193141Sdougb		return (e);
1277193141Sdougb	p -= l;
1278193141Sdougb	len -= l;
1279193141Sdougb	ret += l;
1280193141Sdougb	e = der_put_tag(p, len, class, type, tag, &l);
1281193141Sdougb	if (e)
1282193141Sdougb		return (e);
1283193141Sdougb	p -= l;
1284193141Sdougb	len -= l;
1285234010Sdougb	POST(p); POST(len);
1286193141Sdougb	ret += l;
1287193141Sdougb	*size = ret;
1288193141Sdougb	return (0);
1289193141Sdougb}
1290193141Sdougb
1291193141Sdougbstatic int
1292204619Sdougbencode_enumerated(unsigned char *p, size_t len, const void *data, size_t *size)
1293193141Sdougb{
1294204619Sdougb	unsigned num = *(const unsigned *)data;
1295193141Sdougb	size_t ret = 0;
1296193141Sdougb	size_t l;
1297193141Sdougb	int e;
1298193141Sdougb
1299193141Sdougb	e = der_put_int(p, len, num, &l);
1300193141Sdougb	if (e)
1301193141Sdougb		return (e);
1302193141Sdougb	p -= l;
1303193141Sdougb	len -= l;
1304193141Sdougb	ret += l;
1305193141Sdougb	e = der_put_length_and_tag(p, len, l, ASN1_C_UNIV, PRIM, UT_Enumerated, &l);
1306193141Sdougb	if (e)
1307193141Sdougb		return (e);
1308193141Sdougb	p -= l;
1309193141Sdougb	len -= l;
1310234010Sdougb	POST(p); POST(len);
1311193141Sdougb	ret += l;
1312193141Sdougb	*size = ret;
1313193141Sdougb	return (0);
1314193141Sdougb}
1315193141Sdougb
1316193141Sdougbstatic int
1317193141Sdougbencode_octet_string(unsigned char *p, size_t len,
1318193141Sdougb		    const octet_string *k, size_t *size)
1319193141Sdougb{
1320193141Sdougb	size_t ret = 0;
1321193141Sdougb	size_t l;
1322193141Sdougb	int e;
1323193141Sdougb
1324193141Sdougb	e = der_put_octet_string(p, len, k, &l);
1325193141Sdougb	if (e)
1326193141Sdougb		return (e);
1327193141Sdougb	p -= l;
1328193141Sdougb	len -= l;
1329193141Sdougb	ret += l;
1330193141Sdougb	e = der_put_length_and_tag(p, len, l, ASN1_C_UNIV, PRIM, UT_OctetString, &l);
1331193141Sdougb	if (e)
1332193141Sdougb		return (e);
1333193141Sdougb	p -= l;
1334193141Sdougb	len -= l;
1335234010Sdougb	POST(p); POST(len);
1336193141Sdougb	ret += l;
1337193141Sdougb	*size = ret;
1338193141Sdougb	return (0);
1339193141Sdougb}
1340193141Sdougb
1341193141Sdougbstatic int
1342193141Sdougbencode_oid(unsigned char *p, size_t len,
1343193141Sdougb	   const oid *k, size_t *size)
1344193141Sdougb{
1345193141Sdougb	size_t ret = 0;
1346193141Sdougb	size_t l;
1347193141Sdougb	int e;
1348193141Sdougb
1349193141Sdougb	e = der_put_oid(p, len, k, &l);
1350193141Sdougb	if (e)
1351193141Sdougb		return (e);
1352193141Sdougb	p -= l;
1353193141Sdougb	len -= l;
1354193141Sdougb	ret += l;
1355193141Sdougb	e = der_put_length_and_tag(p, len, l, ASN1_C_UNIV, PRIM, UT_OID, &l);
1356193141Sdougb	if (e)
1357193141Sdougb		return (e);
1358193141Sdougb	p -= l;
1359193141Sdougb	len -= l;
1360234010Sdougb	POST(p); POST(len);
1361193141Sdougb	ret += l;
1362193141Sdougb	*size = ret;
1363193141Sdougb	return (0);
1364193141Sdougb}
1365193141Sdougb
1366193141Sdougb
1367193141Sdougb/* encapsulate.c */
1368193141Sdougb
1369193141Sdougbstatic void
1370193141Sdougbgssapi_encap_length(size_t data_len,
1371193141Sdougb		    size_t *len,
1372193141Sdougb		    size_t *total_len,
1373193141Sdougb		    const gss_OID mech)
1374193141Sdougb{
1375193141Sdougb	size_t len_len;
1376193141Sdougb
1377193141Sdougb	*len = 1 + 1 + mech->length + data_len;
1378193141Sdougb
1379193141Sdougb	len_len = length_len(*len);
1380193141Sdougb
1381193141Sdougb	*total_len = 1 + len_len + *len;
1382193141Sdougb}
1383193141Sdougb
1384193141Sdougbstatic u_char *
1385193141Sdougbgssapi_mech_make_header(u_char *p,
1386193141Sdougb			size_t len,
1387193141Sdougb			const gss_OID mech)
1388193141Sdougb{
1389193141Sdougb	int e;
1390193141Sdougb	size_t len_len, foo;
1391193141Sdougb
1392193141Sdougb	*p++ = 0x60;
1393193141Sdougb	len_len = length_len(len);
1394193141Sdougb	e = der_put_length(p + len_len - 1, len_len, len, &foo);
1395193141Sdougb	if (e || foo != len_len)
1396193141Sdougb		return (NULL);
1397193141Sdougb	p += len_len;
1398193141Sdougb	*p++ = 0x06;
1399193141Sdougb	*p++ = mech->length;
1400262706Serwin	memmove(p, mech->elements, mech->length);
1401193141Sdougb	p += mech->length;
1402193141Sdougb	return (p);
1403193141Sdougb}
1404193141Sdougb
1405193141Sdougb/*
1406193141Sdougb * Give it a krb5_data and it will encapsulate with extra GSS-API wrappings.
1407193141Sdougb */
1408193141Sdougb
1409193141Sdougbstatic OM_uint32
1410193141Sdougbgssapi_spnego_encapsulate(OM_uint32 * minor_status,
1411193141Sdougb			  unsigned char *buf,
1412193141Sdougb			  size_t buf_size,
1413193141Sdougb			  gss_buffer_t output_token,
1414193141Sdougb			  const gss_OID mech)
1415193141Sdougb{
1416193141Sdougb	size_t len, outer_len;
1417193141Sdougb	u_char *p;
1418193141Sdougb
1419193141Sdougb	gssapi_encap_length(buf_size, &len, &outer_len, mech);
1420193141Sdougb
1421193141Sdougb	output_token->length = outer_len;
1422193141Sdougb	output_token->value = malloc(outer_len);
1423193141Sdougb	if (output_token->value == NULL) {
1424193141Sdougb		*minor_status = ENOMEM;
1425193141Sdougb		return (GSS_S_FAILURE);
1426193141Sdougb	}
1427193141Sdougb	p = gssapi_mech_make_header(output_token->value, len, mech);
1428193141Sdougb	if (p == NULL) {
1429225361Sdougb		if (output_token->length != 0U)
1430193141Sdougb			gss_release_buffer(minor_status, output_token);
1431193141Sdougb		return (GSS_S_FAILURE);
1432193141Sdougb	}
1433262706Serwin	memmove(p, buf, buf_size);
1434193141Sdougb	return (GSS_S_COMPLETE);
1435193141Sdougb}
1436193141Sdougb
1437193141Sdougb/* init_sec_context.c */
1438193141Sdougb/*
1439193141Sdougb * SPNEGO wrapper for Kerberos5 GSS-API kouril@ics.muni.cz, 2003 (mostly
1440193141Sdougb * based on Heimdal code)
1441193141Sdougb */
1442193141Sdougb
1443193141Sdougbstatic int
1444193141Sdougbadd_mech(MechTypeList * mech_list, gss_OID mech)
1445193141Sdougb{
1446193141Sdougb	MechType *tmp;
1447193141Sdougb	int ret;
1448193141Sdougb
1449193141Sdougb	tmp = realloc(mech_list->val, (mech_list->len + 1) * sizeof(*tmp));
1450193141Sdougb	if (tmp == NULL)
1451193141Sdougb		return (ENOMEM);
1452193141Sdougb	mech_list->val = tmp;
1453193141Sdougb
1454193141Sdougb	ret = der_get_oid(mech->elements, mech->length,
1455193141Sdougb			  &mech_list->val[mech_list->len], NULL);
1456193141Sdougb	if (ret)
1457193141Sdougb		return (ret);
1458193141Sdougb
1459193141Sdougb	mech_list->len++;
1460193141Sdougb	return (0);
1461193141Sdougb}
1462193141Sdougb
1463193141Sdougb/*
1464193141Sdougb * return the length of the mechanism in token or -1
1465193141Sdougb * (which implies that the token was bad - GSS_S_DEFECTIVE_TOKEN
1466193141Sdougb */
1467193141Sdougb
1468193141Sdougbstatic ssize_t
1469193141Sdougbgssapi_krb5_get_mech(const u_char *ptr,
1470193141Sdougb		     size_t total_len,
1471193141Sdougb		     const u_char **mech_ret)
1472193141Sdougb{
1473193141Sdougb	size_t len, len_len, mech_len, foo;
1474193141Sdougb	const u_char *p = ptr;
1475193141Sdougb	int e;
1476193141Sdougb
1477225361Sdougb	if (total_len < 1U)
1478193141Sdougb		return (-1);
1479193141Sdougb	if (*p++ != 0x60)
1480193141Sdougb		return (-1);
1481193141Sdougb	e = der_get_length (p, total_len - 1, &len, &len_len);
1482193141Sdougb	if (e || 1 + len_len + len != total_len)
1483193141Sdougb		return (-1);
1484193141Sdougb	p += len_len;
1485193141Sdougb	if (*p++ != 0x06)
1486193141Sdougb		return (-1);
1487193141Sdougb	e = der_get_length (p, total_len - 1 - len_len - 1,
1488193141Sdougb			    &mech_len, &foo);
1489193141Sdougb	if (e)
1490193141Sdougb		return (-1);
1491193141Sdougb	p += foo;
1492193141Sdougb	*mech_ret = p;
1493193141Sdougb	return (mech_len);
1494193141Sdougb}
1495193141Sdougb
1496193141Sdougbstatic OM_uint32
1497193141Sdougbspnego_initial(OM_uint32 *minor_status,
1498193141Sdougb	       const gss_cred_id_t initiator_cred_handle,
1499193141Sdougb	       gss_ctx_id_t *context_handle,
1500193141Sdougb	       const gss_name_t target_name,
1501193141Sdougb	       const gss_OID mech_type,
1502193141Sdougb	       OM_uint32 req_flags,
1503193141Sdougb	       OM_uint32 time_req,
1504193141Sdougb	       const gss_channel_bindings_t input_chan_bindings,
1505193141Sdougb	       const gss_buffer_t input_token,
1506193141Sdougb	       gss_OID *actual_mech_type,
1507193141Sdougb	       gss_buffer_t output_token,
1508193141Sdougb	       OM_uint32 *ret_flags,
1509193141Sdougb	       OM_uint32 *time_rec)
1510193141Sdougb{
1511193141Sdougb	NegTokenInit token_init;
1512193141Sdougb	OM_uint32 major_status, minor_status2;
1513193141Sdougb	gss_buffer_desc	krb5_output_token = GSS_C_EMPTY_BUFFER;
1514193141Sdougb	unsigned char *buf = NULL;
1515193141Sdougb	size_t buf_size;
1516193141Sdougb	size_t len;
1517193141Sdougb	int ret;
1518193141Sdougb
1519193141Sdougb	(void)mech_type;
1520193141Sdougb
1521193141Sdougb	memset(&token_init, 0, sizeof(token_init));
1522193141Sdougb
1523193141Sdougb	ret = add_mech(&token_init.mechTypes, GSS_KRB5_MECH);
1524193141Sdougb	if (ret) {
1525193141Sdougb		*minor_status = ret;
1526193141Sdougb		ret = GSS_S_FAILURE;
1527193141Sdougb		goto end;
1528193141Sdougb	}
1529193141Sdougb
1530193141Sdougb	major_status = gss_init_sec_context(minor_status,
1531193141Sdougb					    initiator_cred_handle,
1532193141Sdougb					    context_handle,
1533193141Sdougb					    target_name,
1534193141Sdougb					    GSS_KRB5_MECH,
1535193141Sdougb					    req_flags,
1536193141Sdougb					    time_req,
1537193141Sdougb					    input_chan_bindings,
1538193141Sdougb					    input_token,
1539193141Sdougb					    actual_mech_type,
1540193141Sdougb					    &krb5_output_token,
1541193141Sdougb					    ret_flags,
1542193141Sdougb					    time_rec);
1543193141Sdougb	if (GSS_ERROR(major_status)) {
1544193141Sdougb		ret = major_status;
1545193141Sdougb		goto end;
1546193141Sdougb	}
1547225361Sdougb	if (krb5_output_token.length > 0U) {
1548193141Sdougb		token_init.mechToken = malloc(sizeof(*token_init.mechToken));
1549193141Sdougb		if (token_init.mechToken == NULL) {
1550193141Sdougb			*minor_status = ENOMEM;
1551193141Sdougb			ret = GSS_S_FAILURE;
1552193141Sdougb			goto end;
1553193141Sdougb		}
1554193141Sdougb		token_init.mechToken->data = krb5_output_token.value;
1555193141Sdougb		token_init.mechToken->length = krb5_output_token.length;
1556193141Sdougb	}
1557193141Sdougb	/*
1558193141Sdougb	 * The MS implementation of SPNEGO seems to not like the mechListMIC
1559193141Sdougb	 * field, so we omit it (it's optional anyway)
1560193141Sdougb	 */
1561193141Sdougb
1562193141Sdougb	buf_size = 1024;
1563193141Sdougb	buf = malloc(buf_size);
1564254402Serwin	if (buf == NULL) {
1565254402Serwin		*minor_status = ENOMEM;
1566254402Serwin		ret = GSS_S_FAILURE;
1567254402Serwin		goto end;
1568254402Serwin	}
1569193141Sdougb
1570193141Sdougb	do {
1571193141Sdougb		ret = encode_NegTokenInit(buf + buf_size - 1,
1572193141Sdougb					  buf_size,
1573193141Sdougb					  &token_init, &len);
1574193141Sdougb		if (ret == 0) {
1575193141Sdougb			size_t tmp;
1576193141Sdougb
1577193141Sdougb			ret = der_put_length_and_tag(buf + buf_size - len - 1,
1578193141Sdougb						     buf_size - len,
1579193141Sdougb						     len,
1580193141Sdougb						     ASN1_C_CONTEXT,
1581193141Sdougb						     CONS,
1582193141Sdougb						     0,
1583193141Sdougb						     &tmp);
1584193141Sdougb			if (ret == 0)
1585193141Sdougb				len += tmp;
1586193141Sdougb		}
1587193141Sdougb		if (ret) {
1588193141Sdougb			if (ret == ASN1_OVERFLOW) {
1589193141Sdougb				u_char *tmp;
1590193141Sdougb
1591193141Sdougb				buf_size *= 2;
1592193141Sdougb				tmp = realloc(buf, buf_size);
1593193141Sdougb				if (tmp == NULL) {
1594193141Sdougb					*minor_status = ENOMEM;
1595193141Sdougb					ret = GSS_S_FAILURE;
1596193141Sdougb					goto end;
1597193141Sdougb				}
1598193141Sdougb				buf = tmp;
1599193141Sdougb			} else {
1600193141Sdougb				*minor_status = ret;
1601193141Sdougb				ret = GSS_S_FAILURE;
1602193141Sdougb				goto end;
1603193141Sdougb			}
1604193141Sdougb		}
1605193141Sdougb	} while (ret == ASN1_OVERFLOW);
1606193141Sdougb
1607193141Sdougb	ret = gssapi_spnego_encapsulate(minor_status,
1608193141Sdougb					buf + buf_size - len, len,
1609193141Sdougb					output_token, GSS_SPNEGO_MECH);
1610193141Sdougb	if (ret == GSS_S_COMPLETE)
1611193141Sdougb		ret = major_status;
1612193141Sdougb
1613193141Sdougbend:
1614193141Sdougb	if (token_init.mechToken != NULL) {
1615193141Sdougb		free(token_init.mechToken);
1616193141Sdougb		token_init.mechToken = NULL;
1617193141Sdougb	}
1618193141Sdougb	free_NegTokenInit(&token_init);
1619225361Sdougb	if (krb5_output_token.length != 0U)
1620193141Sdougb		gss_release_buffer(&minor_status2, &krb5_output_token);
1621193141Sdougb	if (buf)
1622193141Sdougb		free(buf);
1623193141Sdougb
1624193141Sdougb	return (ret);
1625193141Sdougb}
1626193141Sdougb
1627193141Sdougbstatic OM_uint32
1628193141Sdougbspnego_reply(OM_uint32 *minor_status,
1629193141Sdougb	     const gss_cred_id_t initiator_cred_handle,
1630193141Sdougb	     gss_ctx_id_t *context_handle,
1631193141Sdougb	     const gss_name_t target_name,
1632193141Sdougb	     const gss_OID mech_type,
1633193141Sdougb	     OM_uint32 req_flags,
1634193141Sdougb	     OM_uint32 time_req,
1635193141Sdougb	     const gss_channel_bindings_t input_chan_bindings,
1636193141Sdougb	     const gss_buffer_t input_token,
1637193141Sdougb	     gss_OID *actual_mech_type,
1638193141Sdougb	     gss_buffer_t output_token,
1639193141Sdougb	     OM_uint32 *ret_flags,
1640193141Sdougb	     OM_uint32 *time_rec)
1641193141Sdougb{
1642193141Sdougb	OM_uint32 ret;
1643193141Sdougb	NegTokenResp resp;
1644193141Sdougb	unsigned char *buf;
1645193141Sdougb	size_t buf_size;
1646193141Sdougb	u_char oidbuf[17];
1647193141Sdougb	size_t oidlen;
1648193141Sdougb	gss_buffer_desc sub_token;
1649193141Sdougb	ssize_t mech_len;
1650193141Sdougb	const u_char *p;
1651193141Sdougb	size_t len, taglen;
1652193141Sdougb
1653193141Sdougb	(void)mech_type;
1654193141Sdougb
1655193141Sdougb	output_token->length = 0;
1656193141Sdougb	output_token->value  = NULL;
1657193141Sdougb
1658193141Sdougb	/*
1659193141Sdougb	 * SPNEGO doesn't include gss wrapping on SubsequentContextToken
1660193141Sdougb	 * like the Kerberos 5 mech does. But lets check for it anyway.
1661193141Sdougb	 */
1662193141Sdougb
1663193141Sdougb	mech_len = gssapi_krb5_get_mech(input_token->value,
1664193141Sdougb					input_token->length,
1665193141Sdougb					&p);
1666193141Sdougb
1667193141Sdougb	if (mech_len < 0) {
1668193141Sdougb		buf = input_token->value;
1669193141Sdougb		buf_size = input_token->length;
1670193141Sdougb	} else if ((size_t)mech_len == GSS_KRB5_MECH->length &&
1671193141Sdougb		   memcmp(GSS_KRB5_MECH->elements, p, mech_len) == 0)
1672193141Sdougb		return (gss_init_sec_context(minor_status,
1673193141Sdougb					     initiator_cred_handle,
1674193141Sdougb					     context_handle,
1675193141Sdougb					     target_name,
1676193141Sdougb					     GSS_KRB5_MECH,
1677193141Sdougb					     req_flags,
1678193141Sdougb					     time_req,
1679193141Sdougb					     input_chan_bindings,
1680193141Sdougb					     input_token,
1681193141Sdougb					     actual_mech_type,
1682193141Sdougb					     output_token,
1683193141Sdougb					     ret_flags,
1684193141Sdougb					     time_rec));
1685193141Sdougb	else if ((size_t)mech_len == GSS_SPNEGO_MECH->length &&
1686193141Sdougb		 memcmp(GSS_SPNEGO_MECH->elements, p, mech_len) == 0) {
1687193141Sdougb		ret = gssapi_spnego_decapsulate(minor_status,
1688193141Sdougb						input_token,
1689193141Sdougb						&buf,
1690193141Sdougb						&buf_size,
1691193141Sdougb						GSS_SPNEGO_MECH);
1692193141Sdougb		if (ret)
1693193141Sdougb			return (ret);
1694193141Sdougb	} else
1695193141Sdougb		return (GSS_S_BAD_MECH);
1696193141Sdougb
1697193141Sdougb	ret = der_match_tag_and_length(buf, buf_size,
1698193141Sdougb				       ASN1_C_CONTEXT, CONS, 1, &len, &taglen);
1699193141Sdougb	if (ret)
1700193141Sdougb		return (ret);
1701193141Sdougb
1702193141Sdougb	if(len > buf_size - taglen)
1703193141Sdougb		return (ASN1_OVERRUN);
1704193141Sdougb
1705193141Sdougb	ret = decode_NegTokenResp(buf + taglen, len, &resp, NULL);
1706193141Sdougb	if (ret) {
1707254402Serwin		free_NegTokenResp(&resp);
1708193141Sdougb		*minor_status = ENOMEM;
1709193141Sdougb		return (GSS_S_FAILURE);
1710193141Sdougb	}
1711193141Sdougb
1712193141Sdougb	if (resp.negState == NULL ||
1713193141Sdougb	    *(resp.negState) == reject ||
1714193141Sdougb	    resp.supportedMech == NULL) {
1715193141Sdougb		free_NegTokenResp(&resp);
1716193141Sdougb		return (GSS_S_BAD_MECH);
1717193141Sdougb	}
1718193141Sdougb
1719193141Sdougb	ret = der_put_oid(oidbuf + sizeof(oidbuf) - 1,
1720193141Sdougb			  sizeof(oidbuf),
1721193141Sdougb			  resp.supportedMech,
1722193141Sdougb			  &oidlen);
1723193141Sdougb	if (ret || oidlen != GSS_KRB5_MECH->length ||
1724193141Sdougb	    memcmp(oidbuf + sizeof(oidbuf) - oidlen,
1725193141Sdougb		   GSS_KRB5_MECH->elements,
1726193141Sdougb		   oidlen) != 0) {
1727193141Sdougb		free_NegTokenResp(&resp);
1728193141Sdougb		return GSS_S_BAD_MECH;
1729193141Sdougb	}
1730193141Sdougb
1731193141Sdougb	if (resp.responseToken != NULL) {
1732193141Sdougb		sub_token.length = resp.responseToken->length;
1733193141Sdougb		sub_token.value  = resp.responseToken->data;
1734193141Sdougb	} else {
1735193141Sdougb		sub_token.length = 0;
1736193141Sdougb		sub_token.value  = NULL;
1737193141Sdougb	}
1738193141Sdougb
1739193141Sdougb	ret = gss_init_sec_context(minor_status,
1740193141Sdougb				   initiator_cred_handle,
1741193141Sdougb				   context_handle,
1742193141Sdougb				   target_name,
1743193141Sdougb				   GSS_KRB5_MECH,
1744193141Sdougb				   req_flags,
1745193141Sdougb				   time_req,
1746193141Sdougb				   input_chan_bindings,
1747193141Sdougb				   &sub_token,
1748193141Sdougb				   actual_mech_type,
1749193141Sdougb				   output_token,
1750193141Sdougb				   ret_flags,
1751193141Sdougb				   time_rec);
1752193141Sdougb	if (ret) {
1753193141Sdougb		free_NegTokenResp(&resp);
1754193141Sdougb		return (ret);
1755193141Sdougb	}
1756193141Sdougb
1757193141Sdougb	/*
1758193141Sdougb	 * XXXSRA I don't think this limited implementation ever needs
1759193141Sdougb	 * to check the MIC -- our preferred mechanism (Kerberos)
1760193141Sdougb	 * authenticates its own messages and is the only mechanism
1761193141Sdougb	 * we'll accept, so if the mechanism negotiation completes
1762193141Sdougb	 * successfully, we don't need the MIC.  See RFC 4178.
1763193141Sdougb	 */
1764193141Sdougb
1765193141Sdougb	free_NegTokenResp(&resp);
1766193141Sdougb	return (ret);
1767193141Sdougb}
1768193141Sdougb
1769193141Sdougb
1770193141Sdougb
1771193141SdougbOM_uint32
1772193141Sdougbgss_init_sec_context_spnego(OM_uint32 *minor_status,
1773193141Sdougb			    const gss_cred_id_t initiator_cred_handle,
1774193141Sdougb			    gss_ctx_id_t *context_handle,
1775193141Sdougb			    const gss_name_t target_name,
1776193141Sdougb			    const gss_OID mech_type,
1777193141Sdougb			    OM_uint32 req_flags,
1778193141Sdougb			    OM_uint32 time_req,
1779193141Sdougb			    const gss_channel_bindings_t input_chan_bindings,
1780193141Sdougb			    const gss_buffer_t input_token,
1781193141Sdougb			    gss_OID *actual_mech_type,
1782193141Sdougb			    gss_buffer_t output_token,
1783193141Sdougb			    OM_uint32 *ret_flags,
1784193141Sdougb			    OM_uint32 *time_rec)
1785193141Sdougb{
1786193141Sdougb	/* Dirty trick to suppress compiler warnings */
1787193141Sdougb
1788193141Sdougb	/* Figure out whether we're starting over or processing a reply */
1789193141Sdougb
1790225361Sdougb	if (input_token == GSS_C_NO_BUFFER || input_token->length == 0U)
1791193141Sdougb		return (spnego_initial(minor_status,
1792193141Sdougb				       initiator_cred_handle,
1793193141Sdougb				       context_handle,
1794193141Sdougb				       target_name,
1795193141Sdougb				       mech_type,
1796193141Sdougb				       req_flags,
1797193141Sdougb				       time_req,
1798193141Sdougb				       input_chan_bindings,
1799193141Sdougb				       input_token,
1800193141Sdougb				       actual_mech_type,
1801193141Sdougb				       output_token,
1802193141Sdougb				       ret_flags,
1803193141Sdougb				       time_rec));
1804193141Sdougb	else
1805193141Sdougb		return (spnego_reply(minor_status,
1806193141Sdougb				     initiator_cred_handle,
1807193141Sdougb				     context_handle,
1808193141Sdougb				     target_name,
1809193141Sdougb				     mech_type,
1810193141Sdougb				     req_flags,
1811193141Sdougb				     time_req,
1812193141Sdougb				     input_chan_bindings,
1813193141Sdougb				     input_token,
1814193141Sdougb				     actual_mech_type,
1815193141Sdougb				     output_token,
1816193141Sdougb				     ret_flags,
1817193141Sdougb				     time_rec));
1818193141Sdougb}
1819193141Sdougb
1820193141Sdougb#endif /* GSSAPI */
1821