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