1/*
2 * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "gsskrb5_locl.h"
35
36krb5_error_code
37_gsskrb5_decode_be_om_uint32(const void *ptr, OM_uint32 *n)
38{
39    const u_char *p = ptr;
40    *n = (p[0] <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
41    return 0;
42}
43
44/*
45 *
46 */
47
48static OM_uint32
49store_ext(krb5_storage *sp, uint16_t type, krb5_data *data)
50{
51    krb5_error_code ret;
52    krb5_ssize_t sret;
53
54    ret = krb5_store_uint16(sp, type);
55    if (ret) return ret;
56    ret = krb5_store_uint16(sp, data->length);
57    if (ret) return ret;
58    sret = krb5_storage_write(sp, data->data, data->length);
59    if (sret < 0 || (size_t)sret != data->length)
60	return ENOMEM;
61    return 0;
62}
63
64/*
65 * create a checksum over the chanel bindings in
66 * `input_chan_bindings', `flags' and `fwd_data' and return it in
67 * `result'
68 */
69
70OM_uint32
71_gsskrb5_create_8003_checksum(OM_uint32 *minor_status,
72			      krb5_context context,
73			      krb5_crypto crypto,
74			      const gss_channel_bindings_t input_chan_bindings,
75			      OM_uint32 flags,
76			      krb5_data *fwd_data,
77			      krb5_data *pkt_cksum,
78			      Checksum *result)
79{
80    uint8_t channelbindings[16];
81    gss_buffer_desc cksum;
82    krb5_error_code ret;
83    krb5_storage *sp;
84    OM_uint32 maj_stat, junk;
85    krb5_ssize_t sret;
86
87    cksum.value = NULL;
88    cksum.length = 0;
89
90    sp = krb5_storage_emem();
91    if (sp == NULL) {
92	ret = ENOMEM;
93	goto out;
94    }
95    krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
96
97    ret = krb5_store_uint32(sp, 16);
98    if (ret) goto out;
99
100    if (input_chan_bindings == GSS_C_NO_CHANNEL_BINDINGS) {
101	memset (channelbindings, 0, 16);
102    } else {
103	maj_stat = gss_mg_gen_cb(minor_status, input_chan_bindings, channelbindings, &cksum);
104	if (maj_stat) {
105	    ret = *minor_status;
106	    goto out;
107	}
108    }
109    sret = krb5_storage_write(sp, channelbindings, sizeof(channelbindings));
110    if (sret != sizeof(channelbindings)) {
111	ret = ENOMEM;
112	goto out;
113    }
114    ret = krb5_store_uint32(sp, flags);
115    if (ret) goto out;
116
117    if (flags & GSS_C_DELEG_FLAG) {
118	ret = store_ext(sp, 1, fwd_data);
119	if (ret) goto out;
120    }
121    if (pkt_cksum->length > 0) {
122	ret = store_ext(sp, 2, pkt_cksum);
123	if (ret) goto out;
124    }
125
126    if (crypto && input_chan_bindings && cksum.length) {
127	Checksum checksum;
128	krb5_data data;
129	size_t size = 0;
130
131	memset(&checksum, 0, sizeof(checksum));
132	ret = krb5_create_checksum(context, crypto, KRB5_KU_GSSAPI_EXTS, 0,
133				   cksum.value, cksum.length, &checksum);
134	if (ret) goto out;
135
136	ASN1_MALLOC_ENCODE(Checksum, data.data, data.length,
137			   &checksum, &size, ret);
138	if (ret)
139	    goto out;
140	if (data.length != size)
141	    krb5_abortx(context, "internal error in ASN.1 encoder");
142
143	ret = store_ext(sp, 0, &data);
144	krb5_data_free(&data);
145	if (ret) goto out;
146    }
147
148    /*
149     * see rfc1964 (section 1.1.1 (Initial Token), and the checksum value
150     * field's format)
151     */
152    result->cksumtype = CKSUMTYPE_GSSAPI;
153    ret = krb5_storage_to_data(sp, &result->checksum);
154 out:
155    gss_release_buffer(&junk, &cksum);
156    if (sp)
157	krb5_storage_free(sp);
158    if (ret) {
159	*minor_status = ret;
160	return GSS_S_FAILURE;
161    }
162
163    return GSS_S_COMPLETE;
164}
165
166/*
167 *
168 */
169
170static krb5_error_code
171read_ext(krb5_storage *sp, uint16_t *type, krb5_data *data)
172{
173    krb5_error_code ret;
174    krb5_ssize_t sret;
175    uint16_t len;
176
177    ret = krb5_ret_uint16(sp, type);
178    if (ret)
179	return ret;
180
181    ret = krb5_ret_uint16(sp, &len);
182    if (ret)
183	return ret;
184
185    ret = krb5_data_alloc(data, len);
186    if (ret)
187	return ret;
188
189    sret = krb5_storage_read(sp, data->data, data->length);
190    if (sret < 0 || (size_t)sret != data->length) {
191	krb5_data_free(data);
192	return HEIM_ERR_EOF;
193    }
194    return 0;
195}
196
197/*
198 * verify the checksum in `cksum' over `input_chan_bindings'
199 * returning  `flags' and `fwd_data'
200 */
201
202OM_uint32
203_gsskrb5_verify_8003_checksum(OM_uint32 *minor_status,
204			      krb5_context context,
205			      krb5_crypto crypto,
206			      const gss_channel_bindings_t input_chan_bindings,
207			      const Checksum *cksum,
208			      OM_uint32 *flags,
209			      krb5_data *fwd_data,
210			      krb5_data *finished)
211{
212    unsigned count = 0, verified_checksum = 0;
213    krb5_error_code ret;
214    krb5_ssize_t sret;
215    krb5_storage *sp;
216    unsigned char hash[16], pkthash[16];
217    uint32_t length;
218    krb5_data data;
219    uint16_t type;
220    OM_uint32 maj_stat, junk;
221    gss_buffer_desc cbdata;
222
223    cbdata.length = 0;
224    krb5_data_zero(&data);
225
226    /* XXX should handle checksums > 24 bytes */
227    if(cksum->cksumtype != CKSUMTYPE_GSSAPI || cksum->checksum.length < 24) {
228	*minor_status = 0;
229	return GSS_S_BAD_BINDINGS;
230    }
231
232    sp = krb5_storage_from_readonly_mem(cksum->checksum.data,
233					cksum->checksum.length);
234    if (sp == NULL) {
235	*minor_status = ENOMEM;
236	return GSS_S_FAILURE;
237    }
238    krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
239
240    ret = krb5_ret_uint32(sp, &length);
241    if (ret) goto out;
242    if(length != sizeof(hash)) {
243	ret = ENOMEM;
244	goto out;
245    }
246
247    sret = krb5_storage_read(sp, pkthash, sizeof(pkthash));
248    if (sret != sizeof(pkthash)) {
249	ret = ENOMEM;
250	goto out;
251    }
252
253    maj_stat = gss_mg_validate_cb(minor_status, input_chan_bindings,
254				  pkthash, &cbdata);
255    if (maj_stat) {
256	ret = *minor_status;
257	goto out;
258    }
259
260    ret = krb5_ret_uint32(sp, flags);
261    if (ret) goto out;
262
263    if ((*flags) & GSS_C_DELEG_FLAG) {
264	ret = read_ext(sp, &type, fwd_data);
265	if (ret) goto out;
266
267	if (type != 1) {
268	    ret = EINVAL;
269	    goto out;
270	}
271    }
272
273    while ((ret = read_ext(sp, &type, &data)) == 0) {
274	count++;
275	if (type == 0 && input_chan_bindings) {
276	    Checksum checksum;
277
278	    /* new checksum */
279
280	    if (crypto == NULL) {
281		ret = ENOMEM;
282		goto out;
283	    }
284
285	    ret = decode_Checksum(data.data, data.length, &checksum, NULL);
286	    if (ret) goto out;
287
288	    ret = krb5_verify_checksum(context, crypto, KRB5_KU_GSSAPI_EXTS,
289				       cbdata.value, cbdata.length,
290				       &checksum);
291	    free_Checksum(&checksum);
292	    krb5_data_free(&data);
293
294	    if (ret) goto out;
295
296	    verified_checksum = 1;
297
298	} else if (type == 2) {
299	    *finished = data;
300	    krb5_data_zero(&data);
301	} else
302	    krb5_data_free(&data);
303    }
304    if (ret != HEIM_ERR_EOF && ret != 0)
305	goto out;
306
307    if (input_chan_bindings && count && !verified_checksum) {
308	ret = EINVAL;
309	goto out;
310    }
311    ret = 0;
312
313 out:
314    gss_release_buffer(&junk, &cbdata);
315    krb5_data_free(&data);
316    krb5_storage_free(sp);
317    if (ret) {
318	*minor_status = ret;
319	return GSS_S_BAD_BINDINGS;
320    }
321    return GSS_S_COMPLETE;
322}
323