dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / krb5 / kadm5 / srv / svr_principal.c
blob0044dc559f3b3401fb89f752bb6b12b325fbaecc
1 /*
2 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
5 /*
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
16 * copyright.
18 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
23 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
25 * $Header$
28 static char *rcsid = "$Header$";
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <errno.h>
33 #include "server_internal.h"
34 #include <kadm5/admin.h>
35 #include <kdb.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdarg.h>
39 #include <stdlib.h>
40 #include <k5-int.h>
41 #include <kadm5/server_internal.h>
42 #include <kadm5/admin.h>
43 #ifdef USE_PASSWORD_SERVER
44 #include <sys/wait.h>
45 #endif
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));
66 if (tempprinc == 0)
67 return ENOMEM;
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);
76 return ENOMEM;
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) {
84 while (--i >= 0)
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);
88 return ENOMEM;
90 if (len)
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);
102 return ENOMEM;
104 if (tempprinc->realm.length)
105 memcpy(tempprinc->realm.data, inprinc->realm.data,
106 inprinc->realm.length);
108 *outprinc = tempprinc;
109 return 0;
112 static void
113 kadm5_free_principal(krb5_context context, krb5_principal val)
115 register krb5_int32 i;
117 if (!val)
118 return;
120 if (val->data) {
121 i = krb5_princ_size(context, val);
122 while(--i >= 0)
123 krb5_db_free(context, krb5_princ_component(context, val, i)->data);
124 krb5_db_free(context, val->data);
126 if (val->realm.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;
138 int i, idx;
140 *to = *from;
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]);
155 return ENOMEM;
157 memcpy(to->key_data_contents[i], from->key_data_contents[i],
158 from->key_data_length[i]);
161 return 0;
164 static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
166 krb5_tl_data *n;
168 n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
169 if (n == NULL)
170 return NULL;
171 n->tl_data_contents = malloc(tl->tl_data_length);
172 if (n->tl_data_contents == NULL) {
173 free(n);
174 return 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;
180 return n;
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;
186 int count;
187 krb5_key_data * data;
189 int i, j;
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);
198 kadm5_ret_t
199 kadm5_create_principal(void *server_handle,
200 kadm5_principal_ent_t entry, long mask,
201 char *password)
203 return
204 kadm5_create_principal_3(server_handle, entry, mask,
205 0, NULL, password);
207 kadm5_ret_t
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,
211 char *password)
213 krb5_db_entry kdb;
214 osa_princ_ent_rec adb;
215 kadm5_policy_ent_rec polent;
216 krb5_int32 now;
217 krb5_tl_data *tl_data_orig, *tl_data_tail;
218 unsigned int ret;
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)
238 return EINVAL;
241 * Check to see if the principal exists
243 ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
245 switch(ret) {
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));
250 break;
251 case 0:
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);
259 return KADM5_DUP;
260 } else {
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.
265 * entry).
267 kdb_free_entry(handle, NULL, &adb);
268 memset(&adb, 0, sizeof(osa_princ_ent_rec));
270 break;
271 default:
272 return ret;
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) {
282 if(ret == EINVAL)
283 return KADM5_BAD_POLICY;
284 else
285 return ret;
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);
292 return ret;
295 * Start populating the various DB fields, using the
296 * "defaults" for fields that were not specified by the
297 * mask.
299 if ((ret = krb5_timeofday(handle->context, &now))) {
300 if (mask & KADM5_POLICY)
301 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
302 return ret;
305 kdb.magic = KRB5_KDB_MAGIC_NUMBER;
306 kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
309 * Solaris Kerberos:
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;
317 } else {
318 kdb.attributes = handle->params.flags;
321 if ((mask & KADM5_MAX_LIFE))
322 kdb.max_life = entry->max_life;
323 else
324 kdb.max_life = handle->params.max_life;
326 if (mask & KADM5_MAX_RLIFE)
327 kdb.max_renewable_life = entry->max_renewable_life;
328 else
329 kdb.max_renewable_life = handle->params.max_rlife;
331 if ((mask & KADM5_PRINC_EXPIRE_TIME))
332 kdb.expiration = entry->princ_expire_time;
333 else
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;
340 else
341 kdb.pw_expiration = 0;
343 if ((mask & KADM5_PW_EXPIRATION))
344 kdb.pw_expiration = entry->pw_expiration;
346 kdb.last_success = 0;
347 kdb.last_failed = 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
352 principal. */
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);
358 return(ret);
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);
365 return(ret);
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);
375 if( ret )
377 krb5_db_free_principal(handle->context, &kdb, 1);
378 if (mask & KADM5_POLICY)
379 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
380 return ret;
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,
390 password,
391 (mask & KADM5_KVNO)?entry->kvno:1,
392 FALSE, &kdb))) {
393 krb5_db_free_principal(handle->context, &kdb, 1);
394 if (mask & KADM5_POLICY)
395 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
396 return(ret);
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,
420 KADM5_REF_COUNT))
421 != KADM5_OK) {
422 krb5_db_free_principal(handle->context, &kdb, 1);
423 if (mask & KADM5_POLICY)
424 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
425 return(ret);
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);
437 if (ret) {
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,
447 KADM5_REF_COUNT);
450 if (mask & KADM5_POLICY)
451 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
452 return(ret);
455 if (mask & KADM5_POLICY)
456 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
458 return KADM5_OK;
462 kadm5_ret_t
463 kadm5_delete_principal(void *server_handle, krb5_principal principal)
465 unsigned int ret;
466 kadm5_policy_ent_rec polent;
467 krb5_db_entry kdb;
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)
476 return EINVAL;
478 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
479 return(ret);
481 if ((adb.aux_attributes & KADM5_POLICY)) {
482 if ((ret = kadm5_get_policy(handle->lhandle,
483 adb.policy, &polent))
484 == KADM5_OK) {
485 polent.policy_refcnt--;
486 if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
487 KADM5_REF_COUNT))
488 != KADM5_OK) {
489 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
490 kdb_free_entry(handle, &kdb, &adb);
491 return(ret);
494 if ((ret = kadm5_free_policy_ent(handle->lhandle, &polent))) {
495 kdb_free_entry(handle, &kdb, &adb);
496 return ret;
500 ret = kdb_delete_entry(handle, principal);
502 kdb_free_entry(handle, &kdb, &adb);
504 return ret;
507 kadm5_ret_t
508 kadm5_modify_principal(void *server_handle,
509 kadm5_principal_ent_t entry, long mask)
511 int ret, ret2, i;
512 kadm5_policy_ent_rec npol, opol;
513 int have_npol = 0, have_opol = 0;
514 krb5_db_entry kdb;
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)
534 return EINVAL;
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);
545 if (ret)
546 return(ret);
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);
555 if (ret) {
556 switch (ret) {
557 case EINVAL:
558 ret = KADM5_BAD_POLICY;
559 break;
560 case KADM5_UNK_POLICY:
561 case KADM5_BAD_POLICY:
562 ret = KADM5_UNK_POLICY;
563 break;
565 goto done;
567 have_npol = 1;
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,
574 adb.policy, &opol);
575 switch(ret) {
576 case EINVAL:
577 case KADM5_BAD_POLICY:
578 case KADM5_UNK_POLICY:
579 break;
580 case KADM5_OK:
581 have_opol = 1;
582 opol.policy_refcnt--;
583 break;
584 default:
585 goto done;
586 break;
588 npol.policy_refcnt++;
590 } else npol.policy_refcnt++;
592 /* set us up to use the new policy */
593 adb.aux_attributes |= KADM5_POLICY;
594 free(adb.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));
601 if (ret)
602 goto done;
603 kdb.pw_expiration += npol.pw_max_life;
604 } else {
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);
612 switch(ret) {
613 case EINVAL:
614 case KADM5_BAD_POLICY:
615 case KADM5_UNK_POLICY:
616 ret = KADM5_BAD_DB;
617 goto done;
618 break;
619 case KADM5_OK:
620 have_opol = 1;
621 free(adb.policy);
622 adb.policy = NULL;
623 adb.aux_attributes &= ~KADM5_POLICY;
624 kdb.pw_expiration = 0;
625 opol.policy_refcnt--;
626 break;
627 default:
628 goto done;
629 break;
633 if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) &&
634 (((have_opol) &&
635 (ret =
636 kadm5_modify_policy_internal(handle->lhandle, &opol,
637 KADM5_REF_COUNT))) ||
638 ((have_npol) &&
639 (ret =
640 kadm5_modify_policy_internal(handle->lhandle, &npol,
641 KADM5_REF_COUNT)))))
642 goto done;
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) {
663 krb5_tl_data *tl;
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);
671 if( ret )
673 goto done;
678 /* let the mask propagate to the database provider */
679 kdb.mask = mask;
681 ret = kdb_put_entry(handle, &kdb, &adb);
682 if (ret) goto done;
684 ret = KADM5_OK;
685 done:
686 if (have_opol) {
687 ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
688 ret = ret ? ret : ret2;
690 if (have_npol) {
691 ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
692 ret = ret ? ret : ret2;
694 kdb_free_entry(handle, &kdb, &adb);
695 return ret;
698 kadm5_ret_t
699 kadm5_rename_principal(void *server_handle,
700 krb5_principal source, krb5_principal target)
702 krb5_db_entry kdb;
703 osa_princ_ent_rec adb;
704 int ret, i;
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)
712 return EINVAL;
714 if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
715 kdb_free_entry(handle, &kdb, &adb);
716 return(KADM5_DUP);
719 if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
720 return ret;
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;
728 goto done;
732 kadm5_free_principal(handle->context, kdb.princ);
733 ret = kadm5_copy_principal(handle->context, target, &kdb.princ);
734 if (ret) {
735 kdb.princ = NULL; /* so freeing the dbe doesn't lose */
736 goto done;
739 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
740 goto done;
742 ret = kdb_delete_entry(handle, source);
744 done:
745 kdb_free_entry(handle, &kdb, &adb);
746 return ret;
749 kadm5_ret_t
750 kadm5_get_principal(void *server_handle, krb5_principal principal,
751 kadm5_principal_ent_t entry,
752 long in_mask)
754 krb5_db_entry kdb;
755 osa_princ_ent_rec adb;
756 krb5_error_code ret = 0;
757 long mask;
758 int i;
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;
773 entry_orig = entry;
774 entry = &entry_local;
775 } else {
776 mask = in_mask;
779 memset((char *) entry, 0, sizeof(*entry));
781 if (principal == NULL)
782 return EINVAL;
784 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
785 return ret;
787 if ((mask & KADM5_POLICY) &&
788 adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
789 if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
790 ret = ENOMEM;
791 goto done;
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))) {
802 goto done;
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)))) {
811 goto done;
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,
823 &(entry->mod_date),
824 &(entry->mod_name));
825 if (ret) {
826 goto done;
829 if (! (mask & KADM5_MOD_TIME))
830 entry->mod_date = 0;
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)
846 entry->mkvno = 0;
847 else {
848 /* XXX I'll be damned if I know how to deal with this one --marc */
849 entry->mkvno = 1;
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;
869 tl = kdb.tl_data;
870 while (tl) {
871 if (tl->tl_data_type > 255) {
872 if ((tl2 = dup_tl_data(tl)) == NULL) {
873 ret = ENOMEM;
874 goto done;
876 tl2->tl_data_next = entry->tl_data;
877 entry->tl_data = tl2;
878 entry->n_tl_data++;
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) {
890 ret = ENOMEM;
891 goto done;
893 } else
894 entry->key_data = NULL;
896 for (i = 0; i < entry->n_key_data; i++)
897 ret = krb5_copy_key_data_contents(handle->context,
898 &kdb.key_data[i],
899 &entry->key_data[i]);
900 if (ret)
901 goto done;
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)));
914 if (newv1 == NULL) {
915 ret = ENOMEM;
916 goto done;
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;
935 ret = KADM5_OK;
937 done:
938 if (ret && entry->principal)
939 krb5_free_principal(handle->context, entry->principal);
940 kdb_free_entry(handle, &kdb, &adb);
942 return ret;
946 * Function: check_pw_reuse
948 * Purpose: Check if a key appears in a list of keys, in order to
949 * enforce password history.
951 * Arguments:
953 * context (r) the krb5 context
954 * hist_keyblock (r) the key that hist_key_data is
955 * encrypted in
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
962 * Effects:
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.
973 static kadm5_ret_t
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)
980 int x, y, z;
981 krb5_keyblock newkey, histkey;
982 krb5_error_code ret;
984 for (x = 0; x < n_new_key_data; x++) {
985 ret = krb5_dbekd_decrypt_key_data(context,
986 master_keyblock,
987 &(new_key_data[x]),
988 &newkey, NULL);
989 if (ret)
990 return(ret);
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,
994 hist_keyblock,
995 &pw_hist_data[y].key_data[z],
996 &histkey, NULL);
997 if (ret)
998 return(ret);
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);
1015 return(0);
1019 * Function: create_history_entry
1021 * Purpose: Creates a password history entry from an array of
1022 * key_data.
1024 * Arguments:
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
1032 * Effects:
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.
1039 static
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)
1044 int i, ret;
1045 krb5_keyblock key;
1046 krb5_keysalt salt;
1048 hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
1049 if (hist->key_data == NULL)
1050 return ENOMEM;
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,
1055 master_keyblock,
1056 &key_data[i],
1057 &key, &salt);
1058 if (ret)
1059 return ret;
1061 ret = krb5_dbekd_encrypt_key_data(context, &hist_key,
1062 &key, &salt,
1063 key_data[i].key_data_kvno,
1064 &hist->key_data[i]);
1065 if (ret)
1066 return ret;
1068 krb5_free_keyblock_contents(context, &key);
1069 /* krb5_free_keysalt(context, &salt); */
1072 hist->n_key_data = n_key_data;
1073 return 0;
1076 static
1077 void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
1079 int i;
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.
1091 * Arguments:
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
1098 * Effects:
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;
1119 uint32_t nhist;
1120 unsigned int i, knext, nkeys;
1122 nhist = pol->pw_history_num;
1123 /* A history of 1 means just check the current password */
1124 if (nhist <= 1)
1125 return 0;
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));
1134 } else {
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)
1140 return(ENOMEM);
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
1146 * knext.
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]
1167 int j;
1168 osa_pw_hist_t tmp;
1170 tmp = (osa_pw_hist_ent *)
1171 malloc((nhist - 1) * sizeof (osa_pw_hist_ent));
1172 if (tmp == NULL)
1173 return ENOMEM;
1174 for (i = 0; i < nhist - 1; i++) {
1176 * Add nkeys once before taking remainder to avoid
1177 * negative values.
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;
1218 return(0);
1220 #undef KADM_MOD
1222 #ifdef USE_PASSWORD_SERVER
1223 /* FIXME: don't use global variable for this */
1224 krb5_boolean use_password_server = 0;
1226 static krb5_boolean
1227 kadm5_use_password_server (void)
1229 return use_password_server;
1232 void
1233 kadm5_set_use_password_server (void)
1235 use_password_server = 1;
1237 #endif
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.
1252 static kadm5_ret_t
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;
1258 int data_pipe[2];
1260 if (data_buffer != NULL) {
1261 ret = pipe (data_pipe);
1262 if (ret) { ret = errno; }
1265 if (!ret) {
1266 pid_t pid = fork ();
1267 if (pid == -1) {
1268 ret = errno;
1269 } else if (pid == 0) {
1270 /* The child: */
1272 if (data_buffer != NULL) {
1273 if (dup2 (data_pipe[0], STDIN_FILENO) == -1) {
1274 _exit (1);
1276 } else {
1277 close (data_pipe[0]);
1280 close (data_pipe[1]);
1282 execv (task_path, task_argv);
1284 _exit (1); /* Fail if execv fails */
1285 } else {
1286 /* The parent: */
1287 int status;
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 */
1294 ret = errno;
1295 kill (pid, SIGKILL);
1299 close (data_buffer[0]);
1300 close (data_buffer[1]);
1302 waitpid (pid, &status, 0);
1304 if (!ret) {
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 */
1310 } else {
1311 /* child read password but crashed or was killed */
1312 ret = KRB5KRB_ERR_GENERIC; /* FIXME: better error */
1318 return ret;
1321 #endif
1323 kadm5_ret_t
1324 kadm5_chpass_principal(void *server_handle,
1325 krb5_principal principal, char *password)
1327 return
1328 kadm5_chpass_principal_3(server_handle, principal, FALSE,
1329 0, NULL, password);
1332 kadm5_ret_t
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,
1336 char *password)
1338 krb5_int32 now;
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;
1343 int have_pol = 0;
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);
1351 if (ret)
1352 return (ret);
1353 krb5_clear_error_message(handle->context);
1355 hist_added = 0;
1356 memset(&hist, 0, sizeof(hist));
1358 if (principal == NULL || password == NULL)
1359 return EINVAL;
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)))
1365 return(ret);
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);
1370 return(ret);
1373 if ((adb.aux_attributes & KADM5_POLICY)) {
1374 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1375 goto done;
1376 have_pol = 1;
1379 if ((ret = passwd_check(handle, password, adb.aux_attributes &
1380 KADM5_POLICY, &pol, principal)))
1381 goto done;
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 */,
1387 keepold, &kdb);
1388 if (ret)
1389 goto done;
1391 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1393 ret = krb5_timeofday(handle->context, &now);
1394 if (ret)
1395 goto done;
1397 if ((adb.aux_attributes & KADM5_POLICY)) {
1398 /* the policy was loaded before */
1400 ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1401 &kdb, &last_pwd);
1402 if (ret)
1403 goto done;
1405 #if 0
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;
1415 goto done;
1417 #endif
1419 ret = create_history_entry(handle->context,
1420 &handle->master_keyblock, kdb_save.n_key_data,
1421 kdb_save.key_data, &hist);
1422 if (ret)
1423 goto done;
1425 ret = check_pw_reuse(handle->context,
1426 &handle->master_keyblock,
1427 &hist_key,
1428 kdb.n_key_data, kdb.key_data,
1429 1, &hist);
1430 if (ret)
1431 goto done;
1433 if (pol.pw_history_num > 1) {
1434 if (adb.admin_history_kvno != hist_kvno) {
1435 ret = KADM5_BAD_HIST_KEY;
1436 goto done;
1439 ret = check_pw_reuse(handle->context,
1440 &handle->master_keyblock,
1441 &hist_key,
1442 kdb.n_key_data, kdb.key_data,
1443 adb.old_key_len, adb.old_keys);
1444 if (ret)
1445 goto done;
1447 ret = add_to_history(handle->context, &adb, &pol, &hist);
1448 if (ret)
1449 goto done;
1450 hist_added = 1;
1453 if (pol.pw_max_life)
1454 kdb.pw_expiration = now + pol.pw_max_life;
1455 else
1456 kdb.pw_expiration = 0;
1457 } else {
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;
1468 char pwbuf[256];
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';
1476 if (!ret) {
1477 pstring = malloc ((princ->length + 1) * sizeof (char));
1478 if (pstring == NULL) { ret = errno; }
1481 if (!ret) {
1482 memcpy (pstring, princ->data, princ->length);
1483 pstring [princ->length] = '\0';
1484 argv[2] = pstring;
1486 ret = kadm5_launch_task (handle->context, path, argv, pwbuf);
1489 free(pstring);
1491 if (ret)
1492 goto done;
1494 #endif
1496 ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1497 if (ret)
1498 goto done;
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 */;
1505 else
1506 kdb.mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES /* | KADM5_CPW_FUNCTION */;
1508 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1509 goto done;
1511 ret = KADM5_OK;
1512 done:
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))
1520 && !ret)
1521 ret = ret2;
1523 return ret;
1526 kadm5_ret_t
1527 kadm5_randkey_principal(void *server_handle,
1528 krb5_principal principal,
1529 krb5_keyblock **keyblocks,
1530 int *n_keys)
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));
1551 kadm5_ret_t
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,
1557 int *n_keys)
1559 krb5_db_entry kdb;
1560 osa_princ_ent_rec adb;
1561 krb5_int32 now;
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;
1567 if (keyblocks)
1568 *keyblocks = NULL;
1570 CHECK_HANDLE(server_handle);
1572 krb5_clear_error_message(handle->context);
1574 if (principal == NULL)
1575 return EINVAL;
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)))
1582 return(ret);
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,
1587 keepold,
1588 &kdb);
1589 if (ret)
1590 goto done;
1592 kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1594 ret = krb5_timeofday(handle->context, &now);
1595 if (ret)
1596 goto done;
1598 if ((adb.aux_attributes & KADM5_POLICY)) {
1599 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1600 &pol)) != KADM5_OK)
1601 goto done;
1602 have_pol = 1;
1604 ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1605 &kdb, &last_pwd);
1606 if (ret)
1607 goto done;
1609 #if 0
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;
1619 goto done;
1621 #endif
1623 if(pol.pw_history_num > 1) {
1624 if(adb.admin_history_kvno != hist_kvno) {
1625 ret = KADM5_BAD_HIST_KEY;
1626 goto done;
1629 ret = check_pw_reuse(handle->context,
1630 &handle->master_keyblock,
1631 &hist_key,
1632 kdb.n_key_data, kdb.key_data,
1633 adb.old_key_len, adb.old_keys);
1634 if (ret)
1635 goto done;
1637 if (pol.pw_max_life)
1638 kdb.pw_expiration = now + pol.pw_max_life;
1639 else
1640 kdb.pw_expiration = 0;
1641 } else {
1642 kdb.pw_expiration = 0;
1645 ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1646 if (ret)
1647 goto done;
1649 if (keyblocks) {
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,
1654 -1, -1, &key_data);
1655 if (ret)
1656 goto done;
1658 ret = decrypt_key_data(handle->context,
1659 &handle->master_keyblock, 1, key_data,
1660 keyblocks, NULL);
1661 if (ret)
1662 goto done;
1663 } else {
1664 ret = decrypt_key_data(handle->context,
1665 &handle->master_keyblock,
1666 kdb.n_key_data, kdb.key_data,
1667 keyblocks, n_keys);
1668 if (ret)
1669 goto done;
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)))
1677 goto done;
1679 ret = KADM5_OK;
1680 done:
1681 kdb_free_entry(handle, &kdb, &adb);
1682 if (have_pol)
1683 kadm5_free_policy_ent(handle->lhandle, &pol);
1685 return ret;
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.
1696 kadm5_ret_t
1697 kadm5_setv4key_principal(void *server_handle,
1698 krb5_principal principal,
1699 krb5_keyblock *keyblock)
1701 krb5_db_entry kdb;
1702 osa_princ_ent_rec adb;
1703 krb5_int32 now;
1704 kadm5_policy_ent_rec pol;
1705 krb5_keysalt keysalt;
1706 int i, k, kvno, ret, have_pol = 0;
1707 #if 0
1708 int last_pwd;
1709 #endif
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)
1720 return EINVAL;
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)))
1730 return(ret);
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)
1741 return ENOMEM;
1742 memset(kdb.key_data, 0, sizeof(krb5_key_data));
1743 kdb.n_key_data = 1;
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,
1752 &tmp_key_data);
1753 if (ret) {
1754 goto done;
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;
1765 kdb.n_key_data = 0;
1766 ret = ENOMEM;
1767 goto done;
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);
1782 if (ret)
1783 goto done;
1785 if ((adb.aux_attributes & KADM5_POLICY)) {
1786 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1787 &pol)) != KADM5_OK)
1788 goto done;
1789 have_pol = 1;
1791 #if 0
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,
1799 &kdb, &last_pwd))
1800 goto done;
1801 if((now - last_pwd) < pol.pw_min_life &&
1802 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1803 ret = KADM5_PASS_TOOSOON;
1804 goto done;
1806 #endif
1807 #if 0
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;
1814 goto done;
1817 if (ret = check_pw_reuse(handle->context,
1818 &hist_key,
1819 kdb.n_key_data, kdb.key_data,
1820 adb.old_key_len, adb.old_keys))
1821 goto done;
1823 #endif
1825 if (pol.pw_max_life)
1826 kdb.pw_expiration = now + pol.pw_max_life;
1827 else
1828 kdb.pw_expiration = 0;
1829 } else {
1830 kdb.pw_expiration = 0;
1833 ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1834 if (ret)
1835 goto done;
1837 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1838 goto done;
1840 ret = KADM5_OK;
1841 done:
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);
1850 if (have_pol)
1851 kadm5_free_policy_ent(handle->lhandle, &pol);
1853 return ret;
1855 #endif
1857 kadm5_ret_t
1858 kadm5_setkey_principal(void *server_handle,
1859 krb5_principal principal,
1860 krb5_keyblock *keyblocks,
1861 int n_keys)
1863 return
1864 kadm5_setkey_principal_3(server_handle, principal,
1865 FALSE, 0, NULL,
1866 keyblocks, n_keys);
1869 kadm5_ret_t
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,
1875 int n_keys)
1877 krb5_db_entry kdb;
1878 osa_princ_ent_rec adb;
1879 krb5_int32 now;
1880 kadm5_policy_ent_rec pol;
1881 krb5_key_data *old_key_data;
1882 int n_old_keys;
1883 int i, j, k, kvno, ret, have_pol = 0;
1884 #if 0
1885 int last_pwd;
1886 #endif
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)
1898 return EINVAL;
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,
1909 &similar)))
1910 return(ret);
1911 if (similar) {
1912 if (n_ks_tuple) {
1913 if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
1914 return KADM5_SETKEY_DUP_ENCTYPES;
1915 } else
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)))
1925 return(ret);
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;
1931 if (keepold) {
1932 old_key_data = kdb.key_data;
1933 n_old_keys = kdb.n_key_data;
1934 } else {
1935 if (kdb.key_data != NULL)
1936 cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1937 n_old_keys = 0;
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) {
1944 ret = ENOMEM;
1945 goto done;
1948 memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
1949 kdb.n_key_data = 0;
1951 for (i = 0; i < n_keys; i++) {
1952 if (n_ks_tuple) {
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;
1958 goto done;
1961 memset (&tmp_key_data, 0, sizeof(tmp_key_data));
1963 ret = krb5_dbekd_encrypt_key_data(handle->context,
1964 &handle->master_keyblock,
1965 &keyblocks[i],
1966 n_ks_tuple ? &keysalt : NULL,
1967 kvno + 1,
1968 &tmp_key_data);
1969 if (ret) {
1970 goto done;
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) {
1979 int i1;
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]);
1987 ret = ENOMEM;
1988 goto done;
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;
1997 kdb.n_key_data++;
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));
2004 kdb.n_key_data++;
2007 if (old_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)))
2014 goto done;
2016 if ((adb.aux_attributes & KADM5_POLICY)) {
2017 if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
2018 &pol)) != KADM5_OK)
2019 goto done;
2020 have_pol = 1;
2022 #if 0
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,
2030 &kdb, &last_pwd))
2031 goto done;
2032 if((now - last_pwd) < pol.pw_min_life &&
2033 !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
2034 ret = KADM5_PASS_TOOSOON;
2035 goto done;
2037 #endif
2038 #if 0
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;
2045 goto done;
2048 if (ret = check_pw_reuse(handle->context,
2049 &handle->master_keyblock,
2050 &hist_key,
2051 kdb.n_key_data, kdb.key_data,
2052 adb.old_key_len, adb.old_keys))
2053 goto done;
2055 #endif
2057 if (pol.pw_max_life)
2058 kdb.pw_expiration = now + pol.pw_max_life;
2059 else
2060 kdb.pw_expiration = 0;
2061 } else {
2062 kdb.pw_expiration = 0;
2065 if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)))
2066 goto done;
2068 if ((ret = kdb_put_entry(handle, &kdb, &adb)))
2069 goto done;
2071 ret = KADM5_OK;
2072 done:
2073 kdb_free_entry(handle, &kdb, &adb);
2074 if (have_pol)
2075 kadm5_free_policy_ent(handle->lhandle, &pol);
2077 return ret;
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;
2092 int ret, i;
2094 keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
2095 if (keys == NULL)
2096 return ENOMEM;
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,
2101 master_keyblock,
2102 &key_data[i],
2103 &keys[i], NULL);
2104 if (ret) {
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));
2113 free(keys);
2114 return ret;
2118 *keyblocks = keys;
2119 if (n_keys)
2120 *n_keys = n_key_data;
2122 return 0;
2126 * Function: kadm5_decrypt_key
2128 * Purpose: Retrieves and decrypts a principal key.
2130 * Arguments:
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
2153 * returned.
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;
2164 int ret;
2166 CHECK_HANDLE(server_handle);
2168 if (entry->n_key_data == 0 || entry->key_data == NULL)
2169 return EINVAL;
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)))
2176 return ret;
2178 if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
2179 &handle->master_keyblock, key_data,
2180 keyblock, keysalt)))
2181 return ret;
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;
2190 if (kvnop)
2191 *kvnop = key_data->key_data_kvno;
2193 return KADM5_OK;
2196 /* Solaris Kerberos */
2197 kadm5_ret_t
2198 kadm5_check_min_life(void *server_handle, krb5_principal principal,
2199 char *msg_ret, unsigned int msg_len)
2201 krb5_int32 now;
2202 kadm5_ret_t ret;
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)
2208 *msg_ret = '\0';
2210 ret = krb5_timeofday(handle->context, &now);
2211 if (ret)
2212 return ret;
2214 ret = kadm5_get_principal(handle->lhandle, principal,
2215 &princ, KADM5_PRINCIPAL_NORMAL_MASK);
2216 if(ret)
2217 return ret;
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);
2222 return ret;
2224 if((now - princ.last_pwd_change) < pol.pw_min_life &&
2225 !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
2226 if (msg_ret != NULL) {
2227 time_t until;
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) {
2236 *errstr = '\0';
2237 } else {
2238 if (*(ptr = &time_string[strlen(time_string)-1]) == '\n')
2239 *ptr = '\0';
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);
2250 if (ret) {
2251 (void) kadm5_free_principal_ent(handle->lhandle, &princ);
2252 return ret;
2256 return kadm5_free_principal_ent(handle->lhandle, &princ);