1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
4 * Use is subject to license terms.
9 #include <kadm5/server_internal.h>
10 #include <kadm5/admin.h>
11 #include <adm_proto.h>
12 #include "kdb5_util.h"
15 #if defined(HAVE_COMPILE) && defined(HAVE_STEP)
16 #define SOLARIS_REGEXPS
17 #elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXEC)
19 #elif defined(HAVE_RE_COMP) && defined(HAVE_RE_EXEC)
22 #error I cannot find any regexp functions
24 #ifdef SOLARIS_REGEXPS
31 extern krb5_keyblock master_keyblock; /* current mkey */
32 extern krb5_kvno master_kvno;
33 extern krb5_principal master_princ;
34 extern krb5_data master_salt;
35 extern char *mkey_fullname;
36 extern char *mkey_password;
37 extern char *progname;
38 extern int exit_status;
39 extern kadm5_config_params global_params;
40 extern krb5_context util_context;
41 extern time_t get_date(char *);
43 static char *strdate(krb5_timestamp when)
49 tm = localtime(&lcltim);
50 strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm);
55 get_next_kvno(krb5_context context, krb5_db_entry *entry)
59 new_kvno = krb5_db_get_key_data_kvno(context, entry->n_key_data,
62 /* deal with wrapping */
64 new_kvno = 1; /* knvo must not be 0 as this is special value (IGNORE_VNO) */
70 add_new_mkey(krb5_context context, krb5_db_entry *master_entry,
71 krb5_keyblock *new_mkey, krb5_kvno use_mkvno)
73 krb5_error_code retval = 0;
74 int old_key_data_count, i;
75 krb5_kvno new_mkey_kvno;
76 krb5_key_data tmp_key_data;
77 krb5_mkey_aux_node *mkey_aux_data_head = NULL, **mkey_aux_data;
78 krb5_keylist_node *keylist_node;
79 krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(context);
81 /* do this before modifying master_entry key_data */
82 new_mkey_kvno = get_next_kvno(context, master_entry);
83 /* verify the requested mkvno if not 0 is the one that would be used here. */
84 if (use_mkvno != 0 && new_mkey_kvno != use_mkvno)
85 return (KRB5_KDB_KVNONOMATCH);
87 old_key_data_count = master_entry->n_key_data;
89 /* alloc enough space to hold new and existing key_data */
91 * The encrypted key is malloc'ed by krb5_dbe_encrypt_key_data and
92 * krb5_key_data key_data_contents is a pointer to this key. Using some
93 * logic from master_key_convert().
95 for (i = 0; i < master_entry->n_key_data; i++)
96 krb5_free_key_data_contents(context, &master_entry->key_data[i]);
97 free(master_entry->key_data);
98 master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) *
99 (old_key_data_count + 1));
100 if (master_entry->key_data == NULL)
103 memset(master_entry->key_data, 0,
104 sizeof(krb5_key_data) * (old_key_data_count + 1));
105 master_entry->n_key_data = old_key_data_count + 1;
107 /* Note, mkey does not have salt */
108 /* add new mkey encrypted with itself to mkey princ entry */
109 if ((retval = krb5_dbe_encrypt_key_data(context, new_mkey, new_mkey, NULL,
111 &master_entry->key_data[0]))) {
114 /* the mvkno should be that of the newest mkey */
115 if ((retval = krb5_dbe_update_mkvno(context, master_entry, new_mkey_kvno))) {
116 krb5_free_key_data_contents(context, &master_entry->key_data[0]);
120 * Need to decrypt old keys with the current mkey which is in the global
121 * master_keyblock and encrypt those keys with the latest mkey. And while
122 * the old keys are being decrypted, use those to create the
123 * KRB5_TL_MKEY_AUX entries which store the latest mkey encrypted by one of
126 * The new mkey is followed by existing keys.
128 * First, set up for creating a krb5_mkey_aux_node list which will be used
129 * to update the mkey aux data for the mkey princ entry.
131 mkey_aux_data_head = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
132 if (mkey_aux_data_head == NULL) {
136 memset(mkey_aux_data_head, 0, sizeof(krb5_mkey_aux_node));
137 mkey_aux_data = &mkey_aux_data_head;
139 for (keylist_node = master_keylist, i = 1; keylist_node != NULL;
140 keylist_node = keylist_node->next, i++) {
143 * Create a list of krb5_mkey_aux_node nodes. One node contains the new
144 * mkey encrypted by an old mkey and the old mkey's kvno (one node per
147 if (*mkey_aux_data == NULL) {
148 /* *mkey_aux_data points to next field of previous node */
149 *mkey_aux_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
150 if (*mkey_aux_data == NULL) {
154 memset(*mkey_aux_data, 0, sizeof(krb5_mkey_aux_node));
157 memset(&tmp_key_data, 0, sizeof(tmp_key_data));
158 /* encrypt the new mkey with the older mkey */
159 retval = krb5_dbe_encrypt_key_data(context, &keylist_node->keyblock,
160 new_mkey, NULL, (int) new_mkey_kvno,
165 (*mkey_aux_data)->latest_mkey = tmp_key_data;
166 (*mkey_aux_data)->mkey_kvno = keylist_node->kvno;
167 mkey_aux_data = &((*mkey_aux_data)->next);
170 * Store old key in master_entry keydata past the new mkey
172 retval = krb5_dbe_encrypt_key_data(context, new_mkey,
173 &keylist_node->keyblock,
174 NULL, (int) keylist_node->kvno,
175 &master_entry->key_data[i]);
179 assert(i == old_key_data_count + 1);
181 if ((retval = krb5_dbe_update_mkey_aux(context, master_entry,
182 mkey_aux_data_head))) {
185 master_entry->mask |= KADM5_KEY_DATA | KADM5_TL_DATA;
188 krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_head);
193 kdb5_add_mkey(int argc, char *argv[])
196 krb5_error_code retval;
198 unsigned int pw_size = 0;
201 krb5_kvno new_mkey_kvno;
202 krb5_keyblock new_mkeyblock;
203 krb5_enctype new_master_enctype = ENCTYPE_UNKNOWN;
204 char *new_mkey_password;
205 krb5_db_entry *master_entry = NULL;
209 * The command table entry for this command causes open_db_and_mkey() to be
210 * called first to open the KDB and get the current mkey.
213 memset(&new_mkeyblock, 0, sizeof(new_mkeyblock));
214 master_salt.data = NULL;
216 while ((optchar = getopt(argc, argv, "e:s")) != -1) {
219 if (krb5_string_to_enctype(optarg, &new_master_enctype)) {
220 com_err(progname, EINVAL, _("%s is an invalid enctype"),
236 if (new_master_enctype == ENCTYPE_UNKNOWN)
237 new_master_enctype = global_params.enctype;
239 retval = krb5_db_get_principal(util_context, master_princ, 0,
242 com_err(progname, retval, _("while getting master key principal %s"),
248 printf(_("Creating new master key for master key principal '%s'\n"),
251 printf(_("You will be prompted for a new database Master Password.\n"));
252 printf(_("It is important that you NOT FORGET this password.\n"));
256 pw_str = malloc(pw_size);
257 if (pw_str == NULL) {
258 com_err(progname, ENOMEM, _("while creating new master key"));
263 retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
266 com_err(progname, retval,
267 _("while reading new master key from keyboard"));
271 new_mkey_password = pw_str;
273 pwd.data = new_mkey_password;
274 pwd.length = strlen(new_mkey_password);
275 retval = krb5_principal2salt(util_context, master_princ, &master_salt);
277 com_err(progname, retval, _("while calculating master key salt"));
282 retval = krb5_c_string_to_key(util_context, new_master_enctype,
283 &pwd, &master_salt, &new_mkeyblock);
285 com_err(progname, retval,
286 _("while transforming master key from password"));
291 new_mkey_kvno = get_next_kvno(util_context, master_entry);
292 retval = add_new_mkey(util_context, master_entry, &new_mkeyblock,
295 com_err(progname, retval,
296 _("adding new master key to master principal"));
301 if ((retval = krb5_timeofday(util_context, &now))) {
302 com_err(progname, retval, _("while getting current time"));
307 if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry,
308 now, master_princ))) {
309 com_err(progname, retval, _("while updating the master key principal "
310 "modification time"));
315 if ((retval = krb5_db_put_principal(util_context, master_entry))) {
316 com_err(progname, retval, _("while adding master key entry to the "
323 retval = krb5_db_store_master_key(util_context,
324 global_params.stash_file,
330 com_err(progname, retval, _("while storing key"));
331 printf(_("Warning: couldn't stash master key.\n"));
337 krb5_db_free_principal(util_context, master_entry);
338 zap((char *)new_mkeyblock.contents, new_mkeyblock.length);
339 free(new_mkeyblock.contents);
341 zap(pw_str, pw_size);
344 free(master_salt.data);
349 kdb5_use_mkey(int argc, char *argv[])
351 krb5_error_code retval;
353 krb5_timestamp now, start_time;
354 krb5_actkvno_node *actkvno_list = NULL, *new_actkvno = NULL,
355 *prev_actkvno, *cur_actkvno;
356 krb5_db_entry *master_entry = NULL;
357 krb5_keylist_node *keylist_node;
358 krb5_boolean inserted = FALSE;
359 krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context);
361 if (argc < 2 || argc > 3) {
362 /* usage calls exit */
366 use_kvno = atoi(argv[1]);
368 com_err(progname, EINVAL, _("0 is an invalid KVNO value"));
372 /* verify use_kvno is valid */
373 for (keylist_node = master_keylist; keylist_node != NULL;
374 keylist_node = keylist_node->next) {
375 if (use_kvno == keylist_node->kvno)
379 com_err(progname, EINVAL, _("%d is an invalid KVNO value"),
386 if ((retval = krb5_timeofday(util_context, &now))) {
387 com_err(progname, retval, _("while getting current time"));
393 time_t t = get_date(argv[2]);
395 com_err(progname, 0, _("could not parse date-time string '%s'"),
400 start_time = (krb5_timestamp) t;
409 * 2. get krb5_actkvno_node list
410 * 3. add use_kvno to actkvno list (sorted in right spot)
411 * 4. update mkey princ's tl data
415 retval = krb5_db_get_principal(util_context, master_princ, 0,
418 com_err(progname, retval, _("while getting master key principal %s"),
424 retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
426 com_err(progname, retval,
427 _("while looking up active version of master key"));
433 * If an entry already exists with the same kvno either delete it or if it's
434 * the only entry, just set its active time.
436 for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
438 prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
440 if (cur_actkvno->act_kvno == use_kvno) {
443 prev_actkvno->next = cur_actkvno->next;
444 cur_actkvno->next = NULL;
445 krb5_dbe_free_actkvno_list(util_context, cur_actkvno);
447 if (cur_actkvno->next) {
448 /* delete it from front of list */
449 actkvno_list = cur_actkvno->next;
450 cur_actkvno->next = NULL;
451 krb5_dbe_free_actkvno_list(util_context, cur_actkvno);
453 /* There's only one entry, go ahead and change the time */
454 cur_actkvno->act_time = start_time;
463 /* alloc enough space to hold new and existing key_data */
464 new_actkvno = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node));
465 if (new_actkvno == NULL) {
466 com_err(progname, ENOMEM, _("while adding new master key"));
470 memset(new_actkvno, 0, sizeof(krb5_actkvno_node));
471 new_actkvno->act_kvno = use_kvno;
472 new_actkvno->act_time = start_time;
474 /* insert new act kvno node */
476 if (actkvno_list == NULL) {
477 /* new actkvno is the list */
478 actkvno_list = new_actkvno;
480 for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
482 prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
484 if (new_actkvno->act_time < cur_actkvno->act_time) {
486 prev_actkvno->next = new_actkvno;
487 new_actkvno->next = cur_actkvno;
489 new_actkvno->next = actkvno_list;
490 actkvno_list = new_actkvno;
493 } else if (cur_actkvno->next == NULL) {
494 /* end of line, just add new node to end of list */
495 cur_actkvno->next = new_actkvno;
502 if (actkvno_list->act_time > now) {
503 com_err(progname, EINVAL,
504 _("there must be one master key currently active"));
509 if ((retval = krb5_dbe_update_actkvno(util_context, master_entry,
511 com_err(progname, retval,
512 _("while updating actkvno data for master principal entry"));
517 if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry,
518 now, master_princ))) {
519 com_err(progname, retval, _("while updating the master key principal "
520 "modification time"));
525 if ((retval = krb5_db_put_principal(util_context, master_entry))) {
526 com_err(progname, retval,
527 _("while adding master key entry to the database"));
534 krb5_db_free_principal(util_context, master_entry);
535 krb5_dbe_free_actkvno_list(util_context, actkvno_list);
540 kdb5_list_mkeys(int argc, char *argv[])
542 krb5_error_code retval;
543 char *output_str = NULL, enctype[BUFSIZ];
545 krb5_timestamp act_time;
546 krb5_actkvno_node *actkvno_list = NULL, *cur_actkvno;
547 krb5_db_entry *master_entry = NULL;
548 krb5_keylist_node *cur_kb_node;
549 krb5_keyblock *act_mkey;
550 krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context);
552 if (master_keylist == NULL) {
553 com_err(progname, 0, _("master keylist not initialized"));
558 retval = krb5_db_get_principal(util_context, master_princ, 0,
561 com_err(progname, retval, _("while getting master key principal %s"),
567 retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
569 com_err(progname, retval, _("while looking up active kvno list"));
574 retval = krb5_dbe_find_act_mkey(util_context, actkvno_list, &act_kvno,
577 com_err(progname, retval, _("while looking up active master key"));
582 printf("Master keys for Principal: %s\n", mkey_fullname);
584 for (cur_kb_node = master_keylist; cur_kb_node != NULL;
585 cur_kb_node = cur_kb_node->next) {
587 if ((retval = krb5_enctype_to_name(cur_kb_node->keyblock.enctype,
588 FALSE, enctype, sizeof(enctype)))) {
589 com_err(progname, retval, _("while getting enctype description"));
594 act_time = -1; /* assume actkvno entry not found */
595 for (cur_actkvno = actkvno_list; cur_actkvno != NULL;
596 cur_actkvno = cur_actkvno->next) {
597 if (cur_actkvno->act_kvno == cur_kb_node->kvno) {
598 act_time = cur_actkvno->act_time;
603 if (cur_kb_node->kvno == act_kvno) {
604 /* * indicates kvno is currently active */
605 retval = asprintf(&output_str,
606 _("KVNO: %d, Enctype: %s, Active on: %s *\n"),
607 cur_kb_node->kvno, enctype, strdate(act_time));
609 if (act_time != -1) {
610 retval = asprintf(&output_str,
611 _("KVNO: %d, Enctype: %s, Active on: %s\n"),
612 cur_kb_node->kvno, enctype, strdate(act_time));
614 retval = asprintf(&output_str,
615 _("KVNO: %d, Enctype: %s, No activate time "
616 "set\n"), cur_kb_node->kvno, enctype);
620 com_err(progname, ENOMEM, _("asprintf could not allocate enough "
621 "memory to hold output"));
625 printf("%s", output_str);
632 krb5_db_free_principal(util_context, master_entry);
634 krb5_dbe_free_actkvno_list(util_context, actkvno_list);
638 struct update_enc_mkvno {
639 unsigned int re_match_count;
640 unsigned int already_current;
641 unsigned int updated;
642 unsigned int dry_run : 1;
643 unsigned int verbose : 1;
644 #ifdef SOLARIS_REGEXPS
650 #if !defined(SOLARIS_REGEXPS) && !defined(POSIX_REGEXPS)
651 unsigned char placeholder;
655 /* XXX Duplicated in libkadm5srv! */
657 * Function: glob_to_regexp
661 * glob (r) the shell-style glob (?*[]) to convert
662 * realm (r) the default realm to append, or NULL
663 * regexp (w) the ed-style regexp created from glob
667 * regexp is filled in with allocated memory contained a regular
668 * expression to be used with re_comp/compile that matches what the
669 * shell-style glob would match. If glob does not contain an "@"
670 * character and realm is not NULL, "@*" is appended to the regexp.
672 * Conversion algorithm:
674 * quoted characters are copied quoted
675 * ? is converted to .
676 * * is converted to .*
677 * active characters are quoted: ^, $, .
678 * [ and ] are active but supported and have the same meaning, so
680 * other characters are copied
681 * regexp is anchored with ^ and $
683 static int glob_to_regexp(char *glob, char *realm, char **regexp)
688 /* validate the glob */
689 if (glob[strlen(glob)-1] == '\\')
692 /* A character of glob can turn into two in regexp, plus ^ and $ */
693 /* and trailing null. If glob has no @, also allocate space for */
695 append_realm = (realm != NULL) && (strchr(glob, '@') == NULL);
696 p = (char *) malloc(strlen(glob)*2+ 3 + (append_realm ? 3 : 0));
740 update_princ_encryption_1(void *cb, krb5_db_entry *ent)
742 struct update_enc_mkvno *p = cb;
744 krb5_error_code retval;
750 retval = krb5_unparse_name(util_context, ent->princ, &pname);
752 com_err(progname, retval,
753 _("getting string representation of principal name"));
757 if (krb5_principal_compare(util_context, ent->princ, master_princ)) {
761 #ifdef SOLARIS_REGEXPS
762 match = (step(pname, p->expbuf) != 0);
765 match = (regexec(&p->preg, pname, 0, NULL, 0) == 0);
768 match = (re_exec(pname) != 0);
774 retval = krb5_dbe_get_mkvno(util_context, ent, &old_mkvno);
776 com_err(progname, retval,
777 _("determining master key used for principal '%s'"), pname);
780 /* Line up "skip" and "update" messages for viewing. */
781 if (old_mkvno == new_mkvno) {
782 if (p->dry_run && p->verbose)
783 printf(_("would skip: %s\n"), pname);
785 printf(_("skipping: %s\n"), pname);
786 p->already_current++;
791 printf(_("would update: %s\n"), pname);
794 } else if (p->verbose)
795 printf(_("updating: %s\n"), pname);
796 retval = master_key_convert (util_context, ent);
798 com_err(progname, retval,
799 _("error re-encrypting key for principal '%s'"), pname);
802 if ((retval = krb5_timeofday(util_context, &now))) {
803 com_err(progname, retval, _("while getting current time"));
807 if ((retval = krb5_dbe_update_mod_princ_data(util_context, ent,
808 now, master_princ))) {
809 com_err(progname, retval,
810 _("while updating principal '%s' modification time"), pname);
814 ent->mask |= KADM5_KEY_DATA;
816 if ((retval = krb5_db_put_principal(util_context, ent))) {
817 com_err(progname, retval, _("while updating principal '%s' key data "
818 "in the database"), pname);
830 krb5_free_unparsed_name(util_context, pname);
834 extern int are_you_sure (const char *, ...)
835 #if !defined(__cplusplus) && (__GNUC__ > 2)
836 __attribute__((__format__(__printf__, 1, 2)))
841 are_you_sure (const char *format, ...)
846 va_start(va, format);
849 printf(_("\n(type 'yes' to confirm)? "));
851 if (fgets(ansbuf, sizeof(ansbuf), stdin) == NULL)
853 if (strcmp(ansbuf, "yes\n"))
859 kdb5_update_princ_encryption(int argc, char *argv[])
861 struct update_enc_mkvno data = { 0 };
862 char *name_pattern = NULL;
865 krb5_error_code retval;
866 krb5_actkvno_node *actkvno_list = 0;
867 krb5_db_entry *master_entry = NULL;
872 krb5_keyblock *act_mkey;
873 krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context);
874 krb5_flags iterflags = 0;
876 while ((optchar = getopt(argc, argv, "fnv")) != -1) {
893 if (argv[optind] != NULL) {
894 name_pattern = argv[optind];
895 if (argv[optind+1] != NULL)
899 if (master_keylist == NULL) {
900 com_err(progname, 0, _("master keylist not initialized"));
905 /* The glob_to_regexp code only cares if the "realm" parameter is
906 NULL or not; the string data is irrelevant. */
907 if (name_pattern == NULL)
909 if (glob_to_regexp(name_pattern, "hi", ®exp) != 0) {
910 com_err(progname, ENOMEM,
911 _("converting glob pattern '%s' to regular expression"),
918 #ifdef SOLARIS_REGEXPS
919 ((data.expbuf = compile(regexp, NULL, NULL)) == NULL)
922 ((regcomp(&data.preg, regexp, REG_NOSUB)) != 0)
925 ((msg = (char *) re_comp(regexp)) != NULL)
928 /* XXX syslog msg or regerr(regerrno) */
929 com_err(progname, 0, _("error compiling converted regexp '%s'"),
935 retval = krb5_db_get_principal(util_context, master_princ, 0,
938 com_err(progname, retval, _("while getting master key principal %s"),
944 retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
946 com_err(progname, retval, _("while looking up active kvno list"));
951 retval = krb5_dbe_find_act_mkey(util_context, actkvno_list, &new_mkvno,
954 com_err(progname, retval, _("while looking up active master key"));
958 new_master_keyblock = *act_mkey;
962 !are_you_sure(_("Re-encrypt all keys not using master key vno %u?"),
964 printf(_("OK, doing nothing.\n"));
970 printf(_("Principals whose keys WOULD BE re-encrypted to master "
971 "key vno %u:\n"), new_mkvno);
973 printf(_("Principals whose keys are being re-encrypted to master "
974 "key vno %u if necessary:\n"), new_mkvno);
979 /* Grab a write lock so we don't have to upgrade to a write lock and
980 * reopen the DB while iterating. */
981 iterflags = KRB5_DB_ITER_WRITE;
984 retval = krb5_db_iterate(util_context, name_pattern,
985 update_princ_encryption_1, &data, iterflags);
986 /* If exit_status is set, then update_princ_encryption_1 already
987 printed a message. */
988 if (retval != 0 && exit_status == 0) {
989 com_err(progname, retval, _("trying to process principal database"));
993 printf(_("%u principals processed: %u would be updated, %u already "
995 data.re_match_count, data.updated, data.already_current);
997 printf(_("%u principals processed: %u updated, %u already current\n"),
998 data.re_match_count, data.updated, data.already_current);
1002 krb5_db_free_principal(util_context, master_entry);
1004 #ifdef POSIX_REGEXPS
1005 regfree(&data.preg);
1007 memset(&new_master_keyblock, 0, sizeof(new_master_keyblock));
1008 krb5_dbe_free_actkvno_list(util_context, actkvno_list);
1011 struct kvnos_in_use {
1013 unsigned int use_count;
1017 krb5_context kcontext;
1018 struct kvnos_in_use *kvnos;
1019 unsigned int num_kvnos;
1022 static krb5_error_code
1023 find_mkvnos_in_use(krb5_pointer ptr,
1024 krb5_db_entry *entry)
1026 krb5_error_code retval;
1027 struct purge_args * args;
1031 args = (struct purge_args *) ptr;
1033 retval = krb5_dbe_get_mkvno(args->kcontext, entry, &mkvno);
1037 for (i = 0; i < args->num_kvnos; i++) {
1038 if (args->kvnos[i].kvno == mkvno) {
1039 /* XXX do I need to worry about use_count wrapping? */
1040 args->kvnos[i].use_count++;
1048 kdb5_purge_mkeys(int argc, char *argv[])
1051 krb5_error_code retval;
1053 krb5_db_entry *master_entry = NULL;
1054 krb5_boolean force = FALSE, dry_run = FALSE, verbose = FALSE;
1055 struct purge_args args;
1057 unsigned int i, j, k, num_kvnos_inuse, num_kvnos_purged;
1058 unsigned int old_key_data_count;
1059 krb5_actkvno_node *actkvno_list = NULL, *actkvno_entry, *prev_actkvno_entry;
1060 krb5_mkey_aux_node *mkey_aux_list = NULL, *mkey_aux_entry, *prev_mkey_aux_entry;
1061 krb5_key_data *old_key_data;
1064 * Verify that the master key list has been initialized before doing
1067 if (krb5_db_mkey_list_alias(util_context) == NULL) {
1068 com_err(progname, KRB5_KDB_DBNOTINITED,
1069 _("master keylist not initialized"));
1074 memset(&args, 0, sizeof(args));
1077 while ((optchar = getopt(argc, argv, "fnv")) != -1) {
1083 dry_run = TRUE; /* mkey princ will not be modified */
1084 force = TRUE; /* implied */
1096 retval = krb5_db_get_principal(util_context, master_princ, 0,
1099 com_err(progname, retval, _("while getting master key principal %s"),
1102 goto cleanup_return;
1106 printf(_("Will purge all unused master keys stored in the '%s' "
1107 "principal, are you sure?\n"), mkey_fullname);
1108 printf(_("(type 'yes' to confirm)? "));
1109 if (fgets(buf, sizeof(buf), stdin) == NULL) {
1111 goto cleanup_return;
1113 if (strcmp(buf, "yes\n")) {
1115 goto cleanup_return;
1117 printf(_("OK, purging unused master keys from '%s'...\n"),
1121 /* save the old keydata */
1122 old_key_data_count = master_entry->n_key_data;
1123 if (old_key_data_count == 1) {
1125 printf(_("There is only one master key which can not be "
1127 goto cleanup_return;
1129 old_key_data = master_entry->key_data;
1131 args.kvnos = (struct kvnos_in_use *) malloc(sizeof(struct kvnos_in_use) * old_key_data_count);
1132 if (args.kvnos == NULL) {
1134 com_err(progname, ENOMEM, _("while allocating args.kvnos"));
1136 goto cleanup_return;
1138 memset(args.kvnos, 0, sizeof(struct kvnos_in_use) * old_key_data_count);
1139 args.num_kvnos = old_key_data_count;
1140 args.kcontext = util_context;
1142 /* populate the kvnos array with all the current mkvnos */
1143 for (i = 0; i < old_key_data_count; i++)
1144 args.kvnos[i].kvno = master_entry->key_data[i].key_data_kvno;
1146 if ((retval = krb5_db_iterate(util_context,
1149 (krb5_pointer) &args, 0))) {
1150 com_err(progname, retval, _("while finding master keys in use"));
1152 goto cleanup_return;
1155 * args.kvnos has been marked with the mkvno's that are currently protecting
1159 printf(_("Would purge the following master key(s) from %s:\n"),
1162 printf(_("Purging the following master key(s) from %s:\n"),
1166 /* find # of keys still in use or print out verbose info */
1167 for (i = num_kvnos_inuse = num_kvnos_purged = 0; i < args.num_kvnos; i++) {
1168 if (args.kvnos[i].use_count > 0) {
1171 /* this key would be deleted */
1172 if (args.kvnos[i].kvno == master_kvno) {
1173 com_err(progname, KRB5_KDB_STORED_MKEY_NOTCURRENT,
1174 _("master key stash file needs updating, command "
1177 goto cleanup_return;
1180 printf(_("KVNO: %d\n"), args.kvnos[i].kvno);
1183 /* didn't find any keys to purge */
1184 if (num_kvnos_inuse == args.num_kvnos) {
1185 printf(_("All keys in use, nothing purged.\n"));
1186 goto cleanup_return;
1189 /* bail before doing anything else */
1190 printf(_("%d key(s) would be purged.\n"), num_kvnos_purged);
1191 goto cleanup_return;
1194 retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
1196 com_err(progname, retval, _("while looking up active kvno list"));
1198 goto cleanup_return;
1201 retval = krb5_dbe_lookup_mkey_aux(util_context, master_entry, &mkey_aux_list);
1203 com_err(progname, retval, _("while looking up mkey aux data list"));
1205 goto cleanup_return;
1208 master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * num_kvnos_inuse);
1209 if (master_entry->key_data == NULL) {
1211 com_err(progname, ENOMEM, _("while allocating key_data"));
1213 goto cleanup_return;
1215 memset(master_entry->key_data, 0, sizeof(krb5_key_data) * num_kvnos_inuse);
1216 master_entry->n_key_data = num_kvnos_inuse; /* there's only 1 mkey per kvno */
1219 * Assuming that the latest mkey will not be purged because it will always
1220 * be "in use" so this code will not bother with encrypting keys again.
1222 for (i = k = 0; i < old_key_data_count; i++) {
1223 for (j = 0; j < args.num_kvnos; j++) {
1224 if (args.kvnos[j].kvno == (krb5_kvno) old_key_data[i].key_data_kvno) {
1225 if (args.kvnos[j].use_count != 0) {
1226 master_entry->key_data[k++] = old_key_data[i];
1227 memset(&old_key_data[i], 0, sizeof(old_key_data[i]));
1230 /* remove unused mkey */
1231 /* adjust the actkno data */
1232 for (prev_actkvno_entry = actkvno_entry = actkvno_list;
1233 actkvno_entry != NULL;
1234 actkvno_entry = actkvno_entry->next) {
1236 if (actkvno_entry->act_kvno == args.kvnos[j].kvno) {
1237 if (actkvno_entry == actkvno_list) {
1238 /* remove from head */
1239 actkvno_list = actkvno_entry->next;
1240 prev_actkvno_entry = actkvno_list;
1241 } else if (actkvno_entry->next == NULL) {
1242 /* remove from tail */
1243 prev_actkvno_entry->next = NULL;
1245 /* remove in between */
1246 prev_actkvno_entry->next = actkvno_entry->next;
1248 actkvno_entry->next = NULL;
1249 krb5_dbe_free_actkvno_list(util_context, actkvno_entry);
1250 break; /* deleted entry, no need to loop further */
1252 prev_actkvno_entry = actkvno_entry;
1255 /* adjust the mkey aux data */
1256 for (prev_mkey_aux_entry = mkey_aux_entry = mkey_aux_list;
1257 mkey_aux_entry != NULL;
1258 mkey_aux_entry = mkey_aux_entry->next) {
1260 if (mkey_aux_entry->mkey_kvno == args.kvnos[j].kvno) {
1261 if (mkey_aux_entry == mkey_aux_list) {
1262 mkey_aux_list = mkey_aux_entry->next;
1263 prev_mkey_aux_entry = mkey_aux_list;
1264 } else if (mkey_aux_entry->next == NULL) {
1265 prev_mkey_aux_entry->next = NULL;
1267 prev_mkey_aux_entry->next = mkey_aux_entry->next;
1269 mkey_aux_entry->next = NULL;
1270 krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_entry);
1271 break; /* deleted entry, no need to loop further */
1273 prev_mkey_aux_entry = mkey_aux_entry;
1280 assert(k == num_kvnos_inuse);
1282 /* Free any key data entries we did not consume in the loop above. */
1283 for (i = 0; i < old_key_data_count; i++)
1284 krb5_dbe_free_key_data_contents(util_context, &old_key_data[i]);
1287 if ((retval = krb5_dbe_update_actkvno(util_context, master_entry,
1289 com_err(progname, retval,
1290 _("while updating actkvno data for master principal entry"));
1292 goto cleanup_return;
1295 if ((retval = krb5_dbe_update_mkey_aux(util_context, master_entry,
1297 com_err(progname, retval,
1298 _("while updating mkey_aux data for master principal entry"));
1303 if ((retval = krb5_timeofday(util_context, &now))) {
1304 com_err(progname, retval, _("while getting current time"));
1306 goto cleanup_return;
1309 if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry,
1310 now, master_princ))) {
1311 com_err(progname, retval, _("while updating the master key principal "
1312 "modification time"));
1314 goto cleanup_return;
1317 master_entry->mask |= KADM5_KEY_DATA | KADM5_TL_DATA;
1319 if ((retval = krb5_db_put_principal(util_context, master_entry))) {
1320 com_err(progname, retval,
1321 _("while adding master key entry to the database"));
1323 goto cleanup_return;
1325 printf(_("%d key(s) purged.\n"), num_kvnos_purged);
1328 krb5_db_free_principal(util_context, master_entry);
1330 krb5_dbe_free_actkvno_list(util_context, actkvno_list);
1331 krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_list);