1178825Sdfr/*
2178825Sdfr * Copyright (c) 2006 - 2007 Kungliga Tekniska H�gskolan
3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden).
4178825Sdfr * All rights reserved.
5178825Sdfr *
6178825Sdfr * Redistribution and use in source and binary forms, with or without
7178825Sdfr * modification, are permitted provided that the following conditions
8178825Sdfr * are met:
9178825Sdfr *
10178825Sdfr * 1. Redistributions of source code must retain the above copyright
11178825Sdfr *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
14178825Sdfr *    notice, this list of conditions and the following disclaimer in the
15178825Sdfr *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors
18178825Sdfr *    may be used to endorse or promote products derived from this software
19178825Sdfr *    without specific prior written permission.
20178825Sdfr *
21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31178825Sdfr * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include "krb5_locl.h"
35178825Sdfr
36178825SdfrRCSID("$Id: pac.c 21934 2007-08-27 14:21:04Z lha $");
37178825Sdfr
38178825Sdfrstruct PAC_INFO_BUFFER {
39178825Sdfr    uint32_t type;
40178825Sdfr    uint32_t buffersize;
41178825Sdfr    uint32_t offset_hi;
42178825Sdfr    uint32_t offset_lo;
43178825Sdfr};
44178825Sdfr
45178825Sdfrstruct PACTYPE {
46178825Sdfr    uint32_t numbuffers;
47178825Sdfr    uint32_t version;
48178825Sdfr    struct PAC_INFO_BUFFER buffers[1];
49178825Sdfr};
50178825Sdfr
51178825Sdfrstruct krb5_pac_data {
52178825Sdfr    struct PACTYPE *pac;
53178825Sdfr    krb5_data data;
54178825Sdfr    struct PAC_INFO_BUFFER *server_checksum;
55178825Sdfr    struct PAC_INFO_BUFFER *privsvr_checksum;
56178825Sdfr    struct PAC_INFO_BUFFER *logon_name;
57178825Sdfr};
58178825Sdfr
59178825Sdfr#define PAC_ALIGNMENT			8
60178825Sdfr
61178825Sdfr#define PACTYPE_SIZE			8
62178825Sdfr#define PAC_INFO_BUFFER_SIZE		16
63178825Sdfr
64178825Sdfr#define PAC_SERVER_CHECKSUM		6
65178825Sdfr#define PAC_PRIVSVR_CHECKSUM		7
66178825Sdfr#define PAC_LOGON_NAME			10
67178825Sdfr#define PAC_CONSTRAINED_DELEGATION	11
68178825Sdfr
69178825Sdfr#define CHECK(r,f,l)						\
70178825Sdfr	do {							\
71178825Sdfr		if (((r) = f ) != 0) {				\
72178825Sdfr			krb5_clear_error_string(context);	\
73178825Sdfr			goto l;					\
74178825Sdfr		}						\
75178825Sdfr	} while(0)
76178825Sdfr
77178825Sdfrstatic const char zeros[PAC_ALIGNMENT] = { 0 };
78178825Sdfr
79178825Sdfr/*
80178825Sdfr *
81178825Sdfr */
82178825Sdfr
83178825Sdfrkrb5_error_code
84178825Sdfrkrb5_pac_parse(krb5_context context, const void *ptr, size_t len,
85178825Sdfr	       krb5_pac *pac)
86178825Sdfr{
87178825Sdfr    krb5_error_code ret;
88178825Sdfr    krb5_pac p;
89178825Sdfr    krb5_storage *sp = NULL;
90178825Sdfr    uint32_t i, tmp, tmp2, header_end;
91178825Sdfr
92178825Sdfr    p = calloc(1, sizeof(*p));
93178825Sdfr    if (p == NULL) {
94178825Sdfr	ret = ENOMEM;
95178825Sdfr	krb5_set_error_string(context, "out of memory");
96178825Sdfr	goto out;
97178825Sdfr    }
98178825Sdfr
99178825Sdfr    sp = krb5_storage_from_readonly_mem(ptr, len);
100178825Sdfr    if (sp == NULL) {
101178825Sdfr	ret = ENOMEM;
102178825Sdfr	krb5_set_error_string(context, "out of memory");
103178825Sdfr	goto out;
104178825Sdfr    }
105178825Sdfr    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
106178825Sdfr
107178825Sdfr    CHECK(ret, krb5_ret_uint32(sp, &tmp), out);
108178825Sdfr    CHECK(ret, krb5_ret_uint32(sp, &tmp2), out);
109178825Sdfr    if (tmp < 1) {
110178825Sdfr	krb5_set_error_string(context, "PAC have too few buffer");
111178825Sdfr	ret = EINVAL; /* Too few buffers */
112178825Sdfr	goto out;
113178825Sdfr    }
114178825Sdfr    if (tmp2 != 0) {
115178825Sdfr	krb5_set_error_string(context, "PAC have wrong version");
116178825Sdfr	ret = EINVAL; /* Wrong version */
117178825Sdfr	goto out;
118178825Sdfr    }
119178825Sdfr
120178825Sdfr    p->pac = calloc(1,
121178825Sdfr		    sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1)));
122178825Sdfr    if (p->pac == NULL) {
123178825Sdfr	krb5_set_error_string(context, "out of memory");
124178825Sdfr	ret = ENOMEM;
125178825Sdfr	goto out;
126178825Sdfr    }
127178825Sdfr
128178825Sdfr    p->pac->numbuffers = tmp;
129178825Sdfr    p->pac->version = tmp2;
130178825Sdfr
131178825Sdfr    header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
132178825Sdfr    if (header_end > len) {
133178825Sdfr	ret = EINVAL;
134178825Sdfr	goto out;
135178825Sdfr    }
136178825Sdfr
137178825Sdfr    for (i = 0; i < p->pac->numbuffers; i++) {
138178825Sdfr	CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out);
139178825Sdfr	CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out);
140178825Sdfr	CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out);
141178825Sdfr	CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out);
142178825Sdfr
143178825Sdfr	/* consistency checks */
144178825Sdfr	if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) {
145178825Sdfr	    krb5_set_error_string(context, "PAC out of allignment");
146178825Sdfr	    ret = EINVAL;
147178825Sdfr	    goto out;
148178825Sdfr	}
149178825Sdfr	if (p->pac->buffers[i].offset_hi) {
150178825Sdfr	    krb5_set_error_string(context, "PAC high offset set");
151178825Sdfr	    ret = EINVAL;
152178825Sdfr	    goto out;
153178825Sdfr	}
154178825Sdfr	if (p->pac->buffers[i].offset_lo > len) {
155178825Sdfr	    krb5_set_error_string(context, "PAC offset off end");
156178825Sdfr	    ret = EINVAL;
157178825Sdfr	    goto out;
158178825Sdfr	}
159178825Sdfr	if (p->pac->buffers[i].offset_lo < header_end) {
160178825Sdfr	    krb5_set_error_string(context, "PAC offset inside header: %d %d",
161178825Sdfr				  p->pac->buffers[i].offset_lo, header_end);
162178825Sdfr	    ret = EINVAL;
163178825Sdfr	    goto out;
164178825Sdfr	}
165178825Sdfr	if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){
166178825Sdfr	    krb5_set_error_string(context, "PAC length off end");
167178825Sdfr	    ret = EINVAL;
168178825Sdfr	    goto out;
169178825Sdfr	}
170178825Sdfr
171178825Sdfr	/* let save pointer to data we need later */
172178825Sdfr	if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
173178825Sdfr	    if (p->server_checksum) {
174178825Sdfr		krb5_set_error_string(context, "PAC have two server checksums");
175178825Sdfr		ret = EINVAL;
176178825Sdfr		goto out;
177178825Sdfr	    }
178178825Sdfr	    p->server_checksum = &p->pac->buffers[i];
179178825Sdfr	} else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
180178825Sdfr	    if (p->privsvr_checksum) {
181178825Sdfr		krb5_set_error_string(context, "PAC have two KDC checksums");
182178825Sdfr		ret = EINVAL;
183178825Sdfr		goto out;
184178825Sdfr	    }
185178825Sdfr	    p->privsvr_checksum = &p->pac->buffers[i];
186178825Sdfr	} else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
187178825Sdfr	    if (p->logon_name) {
188178825Sdfr		krb5_set_error_string(context, "PAC have two logon names");
189178825Sdfr		ret = EINVAL;
190178825Sdfr		goto out;
191178825Sdfr	    }
192178825Sdfr	    p->logon_name = &p->pac->buffers[i];
193178825Sdfr	}
194178825Sdfr    }
195178825Sdfr
196178825Sdfr    ret = krb5_data_copy(&p->data, ptr, len);
197178825Sdfr    if (ret)
198178825Sdfr	goto out;
199178825Sdfr
200178825Sdfr    krb5_storage_free(sp);
201178825Sdfr
202178825Sdfr    *pac = p;
203178825Sdfr    return 0;
204178825Sdfr
205178825Sdfrout:
206178825Sdfr    if (sp)
207178825Sdfr	krb5_storage_free(sp);
208178825Sdfr    if (p) {
209178825Sdfr	if (p->pac)
210178825Sdfr	    free(p->pac);
211178825Sdfr	free(p);
212178825Sdfr    }
213178825Sdfr    *pac = NULL;
214178825Sdfr
215178825Sdfr    return ret;
216178825Sdfr}
217178825Sdfr
218178825Sdfrkrb5_error_code
219178825Sdfrkrb5_pac_init(krb5_context context, krb5_pac *pac)
220178825Sdfr{
221178825Sdfr    krb5_error_code ret;
222178825Sdfr    krb5_pac p;
223178825Sdfr
224178825Sdfr    p = calloc(1, sizeof(*p));
225178825Sdfr    if (p == NULL) {
226178825Sdfr	krb5_set_error_string(context, "out of memory");
227178825Sdfr	return ENOMEM;
228178825Sdfr    }
229178825Sdfr
230178825Sdfr    p->pac = calloc(1, sizeof(*p->pac));
231178825Sdfr    if (p->pac == NULL) {
232178825Sdfr	free(p);
233178825Sdfr	krb5_set_error_string(context, "out of memory");
234178825Sdfr	return ENOMEM;
235178825Sdfr    }
236178825Sdfr
237178825Sdfr    ret = krb5_data_alloc(&p->data, PACTYPE_SIZE);
238178825Sdfr    if (ret) {
239178825Sdfr	free (p->pac);
240178825Sdfr	free(p);
241178825Sdfr	krb5_set_error_string(context, "out of memory");
242178825Sdfr	return ret;
243178825Sdfr    }
244178825Sdfr
245178825Sdfr
246178825Sdfr    *pac = p;
247178825Sdfr    return 0;
248178825Sdfr}
249178825Sdfr
250178825Sdfrkrb5_error_code
251178825Sdfrkrb5_pac_add_buffer(krb5_context context, krb5_pac p,
252178825Sdfr		    uint32_t type, const krb5_data *data)
253178825Sdfr{
254178825Sdfr    krb5_error_code ret;
255178825Sdfr    void *ptr;
256178825Sdfr    size_t len, offset, header_end, old_end;
257178825Sdfr    uint32_t i;
258178825Sdfr
259178825Sdfr    len = p->pac->numbuffers;
260178825Sdfr
261178825Sdfr    ptr = realloc(p->pac,
262178825Sdfr		  sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len));
263178825Sdfr    if (ptr == NULL) {
264178825Sdfr	krb5_set_error_string(context, "out of memory");
265178825Sdfr	return ENOMEM;
266178825Sdfr    }
267178825Sdfr    p->pac = ptr;
268178825Sdfr
269178825Sdfr    for (i = 0; i < len; i++)
270178825Sdfr	p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE;
271178825Sdfr
272178825Sdfr    offset = p->data.length + PAC_INFO_BUFFER_SIZE;
273178825Sdfr
274178825Sdfr    p->pac->buffers[len].type = type;
275178825Sdfr    p->pac->buffers[len].buffersize = data->length;
276178825Sdfr    p->pac->buffers[len].offset_lo = offset;
277178825Sdfr    p->pac->buffers[len].offset_hi = 0;
278178825Sdfr
279178825Sdfr    old_end = p->data.length;
280178825Sdfr    len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE;
281178825Sdfr    if (len < p->data.length) {
282178825Sdfr	krb5_set_error_string(context, "integer overrun");
283178825Sdfr	return EINVAL;
284178825Sdfr    }
285178825Sdfr
286178825Sdfr    /* align to PAC_ALIGNMENT */
287178825Sdfr    len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
288178825Sdfr
289178825Sdfr    ret = krb5_data_realloc(&p->data, len);
290178825Sdfr    if (ret) {
291178825Sdfr	krb5_set_error_string(context, "out of memory");
292178825Sdfr	return ret;
293178825Sdfr    }
294178825Sdfr
295178825Sdfr    /*
296178825Sdfr     * make place for new PAC INFO BUFFER header
297178825Sdfr     */
298178825Sdfr    header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
299178825Sdfr    memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE,
300178825Sdfr	    (unsigned char *)p->data.data + header_end ,
301178825Sdfr	    old_end - header_end);
302178825Sdfr    memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE);
303178825Sdfr
304178825Sdfr    /*
305178825Sdfr     * copy in new data part
306178825Sdfr     */
307178825Sdfr
308178825Sdfr    memcpy((unsigned char *)p->data.data + offset,
309178825Sdfr	   data->data, data->length);
310178825Sdfr    memset((unsigned char *)p->data.data + offset + data->length,
311178825Sdfr	   0, p->data.length - offset - data->length);
312178825Sdfr
313178825Sdfr    p->pac->numbuffers += 1;
314178825Sdfr
315178825Sdfr    return 0;
316178825Sdfr}
317178825Sdfr
318178825Sdfrkrb5_error_code
319178825Sdfrkrb5_pac_get_buffer(krb5_context context, krb5_pac p,
320178825Sdfr		    uint32_t type, krb5_data *data)
321178825Sdfr{
322178825Sdfr    krb5_error_code ret;
323178825Sdfr    uint32_t i;
324178825Sdfr
325178825Sdfr    /*
326178825Sdfr     * Hide the checksums from external consumers
327178825Sdfr     */
328178825Sdfr
329178825Sdfr    if (type == PAC_PRIVSVR_CHECKSUM || type == PAC_SERVER_CHECKSUM) {
330178825Sdfr	ret = krb5_data_alloc(data, 16);
331178825Sdfr	if (ret) {
332178825Sdfr	    krb5_set_error_string(context, "out of memory");
333178825Sdfr	    return ret;
334178825Sdfr	}
335178825Sdfr	memset(data->data, 0, data->length);
336178825Sdfr	return 0;
337178825Sdfr    }
338178825Sdfr
339178825Sdfr    for (i = 0; i < p->pac->numbuffers; i++) {
340178825Sdfr	size_t len = p->pac->buffers[i].buffersize;
341178825Sdfr	size_t offset = p->pac->buffers[i].offset_lo;
342178825Sdfr
343178825Sdfr	if (p->pac->buffers[i].type != type)
344178825Sdfr	    continue;
345178825Sdfr
346178825Sdfr	ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len);
347178825Sdfr	if (ret) {
348178825Sdfr	    krb5_set_error_string(context, "Out of memory");
349178825Sdfr	    return ret;
350178825Sdfr	}
351178825Sdfr	return 0;
352178825Sdfr    }
353178825Sdfr    krb5_set_error_string(context, "No PAC buffer of type %lu was found",
354178825Sdfr			  (unsigned long)type);
355178825Sdfr    return ENOENT;
356178825Sdfr}
357178825Sdfr
358178825Sdfr/*
359178825Sdfr *
360178825Sdfr */
361178825Sdfr
362178825Sdfrkrb5_error_code
363178825Sdfrkrb5_pac_get_types(krb5_context context,
364178825Sdfr		   krb5_pac p,
365178825Sdfr		   size_t *len,
366178825Sdfr		   uint32_t **types)
367178825Sdfr{
368178825Sdfr    size_t i;
369178825Sdfr
370178825Sdfr    *types = calloc(p->pac->numbuffers, sizeof(*types));
371178825Sdfr    if (*types == NULL) {
372178825Sdfr	*len = 0;
373178825Sdfr	krb5_set_error_string(context, "out of memory");
374178825Sdfr	return ENOMEM;
375178825Sdfr    }
376178825Sdfr    for (i = 0; i < p->pac->numbuffers; i++)
377178825Sdfr	(*types)[i] = p->pac->buffers[i].type;
378178825Sdfr    *len = p->pac->numbuffers;
379178825Sdfr
380178825Sdfr    return 0;
381178825Sdfr}
382178825Sdfr
383178825Sdfr/*
384178825Sdfr *
385178825Sdfr */
386178825Sdfr
387178825Sdfrvoid
388178825Sdfrkrb5_pac_free(krb5_context context, krb5_pac pac)
389178825Sdfr{
390178825Sdfr    krb5_data_free(&pac->data);
391178825Sdfr    free(pac->pac);
392178825Sdfr    free(pac);
393178825Sdfr}
394178825Sdfr
395178825Sdfr/*
396178825Sdfr *
397178825Sdfr */
398178825Sdfr
399178825Sdfrstatic krb5_error_code
400178825Sdfrverify_checksum(krb5_context context,
401178825Sdfr		const struct PAC_INFO_BUFFER *sig,
402178825Sdfr		const krb5_data *data,
403178825Sdfr		void *ptr, size_t len,
404178825Sdfr		const krb5_keyblock *key)
405178825Sdfr{
406178825Sdfr    krb5_crypto crypto = NULL;
407178825Sdfr    krb5_storage *sp = NULL;
408178825Sdfr    uint32_t type;
409178825Sdfr    krb5_error_code ret;
410178825Sdfr    Checksum cksum;
411178825Sdfr
412178825Sdfr    memset(&cksum, 0, sizeof(cksum));
413178825Sdfr
414178825Sdfr    sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo,
415178825Sdfr			       sig->buffersize);
416178825Sdfr    if (sp == NULL) {
417178825Sdfr	krb5_set_error_string(context, "out of memory");
418178825Sdfr	return ENOMEM;
419178825Sdfr    }
420178825Sdfr    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
421178825Sdfr
422178825Sdfr    CHECK(ret, krb5_ret_uint32(sp, &type), out);
423178825Sdfr    cksum.cksumtype = type;
424178825Sdfr    cksum.checksum.length =
425178825Sdfr	sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR);
426178825Sdfr    cksum.checksum.data = malloc(cksum.checksum.length);
427178825Sdfr    if (cksum.checksum.data == NULL) {
428178825Sdfr	krb5_set_error_string(context, "out of memory");
429178825Sdfr	ret = ENOMEM;
430178825Sdfr	goto out;
431178825Sdfr    }
432178825Sdfr    ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length);
433178825Sdfr    if (ret != cksum.checksum.length) {
434178825Sdfr	krb5_set_error_string(context, "PAC checksum missing checksum");
435178825Sdfr	ret = EINVAL;
436178825Sdfr	goto out;
437178825Sdfr    }
438178825Sdfr
439178825Sdfr    if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) {
440178825Sdfr	krb5_set_error_string (context, "Checksum type %d not keyed",
441178825Sdfr			       cksum.cksumtype);
442178825Sdfr	ret = EINVAL;
443178825Sdfr	goto out;
444178825Sdfr    }
445178825Sdfr
446178825Sdfr    ret = krb5_crypto_init(context, key, 0, &crypto);
447178825Sdfr    if (ret)
448178825Sdfr	goto out;
449178825Sdfr
450178825Sdfr    ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM,
451178825Sdfr			       ptr, len, &cksum);
452178825Sdfr    free(cksum.checksum.data);
453178825Sdfr    krb5_crypto_destroy(context, crypto);
454178825Sdfr    krb5_storage_free(sp);
455178825Sdfr
456178825Sdfr    return ret;
457178825Sdfr
458178825Sdfrout:
459178825Sdfr    if (cksum.checksum.data)
460178825Sdfr	free(cksum.checksum.data);
461178825Sdfr    if (sp)
462178825Sdfr	krb5_storage_free(sp);
463178825Sdfr    if (crypto)
464178825Sdfr	krb5_crypto_destroy(context, crypto);
465178825Sdfr    return ret;
466178825Sdfr}
467178825Sdfr
468178825Sdfrstatic krb5_error_code
469178825Sdfrcreate_checksum(krb5_context context,
470178825Sdfr		const krb5_keyblock *key,
471178825Sdfr		void *data, size_t datalen,
472178825Sdfr		void *sig, size_t siglen)
473178825Sdfr{
474178825Sdfr    krb5_crypto crypto = NULL;
475178825Sdfr    krb5_error_code ret;
476178825Sdfr    Checksum cksum;
477178825Sdfr
478178825Sdfr    ret = krb5_crypto_init(context, key, 0, &crypto);
479178825Sdfr    if (ret)
480178825Sdfr	return ret;
481178825Sdfr
482178825Sdfr    ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0,
483178825Sdfr			       data, datalen, &cksum);
484178825Sdfr    krb5_crypto_destroy(context, crypto);
485178825Sdfr    if (ret)
486178825Sdfr	return ret;
487178825Sdfr
488178825Sdfr    if (cksum.checksum.length != siglen) {
489178825Sdfr	krb5_set_error_string(context, "pac checksum wrong length");
490178825Sdfr	free_Checksum(&cksum);
491178825Sdfr	return EINVAL;
492178825Sdfr    }
493178825Sdfr
494178825Sdfr    memcpy(sig, cksum.checksum.data, siglen);
495178825Sdfr    free_Checksum(&cksum);
496178825Sdfr
497178825Sdfr    return 0;
498178825Sdfr}
499178825Sdfr
500178825Sdfr
501178825Sdfr/*
502178825Sdfr *
503178825Sdfr */
504178825Sdfr
505178825Sdfr#define NTTIME_EPOCH 0x019DB1DED53E8000LL
506178825Sdfr
507178825Sdfrstatic uint64_t
508178825Sdfrunix2nttime(time_t unix_time)
509178825Sdfr{
510178825Sdfr    long long wt;
511178825Sdfr    wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
512178825Sdfr    return wt;
513178825Sdfr}
514178825Sdfr
515178825Sdfrstatic krb5_error_code
516178825Sdfrverify_logonname(krb5_context context,
517178825Sdfr		 const struct PAC_INFO_BUFFER *logon_name,
518178825Sdfr		 const krb5_data *data,
519178825Sdfr		 time_t authtime,
520178825Sdfr		 krb5_const_principal principal)
521178825Sdfr{
522178825Sdfr    krb5_error_code ret;
523178825Sdfr    krb5_principal p2;
524178825Sdfr    uint32_t time1, time2;
525178825Sdfr    krb5_storage *sp;
526178825Sdfr    uint16_t len;
527178825Sdfr    char *s;
528178825Sdfr
529178825Sdfr    sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo,
530178825Sdfr					logon_name->buffersize);
531178825Sdfr    if (sp == NULL) {
532178825Sdfr	krb5_set_error_string(context, "Out of memory");
533178825Sdfr	return ENOMEM;
534178825Sdfr    }
535178825Sdfr
536178825Sdfr    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
537178825Sdfr
538178825Sdfr    CHECK(ret, krb5_ret_uint32(sp, &time1), out);
539178825Sdfr    CHECK(ret, krb5_ret_uint32(sp, &time2), out);
540178825Sdfr
541178825Sdfr    {
542178825Sdfr	uint64_t t1, t2;
543178825Sdfr	t1 = unix2nttime(authtime);
544178825Sdfr	t2 = ((uint64_t)time2 << 32) | time1;
545178825Sdfr	if (t1 != t2) {
546178825Sdfr	    krb5_storage_free(sp);
547178825Sdfr	    krb5_set_error_string(context, "PAC timestamp mismatch");
548178825Sdfr	    return EINVAL;
549178825Sdfr	}
550178825Sdfr    }
551178825Sdfr    CHECK(ret, krb5_ret_uint16(sp, &len), out);
552178825Sdfr    if (len == 0) {
553178825Sdfr	krb5_storage_free(sp);
554178825Sdfr	krb5_set_error_string(context, "PAC logon name length missing");
555178825Sdfr	return EINVAL;
556178825Sdfr    }
557178825Sdfr
558178825Sdfr    s = malloc(len);
559178825Sdfr    if (s == NULL) {
560178825Sdfr	krb5_storage_free(sp);
561178825Sdfr	krb5_set_error_string(context, "Out of memory");
562178825Sdfr	return ENOMEM;
563178825Sdfr    }
564178825Sdfr    ret = krb5_storage_read(sp, s, len);
565178825Sdfr    if (ret != len) {
566178825Sdfr	krb5_storage_free(sp);
567178825Sdfr	krb5_set_error_string(context, "Failed to read pac logon name");
568178825Sdfr	return EINVAL;
569178825Sdfr    }
570178825Sdfr    krb5_storage_free(sp);
571178825Sdfr#if 1 /* cheat for now */
572178825Sdfr    {
573178825Sdfr	size_t i;
574178825Sdfr
575178825Sdfr	if (len & 1) {
576178825Sdfr	    krb5_set_error_string(context, "PAC logon name malformed");
577178825Sdfr	    return EINVAL;
578178825Sdfr	}
579178825Sdfr
580178825Sdfr	for (i = 0; i < len / 2; i++) {
581178825Sdfr	    if (s[(i * 2) + 1]) {
582178825Sdfr		krb5_set_error_string(context, "PAC logon name not ASCII");
583178825Sdfr		return EINVAL;
584178825Sdfr	    }
585178825Sdfr	    s[i] = s[i * 2];
586178825Sdfr	}
587178825Sdfr	s[i] = '\0';
588178825Sdfr    }
589178825Sdfr#else
590178825Sdfr    {
591178825Sdfr	uint16_t *ucs2;
592178825Sdfr	ssize_t ucs2len;
593178825Sdfr	size_t u8len;
594178825Sdfr
595178825Sdfr	ucs2 = malloc(sizeof(ucs2[0]) * len / 2);
596178825Sdfr	if (ucs2)
597178825Sdfr	    abort();
598178825Sdfr	ucs2len = wind_ucs2read(s, len / 2, ucs2);
599178825Sdfr	free(s);
600178825Sdfr	if (len < 0)
601178825Sdfr	    return -1;
602178825Sdfr	ret = wind_ucs2toutf8(ucs2, ucs2len, NULL, &u8len);
603178825Sdfr	if (ret < 0)
604178825Sdfr	    abort();
605178825Sdfr	s = malloc(u8len + 1);
606178825Sdfr	if (s == NULL)
607178825Sdfr	    abort();
608178825Sdfr	wind_ucs2toutf8(ucs2, ucs2len, s, &u8len);
609178825Sdfr	free(ucs2);
610178825Sdfr    }
611178825Sdfr#endif
612178825Sdfr    ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2);
613178825Sdfr    free(s);
614178825Sdfr    if (ret)
615178825Sdfr	return ret;
616178825Sdfr
617178825Sdfr    if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) {
618178825Sdfr	krb5_set_error_string(context, "PAC logon name mismatch");
619178825Sdfr	ret = EINVAL;
620178825Sdfr    }
621178825Sdfr    krb5_free_principal(context, p2);
622178825Sdfr    return ret;
623178825Sdfrout:
624178825Sdfr    return ret;
625178825Sdfr}
626178825Sdfr
627178825Sdfr/*
628178825Sdfr *
629178825Sdfr */
630178825Sdfr
631178825Sdfrstatic krb5_error_code
632178825Sdfrbuild_logon_name(krb5_context context,
633178825Sdfr		 time_t authtime,
634178825Sdfr		 krb5_const_principal principal,
635178825Sdfr		 krb5_data *logon)
636178825Sdfr{
637178825Sdfr    krb5_error_code ret;
638178825Sdfr    krb5_storage *sp;
639178825Sdfr    uint64_t t;
640178825Sdfr    char *s, *s2;
641178825Sdfr    size_t i, len;
642178825Sdfr
643178825Sdfr    t = unix2nttime(authtime);
644178825Sdfr
645178825Sdfr    krb5_data_zero(logon);
646178825Sdfr
647178825Sdfr    sp = krb5_storage_emem();
648178825Sdfr    if (sp == NULL) {
649178825Sdfr	krb5_set_error_string(context, "out of memory");
650178825Sdfr	return ENOMEM;
651178825Sdfr    }
652178825Sdfr    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
653178825Sdfr
654178825Sdfr    CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out);
655178825Sdfr    CHECK(ret, krb5_store_uint32(sp, t >> 32), out);
656178825Sdfr
657178825Sdfr    ret = krb5_unparse_name_flags(context, principal,
658178825Sdfr				  KRB5_PRINCIPAL_UNPARSE_NO_REALM, &s);
659178825Sdfr    if (ret)
660178825Sdfr	goto out;
661178825Sdfr
662178825Sdfr    len = strlen(s);
663178825Sdfr
664178825Sdfr    CHECK(ret, krb5_store_uint16(sp, len * 2), out);
665178825Sdfr
666178825Sdfr#if 1 /* cheat for now */
667178825Sdfr    s2 = malloc(len * 2);
668178825Sdfr    if (s2 == NULL) {
669178825Sdfr	ret = ENOMEM;
670178825Sdfr	free(s);
671178825Sdfr	goto out;
672178825Sdfr    }
673178825Sdfr    for (i = 0; i < len; i++) {
674178825Sdfr	s2[i * 2] = s[i];
675178825Sdfr	s2[i * 2 + 1] = 0;
676178825Sdfr    }
677178825Sdfr    free(s);
678178825Sdfr#else
679178825Sdfr    /* write libwind code here */
680178825Sdfr#endif
681178825Sdfr
682178825Sdfr    ret = krb5_storage_write(sp, s2, len * 2);
683178825Sdfr    free(s2);
684178825Sdfr    if (ret != len * 2) {
685178825Sdfr	ret = ENOMEM;
686178825Sdfr	goto out;
687178825Sdfr    }
688178825Sdfr    ret = krb5_storage_to_data(sp, logon);
689178825Sdfr    if (ret)
690178825Sdfr	goto out;
691178825Sdfr    krb5_storage_free(sp);
692178825Sdfr
693178825Sdfr    return 0;
694178825Sdfrout:
695178825Sdfr    krb5_storage_free(sp);
696178825Sdfr    return ret;
697178825Sdfr}
698178825Sdfr
699178825Sdfr
700178825Sdfr/*
701178825Sdfr *
702178825Sdfr */
703178825Sdfr
704178825Sdfrkrb5_error_code
705178825Sdfrkrb5_pac_verify(krb5_context context,
706178825Sdfr		const krb5_pac pac,
707178825Sdfr		time_t authtime,
708178825Sdfr		krb5_const_principal principal,
709178825Sdfr		const krb5_keyblock *server,
710178825Sdfr		const krb5_keyblock *privsvr)
711178825Sdfr{
712178825Sdfr    krb5_error_code ret;
713178825Sdfr
714178825Sdfr    if (pac->server_checksum == NULL) {
715178825Sdfr	krb5_set_error_string(context, "PAC missing server checksum");
716178825Sdfr	return EINVAL;
717178825Sdfr    }
718178825Sdfr    if (pac->privsvr_checksum == NULL) {
719178825Sdfr	krb5_set_error_string(context, "PAC missing kdc checksum");
720178825Sdfr	return EINVAL;
721178825Sdfr    }
722178825Sdfr    if (pac->logon_name == NULL) {
723178825Sdfr	krb5_set_error_string(context, "PAC missing logon name");
724178825Sdfr	return EINVAL;
725178825Sdfr    }
726178825Sdfr
727178825Sdfr    ret = verify_logonname(context,
728178825Sdfr			   pac->logon_name,
729178825Sdfr			   &pac->data,
730178825Sdfr			   authtime,
731178825Sdfr			   principal);
732178825Sdfr    if (ret)
733178825Sdfr	return ret;
734178825Sdfr
735178825Sdfr    /*
736178825Sdfr     * in the service case, clean out data option of the privsvr and
737178825Sdfr     * server checksum before checking the checksum.
738178825Sdfr     */
739178825Sdfr    {
740178825Sdfr	krb5_data *copy;
741178825Sdfr
742178825Sdfr	ret = krb5_copy_data(context, &pac->data, &copy);
743178825Sdfr	if (ret)
744178825Sdfr	    return ret;
745178825Sdfr
746178825Sdfr	if (pac->server_checksum->buffersize < 4)
747178825Sdfr	    return EINVAL;
748178825Sdfr	if (pac->privsvr_checksum->buffersize < 4)
749178825Sdfr	    return EINVAL;
750178825Sdfr
751178825Sdfr	memset((char *)copy->data + pac->server_checksum->offset_lo + 4,
752178825Sdfr	       0,
753178825Sdfr	       pac->server_checksum->buffersize - 4);
754178825Sdfr
755178825Sdfr	memset((char *)copy->data + pac->privsvr_checksum->offset_lo + 4,
756178825Sdfr	       0,
757178825Sdfr	       pac->privsvr_checksum->buffersize - 4);
758178825Sdfr
759178825Sdfr	ret = verify_checksum(context,
760178825Sdfr			      pac->server_checksum,
761178825Sdfr			      &pac->data,
762178825Sdfr			      copy->data,
763178825Sdfr			      copy->length,
764178825Sdfr			      server);
765178825Sdfr	krb5_free_data(context, copy);
766178825Sdfr	if (ret)
767178825Sdfr	    return ret;
768178825Sdfr    }
769178825Sdfr    if (privsvr) {
770178825Sdfr	ret = verify_checksum(context,
771178825Sdfr			      pac->privsvr_checksum,
772178825Sdfr			      &pac->data,
773178825Sdfr			      (char *)pac->data.data
774178825Sdfr			      + pac->server_checksum->offset_lo + 4,
775178825Sdfr			      pac->server_checksum->buffersize - 4,
776178825Sdfr			      privsvr);
777178825Sdfr	if (ret)
778178825Sdfr	    return ret;
779178825Sdfr    }
780178825Sdfr
781178825Sdfr    return 0;
782178825Sdfr}
783178825Sdfr
784178825Sdfr/*
785178825Sdfr *
786178825Sdfr */
787178825Sdfr
788178825Sdfrstatic krb5_error_code
789178825Sdfrfill_zeros(krb5_context context, krb5_storage *sp, size_t len)
790178825Sdfr{
791178825Sdfr    ssize_t sret;
792178825Sdfr    size_t l;
793178825Sdfr
794178825Sdfr    while (len) {
795178825Sdfr	l = len;
796178825Sdfr	if (l > sizeof(zeros))
797178825Sdfr	    l = sizeof(zeros);
798178825Sdfr	sret = krb5_storage_write(sp, zeros, l);
799178825Sdfr	if (sret <= 0) {
800178825Sdfr	    krb5_set_error_string(context, "out of memory");
801178825Sdfr	    return ENOMEM;
802178825Sdfr	}
803178825Sdfr	len -= sret;
804178825Sdfr    }
805178825Sdfr    return 0;
806178825Sdfr}
807178825Sdfr
808178825Sdfrstatic krb5_error_code
809178825Sdfrpac_checksum(krb5_context context,
810178825Sdfr	     const krb5_keyblock *key,
811178825Sdfr	     uint32_t *cksumtype,
812178825Sdfr	     size_t *cksumsize)
813178825Sdfr{
814178825Sdfr    krb5_cksumtype cktype;
815178825Sdfr    krb5_error_code ret;
816178825Sdfr    krb5_crypto crypto = NULL;
817178825Sdfr
818178825Sdfr    ret = krb5_crypto_init(context, key, 0, &crypto);
819178825Sdfr    if (ret)
820178825Sdfr	return ret;
821178825Sdfr
822178825Sdfr    ret = krb5_crypto_get_checksum_type(context, crypto, &cktype);
823178825Sdfr    ret = krb5_crypto_destroy(context, crypto);
824178825Sdfr    if (ret)
825178825Sdfr	return ret;
826178825Sdfr
827178825Sdfr    if (krb5_checksum_is_keyed(context, cktype) == FALSE) {
828178825Sdfr	krb5_set_error_string(context, "PAC checksum type is not keyed");
829178825Sdfr	return EINVAL;
830178825Sdfr    }
831178825Sdfr
832178825Sdfr    ret = krb5_checksumsize(context, cktype, cksumsize);
833178825Sdfr    if (ret)
834178825Sdfr	return ret;
835178825Sdfr
836178825Sdfr    *cksumtype = (uint32_t)cktype;
837178825Sdfr
838178825Sdfr    return 0;
839178825Sdfr}
840178825Sdfr
841178825Sdfrkrb5_error_code
842178825Sdfr_krb5_pac_sign(krb5_context context,
843178825Sdfr	       krb5_pac p,
844178825Sdfr	       time_t authtime,
845178825Sdfr	       krb5_principal principal,
846178825Sdfr	       const krb5_keyblock *server_key,
847178825Sdfr	       const krb5_keyblock *priv_key,
848178825Sdfr	       krb5_data *data)
849178825Sdfr{
850178825Sdfr    krb5_error_code ret;
851178825Sdfr    krb5_storage *sp = NULL, *spdata = NULL;
852178825Sdfr    uint32_t end;
853178825Sdfr    size_t server_size, priv_size;
854178825Sdfr    uint32_t server_offset = 0, priv_offset = 0;
855178825Sdfr    uint32_t server_cksumtype = 0, priv_cksumtype = 0;
856178825Sdfr    int i, num = 0;
857178825Sdfr    krb5_data logon, d;
858178825Sdfr
859178825Sdfr    krb5_data_zero(&logon);
860178825Sdfr
861178825Sdfr    if (p->logon_name == NULL)
862178825Sdfr	num++;
863178825Sdfr    if (p->server_checksum == NULL)
864178825Sdfr	num++;
865178825Sdfr    if (p->privsvr_checksum == NULL)
866178825Sdfr	num++;
867178825Sdfr
868178825Sdfr    if (num) {
869178825Sdfr	void *ptr;
870178825Sdfr
871178825Sdfr	ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1)));
872178825Sdfr	if (ptr == NULL) {
873178825Sdfr	    krb5_set_error_string(context, "out of memory");
874178825Sdfr	    return ENOMEM;
875178825Sdfr	}
876178825Sdfr	p->pac = ptr;
877178825Sdfr
878178825Sdfr	if (p->logon_name == NULL) {
879178825Sdfr	    p->logon_name = &p->pac->buffers[p->pac->numbuffers++];
880178825Sdfr	    memset(p->logon_name, 0, sizeof(*p->logon_name));
881178825Sdfr	    p->logon_name->type = PAC_LOGON_NAME;
882178825Sdfr	}
883178825Sdfr	if (p->server_checksum == NULL) {
884178825Sdfr	    p->server_checksum = &p->pac->buffers[p->pac->numbuffers++];
885178825Sdfr	    memset(p->server_checksum, 0, sizeof(*p->server_checksum));
886178825Sdfr	    p->server_checksum->type = PAC_SERVER_CHECKSUM;
887178825Sdfr	}
888178825Sdfr	if (p->privsvr_checksum == NULL) {
889178825Sdfr	    p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++];
890178825Sdfr	    memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum));
891178825Sdfr	    p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM;
892178825Sdfr	}
893178825Sdfr    }
894178825Sdfr
895178825Sdfr    /* Calculate LOGON NAME */
896178825Sdfr    ret = build_logon_name(context, authtime, principal, &logon);
897178825Sdfr    if (ret)
898178825Sdfr	goto out;
899178825Sdfr
900178825Sdfr    /* Set lengths for checksum */
901178825Sdfr    ret = pac_checksum(context, server_key, &server_cksumtype, &server_size);
902178825Sdfr    if (ret)
903178825Sdfr	goto out;
904178825Sdfr    ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size);
905178825Sdfr    if (ret)
906178825Sdfr	goto out;
907178825Sdfr
908178825Sdfr    /* Encode PAC */
909178825Sdfr    sp = krb5_storage_emem();
910178825Sdfr    if (sp == NULL) {
911178825Sdfr	krb5_set_error_string(context, "out of memory");
912178825Sdfr	return ENOMEM;
913178825Sdfr    }
914178825Sdfr    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
915178825Sdfr
916178825Sdfr    spdata = krb5_storage_emem();
917178825Sdfr    if (spdata == NULL) {
918178825Sdfr	krb5_storage_free(sp);
919178825Sdfr	krb5_set_error_string(context, "out of memory");
920178825Sdfr	return ENOMEM;
921178825Sdfr    }
922178825Sdfr    krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE);
923178825Sdfr
924178825Sdfr    CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out);
925178825Sdfr    CHECK(ret, krb5_store_uint32(sp, p->pac->version), out);
926178825Sdfr
927178825Sdfr    end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
928178825Sdfr
929178825Sdfr    for (i = 0; i < p->pac->numbuffers; i++) {
930178825Sdfr	uint32_t len;
931178825Sdfr	size_t sret;
932178825Sdfr	void *ptr = NULL;
933178825Sdfr
934178825Sdfr	/* store data */
935178825Sdfr
936178825Sdfr	if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
937178825Sdfr	    len = server_size + 4;
938178825Sdfr	    server_offset = end + 4;
939178825Sdfr	    CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out);
940178825Sdfr	    CHECK(ret, fill_zeros(context, spdata, server_size), out);
941178825Sdfr	} else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
942178825Sdfr	    len = priv_size + 4;
943178825Sdfr	    priv_offset = end + 4;
944178825Sdfr	    CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
945178825Sdfr	    CHECK(ret, fill_zeros(context, spdata, priv_size), out);
946178825Sdfr	} else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
947178825Sdfr	    len = krb5_storage_write(spdata, logon.data, logon.length);
948178825Sdfr	    if (logon.length != len) {
949178825Sdfr		ret = EINVAL;
950178825Sdfr		goto out;
951178825Sdfr	    }
952178825Sdfr	} else {
953178825Sdfr	    len = p->pac->buffers[i].buffersize;
954178825Sdfr	    ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo;
955178825Sdfr
956178825Sdfr	    sret = krb5_storage_write(spdata, ptr, len);
957178825Sdfr	    if (sret != len) {
958178825Sdfr		krb5_set_error_string(context, "out of memory");
959178825Sdfr		ret = ENOMEM;
960178825Sdfr		goto out;
961178825Sdfr	    }
962178825Sdfr	    /* XXX if not aligned, fill_zeros */
963178825Sdfr	}
964178825Sdfr
965178825Sdfr	/* write header */
966178825Sdfr	CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out);
967178825Sdfr	CHECK(ret, krb5_store_uint32(sp, len), out);
968178825Sdfr	CHECK(ret, krb5_store_uint32(sp, end), out);
969178825Sdfr	CHECK(ret, krb5_store_uint32(sp, 0), out);
970178825Sdfr
971178825Sdfr	/* advance data endpointer and align */
972178825Sdfr	{
973178825Sdfr	    int32_t e;
974178825Sdfr
975178825Sdfr	    end += len;
976178825Sdfr	    e = ((end + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
977178825Sdfr	    if (end != e) {
978178825Sdfr		CHECK(ret, fill_zeros(context, spdata, e - end), out);
979178825Sdfr	    }
980178825Sdfr	    end = e;
981178825Sdfr	}
982178825Sdfr
983178825Sdfr    }
984178825Sdfr
985178825Sdfr    /* assert (server_offset != 0 && priv_offset != 0); */
986178825Sdfr
987178825Sdfr    /* export PAC */
988178825Sdfr    ret = krb5_storage_to_data(spdata, &d);
989178825Sdfr    if (ret) {
990178825Sdfr	krb5_set_error_string(context, "out of memory");
991178825Sdfr	goto out;
992178825Sdfr    }
993178825Sdfr    ret = krb5_storage_write(sp, d.data, d.length);
994178825Sdfr    if (ret != d.length) {
995178825Sdfr	krb5_data_free(&d);
996178825Sdfr	krb5_set_error_string(context, "out of memory");
997178825Sdfr	ret = ENOMEM;
998178825Sdfr	goto out;
999178825Sdfr    }
1000178825Sdfr    krb5_data_free(&d);
1001178825Sdfr
1002178825Sdfr    ret = krb5_storage_to_data(sp, &d);
1003178825Sdfr    if (ret) {
1004178825Sdfr	krb5_set_error_string(context, "out of memory");
1005178825Sdfr	goto out;
1006178825Sdfr    }
1007178825Sdfr
1008178825Sdfr    /* sign */
1009178825Sdfr
1010178825Sdfr    ret = create_checksum(context, server_key,
1011178825Sdfr			  d.data, d.length,
1012178825Sdfr			  (char *)d.data + server_offset, server_size);
1013178825Sdfr    if (ret) {
1014178825Sdfr	krb5_data_free(&d);
1015178825Sdfr	goto out;
1016178825Sdfr    }
1017178825Sdfr
1018178825Sdfr    ret = create_checksum(context, priv_key,
1019178825Sdfr			  (char *)d.data + server_offset, server_size,
1020178825Sdfr			  (char *)d.data + priv_offset, priv_size);
1021178825Sdfr    if (ret) {
1022178825Sdfr	krb5_data_free(&d);
1023178825Sdfr	goto out;
1024178825Sdfr    }
1025178825Sdfr
1026178825Sdfr    /* done */
1027178825Sdfr    *data = d;
1028178825Sdfr
1029178825Sdfr    krb5_data_free(&logon);
1030178825Sdfr    krb5_storage_free(sp);
1031178825Sdfr    krb5_storage_free(spdata);
1032178825Sdfr
1033178825Sdfr    return 0;
1034178825Sdfrout:
1035178825Sdfr    krb5_data_free(&logon);
1036178825Sdfr    if (sp)
1037178825Sdfr	krb5_storage_free(sp);
1038178825Sdfr    if (spdata)
1039178825Sdfr	krb5_storage_free(spdata);
1040178825Sdfr    return ret;
1041178825Sdfr}
1042