1/*
2 * Copyright (c) 1997-2002 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 "hdb_locl.h"
35
36RCSID("$Id: common.c 20236 2007-02-16 23:52:29Z lha $");
37
38int
39hdb_principal2key(krb5_context context, krb5_const_principal p, krb5_data *key)
40{
41    Principal new;
42    size_t len;
43    int ret;
44
45    ret = copy_Principal(p, &new);
46    if(ret)
47	return ret;
48    new.name.name_type = 0;
49
50    ASN1_MALLOC_ENCODE(Principal, key->data, key->length, &new, &len, ret);
51    if (ret == 0 && key->length != len)
52	krb5_abortx(context, "internal asn.1 encoder error");
53    free_Principal(&new);
54    return ret;
55}
56
57int
58hdb_key2principal(krb5_context context, krb5_data *key, krb5_principal p)
59{
60    return decode_Principal(key->data, key->length, p, NULL);
61}
62
63int
64hdb_entry2value(krb5_context context, const hdb_entry *ent, krb5_data *value)
65{
66    size_t len;
67    int ret;
68
69    ASN1_MALLOC_ENCODE(hdb_entry, value->data, value->length, ent, &len, ret);
70    if (ret == 0 && value->length != len)
71	krb5_abortx(context, "internal asn.1 encoder error");
72    return ret;
73}
74
75int
76hdb_value2entry(krb5_context context, krb5_data *value, hdb_entry *ent)
77{
78    return decode_hdb_entry(value->data, value->length, ent, NULL);
79}
80
81int
82hdb_entry_alias2value(krb5_context context,
83		      const hdb_entry_alias *alias,
84		      krb5_data *value)
85{
86    size_t len;
87    int ret;
88
89    ASN1_MALLOC_ENCODE(hdb_entry_alias, value->data, value->length,
90		       alias, &len, ret);
91    if (ret == 0 && value->length != len)
92	krb5_abortx(context, "internal asn.1 encoder error");
93    return ret;
94}
95
96int
97hdb_value2entry_alias(krb5_context context, krb5_data *value,
98		      hdb_entry_alias *ent)
99{
100    return decode_hdb_entry_alias(value->data, value->length, ent, NULL);
101}
102
103krb5_error_code
104_hdb_fetch(krb5_context context, HDB *db, krb5_const_principal principal,
105	   unsigned flags, hdb_entry_ex *entry)
106{
107    krb5_data key, value;
108    int code;
109
110    hdb_principal2key(context, principal, &key);
111    code = db->hdb__get(context, db, key, &value);
112    krb5_data_free(&key);
113    if(code)
114	return code;
115    code = hdb_value2entry(context, &value, &entry->entry);
116    if (code == ASN1_BAD_ID && (flags & HDB_F_CANON) == 0) {
117	krb5_data_free(&value);
118	return HDB_ERR_NOENTRY;
119    } else if (code == ASN1_BAD_ID) {
120	hdb_entry_alias alias;
121
122	code = hdb_value2entry_alias(context, &value, &alias);
123	if (code) {
124	    krb5_data_free(&value);
125	    return code;
126	}
127	hdb_principal2key(context, alias.principal, &key);
128	krb5_data_free(&value);
129	free_hdb_entry_alias(&alias);
130
131	code = db->hdb__get(context, db, key, &value);
132	krb5_data_free(&key);
133	if (code)
134	    return code;
135	code = hdb_value2entry(context, &value, &entry->entry);
136	if (code) {
137	    krb5_data_free(&value);
138	    return code;
139	}
140    }
141    krb5_data_free(&value);
142    if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
143	code = hdb_unseal_keys (context, db, &entry->entry);
144	if (code)
145	    hdb_free_entry(context, entry);
146    }
147    return code;
148}
149
150static krb5_error_code
151hdb_remove_aliases(krb5_context context, HDB *db, krb5_data *key)
152{
153    const HDB_Ext_Aliases *aliases;
154    krb5_error_code code;
155    hdb_entry oldentry;
156    krb5_data value;
157    int i;
158
159    code = db->hdb__get(context, db, *key, &value);
160    if (code == HDB_ERR_NOENTRY)
161	return 0;
162    else if (code)
163	return code;
164
165    code = hdb_value2entry(context, &value, &oldentry);
166    krb5_data_free(&value);
167    if (code)
168	return code;
169
170    code = hdb_entry_get_aliases(&oldentry, &aliases);
171    if (code || aliases == NULL) {
172	free_hdb_entry(&oldentry);
173	return code;
174    }
175    for (i = 0; i < aliases->aliases.len; i++) {
176	krb5_data akey;
177
178	hdb_principal2key(context, &aliases->aliases.val[i], &akey);
179	code = db->hdb__del(context, db, akey);
180	krb5_data_free(&akey);
181	if (code) {
182	    free_hdb_entry(&oldentry);
183	    return code;
184	}
185    }
186    free_hdb_entry(&oldentry);
187    return 0;
188}
189
190static krb5_error_code
191hdb_add_aliases(krb5_context context, HDB *db,
192		unsigned flags, hdb_entry_ex *entry)
193{
194    const HDB_Ext_Aliases *aliases;
195    krb5_error_code code;
196    krb5_data key, value;
197    int i;
198
199    code = hdb_entry_get_aliases(&entry->entry, &aliases);
200    if (code || aliases == NULL)
201	return code;
202
203    for (i = 0; i < aliases->aliases.len; i++) {
204	hdb_entry_alias entryalias;
205	entryalias.principal = entry->entry.principal;
206
207	hdb_principal2key(context, &aliases->aliases.val[i], &key);
208	code = hdb_entry_alias2value(context, &entryalias, &value);
209	if (code) {
210	    krb5_data_free(&key);
211	    return code;
212	}
213	code = db->hdb__put(context, db, flags, key, value);
214	krb5_data_free(&key);
215	krb5_data_free(&value);
216	if (code)
217	    return code;
218    }
219    return 0;
220}
221
222krb5_error_code
223_hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
224{
225    krb5_data key, value;
226    int code;
227
228    if(entry->entry.generation == NULL) {
229	struct timeval t;
230	entry->entry.generation = malloc(sizeof(*entry->entry.generation));
231	if(entry->entry.generation == NULL) {
232	    krb5_set_error_string(context, "malloc: out of memory");
233	    return ENOMEM;
234	}
235	gettimeofday(&t, NULL);
236	entry->entry.generation->time = t.tv_sec;
237	entry->entry.generation->usec = t.tv_usec;
238	entry->entry.generation->gen = 0;
239    } else
240	entry->entry.generation->gen++;
241    hdb_principal2key(context, entry->entry.principal, &key);
242    code = hdb_seal_keys(context, db, &entry->entry);
243    if (code) {
244	krb5_data_free(&key);
245	return code;
246    }
247
248    /* remove aliases */
249    code = hdb_remove_aliases(context, db, &key);
250    if (code) {
251	krb5_data_free(&key);
252	return code;
253    }
254    hdb_entry2value(context, &entry->entry, &value);
255    code = db->hdb__put(context, db, flags & HDB_F_REPLACE, key, value);
256    krb5_data_free(&value);
257    krb5_data_free(&key);
258    if (code)
259	return code;
260
261    code = hdb_add_aliases(context, db, flags, entry);
262
263    return code;
264}
265
266krb5_error_code
267_hdb_remove(krb5_context context, HDB *db, krb5_const_principal principal)
268{
269    krb5_data key;
270    int code;
271
272    hdb_principal2key(context, principal, &key);
273
274    code = hdb_remove_aliases(context, db, &key);
275    if (code) {
276	krb5_data_free(&key);
277	return code;
278    }
279    code = db->hdb__del(context, db, key);
280    krb5_data_free(&key);
281    return code;
282}
283
284