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.
11 #include <kadm5/server_internal.h>
12 #include <kadm5/admin.h>
13 #include <adm_proto.h>
14 #include "kdb5_util.h"
16 #if defined(HAVE_COMPILE) && defined(HAVE_STEP)
17 #define SOLARIS_REGEXPS
18 #elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXEC)
20 #elif defined(HAVE_RE_COMP) && defined(HAVE_RE_EXEC)
23 #error I cannot find any regexp functions
25 #ifdef SOLARIS_REGEXPS
32 extern krb5_keyblock master_keyblock; /* current mkey */
33 extern krb5_kvno master_kvno;
34 extern krb5_principal master_princ;
35 extern krb5_keylist_node *master_keylist;
36 extern krb5_data master_salt;
37 extern char *mkey_password;
38 extern char *progname;
39 extern int exit_status;
40 extern kadm5_config_params global_params;
41 extern krb5_context util_context;
42 extern time_t get_date(char *);
44 static char *strdate(krb5_timestamp when)
50 tm = localtime(&lcltim);
51 strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm);
56 get_next_kvno(krb5_context context, krb5_db_entry *entry)
60 new_kvno = krb5_db_get_key_data_kvno(context, entry->n_key_data,
63 /* deal with wrapping */
65 new_kvno = 1; /* knvo must not be 0 as this is special value (IGNORE_VNO) */
71 add_new_mkey(krb5_context context, krb5_db_entry *master_entry,
72 krb5_keyblock *new_mkey, krb5_kvno use_mkvno)
74 krb5_error_code retval = 0;
75 int old_key_data_count, i;
76 krb5_kvno new_mkey_kvno;
77 krb5_key_data tmp_key_data, *old_key_data;
78 krb5_mkey_aux_node *mkey_aux_data_head = NULL, **mkey_aux_data;
79 krb5_keylist_node *keylist_node;
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 /* save the old keydata */
88 old_key_data_count = master_entry->n_key_data;
89 old_key_data = master_entry->key_data;
91 /* alloc enough space to hold new and existing key_data */
93 * The encrypted key is malloc'ed by krb5_dbe_encrypt_key_data and
94 * krb5_key_data key_data_contents is a pointer to this key. Using some
95 * logic from master_key_convert().
97 master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) *
98 (old_key_data_count + 1));
99 if (master_entry->key_data == NULL)
102 memset(master_entry->key_data, 0,
103 sizeof(krb5_key_data) * (old_key_data_count + 1));
104 master_entry->n_key_data = old_key_data_count + 1;
106 /* Note, mkey does not have salt */
107 /* add new mkey encrypted with itself to mkey princ entry */
108 if ((retval = krb5_dbe_encrypt_key_data(context, new_mkey, new_mkey, NULL,
110 &master_entry->key_data[0]))) {
113 /* the mvkno should be that of the newest mkey */
114 if ((retval = krb5_dbe_update_mkvno(context, master_entry, new_mkey_kvno))) {
115 krb5_free_key_data_contents(context, &master_entry->key_data[0]);
119 * Need to decrypt old keys with the current mkey which is in the global
120 * master_keyblock and encrypt those keys with the latest mkey. And while
121 * the old keys are being decrypted, use those to create the
122 * KRB5_TL_MKEY_AUX entries which store the latest mkey encrypted by one of
125 * The new mkey is followed by existing keys.
127 * First, set up for creating a krb5_mkey_aux_node list which will be used
128 * to update the mkey aux data for the mkey princ entry.
130 mkey_aux_data_head = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
131 if (mkey_aux_data_head == NULL) {
135 memset(mkey_aux_data_head, 0, sizeof(krb5_mkey_aux_node));
136 mkey_aux_data = &mkey_aux_data_head;
138 for (keylist_node = master_keylist, i = 1; keylist_node != NULL;
139 keylist_node = keylist_node->next, i++) {
142 * Create a list of krb5_mkey_aux_node nodes. One node contains the new
143 * mkey encrypted by an old mkey and the old mkey's kvno (one node per
146 if (*mkey_aux_data == NULL) {
147 /* *mkey_aux_data points to next field of previous node */
148 *mkey_aux_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
149 if (*mkey_aux_data == NULL) {
153 memset(*mkey_aux_data, 0, sizeof(krb5_mkey_aux_node));
156 memset(&tmp_key_data, 0, sizeof(tmp_key_data));
157 /* encrypt the new mkey with the older mkey */
158 retval = krb5_dbe_encrypt_key_data(context, &keylist_node->keyblock,
159 new_mkey, NULL, (int) new_mkey_kvno,
164 (*mkey_aux_data)->latest_mkey = tmp_key_data;
165 (*mkey_aux_data)->mkey_kvno = keylist_node->kvno;
166 mkey_aux_data = &((*mkey_aux_data)->next);
169 * Store old key in master_entry keydata past the new mkey
171 retval = krb5_dbe_encrypt_key_data(context, new_mkey,
172 &keylist_node->keyblock,
173 NULL, (int) keylist_node->kvno,
174 &master_entry->key_data[i]);
178 assert(i == old_key_data_count + 1);
180 if ((retval = krb5_dbe_update_mkey_aux(context, master_entry,
181 mkey_aux_data_head))) {
184 master_entry->mask |= KADM5_KEY_DATA;
187 krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_head);
192 kdb5_add_mkey(int argc, char *argv[])
195 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;
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 memset(&master_princ, 0, sizeof(master_princ));
215 master_salt.data = NULL;
217 while ((optchar = getopt(argc, argv, "e:s")) != -1) {
220 if (krb5_string_to_enctype(optarg, &new_master_enctype)) {
221 com_err(progname, EINVAL, _("%s is an invalid enctype"),
237 if (new_master_enctype == ENCTYPE_UNKNOWN)
238 new_master_enctype = global_params.enctype;
240 /* assemble & parse the master key name */
241 if ((retval = krb5_db_setup_mkey_name(util_context,
242 global_params.mkey_name,
244 &mkey_fullname, &master_princ))) {
245 com_err(progname, retval, _("while setting up master key name"));
250 retval = krb5_db_get_principal(util_context, master_princ, 0,
253 com_err(progname, retval, _("while getting master key principal %s"),
259 printf(_("Creating new master key for master key principal '%s'\n"),
262 printf(_("You will be prompted for a new database Master Password.\n"));
263 printf(_("It is important that you NOT FORGET this password.\n"));
267 pw_str = malloc(pw_size);
268 if (pw_str == NULL) {
269 com_err(progname, ENOMEM, _("while creating new master key"));
274 retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
277 com_err(progname, retval,
278 _("while reading new master key from keyboard"));
282 new_mkey_password = pw_str;
284 pwd.data = new_mkey_password;
285 pwd.length = strlen(new_mkey_password);
286 retval = krb5_principal2salt(util_context, master_princ, &master_salt);
288 com_err(progname, retval, _("while calculating master key salt"));
293 retval = krb5_c_string_to_key(util_context, new_master_enctype,
294 &pwd, &master_salt, &new_mkeyblock);
296 com_err(progname, retval,
297 _("while transforming master key from password"));
302 new_mkey_kvno = get_next_kvno(util_context, master_entry);
303 retval = add_new_mkey(util_context, master_entry, &new_mkeyblock,
306 com_err(progname, retval,
307 _("adding new master key to master principal"));
312 if ((retval = krb5_timeofday(util_context, &now))) {
313 com_err(progname, retval, _("while getting current time"));
318 if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry,
319 now, master_princ))) {
320 com_err(progname, retval, _("while updating the master key principal "
321 "modification time"));
326 if ((retval = krb5_db_put_principal(util_context, master_entry))) {
327 (void) krb5_db_fini(util_context);
328 com_err(progname, retval, _("while adding master key entry to the "
335 retval = krb5_db_store_master_key(util_context,
336 global_params.stash_file,
342 com_err(progname, errno, _("while storing key"));
343 printf(_("Warning: couldn't stash master key.\n"));
349 (void) krb5_db_fini(util_context);
350 zap((char *)master_keyblock.contents, master_keyblock.length);
351 free(master_keyblock.contents);
352 zap((char *)new_mkeyblock.contents, new_mkeyblock.length);
353 free(new_mkeyblock.contents);
355 zap(pw_str, pw_size);
358 free(master_salt.data);
359 krb5_free_unparsed_name(util_context, mkey_fullname);
364 kdb5_use_mkey(int argc, char *argv[])
366 krb5_error_code retval;
367 char *mkey_fullname = NULL;
369 krb5_timestamp now, start_time;
370 krb5_actkvno_node *actkvno_list = NULL, *new_actkvno = NULL,
371 *prev_actkvno, *cur_actkvno;
372 krb5_db_entry *master_entry;
373 krb5_keylist_node *keylist_node;
374 krb5_boolean inserted = FALSE;
376 memset(&master_princ, 0, sizeof(master_princ));
378 if (argc < 2 || argc > 3) {
379 /* usage calls exit */
383 use_kvno = atoi(argv[1]);
385 com_err(progname, EINVAL, _("0 is an invalid KVNO value"));
389 /* verify use_kvno is valid */
390 for (keylist_node = master_keylist; keylist_node != NULL;
391 keylist_node = keylist_node->next) {
392 if (use_kvno == keylist_node->kvno)
396 com_err(progname, EINVAL, _("%d is an invalid KVNO value"),
403 if ((retval = krb5_timeofday(util_context, &now))) {
404 com_err(progname, retval, _("while getting current time"));
410 time_t t = get_date(argv[2]);
412 com_err(progname, 0, _("could not parse date-time string '%s'"),
417 start_time = (krb5_timestamp) t;
426 * 2. get krb5_actkvno_node list
427 * 3. add use_kvno to actkvno list (sorted in right spot)
428 * 4. update mkey princ's tl data
432 /* assemble & parse the master key name */
433 if ((retval = krb5_db_setup_mkey_name(util_context,
434 global_params.mkey_name,
436 &mkey_fullname, &master_princ))) {
437 com_err(progname, retval, _("while setting up master key name"));
442 retval = krb5_db_get_principal(util_context, master_princ, 0,
445 com_err(progname, retval, _("while getting master key principal %s"),
451 retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
453 com_err(progname, retval,
454 _("while looking up active version of master key"));
460 * If an entry already exists with the same kvno either delete it or if it's
461 * the only entry, just set its active time.
463 for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
465 prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
467 if (cur_actkvno->act_kvno == use_kvno) {
470 prev_actkvno->next = cur_actkvno->next;
471 cur_actkvno->next = NULL;
472 krb5_dbe_free_actkvno_list(util_context, cur_actkvno);
474 if (cur_actkvno->next) {
475 /* delete it from front of list */
476 actkvno_list = cur_actkvno->next;
477 cur_actkvno->next = NULL;
478 krb5_dbe_free_actkvno_list(util_context, cur_actkvno);
480 /* There's only one entry, go ahead and change the time */
481 cur_actkvno->act_time = start_time;
490 /* alloc enough space to hold new and existing key_data */
491 new_actkvno = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node));
492 if (new_actkvno == NULL) {
493 com_err(progname, ENOMEM, _("while adding new master key"));
497 memset(new_actkvno, 0, sizeof(krb5_actkvno_node));
498 new_actkvno->act_kvno = use_kvno;
499 new_actkvno->act_time = start_time;
501 /* insert new act kvno node */
503 if (actkvno_list == NULL) {
504 /* new actkvno is the list */
505 actkvno_list = new_actkvno;
507 for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
509 prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
511 if (new_actkvno->act_time < cur_actkvno->act_time) {
513 prev_actkvno->next = new_actkvno;
514 new_actkvno->next = cur_actkvno;
516 new_actkvno->next = actkvno_list;
517 actkvno_list = new_actkvno;
520 } else if (cur_actkvno->next == NULL) {
521 /* end of line, just add new node to end of list */
522 cur_actkvno->next = new_actkvno;
529 if (actkvno_list->act_time > now) {
530 com_err(progname, EINVAL,
531 _("there must be one master key currently active"));
536 if ((retval = krb5_dbe_update_actkvno(util_context, master_entry,
538 com_err(progname, retval,
539 _("while updating actkvno data for master principal entry"));
544 if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry,
545 now, master_princ))) {
546 com_err(progname, retval, _("while updating the master key principal "
547 "modification time"));
552 if ((retval = krb5_db_put_principal(util_context, master_entry))) {
553 (void) krb5_db_fini(util_context);
554 com_err(progname, retval,
555 _("while adding master key entry to the database"));
562 (void) krb5_db_fini(util_context);
563 krb5_free_unparsed_name(util_context, mkey_fullname);
564 krb5_free_principal(util_context, master_princ);
565 krb5_dbe_free_actkvno_list(util_context, actkvno_list);
570 kdb5_list_mkeys(int argc, char *argv[])
572 krb5_error_code retval;
573 char *mkey_fullname = NULL, *output_str = NULL, enctype[BUFSIZ];
575 krb5_timestamp act_time;
576 krb5_actkvno_node *actkvno_list = NULL, *cur_actkvno;
577 krb5_db_entry *master_entry;
578 krb5_keylist_node *cur_kb_node;
579 krb5_keyblock *act_mkey;
581 if (master_keylist == NULL) {
582 com_err(progname, 0, _("master keylist not initialized"));
587 /* assemble & parse the master key name */
588 if ((retval = krb5_db_setup_mkey_name(util_context,
589 global_params.mkey_name,
591 &mkey_fullname, &master_princ))) {
592 com_err(progname, retval, _("while setting up master key name"));
597 retval = krb5_db_get_principal(util_context, master_princ, 0,
600 com_err(progname, retval, _("while getting master key principal %s"),
606 retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
608 com_err(progname, retval, _("while looking up active kvno list"));
613 if (actkvno_list == NULL) {
614 act_kvno = master_entry->key_data[0].key_data_kvno;
616 retval = krb5_dbe_find_act_mkey(util_context, master_keylist,
617 actkvno_list, &act_kvno, &act_mkey);
618 if (retval == KRB5_KDB_NOACTMASTERKEY) {
619 /* Maybe we went through a time warp, and the only keys
620 with activation dates have them set in the future? */
621 com_err(progname, retval, "");
624 } else if (retval != 0) {
625 com_err(progname, retval, _("while looking up active master key"));
631 printf("Master keys for Principal: %s\n", mkey_fullname);
633 for (cur_kb_node = master_keylist; cur_kb_node != NULL;
634 cur_kb_node = cur_kb_node->next) {
636 if ((retval = krb5_enctype_to_name(cur_kb_node->keyblock.enctype,
637 FALSE, enctype, sizeof(enctype)))) {
638 com_err(progname, retval, _("while getting enctype description"));
643 if (actkvno_list != NULL) {
644 act_time = -1; /* assume actkvno entry not found */
645 for (cur_actkvno = actkvno_list; cur_actkvno != NULL;
646 cur_actkvno = cur_actkvno->next) {
647 if (cur_actkvno->act_kvno == cur_kb_node->kvno) {
648 act_time = cur_actkvno->act_time;
654 * mkey princ doesn't have an active knvo list so assume the current
657 if ((retval = krb5_timeofday(util_context, &act_time))) {
658 com_err(progname, retval, _("while getting current time"));
664 if (cur_kb_node->kvno == act_kvno) {
665 /* * indicates kvno is currently active */
666 retval = asprintf(&output_str,
667 _("KNVO: %d, Enctype: %s, Active on: %s *\n"),
668 cur_kb_node->kvno, enctype, strdate(act_time));
670 if (act_time != -1) {
671 retval = asprintf(&output_str,
672 _("KNVO: %d, Enctype: %s, Active on: %s\n"),
673 cur_kb_node->kvno, enctype, strdate(act_time));
675 retval = asprintf(&output_str,
676 _("KNVO: %d, Enctype: %s, No activate time "
677 "set\n"), cur_kb_node->kvno, enctype);
681 com_err(progname, ENOMEM, _("asprintf could not allocate enough "
682 "memory to hold output"));
686 printf("%s", output_str);
693 (void) krb5_db_fini(util_context);
694 krb5_free_unparsed_name(util_context, mkey_fullname);
696 krb5_free_principal(util_context, master_princ);
697 krb5_dbe_free_actkvno_list(util_context, actkvno_list);
701 struct update_enc_mkvno {
702 unsigned int re_match_count;
703 unsigned int already_current;
704 unsigned int updated;
705 unsigned int dry_run : 1;
706 unsigned int verbose : 1;
707 #ifdef SOLARIS_REGEXPS
713 #if !defined(SOLARIS_REGEXPS) && !defined(POSIX_REGEXPS)
714 unsigned char placeholder;
718 /* XXX Duplicated in libkadm5srv! */
720 * Function: glob_to_regexp
724 * glob (r) the shell-style glob (?*[]) to convert
725 * realm (r) the default realm to append, or NULL
726 * regexp (w) the ed-style regexp created from glob
730 * regexp is filled in with allocated memory contained a regular
731 * expression to be used with re_comp/compile that matches what the
732 * shell-style glob would match. If glob does not contain an "@"
733 * character and realm is not NULL, "@*" is appended to the regexp.
735 * Conversion algorithm:
737 * quoted characters are copied quoted
738 * ? is converted to .
739 * * is converted to .*
740 * active characters are quoted: ^, $, .
741 * [ and ] are active but supported and have the same meaning, so
743 * other characters are copied
744 * regexp is anchored with ^ and $
746 static int glob_to_regexp(char *glob, char *realm, char **regexp)
751 /* validate the glob */
752 if (glob[strlen(glob)-1] == '\\')
755 /* A character of glob can turn into two in regexp, plus ^ and $ */
756 /* and trailing null. If glob has no @, also allocate space for */
758 append_realm = (realm != NULL) && (strchr(glob, '@') == NULL);
759 p = (char *) malloc(strlen(glob)*2+ 3 + (append_realm ? 3 : 0));
803 update_princ_encryption_1(void *cb, krb5_db_entry *ent)
805 struct update_enc_mkvno *p = cb;
807 krb5_error_code retval;
813 retval = krb5_unparse_name(util_context, ent->princ, &pname);
815 com_err(progname, retval,
816 _("getting string representation of principal name"));
820 if (krb5_principal_compare(util_context, ent->princ, master_princ)) {
824 #ifdef SOLARIS_REGEXPS
825 match = (step(pname, p->expbuf) != 0);
828 match = (regexec(&p->preg, pname, 0, NULL, 0) == 0);
831 match = (re_exec(pname) != 0);
837 retval = krb5_dbe_get_mkvno(util_context, ent, master_keylist, &old_mkvno);
839 com_err(progname, retval,
840 _("determining master key used for principal '%s'"), pname);
843 /* Line up "skip" and "update" messages for viewing. */
844 if (old_mkvno == new_mkvno) {
845 if (p->dry_run && p->verbose)
846 printf(_("would skip: %s\n"), pname);
848 printf(_("skipping: %s\n"), pname);
849 p->already_current++;
854 printf(_("would update: %s\n"), pname);
857 } else if (p->verbose)
858 printf(_("updating: %s\n"), pname);
859 retval = master_key_convert (util_context, ent);
861 com_err(progname, retval,
862 _("error re-encrypting key for principal '%s'"), pname);
865 if ((retval = krb5_timeofday(util_context, &now))) {
866 com_err(progname, retval, _("while getting current time"));
870 if ((retval = krb5_dbe_update_mod_princ_data(util_context, ent,
871 now, master_princ))) {
872 com_err(progname, retval,
873 _("while updating principal '%s' modification time"), pname);
877 ent->mask |= KADM5_KEY_DATA;
879 if ((retval = krb5_db_put_principal(util_context, ent))) {
880 com_err(progname, retval, _("while updating principal '%s' key data "
881 "in the database"), pname);
893 krb5_free_unparsed_name(util_context, pname);
897 extern int are_you_sure (const char *, ...)
898 #if !defined(__cplusplus) && (__GNUC__ > 2)
899 __attribute__((__format__(__printf__, 1, 2)))
904 are_you_sure (const char *format, ...)
909 va_start(va, format);
912 printf(_("\n(type 'yes' to confirm)? "));
914 if (fgets(ansbuf, sizeof(ansbuf), stdin) == NULL)
916 if (strcmp(ansbuf, "yes\n"))
922 kdb5_update_princ_encryption(int argc, char *argv[])
924 struct update_enc_mkvno data = { 0 };
925 char *name_pattern = NULL;
928 krb5_error_code retval;
929 krb5_actkvno_node *actkvno_list = 0;
930 krb5_db_entry *master_entry;
931 char *mkey_fullname = 0;
936 krb5_keyblock *tmp_keyblock = NULL;
938 while ((optchar = getopt(argc, argv, "fnv")) != -1) {
955 if (argv[optind] != NULL) {
956 name_pattern = argv[optind];
957 if (argv[optind+1] != NULL)
961 retval = krb5_unparse_name(util_context, master_princ, &mkey_fullname);
963 com_err(progname, retval, _("while formatting master principal name"));
968 if (master_keylist == NULL) {
969 com_err(progname, retval, _("master keylist not initialized"));
974 /* The glob_to_regexp code only cares if the "realm" parameter is
975 NULL or not; the string data is irrelevant. */
976 if (name_pattern == NULL)
978 if (glob_to_regexp(name_pattern, "hi", ®exp) != 0) {
979 com_err(progname, ENOMEM,
980 _("converting glob pattern '%s' to regular expression"),
987 #ifdef SOLARIS_REGEXPS
988 ((data.expbuf = compile(regexp, NULL, NULL)) == NULL)
991 ((regcomp(&data.preg, regexp, REG_NOSUB)) != 0)
994 ((msg = (char *) re_comp(regexp)) != NULL)
997 /* XXX syslog msg or regerr(regerrno) */
998 com_err(progname, 0, _("error compiling converted regexp '%s'"),
1004 retval = krb5_db_get_principal(util_context, master_princ, 0,
1007 com_err(progname, retval, _("while getting master key principal %s"),
1013 retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
1015 com_err(progname, retval, _("while looking up active kvno list"));
1020 /* Master key is always stored encrypted in the latest version of
1022 new_mkvno = krb5_db_get_key_data_kvno(util_context,
1023 master_entry->n_key_data,
1024 master_entry->key_data);
1026 retval = krb5_dbe_find_mkey(util_context, master_keylist,
1027 master_entry, &tmp_keyblock);
1029 com_err(progname, retval, _("retrieving the most recent master key"));
1033 new_master_keyblock = *tmp_keyblock;
1037 !are_you_sure(_("Re-encrypt all keys not using master key vno %u?"),
1039 printf(_("OK, doing nothing.\n"));
1045 printf(_("Principals whose keys WOULD BE re-encrypted to master "
1046 "key vno %u:\n"), new_mkvno);
1048 printf(_("Principals whose keys are being re-encrypted to master "
1049 "key vno %u if necessary:\n"), new_mkvno);
1053 retval = krb5_db_iterate(util_context, name_pattern,
1054 update_princ_encryption_1, &data);
1055 /* If exit_status is set, then update_princ_encryption_1 already
1056 printed a message. */
1057 if (retval != 0 && exit_status == 0) {
1058 com_err(progname, retval, _("trying to process principal database"));
1061 (void) krb5_db_fini(util_context);
1063 printf(_("%u principals processed: %u would be updated, %u already "
1065 data.re_match_count, data.updated, data.already_current);
1067 printf(_("%u principals processed: %u updated, %u already current\n"),
1068 data.re_match_count, data.updated, data.already_current);
1073 memset(&new_master_keyblock, 0, sizeof(new_master_keyblock));
1074 krb5_free_keyblock(util_context, tmp_keyblock);
1075 krb5_free_unparsed_name(util_context, mkey_fullname);
1076 krb5_dbe_free_actkvno_list(util_context, actkvno_list);
1079 struct kvnos_in_use {
1081 unsigned int use_count;
1085 krb5_context kcontext;
1086 struct kvnos_in_use *kvnos;
1087 unsigned int num_kvnos;
1090 static krb5_error_code
1091 find_mkvnos_in_use(krb5_pointer ptr,
1092 krb5_db_entry *entry)
1094 krb5_error_code retval;
1095 struct purge_args * args;
1099 args = (struct purge_args *) ptr;
1101 retval = krb5_dbe_get_mkvno(args->kcontext, entry, master_keylist, &mkvno);
1105 for (i = 0; i < args->num_kvnos; i++) {
1106 if (args->kvnos[i].kvno == mkvno) {
1107 /* XXX do I need to worry about use_count wrapping? */
1108 args->kvnos[i].use_count++;
1116 kdb5_purge_mkeys(int argc, char *argv[])
1119 krb5_error_code retval;
1120 char *mkey_fullname = NULL;
1122 krb5_db_entry *master_entry;
1123 krb5_boolean force = FALSE, dry_run = FALSE, verbose = FALSE;
1124 struct purge_args args;
1126 unsigned int i, j, k, num_kvnos_inuse, num_kvnos_purged;
1127 unsigned int old_key_data_count;
1128 krb5_actkvno_node *actkvno_list = NULL, *actkvno_entry, *prev_actkvno_entry;
1129 krb5_mkey_aux_node *mkey_aux_list = NULL, *mkey_aux_entry, *prev_mkey_aux_entry;
1130 krb5_key_data *old_key_data;
1132 memset(&master_princ, 0, sizeof(master_princ));
1133 memset(&args, 0, sizeof(args));
1136 while ((optchar = getopt(argc, argv, "fnv")) != -1) {
1142 dry_run = TRUE; /* mkey princ will not be modified */
1143 force = TRUE; /* implied */
1155 if (master_keylist == NULL) {
1156 com_err(progname, 0, _("master keylist not initialized"));
1161 /* assemble & parse the master key name */
1162 if ((retval = krb5_db_setup_mkey_name(util_context,
1163 global_params.mkey_name,
1164 global_params.realm,
1165 &mkey_fullname, &master_princ))) {
1166 com_err(progname, retval, _("while setting up master key name"));
1171 retval = krb5_db_get_principal(util_context, master_princ, 0,
1174 com_err(progname, retval, _("while getting master key principal %s"),
1177 goto cleanup_return;
1181 printf(_("Will purge all unused master keys stored in the '%s' "
1182 "principal, are you sure?\n"), mkey_fullname);
1183 printf(_("(type 'yes' to confirm)? "));
1184 if (fgets(buf, sizeof(buf), stdin) == NULL) {
1186 goto cleanup_return;
1188 if (strcmp(buf, "yes\n")) {
1190 goto cleanup_return;
1192 printf(_("OK, purging unused master keys from '%s'...\n"),
1196 /* save the old keydata */
1197 old_key_data_count = master_entry->n_key_data;
1198 if (old_key_data_count == 1) {
1200 printf(_("There is only one master key which can not be "
1202 goto cleanup_return;
1204 old_key_data = master_entry->key_data;
1206 args.kvnos = (struct kvnos_in_use *) malloc(sizeof(struct kvnos_in_use) * old_key_data_count);
1207 if (args.kvnos == NULL) {
1209 com_err(progname, ENOMEM, _("while allocating args.kvnos"));
1211 goto cleanup_return;
1213 memset(args.kvnos, 0, sizeof(struct kvnos_in_use) * old_key_data_count);
1214 args.num_kvnos = old_key_data_count;
1215 args.kcontext = util_context;
1217 /* populate the kvnos array with all the current mkvnos */
1218 for (i = 0; i < old_key_data_count; i++)
1219 args.kvnos[i].kvno = master_entry->key_data[i].key_data_kvno;
1221 if ((retval = krb5_db_iterate(util_context,
1224 (krb5_pointer) &args))) {
1225 com_err(progname, retval, _("while finding master keys in use"));
1227 goto cleanup_return;
1230 * args.kvnos has been marked with the mkvno's that are currently protecting
1234 printf(_("Would purge the follwing master key(s) from %s:\n"),
1237 printf(_("Purging the follwing master key(s) from %s:\n"),
1241 /* find # of keys still in use or print out verbose info */
1242 for (i = num_kvnos_inuse = num_kvnos_purged = 0; i < args.num_kvnos; i++) {
1243 if (args.kvnos[i].use_count > 0) {
1246 /* this key would be deleted */
1247 if (args.kvnos[i].kvno == master_kvno) {
1248 com_err(progname, KRB5_KDB_STORED_MKEY_NOTCURRENT,
1249 _("master key stash file needs updating, command "
1252 goto cleanup_return;
1255 printf(_("KVNO: %d\n"), args.kvnos[i].kvno);
1258 /* didn't find any keys to purge */
1259 if (num_kvnos_inuse == args.num_kvnos) {
1260 printf(_("All keys in use, nothing purged.\n"));
1261 goto cleanup_return;
1264 /* bail before doing anything else */
1265 printf(_("%d key(s) would be purged.\n"), num_kvnos_purged);
1266 goto cleanup_return;
1269 retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
1271 com_err(progname, retval, _("while looking up active kvno list"));
1273 goto cleanup_return;
1276 retval = krb5_dbe_lookup_mkey_aux(util_context, master_entry, &mkey_aux_list);
1278 com_err(progname, retval, _("while looking up mkey aux data list"));
1280 goto cleanup_return;
1283 master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * num_kvnos_inuse);
1284 if (master_entry->key_data == NULL) {
1286 com_err(progname, ENOMEM, _("while allocating key_data"));
1288 goto cleanup_return;
1290 memset(master_entry->key_data, 0, sizeof(krb5_key_data) * num_kvnos_inuse);
1291 master_entry->n_key_data = num_kvnos_inuse; /* there's only 1 mkey per kvno */
1294 * Assuming that the latest mkey will not be purged because it will always
1295 * be "in use" so this code will not bother with encrypting keys again.
1297 for (i = k = 0; i < old_key_data_count; i++) {
1298 for (j = 0; j < args.num_kvnos; j++) {
1299 if (args.kvnos[j].kvno == (krb5_kvno) old_key_data[i].key_data_kvno) {
1300 if (args.kvnos[j].use_count != 0) {
1301 master_entry->key_data[k++] = old_key_data[i];
1304 /* remove unused mkey */
1305 /* adjust the actkno data */
1306 for (prev_actkvno_entry = actkvno_entry = actkvno_list;
1307 actkvno_entry != NULL;
1308 actkvno_entry = actkvno_entry->next) {
1310 if (actkvno_entry->act_kvno == args.kvnos[j].kvno) {
1311 if (actkvno_entry == actkvno_list) {
1312 /* remove from head */
1313 actkvno_list = actkvno_entry->next;
1314 prev_actkvno_entry = actkvno_list;
1315 } else if (actkvno_entry->next == NULL) {
1316 /* remove from tail */
1317 prev_actkvno_entry->next = NULL;
1319 /* remove in between */
1320 prev_actkvno_entry->next = actkvno_entry->next;
1322 actkvno_entry->next = NULL;
1323 krb5_dbe_free_actkvno_list(util_context, actkvno_entry);
1324 break; /* deleted entry, no need to loop further */
1326 prev_actkvno_entry = actkvno_entry;
1329 /* adjust the mkey aux data */
1330 for (prev_mkey_aux_entry = mkey_aux_entry = mkey_aux_list;
1331 mkey_aux_entry != NULL;
1332 mkey_aux_entry = mkey_aux_entry->next) {
1334 if (mkey_aux_entry->mkey_kvno == args.kvnos[j].kvno) {
1335 if (mkey_aux_entry == mkey_aux_list) {
1336 mkey_aux_list = mkey_aux_entry->next;
1337 prev_mkey_aux_entry = mkey_aux_list;
1338 } else if (mkey_aux_entry->next == NULL) {
1339 prev_mkey_aux_entry->next = NULL;
1341 prev_mkey_aux_entry->next = mkey_aux_entry->next;
1343 mkey_aux_entry->next = NULL;
1344 krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_entry);
1345 break; /* deleted entry, no need to loop further */
1347 prev_mkey_aux_entry = mkey_aux_entry;
1354 assert(k == num_kvnos_inuse);
1356 if ((retval = krb5_dbe_update_actkvno(util_context, master_entry,
1358 com_err(progname, retval,
1359 _("while updating actkvno data for master principal entry"));
1361 goto cleanup_return;
1364 if ((retval = krb5_dbe_update_mkey_aux(util_context, master_entry,
1366 com_err(progname, retval,
1367 _("while updating mkey_aux data for master principal entry"));
1372 if ((retval = krb5_timeofday(util_context, &now))) {
1373 com_err(progname, retval, _("while getting current time"));
1375 goto cleanup_return;
1378 if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry,
1379 now, master_princ))) {
1380 com_err(progname, retval, _("while updating the master key principal "
1381 "modification time"));
1383 goto cleanup_return;
1386 master_entry->mask |= KADM5_KEY_DATA;
1388 if ((retval = krb5_db_put_principal(util_context, master_entry))) {
1389 (void) krb5_db_fini(util_context);
1390 com_err(progname, retval,
1391 _("while adding master key entry to the database"));
1393 goto cleanup_return;
1395 printf(_("%d key(s) purged.\n"), num_kvnos_purged);
1399 (void) krb5_db_fini(util_context);
1400 krb5_free_principal(util_context, master_princ);
1402 krb5_free_unparsed_name(util_context, mkey_fullname);
1403 krb5_dbe_free_actkvno_list(util_context, actkvno_list);
1404 krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_list);