Windows: update default timestamping service
[heimdal.git] / lib / hdb / common.c
blob741956beafa5b0b3584d0d794cb08dcc637851fb
1 /*
2 * Copyright (c) 1997-2002 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "krb5_locl.h"
35 #include "hdb_locl.h"
37 int
38 hdb_principal2key(krb5_context context, krb5_const_principal p, krb5_data *key)
40 Principal new;
41 size_t len = 0;
42 int ret;
44 ret = copy_Principal(p, &new);
45 if(ret)
46 return ret;
47 new.name.name_type = 0;
49 ASN1_MALLOC_ENCODE(Principal, key->data, key->length, &new, &len, ret);
50 if (ret == 0 && key->length != len)
51 krb5_abortx(context, "internal asn.1 encoder error");
52 free_Principal(&new);
53 return ret;
56 int
57 hdb_key2principal(krb5_context context, krb5_data *key, krb5_principal p)
59 return decode_Principal(key->data, key->length, p, NULL);
62 int
63 hdb_entry2value(krb5_context context, const hdb_entry *ent, krb5_data *value)
65 size_t len = 0;
66 int ret;
68 ASN1_MALLOC_ENCODE(hdb_entry, value->data, value->length, ent, &len, ret);
69 if (ret == 0 && value->length != len)
70 krb5_abortx(context, "internal asn.1 encoder error");
71 return ret;
74 int
75 hdb_value2entry(krb5_context context, krb5_data *value, hdb_entry *ent)
77 return decode_hdb_entry(value->data, value->length, ent, NULL);
80 int
81 hdb_entry_alias2value(krb5_context context,
82 const hdb_entry_alias *alias,
83 krb5_data *value)
85 size_t len = 0;
86 int ret;
88 ASN1_MALLOC_ENCODE(hdb_entry_alias, value->data, value->length,
89 alias, &len, ret);
90 if (ret == 0 && value->length != len)
91 krb5_abortx(context, "internal asn.1 encoder error");
92 return ret;
95 int
96 hdb_value2entry_alias(krb5_context context, krb5_data *value,
97 hdb_entry_alias *ent)
99 return decode_hdb_entry_alias(value->data, value->length, ent, NULL);
103 * Some old databases may not have stored the salt with each key, which will
104 * break clients when aliases or canonicalization are used. Generate a
105 * default salt based on the real principal name in the entry to handle
106 * this case.
108 static krb5_error_code
109 add_default_salts(krb5_context context, HDB *db, hdb_entry *entry)
111 krb5_error_code ret;
112 size_t i;
113 krb5_salt pwsalt;
115 ret = krb5_get_pw_salt(context, entry->principal, &pwsalt);
116 if (ret)
117 return ret;
119 for (i = 0; i < entry->keys.len; i++) {
120 Key *key = &entry->keys.val[i];
122 if (key->salt != NULL ||
123 _krb5_enctype_requires_random_salt(context, key->key.keytype))
124 continue;
126 key->salt = calloc(1, sizeof(*key->salt));
127 if (key->salt == NULL) {
128 ret = krb5_enomem(context);
129 break;
132 key->salt->type = KRB5_PADATA_PW_SALT;
134 ret = krb5_data_copy(&key->salt->salt,
135 pwsalt.saltvalue.data,
136 pwsalt.saltvalue.length);
137 if (ret)
138 break;
141 krb5_free_salt(context, pwsalt);
143 return ret;
146 krb5_error_code
147 _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
148 unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry)
150 krb5_principal enterprise_principal = NULL;
151 krb5_data key, value;
152 krb5_error_code ret;
154 if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
155 if (principal->name.name_string.len != 1) {
156 ret = KRB5_PARSE_MALFORMED;
157 krb5_set_error_message(context, ret, "malformed principal: "
158 "enterprise name with %d name components",
159 principal->name.name_string.len);
160 return ret;
162 ret = krb5_parse_name(context, principal->name.name_string.val[0],
163 &enterprise_principal);
164 if (ret)
165 return ret;
166 principal = enterprise_principal;
169 hdb_principal2key(context, principal, &key);
170 if (enterprise_principal)
171 krb5_free_principal(context, enterprise_principal);
172 ret = db->hdb__get(context, db, key, &value);
173 krb5_data_free(&key);
174 if(ret)
175 return ret;
176 ret = hdb_value2entry(context, &value, &entry->entry);
177 /* HDB_F_GET_ANY indicates request originated from KDC (not kadmin) */
178 if (ret == ASN1_BAD_ID && (flags & (HDB_F_CANON|HDB_F_GET_ANY)) == 0) {
179 krb5_data_free(&value);
180 return HDB_ERR_NOENTRY;
181 } else if (ret == ASN1_BAD_ID) {
182 hdb_entry_alias alias;
184 ret = hdb_value2entry_alias(context, &value, &alias);
185 if (ret) {
186 krb5_data_free(&value);
187 return ret;
189 hdb_principal2key(context, alias.principal, &key);
190 krb5_data_free(&value);
191 free_hdb_entry_alias(&alias);
193 ret = db->hdb__get(context, db, key, &value);
194 krb5_data_free(&key);
195 if (ret)
196 return ret;
197 ret = hdb_value2entry(context, &value, &entry->entry);
198 if (ret) {
199 krb5_data_free(&value);
200 return ret;
203 krb5_data_free(&value);
204 if ((flags & HDB_F_DECRYPT) && (flags & HDB_F_ALL_KVNOS)) {
205 /* Decrypt the current keys */
206 ret = hdb_unseal_keys(context, db, &entry->entry);
207 if (ret) {
208 hdb_free_entry(context, entry);
209 return ret;
211 /* Decrypt the key history too */
212 ret = hdb_unseal_keys_kvno(context, db, 0, flags, &entry->entry);
213 if (ret) {
214 hdb_free_entry(context, entry);
215 return ret;
217 } else if ((flags & HDB_F_DECRYPT)) {
218 if ((flags & HDB_F_KVNO_SPECIFIED) == 0 || kvno == entry->entry.kvno) {
219 /* Decrypt the current keys */
220 ret = hdb_unseal_keys(context, db, &entry->entry);
221 if (ret) {
222 hdb_free_entry(context, entry);
223 return ret;
225 } else {
226 if ((flags & HDB_F_ALL_KVNOS))
227 kvno = 0;
229 * Find and decrypt the keys from the history that we want,
230 * and swap them with the current keys
232 ret = hdb_unseal_keys_kvno(context, db, kvno, flags, &entry->entry);
233 if (ret) {
234 hdb_free_entry(context, entry);
235 return ret;
239 if ((flags & HDB_F_FOR_AS_REQ) && (flags & HDB_F_GET_CLIENT)) {
241 * Generate default salt for any principals missing one; note such
242 * principals could include those for which a random (non-password)
243 * key was generated, but given the salt will be ignored by a keytab
244 * client it doesn't hurt to include the default salt.
246 ret = add_default_salts(context, db, &entry->entry);
247 if (ret) {
248 hdb_free_entry(context, entry);
249 return ret;
252 if (enterprise_principal) {
254 * Whilst Windows does not canonicalize enterprise principal names if
255 * the canonicalize flag is unset, the original specification in
256 * draft-ietf-krb-wg-kerberos-referrals-03.txt says we should.
258 entry->entry.flags.force_canonicalize = 1;
261 return 0;
264 static krb5_error_code
265 hdb_remove_aliases(krb5_context context, HDB *db, krb5_data *key)
267 const HDB_Ext_Aliases *aliases;
268 krb5_error_code code;
269 hdb_entry oldentry;
270 krb5_data value;
271 size_t i;
273 code = db->hdb__get(context, db, *key, &value);
274 if (code == HDB_ERR_NOENTRY)
275 return 0;
276 else if (code)
277 return code;
279 code = hdb_value2entry(context, &value, &oldentry);
280 krb5_data_free(&value);
281 if (code)
282 return code;
284 code = hdb_entry_get_aliases(&oldentry, &aliases);
285 if (code || aliases == NULL) {
286 free_hdb_entry(&oldentry);
287 return code;
289 for (i = 0; i < aliases->aliases.len; i++) {
290 krb5_data akey;
292 code = hdb_principal2key(context, &aliases->aliases.val[i], &akey);
293 if (code == 0) {
294 code = db->hdb__del(context, db, akey);
295 krb5_data_free(&akey);
297 if (code) {
298 free_hdb_entry(&oldentry);
299 return code;
302 free_hdb_entry(&oldentry);
303 return 0;
306 static krb5_error_code
307 hdb_add_aliases(krb5_context context, HDB *db,
308 unsigned flags, hdb_entry_ex *entry)
310 const HDB_Ext_Aliases *aliases;
311 krb5_error_code code;
312 krb5_data key, value;
313 size_t i;
315 code = hdb_entry_get_aliases(&entry->entry, &aliases);
316 if (code || aliases == NULL)
317 return code;
319 for (i = 0; i < aliases->aliases.len; i++) {
320 hdb_entry_alias entryalias;
321 entryalias.principal = entry->entry.principal;
323 code = hdb_entry_alias2value(context, &entryalias, &value);
324 if (code)
325 return code;
327 code = hdb_principal2key(context, &aliases->aliases.val[i], &key);
328 if (code == 0) {
329 code = db->hdb__put(context, db, flags, key, value);
330 krb5_data_free(&key);
332 krb5_data_free(&value);
333 if (code)
334 return code;
336 return 0;
339 static krb5_error_code
340 hdb_check_aliases(krb5_context context, HDB *db, hdb_entry_ex *entry)
342 const HDB_Ext_Aliases *aliases;
343 int code;
344 size_t i;
346 /* check if new aliases already is used */
348 code = hdb_entry_get_aliases(&entry->entry, &aliases);
349 if (code)
350 return code;
352 for (i = 0; aliases && i < aliases->aliases.len; i++) {
353 hdb_entry_alias alias;
354 krb5_data akey, value;
356 code = hdb_principal2key(context, &aliases->aliases.val[i], &akey);
357 if (code == 0) {
358 code = db->hdb__get(context, db, akey, &value);
359 krb5_data_free(&akey);
361 if (code == HDB_ERR_NOENTRY)
362 continue;
363 else if (code)
364 return code;
366 code = hdb_value2entry_alias(context, &value, &alias);
367 krb5_data_free(&value);
369 if (code == ASN1_BAD_ID)
370 return HDB_ERR_EXISTS;
371 else if (code)
372 return code;
374 code = krb5_principal_compare(context, alias.principal,
375 entry->entry.principal);
376 free_hdb_entry_alias(&alias);
377 if (code == 0)
378 return HDB_ERR_EXISTS;
380 return 0;
383 krb5_error_code
384 _hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
386 krb5_data key, value;
387 int code;
389 if (entry->entry.flags.do_not_store ||
390 entry->entry.flags.force_canonicalize)
391 return HDB_ERR_MISUSE;
392 /* check if new aliases already is used */
393 code = hdb_check_aliases(context, db, entry);
394 if (code)
395 return code;
397 if ((flags & HDB_F_PRECHECK) && (flags & HDB_F_REPLACE))
398 return 0;
400 if ((flags & HDB_F_PRECHECK)) {
401 code = hdb_principal2key(context, entry->entry.principal, &key);
402 if (code)
403 return code;
404 code = db->hdb__get(context, db, key, &value);
405 krb5_data_free(&key);
406 if (code == 0)
407 krb5_data_free(&value);
408 if (code == HDB_ERR_NOENTRY)
409 return 0;
410 return code ? code : HDB_ERR_EXISTS;
413 if(entry->entry.generation == NULL) {
414 struct timeval t;
415 entry->entry.generation = malloc(sizeof(*entry->entry.generation));
416 if(entry->entry.generation == NULL) {
417 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
418 return ENOMEM;
420 gettimeofday(&t, NULL);
421 entry->entry.generation->time = t.tv_sec;
422 entry->entry.generation->usec = t.tv_usec;
423 entry->entry.generation->gen = 0;
424 } else
425 entry->entry.generation->gen++;
427 code = hdb_seal_keys(context, db, &entry->entry);
428 if (code)
429 return code;
431 hdb_principal2key(context, entry->entry.principal, &key);
433 /* remove aliases */
434 code = hdb_remove_aliases(context, db, &key);
435 if (code) {
436 krb5_data_free(&key);
437 return code;
439 hdb_entry2value(context, &entry->entry, &value);
440 code = db->hdb__put(context, db, flags & HDB_F_REPLACE, key, value);
441 krb5_data_free(&value);
442 krb5_data_free(&key);
443 if (code)
444 return code;
446 code = hdb_add_aliases(context, db, flags, entry);
448 return code;
451 krb5_error_code
452 _hdb_remove(krb5_context context, HDB *db,
453 unsigned flags, krb5_const_principal principal)
455 krb5_data key, value;
456 int code;
458 hdb_principal2key(context, principal, &key);
460 if ((flags & HDB_F_PRECHECK)) {
462 * We don't check that we can delete the aliases because we
463 * assume that the DB is consistent. If we did check for alias
464 * consistency we'd also have to provide a way to fsck the DB,
465 * otherwise admins would have no way to recover -- papering
466 * over this here is less work, but we really ought to provide
467 * an HDB fsck.
469 code = db->hdb__get(context, db, key, &value);
470 krb5_data_free(&key);
471 if (code == 0) {
472 krb5_data_free(&value);
473 return 0;
475 return code;
478 code = hdb_remove_aliases(context, db, &key);
479 if (code) {
480 krb5_data_free(&key);
481 return code;
483 code = db->hdb__del(context, db, key);
484 krb5_data_free(&key);
485 return code;