2 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
6 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
8 * Openvision retains the copyright to derivative works of
9 * this source code. Do *NOT* create a derivative of this
10 * source code before consulting with your legal department.
11 * Do *NOT* integrate *ANY* of this source code into another
12 * product before consulting with your legal department.
14 * For further information, read the top-level Openvision
15 * copyright which is contained in the top-level MIT Kerberos
18 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
23 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
28 static char *rcsid
= "$Header$";
30 #include <sys/types.h>
33 #include "server_internal.h"
34 #include <kadm5/admin.h>
41 #include <kadm5/server_internal.h>
42 #include <kadm5/admin.h>
43 #ifdef USE_PASSWORD_SERVER
47 extern krb5_principal master_princ
;
48 extern krb5_principal hist_princ
;
49 extern krb5_keyblock hist_key
;
50 extern krb5_db_entry master_db
;
51 extern krb5_db_entry hist_db
;
52 extern krb5_kvno hist_kvno
;
54 static int decrypt_key_data(krb5_context context
,
55 krb5_keyblock
*, int n_key_data
, krb5_key_data
*key_data
,
56 krb5_keyblock
**keyblocks
, int *n_keys
);
58 static krb5_error_code
59 kadm5_copy_principal(krb5_context context
, krb5_const_principal inprinc
, krb5_principal
*outprinc
)
61 register krb5_principal tempprinc
;
62 register int i
, nelems
;
64 tempprinc
= (krb5_principal
)krb5_db_alloc(context
, NULL
, sizeof(krb5_principal_data
));
69 memcpy(tempprinc
, inprinc
, sizeof(krb5_principal_data
));
71 nelems
= (int) krb5_princ_size(context
, inprinc
);
72 tempprinc
->data
= krb5_db_alloc(context
, NULL
, nelems
* sizeof(krb5_data
));
74 if (tempprinc
->data
== 0) {
75 krb5_db_free(context
, (char *)tempprinc
);
79 for (i
= 0; i
< nelems
; i
++) {
80 unsigned int len
= krb5_princ_component(context
, inprinc
, i
)->length
;
81 krb5_princ_component(context
, tempprinc
, i
)->length
= len
;
82 if (((krb5_princ_component(context
, tempprinc
, i
)->data
=
83 krb5_db_alloc(context
, NULL
, len
)) == 0) && len
) {
85 krb5_db_free(context
, krb5_princ_component(context
, tempprinc
, i
)->data
);
86 krb5_db_free (context
, tempprinc
->data
);
87 krb5_db_free (context
, tempprinc
);
91 memcpy(krb5_princ_component(context
, tempprinc
, i
)->data
,
92 krb5_princ_component(context
, inprinc
, i
)->data
, len
);
95 tempprinc
->realm
.data
=
96 krb5_db_alloc(context
, NULL
, tempprinc
->realm
.length
= inprinc
->realm
.length
);
97 if (!tempprinc
->realm
.data
&& tempprinc
->realm
.length
) {
98 for (i
= 0; i
< nelems
; i
++)
99 krb5_db_free(context
, krb5_princ_component(context
, tempprinc
, i
)->data
);
100 krb5_db_free(context
, tempprinc
->data
);
101 krb5_db_free(context
, tempprinc
);
104 if (tempprinc
->realm
.length
)
105 memcpy(tempprinc
->realm
.data
, inprinc
->realm
.data
,
106 inprinc
->realm
.length
);
108 *outprinc
= tempprinc
;
113 kadm5_free_principal(krb5_context context
, krb5_principal val
)
115 register krb5_int32 i
;
121 i
= krb5_princ_size(context
, val
);
123 krb5_db_free(context
, krb5_princ_component(context
, val
, i
)->data
);
124 krb5_db_free(context
, val
->data
);
127 krb5_db_free(context
, val
->realm
.data
);
128 krb5_db_free(context
, val
);
132 * XXX Functions that ought to be in libkrb5.a, but aren't.
134 kadm5_ret_t
krb5_copy_key_data_contents(context
, from
, to
)
135 krb5_context context
;
136 krb5_key_data
*from
, *to
;
142 idx
= (from
->key_data_ver
== 1 ? 1 : 2);
144 for (i
= 0; i
< idx
; i
++) {
145 if ( from
->key_data_length
[i
] ) {
146 to
->key_data_contents
[i
] = malloc(from
->key_data_length
[i
]);
147 if (to
->key_data_contents
[i
] == NULL
) {
148 for (i
= 0; i
< idx
; i
++) {
149 if (to
->key_data_contents
[i
]) {
150 memset(to
->key_data_contents
[i
], 0,
151 to
->key_data_length
[i
]);
152 free(to
->key_data_contents
[i
]);
157 memcpy(to
->key_data_contents
[i
], from
->key_data_contents
[i
],
158 from
->key_data_length
[i
]);
164 static krb5_tl_data
*dup_tl_data(krb5_tl_data
*tl
)
168 n
= (krb5_tl_data
*) malloc(sizeof(krb5_tl_data
));
171 n
->tl_data_contents
= malloc(tl
->tl_data_length
);
172 if (n
->tl_data_contents
== NULL
) {
176 memcpy(n
->tl_data_contents
, tl
->tl_data_contents
, tl
->tl_data_length
);
177 n
->tl_data_type
= tl
->tl_data_type
;
178 n
->tl_data_length
= tl
->tl_data_length
;
179 n
->tl_data_next
= NULL
;
183 /* This is in lib/kdb/kdb_cpw.c, but is static */
184 static void cleanup_key_data(context
, count
, data
)
185 krb5_context context
;
187 krb5_key_data
* data
;
191 for (i
= 0; i
< count
; i
++)
192 for (j
= 0; j
< data
[i
].key_data_ver
; j
++)
193 if (data
[i
].key_data_length
[j
])
194 krb5_db_free(context
, data
[i
].key_data_contents
[j
]);
195 krb5_db_free(context
, data
);
199 kadm5_create_principal(void *server_handle
,
200 kadm5_principal_ent_t entry
, long mask
,
204 kadm5_create_principal_3(server_handle
, entry
, mask
,
208 kadm5_create_principal_3(void *server_handle
,
209 kadm5_principal_ent_t entry
, long mask
,
210 int n_ks_tuple
, krb5_key_salt_tuple
*ks_tuple
,
214 osa_princ_ent_rec adb
;
215 kadm5_policy_ent_rec polent
;
217 krb5_tl_data
*tl_data_orig
, *tl_data_tail
;
219 kadm5_server_handle_t handle
= server_handle
;
221 CHECK_HANDLE(server_handle
);
223 krb5_clear_error_message(handle
->context
);
226 * Argument sanity checking, and opening up the DB
228 if(!(mask
& KADM5_PRINCIPAL
) || (mask
& KADM5_MOD_NAME
) ||
229 (mask
& KADM5_MOD_TIME
) || (mask
& KADM5_LAST_PWD_CHANGE
) ||
230 (mask
& KADM5_MKVNO
) || (mask
& KADM5_POLICY_CLR
) ||
231 (mask
& KADM5_AUX_ATTRIBUTES
) || (mask
& KADM5_KEY_DATA
) ||
232 (mask
& KADM5_LAST_SUCCESS
) || (mask
& KADM5_LAST_FAILED
) ||
233 (mask
& KADM5_FAIL_AUTH_COUNT
))
234 return KADM5_BAD_MASK
;
235 if((mask
& ~ALL_PRINC_MASK
))
236 return KADM5_BAD_MASK
;
237 if (entry
== (kadm5_principal_ent_t
) NULL
|| password
== NULL
)
241 * Check to see if the principal exists
243 ret
= kdb_get_entry(handle
, entry
->principal
, &kdb
, &adb
);
246 case KADM5_UNK_PRINC
:
247 /* Solaris Kerberos */
248 memset(&kdb
, 0, sizeof(krb5_db_entry
));
249 memset(&adb
, 0, sizeof(osa_princ_ent_rec
));
253 * Solaris Kerberos: this allows an addprinc to be done on a mix-in
254 * princ which has no keys initially.
256 if (kdb
.n_key_data
!= 0) {
257 /* have a princ with keys, return dupe princ error */
258 kdb_free_entry(handle
, &kdb
, &adb
);
262 * have a princ with no keys, let's replace it. Note, want to
263 * keep the existing kdb tl_data (specifically the LDAP plugin
264 * adds the DN to the tl_data which is needed to locate the dir.
267 kdb_free_entry(handle
, NULL
, &adb
);
268 memset(&adb
, 0, sizeof(osa_princ_ent_rec
));
276 * If a policy was specified, load it.
277 * If we can not find the one specified return an error
279 if ((mask
& KADM5_POLICY
)) {
280 if ((ret
= kadm5_get_policy(handle
->lhandle
, entry
->policy
,
281 &polent
)) != KADM5_OK
) {
283 return KADM5_BAD_POLICY
;
288 if ((ret
= passwd_check(handle
, password
, (mask
& KADM5_POLICY
),
289 &polent
, entry
->principal
))) {
290 if (mask
& KADM5_POLICY
)
291 (void) kadm5_free_policy_ent(handle
->lhandle
, &polent
);
295 * Start populating the various DB fields, using the
296 * "defaults" for fields that were not specified by the
299 if ((ret
= krb5_timeofday(handle
->context
, &now
))) {
300 if (mask
& KADM5_POLICY
)
301 (void) kadm5_free_policy_ent(handle
->lhandle
, &polent
);
305 kdb
.magic
= KRB5_KDB_MAGIC_NUMBER
;
306 kdb
.len
= KRB5_KDB_V1_BASE_LENGTH
; /* gag me with a chainsaw */
310 * If KADM5_ATTRIBUTES is set, we want to rope in not only
311 * entry->attributes, but also the generic params.flags
312 * obtained previously via kadm5_get_config_params.
314 if ((mask
& KADM5_ATTRIBUTES
)) {
315 kdb
.attributes
= handle
->params
.flags
;
316 kdb
.attributes
|= entry
->attributes
;
318 kdb
.attributes
= handle
->params
.flags
;
321 if ((mask
& KADM5_MAX_LIFE
))
322 kdb
.max_life
= entry
->max_life
;
324 kdb
.max_life
= handle
->params
.max_life
;
326 if (mask
& KADM5_MAX_RLIFE
)
327 kdb
.max_renewable_life
= entry
->max_renewable_life
;
329 kdb
.max_renewable_life
= handle
->params
.max_rlife
;
331 if ((mask
& KADM5_PRINC_EXPIRE_TIME
))
332 kdb
.expiration
= entry
->princ_expire_time
;
334 kdb
.expiration
= handle
->params
.expiration
;
336 kdb
.pw_expiration
= 0;
337 if ((mask
& KADM5_POLICY
)) {
338 if(polent
.pw_max_life
)
339 kdb
.pw_expiration
= now
+ polent
.pw_max_life
;
341 kdb
.pw_expiration
= 0;
343 if ((mask
& KADM5_PW_EXPIRATION
))
344 kdb
.pw_expiration
= entry
->pw_expiration
;
346 kdb
.last_success
= 0;
348 kdb
.fail_auth_count
= 0;
350 /* this is kind of gross, but in order to free the tl data, I need
351 to free the entire kdb entry, and that will try to free the
354 if ((ret
= kadm5_copy_principal(handle
->context
,
355 entry
->principal
, &(kdb
.princ
)))) {
356 if (mask
& KADM5_POLICY
)
357 (void) kadm5_free_policy_ent(handle
->lhandle
, &polent
);
361 if ((ret
= krb5_dbe_update_last_pwd_change(handle
->context
, &kdb
, now
))) {
362 krb5_db_free_principal(handle
->context
, &kdb
, 1);
363 if (mask
& KADM5_POLICY
)
364 (void) kadm5_free_policy_ent(handle
->lhandle
, &polent
);
368 if (mask
& KADM5_TL_DATA
) {
369 /* splice entry->tl_data onto the front of kdb.tl_data */
370 tl_data_orig
= kdb
.tl_data
;
371 for (tl_data_tail
= entry
->tl_data
; tl_data_tail
;
372 tl_data_tail
= tl_data_tail
->tl_data_next
)
374 ret
= krb5_dbe_update_tl_data(handle
->context
, &kdb
, tl_data_tail
);
377 krb5_db_free_principal(handle
->context
, &kdb
, 1);
378 if (mask
& KADM5_POLICY
)
379 (void) kadm5_free_policy_ent(handle
->lhandle
, &polent
);
385 /* initialize the keys */
387 if ((ret
= krb5_dbe_cpw(handle
->context
, &handle
->master_keyblock
,
388 n_ks_tuple
?ks_tuple
:handle
->params
.keysalts
,
389 n_ks_tuple
?n_ks_tuple
:handle
->params
.num_keysalts
,
391 (mask
& KADM5_KVNO
)?entry
->kvno
:1,
393 krb5_db_free_principal(handle
->context
, &kdb
, 1);
394 if (mask
& KADM5_POLICY
)
395 (void) kadm5_free_policy_ent(handle
->lhandle
, &polent
);
399 /* populate the admin-server-specific fields. In the OV server,
400 this used to be in a separate database. Since there's already
401 marshalling code for the admin fields, to keep things simple,
402 I'm going to keep it, and make all the admin stuff occupy a
403 single tl_data record, */
405 adb
.admin_history_kvno
= hist_kvno
;
406 if ((mask
& KADM5_POLICY
)) {
407 adb
.aux_attributes
= KADM5_POLICY
;
409 /* this does *not* need to be strdup'ed, because adb is xdr */
410 /* encoded in osa_adb_create_princ, and not ever freed */
412 adb
.policy
= entry
->policy
;
415 /* increment the policy ref count, if any */
417 if ((mask
& KADM5_POLICY
)) {
418 polent
.policy_refcnt
++;
419 if ((ret
= kadm5_modify_policy_internal(handle
->lhandle
, &polent
,
422 krb5_db_free_principal(handle
->context
, &kdb
, 1);
423 if (mask
& KADM5_POLICY
)
424 (void) kadm5_free_policy_ent(handle
->lhandle
, &polent
);
429 /* In all cases key and the principal data is set, let the database provider know */
430 kdb
.mask
= mask
| KADM5_KEY_DATA
| KADM5_PRINCIPAL
;
432 /* store the new db entry */
433 ret
= kdb_put_entry(handle
, &kdb
, &adb
);
435 krb5_db_free_principal(handle
->context
, &kdb
, 1);
438 if ((mask
& KADM5_POLICY
)) {
439 /* decrement the policy ref count */
441 polent
.policy_refcnt
--;
443 * if this fails, there's nothing we can do anyway. the
444 * policy refcount wil be too high.
446 (void) kadm5_modify_policy_internal(handle
->lhandle
, &polent
,
450 if (mask
& KADM5_POLICY
)
451 (void) kadm5_free_policy_ent(handle
->lhandle
, &polent
);
455 if (mask
& KADM5_POLICY
)
456 (void) kadm5_free_policy_ent(handle
->lhandle
, &polent
);
463 kadm5_delete_principal(void *server_handle
, krb5_principal principal
)
466 kadm5_policy_ent_rec polent
;
468 osa_princ_ent_rec adb
;
469 kadm5_server_handle_t handle
= server_handle
;
471 CHECK_HANDLE(server_handle
);
473 krb5_clear_error_message(handle
->context
);
475 if (principal
== NULL
)
478 if ((ret
= kdb_get_entry(handle
, principal
, &kdb
, &adb
)))
481 if ((adb
.aux_attributes
& KADM5_POLICY
)) {
482 if ((ret
= kadm5_get_policy(handle
->lhandle
,
483 adb
.policy
, &polent
))
485 polent
.policy_refcnt
--;
486 if ((ret
= kadm5_modify_policy_internal(handle
->lhandle
, &polent
,
489 (void) kadm5_free_policy_ent(handle
->lhandle
, &polent
);
490 kdb_free_entry(handle
, &kdb
, &adb
);
494 if ((ret
= kadm5_free_policy_ent(handle
->lhandle
, &polent
))) {
495 kdb_free_entry(handle
, &kdb
, &adb
);
500 ret
= kdb_delete_entry(handle
, principal
);
502 kdb_free_entry(handle
, &kdb
, &adb
);
508 kadm5_modify_principal(void *server_handle
,
509 kadm5_principal_ent_t entry
, long mask
)
512 kadm5_policy_ent_rec npol
, opol
;
513 int have_npol
= 0, have_opol
= 0;
515 krb5_tl_data
*tl_data_orig
;
516 osa_princ_ent_rec adb
;
517 kadm5_server_handle_t handle
= server_handle
;
519 CHECK_HANDLE(server_handle
);
521 krb5_clear_error_message(handle
->context
);
523 if((mask
& KADM5_PRINCIPAL
) || (mask
& KADM5_LAST_PWD_CHANGE
) ||
524 (mask
& KADM5_MOD_TIME
) || (mask
& KADM5_MOD_NAME
) ||
525 (mask
& KADM5_MKVNO
) || (mask
& KADM5_AUX_ATTRIBUTES
) ||
526 (mask
& KADM5_KEY_DATA
) || (mask
& KADM5_LAST_SUCCESS
) ||
527 (mask
& KADM5_LAST_FAILED
))
528 return KADM5_BAD_MASK
;
529 if((mask
& ~ALL_PRINC_MASK
))
530 return KADM5_BAD_MASK
;
531 if((mask
& KADM5_POLICY
) && (mask
& KADM5_POLICY_CLR
))
532 return KADM5_BAD_MASK
;
533 if(entry
== (kadm5_principal_ent_t
) NULL
)
535 if (mask
& KADM5_TL_DATA
) {
536 tl_data_orig
= entry
->tl_data
;
537 while (tl_data_orig
) {
538 if (tl_data_orig
->tl_data_type
< 256)
539 return KADM5_BAD_TL_TYPE
;
540 tl_data_orig
= tl_data_orig
->tl_data_next
;
544 ret
= kdb_get_entry(handle
, entry
->principal
, &kdb
, &adb
);
549 * This is pretty much the same as create ...
552 if ((mask
& KADM5_POLICY
)) {
553 /* get the new policy */
554 ret
= kadm5_get_policy(handle
->lhandle
, entry
->policy
, &npol
);
558 ret
= KADM5_BAD_POLICY
;
560 case KADM5_UNK_POLICY
:
561 case KADM5_BAD_POLICY
:
562 ret
= KADM5_UNK_POLICY
;
569 /* if we already have a policy, get it to decrement the refcnt */
570 if(adb
.aux_attributes
& KADM5_POLICY
) {
571 /* ... but not if the old and new are the same */
572 if(strcmp(adb
.policy
, entry
->policy
)) {
573 ret
= kadm5_get_policy(handle
->lhandle
,
577 case KADM5_BAD_POLICY
:
578 case KADM5_UNK_POLICY
:
582 opol
.policy_refcnt
--;
588 npol
.policy_refcnt
++;
590 } else npol
.policy_refcnt
++;
592 /* set us up to use the new policy */
593 adb
.aux_attributes
|= KADM5_POLICY
;
595 adb
.policy
= strdup(entry
->policy
);
597 /* set pw_max_life based on new policy */
598 if (npol
.pw_max_life
) {
599 ret
= krb5_dbe_lookup_last_pwd_change(handle
->context
, &kdb
,
600 &(kdb
.pw_expiration
));
603 kdb
.pw_expiration
+= npol
.pw_max_life
;
605 kdb
.pw_expiration
= 0;
609 if ((mask
& KADM5_POLICY_CLR
) &&
610 (adb
.aux_attributes
& KADM5_POLICY
)) {
611 ret
= kadm5_get_policy(handle
->lhandle
, adb
.policy
, &opol
);
614 case KADM5_BAD_POLICY
:
615 case KADM5_UNK_POLICY
:
623 adb
.aux_attributes
&= ~KADM5_POLICY
;
624 kdb
.pw_expiration
= 0;
625 opol
.policy_refcnt
--;
633 if (((mask
& KADM5_POLICY
) || (mask
& KADM5_POLICY_CLR
)) &&
636 kadm5_modify_policy_internal(handle
->lhandle
, &opol
,
637 KADM5_REF_COUNT
))) ||
640 kadm5_modify_policy_internal(handle
->lhandle
, &npol
,
644 if ((mask
& KADM5_ATTRIBUTES
))
645 kdb
.attributes
= entry
->attributes
;
646 if ((mask
& KADM5_MAX_LIFE
))
647 kdb
.max_life
= entry
->max_life
;
648 if ((mask
& KADM5_PRINC_EXPIRE_TIME
))
649 kdb
.expiration
= entry
->princ_expire_time
;
650 if (mask
& KADM5_PW_EXPIRATION
)
651 kdb
.pw_expiration
= entry
->pw_expiration
;
652 if (mask
& KADM5_MAX_RLIFE
)
653 kdb
.max_renewable_life
= entry
->max_renewable_life
;
654 if (mask
& KADM5_FAIL_AUTH_COUNT
)
655 kdb
.fail_auth_count
= entry
->fail_auth_count
;
657 if((mask
& KADM5_KVNO
)) {
658 for (i
= 0; i
< kdb
.n_key_data
; i
++)
659 kdb
.key_data
[i
].key_data_kvno
= entry
->kvno
;
662 if (mask
& KADM5_TL_DATA
) {
665 /* may have to change the version number of the API. Updates the list with the given tl_data rather than over-writting */
667 for (tl
= entry
->tl_data
; tl
;
668 tl
= tl
->tl_data_next
)
670 ret
= krb5_dbe_update_tl_data(handle
->context
, &kdb
, tl
);
678 /* let the mask propagate to the database provider */
681 ret
= kdb_put_entry(handle
, &kdb
, &adb
);
687 ret2
= kadm5_free_policy_ent(handle
->lhandle
, &opol
);
688 ret
= ret
? ret
: ret2
;
691 ret2
= kadm5_free_policy_ent(handle
->lhandle
, &npol
);
692 ret
= ret
? ret
: ret2
;
694 kdb_free_entry(handle
, &kdb
, &adb
);
699 kadm5_rename_principal(void *server_handle
,
700 krb5_principal source
, krb5_principal target
)
703 osa_princ_ent_rec adb
;
705 kadm5_server_handle_t handle
= server_handle
;
707 CHECK_HANDLE(server_handle
);
709 krb5_clear_error_message(handle
->context
);
711 if (source
== NULL
|| target
== NULL
)
714 if ((ret
= kdb_get_entry(handle
, target
, &kdb
, &adb
)) == 0) {
715 kdb_free_entry(handle
, &kdb
, &adb
);
719 if ((ret
= kdb_get_entry(handle
, source
, &kdb
, &adb
)))
722 /* this is kinda gross, but unavoidable */
724 for (i
=0; i
<kdb
.n_key_data
; i
++) {
725 if ((kdb
.key_data
[i
].key_data_ver
== 1) ||
726 (kdb
.key_data
[i
].key_data_type
[1] == KRB5_KDB_SALTTYPE_NORMAL
)) {
727 ret
= KADM5_NO_RENAME_SALT
;
732 kadm5_free_principal(handle
->context
, kdb
.princ
);
733 ret
= kadm5_copy_principal(handle
->context
, target
, &kdb
.princ
);
735 kdb
.princ
= NULL
; /* so freeing the dbe doesn't lose */
739 if ((ret
= kdb_put_entry(handle
, &kdb
, &adb
)))
742 ret
= kdb_delete_entry(handle
, source
);
745 kdb_free_entry(handle
, &kdb
, &adb
);
750 kadm5_get_principal(void *server_handle
, krb5_principal principal
,
751 kadm5_principal_ent_t entry
,
755 osa_princ_ent_rec adb
;
756 krb5_error_code ret
= 0;
759 kadm5_server_handle_t handle
= server_handle
;
760 kadm5_principal_ent_rec entry_local
, *entry_orig
;
762 CHECK_HANDLE(server_handle
);
764 krb5_clear_error_message(handle
->context
);
767 * In version 1, all the defined fields are always returned.
768 * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
769 * filled with allocated memory.
771 if (handle
->api_version
== KADM5_API_VERSION_1
) {
772 mask
= KADM5_PRINCIPAL_NORMAL_MASK
;
774 entry
= &entry_local
;
779 memset((char *) entry
, 0, sizeof(*entry
));
781 if (principal
== NULL
)
784 if ((ret
= kdb_get_entry(handle
, principal
, &kdb
, &adb
)))
787 if ((mask
& KADM5_POLICY
) &&
788 adb
.policy
&& (adb
.aux_attributes
& KADM5_POLICY
)) {
789 if ((entry
->policy
= (char *) malloc(strlen(adb
.policy
) + 1)) == NULL
) {
793 strcpy(entry
->policy
, adb
.policy
);
796 if (mask
& KADM5_AUX_ATTRIBUTES
)
797 entry
->aux_attributes
= adb
.aux_attributes
;
799 if ((mask
& KADM5_PRINCIPAL
) &&
800 (ret
= krb5_copy_principal(handle
->context
, principal
,
801 &entry
->principal
))) {
805 if (mask
& KADM5_PRINC_EXPIRE_TIME
)
806 entry
->princ_expire_time
= kdb
.expiration
;
808 if ((mask
& KADM5_LAST_PWD_CHANGE
) &&
809 (ret
= krb5_dbe_lookup_last_pwd_change(handle
->context
, &kdb
,
810 &(entry
->last_pwd_change
)))) {
814 if (mask
& KADM5_PW_EXPIRATION
)
815 entry
->pw_expiration
= kdb
.pw_expiration
;
816 if (mask
& KADM5_MAX_LIFE
)
817 entry
->max_life
= kdb
.max_life
;
819 /* this is a little non-sensical because the function returns two */
820 /* values that must be checked separately against the mask */
821 if ((mask
& KADM5_MOD_NAME
) || (mask
& KADM5_MOD_TIME
)) {
822 ret
= krb5_dbe_lookup_mod_princ_data(handle
->context
, &kdb
,
829 if (! (mask
& KADM5_MOD_TIME
))
831 if (! (mask
& KADM5_MOD_NAME
)) {
832 krb5_free_principal(handle
->context
, entry
->principal
);
833 entry
->principal
= NULL
;
837 if (mask
& KADM5_ATTRIBUTES
)
838 entry
->attributes
= kdb
.attributes
;
840 if (mask
& KADM5_KVNO
)
841 for (entry
->kvno
= 0, i
=0; i
<kdb
.n_key_data
; i
++)
842 if (kdb
.key_data
[i
].key_data_kvno
> entry
->kvno
)
843 entry
->kvno
= kdb
.key_data
[i
].key_data_kvno
;
845 if (handle
->api_version
== KADM5_API_VERSION_2
)
848 /* XXX I'll be damned if I know how to deal with this one --marc */
853 * The new fields that only exist in version 2 start here
855 if (handle
->api_version
== KADM5_API_VERSION_2
) {
856 if (mask
& KADM5_MAX_RLIFE
)
857 entry
->max_renewable_life
= kdb
.max_renewable_life
;
858 if (mask
& KADM5_LAST_SUCCESS
)
859 entry
->last_success
= kdb
.last_success
;
860 if (mask
& KADM5_LAST_FAILED
)
861 entry
->last_failed
= kdb
.last_failed
;
862 if (mask
& KADM5_FAIL_AUTH_COUNT
)
863 entry
->fail_auth_count
= kdb
.fail_auth_count
;
864 if (mask
& KADM5_TL_DATA
) {
865 krb5_tl_data
*tl
, *tl2
;
867 entry
->tl_data
= NULL
;
871 if (tl
->tl_data_type
> 255) {
872 if ((tl2
= dup_tl_data(tl
)) == NULL
) {
876 tl2
->tl_data_next
= entry
->tl_data
;
877 entry
->tl_data
= tl2
;
881 tl
= tl
->tl_data_next
;
884 if (mask
& KADM5_KEY_DATA
) {
885 entry
->n_key_data
= kdb
.n_key_data
;
886 if(entry
->n_key_data
) {
887 entry
->key_data
= (krb5_key_data
*)
888 malloc(entry
->n_key_data
*sizeof(krb5_key_data
));
889 if (entry
->key_data
== NULL
) {
894 entry
->key_data
= NULL
;
896 for (i
= 0; i
< entry
->n_key_data
; i
++)
897 ret
= krb5_copy_key_data_contents(handle
->context
,
899 &entry
->key_data
[i
]);
906 * If KADM5_API_VERSION_1, we return an allocated structure, and
907 * we need to convert the new structure back into the format the
908 * caller is expecting.
910 if (handle
->api_version
== KADM5_API_VERSION_1
) {
911 kadm5_principal_ent_t_v1 newv1
;
913 newv1
= ((kadm5_principal_ent_t_v1
) calloc(1, sizeof(*newv1
)));
919 newv1
->principal
= entry
->principal
;
920 newv1
->princ_expire_time
= entry
->princ_expire_time
;
921 newv1
->last_pwd_change
= entry
->last_pwd_change
;
922 newv1
->pw_expiration
= entry
->pw_expiration
;
923 newv1
->max_life
= entry
->max_life
;
924 newv1
->mod_name
= entry
->mod_name
;
925 newv1
->mod_date
= entry
->mod_date
;
926 newv1
->attributes
= entry
->attributes
;
927 newv1
->kvno
= entry
->kvno
;
928 newv1
->mkvno
= entry
->mkvno
;
929 newv1
->policy
= entry
->policy
;
930 newv1
->aux_attributes
= entry
->aux_attributes
;
932 *((kadm5_principal_ent_t_v1
*) entry_orig
) = newv1
;
938 if (ret
&& entry
->principal
)
939 krb5_free_principal(handle
->context
, entry
->principal
);
940 kdb_free_entry(handle
, &kdb
, &adb
);
946 * Function: check_pw_reuse
948 * Purpose: Check if a key appears in a list of keys, in order to
949 * enforce password history.
953 * context (r) the krb5 context
954 * hist_keyblock (r) the key that hist_key_data is
956 * n_new_key_data (r) length of new_key_data
957 * new_key_data (r) keys to check against
958 * pw_hist_data, encrypted in hist_keyblock
959 * n_pw_hist_data (r) length of pw_hist_data
960 * pw_hist_data (r) passwords to check new_key_data against
963 * For each new_key in new_key_data:
964 * decrypt new_key with the master_keyblock
965 * for each password in pw_hist_data:
966 * for each hist_key in password:
967 * decrypt hist_key with hist_keyblock
968 * compare the new_key and hist_key
970 * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
971 * new_key_data is the same as a key in pw_hist_data, or 0.
974 check_pw_reuse(krb5_context context
,
975 krb5_keyblock
*master_keyblock
,
976 krb5_keyblock
*hist_keyblock
,
977 int n_new_key_data
, krb5_key_data
*new_key_data
,
978 unsigned int n_pw_hist_data
, osa_pw_hist_ent
*pw_hist_data
)
981 krb5_keyblock newkey
, histkey
;
984 for (x
= 0; x
< n_new_key_data
; x
++) {
985 ret
= krb5_dbekd_decrypt_key_data(context
,
991 for (y
= 0; y
< n_pw_hist_data
; y
++) {
992 for (z
= 0; z
< pw_hist_data
[y
].n_key_data
; z
++) {
993 ret
= krb5_dbekd_decrypt_key_data(context
,
995 &pw_hist_data
[y
].key_data
[z
],
1000 if ((newkey
.length
== histkey
.length
) &&
1001 (newkey
.enctype
== histkey
.enctype
) &&
1002 (memcmp(newkey
.contents
, histkey
.contents
,
1003 histkey
.length
) == 0)) {
1004 krb5_free_keyblock_contents(context
, &histkey
);
1005 krb5_free_keyblock_contents(context
, &newkey
);
1007 return(KADM5_PASS_REUSE
);
1009 krb5_free_keyblock_contents(context
, &histkey
);
1012 krb5_free_keyblock_contents(context
, &newkey
);
1019 * Function: create_history_entry
1021 * Purpose: Creates a password history entry from an array of
1026 * context (r) krb5_context to use
1027 * master_keyblcok (r) master key block
1028 * n_key_data (r) number of elements in key_data
1029 * key_data (r) keys to add to the history entry
1030 * hist (w) history entry to fill in
1034 * hist->key_data is allocated to store n_key_data key_datas. Each
1035 * element of key_data is decrypted with master_keyblock, re-encrypted
1036 * in hist_key, and added to hist->key_data. hist->n_key_data is
1037 * set to n_key_data.
1040 int create_history_entry(krb5_context context
,
1041 krb5_keyblock
*master_keyblock
, int n_key_data
,
1042 krb5_key_data
*key_data
, osa_pw_hist_ent
*hist
)
1048 hist
->key_data
= (krb5_key_data
*)malloc(n_key_data
*sizeof(krb5_key_data
));
1049 if (hist
->key_data
== NULL
)
1051 memset(hist
->key_data
, 0, n_key_data
*sizeof(krb5_key_data
));
1053 for (i
= 0; i
< n_key_data
; i
++) {
1054 ret
= krb5_dbekd_decrypt_key_data(context
,
1061 ret
= krb5_dbekd_encrypt_key_data(context
, &hist_key
,
1063 key_data
[i
].key_data_kvno
,
1064 &hist
->key_data
[i
]);
1068 krb5_free_keyblock_contents(context
, &key
);
1069 /* krb5_free_keysalt(context, &salt); */
1072 hist
->n_key_data
= n_key_data
;
1077 void free_history_entry(krb5_context context
, osa_pw_hist_ent
*hist
)
1081 for (i
= 0; i
< hist
->n_key_data
; i
++)
1082 krb5_free_key_data_contents(context
, &hist
->key_data
[i
]);
1083 free(hist
->key_data
);
1087 * Function: add_to_history
1089 * Purpose: Adds a password to a principal's password history.
1093 * context (r) krb5_context to use
1094 * adb (r/w) admin principal entry to add keys to
1095 * pol (r) adb's policy
1096 * pw (r) keys for the password to add to adb's key history
1100 * add_to_history adds a single password to adb's password history.
1101 * pw contains n_key_data keys in its key_data, in storage should be
1102 * allocated but not freed by the caller (XXX blech!).
1104 * This function maintains adb->old_keys as a circular queue. It
1105 * starts empty, and grows each time this function is called until it
1106 * is pol->pw_history_num items long. adb->old_key_len holds the
1107 * number of allocated entries in the array, and must therefore be [0,
1108 * pol->pw_history_num). adb->old_key_next is the index into the
1109 * array where the next element should be written, and must be [0,
1110 * adb->old_key_len).
1112 #define KADM_MOD(x) (x + adb->old_key_next) % adb->old_key_len
1113 static kadm5_ret_t
add_to_history(krb5_context context
,
1114 osa_princ_ent_t adb
,
1115 kadm5_policy_ent_t pol
,
1116 osa_pw_hist_ent
*pw
)
1118 osa_pw_hist_ent
*histp
;
1120 unsigned int i
, knext
, nkeys
;
1122 nhist
= pol
->pw_history_num
;
1123 /* A history of 1 means just check the current password */
1127 nkeys
= adb
->old_key_len
;
1128 knext
= adb
->old_key_next
;
1129 /* resize the adb->old_keys array if necessary */
1130 if (nkeys
+ 1 < nhist
) {
1131 if (adb
->old_keys
== NULL
) {
1132 adb
->old_keys
= (osa_pw_hist_ent
*)
1133 malloc((nkeys
+ 1) * sizeof (osa_pw_hist_ent
));
1135 adb
->old_keys
= (osa_pw_hist_ent
*)
1136 reallocarray(adb
->old_keys
, (nkeys
+ 1),
1137 sizeof(osa_pw_hist_ent
));
1139 if (adb
->old_keys
== NULL
)
1142 memset(&adb
->old_keys
[nkeys
], 0, sizeof(osa_pw_hist_ent
));
1143 nkeys
= ++adb
->old_key_len
;
1145 * To avoid losing old keys, shift forward each entry after
1148 for (i
= nkeys
- 1; i
> knext
; i
--) {
1149 adb
->old_keys
[i
] = adb
->old_keys
[i
- 1];
1151 memset(&adb
->old_keys
[knext
], 0, sizeof(osa_pw_hist_ent
));
1152 } else if (nkeys
+ 1 > nhist
) {
1154 * The policy must have changed! Shrink the array.
1155 * Can't simply realloc() down, since it might be wrapped.
1156 * To understand the arithmetic below, note that we are
1157 * copying into new positions 0 .. N-1 from old positions
1158 * old_key_next-N .. old_key_next-1, modulo old_key_len,
1159 * where N = pw_history_num - 1 is the length of the
1160 * shortened list. Matt Crawford, FNAL
1163 * M = adb->old_key_len, N = pol->pw_history_num - 1
1165 * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M]
1170 tmp
= (osa_pw_hist_ent
*)
1171 malloc((nhist
- 1) * sizeof (osa_pw_hist_ent
));
1174 for (i
= 0; i
< nhist
- 1; i
++) {
1176 * Add nkeys once before taking remainder to avoid
1179 j
= (i
+ nkeys
+ knext
- (nhist
- 1)) % nkeys
;
1180 tmp
[i
] = adb
->old_keys
[j
];
1182 /* Now free the ones we don't keep (the oldest ones) */
1183 for (i
= 0; i
< nkeys
- (nhist
- 1); i
++) {
1184 j
= (i
+ nkeys
+ knext
) % nkeys
;
1185 histp
= &adb
->old_keys
[j
];
1186 for (j
= 0; j
< histp
->n_key_data
; j
++) {
1187 krb5_free_key_data_contents(context
, &histp
->key_data
[j
]);
1189 free(histp
->key_data
);
1191 free((void *)adb
->old_keys
);
1192 adb
->old_keys
= tmp
;
1193 nkeys
= adb
->old_key_len
= nhist
- 1;
1194 knext
= adb
->old_key_next
= 0;
1198 * If nhist decreased since the last password change, and nkeys+1
1199 * is less than the previous nhist, it is possible for knext to
1200 * index into unallocated space. This condition would not be
1201 * caught by the resizing code above.
1203 if (knext
+ 1 > nkeys
)
1204 knext
= adb
->old_key_next
= 0;
1205 /* free the old pw history entry if it contains data */
1206 histp
= &adb
->old_keys
[knext
];
1207 for (i
= 0; i
< histp
->n_key_data
; i
++)
1208 krb5_free_key_data_contents(context
, &histp
->key_data
[i
]);
1209 free(histp
->key_data
);
1211 /* store the new entry */
1212 adb
->old_keys
[knext
] = *pw
;
1214 /* update the next pointer */
1215 if (++adb
->old_key_next
== nhist
- 1)
1216 adb
->old_key_next
= 0;
1222 #ifdef USE_PASSWORD_SERVER
1223 /* FIXME: don't use global variable for this */
1224 krb5_boolean use_password_server
= 0;
1227 kadm5_use_password_server (void)
1229 return use_password_server
;
1233 kadm5_set_use_password_server (void)
1235 use_password_server
= 1;
1239 #ifdef USE_PASSWORD_SERVER
1242 * kadm5_launch_task () runs a program (task_path) to synchronize the
1243 * Apple password server with the Kerberos database. Password server
1244 * programs can receive arguments on the command line (task_argv)
1245 * and a block of data via stdin (data_buffer).
1247 * Because a failure to communicate with the tool results in the
1248 * password server falling out of sync with the database,
1249 * kadm5_launch_task() always fails if it can't talk to the tool.
1253 kadm5_launch_task (krb5_context context
,
1254 const char *task_path
, char * const task_argv
[],
1255 const char *data_buffer
)
1257 kadm5_ret_t ret
= 0;
1260 if (data_buffer
!= NULL
) {
1261 ret
= pipe (data_pipe
);
1262 if (ret
) { ret
= errno
; }
1266 pid_t pid
= fork ();
1269 } else if (pid
== 0) {
1272 if (data_buffer
!= NULL
) {
1273 if (dup2 (data_pipe
[0], STDIN_FILENO
) == -1) {
1277 close (data_pipe
[0]);
1280 close (data_pipe
[1]);
1282 execv (task_path
, task_argv
);
1284 _exit (1); /* Fail if execv fails */
1289 if (data_buffer
!= NULL
) {
1290 /* Write out the buffer to the child */
1291 if (krb5_net_write (context
, data_pipe
[1],
1292 data_buffer
, strlen (data_buffer
)) < 0) {
1293 /* kill the child to make sure waitpid() won't hang later */
1295 kill (pid
, SIGKILL
);
1299 close (data_buffer
[0]);
1300 close (data_buffer
[1]);
1302 waitpid (pid
, &status
, 0);
1305 if (WIFEXITED (status
)) {
1306 /* child read password and exited. Check the return value. */
1307 if ((WEXITSTATUS (status
) != 0) && (WEXITSTATUS (status
) != 252)) {
1308 ret
= KRB5KDC_ERR_POLICY
; /* password change rejected */
1311 /* child read password but crashed or was killed */
1312 ret
= KRB5KRB_ERR_GENERIC
; /* FIXME: better error */
1324 kadm5_chpass_principal(void *server_handle
,
1325 krb5_principal principal
, char *password
)
1328 kadm5_chpass_principal_3(server_handle
, principal
, FALSE
,
1333 kadm5_chpass_principal_3(void *server_handle
,
1334 krb5_principal principal
, krb5_boolean keepold
,
1335 int n_ks_tuple
, krb5_key_salt_tuple
*ks_tuple
,
1339 kadm5_policy_ent_rec pol
;
1340 osa_princ_ent_rec adb
;
1341 krb5_db_entry kdb
, kdb_save
;
1342 int ret
, ret2
, last_pwd
, hist_added
;
1344 kadm5_server_handle_t handle
= server_handle
;
1345 osa_pw_hist_ent hist
;
1347 CHECK_HANDLE(server_handle
);
1349 /* Solaris Kerberos - kadm5_check_min_life checks for null principal. */
1350 ret
= kadm5_check_min_life(server_handle
,principal
,NULL
,0);
1353 krb5_clear_error_message(handle
->context
);
1356 memset(&hist
, 0, sizeof(hist
));
1358 if (principal
== NULL
|| password
== NULL
)
1360 if ((krb5_principal_compare(handle
->context
,
1361 principal
, hist_princ
)) == TRUE
)
1362 return KADM5_PROTECT_PRINCIPAL
;
1364 if ((ret
= kdb_get_entry(handle
, principal
, &kdb
, &adb
)))
1367 /* we are going to need the current keys after the new keys are set */
1368 if ((ret
= kdb_get_entry(handle
, principal
, &kdb_save
, NULL
))) {
1369 kdb_free_entry(handle
, &kdb
, &adb
);
1373 if ((adb
.aux_attributes
& KADM5_POLICY
)) {
1374 if ((ret
= kadm5_get_policy(handle
->lhandle
, adb
.policy
, &pol
)))
1379 if ((ret
= passwd_check(handle
, password
, adb
.aux_attributes
&
1380 KADM5_POLICY
, &pol
, principal
)))
1383 ret
= krb5_dbe_cpw(handle
->context
, &handle
->master_keyblock
,
1384 n_ks_tuple
?ks_tuple
:handle
->params
.keysalts
,
1385 n_ks_tuple
?n_ks_tuple
:handle
->params
.num_keysalts
,
1386 password
, 0 /* increment kvno */,
1391 kdb
.attributes
&= ~KRB5_KDB_REQUIRES_PWCHANGE
;
1393 ret
= krb5_timeofday(handle
->context
, &now
);
1397 if ((adb
.aux_attributes
& KADM5_POLICY
)) {
1398 /* the policy was loaded before */
1400 ret
= krb5_dbe_lookup_last_pwd_change(handle
->context
,
1407 * The spec says this check is overridden if the caller has
1408 * modify privilege. The admin server therefore makes this
1409 * check itself (in chpass_principal_wrapper, misc.c). A
1410 * local caller implicitly has all authorization bits.
1412 if ((now
- last_pwd
) < pol
.pw_min_life
&&
1413 !(kdb
.attributes
& KRB5_KDB_REQUIRES_PWCHANGE
)) {
1414 ret
= KADM5_PASS_TOOSOON
;
1419 ret
= create_history_entry(handle
->context
,
1420 &handle
->master_keyblock
, kdb_save
.n_key_data
,
1421 kdb_save
.key_data
, &hist
);
1425 ret
= check_pw_reuse(handle
->context
,
1426 &handle
->master_keyblock
,
1428 kdb
.n_key_data
, kdb
.key_data
,
1433 if (pol
.pw_history_num
> 1) {
1434 if (adb
.admin_history_kvno
!= hist_kvno
) {
1435 ret
= KADM5_BAD_HIST_KEY
;
1439 ret
= check_pw_reuse(handle
->context
,
1440 &handle
->master_keyblock
,
1442 kdb
.n_key_data
, kdb
.key_data
,
1443 adb
.old_key_len
, adb
.old_keys
);
1447 ret
= add_to_history(handle
->context
, &adb
, &pol
, &hist
);
1453 if (pol
.pw_max_life
)
1454 kdb
.pw_expiration
= now
+ pol
.pw_max_life
;
1456 kdb
.pw_expiration
= 0;
1458 kdb
.pw_expiration
= 0;
1461 #ifdef USE_PASSWORD_SERVER
1462 if (kadm5_use_password_server () &&
1463 (krb5_princ_size (handle
->context
, principal
) == 1)) {
1464 krb5_data
*princ
= krb5_princ_component (handle
->context
, principal
, 0);
1465 const char *path
= "/usr/sbin/mkpassdb";
1466 char *argv
[] = { "mkpassdb", "-setpassword", NULL
, NULL
};
1467 char *pstring
= NULL
;
1469 int pwlen
= strlen (password
);
1471 if (pwlen
> 254) pwlen
= 254;
1472 strncpy (pwbuf
, password
, pwlen
);
1473 pwbuf
[pwlen
] = '\n';
1474 pwbuf
[pwlen
+ 1] = '\0';
1477 pstring
= malloc ((princ
->length
+ 1) * sizeof (char));
1478 if (pstring
== NULL
) { ret
= errno
; }
1482 memcpy (pstring
, princ
->data
, princ
->length
);
1483 pstring
[princ
->length
] = '\0';
1486 ret
= kadm5_launch_task (handle
->context
, path
, argv
, pwbuf
);
1496 ret
= krb5_dbe_update_last_pwd_change(handle
->context
, &kdb
, now
);
1500 /* key data and attributes changed, let the database provider know */
1501 /* Solaris Kerberos: adding support for key history in LDAP KDB */
1502 if (hist_added
== 1)
1503 kdb
.mask
= KADM5_KEY_DATA
| KADM5_ATTRIBUTES
| KADM5_KEY_HIST
1504 /* | KADM5_CPW_FUNCTION */;
1506 kdb
.mask
= KADM5_KEY_DATA
| KADM5_ATTRIBUTES
/* | KADM5_CPW_FUNCTION */;
1508 if ((ret
= kdb_put_entry(handle
, &kdb
, &adb
)))
1513 if (!hist_added
&& hist
.key_data
)
1514 free_history_entry(handle
->context
, &hist
);
1515 kdb_free_entry(handle
, &kdb
, &adb
);
1516 kdb_free_entry(handle
, &kdb_save
, NULL
);
1517 krb5_db_free_principal(handle
->context
, &kdb
, 1);
1519 if (have_pol
&& (ret2
= kadm5_free_policy_ent(handle
->lhandle
, &pol
))
1527 kadm5_randkey_principal(void *server_handle
,
1528 krb5_principal principal
,
1529 krb5_keyblock
**keyblocks
,
1532 /* Solaris Kerberos: */
1533 krb5_key_salt_tuple keysalts
[2];
1536 * Anyone calling this routine is forced to use only DES
1537 * enctypes to be compatible with earlier releases that
1538 * did not support stronger crypto.
1540 * S10 (and later) kadmin clients will not use this API,
1541 * so we can assume the request is from an older version.
1543 keysalts
[0].ks_enctype
= ENCTYPE_DES_CBC_MD5
;
1544 keysalts
[0].ks_salttype
= KRB5_KDB_SALTTYPE_NORMAL
;
1545 keysalts
[1].ks_enctype
= ENCTYPE_DES_CBC_CRC
;
1546 keysalts
[1].ks_salttype
= KRB5_KDB_SALTTYPE_NORMAL
;
1548 return (kadm5_randkey_principal_3(server_handle
, principal
,
1549 FALSE
, 2, keysalts
, keyblocks
, n_keys
));
1552 kadm5_randkey_principal_3(void *server_handle
,
1553 krb5_principal principal
,
1554 krb5_boolean keepold
,
1555 int n_ks_tuple
, krb5_key_salt_tuple
*ks_tuple
,
1556 krb5_keyblock
**keyblocks
,
1560 osa_princ_ent_rec adb
;
1562 kadm5_policy_ent_rec pol
;
1563 krb5_key_data
*key_data
;
1564 int ret
, last_pwd
, have_pol
= 0;
1565 kadm5_server_handle_t handle
= server_handle
;
1570 CHECK_HANDLE(server_handle
);
1572 krb5_clear_error_message(handle
->context
);
1574 if (principal
== NULL
)
1576 if (hist_princ
&& /* this will be NULL when initializing the databse */
1577 ((krb5_principal_compare(handle
->context
,
1578 principal
, hist_princ
)) == TRUE
))
1579 return KADM5_PROTECT_PRINCIPAL
;
1581 if ((ret
= kdb_get_entry(handle
, principal
, &kdb
, &adb
)))
1584 ret
= krb5_dbe_crk(handle
->context
, &handle
->master_keyblock
,
1585 n_ks_tuple
?ks_tuple
:handle
->params
.keysalts
,
1586 n_ks_tuple
?n_ks_tuple
:handle
->params
.num_keysalts
,
1592 kdb
.attributes
&= ~KRB5_KDB_REQUIRES_PWCHANGE
;
1594 ret
= krb5_timeofday(handle
->context
, &now
);
1598 if ((adb
.aux_attributes
& KADM5_POLICY
)) {
1599 if ((ret
= kadm5_get_policy(handle
->lhandle
, adb
.policy
,
1604 ret
= krb5_dbe_lookup_last_pwd_change(handle
->context
,
1611 * The spec says this check is overridden if the caller has
1612 * modify privilege. The admin server therefore makes this
1613 * check itself (in chpass_principal_wrapper, misc.c). A
1614 * local caller implicitly has all authorization bits.
1616 if((now
- last_pwd
) < pol
.pw_min_life
&&
1617 !(kdb
.attributes
& KRB5_KDB_REQUIRES_PWCHANGE
)) {
1618 ret
= KADM5_PASS_TOOSOON
;
1623 if(pol
.pw_history_num
> 1) {
1624 if(adb
.admin_history_kvno
!= hist_kvno
) {
1625 ret
= KADM5_BAD_HIST_KEY
;
1629 ret
= check_pw_reuse(handle
->context
,
1630 &handle
->master_keyblock
,
1632 kdb
.n_key_data
, kdb
.key_data
,
1633 adb
.old_key_len
, adb
.old_keys
);
1637 if (pol
.pw_max_life
)
1638 kdb
.pw_expiration
= now
+ pol
.pw_max_life
;
1640 kdb
.pw_expiration
= 0;
1642 kdb
.pw_expiration
= 0;
1645 ret
= krb5_dbe_update_last_pwd_change(handle
->context
, &kdb
, now
);
1650 if (handle
->api_version
== KADM5_API_VERSION_1
) {
1651 /* Version 1 clients will expect to see a DES_CRC enctype. */
1652 ret
= krb5_dbe_find_enctype(handle
->context
, &kdb
,
1653 ENCTYPE_DES_CBC_CRC
,
1658 ret
= decrypt_key_data(handle
->context
,
1659 &handle
->master_keyblock
, 1, key_data
,
1664 ret
= decrypt_key_data(handle
->context
,
1665 &handle
->master_keyblock
,
1666 kdb
.n_key_data
, kdb
.key_data
,
1673 /* key data changed, let the database provider know */
1674 kdb
.mask
= KADM5_KEY_DATA
/* | KADM5_RANDKEY_USED */;
1676 if ((ret
= kdb_put_entry(handle
, &kdb
, &adb
)))
1681 kdb_free_entry(handle
, &kdb
, &adb
);
1683 kadm5_free_policy_ent(handle
->lhandle
, &pol
);
1688 #if 0 /* Solaris Kerberos */
1690 * kadm5_setv4key_principal:
1692 * Set only ONE key of the principal, removing all others. This key
1693 * must have the DES_CBC_CRC enctype and is entered as having the
1694 * krb4 salttype. This is to enable things like kadmind4 to work.
1697 kadm5_setv4key_principal(void *server_handle
,
1698 krb5_principal principal
,
1699 krb5_keyblock
*keyblock
)
1702 osa_princ_ent_rec adb
;
1704 kadm5_policy_ent_rec pol
;
1705 krb5_keysalt keysalt
;
1706 int i
, k
, kvno
, ret
, have_pol
= 0;
1710 kadm5_server_handle_t handle
= server_handle
;
1711 krb5_key_data tmp_key_data
;
1713 memset( &tmp_key_data
, 0, sizeof(tmp_key_data
));
1715 CHECK_HANDLE(server_handle
);
1717 krb5_clear_error_message(handle
->context
);
1719 if (principal
== NULL
|| keyblock
== NULL
)
1721 if (hist_princ
&& /* this will be NULL when initializing the databse */
1722 ((krb5_principal_compare(handle
->context
,
1723 principal
, hist_princ
)) == TRUE
))
1724 return KADM5_PROTECT_PRINCIPAL
;
1726 if (keyblock
->enctype
!= ENCTYPE_DES_CBC_CRC
)
1727 return KADM5_SETV4KEY_INVAL_ENCTYPE
;
1729 if ((ret
= kdb_get_entry(handle
, principal
, &kdb
, &adb
)))
1732 for (kvno
= 0, i
=0; i
<kdb
.n_key_data
; i
++)
1733 if (kdb
.key_data
[i
].key_data_kvno
> kvno
)
1734 kvno
= kdb
.key_data
[i
].key_data_kvno
;
1736 if (kdb
.key_data
!= NULL
)
1737 cleanup_key_data(handle
->context
, kdb
.n_key_data
, kdb
.key_data
);
1739 kdb
.key_data
= (krb5_key_data
*)krb5_db_alloc(handle
->context
, NULL
, sizeof(krb5_key_data
));
1740 if (kdb
.key_data
== NULL
)
1742 memset(kdb
.key_data
, 0, sizeof(krb5_key_data
));
1744 keysalt
.type
= KRB5_KDB_SALTTYPE_V4
;
1745 /* XXX data.magic? */
1746 keysalt
.data
.length
= 0;
1747 keysalt
.data
.data
= NULL
;
1749 /* use tmp_key_data as temporary location and reallocate later */
1750 ret
= krb5_dbekd_encrypt_key_data(handle
->context
, &master_keyblock
,
1751 keyblock
, &keysalt
, kvno
+ 1,
1757 for (k
= 0; k
< tmp_key_data
.key_data_ver
; k
++) {
1758 kdb
.key_data
->key_data_type
[k
] = tmp_key_data
.key_data_type
[k
];
1759 kdb
.key_data
->key_data_length
[k
] = tmp_key_data
.key_data_length
[k
];
1760 if (tmp_key_data
.key_data_contents
[k
]) {
1761 kdb
.key_data
->key_data_contents
[k
] = krb5_db_alloc(handle
->context
, NULL
, tmp_key_data
.key_data_length
[k
]);
1762 if (kdb
.key_data
->key_data_contents
[k
] == NULL
) {
1763 cleanup_key_data(handle
->context
, kdb
.n_key_data
, kdb
.key_data
);
1764 kdb
.key_data
= NULL
;
1769 memcpy (kdb
.key_data
->key_data_contents
[k
], tmp_key_data
.key_data_contents
[k
], tmp_key_data
.key_data_length
[k
]);
1771 memset (tmp_key_data
.key_data_contents
[k
], 0, tmp_key_data
.key_data_length
[k
]);
1772 free (tmp_key_data
.key_data_contents
[k
]);
1773 tmp_key_data
.key_data_contents
[k
] = NULL
;
1779 kdb
.attributes
&= ~KRB5_KDB_REQUIRES_PWCHANGE
;
1781 ret
= krb5_timeofday(handle
->context
, &now
);
1785 if ((adb
.aux_attributes
& KADM5_POLICY
)) {
1786 if ((ret
= kadm5_get_policy(handle
->lhandle
, adb
.policy
,
1793 * The spec says this check is overridden if the caller has
1794 * modify privilege. The admin server therefore makes this
1795 * check itself (in chpass_principal_wrapper, misc.c). A
1796 * local caller implicitly has all authorization bits.
1798 if (ret
= krb5_dbe_lookup_last_pwd_change(handle
->context
,
1801 if((now
- last_pwd
) < pol
.pw_min_life
&&
1802 !(kdb
.attributes
& KRB5_KDB_REQUIRES_PWCHANGE
)) {
1803 ret
= KADM5_PASS_TOOSOON
;
1809 * Should we be checking/updating pw history here?
1811 if(pol
.pw_history_num
> 1) {
1812 if(adb
.admin_history_kvno
!= hist_kvno
) {
1813 ret
= KADM5_BAD_HIST_KEY
;
1817 if (ret
= check_pw_reuse(handle
->context
,
1819 kdb
.n_key_data
, kdb
.key_data
,
1820 adb
.old_key_len
, adb
.old_keys
))
1825 if (pol
.pw_max_life
)
1826 kdb
.pw_expiration
= now
+ pol
.pw_max_life
;
1828 kdb
.pw_expiration
= 0;
1830 kdb
.pw_expiration
= 0;
1833 ret
= krb5_dbe_update_last_pwd_change(handle
->context
, &kdb
, now
);
1837 if ((ret
= kdb_put_entry(handle
, &kdb
, &adb
)))
1842 for (i
= 0; i
< tmp_key_data
.key_data_ver
; i
++) {
1843 if (tmp_key_data
.key_data_contents
[i
]) {
1844 memset (tmp_key_data
.key_data_contents
[i
], 0, tmp_key_data
.key_data_length
[i
]);
1845 free (tmp_key_data
.key_data_contents
[i
]);
1849 kdb_free_entry(handle
, &kdb
, &adb
);
1851 kadm5_free_policy_ent(handle
->lhandle
, &pol
);
1858 kadm5_setkey_principal(void *server_handle
,
1859 krb5_principal principal
,
1860 krb5_keyblock
*keyblocks
,
1864 kadm5_setkey_principal_3(server_handle
, principal
,
1870 kadm5_setkey_principal_3(void *server_handle
,
1871 krb5_principal principal
,
1872 krb5_boolean keepold
,
1873 int n_ks_tuple
, krb5_key_salt_tuple
*ks_tuple
,
1874 krb5_keyblock
*keyblocks
,
1878 osa_princ_ent_rec adb
;
1880 kadm5_policy_ent_rec pol
;
1881 krb5_key_data
*old_key_data
;
1883 int i
, j
, k
, kvno
, ret
, have_pol
= 0;
1887 kadm5_server_handle_t handle
= server_handle
;
1888 krb5_boolean similar
;
1889 krb5_keysalt keysalt
;
1890 krb5_key_data tmp_key_data
;
1891 krb5_key_data
*tptr
;
1893 CHECK_HANDLE(server_handle
);
1895 krb5_clear_error_message(handle
->context
);
1897 if (principal
== NULL
|| keyblocks
== NULL
)
1899 if (hist_princ
&& /* this will be NULL when initializing the databse */
1900 ((krb5_principal_compare(handle
->context
,
1901 principal
, hist_princ
)) == TRUE
))
1902 return KADM5_PROTECT_PRINCIPAL
;
1904 for (i
= 0; i
< n_keys
; i
++) {
1905 for (j
= i
+1; j
< n_keys
; j
++) {
1906 if ((ret
= krb5_c_enctype_compare(handle
->context
,
1907 keyblocks
[i
].enctype
,
1908 keyblocks
[j
].enctype
,
1913 if (ks_tuple
[i
].ks_salttype
== ks_tuple
[j
].ks_salttype
)
1914 return KADM5_SETKEY_DUP_ENCTYPES
;
1916 return KADM5_SETKEY_DUP_ENCTYPES
;
1921 if (n_ks_tuple
&& n_ks_tuple
!= n_keys
)
1922 return KADM5_SETKEY3_ETYPE_MISMATCH
;
1924 if ((ret
= kdb_get_entry(handle
, principal
, &kdb
, &adb
)))
1927 for (kvno
= 0, i
=0; i
<kdb
.n_key_data
; i
++)
1928 if (kdb
.key_data
[i
].key_data_kvno
> kvno
)
1929 kvno
= kdb
.key_data
[i
].key_data_kvno
;
1932 old_key_data
= kdb
.key_data
;
1933 n_old_keys
= kdb
.n_key_data
;
1935 if (kdb
.key_data
!= NULL
)
1936 cleanup_key_data(handle
->context
, kdb
.n_key_data
, kdb
.key_data
);
1938 old_key_data
= NULL
;
1941 kdb
.key_data
= (krb5_key_data
*)krb5_db_alloc(handle
->context
, NULL
, (n_keys
+n_old_keys
)
1942 *sizeof(krb5_key_data
));
1943 if (kdb
.key_data
== NULL
) {
1948 memset(kdb
.key_data
, 0, (n_keys
+n_old_keys
)*sizeof(krb5_key_data
));
1951 for (i
= 0; i
< n_keys
; i
++) {
1953 keysalt
.type
= ks_tuple
[i
].ks_salttype
;
1954 keysalt
.data
.length
= 0;
1955 keysalt
.data
.data
= NULL
;
1956 if (ks_tuple
[i
].ks_enctype
!= keyblocks
[i
].enctype
) {
1957 ret
= KADM5_SETKEY3_ETYPE_MISMATCH
;
1961 memset (&tmp_key_data
, 0, sizeof(tmp_key_data
));
1963 ret
= krb5_dbekd_encrypt_key_data(handle
->context
,
1964 &handle
->master_keyblock
,
1966 n_ks_tuple
? &keysalt
: NULL
,
1972 tptr
= &kdb
.key_data
[i
];
1973 for (k
= 0; k
< tmp_key_data
.key_data_ver
; k
++) {
1974 tptr
->key_data_type
[k
] = tmp_key_data
.key_data_type
[k
];
1975 tptr
->key_data_length
[k
] = tmp_key_data
.key_data_length
[k
];
1976 if (tmp_key_data
.key_data_contents
[k
]) {
1977 tptr
->key_data_contents
[k
] = krb5_db_alloc(handle
->context
, NULL
, tmp_key_data
.key_data_length
[k
]);
1978 if (tptr
->key_data_contents
[k
] == NULL
) {
1980 for (i1
= k
; i1
< tmp_key_data
.key_data_ver
; i1
++) {
1981 if (tmp_key_data
.key_data_contents
[i1
]) {
1982 memset (tmp_key_data
.key_data_contents
[i1
], 0, tmp_key_data
.key_data_length
[i1
]);
1983 free (tmp_key_data
.key_data_contents
[i1
]);
1990 memcpy (tptr
->key_data_contents
[k
], tmp_key_data
.key_data_contents
[k
], tmp_key_data
.key_data_length
[k
]);
1992 memset (tmp_key_data
.key_data_contents
[k
], 0, tmp_key_data
.key_data_length
[k
]);
1993 free (tmp_key_data
.key_data_contents
[k
]);
1994 tmp_key_data
.key_data_contents
[k
] = NULL
;
2000 /* copy old key data if necessary */
2001 for (i
= 0; i
< n_old_keys
; i
++) {
2002 kdb
.key_data
[i
+n_keys
] = old_key_data
[i
];
2003 memset(&old_key_data
[i
], 0, sizeof (krb5_key_data
));
2008 krb5_db_free(handle
->context
, old_key_data
);
2010 /* assert(kdb.n_key_data == n_keys + n_old_keys) */
2011 kdb
.attributes
&= ~KRB5_KDB_REQUIRES_PWCHANGE
;
2013 if ((ret
= krb5_timeofday(handle
->context
, &now
)))
2016 if ((adb
.aux_attributes
& KADM5_POLICY
)) {
2017 if ((ret
= kadm5_get_policy(handle
->lhandle
, adb
.policy
,
2024 * The spec says this check is overridden if the caller has
2025 * modify privilege. The admin server therefore makes this
2026 * check itself (in chpass_principal_wrapper, misc.c). A
2027 * local caller implicitly has all authorization bits.
2029 if (ret
= krb5_dbe_lookup_last_pwd_change(handle
->context
,
2032 if((now
- last_pwd
) < pol
.pw_min_life
&&
2033 !(kdb
.attributes
& KRB5_KDB_REQUIRES_PWCHANGE
)) {
2034 ret
= KADM5_PASS_TOOSOON
;
2040 * Should we be checking/updating pw history here?
2042 if (pol
.pw_history_num
> 1) {
2043 if(adb
.admin_history_kvno
!= hist_kvno
) {
2044 ret
= KADM5_BAD_HIST_KEY
;
2048 if (ret
= check_pw_reuse(handle
->context
,
2049 &handle
->master_keyblock
,
2051 kdb
.n_key_data
, kdb
.key_data
,
2052 adb
.old_key_len
, adb
.old_keys
))
2057 if (pol
.pw_max_life
)
2058 kdb
.pw_expiration
= now
+ pol
.pw_max_life
;
2060 kdb
.pw_expiration
= 0;
2062 kdb
.pw_expiration
= 0;
2065 if ((ret
= krb5_dbe_update_last_pwd_change(handle
->context
, &kdb
, now
)))
2068 if ((ret
= kdb_put_entry(handle
, &kdb
, &adb
)))
2073 kdb_free_entry(handle
, &kdb
, &adb
);
2075 kadm5_free_policy_ent(handle
->lhandle
, &pol
);
2081 * Allocate an array of n_key_data krb5_keyblocks, fill in each
2082 * element with the results of decrypting the nth key in key_data with
2083 * master_keyblock, and if n_keys is not NULL fill it in with the
2084 * number of keys decrypted.
2086 static int decrypt_key_data(krb5_context context
,
2087 krb5_keyblock
*master_keyblock
,
2088 int n_key_data
, krb5_key_data
*key_data
,
2089 krb5_keyblock
**keyblocks
, int *n_keys
)
2091 krb5_keyblock
*keys
;
2094 keys
= (krb5_keyblock
*) malloc(n_key_data
*sizeof(krb5_keyblock
));
2097 memset((char *) keys
, 0, n_key_data
*sizeof(krb5_keyblock
));
2099 for (i
= 0; i
< n_key_data
; i
++) {
2100 ret
= krb5_dbekd_decrypt_key_data(context
,
2105 for (; i
>= 0; i
--) {
2106 if (keys
[i
].contents
) {
2107 memset (keys
[i
].contents
, 0, keys
[i
].length
);
2108 free( keys
[i
].contents
);
2112 memset((char *) keys
, 0, n_key_data
*sizeof(krb5_keyblock
));
2120 *n_keys
= n_key_data
;
2126 * Function: kadm5_decrypt_key
2128 * Purpose: Retrieves and decrypts a principal key.
2132 * server_handle (r) kadm5 handle
2133 * entry (r) principal retrieved with kadm5_get_principal
2134 * ktype (r) enctype to search for, or -1 to ignore
2135 * stype (r) salt type to search for, or -1 to ignore
2136 * kvno (r) kvno to search for, -1 for max, 0 for max
2137 * only if it also matches ktype and stype
2138 * keyblock (w) keyblock to fill in
2139 * keysalt (w) keysalt to fill in, or NULL
2140 * kvnop (w) kvno to fill in, or NULL
2142 * Effects: Searches the key_data array of entry, which must have been
2143 * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
2144 * find a key with a specified enctype, salt type, and kvno in a
2145 * principal entry. If not found, return ENOENT. Otherwise, decrypt
2146 * it with the master key, and return the key in keyblock, the salt
2147 * in salttype, and the key version number in kvno.
2149 * If ktype or stype is -1, it is ignored for the search. If kvno is
2150 * -1, ktype and stype are ignored and the key with the max kvno is
2151 * returned. If kvno is 0, only the key with the max kvno is returned
2152 * and only if it matches the ktype and stype; otherwise, ENOENT is
2155 kadm5_ret_t
kadm5_decrypt_key(void *server_handle
,
2156 kadm5_principal_ent_t entry
, krb5_int32
2157 ktype
, krb5_int32 stype
, krb5_int32
2158 kvno
, krb5_keyblock
*keyblock
,
2159 krb5_keysalt
*keysalt
, int *kvnop
)
2161 kadm5_server_handle_t handle
= server_handle
;
2162 krb5_db_entry dbent
;
2163 krb5_key_data
*key_data
;
2166 CHECK_HANDLE(server_handle
);
2168 if (entry
->n_key_data
== 0 || entry
->key_data
== NULL
)
2171 /* find_enctype only uses these two fields */
2172 dbent
.n_key_data
= entry
->n_key_data
;
2173 dbent
.key_data
= entry
->key_data
;
2174 if ((ret
= krb5_dbe_find_enctype(handle
->context
, &dbent
, ktype
,
2175 stype
, kvno
, &key_data
)))
2178 if ((ret
= krb5_dbekd_decrypt_key_data(handle
->context
,
2179 &handle
->master_keyblock
, key_data
,
2180 keyblock
, keysalt
)))
2184 * Coerce the enctype of the output keyblock in case we got an
2185 * inexact match on the enctype; this behavior will go away when
2186 * the key storage architecture gets redesigned for 1.3.
2188 keyblock
->enctype
= ktype
;
2191 *kvnop
= key_data
->key_data_kvno
;
2196 /* Solaris Kerberos */
2198 kadm5_check_min_life(void *server_handle
, krb5_principal principal
,
2199 char *msg_ret
, unsigned int msg_len
)
2203 kadm5_policy_ent_rec pol
;
2204 kadm5_principal_ent_rec princ
;
2205 kadm5_server_handle_t handle
= server_handle
;
2207 if (msg_ret
!= NULL
)
2210 ret
= krb5_timeofday(handle
->context
, &now
);
2214 ret
= kadm5_get_principal(handle
->lhandle
, principal
,
2215 &princ
, KADM5_PRINCIPAL_NORMAL_MASK
);
2218 if(princ
.aux_attributes
& KADM5_POLICY
) {
2219 if((ret
=kadm5_get_policy(handle
->lhandle
,
2220 princ
.policy
, &pol
)) != KADM5_OK
) {
2221 (void) kadm5_free_principal_ent(handle
->lhandle
, &princ
);
2224 if((now
- princ
.last_pwd_change
) < pol
.pw_min_life
&&
2225 !(princ
.attributes
& KRB5_KDB_REQUIRES_PWCHANGE
)) {
2226 if (msg_ret
!= NULL
) {
2228 char *time_string
, *ptr
, *errstr
;
2230 until
= princ
.last_pwd_change
+ pol
.pw_min_life
;
2232 time_string
= ctime(&until
);
2233 errstr
= (char *)error_message(CHPASS_UTIL_PASSWORD_TOO_SOON
);
2235 if (strlen(errstr
) + strlen(time_string
) >= msg_len
) {
2238 if (*(ptr
= &time_string
[strlen(time_string
)-1]) == '\n')
2240 sprintf(msg_ret
, errstr
, time_string
);
2244 (void) kadm5_free_policy_ent(handle
->lhandle
, &pol
);
2245 (void) kadm5_free_principal_ent(handle
->lhandle
, &princ
);
2246 return KADM5_PASS_TOOSOON
;
2249 ret
= kadm5_free_policy_ent(handle
->lhandle
, &pol
);
2251 (void) kadm5_free_principal_ent(handle
->lhandle
, &princ
);
2256 return kadm5_free_principal_ent(handle
->lhandle
, &princ
);