1 /*	$NetBSD: common.c,v 1.2 2017/01/28 21:31:48 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-2002 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "hdb_locl.h"
37 
38 int
hdb_principal2key(krb5_context context,krb5_const_principal p,krb5_data * key)39 hdb_principal2key(krb5_context context, krb5_const_principal p, krb5_data *key)
40 {
41     Principal new;
42     size_t len = 0;
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 
57 int
hdb_key2principal(krb5_context context,krb5_data * key,krb5_principal p)58 hdb_key2principal(krb5_context context, krb5_data *key, krb5_principal p)
59 {
60     return decode_Principal(key->data, key->length, p, NULL);
61 }
62 
63 int
hdb_entry2value(krb5_context context,const hdb_entry * ent,krb5_data * value)64 hdb_entry2value(krb5_context context, const hdb_entry *ent, krb5_data *value)
65 {
66     size_t len = 0;
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 
75 int
hdb_value2entry(krb5_context context,krb5_data * value,hdb_entry * ent)76 hdb_value2entry(krb5_context context, krb5_data *value, hdb_entry *ent)
77 {
78     return decode_hdb_entry(value->data, value->length, ent, NULL);
79 }
80 
81 int
hdb_entry_alias2value(krb5_context context,const hdb_entry_alias * alias,krb5_data * value)82 hdb_entry_alias2value(krb5_context context,
83 		      const hdb_entry_alias *alias,
84 		      krb5_data *value)
85 {
86     size_t len = 0;
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 
96 int
hdb_value2entry_alias(krb5_context context,krb5_data * value,hdb_entry_alias * ent)97 hdb_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 
103 krb5_error_code
_hdb_fetch_kvno(krb5_context context,HDB * db,krb5_const_principal principal,unsigned flags,krb5_kvno kvno,hdb_entry_ex * entry)104 _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
105 		unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry)
106 {
107     krb5_principal enterprise_principal = NULL;
108     krb5_data key, value;
109     krb5_error_code ret;
110 
111     if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
112 	if (principal->name.name_string.len != 1) {
113 	    ret = KRB5_PARSE_MALFORMED;
114 	    krb5_set_error_message(context, ret, "malformed principal: "
115 				   "enterprise name with %d name components",
116 				   principal->name.name_string.len);
117 	    return ret;
118 	}
119 	ret = krb5_parse_name(context, principal->name.name_string.val[0],
120 			      &enterprise_principal);
121 	if (ret)
122 	    return ret;
123 	principal = enterprise_principal;
124     }
125 
126     hdb_principal2key(context, principal, &key);
127     if (enterprise_principal)
128 	krb5_free_principal(context, enterprise_principal);
129     ret = db->hdb__get(context, db, key, &value);
130     krb5_data_free(&key);
131     if(ret)
132 	return ret;
133     ret = hdb_value2entry(context, &value, &entry->entry);
134     if (ret == ASN1_BAD_ID && (flags & HDB_F_CANON) == 0) {
135 	krb5_data_free(&value);
136 	return HDB_ERR_NOENTRY;
137     } else if (ret == ASN1_BAD_ID) {
138 	hdb_entry_alias alias;
139 
140 	ret = hdb_value2entry_alias(context, &value, &alias);
141 	if (ret) {
142 	    krb5_data_free(&value);
143 	    return ret;
144 	}
145 	hdb_principal2key(context, alias.principal, &key);
146 	krb5_data_free(&value);
147 	free_hdb_entry_alias(&alias);
148 
149 	ret = db->hdb__get(context, db, key, &value);
150 	krb5_data_free(&key);
151 	if (ret)
152 	    return ret;
153 	ret = hdb_value2entry(context, &value, &entry->entry);
154 	if (ret) {
155 	    krb5_data_free(&value);
156 	    return ret;
157 	}
158     }
159     krb5_data_free(&value);
160     if ((flags & HDB_F_DECRYPT) && (flags & HDB_F_ALL_KVNOS)) {
161 	/* Decrypt the current keys */
162 	ret = hdb_unseal_keys(context, db, &entry->entry);
163 	if (ret) {
164 	    hdb_free_entry(context, entry);
165 	    return ret;
166 	}
167 	/* Decrypt the key history too */
168 	ret = hdb_unseal_keys_kvno(context, db, 0, flags, &entry->entry);
169 	if (ret) {
170 	    hdb_free_entry(context, entry);
171 	    return ret;
172 	}
173     } else if ((flags & HDB_F_DECRYPT)) {
174 	if ((flags & HDB_F_KVNO_SPECIFIED) == 0 || kvno == entry->entry.kvno) {
175 	    /* Decrypt the current keys */
176 	    ret = hdb_unseal_keys(context, db, &entry->entry);
177 	    if (ret) {
178 		hdb_free_entry(context, entry);
179 		return ret;
180 	    }
181 	} else {
182 	    if ((flags & HDB_F_ALL_KVNOS))
183 		kvno = 0;
184 	    /*
185 	     * Find and decrypt the keys from the history that we want,
186 	     * and swap them with the current keys
187 	     */
188 	    ret = hdb_unseal_keys_kvno(context, db, kvno, flags, &entry->entry);
189 	    if (ret) {
190 		hdb_free_entry(context, entry);
191 		return ret;
192 	    }
193 	}
194     }
195 
196     return 0;
197 }
198 
199 static krb5_error_code
hdb_remove_aliases(krb5_context context,HDB * db,krb5_data * key)200 hdb_remove_aliases(krb5_context context, HDB *db, krb5_data *key)
201 {
202     const HDB_Ext_Aliases *aliases;
203     krb5_error_code code;
204     hdb_entry oldentry;
205     krb5_data value;
206     size_t i;
207 
208     code = db->hdb__get(context, db, *key, &value);
209     if (code == HDB_ERR_NOENTRY)
210 	return 0;
211     else if (code)
212 	return code;
213 
214     code = hdb_value2entry(context, &value, &oldentry);
215     krb5_data_free(&value);
216     if (code)
217 	return code;
218 
219     code = hdb_entry_get_aliases(&oldentry, &aliases);
220     if (code || aliases == NULL) {
221 	free_hdb_entry(&oldentry);
222 	return code;
223     }
224     for (i = 0; i < aliases->aliases.len; i++) {
225 	krb5_data akey;
226 
227 	code = hdb_principal2key(context, &aliases->aliases.val[i], &akey);
228         if (code == 0) {
229             code = db->hdb__del(context, db, akey);
230             krb5_data_free(&akey);
231         }
232 	if (code) {
233 	    free_hdb_entry(&oldentry);
234 	    return code;
235 	}
236     }
237     free_hdb_entry(&oldentry);
238     return 0;
239 }
240 
241 static krb5_error_code
hdb_add_aliases(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)242 hdb_add_aliases(krb5_context context, HDB *db,
243 		unsigned flags, hdb_entry_ex *entry)
244 {
245     const HDB_Ext_Aliases *aliases;
246     krb5_error_code code;
247     krb5_data key, value;
248     size_t i;
249 
250     code = hdb_entry_get_aliases(&entry->entry, &aliases);
251     if (code || aliases == NULL)
252 	return code;
253 
254     for (i = 0; i < aliases->aliases.len; i++) {
255 	hdb_entry_alias entryalias;
256 	entryalias.principal = entry->entry.principal;
257 
258 	code = hdb_entry_alias2value(context, &entryalias, &value);
259 	if (code)
260 	    return code;
261 
262 	code = hdb_principal2key(context, &aliases->aliases.val[i], &key);
263         if (code == 0) {
264             code = db->hdb__put(context, db, flags, key, value);
265             krb5_data_free(&key);
266         }
267 	krb5_data_free(&value);
268 	if (code)
269 	    return code;
270     }
271     return 0;
272 }
273 
274 static krb5_error_code
hdb_check_aliases(krb5_context context,HDB * db,hdb_entry_ex * entry)275 hdb_check_aliases(krb5_context context, HDB *db, hdb_entry_ex *entry)
276 {
277     const HDB_Ext_Aliases *aliases;
278     int code;
279     size_t i;
280 
281     /* check if new aliases already is used */
282 
283     code = hdb_entry_get_aliases(&entry->entry, &aliases);
284     if (code)
285 	return code;
286 
287     for (i = 0; aliases && i < aliases->aliases.len; i++) {
288 	hdb_entry_alias alias;
289 	krb5_data akey, value;
290 
291 	code = hdb_principal2key(context, &aliases->aliases.val[i], &akey);
292         if (code == 0) {
293             code = db->hdb__get(context, db, akey, &value);
294             krb5_data_free(&akey);
295         }
296 	if (code == HDB_ERR_NOENTRY)
297 	    continue;
298 	else if (code)
299 	    return code;
300 
301 	code = hdb_value2entry_alias(context, &value, &alias);
302 	krb5_data_free(&value);
303 
304 	if (code == ASN1_BAD_ID)
305 	    return HDB_ERR_EXISTS;
306 	else if (code)
307 	    return code;
308 
309 	code = krb5_principal_compare(context, alias.principal,
310 				      entry->entry.principal);
311 	free_hdb_entry_alias(&alias);
312 	if (code == 0)
313 	    return HDB_ERR_EXISTS;
314     }
315     return 0;
316 }
317 
318 krb5_error_code
_hdb_store(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)319 _hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
320 {
321     krb5_data key, value;
322     int code;
323 
324     if (entry->entry.flags.do_not_store)
325 	return HDB_ERR_MISUSE;
326     /* check if new aliases already is used */
327     code = hdb_check_aliases(context, db, entry);
328     if (code)
329 	return code;
330 
331     if ((flags & HDB_F_PRECHECK) && (flags & HDB_F_REPLACE))
332         return 0;
333 
334     if ((flags & HDB_F_PRECHECK)) {
335         code = hdb_principal2key(context, entry->entry.principal, &key);
336         if (code)
337             return code;
338         code = db->hdb__get(context, db, key, &value);
339         krb5_data_free(&key);
340         if (code == 0)
341             krb5_data_free(&value);
342         if (code == HDB_ERR_NOENTRY)
343             return 0;
344         return code ? code : HDB_ERR_EXISTS;
345     }
346 
347     if(entry->entry.generation == NULL) {
348 	struct timeval t;
349 	entry->entry.generation = malloc(sizeof(*entry->entry.generation));
350 	if(entry->entry.generation == NULL) {
351 	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
352 	    return ENOMEM;
353 	}
354 	gettimeofday(&t, NULL);
355 	entry->entry.generation->time = t.tv_sec;
356 	entry->entry.generation->usec = t.tv_usec;
357 	entry->entry.generation->gen = 0;
358     } else
359 	entry->entry.generation->gen++;
360 
361     code = hdb_seal_keys(context, db, &entry->entry);
362     if (code)
363 	return code;
364 
365     hdb_principal2key(context, entry->entry.principal, &key);
366 
367     /* remove aliases */
368     code = hdb_remove_aliases(context, db, &key);
369     if (code) {
370 	krb5_data_free(&key);
371 	return code;
372     }
373     hdb_entry2value(context, &entry->entry, &value);
374     code = db->hdb__put(context, db, flags & HDB_F_REPLACE, key, value);
375     krb5_data_free(&value);
376     krb5_data_free(&key);
377     if (code)
378 	return code;
379 
380     code = hdb_add_aliases(context, db, flags, entry);
381 
382     return code;
383 }
384 
385 krb5_error_code
_hdb_remove(krb5_context context,HDB * db,unsigned flags,krb5_const_principal principal)386 _hdb_remove(krb5_context context, HDB *db,
387             unsigned flags, krb5_const_principal principal)
388 {
389     krb5_data key, value;
390     int code;
391 
392     hdb_principal2key(context, principal, &key);
393 
394     if ((flags & HDB_F_PRECHECK)) {
395         /*
396          * We don't check that we can delete the aliases because we
397          * assume that the DB is consistent.  If we did check for alias
398          * consistency we'd also have to provide a way to fsck the DB,
399          * otherwise admins would have no way to recover -- papering
400          * over this here is less work, but we really ought to provide
401          * an HDB fsck.
402          */
403         code = db->hdb__get(context, db, key, &value);
404         krb5_data_free(&key);
405         if (code == 0) {
406             krb5_data_free(&value);
407             return 0;
408         }
409         return code;
410     }
411 
412     code = hdb_remove_aliases(context, db, &key);
413     if (code) {
414 	krb5_data_free(&key);
415 	return code;
416     }
417     code = db->hdb__del(context, db, key);
418     krb5_data_free(&key);
419     return code;
420 }
421 
422