1/*
2 * Copyright (c) 1997 - 2005 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "heim.h"
35#include <string.h>
36#include <errno.h>
37
38mit_krb5_error_code KRB5_CALLCONV
39krb5_kt_start_seq_get(mit_krb5_context context, mit_krb5_keytab keytab,
40		      mit_krb5_kt_cursor *cursor)
41{
42    krb5_error_code ret;
43
44    LOG_ENTRY();
45
46    *cursor = calloc(1, sizeof(krb5_kt_cursor));
47
48    ret = heim_krb5_kt_start_seq_get(HC(context), (krb5_keytab)keytab, (krb5_kt_cursor *)*cursor);
49    if (ret) {
50	free(*cursor);
51	*cursor = NULL;
52    }
53    return ret;
54}
55
56mit_krb5_error_code KRB5_CALLCONV
57krb5_kt_next_entry(mit_krb5_context context, mit_krb5_keytab keytab,
58		   mit_krb5_keytab_entry *entry, mit_krb5_kt_cursor *cursor)
59{
60    krb5_error_code ret;
61    krb5_keytab_entry e;
62
63    LOG_ENTRY();
64
65    ret = heim_krb5_kt_next_entry(HC(context), (krb5_keytab)keytab,
66				  &e, (krb5_kt_cursor *)*cursor);
67    if (ret)
68	return ret;
69
70    entry->magic = 0;
71    entry->principal = mshim_hprinc2mprinc(HC(context), e.principal);
72    entry->timestamp = e.timestamp;
73    entry->vno = e.vno;
74    mshim_hkeyblock2mkeyblock(&e.keyblock, &entry->key);
75
76    heim_krb5_kt_free_entry(HC(context), &e);
77
78    return 0;
79}
80
81mit_krb5_error_code KRB5_CALLCONV
82krb5_free_keytab_entry_contents(mit_krb5_context context,
83				mit_krb5_keytab_entry *entry)
84{
85    int eric = entry->vno;
86    LOG_ENTRY();
87
88    krb5_free_principal(context, entry->principal);
89    memset(entry->key.contents, 0, entry->key.length);
90    free(entry->key.contents);
91    memset(entry, 0, sizeof(*entry));
92    entry->vno = eric;
93
94    return 0;
95}
96
97
98mit_krb5_error_code KRB5_CALLCONV
99krb5_kt_end_seq_get(mit_krb5_context context, mit_krb5_keytab keytab,
100		    mit_krb5_kt_cursor *cursor)
101{
102    krb5_error_code ret;
103
104    LOG_ENTRY();
105
106    ret = heim_krb5_kt_end_seq_get(HC(context), (krb5_keytab)keytab, (krb5_kt_cursor *)*cursor);
107    free(*cursor);
108    *cursor = NULL;
109
110    return ret;
111}
112
113static int
114krb5_kt_compare(mit_krb5_context context,
115		mit_krb5_keytab_entry *entry,
116		mit_krb5_const_principal principal,
117		mit_krb5_kvno vno,
118		mit_krb5_enctype enctype)
119{
120    LOG_ENTRY();
121
122    if(principal != NULL &&
123       !krb5_principal_compare(context, entry->principal, principal))
124	return 0;
125    if(vno && vno != entry->vno)
126	return 0;
127    if(enctype && enctype != entry->key.enctype)
128	return 0;
129    return 1;
130}
131
132static mit_krb5_error_code
133krb5_kt_free_entry(mit_krb5_context context,
134		   mit_krb5_keytab_entry *entry)
135{
136    LOG_ENTRY();
137
138    krb5_free_principal (context, entry->principal);
139    krb5_free_keyblock_contents (context, &entry->key);
140    memset(entry, 0, sizeof(*entry));
141    return 0;
142}
143
144static mit_krb5_error_code
145krb5_kt_copy_entry_contents(mit_krb5_context context,
146			    const mit_krb5_keytab_entry *in,
147			    mit_krb5_keytab_entry *out)
148{
149    krb5_error_code ret;
150
151    LOG_ENTRY();
152
153    memset(out, 0, sizeof(*out));
154    out->vno = in->vno;
155
156    ret = krb5_copy_principal (context, in->principal, &out->principal);
157    if (ret)
158	goto fail;
159    ret = krb5_copy_keyblock_contents (context, &in->key, &out->key);
160    if (ret)
161	goto fail;
162    out->timestamp = in->timestamp;
163    return 0;
164fail:
165    krb5_kt_free_entry (context, out);
166    return ret;
167}
168
169
170mit_krb5_error_code KRB5_CALLCONV
171krb5_kt_get_entry(mit_krb5_context context,
172		  mit_krb5_keytab id,
173		  mit_krb5_const_principal principal,
174		  mit_krb5_kvno kvno,
175		  mit_krb5_enctype enctype,
176		  mit_krb5_keytab_entry *entry)
177{
178    mit_krb5_keytab_entry tmp;
179    mit_krb5_error_code ret;
180    mit_krb5_kt_cursor cursor;
181
182    LOG_ENTRY();
183
184    memset(entry, 0, sizeof(*entry));
185
186    ret = krb5_kt_start_seq_get (context, id, &cursor);
187    if (ret)
188	return KRB5_KT_NOTFOUND;
189
190    entry->vno = 0;
191    while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
192	if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
193	    /* the file keytab might only store the lower 8 bits of
194	       the kvno, so only compare those bits */
195	    if (kvno == tmp.vno
196		|| (tmp.vno < 256 && kvno % 256 == tmp.vno)) {
197		krb5_kt_copy_entry_contents (context, &tmp, entry);
198		krb5_kt_free_entry (context, &tmp);
199		krb5_kt_end_seq_get(context, id, &cursor);
200		return 0;
201	    } else if (kvno == 0 && tmp.vno > entry->vno) {
202		if (entry->vno)
203		    krb5_kt_free_entry (context, entry);
204		krb5_kt_copy_entry_contents (context, &tmp, entry);
205	    }
206	}
207	krb5_kt_free_entry(context, &tmp);
208    }
209    krb5_kt_end_seq_get (context, id, &cursor);
210    if (entry->vno == 0)
211	return KRB5_KT_NOTFOUND;
212    return 0;
213}
214
215mit_krb5_error_code KRB5_CALLCONV
216krb5_kt_get_name(mit_krb5_context context,
217		 mit_krb5_keytab keytab,
218		 char *name,
219		 unsigned int namelen)
220{
221    return heim_krb5_kt_get_name(HC(context), (krb5_keytab)keytab, name, namelen);
222}
223
224mit_krb5_error_code KRB5_CALLCONV
225krb5_kt_read_service_key(mit_krb5_context context,
226			 mit_krb5_pointer keyprocarg,
227			 mit_krb5_principal principal,
228			 mit_krb5_kvno vno,
229			 mit_krb5_enctype enctype,
230			 mit_krb5_keyblock **key)
231{
232    mit_krb5_keytab keytab;
233    mit_krb5_keytab_entry entry;
234    mit_krb5_error_code ret;
235
236    LOG_ENTRY();
237
238    if (keyprocarg)
239	ret = krb5_kt_resolve (context, keyprocarg, &keytab);
240    else
241	ret = krb5_kt_default (context, &keytab);
242
243    if (ret)
244	return ret;
245
246    ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry);
247    krb5_kt_close (context, keytab);
248    if (ret)
249	return ret;
250    ret = krb5_copy_keyblock (context, &entry.key, key);
251    krb5_kt_free_entry(context, &entry);
252    return ret;
253}
254
255mit_krb5_error_code KRB5_LIB_FUNCTION
256krb5_kt_remove_entry(mit_krb5_context context,
257		     mit_krb5_keytab id,
258		     mit_krb5_keytab_entry *entry)
259{
260    struct comb_principal *p = (struct comb_principal *)entry->principal;
261    krb5_keytab_entry e;
262
263    LOG_ENTRY();
264
265    memset(&e, 0, sizeof(e));
266
267    e.principal = p->heim;
268    e.vno = entry->vno;
269    e.timestamp = entry->timestamp;
270
271    return heim_krb5_kt_remove_entry(HC(context), (krb5_keytab)id, &e);
272}
273
274mit_krb5_error_code KRB5_CALLCONV
275krb5_kt_add_entry(mit_krb5_context context,
276		  mit_krb5_keytab id,
277		  mit_krb5_keytab_entry *entry)
278{
279    struct comb_principal *p = (struct comb_principal *)entry->principal;
280    krb5_keytab_entry e;
281
282    LOG_ENTRY();
283
284    memset(&e, 0, sizeof(e));
285
286    e.principal = p->heim;
287    e.vno = entry->vno;
288    e.timestamp = entry->timestamp;
289    e.keyblock.keytype = entry->key.enctype;
290    e.keyblock.keyvalue.data = entry->key.contents;
291    e.keyblock.keyvalue.length = entry->key.length;
292
293    return heim_krb5_kt_add_entry(HC(context), (krb5_keytab)id, &e);
294}
295
296
297
298const char * KRB5_CALLCONV
299krb5_kt_get_type(mit_krb5_context context, mit_krb5_keytab id)
300{
301    krb5_error_code ret;
302    static char name[80];
303
304    LOG_ENTRY();
305
306    ret = heim_krb5_kt_get_type (HC(context),
307				 (krb5_keytab)id,
308				 name,
309				 sizeof(name));
310    if (ret)
311	name[0] = '\0';
312    return name;
313}
314