2 * Copyright (c) 2004 - 2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
38 hdb_entry_check_mandatory(krb5_context context
, const hdb_entry
*ent
)
42 if (ent
->extensions
== NULL
)
46 * check for unknown extensions and if they were tagged mandatory
49 for (i
= 0; i
< ent
->extensions
->len
; i
++) {
50 if (ent
->extensions
->val
[i
].data
.element
!=
51 choice_HDB_extension_data_asn1_ellipsis
)
53 if (ent
->extensions
->val
[i
].mandatory
) {
54 krb5_set_error_message(context
, HDB_ERR_MANDATORY_OPTION
,
55 "Principal has unknown "
56 "mandatory extension");
57 return HDB_ERR_MANDATORY_OPTION
;
64 hdb_find_extension(const hdb_entry
*entry
, int type
)
68 if (entry
->extensions
== NULL
)
71 for (i
= 0; i
< entry
->extensions
->len
; i
++)
72 if (entry
->extensions
->val
[i
].data
.element
== (unsigned)type
)
73 return &entry
->extensions
->val
[i
];
78 * Replace the extension `ext' in `entry'. Make a copy of the
79 * extension, so the caller must still free `ext' on both success and
80 * failure. Returns 0 or error code.
84 hdb_replace_extension(krb5_context context
,
86 const HDB_extension
*ext
)
93 if (entry
->extensions
== NULL
) {
94 entry
->extensions
= calloc(1, sizeof(*entry
->extensions
));
95 if (entry
->extensions
== NULL
) {
96 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
99 } else if (ext
->data
.element
!= choice_HDB_extension_data_asn1_ellipsis
) {
100 ext2
= hdb_find_extension(entry
, ext
->data
.element
);
103 * This is an unknown extension, and we are asked to replace a
104 * possible entry in `entry' that is of the same type. This
105 * might seem impossible, but ASN.1 CHOICE comes to our
106 * rescue. The first tag in each branch in the CHOICE is
107 * unique, so just find the element in the list that have the
108 * same tag was we are putting into the list.
110 Der_class replace_class
, list_class
;
111 Der_type replace_type
, list_type
;
112 unsigned int replace_tag
, list_tag
;
116 ret
= der_get_tag(ext
->data
.u
.asn1_ellipsis
.data
,
117 ext
->data
.u
.asn1_ellipsis
.length
,
118 &replace_class
, &replace_type
, &replace_tag
,
121 krb5_set_error_message(context
, ret
, "hdb: failed to decode "
122 "replacement hdb extension");
126 for (i
= 0; i
< entry
->extensions
->len
; i
++) {
127 HDB_extension
*ext3
= &entry
->extensions
->val
[i
];
129 if (ext3
->data
.element
!= choice_HDB_extension_data_asn1_ellipsis
)
132 ret
= der_get_tag(ext3
->data
.u
.asn1_ellipsis
.data
,
133 ext3
->data
.u
.asn1_ellipsis
.length
,
134 &list_class
, &list_type
, &list_tag
,
137 krb5_set_error_message(context
, ret
, "hdb: failed to decode "
138 "present hdb extension");
142 if (MAKE_TAG(replace_class
,replace_type
,replace_type
) ==
143 MAKE_TAG(list_class
,list_type
,list_type
)) {
151 free_HDB_extension(ext2
);
152 ret
= copy_HDB_extension(ext
, ext2
);
154 krb5_set_error_message(context
, ret
, "hdb: failed to copy replacement "
159 return add_HDB_extensions(entry
->extensions
, ext
);
163 hdb_clear_extension(krb5_context context
,
169 if (entry
->extensions
== NULL
)
172 for (i
= 0; i
< entry
->extensions
->len
; ) {
173 if (entry
->extensions
->val
[i
].data
.element
== (unsigned)type
)
174 (void) remove_HDB_extensions(entry
->extensions
, i
);
178 if (entry
->extensions
->len
== 0) {
179 free(entry
->extensions
->val
);
180 free(entry
->extensions
);
181 entry
->extensions
= NULL
;
189 hdb_entry_get_pkinit_acl(const hdb_entry
*entry
, const HDB_Ext_PKINIT_acl
**a
)
191 const HDB_extension
*ext
;
193 ext
= hdb_find_extension(entry
, choice_HDB_extension_data_pkinit_acl
);
195 *a
= &ext
->data
.u
.pkinit_acl
;
203 hdb_entry_get_pkinit_hash(const hdb_entry
*entry
, const HDB_Ext_PKINIT_hash
**a
)
205 const HDB_extension
*ext
;
207 ext
= hdb_find_extension(entry
, choice_HDB_extension_data_pkinit_cert_hash
);
209 *a
= &ext
->data
.u
.pkinit_cert_hash
;
217 hdb_entry_get_pkinit_cert(const hdb_entry
*entry
, const HDB_Ext_PKINIT_cert
**a
)
219 const HDB_extension
*ext
;
221 ext
= hdb_find_extension(entry
, choice_HDB_extension_data_pkinit_cert
);
223 *a
= &ext
->data
.u
.pkinit_cert
;
231 hdb_entry_get_krb5_config(const hdb_entry
*entry
, heim_octet_string
*c
)
233 const HDB_extension
*ext
;
237 ext
= hdb_find_extension(entry
, choice_HDB_extension_data_krb5_config
);
239 *c
= ext
->data
.u
.krb5_config
;
244 hdb_entry_set_krb5_config(krb5_context context
,
246 heim_octet_string
*s
)
250 ext
.mandatory
= FALSE
;
251 ext
.data
.element
= choice_HDB_extension_data_last_pw_change
;
252 /* hdb_replace_extension() copies this, so no need to copy it here */
253 ext
.data
.u
.krb5_config
= *s
;
254 return hdb_replace_extension(context
, entry
, &ext
);
258 hdb_entry_get_pw_change_time(const hdb_entry
*entry
, time_t *t
)
260 const HDB_extension
*ext
;
262 ext
= hdb_find_extension(entry
, choice_HDB_extension_data_last_pw_change
);
264 *t
= ext
->data
.u
.last_pw_change
;
272 hdb_entry_set_pw_change_time(krb5_context context
,
278 ext
.mandatory
= FALSE
;
279 ext
.data
.element
= choice_HDB_extension_data_last_pw_change
;
282 ext
.data
.u
.last_pw_change
= t
;
284 return hdb_replace_extension(context
, entry
, &ext
);
288 hdb_entry_get_password(krb5_context context
, HDB
*db
,
289 const hdb_entry
*entry
, char **p
)
295 ext
= hdb_find_extension(entry
, choice_HDB_extension_data_password
);
297 heim_utf8_string xstr
;
298 heim_octet_string pw
;
300 if (db
->hdb_master_key_set
&& ext
->data
.u
.password
.mkvno
) {
303 key
= _hdb_find_master_key(ext
->data
.u
.password
.mkvno
,
307 krb5_set_error_message(context
, HDB_ERR_NO_MKEY
,
308 "master key %d missing",
309 *ext
->data
.u
.password
.mkvno
);
310 return HDB_ERR_NO_MKEY
;
313 ret
= _hdb_mkey_decrypt(context
, key
, HDB_KU_MKEY
,
314 ext
->data
.u
.password
.password
.data
,
315 ext
->data
.u
.password
.password
.length
,
318 ret
= der_copy_octet_string(&ext
->data
.u
.password
.password
, &pw
);
321 krb5_clear_error_message(context
);
326 if (xstr
[pw
.length
- 1] != '\0') {
327 krb5_set_error_message(context
, EINVAL
, "malformed password");
333 der_free_octet_string(&pw
);
335 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
341 ret
= krb5_unparse_name(context
, entry
->principal
, &str
);
343 krb5_set_error_message(context
, ENOENT
,
344 "no password attribute for %s", str
);
347 krb5_clear_error_message(context
);
353 hdb_entry_set_password(krb5_context context
, HDB
*db
,
354 hdb_entry
*entry
, const char *p
)
360 ext
.mandatory
= FALSE
;
361 ext
.data
.element
= choice_HDB_extension_data_password
;
363 if (db
->hdb_master_key_set
) {
365 key
= _hdb_find_master_key(NULL
, db
->hdb_master_key
);
367 krb5_set_error_message(context
, HDB_ERR_NO_MKEY
,
368 "hdb_entry_set_password: "
369 "failed to find masterkey");
370 return HDB_ERR_NO_MKEY
;
373 ret
= _hdb_mkey_encrypt(context
, key
, HDB_KU_MKEY
,
375 &ext
.data
.u
.password
.password
);
379 ext
.data
.u
.password
.mkvno
=
380 malloc(sizeof(*ext
.data
.u
.password
.mkvno
));
381 if (ext
.data
.u
.password
.mkvno
== NULL
) {
382 free_HDB_extension(&ext
);
383 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
386 *ext
.data
.u
.password
.mkvno
= _hdb_mkey_version(key
);
389 ext
.data
.u
.password
.mkvno
= NULL
;
391 ret
= krb5_data_copy(&ext
.data
.u
.password
.password
,
394 krb5_set_error_message(context
, ret
, "malloc: out of memory");
395 free_HDB_extension(&ext
);
400 ret
= hdb_replace_extension(context
, entry
, &ext
);
402 free_HDB_extension(&ext
);
408 hdb_entry_clear_password(krb5_context context
, hdb_entry
*entry
)
410 return hdb_clear_extension(context
, entry
,
411 choice_HDB_extension_data_password
);
415 hdb_entry_get_ConstrainedDelegACL(const hdb_entry
*entry
,
416 const HDB_Ext_Constrained_delegation_acl
**a
)
418 const HDB_extension
*ext
;
420 ext
= hdb_find_extension(entry
,
421 choice_HDB_extension_data_allowed_to_delegate_to
);
423 *a
= &ext
->data
.u
.allowed_to_delegate_to
;
431 hdb_entry_get_aliases(const hdb_entry
*entry
, const HDB_Ext_Aliases
**a
)
433 const HDB_extension
*ext
;
435 ext
= hdb_find_extension(entry
, choice_HDB_extension_data_aliases
);
437 *a
= &ext
->data
.u
.aliases
;
445 hdb_entry_get_kvno_diff_clnt(const hdb_entry
*entry
)
447 const HDB_extension
*ext
;
449 ext
= hdb_find_extension(entry
,
450 choice_HDB_extension_data_hist_kvno_diff_clnt
);
452 return ext
->data
.u
.hist_kvno_diff_clnt
;
457 hdb_entry_set_kvno_diff_clnt(krb5_context context
, hdb_entry
*entry
,
464 ext
.mandatory
= FALSE
;
465 ext
.data
.element
= choice_HDB_extension_data_hist_kvno_diff_clnt
;
466 ext
.data
.u
.hist_kvno_diff_clnt
= diff
;
467 return hdb_replace_extension(context
, entry
, &ext
);
471 hdb_entry_clear_kvno_diff_clnt(krb5_context context
, hdb_entry
*entry
)
473 return hdb_clear_extension(context
, entry
,
474 choice_HDB_extension_data_hist_kvno_diff_clnt
);
478 hdb_entry_get_kvno_diff_svc(const hdb_entry
*entry
)
480 const HDB_extension
*ext
;
482 ext
= hdb_find_extension(entry
,
483 choice_HDB_extension_data_hist_kvno_diff_svc
);
485 return ext
->data
.u
.hist_kvno_diff_svc
;
486 return 1024; /* max_life effectively provides a better default */
490 hdb_entry_set_kvno_diff_svc(krb5_context context
, hdb_entry
*entry
,
497 ext
.mandatory
= FALSE
;
498 ext
.data
.element
= choice_HDB_extension_data_hist_kvno_diff_svc
;
499 ext
.data
.u
.hist_kvno_diff_svc
= diff
;
500 return hdb_replace_extension(context
, entry
, &ext
);
504 hdb_entry_clear_kvno_diff_svc(krb5_context context
, hdb_entry
*entry
)
506 return hdb_clear_extension(context
, entry
,
507 choice_HDB_extension_data_hist_kvno_diff_svc
);
511 hdb_set_last_modified_by(krb5_context context
, hdb_entry
*entry
,
512 krb5_principal modby
, time_t modtime
)
518 old_ev
= entry
->modified_by
;
520 ev
= calloc(1, sizeof (*ev
));
524 ret
= krb5_copy_principal(context
, modby
, &ev
->principal
);
526 ret
= krb5_parse_name(context
, "root/admin", &ev
->principal
);
535 entry
->modified_by
= ev
;
542 hdb_entry_get_key_rotation(krb5_context context
,
543 const hdb_entry
*entry
,
544 const HDB_Ext_KeyRotation
**kr
)
547 hdb_find_extension(entry
, choice_HDB_extension_data_key_rotation
);
549 *kr
= ext
? &ext
->data
.u
.key_rotation
: NULL
;
554 hdb_validate_key_rotation(krb5_context context
,
555 const KeyRotation
*past_kr
,
556 const KeyRotation
*new_kr
)
558 unsigned int last_kvno
;
560 if (new_kr
->period
< 1) {
561 krb5_set_error_message(context
, EINVAL
,
562 "Key rotation periods must be non-zero "
566 if (new_kr
->base_key_kvno
< 1 || new_kr
->base_kvno
< 1) {
567 krb5_set_error_message(context
, EINVAL
,
568 "Key version number zero not allowed "
575 if (past_kr
->base_key_kvno
== new_kr
->base_key_kvno
) {
577 * The new base keys can be the same as the old, but must have
578 * different kvnos. (Well, not must must. It's a convention for now.)
580 krb5_set_error_message(context
, EINVAL
,
581 "Base key version numbers for KRs must differ");
584 if (new_kr
->epoch
- past_kr
->epoch
<= 0) {
585 krb5_set_error_message(context
, EINVAL
,
586 "New key rotation periods must start later "
587 "than existing ones");
591 last_kvno
= 1 + ((new_kr
->epoch
- past_kr
->epoch
) / past_kr
->period
);
592 if (new_kr
->base_kvno
<= last_kvno
) {
593 krb5_set_error_message(context
, EINVAL
,
594 "New key rotation base kvno must be larger "
595 "than the last kvno for the current key "
596 "rotation (%u)", last_kvno
);
603 kr_eq(const KeyRotation
*a
, const KeyRotation
*b
)
606 a
->epoch
== b
->epoch
&&
607 a
->period
== b
->period
&&
608 a
->base_kvno
== b
->base_kvno
&&
609 a
->base_key_kvno
== b
->base_key_kvno
&&
610 KeyRotationFlags2int(a
->flags
) == KeyRotationFlags2int(b
->flags
)
615 hdb_validate_key_rotations(krb5_context context
,
616 const HDB_Ext_KeyRotation
*existing
,
617 const HDB_Ext_KeyRotation
*krs
)
619 krb5_error_code ret
= 0;
623 if ((!existing
|| !existing
->len
) && (!krs
|| !krs
->len
))
624 return 0; /* Nothing to do; weird */
627 * HDB_Ext_KeyRotation has to have 1..3 elements, and this is enforced by
628 * the ASN.1 compiler and the code it generates. Nonetheless we'll check
629 * that there's not zero elements.
631 if ((!krs
|| !krs
->len
)) {
633 * NOTE: We can clear this on concrete principals with virtual keys
634 * though. The caller can check for that case.
636 krb5_set_error_message(context
, EINVAL
,
637 "Cannot clear key rotation metadata on "
638 "virtual principal namespaces");
642 /* Validate the new KRs by themselves */
643 for (i
= 0; ret
== 0 && i
< krs
->len
; i
++) {
644 ret
= hdb_validate_key_rotation(context
,
645 i
+1 < krs
->len
? &krs
->val
[i
+1] : 0,
648 if (ret
|| !existing
|| !existing
->len
)
651 if (existing
->len
== krs
->len
) {
652 /* Check for no change */
653 for (i
= 0; i
< krs
->len
; i
++)
654 if (!kr_eq(&existing
->val
[i
], &krs
->val
[i
]))
657 return 0; /* No change */
661 * Check that new KRs make sense in the context of the previous KRs.
665 * - add one new KR in front
668 * Start by checking if we're adding a KR, then go on to check for dropped
669 * KRs and/or last KR alteration.
671 if (existing
->val
[0].epoch
== krs
->val
[0].epoch
||
672 existing
->val
[0].base_kvno
== krs
->val
[0].base_kvno
) {
673 if (!kr_eq(&existing
->val
[0], &krs
->val
[0])) {
674 krb5_set_error_message(context
, EINVAL
,
675 "Key rotation change not sensible");
678 /* Key rotation *not* added */
680 /* Key rotation added; check it first */
681 ret
= hdb_validate_key_rotation(context
,
686 for (i
= 0; ret
== 0 && i
< existing
->len
&& i
+ added
< krs
->len
; i
++)
687 if (!kr_eq(&existing
->val
[i
], &krs
->val
[i
+ added
]))
688 krb5_set_error_message(context
, ret
= EINVAL
,
689 "Only last key rotation may be truncated");
693 /* XXX We need a function to "revoke" the past */
696 * This function adds a KeyRotation value to an entry, validating the
697 * change. One of `entry' and `krs' must be NULL, and the other non-NULL, and
698 * whichever is given will be altered.
700 * @param context Context
701 * @param entry An HDB entry
702 * @param krs A key rotation extension for hdb_entry
703 * @param kr A new KeyRotation value
705 * @return Zero on success, an error otherwise.
708 hdb_entry_add_key_rotation(krb5_context context
,
710 HDB_Ext_KeyRotation
*krs
,
711 const KeyRotation
*kr
)
714 HDB_extension new_ext
;
715 HDB_extension
*ext
= &new_ext
;
719 if (kr
->period
< 1) {
720 krb5_set_error_message(context
, EINVAL
,
721 "Key rotation period cannot be zero");
725 new_ext
.mandatory
= TRUE
;
726 new_ext
.data
.element
= choice_HDB_extension_data_key_rotation
;
727 new_ext
.data
.u
.key_rotation
.len
= 0;
728 new_ext
.data
.u
.key_rotation
.val
= 0;
734 ext
= hdb_find_extension(entry
, choice_HDB_extension_data_key_rotation
);
738 const KeyRotation
*prev_kr
= &krs
->val
[0];
739 unsigned int last_kvno
= 0;
741 if (kr
->epoch
- prev_kr
->epoch
<= 0) {
742 krb5_set_error_message(context
, EINVAL
,
743 "New key rotation periods must start later "
744 "than existing ones");
748 if (kr
->base_kvno
<= prev_kr
->base_kvno
||
749 kr
->base_kvno
- prev_kr
->base_kvno
<=
751 ((kr
->epoch
- prev_kr
->epoch
) / prev_kr
->period
))) {
752 krb5_set_error_message(context
, EINVAL
,
753 "New key rotation base kvno must be larger "
754 "than the last kvno for the current key "
755 "rotation (%u)", last_kvno
);
761 ret
= add_HDB_Ext_KeyRotation(&ext
->data
.u
.key_rotation
, kr
);
765 /* Rotate new to front */
766 tmp
= ext
->data
.u
.key_rotation
.val
[ext
->data
.u
.key_rotation
.len
- 1];
767 sz
= sizeof(ext
->data
.u
.key_rotation
.val
[0]);
768 memmove(&ext
->data
.u
.key_rotation
.val
[1], &ext
->data
.u
.key_rotation
.val
[0],
769 (ext
->data
.u
.key_rotation
.len
- 1) * sz
);
770 ext
->data
.u
.key_rotation
.val
[0] = tmp
;
772 /* Drop too old entries */
773 for (i
= 3; i
< ext
->data
.u
.key_rotation
.len
; i
++)
774 free_KeyRotation(&ext
->data
.u
.key_rotation
.val
[i
]);
775 ext
->data
.u
.key_rotation
.len
=
776 ext
->data
.u
.key_rotation
.len
> 3 ? 3 : ext
->data
.u
.key_rotation
.len
;
781 /* Install new extension */
782 if (ret
== 0 && entry
)
783 ret
= hdb_replace_extension(context
, entry
, ext
);
784 free_HDB_extension(&new_ext
);