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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 | |