1*0a6a1f1dSLionel Sambuc /* $NetBSD: pac.c,v 1.2 2014/05/12 15:21:46 christos Exp $ */
2ebfedea0SLionel Sambuc
3ebfedea0SLionel Sambuc /*
4ebfedea0SLionel Sambuc * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
5ebfedea0SLionel Sambuc * (Royal Institute of Technology, Stockholm, Sweden).
6ebfedea0SLionel Sambuc * All rights reserved.
7ebfedea0SLionel Sambuc *
8ebfedea0SLionel Sambuc * Redistribution and use in source and binary forms, with or without
9ebfedea0SLionel Sambuc * modification, are permitted provided that the following conditions
10ebfedea0SLionel Sambuc * are met:
11ebfedea0SLionel Sambuc *
12ebfedea0SLionel Sambuc * 1. Redistributions of source code must retain the above copyright
13ebfedea0SLionel Sambuc * notice, this list of conditions and the following disclaimer.
14ebfedea0SLionel Sambuc *
15ebfedea0SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright
16ebfedea0SLionel Sambuc * notice, this list of conditions and the following disclaimer in the
17ebfedea0SLionel Sambuc * documentation and/or other materials provided with the distribution.
18ebfedea0SLionel Sambuc *
19ebfedea0SLionel Sambuc * 3. Neither the name of the Institute nor the names of its contributors
20ebfedea0SLionel Sambuc * may be used to endorse or promote products derived from this software
21ebfedea0SLionel Sambuc * without specific prior written permission.
22ebfedea0SLionel Sambuc *
23ebfedea0SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24ebfedea0SLionel Sambuc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25ebfedea0SLionel Sambuc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ebfedea0SLionel Sambuc * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27ebfedea0SLionel Sambuc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28ebfedea0SLionel Sambuc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29ebfedea0SLionel Sambuc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30ebfedea0SLionel Sambuc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31ebfedea0SLionel Sambuc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32ebfedea0SLionel Sambuc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33ebfedea0SLionel Sambuc * SUCH DAMAGE.
34ebfedea0SLionel Sambuc */
35ebfedea0SLionel Sambuc
36ebfedea0SLionel Sambuc #include "krb5_locl.h"
37ebfedea0SLionel Sambuc #include <krb5/wind.h>
38ebfedea0SLionel Sambuc
39ebfedea0SLionel Sambuc struct PAC_INFO_BUFFER {
40ebfedea0SLionel Sambuc uint32_t type;
41ebfedea0SLionel Sambuc uint32_t buffersize;
42ebfedea0SLionel Sambuc uint32_t offset_hi;
43ebfedea0SLionel Sambuc uint32_t offset_lo;
44ebfedea0SLionel Sambuc };
45ebfedea0SLionel Sambuc
46ebfedea0SLionel Sambuc struct PACTYPE {
47ebfedea0SLionel Sambuc uint32_t numbuffers;
48ebfedea0SLionel Sambuc uint32_t version;
49ebfedea0SLionel Sambuc struct PAC_INFO_BUFFER buffers[1];
50ebfedea0SLionel Sambuc };
51ebfedea0SLionel Sambuc
52ebfedea0SLionel Sambuc struct krb5_pac_data {
53ebfedea0SLionel Sambuc struct PACTYPE *pac;
54ebfedea0SLionel Sambuc krb5_data data;
55ebfedea0SLionel Sambuc struct PAC_INFO_BUFFER *server_checksum;
56ebfedea0SLionel Sambuc struct PAC_INFO_BUFFER *privsvr_checksum;
57ebfedea0SLionel Sambuc struct PAC_INFO_BUFFER *logon_name;
58ebfedea0SLionel Sambuc };
59ebfedea0SLionel Sambuc
60ebfedea0SLionel Sambuc #define PAC_ALIGNMENT 8
61ebfedea0SLionel Sambuc
62ebfedea0SLionel Sambuc #define PACTYPE_SIZE 8
63ebfedea0SLionel Sambuc #define PAC_INFO_BUFFER_SIZE 16
64ebfedea0SLionel Sambuc
65ebfedea0SLionel Sambuc #define PAC_SERVER_CHECKSUM 6
66ebfedea0SLionel Sambuc #define PAC_PRIVSVR_CHECKSUM 7
67ebfedea0SLionel Sambuc #define PAC_LOGON_NAME 10
68ebfedea0SLionel Sambuc #define PAC_CONSTRAINED_DELEGATION 11
69ebfedea0SLionel Sambuc
70ebfedea0SLionel Sambuc #define CHECK(r,f,l) \
71ebfedea0SLionel Sambuc do { \
72ebfedea0SLionel Sambuc if (((r) = f ) != 0) { \
73ebfedea0SLionel Sambuc krb5_clear_error_message(context); \
74ebfedea0SLionel Sambuc goto l; \
75ebfedea0SLionel Sambuc } \
76ebfedea0SLionel Sambuc } while(0)
77ebfedea0SLionel Sambuc
78ebfedea0SLionel Sambuc static const char zeros[PAC_ALIGNMENT] = { 0 };
79ebfedea0SLionel Sambuc
80ebfedea0SLionel Sambuc /*
81ebfedea0SLionel Sambuc * HMAC-MD5 checksum over any key (needed for the PAC routines)
82ebfedea0SLionel Sambuc */
83ebfedea0SLionel Sambuc
84ebfedea0SLionel Sambuc static krb5_error_code
HMAC_MD5_any_checksum(krb5_context context,const krb5_keyblock * key,const void * data,size_t len,unsigned usage,Checksum * result)85ebfedea0SLionel Sambuc HMAC_MD5_any_checksum(krb5_context context,
86ebfedea0SLionel Sambuc const krb5_keyblock *key,
87ebfedea0SLionel Sambuc const void *data,
88ebfedea0SLionel Sambuc size_t len,
89ebfedea0SLionel Sambuc unsigned usage,
90ebfedea0SLionel Sambuc Checksum *result)
91ebfedea0SLionel Sambuc {
92ebfedea0SLionel Sambuc struct _krb5_key_data local_key;
93ebfedea0SLionel Sambuc krb5_error_code ret;
94ebfedea0SLionel Sambuc
95ebfedea0SLionel Sambuc memset(&local_key, 0, sizeof(local_key));
96ebfedea0SLionel Sambuc
97ebfedea0SLionel Sambuc ret = krb5_copy_keyblock(context, key, &local_key.key);
98ebfedea0SLionel Sambuc if (ret)
99ebfedea0SLionel Sambuc return ret;
100ebfedea0SLionel Sambuc
101ebfedea0SLionel Sambuc ret = krb5_data_alloc (&result->checksum, 16);
102ebfedea0SLionel Sambuc if (ret) {
103ebfedea0SLionel Sambuc krb5_free_keyblock(context, local_key.key);
104ebfedea0SLionel Sambuc return ret;
105ebfedea0SLionel Sambuc }
106ebfedea0SLionel Sambuc
107ebfedea0SLionel Sambuc result->cksumtype = CKSUMTYPE_HMAC_MD5;
108ebfedea0SLionel Sambuc ret = _krb5_HMAC_MD5_checksum(context, &local_key, data, len, usage, result);
109ebfedea0SLionel Sambuc if (ret)
110ebfedea0SLionel Sambuc krb5_data_free(&result->checksum);
111ebfedea0SLionel Sambuc
112ebfedea0SLionel Sambuc krb5_free_keyblock(context, local_key.key);
113ebfedea0SLionel Sambuc return ret;
114ebfedea0SLionel Sambuc }
115ebfedea0SLionel Sambuc
116ebfedea0SLionel Sambuc
117ebfedea0SLionel Sambuc /*
118ebfedea0SLionel Sambuc *
119ebfedea0SLionel Sambuc */
120ebfedea0SLionel Sambuc
121ebfedea0SLionel Sambuc KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_pac_parse(krb5_context context,const void * ptr,size_t len,krb5_pac * pac)122ebfedea0SLionel Sambuc krb5_pac_parse(krb5_context context, const void *ptr, size_t len,
123ebfedea0SLionel Sambuc krb5_pac *pac)
124ebfedea0SLionel Sambuc {
125ebfedea0SLionel Sambuc krb5_error_code ret;
126ebfedea0SLionel Sambuc krb5_pac p;
127ebfedea0SLionel Sambuc krb5_storage *sp = NULL;
128ebfedea0SLionel Sambuc uint32_t i, tmp, tmp2, header_end;
129ebfedea0SLionel Sambuc
130ebfedea0SLionel Sambuc p = calloc(1, sizeof(*p));
131ebfedea0SLionel Sambuc if (p == NULL) {
132ebfedea0SLionel Sambuc ret = krb5_enomem(context);
133ebfedea0SLionel Sambuc goto out;
134ebfedea0SLionel Sambuc }
135ebfedea0SLionel Sambuc
136ebfedea0SLionel Sambuc sp = krb5_storage_from_readonly_mem(ptr, len);
137ebfedea0SLionel Sambuc if (sp == NULL) {
138ebfedea0SLionel Sambuc ret = krb5_enomem(context);
139ebfedea0SLionel Sambuc goto out;
140ebfedea0SLionel Sambuc }
141ebfedea0SLionel Sambuc krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
142ebfedea0SLionel Sambuc
143ebfedea0SLionel Sambuc CHECK(ret, krb5_ret_uint32(sp, &tmp), out);
144ebfedea0SLionel Sambuc CHECK(ret, krb5_ret_uint32(sp, &tmp2), out);
145ebfedea0SLionel Sambuc if (tmp < 1) {
146ebfedea0SLionel Sambuc ret = EINVAL; /* Too few buffers */
147ebfedea0SLionel Sambuc krb5_set_error_message(context, ret, N_("PAC have too few buffer", ""));
148ebfedea0SLionel Sambuc goto out;
149ebfedea0SLionel Sambuc }
150ebfedea0SLionel Sambuc if (tmp2 != 0) {
151ebfedea0SLionel Sambuc ret = EINVAL; /* Wrong version */
152ebfedea0SLionel Sambuc krb5_set_error_message(context, ret,
153ebfedea0SLionel Sambuc N_("PAC have wrong version %d", ""),
154ebfedea0SLionel Sambuc (int)tmp2);
155ebfedea0SLionel Sambuc goto out;
156ebfedea0SLionel Sambuc }
157ebfedea0SLionel Sambuc
158ebfedea0SLionel Sambuc p->pac = calloc(1,
159ebfedea0SLionel Sambuc sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1)));
160ebfedea0SLionel Sambuc if (p->pac == NULL) {
161ebfedea0SLionel Sambuc ret = krb5_enomem(context);
162ebfedea0SLionel Sambuc goto out;
163ebfedea0SLionel Sambuc }
164ebfedea0SLionel Sambuc
165ebfedea0SLionel Sambuc p->pac->numbuffers = tmp;
166ebfedea0SLionel Sambuc p->pac->version = tmp2;
167ebfedea0SLionel Sambuc
168ebfedea0SLionel Sambuc header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
169ebfedea0SLionel Sambuc if (header_end > len) {
170ebfedea0SLionel Sambuc ret = EINVAL;
171ebfedea0SLionel Sambuc goto out;
172ebfedea0SLionel Sambuc }
173ebfedea0SLionel Sambuc
174ebfedea0SLionel Sambuc for (i = 0; i < p->pac->numbuffers; i++) {
175ebfedea0SLionel Sambuc CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out);
176ebfedea0SLionel Sambuc CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out);
177ebfedea0SLionel Sambuc CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out);
178ebfedea0SLionel Sambuc CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out);
179ebfedea0SLionel Sambuc
180ebfedea0SLionel Sambuc /* consistency checks */
181ebfedea0SLionel Sambuc if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) {
182ebfedea0SLionel Sambuc ret = EINVAL;
183ebfedea0SLionel Sambuc krb5_set_error_message(context, ret,
184ebfedea0SLionel Sambuc N_("PAC out of allignment", ""));
185ebfedea0SLionel Sambuc goto out;
186ebfedea0SLionel Sambuc }
187ebfedea0SLionel Sambuc if (p->pac->buffers[i].offset_hi) {
188ebfedea0SLionel Sambuc ret = EINVAL;
189ebfedea0SLionel Sambuc krb5_set_error_message(context, ret,
190ebfedea0SLionel Sambuc N_("PAC high offset set", ""));
191ebfedea0SLionel Sambuc goto out;
192ebfedea0SLionel Sambuc }
193ebfedea0SLionel Sambuc if (p->pac->buffers[i].offset_lo > len) {
194ebfedea0SLionel Sambuc ret = EINVAL;
195ebfedea0SLionel Sambuc krb5_set_error_message(context, ret,
196ebfedea0SLionel Sambuc N_("PAC offset off end", ""));
197ebfedea0SLionel Sambuc goto out;
198ebfedea0SLionel Sambuc }
199ebfedea0SLionel Sambuc if (p->pac->buffers[i].offset_lo < header_end) {
200ebfedea0SLionel Sambuc ret = EINVAL;
201ebfedea0SLionel Sambuc krb5_set_error_message(context, ret,
202ebfedea0SLionel Sambuc N_("PAC offset inside header: %lu %lu", ""),
203ebfedea0SLionel Sambuc (unsigned long)p->pac->buffers[i].offset_lo,
204ebfedea0SLionel Sambuc (unsigned long)header_end);
205ebfedea0SLionel Sambuc goto out;
206ebfedea0SLionel Sambuc }
207ebfedea0SLionel Sambuc if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){
208ebfedea0SLionel Sambuc ret = EINVAL;
209ebfedea0SLionel Sambuc krb5_set_error_message(context, ret, N_("PAC length off end", ""));
210ebfedea0SLionel Sambuc goto out;
211ebfedea0SLionel Sambuc }
212ebfedea0SLionel Sambuc
213ebfedea0SLionel Sambuc /* let save pointer to data we need later */
214ebfedea0SLionel Sambuc if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
215ebfedea0SLionel Sambuc if (p->server_checksum) {
216ebfedea0SLionel Sambuc ret = EINVAL;
217ebfedea0SLionel Sambuc krb5_set_error_message(context, ret,
218ebfedea0SLionel Sambuc N_("PAC have two server checksums", ""));
219ebfedea0SLionel Sambuc goto out;
220ebfedea0SLionel Sambuc }
221ebfedea0SLionel Sambuc p->server_checksum = &p->pac->buffers[i];
222ebfedea0SLionel Sambuc } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
223ebfedea0SLionel Sambuc if (p->privsvr_checksum) {
224ebfedea0SLionel Sambuc ret = EINVAL;
225ebfedea0SLionel Sambuc krb5_set_error_message(context, ret,
226ebfedea0SLionel Sambuc N_("PAC have two KDC checksums", ""));
227ebfedea0SLionel Sambuc goto out;
228ebfedea0SLionel Sambuc }
229ebfedea0SLionel Sambuc p->privsvr_checksum = &p->pac->buffers[i];
230ebfedea0SLionel Sambuc } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
231ebfedea0SLionel Sambuc if (p->logon_name) {
232ebfedea0SLionel Sambuc ret = EINVAL;
233ebfedea0SLionel Sambuc krb5_set_error_message(context, ret,
234ebfedea0SLionel Sambuc N_("PAC have two logon names", ""));
235ebfedea0SLionel Sambuc goto out;
236ebfedea0SLionel Sambuc }
237ebfedea0SLionel Sambuc p->logon_name = &p->pac->buffers[i];
238ebfedea0SLionel Sambuc }
239ebfedea0SLionel Sambuc }
240ebfedea0SLionel Sambuc
241ebfedea0SLionel Sambuc ret = krb5_data_copy(&p->data, ptr, len);
242ebfedea0SLionel Sambuc if (ret)
243ebfedea0SLionel Sambuc goto out;
244ebfedea0SLionel Sambuc
245ebfedea0SLionel Sambuc krb5_storage_free(sp);
246ebfedea0SLionel Sambuc
247ebfedea0SLionel Sambuc *pac = p;
248ebfedea0SLionel Sambuc return 0;
249ebfedea0SLionel Sambuc
250ebfedea0SLionel Sambuc out:
251ebfedea0SLionel Sambuc if (sp)
252ebfedea0SLionel Sambuc krb5_storage_free(sp);
253ebfedea0SLionel Sambuc if (p) {
254ebfedea0SLionel Sambuc if (p->pac)
255ebfedea0SLionel Sambuc free(p->pac);
256ebfedea0SLionel Sambuc free(p);
257ebfedea0SLionel Sambuc }
258ebfedea0SLionel Sambuc *pac = NULL;
259ebfedea0SLionel Sambuc
260ebfedea0SLionel Sambuc return ret;
261ebfedea0SLionel Sambuc }
262ebfedea0SLionel Sambuc
263ebfedea0SLionel Sambuc KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_pac_init(krb5_context context,krb5_pac * pac)264ebfedea0SLionel Sambuc krb5_pac_init(krb5_context context, krb5_pac *pac)
265ebfedea0SLionel Sambuc {
266ebfedea0SLionel Sambuc krb5_error_code ret;
267ebfedea0SLionel Sambuc krb5_pac p;
268ebfedea0SLionel Sambuc
269ebfedea0SLionel Sambuc p = calloc(1, sizeof(*p));
270ebfedea0SLionel Sambuc if (p == NULL) {
271ebfedea0SLionel Sambuc return krb5_enomem(context);
272ebfedea0SLionel Sambuc }
273ebfedea0SLionel Sambuc
274ebfedea0SLionel Sambuc p->pac = calloc(1, sizeof(*p->pac));
275ebfedea0SLionel Sambuc if (p->pac == NULL) {
276ebfedea0SLionel Sambuc free(p);
277ebfedea0SLionel Sambuc return krb5_enomem(context);
278ebfedea0SLionel Sambuc }
279ebfedea0SLionel Sambuc
280ebfedea0SLionel Sambuc ret = krb5_data_alloc(&p->data, PACTYPE_SIZE);
281ebfedea0SLionel Sambuc if (ret) {
282ebfedea0SLionel Sambuc free (p->pac);
283ebfedea0SLionel Sambuc free(p);
284ebfedea0SLionel Sambuc return krb5_enomem(context);
285ebfedea0SLionel Sambuc }
286ebfedea0SLionel Sambuc
287ebfedea0SLionel Sambuc *pac = p;
288ebfedea0SLionel Sambuc return 0;
289ebfedea0SLionel Sambuc }
290ebfedea0SLionel Sambuc
291ebfedea0SLionel Sambuc KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_pac_add_buffer(krb5_context context,krb5_pac p,uint32_t type,const krb5_data * data)292ebfedea0SLionel Sambuc krb5_pac_add_buffer(krb5_context context, krb5_pac p,
293ebfedea0SLionel Sambuc uint32_t type, const krb5_data *data)
294ebfedea0SLionel Sambuc {
295ebfedea0SLionel Sambuc krb5_error_code ret;
296ebfedea0SLionel Sambuc void *ptr;
297ebfedea0SLionel Sambuc size_t len, offset, header_end, old_end;
298ebfedea0SLionel Sambuc uint32_t i;
299ebfedea0SLionel Sambuc
300ebfedea0SLionel Sambuc len = p->pac->numbuffers;
301ebfedea0SLionel Sambuc
302ebfedea0SLionel Sambuc ptr = realloc(p->pac,
303ebfedea0SLionel Sambuc sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len));
304ebfedea0SLionel Sambuc if (ptr == NULL)
305ebfedea0SLionel Sambuc return krb5_enomem(context);
306ebfedea0SLionel Sambuc
307ebfedea0SLionel Sambuc p->pac = ptr;
308ebfedea0SLionel Sambuc
309ebfedea0SLionel Sambuc for (i = 0; i < len; i++)
310ebfedea0SLionel Sambuc p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE;
311ebfedea0SLionel Sambuc
312ebfedea0SLionel Sambuc offset = p->data.length + PAC_INFO_BUFFER_SIZE;
313ebfedea0SLionel Sambuc
314ebfedea0SLionel Sambuc p->pac->buffers[len].type = type;
315ebfedea0SLionel Sambuc p->pac->buffers[len].buffersize = data->length;
316ebfedea0SLionel Sambuc p->pac->buffers[len].offset_lo = offset;
317ebfedea0SLionel Sambuc p->pac->buffers[len].offset_hi = 0;
318ebfedea0SLionel Sambuc
319ebfedea0SLionel Sambuc old_end = p->data.length;
320ebfedea0SLionel Sambuc len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE;
321ebfedea0SLionel Sambuc if (len < p->data.length) {
322ebfedea0SLionel Sambuc krb5_set_error_message(context, EINVAL, "integer overrun");
323ebfedea0SLionel Sambuc return EINVAL;
324ebfedea0SLionel Sambuc }
325ebfedea0SLionel Sambuc
326ebfedea0SLionel Sambuc /* align to PAC_ALIGNMENT */
327ebfedea0SLionel Sambuc len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
328ebfedea0SLionel Sambuc
329ebfedea0SLionel Sambuc ret = krb5_data_realloc(&p->data, len);
330ebfedea0SLionel Sambuc if (ret) {
331ebfedea0SLionel Sambuc krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
332ebfedea0SLionel Sambuc return ret;
333ebfedea0SLionel Sambuc }
334ebfedea0SLionel Sambuc
335ebfedea0SLionel Sambuc /*
336ebfedea0SLionel Sambuc * make place for new PAC INFO BUFFER header
337ebfedea0SLionel Sambuc */
338ebfedea0SLionel Sambuc header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
339ebfedea0SLionel Sambuc memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE,
340ebfedea0SLionel Sambuc (unsigned char *)p->data.data + header_end ,
341ebfedea0SLionel Sambuc old_end - header_end);
342ebfedea0SLionel Sambuc memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE);
343ebfedea0SLionel Sambuc
344ebfedea0SLionel Sambuc /*
345ebfedea0SLionel Sambuc * copy in new data part
346ebfedea0SLionel Sambuc */
347ebfedea0SLionel Sambuc
348ebfedea0SLionel Sambuc memcpy((unsigned char *)p->data.data + offset,
349ebfedea0SLionel Sambuc data->data, data->length);
350ebfedea0SLionel Sambuc memset((unsigned char *)p->data.data + offset + data->length,
351ebfedea0SLionel Sambuc 0, p->data.length - offset - data->length);
352ebfedea0SLionel Sambuc
353ebfedea0SLionel Sambuc p->pac->numbuffers += 1;
354ebfedea0SLionel Sambuc
355ebfedea0SLionel Sambuc return 0;
356ebfedea0SLionel Sambuc }
357ebfedea0SLionel Sambuc
358ebfedea0SLionel Sambuc /**
359ebfedea0SLionel Sambuc * Get the PAC buffer of specific type from the pac.
360ebfedea0SLionel Sambuc *
361ebfedea0SLionel Sambuc * @param context Kerberos 5 context.
362ebfedea0SLionel Sambuc * @param p the pac structure returned by krb5_pac_parse().
363ebfedea0SLionel Sambuc * @param type type of buffer to get
364ebfedea0SLionel Sambuc * @param data return data, free with krb5_data_free().
365ebfedea0SLionel Sambuc *
366ebfedea0SLionel Sambuc * @return Returns 0 to indicate success. Otherwise an kerberos et
367ebfedea0SLionel Sambuc * error code is returned, see krb5_get_error_message().
368ebfedea0SLionel Sambuc *
369ebfedea0SLionel Sambuc * @ingroup krb5_pac
370ebfedea0SLionel Sambuc */
371ebfedea0SLionel Sambuc
372ebfedea0SLionel Sambuc KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_pac_get_buffer(krb5_context context,krb5_pac p,uint32_t type,krb5_data * data)373ebfedea0SLionel Sambuc krb5_pac_get_buffer(krb5_context context, krb5_pac p,
374ebfedea0SLionel Sambuc uint32_t type, krb5_data *data)
375ebfedea0SLionel Sambuc {
376ebfedea0SLionel Sambuc krb5_error_code ret;
377ebfedea0SLionel Sambuc uint32_t i;
378ebfedea0SLionel Sambuc
379ebfedea0SLionel Sambuc for (i = 0; i < p->pac->numbuffers; i++) {
380ebfedea0SLionel Sambuc const size_t len = p->pac->buffers[i].buffersize;
381ebfedea0SLionel Sambuc const size_t offset = p->pac->buffers[i].offset_lo;
382ebfedea0SLionel Sambuc
383ebfedea0SLionel Sambuc if (p->pac->buffers[i].type != type)
384ebfedea0SLionel Sambuc continue;
385ebfedea0SLionel Sambuc
386ebfedea0SLionel Sambuc ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len);
387ebfedea0SLionel Sambuc if (ret) {
388ebfedea0SLionel Sambuc krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
389ebfedea0SLionel Sambuc return ret;
390ebfedea0SLionel Sambuc }
391ebfedea0SLionel Sambuc return 0;
392ebfedea0SLionel Sambuc }
393ebfedea0SLionel Sambuc krb5_set_error_message(context, ENOENT, "No PAC buffer of type %lu was found",
394ebfedea0SLionel Sambuc (unsigned long)type);
395ebfedea0SLionel Sambuc return ENOENT;
396ebfedea0SLionel Sambuc }
397ebfedea0SLionel Sambuc
398ebfedea0SLionel Sambuc /*
399ebfedea0SLionel Sambuc *
400ebfedea0SLionel Sambuc */
401ebfedea0SLionel Sambuc
402ebfedea0SLionel Sambuc KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_pac_get_types(krb5_context context,krb5_pac p,size_t * len,uint32_t ** types)403ebfedea0SLionel Sambuc krb5_pac_get_types(krb5_context context,
404ebfedea0SLionel Sambuc krb5_pac p,
405ebfedea0SLionel Sambuc size_t *len,
406ebfedea0SLionel Sambuc uint32_t **types)
407ebfedea0SLionel Sambuc {
408ebfedea0SLionel Sambuc size_t i;
409ebfedea0SLionel Sambuc
410ebfedea0SLionel Sambuc *types = calloc(p->pac->numbuffers, sizeof(*types));
411ebfedea0SLionel Sambuc if (*types == NULL) {
412ebfedea0SLionel Sambuc *len = 0;
413ebfedea0SLionel Sambuc return krb5_enomem(context);
414ebfedea0SLionel Sambuc }
415ebfedea0SLionel Sambuc for (i = 0; i < p->pac->numbuffers; i++)
416ebfedea0SLionel Sambuc (*types)[i] = p->pac->buffers[i].type;
417ebfedea0SLionel Sambuc *len = p->pac->numbuffers;
418ebfedea0SLionel Sambuc
419ebfedea0SLionel Sambuc return 0;
420ebfedea0SLionel Sambuc }
421ebfedea0SLionel Sambuc
422ebfedea0SLionel Sambuc /*
423ebfedea0SLionel Sambuc *
424ebfedea0SLionel Sambuc */
425ebfedea0SLionel Sambuc
426ebfedea0SLionel Sambuc KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_pac_free(krb5_context context,krb5_pac pac)427ebfedea0SLionel Sambuc krb5_pac_free(krb5_context context, krb5_pac pac)
428ebfedea0SLionel Sambuc {
429ebfedea0SLionel Sambuc krb5_data_free(&pac->data);
430ebfedea0SLionel Sambuc free(pac->pac);
431ebfedea0SLionel Sambuc free(pac);
432ebfedea0SLionel Sambuc }
433ebfedea0SLionel Sambuc
434ebfedea0SLionel Sambuc /*
435ebfedea0SLionel Sambuc *
436ebfedea0SLionel Sambuc */
437ebfedea0SLionel Sambuc
438ebfedea0SLionel Sambuc static krb5_error_code
verify_checksum(krb5_context context,const struct PAC_INFO_BUFFER * sig,const krb5_data * data,void * ptr,size_t len,const krb5_keyblock * key)439ebfedea0SLionel Sambuc verify_checksum(krb5_context context,
440ebfedea0SLionel Sambuc const struct PAC_INFO_BUFFER *sig,
441ebfedea0SLionel Sambuc const krb5_data *data,
442ebfedea0SLionel Sambuc void *ptr, size_t len,
443ebfedea0SLionel Sambuc const krb5_keyblock *key)
444ebfedea0SLionel Sambuc {
445ebfedea0SLionel Sambuc krb5_storage *sp = NULL;
446ebfedea0SLionel Sambuc uint32_t type;
447ebfedea0SLionel Sambuc krb5_error_code ret;
448ebfedea0SLionel Sambuc Checksum cksum;
449ebfedea0SLionel Sambuc
450ebfedea0SLionel Sambuc memset(&cksum, 0, sizeof(cksum));
451ebfedea0SLionel Sambuc
452ebfedea0SLionel Sambuc sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo,
453ebfedea0SLionel Sambuc sig->buffersize);
454ebfedea0SLionel Sambuc if (sp == NULL)
455ebfedea0SLionel Sambuc return krb5_enomem(context);
456ebfedea0SLionel Sambuc
457ebfedea0SLionel Sambuc krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
458ebfedea0SLionel Sambuc
459ebfedea0SLionel Sambuc CHECK(ret, krb5_ret_uint32(sp, &type), out);
460ebfedea0SLionel Sambuc cksum.cksumtype = type;
461ebfedea0SLionel Sambuc cksum.checksum.length =
462ebfedea0SLionel Sambuc sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR);
463ebfedea0SLionel Sambuc cksum.checksum.data = malloc(cksum.checksum.length);
464ebfedea0SLionel Sambuc if (cksum.checksum.data == NULL) {
465ebfedea0SLionel Sambuc ret = krb5_enomem(context);
466ebfedea0SLionel Sambuc goto out;
467ebfedea0SLionel Sambuc }
468ebfedea0SLionel Sambuc ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length);
469*0a6a1f1dSLionel Sambuc if (ret != (int)cksum.checksum.length) {
470ebfedea0SLionel Sambuc ret = EINVAL;
471ebfedea0SLionel Sambuc krb5_set_error_message(context, ret, "PAC checksum missing checksum");
472ebfedea0SLionel Sambuc goto out;
473ebfedea0SLionel Sambuc }
474ebfedea0SLionel Sambuc
475ebfedea0SLionel Sambuc if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) {
476ebfedea0SLionel Sambuc ret = EINVAL;
477ebfedea0SLionel Sambuc krb5_set_error_message(context, ret, "Checksum type %d not keyed",
478ebfedea0SLionel Sambuc cksum.cksumtype);
479ebfedea0SLionel Sambuc goto out;
480ebfedea0SLionel Sambuc }
481ebfedea0SLionel Sambuc
482ebfedea0SLionel Sambuc /* If the checksum is HMAC-MD5, the checksum type is not tied to
483ebfedea0SLionel Sambuc * the key type, instead the HMAC-MD5 checksum is applied blindly
484ebfedea0SLionel Sambuc * on whatever key is used for this connection, avoiding issues
485ebfedea0SLionel Sambuc * with unkeyed checksums on des-cbc-md5 and des-cbc-crc. See
486ebfedea0SLionel Sambuc * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743
487ebfedea0SLionel Sambuc * for the same issue in MIT, and
488ebfedea0SLionel Sambuc * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx
489ebfedea0SLionel Sambuc * for Microsoft's explaination */
490ebfedea0SLionel Sambuc
491ebfedea0SLionel Sambuc if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5) {
492ebfedea0SLionel Sambuc Checksum local_checksum;
493ebfedea0SLionel Sambuc
494ebfedea0SLionel Sambuc memset(&local_checksum, 0, sizeof(local_checksum));
495ebfedea0SLionel Sambuc
496ebfedea0SLionel Sambuc ret = HMAC_MD5_any_checksum(context, key, ptr, len,
497ebfedea0SLionel Sambuc KRB5_KU_OTHER_CKSUM, &local_checksum);
498ebfedea0SLionel Sambuc
499ebfedea0SLionel Sambuc if (ret != 0 || krb5_data_ct_cmp(&local_checksum.checksum, &cksum.checksum) != 0) {
500ebfedea0SLionel Sambuc ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
501ebfedea0SLionel Sambuc krb5_set_error_message(context, ret,
502ebfedea0SLionel Sambuc N_("PAC integrity check failed for "
503ebfedea0SLionel Sambuc "hmac-md5 checksum", ""));
504ebfedea0SLionel Sambuc }
505ebfedea0SLionel Sambuc krb5_data_free(&local_checksum.checksum);
506ebfedea0SLionel Sambuc
507ebfedea0SLionel Sambuc } else {
508ebfedea0SLionel Sambuc krb5_crypto crypto = NULL;
509ebfedea0SLionel Sambuc
510ebfedea0SLionel Sambuc ret = krb5_crypto_init(context, key, 0, &crypto);
511ebfedea0SLionel Sambuc if (ret)
512ebfedea0SLionel Sambuc goto out;
513ebfedea0SLionel Sambuc
514ebfedea0SLionel Sambuc ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM,
515ebfedea0SLionel Sambuc ptr, len, &cksum);
516ebfedea0SLionel Sambuc krb5_crypto_destroy(context, crypto);
517ebfedea0SLionel Sambuc }
518ebfedea0SLionel Sambuc free(cksum.checksum.data);
519ebfedea0SLionel Sambuc krb5_storage_free(sp);
520ebfedea0SLionel Sambuc
521ebfedea0SLionel Sambuc return ret;
522ebfedea0SLionel Sambuc
523ebfedea0SLionel Sambuc out:
524ebfedea0SLionel Sambuc if (cksum.checksum.data)
525ebfedea0SLionel Sambuc free(cksum.checksum.data);
526ebfedea0SLionel Sambuc if (sp)
527ebfedea0SLionel Sambuc krb5_storage_free(sp);
528ebfedea0SLionel Sambuc return ret;
529ebfedea0SLionel Sambuc }
530ebfedea0SLionel Sambuc
531ebfedea0SLionel Sambuc static krb5_error_code
create_checksum(krb5_context context,const krb5_keyblock * key,uint32_t cksumtype,void * data,size_t datalen,void * sig,size_t siglen)532ebfedea0SLionel Sambuc create_checksum(krb5_context context,
533ebfedea0SLionel Sambuc const krb5_keyblock *key,
534ebfedea0SLionel Sambuc uint32_t cksumtype,
535ebfedea0SLionel Sambuc void *data, size_t datalen,
536ebfedea0SLionel Sambuc void *sig, size_t siglen)
537ebfedea0SLionel Sambuc {
538ebfedea0SLionel Sambuc krb5_crypto crypto = NULL;
539ebfedea0SLionel Sambuc krb5_error_code ret;
540ebfedea0SLionel Sambuc Checksum cksum;
541ebfedea0SLionel Sambuc
542ebfedea0SLionel Sambuc /* If the checksum is HMAC-MD5, the checksum type is not tied to
543ebfedea0SLionel Sambuc * the key type, instead the HMAC-MD5 checksum is applied blindly
544ebfedea0SLionel Sambuc * on whatever key is used for this connection, avoiding issues
545ebfedea0SLionel Sambuc * with unkeyed checksums on des-cbc-md5 and des-cbc-crc. See
546ebfedea0SLionel Sambuc * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743
547ebfedea0SLionel Sambuc * for the same issue in MIT, and
548ebfedea0SLionel Sambuc * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx
549ebfedea0SLionel Sambuc * for Microsoft's explaination */
550ebfedea0SLionel Sambuc
551*0a6a1f1dSLionel Sambuc if (cksumtype == (uint32_t)CKSUMTYPE_HMAC_MD5) {
552ebfedea0SLionel Sambuc ret = HMAC_MD5_any_checksum(context, key, data, datalen,
553ebfedea0SLionel Sambuc KRB5_KU_OTHER_CKSUM, &cksum);
554ebfedea0SLionel Sambuc } else {
555ebfedea0SLionel Sambuc ret = krb5_crypto_init(context, key, 0, &crypto);
556ebfedea0SLionel Sambuc if (ret)
557ebfedea0SLionel Sambuc return ret;
558ebfedea0SLionel Sambuc
559ebfedea0SLionel Sambuc ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0,
560ebfedea0SLionel Sambuc data, datalen, &cksum);
561ebfedea0SLionel Sambuc krb5_crypto_destroy(context, crypto);
562ebfedea0SLionel Sambuc if (ret)
563ebfedea0SLionel Sambuc return ret;
564ebfedea0SLionel Sambuc }
565ebfedea0SLionel Sambuc if (cksum.checksum.length != siglen) {
566ebfedea0SLionel Sambuc krb5_set_error_message(context, EINVAL, "pac checksum wrong length");
567ebfedea0SLionel Sambuc free_Checksum(&cksum);
568ebfedea0SLionel Sambuc return EINVAL;
569ebfedea0SLionel Sambuc }
570ebfedea0SLionel Sambuc
571ebfedea0SLionel Sambuc memcpy(sig, cksum.checksum.data, siglen);
572ebfedea0SLionel Sambuc free_Checksum(&cksum);
573ebfedea0SLionel Sambuc
574ebfedea0SLionel Sambuc return 0;
575ebfedea0SLionel Sambuc }
576ebfedea0SLionel Sambuc
577ebfedea0SLionel Sambuc
578ebfedea0SLionel Sambuc /*
579ebfedea0SLionel Sambuc *
580ebfedea0SLionel Sambuc */
581ebfedea0SLionel Sambuc
582ebfedea0SLionel Sambuc #define NTTIME_EPOCH 0x019DB1DED53E8000LL
583ebfedea0SLionel Sambuc
584ebfedea0SLionel Sambuc static uint64_t
unix2nttime(time_t unix_time)585ebfedea0SLionel Sambuc unix2nttime(time_t unix_time)
586ebfedea0SLionel Sambuc {
587ebfedea0SLionel Sambuc long long wt;
588ebfedea0SLionel Sambuc wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
589ebfedea0SLionel Sambuc return wt;
590ebfedea0SLionel Sambuc }
591ebfedea0SLionel Sambuc
592ebfedea0SLionel Sambuc static krb5_error_code
verify_logonname(krb5_context context,const struct PAC_INFO_BUFFER * logon_name,const krb5_data * data,time_t authtime,krb5_const_principal principal)593ebfedea0SLionel Sambuc verify_logonname(krb5_context context,
594ebfedea0SLionel Sambuc const struct PAC_INFO_BUFFER *logon_name,
595ebfedea0SLionel Sambuc const krb5_data *data,
596ebfedea0SLionel Sambuc time_t authtime,
597ebfedea0SLionel Sambuc krb5_const_principal principal)
598ebfedea0SLionel Sambuc {
599ebfedea0SLionel Sambuc krb5_error_code ret;
600ebfedea0SLionel Sambuc krb5_principal p2;
601ebfedea0SLionel Sambuc uint32_t time1, time2;
602ebfedea0SLionel Sambuc krb5_storage *sp;
603ebfedea0SLionel Sambuc uint16_t len;
604ebfedea0SLionel Sambuc char *s;
605ebfedea0SLionel Sambuc
606ebfedea0SLionel Sambuc sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo,
607ebfedea0SLionel Sambuc logon_name->buffersize);
608ebfedea0SLionel Sambuc if (sp == NULL)
609ebfedea0SLionel Sambuc return krb5_enomem(context);
610ebfedea0SLionel Sambuc
611ebfedea0SLionel Sambuc krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
612ebfedea0SLionel Sambuc
613ebfedea0SLionel Sambuc CHECK(ret, krb5_ret_uint32(sp, &time1), out);
614ebfedea0SLionel Sambuc CHECK(ret, krb5_ret_uint32(sp, &time2), out);
615ebfedea0SLionel Sambuc
616ebfedea0SLionel Sambuc {
617ebfedea0SLionel Sambuc uint64_t t1, t2;
618ebfedea0SLionel Sambuc t1 = unix2nttime(authtime);
619ebfedea0SLionel Sambuc t2 = ((uint64_t)time2 << 32) | time1;
620ebfedea0SLionel Sambuc if (t1 != t2) {
621ebfedea0SLionel Sambuc krb5_storage_free(sp);
622ebfedea0SLionel Sambuc krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch");
623ebfedea0SLionel Sambuc return EINVAL;
624ebfedea0SLionel Sambuc }
625ebfedea0SLionel Sambuc }
626ebfedea0SLionel Sambuc CHECK(ret, krb5_ret_uint16(sp, &len), out);
627ebfedea0SLionel Sambuc if (len == 0) {
628ebfedea0SLionel Sambuc krb5_storage_free(sp);
629ebfedea0SLionel Sambuc krb5_set_error_message(context, EINVAL, "PAC logon name length missing");
630ebfedea0SLionel Sambuc return EINVAL;
631ebfedea0SLionel Sambuc }
632ebfedea0SLionel Sambuc
633ebfedea0SLionel Sambuc s = malloc(len);
634ebfedea0SLionel Sambuc if (s == NULL) {
635ebfedea0SLionel Sambuc krb5_storage_free(sp);
636ebfedea0SLionel Sambuc return krb5_enomem(context);
637ebfedea0SLionel Sambuc }
638ebfedea0SLionel Sambuc ret = krb5_storage_read(sp, s, len);
639ebfedea0SLionel Sambuc if (ret != len) {
640ebfedea0SLionel Sambuc krb5_storage_free(sp);
641ebfedea0SLionel Sambuc krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name");
642ebfedea0SLionel Sambuc return EINVAL;
643ebfedea0SLionel Sambuc }
644ebfedea0SLionel Sambuc krb5_storage_free(sp);
645ebfedea0SLionel Sambuc {
646ebfedea0SLionel Sambuc size_t ucs2len = len / 2;
647ebfedea0SLionel Sambuc uint16_t *ucs2;
648ebfedea0SLionel Sambuc size_t u8len;
649ebfedea0SLionel Sambuc unsigned int flags = WIND_RW_LE;
650ebfedea0SLionel Sambuc
651ebfedea0SLionel Sambuc ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
652ebfedea0SLionel Sambuc if (ucs2 == NULL)
653ebfedea0SLionel Sambuc return krb5_enomem(context);
654ebfedea0SLionel Sambuc
655ebfedea0SLionel Sambuc ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len);
656ebfedea0SLionel Sambuc free(s);
657ebfedea0SLionel Sambuc if (ret) {
658ebfedea0SLionel Sambuc free(ucs2);
659ebfedea0SLionel Sambuc krb5_set_error_message(context, ret, "Failed to convert string to UCS-2");
660ebfedea0SLionel Sambuc return ret;
661ebfedea0SLionel Sambuc }
662ebfedea0SLionel Sambuc ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len);
663ebfedea0SLionel Sambuc if (ret) {
664ebfedea0SLionel Sambuc free(ucs2);
665ebfedea0SLionel Sambuc krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string");
666ebfedea0SLionel Sambuc return ret;
667ebfedea0SLionel Sambuc }
668ebfedea0SLionel Sambuc u8len += 1; /* Add space for NUL */
669ebfedea0SLionel Sambuc s = malloc(u8len);
670ebfedea0SLionel Sambuc if (s == NULL) {
671ebfedea0SLionel Sambuc free(ucs2);
672ebfedea0SLionel Sambuc return krb5_enomem(context);
673ebfedea0SLionel Sambuc }
674ebfedea0SLionel Sambuc ret = wind_ucs2utf8(ucs2, ucs2len, s, &u8len);
675ebfedea0SLionel Sambuc free(ucs2);
676ebfedea0SLionel Sambuc if (ret) {
677ebfedea0SLionel Sambuc free(s);
678ebfedea0SLionel Sambuc krb5_set_error_message(context, ret, "Failed to convert to UTF-8");
679ebfedea0SLionel Sambuc return ret;
680ebfedea0SLionel Sambuc }
681ebfedea0SLionel Sambuc }
682ebfedea0SLionel Sambuc ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2);
683ebfedea0SLionel Sambuc free(s);
684ebfedea0SLionel Sambuc if (ret)
685ebfedea0SLionel Sambuc return ret;
686ebfedea0SLionel Sambuc
687ebfedea0SLionel Sambuc if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) {
688ebfedea0SLionel Sambuc ret = EINVAL;
689ebfedea0SLionel Sambuc krb5_set_error_message(context, ret, "PAC logon name mismatch");
690ebfedea0SLionel Sambuc }
691ebfedea0SLionel Sambuc krb5_free_principal(context, p2);
692ebfedea0SLionel Sambuc return ret;
693ebfedea0SLionel Sambuc out:
694ebfedea0SLionel Sambuc return ret;
695ebfedea0SLionel Sambuc }
696ebfedea0SLionel Sambuc
697ebfedea0SLionel Sambuc /*
698ebfedea0SLionel Sambuc *
699ebfedea0SLionel Sambuc */
700ebfedea0SLionel Sambuc
701ebfedea0SLionel Sambuc static krb5_error_code
build_logon_name(krb5_context context,time_t authtime,krb5_const_principal principal,krb5_data * logon)702ebfedea0SLionel Sambuc build_logon_name(krb5_context context,
703ebfedea0SLionel Sambuc time_t authtime,
704ebfedea0SLionel Sambuc krb5_const_principal principal,
705ebfedea0SLionel Sambuc krb5_data *logon)
706ebfedea0SLionel Sambuc {
707ebfedea0SLionel Sambuc krb5_error_code ret;
708ebfedea0SLionel Sambuc krb5_storage *sp;
709ebfedea0SLionel Sambuc uint64_t t;
710ebfedea0SLionel Sambuc char *s, *s2;
711*0a6a1f1dSLionel Sambuc size_t s2_len;
712ebfedea0SLionel Sambuc
713ebfedea0SLionel Sambuc t = unix2nttime(authtime);
714ebfedea0SLionel Sambuc
715ebfedea0SLionel Sambuc krb5_data_zero(logon);
716ebfedea0SLionel Sambuc
717ebfedea0SLionel Sambuc sp = krb5_storage_emem();
718ebfedea0SLionel Sambuc if (sp == NULL)
719ebfedea0SLionel Sambuc return krb5_enomem(context);
720ebfedea0SLionel Sambuc
721ebfedea0SLionel Sambuc krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
722ebfedea0SLionel Sambuc
723ebfedea0SLionel Sambuc CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out);
724ebfedea0SLionel Sambuc CHECK(ret, krb5_store_uint32(sp, t >> 32), out);
725ebfedea0SLionel Sambuc
726ebfedea0SLionel Sambuc ret = krb5_unparse_name_flags(context, principal,
727ebfedea0SLionel Sambuc KRB5_PRINCIPAL_UNPARSE_NO_REALM, &s);
728ebfedea0SLionel Sambuc if (ret)
729ebfedea0SLionel Sambuc goto out;
730ebfedea0SLionel Sambuc
731*0a6a1f1dSLionel Sambuc {
732*0a6a1f1dSLionel Sambuc size_t ucs2_len;
733*0a6a1f1dSLionel Sambuc uint16_t *ucs2;
734*0a6a1f1dSLionel Sambuc unsigned int flags;
735ebfedea0SLionel Sambuc
736*0a6a1f1dSLionel Sambuc ret = wind_utf8ucs2_length(s, &ucs2_len);
737*0a6a1f1dSLionel Sambuc if (ret) {
738*0a6a1f1dSLionel Sambuc free(s);
739*0a6a1f1dSLionel Sambuc krb5_set_error_message(context, ret, "Failed to count length of UTF-8 string");
740*0a6a1f1dSLionel Sambuc return ret;
741*0a6a1f1dSLionel Sambuc }
742ebfedea0SLionel Sambuc
743*0a6a1f1dSLionel Sambuc ucs2 = malloc(sizeof(ucs2[0]) * ucs2_len);
744*0a6a1f1dSLionel Sambuc if (ucs2 == NULL) {
745*0a6a1f1dSLionel Sambuc free(s);
746*0a6a1f1dSLionel Sambuc return krb5_enomem(context);
747*0a6a1f1dSLionel Sambuc }
748*0a6a1f1dSLionel Sambuc
749*0a6a1f1dSLionel Sambuc ret = wind_utf8ucs2(s, ucs2, &ucs2_len);
750*0a6a1f1dSLionel Sambuc free(s);
751*0a6a1f1dSLionel Sambuc if (ret) {
752*0a6a1f1dSLionel Sambuc free(ucs2);
753*0a6a1f1dSLionel Sambuc krb5_set_error_message(context, ret, "Failed to convert string to UCS-2");
754*0a6a1f1dSLionel Sambuc return ret;
755*0a6a1f1dSLionel Sambuc }
756*0a6a1f1dSLionel Sambuc
757*0a6a1f1dSLionel Sambuc s2_len = (ucs2_len + 1) * 2;
758*0a6a1f1dSLionel Sambuc s2 = malloc(s2_len);
759ebfedea0SLionel Sambuc if (s2 == NULL) {
760*0a6a1f1dSLionel Sambuc free(ucs2);
761*0a6a1f1dSLionel Sambuc return krb5_enomem(context);
762ebfedea0SLionel Sambuc }
763ebfedea0SLionel Sambuc
764*0a6a1f1dSLionel Sambuc flags = WIND_RW_LE;
765*0a6a1f1dSLionel Sambuc ret = wind_ucs2write(ucs2, ucs2_len,
766*0a6a1f1dSLionel Sambuc &flags, s2, &s2_len);
767*0a6a1f1dSLionel Sambuc free(ucs2);
768*0a6a1f1dSLionel Sambuc if (ret) {
769ebfedea0SLionel Sambuc free(s2);
770*0a6a1f1dSLionel Sambuc krb5_set_error_message(context, ret, "Failed to write to UCS-2 buffer");
771*0a6a1f1dSLionel Sambuc return ret;
772*0a6a1f1dSLionel Sambuc }
773*0a6a1f1dSLionel Sambuc
774*0a6a1f1dSLionel Sambuc /*
775*0a6a1f1dSLionel Sambuc * we do not want zero termination
776*0a6a1f1dSLionel Sambuc */
777*0a6a1f1dSLionel Sambuc s2_len = ucs2_len * 2;
778*0a6a1f1dSLionel Sambuc }
779*0a6a1f1dSLionel Sambuc
780*0a6a1f1dSLionel Sambuc CHECK(ret, krb5_store_uint16(sp, s2_len), out);
781*0a6a1f1dSLionel Sambuc
782*0a6a1f1dSLionel Sambuc ret = krb5_storage_write(sp, s2, s2_len);
783*0a6a1f1dSLionel Sambuc free(s2);
784*0a6a1f1dSLionel Sambuc if (ret != (int)s2_len) {
785ebfedea0SLionel Sambuc ret = krb5_enomem(context);
786ebfedea0SLionel Sambuc goto out;
787ebfedea0SLionel Sambuc }
788ebfedea0SLionel Sambuc ret = krb5_storage_to_data(sp, logon);
789ebfedea0SLionel Sambuc if (ret)
790ebfedea0SLionel Sambuc goto out;
791ebfedea0SLionel Sambuc krb5_storage_free(sp);
792ebfedea0SLionel Sambuc
793ebfedea0SLionel Sambuc return 0;
794ebfedea0SLionel Sambuc out:
795ebfedea0SLionel Sambuc krb5_storage_free(sp);
796ebfedea0SLionel Sambuc return ret;
797ebfedea0SLionel Sambuc }
798ebfedea0SLionel Sambuc
799ebfedea0SLionel Sambuc
800ebfedea0SLionel Sambuc /**
801ebfedea0SLionel Sambuc * Verify the PAC.
802ebfedea0SLionel Sambuc *
803ebfedea0SLionel Sambuc * @param context Kerberos 5 context.
804ebfedea0SLionel Sambuc * @param pac the pac structure returned by krb5_pac_parse().
805ebfedea0SLionel Sambuc * @param authtime The time of the ticket the PAC belongs to.
806ebfedea0SLionel Sambuc * @param principal the principal to verify.
807ebfedea0SLionel Sambuc * @param server The service key, most always be given.
808ebfedea0SLionel Sambuc * @param privsvr The KDC key, may be given.
809ebfedea0SLionel Sambuc
810ebfedea0SLionel Sambuc * @return Returns 0 to indicate success. Otherwise an kerberos et
811ebfedea0SLionel Sambuc * error code is returned, see krb5_get_error_message().
812ebfedea0SLionel Sambuc *
813ebfedea0SLionel Sambuc * @ingroup krb5_pac
814ebfedea0SLionel Sambuc */
815ebfedea0SLionel Sambuc
816ebfedea0SLionel Sambuc KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_pac_verify(krb5_context context,const krb5_pac pac,time_t authtime,krb5_const_principal principal,const krb5_keyblock * server,const krb5_keyblock * privsvr)817ebfedea0SLionel Sambuc krb5_pac_verify(krb5_context context,
818ebfedea0SLionel Sambuc const krb5_pac pac,
819ebfedea0SLionel Sambuc time_t authtime,
820ebfedea0SLionel Sambuc krb5_const_principal principal,
821ebfedea0SLionel Sambuc const krb5_keyblock *server,
822ebfedea0SLionel Sambuc const krb5_keyblock *privsvr)
823ebfedea0SLionel Sambuc {
824ebfedea0SLionel Sambuc krb5_error_code ret;
825ebfedea0SLionel Sambuc
826ebfedea0SLionel Sambuc if (pac->server_checksum == NULL) {
827ebfedea0SLionel Sambuc krb5_set_error_message(context, EINVAL, "PAC missing server checksum");
828ebfedea0SLionel Sambuc return EINVAL;
829ebfedea0SLionel Sambuc }
830ebfedea0SLionel Sambuc if (pac->privsvr_checksum == NULL) {
831ebfedea0SLionel Sambuc krb5_set_error_message(context, EINVAL, "PAC missing kdc checksum");
832ebfedea0SLionel Sambuc return EINVAL;
833ebfedea0SLionel Sambuc }
834ebfedea0SLionel Sambuc if (pac->logon_name == NULL) {
835ebfedea0SLionel Sambuc krb5_set_error_message(context, EINVAL, "PAC missing logon name");
836ebfedea0SLionel Sambuc return EINVAL;
837ebfedea0SLionel Sambuc }
838ebfedea0SLionel Sambuc
839ebfedea0SLionel Sambuc ret = verify_logonname(context,
840ebfedea0SLionel Sambuc pac->logon_name,
841ebfedea0SLionel Sambuc &pac->data,
842ebfedea0SLionel Sambuc authtime,
843ebfedea0SLionel Sambuc principal);
844ebfedea0SLionel Sambuc if (ret)
845ebfedea0SLionel Sambuc return ret;
846ebfedea0SLionel Sambuc
847ebfedea0SLionel Sambuc /*
848ebfedea0SLionel Sambuc * in the service case, clean out data option of the privsvr and
849ebfedea0SLionel Sambuc * server checksum before checking the checksum.
850ebfedea0SLionel Sambuc */
851ebfedea0SLionel Sambuc {
852ebfedea0SLionel Sambuc krb5_data *copy;
853ebfedea0SLionel Sambuc
854ebfedea0SLionel Sambuc ret = krb5_copy_data(context, &pac->data, ©);
855ebfedea0SLionel Sambuc if (ret)
856ebfedea0SLionel Sambuc return ret;
857ebfedea0SLionel Sambuc
858ebfedea0SLionel Sambuc if (pac->server_checksum->buffersize < 4)
859ebfedea0SLionel Sambuc return EINVAL;
860ebfedea0SLionel Sambuc if (pac->privsvr_checksum->buffersize < 4)
861ebfedea0SLionel Sambuc return EINVAL;
862ebfedea0SLionel Sambuc
863ebfedea0SLionel Sambuc memset((char *)copy->data + pac->server_checksum->offset_lo + 4,
864ebfedea0SLionel Sambuc 0,
865ebfedea0SLionel Sambuc pac->server_checksum->buffersize - 4);
866ebfedea0SLionel Sambuc
867ebfedea0SLionel Sambuc memset((char *)copy->data + pac->privsvr_checksum->offset_lo + 4,
868ebfedea0SLionel Sambuc 0,
869ebfedea0SLionel Sambuc pac->privsvr_checksum->buffersize - 4);
870ebfedea0SLionel Sambuc
871ebfedea0SLionel Sambuc ret = verify_checksum(context,
872ebfedea0SLionel Sambuc pac->server_checksum,
873ebfedea0SLionel Sambuc &pac->data,
874ebfedea0SLionel Sambuc copy->data,
875ebfedea0SLionel Sambuc copy->length,
876ebfedea0SLionel Sambuc server);
877ebfedea0SLionel Sambuc krb5_free_data(context, copy);
878ebfedea0SLionel Sambuc if (ret)
879ebfedea0SLionel Sambuc return ret;
880ebfedea0SLionel Sambuc }
881ebfedea0SLionel Sambuc if (privsvr) {
882ebfedea0SLionel Sambuc /* The priv checksum covers the server checksum */
883ebfedea0SLionel Sambuc ret = verify_checksum(context,
884ebfedea0SLionel Sambuc pac->privsvr_checksum,
885ebfedea0SLionel Sambuc &pac->data,
886ebfedea0SLionel Sambuc (char *)pac->data.data
887ebfedea0SLionel Sambuc + pac->server_checksum->offset_lo + 4,
888ebfedea0SLionel Sambuc pac->server_checksum->buffersize - 4,
889ebfedea0SLionel Sambuc privsvr);
890ebfedea0SLionel Sambuc if (ret)
891ebfedea0SLionel Sambuc return ret;
892ebfedea0SLionel Sambuc }
893ebfedea0SLionel Sambuc
894ebfedea0SLionel Sambuc return 0;
895ebfedea0SLionel Sambuc }
896ebfedea0SLionel Sambuc
897ebfedea0SLionel Sambuc /*
898ebfedea0SLionel Sambuc *
899ebfedea0SLionel Sambuc */
900ebfedea0SLionel Sambuc
901ebfedea0SLionel Sambuc static krb5_error_code
fill_zeros(krb5_context context,krb5_storage * sp,size_t len)902ebfedea0SLionel Sambuc fill_zeros(krb5_context context, krb5_storage *sp, size_t len)
903ebfedea0SLionel Sambuc {
904ebfedea0SLionel Sambuc ssize_t sret;
905ebfedea0SLionel Sambuc size_t l;
906ebfedea0SLionel Sambuc
907ebfedea0SLionel Sambuc while (len) {
908ebfedea0SLionel Sambuc l = len;
909ebfedea0SLionel Sambuc if (l > sizeof(zeros))
910ebfedea0SLionel Sambuc l = sizeof(zeros);
911ebfedea0SLionel Sambuc sret = krb5_storage_write(sp, zeros, l);
912ebfedea0SLionel Sambuc if (sret <= 0)
913ebfedea0SLionel Sambuc return krb5_enomem(context);
914ebfedea0SLionel Sambuc
915ebfedea0SLionel Sambuc len -= sret;
916ebfedea0SLionel Sambuc }
917ebfedea0SLionel Sambuc return 0;
918ebfedea0SLionel Sambuc }
919ebfedea0SLionel Sambuc
920ebfedea0SLionel Sambuc static krb5_error_code
pac_checksum(krb5_context context,const krb5_keyblock * key,uint32_t * cksumtype,size_t * cksumsize)921ebfedea0SLionel Sambuc pac_checksum(krb5_context context,
922ebfedea0SLionel Sambuc const krb5_keyblock *key,
923ebfedea0SLionel Sambuc uint32_t *cksumtype,
924ebfedea0SLionel Sambuc size_t *cksumsize)
925ebfedea0SLionel Sambuc {
926ebfedea0SLionel Sambuc krb5_cksumtype cktype;
927ebfedea0SLionel Sambuc krb5_error_code ret;
928ebfedea0SLionel Sambuc krb5_crypto crypto = NULL;
929ebfedea0SLionel Sambuc
930ebfedea0SLionel Sambuc ret = krb5_crypto_init(context, key, 0, &crypto);
931ebfedea0SLionel Sambuc if (ret)
932ebfedea0SLionel Sambuc return ret;
933ebfedea0SLionel Sambuc
934ebfedea0SLionel Sambuc ret = krb5_crypto_get_checksum_type(context, crypto, &cktype);
935ebfedea0SLionel Sambuc krb5_crypto_destroy(context, crypto);
936ebfedea0SLionel Sambuc if (ret)
937ebfedea0SLionel Sambuc return ret;
938ebfedea0SLionel Sambuc
939ebfedea0SLionel Sambuc if (krb5_checksum_is_keyed(context, cktype) == FALSE) {
940ebfedea0SLionel Sambuc *cksumtype = CKSUMTYPE_HMAC_MD5;
941ebfedea0SLionel Sambuc *cksumsize = 16;
942ebfedea0SLionel Sambuc }
943ebfedea0SLionel Sambuc
944ebfedea0SLionel Sambuc ret = krb5_checksumsize(context, cktype, cksumsize);
945ebfedea0SLionel Sambuc if (ret)
946ebfedea0SLionel Sambuc return ret;
947ebfedea0SLionel Sambuc
948ebfedea0SLionel Sambuc *cksumtype = (uint32_t)cktype;
949ebfedea0SLionel Sambuc
950ebfedea0SLionel Sambuc return 0;
951ebfedea0SLionel Sambuc }
952ebfedea0SLionel Sambuc
953ebfedea0SLionel Sambuc krb5_error_code
_krb5_pac_sign(krb5_context context,krb5_pac p,time_t authtime,krb5_principal principal,const krb5_keyblock * server_key,const krb5_keyblock * priv_key,krb5_data * data)954ebfedea0SLionel Sambuc _krb5_pac_sign(krb5_context context,
955ebfedea0SLionel Sambuc krb5_pac p,
956ebfedea0SLionel Sambuc time_t authtime,
957ebfedea0SLionel Sambuc krb5_principal principal,
958ebfedea0SLionel Sambuc const krb5_keyblock *server_key,
959ebfedea0SLionel Sambuc const krb5_keyblock *priv_key,
960ebfedea0SLionel Sambuc krb5_data *data)
961ebfedea0SLionel Sambuc {
962ebfedea0SLionel Sambuc krb5_error_code ret;
963ebfedea0SLionel Sambuc krb5_storage *sp = NULL, *spdata = NULL;
964ebfedea0SLionel Sambuc uint32_t end;
965ebfedea0SLionel Sambuc size_t server_size, priv_size;
966ebfedea0SLionel Sambuc uint32_t server_offset = 0, priv_offset = 0;
967ebfedea0SLionel Sambuc uint32_t server_cksumtype = 0, priv_cksumtype = 0;
968*0a6a1f1dSLionel Sambuc int num = 0;
969*0a6a1f1dSLionel Sambuc size_t i;
970ebfedea0SLionel Sambuc krb5_data logon, d;
971ebfedea0SLionel Sambuc
972ebfedea0SLionel Sambuc krb5_data_zero(&logon);
973ebfedea0SLionel Sambuc
974ebfedea0SLionel Sambuc if (p->logon_name == NULL)
975ebfedea0SLionel Sambuc num++;
976ebfedea0SLionel Sambuc if (p->server_checksum == NULL)
977ebfedea0SLionel Sambuc num++;
978ebfedea0SLionel Sambuc if (p->privsvr_checksum == NULL)
979ebfedea0SLionel Sambuc num++;
980ebfedea0SLionel Sambuc
981ebfedea0SLionel Sambuc if (num) {
982ebfedea0SLionel Sambuc void *ptr;
983ebfedea0SLionel Sambuc
984ebfedea0SLionel Sambuc ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1)));
985ebfedea0SLionel Sambuc if (ptr == NULL)
986ebfedea0SLionel Sambuc return krb5_enomem(context);
987ebfedea0SLionel Sambuc
988ebfedea0SLionel Sambuc p->pac = ptr;
989ebfedea0SLionel Sambuc
990ebfedea0SLionel Sambuc if (p->logon_name == NULL) {
991ebfedea0SLionel Sambuc p->logon_name = &p->pac->buffers[p->pac->numbuffers++];
992ebfedea0SLionel Sambuc memset(p->logon_name, 0, sizeof(*p->logon_name));
993ebfedea0SLionel Sambuc p->logon_name->type = PAC_LOGON_NAME;
994ebfedea0SLionel Sambuc }
995ebfedea0SLionel Sambuc if (p->server_checksum == NULL) {
996ebfedea0SLionel Sambuc p->server_checksum = &p->pac->buffers[p->pac->numbuffers++];
997ebfedea0SLionel Sambuc memset(p->server_checksum, 0, sizeof(*p->server_checksum));
998ebfedea0SLionel Sambuc p->server_checksum->type = PAC_SERVER_CHECKSUM;
999ebfedea0SLionel Sambuc }
1000ebfedea0SLionel Sambuc if (p->privsvr_checksum == NULL) {
1001ebfedea0SLionel Sambuc p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++];
1002ebfedea0SLionel Sambuc memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum));
1003ebfedea0SLionel Sambuc p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM;
1004ebfedea0SLionel Sambuc }
1005ebfedea0SLionel Sambuc }
1006ebfedea0SLionel Sambuc
1007ebfedea0SLionel Sambuc /* Calculate LOGON NAME */
1008ebfedea0SLionel Sambuc ret = build_logon_name(context, authtime, principal, &logon);
1009ebfedea0SLionel Sambuc if (ret)
1010ebfedea0SLionel Sambuc goto out;
1011ebfedea0SLionel Sambuc
1012ebfedea0SLionel Sambuc /* Set lengths for checksum */
1013ebfedea0SLionel Sambuc ret = pac_checksum(context, server_key, &server_cksumtype, &server_size);
1014ebfedea0SLionel Sambuc if (ret)
1015ebfedea0SLionel Sambuc goto out;
1016ebfedea0SLionel Sambuc ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size);
1017ebfedea0SLionel Sambuc if (ret)
1018ebfedea0SLionel Sambuc goto out;
1019ebfedea0SLionel Sambuc
1020ebfedea0SLionel Sambuc /* Encode PAC */
1021ebfedea0SLionel Sambuc sp = krb5_storage_emem();
1022ebfedea0SLionel Sambuc if (sp == NULL)
1023ebfedea0SLionel Sambuc return krb5_enomem(context);
1024ebfedea0SLionel Sambuc
1025ebfedea0SLionel Sambuc krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
1026ebfedea0SLionel Sambuc
1027ebfedea0SLionel Sambuc spdata = krb5_storage_emem();
1028ebfedea0SLionel Sambuc if (spdata == NULL) {
1029ebfedea0SLionel Sambuc krb5_storage_free(sp);
1030ebfedea0SLionel Sambuc return krb5_enomem(context);
1031ebfedea0SLionel Sambuc }
1032ebfedea0SLionel Sambuc krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE);
1033ebfedea0SLionel Sambuc
1034ebfedea0SLionel Sambuc CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out);
1035ebfedea0SLionel Sambuc CHECK(ret, krb5_store_uint32(sp, p->pac->version), out);
1036ebfedea0SLionel Sambuc
1037ebfedea0SLionel Sambuc end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
1038ebfedea0SLionel Sambuc
1039ebfedea0SLionel Sambuc for (i = 0; i < p->pac->numbuffers; i++) {
1040ebfedea0SLionel Sambuc uint32_t len;
1041ebfedea0SLionel Sambuc size_t sret;
1042ebfedea0SLionel Sambuc void *ptr = NULL;
1043ebfedea0SLionel Sambuc
1044ebfedea0SLionel Sambuc /* store data */
1045ebfedea0SLionel Sambuc
1046ebfedea0SLionel Sambuc if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
1047ebfedea0SLionel Sambuc len = server_size + 4;
1048ebfedea0SLionel Sambuc server_offset = end + 4;
1049ebfedea0SLionel Sambuc CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out);
1050ebfedea0SLionel Sambuc CHECK(ret, fill_zeros(context, spdata, server_size), out);
1051ebfedea0SLionel Sambuc } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
1052ebfedea0SLionel Sambuc len = priv_size + 4;
1053ebfedea0SLionel Sambuc priv_offset = end + 4;
1054ebfedea0SLionel Sambuc CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
1055ebfedea0SLionel Sambuc CHECK(ret, fill_zeros(context, spdata, priv_size), out);
1056ebfedea0SLionel Sambuc } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
1057ebfedea0SLionel Sambuc len = krb5_storage_write(spdata, logon.data, logon.length);
1058ebfedea0SLionel Sambuc if (logon.length != len) {
1059ebfedea0SLionel Sambuc ret = EINVAL;
1060ebfedea0SLionel Sambuc goto out;
1061ebfedea0SLionel Sambuc }
1062ebfedea0SLionel Sambuc } else {
1063ebfedea0SLionel Sambuc len = p->pac->buffers[i].buffersize;
1064ebfedea0SLionel Sambuc ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo;
1065ebfedea0SLionel Sambuc
1066ebfedea0SLionel Sambuc sret = krb5_storage_write(spdata, ptr, len);
1067ebfedea0SLionel Sambuc if (sret != len) {
1068ebfedea0SLionel Sambuc ret = krb5_enomem(context);
1069ebfedea0SLionel Sambuc goto out;
1070ebfedea0SLionel Sambuc }
1071ebfedea0SLionel Sambuc /* XXX if not aligned, fill_zeros */
1072ebfedea0SLionel Sambuc }
1073ebfedea0SLionel Sambuc
1074ebfedea0SLionel Sambuc /* write header */
1075ebfedea0SLionel Sambuc CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out);
1076ebfedea0SLionel Sambuc CHECK(ret, krb5_store_uint32(sp, len), out);
1077ebfedea0SLionel Sambuc CHECK(ret, krb5_store_uint32(sp, end), out);
1078ebfedea0SLionel Sambuc CHECK(ret, krb5_store_uint32(sp, 0), out);
1079ebfedea0SLionel Sambuc
1080ebfedea0SLionel Sambuc /* advance data endpointer and align */
1081ebfedea0SLionel Sambuc {
1082ebfedea0SLionel Sambuc int32_t e;
1083ebfedea0SLionel Sambuc
1084ebfedea0SLionel Sambuc end += len;
1085ebfedea0SLionel Sambuc e = ((end + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
1086*0a6a1f1dSLionel Sambuc if ((int32_t)end != e) {
1087ebfedea0SLionel Sambuc CHECK(ret, fill_zeros(context, spdata, e - end), out);
1088ebfedea0SLionel Sambuc }
1089ebfedea0SLionel Sambuc end = e;
1090ebfedea0SLionel Sambuc }
1091ebfedea0SLionel Sambuc
1092ebfedea0SLionel Sambuc }
1093ebfedea0SLionel Sambuc
1094ebfedea0SLionel Sambuc /* assert (server_offset != 0 && priv_offset != 0); */
1095ebfedea0SLionel Sambuc
1096ebfedea0SLionel Sambuc /* export PAC */
1097ebfedea0SLionel Sambuc ret = krb5_storage_to_data(spdata, &d);
1098ebfedea0SLionel Sambuc if (ret) {
1099ebfedea0SLionel Sambuc krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1100ebfedea0SLionel Sambuc goto out;
1101ebfedea0SLionel Sambuc }
1102ebfedea0SLionel Sambuc ret = krb5_storage_write(sp, d.data, d.length);
1103*0a6a1f1dSLionel Sambuc if (ret != (int)d.length) {
1104ebfedea0SLionel Sambuc krb5_data_free(&d);
1105ebfedea0SLionel Sambuc ret = krb5_enomem(context);
1106ebfedea0SLionel Sambuc goto out;
1107ebfedea0SLionel Sambuc }
1108ebfedea0SLionel Sambuc krb5_data_free(&d);
1109ebfedea0SLionel Sambuc
1110ebfedea0SLionel Sambuc ret = krb5_storage_to_data(sp, &d);
1111ebfedea0SLionel Sambuc if (ret) {
1112ebfedea0SLionel Sambuc ret = krb5_enomem(context);
1113ebfedea0SLionel Sambuc goto out;
1114ebfedea0SLionel Sambuc }
1115ebfedea0SLionel Sambuc
1116ebfedea0SLionel Sambuc /* sign */
1117ebfedea0SLionel Sambuc ret = create_checksum(context, server_key, server_cksumtype,
1118ebfedea0SLionel Sambuc d.data, d.length,
1119ebfedea0SLionel Sambuc (char *)d.data + server_offset, server_size);
1120ebfedea0SLionel Sambuc if (ret) {
1121ebfedea0SLionel Sambuc krb5_data_free(&d);
1122ebfedea0SLionel Sambuc goto out;
1123ebfedea0SLionel Sambuc }
1124ebfedea0SLionel Sambuc ret = create_checksum(context, priv_key, priv_cksumtype,
1125ebfedea0SLionel Sambuc (char *)d.data + server_offset, server_size,
1126ebfedea0SLionel Sambuc (char *)d.data + priv_offset, priv_size);
1127ebfedea0SLionel Sambuc if (ret) {
1128ebfedea0SLionel Sambuc krb5_data_free(&d);
1129ebfedea0SLionel Sambuc goto out;
1130ebfedea0SLionel Sambuc }
1131ebfedea0SLionel Sambuc
1132ebfedea0SLionel Sambuc /* done */
1133ebfedea0SLionel Sambuc *data = d;
1134ebfedea0SLionel Sambuc
1135ebfedea0SLionel Sambuc krb5_data_free(&logon);
1136ebfedea0SLionel Sambuc krb5_storage_free(sp);
1137ebfedea0SLionel Sambuc krb5_storage_free(spdata);
1138ebfedea0SLionel Sambuc
1139ebfedea0SLionel Sambuc return 0;
1140ebfedea0SLionel Sambuc out:
1141ebfedea0SLionel Sambuc krb5_data_free(&logon);
1142ebfedea0SLionel Sambuc if (sp)
1143ebfedea0SLionel Sambuc krb5_storage_free(sp);
1144ebfedea0SLionel Sambuc if (spdata)
1145ebfedea0SLionel Sambuc krb5_storage_free(spdata);
1146ebfedea0SLionel Sambuc return ret;
1147ebfedea0SLionel Sambuc }
1148