1
/* -*- mode: c; 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_dbekd_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((char *) 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_dbekd_encrypt_key_data(context, new_mkey,
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_dbekd_encrypt_key_data(context, &keylist_node->keyblock,
161
NULL, /* no keysalt */
167
(*mkey_aux_data)->latest_mkey = tmp_key_data;
168
(*mkey_aux_data)->mkey_kvno = keylist_node->kvno;
169
mkey_aux_data = &((*mkey_aux_data)->next);
172
* Store old key in master_entry keydata past the new mkey
174
retval = krb5_dbekd_encrypt_key_data(context, new_mkey,
175
&keylist_node->keyblock,
176
NULL, /* no keysalt */
177
(int) keylist_node->kvno,
178
&master_entry->key_data[i]);
182
assert(i == old_key_data_count + 1);
184
if ((retval = krb5_dbe_update_mkey_aux(context, master_entry,
185
mkey_aux_data_head))) {
188
master_entry->mask |= KADM5_KEY_DATA;
191
krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_head);
196
kdb5_add_mkey(int argc, char *argv[])
199
krb5_error_code retval;
202
unsigned int pw_size = 0;
203
int do_stash = 0, nentries = 0;
204
krb5_boolean more = 0;
206
krb5_kvno new_mkey_kvno;
207
krb5_keyblock new_mkeyblock;
208
krb5_enctype new_master_enctype = ENCTYPE_UNKNOWN;
209
char *new_mkey_password;
210
krb5_db_entry master_entry;
214
* The command table entry for this command causes open_db_and_mkey() to be
215
* called first to open the KDB and get the current mkey.
218
memset(&new_mkeyblock, 0, sizeof(new_mkeyblock));
219
memset(&master_princ, 0, sizeof(master_princ));
220
master_salt.data = NULL;
222
while ((optchar = getopt(argc, argv, "e:s")) != -1) {
225
if (krb5_string_to_enctype(optarg, &new_master_enctype)) {
226
com_err(progname, EINVAL, "%s is an invalid enctype", optarg);
241
if (new_master_enctype == ENCTYPE_UNKNOWN)
242
new_master_enctype = global_params.enctype;
244
/* assemble & parse the master key name */
245
if ((retval = krb5_db_setup_mkey_name(util_context,
246
global_params.mkey_name,
248
&mkey_fullname, &master_princ))) {
249
com_err(progname, retval, "while setting up master key name");
254
retval = krb5_db_get_principal(util_context, master_princ, &master_entry,
257
com_err(progname, retval,
258
"while getting master key principal %s",
262
} else if (nentries == 0) {
263
com_err(progname, KRB5_KDB_NOENTRY,
264
"principal %s not found in Kerberos database",
268
} else if (nentries > 1) {
269
com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
270
"principal %s has multiple entries in Kerberos database",
276
printf("Creating new master key for master key principal '%s'\n",
279
printf("You will be prompted for a new database Master Password.\n");
280
printf("It is important that you NOT FORGET this password.\n");
284
pw_str = malloc(pw_size);
285
if (pw_str == NULL) {
286
com_err(progname, ENOMEM, "while creating new master key");
291
retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
294
com_err(progname, retval, "while reading new master key from keyboard");
298
new_mkey_password = pw_str;
300
pwd.data = new_mkey_password;
301
pwd.length = strlen(new_mkey_password);
302
retval = krb5_principal2salt(util_context, master_princ, &master_salt);
304
com_err(progname, retval, "while calculating master key salt");
309
retval = krb5_c_string_to_key(util_context, new_master_enctype,
310
&pwd, &master_salt, &new_mkeyblock);
312
com_err(progname, retval, "while transforming master key from password");
317
retval = add_new_mkey(util_context, &master_entry, &new_mkeyblock, 0);
319
com_err(progname, retval, "adding new master key to master principal");
324
if ((retval = krb5_timeofday(util_context, &now))) {
325
com_err(progname, retval, "while getting current time");
330
if ((retval = krb5_dbe_update_mod_princ_data(util_context, &master_entry,
331
now, master_princ))) {
332
com_err(progname, retval, "while updating the master key principal modification time");
337
if ((retval = krb5_db_put_principal(util_context, &master_entry, &nentries))) {
338
(void) krb5_db_fini(util_context);
339
com_err(progname, retval, "while adding master key entry to the database");
345
retval = krb5_db_store_master_key(util_context,
346
global_params.stash_file,
352
com_err(progname, errno, "while storing key");
353
printf("Warning: couldn't stash master key.\n");
359
(void) krb5_db_fini(util_context);
360
zap((char *)master_keyblock.contents, master_keyblock.length);
361
free(master_keyblock.contents);
362
zap((char *)new_mkeyblock.contents, new_mkeyblock.length);
363
free(new_mkeyblock.contents);
365
zap(pw_str, pw_size);
368
free(master_salt.data);
369
krb5_free_unparsed_name(util_context, mkey_fullname);
374
kdb5_use_mkey(int argc, char *argv[])
376
krb5_error_code retval;
377
char *mkey_fullname = NULL;
379
krb5_timestamp now, start_time;
380
krb5_actkvno_node *actkvno_list = NULL, *new_actkvno = NULL,
381
*prev_actkvno, *cur_actkvno;
382
krb5_db_entry master_entry;
384
krb5_boolean more = FALSE;
385
krb5_keylist_node *keylist_node;
386
krb5_boolean inserted = FALSE;
388
memset(&master_princ, 0, sizeof(master_princ));
390
if (argc < 2 || argc > 3) {
391
/* usage calls exit */
395
use_kvno = atoi(argv[1]);
397
com_err(progname, EINVAL, "0 is an invalid KVNO value");
401
/* verify use_kvno is valid */
402
for (keylist_node = master_keylist; keylist_node != NULL;
403
keylist_node = keylist_node->next) {
404
if (use_kvno == keylist_node->kvno)
408
com_err(progname, EINVAL, "%d is an invalid KVNO value", use_kvno);
414
if ((retval = krb5_timeofday(util_context, &now))) {
415
com_err(progname, retval, "while getting current time");
421
time_t t = get_date(argv[2]);
423
com_err(progname, 0, "could not parse date-time string '%s'",
428
start_time = (krb5_timestamp) t;
437
* 2. get krb5_actkvno_node list
438
* 3. add use_kvno to actkvno list (sorted in right spot)
439
* 4. update mkey princ's tl data
443
/* assemble & parse the master key name */
444
if ((retval = krb5_db_setup_mkey_name(util_context,
445
global_params.mkey_name,
447
&mkey_fullname, &master_princ))) {
448
com_err(progname, retval, "while setting up master key name");
453
retval = krb5_db_get_principal(util_context, master_princ, &master_entry,
456
com_err(progname, retval,
457
"while getting master key principal %s",
461
} else if (nentries == 0) {
462
com_err(progname, KRB5_KDB_NOENTRY,
463
"principal %s not found in Kerberos database",
467
} else if (nentries > 1) {
468
com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
469
"principal %s has multiple entries in Kerberos database",
475
retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
477
com_err(progname, retval,
478
"while looking up active version of master key");
484
* If an entry already exists with the same kvno either delete it or if it's
485
* the only entry, just set its active time.
487
for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
489
prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
491
if (cur_actkvno->act_kvno == use_kvno) {
494
prev_actkvno->next = cur_actkvno->next;
495
cur_actkvno->next = NULL;
496
krb5_dbe_free_actkvno_list(util_context, cur_actkvno);
498
if (cur_actkvno->next) {
499
/* delete it from front of list */
500
actkvno_list = cur_actkvno->next;
501
cur_actkvno->next = NULL;
502
krb5_dbe_free_actkvno_list(util_context, cur_actkvno);
504
/* There's only one entry, go ahead and change the time */
505
cur_actkvno->act_time = start_time;
514
/* alloc enough space to hold new and existing key_data */
515
new_actkvno = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node));
516
if (new_actkvno == NULL) {
517
com_err(progname, ENOMEM, "while adding new master key");
521
memset(new_actkvno, 0, sizeof(krb5_actkvno_node));
522
new_actkvno->act_kvno = use_kvno;
523
new_actkvno->act_time = start_time;
525
/* insert new act kvno node */
527
if (actkvno_list == NULL) {
528
/* new actkvno is the list */
529
actkvno_list = new_actkvno;
531
for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
533
prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
535
if (new_actkvno->act_time < cur_actkvno->act_time) {
537
prev_actkvno->next = new_actkvno;
538
new_actkvno->next = cur_actkvno;
540
new_actkvno->next = actkvno_list;
541
actkvno_list = new_actkvno;
544
} else if (cur_actkvno->next == NULL) {
545
/* end of line, just add new node to end of list */
546
cur_actkvno->next = new_actkvno;
553
if (actkvno_list->act_time > now) {
554
com_err(progname, EINVAL, "there must be one master key currently active");
559
if ((retval = krb5_dbe_update_actkvno(util_context, &master_entry,
561
com_err(progname, retval, "while updating actkvno data for master principal entry");
566
if ((retval = krb5_dbe_update_mod_princ_data(util_context, &master_entry,
567
now, master_princ))) {
568
com_err(progname, retval, "while updating the master key principal modification time");
573
if ((retval = krb5_db_put_principal(util_context, &master_entry, &nentries))) {
574
(void) krb5_db_fini(util_context);
575
com_err(progname, retval, "while adding master key entry to the database");
582
(void) krb5_db_fini(util_context);
583
krb5_free_unparsed_name(util_context, mkey_fullname);
584
krb5_free_principal(util_context, master_princ);
585
krb5_dbe_free_actkvno_list(util_context, actkvno_list);
590
kdb5_list_mkeys(int argc, char *argv[])
592
krb5_error_code retval;
593
char *mkey_fullname = NULL, *output_str = NULL, enctype[BUFSIZ];
595
krb5_timestamp act_time;
596
krb5_actkvno_node *actkvno_list = NULL, *cur_actkvno;
597
krb5_db_entry master_entry;
599
krb5_boolean more = FALSE;
600
krb5_keylist_node *cur_kb_node;
601
krb5_keyblock *act_mkey;
603
if (master_keylist == NULL) {
604
com_err(progname, 0, "master keylist not initialized");
609
/* assemble & parse the master key name */
610
if ((retval = krb5_db_setup_mkey_name(util_context,
611
global_params.mkey_name,
613
&mkey_fullname, &master_princ))) {
614
com_err(progname, retval, "while setting up master key name");
619
retval = krb5_db_get_principal(util_context, master_princ, &master_entry,
622
com_err(progname, retval,
623
"while getting master key principal %s",
627
} else if (nentries == 0) {
628
com_err(progname, KRB5_KDB_NOENTRY,
629
"principal %s not found in Kerberos database",
633
} else if (nentries > 1) {
634
com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
635
"principal %s has multiple entries in Kerberos database",
641
retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
643
com_err(progname, retval, "while looking up active kvno list");
648
if (actkvno_list == NULL) {
649
act_kvno = master_entry.key_data[0].key_data_kvno;
651
retval = krb5_dbe_find_act_mkey(util_context, master_keylist,
652
actkvno_list, &act_kvno, &act_mkey);
653
if (retval == KRB5_KDB_NOACTMASTERKEY) {
654
/* Maybe we went through a time warp, and the only keys
655
with activation dates have them set in the future? */
656
com_err(progname, retval, "");
659
} else if (retval != 0) {
660
com_err(progname, retval, "while looking up active master key");
666
printf("Master keys for Principal: %s\n", mkey_fullname);
668
for (cur_kb_node = master_keylist; cur_kb_node != NULL;
669
cur_kb_node = cur_kb_node->next) {
671
if ((retval = krb5_enctype_to_string(cur_kb_node->keyblock.enctype,
672
enctype, sizeof(enctype)))) {
673
com_err(progname, retval, "while getting enctype description");
678
if (actkvno_list != NULL) {
679
act_time = -1; /* assume actkvno entry not found */
680
for (cur_actkvno = actkvno_list; cur_actkvno != NULL;
681
cur_actkvno = cur_actkvno->next) {
682
if (cur_actkvno->act_kvno == cur_kb_node->kvno) {
683
act_time = cur_actkvno->act_time;
689
* mkey princ doesn't have an active knvo list so assume the current
692
if ((retval = krb5_timeofday(util_context, &act_time))) {
693
com_err(progname, retval, "while getting current time");
699
if (cur_kb_node->kvno == act_kvno) {
700
/* * indicates kvno is currently active */
701
retval = asprintf(&output_str, "KNVO: %d, Enctype: %s, Active on: %s *\n",
702
cur_kb_node->kvno, enctype, strdate(act_time));
704
if (act_time != -1) {
705
retval = asprintf(&output_str, "KNVO: %d, Enctype: %s, Active on: %s\n",
706
cur_kb_node->kvno, enctype, strdate(act_time));
708
retval = asprintf(&output_str, "KNVO: %d, Enctype: %s, No activate time set\n",
709
cur_kb_node->kvno, enctype);
713
com_err(progname, ENOMEM, "asprintf could not allocate enough memory to hold output");
717
printf("%s", output_str);
724
(void) krb5_db_fini(util_context);
725
krb5_free_unparsed_name(util_context, mkey_fullname);
727
krb5_free_principal(util_context, master_princ);
728
krb5_dbe_free_actkvno_list(util_context, actkvno_list);
732
struct update_enc_mkvno {
733
unsigned int re_match_count;
734
unsigned int already_current;
735
unsigned int updated;
736
unsigned int dry_run : 1;
737
unsigned int verbose : 1;
738
#ifdef SOLARIS_REGEXPS
744
#if !defined(SOLARIS_REGEXPS) && !defined(POSIX_REGEXPS)
745
unsigned char placeholder;
749
/* XXX Duplicated in libkadm5srv! */
751
* Function: glob_to_regexp
755
* glob (r) the shell-style glob (?*[]) to convert
756
* realm (r) the default realm to append, or NULL
757
* regexp (w) the ed-style regexp created from glob
761
* regexp is filled in with allocated memory contained a regular
762
* expression to be used with re_comp/compile that matches what the
763
* shell-style glob would match. If glob does not contain an "@"
764
* character and realm is not NULL, "@*" is appended to the regexp.
766
* Conversion algorithm:
768
* quoted characters are copied quoted
769
* ? is converted to .
770
* * is converted to .*
771
* active characters are quoted: ^, $, .
772
* [ and ] are active but supported and have the same meaning, so
774
* other characters are copied
775
* regexp is anchored with ^ and $
777
static int glob_to_regexp(char *glob, char *realm, char **regexp)
782
/* validate the glob */
783
if (glob[strlen(glob)-1] == '\\')
786
/* A character of glob can turn into two in regexp, plus ^ and $ */
787
/* and trailing null. If glob has no @, also allocate space for */
789
append_realm = (realm != NULL) && (strchr(glob, '@') == NULL);
790
p = (char *) malloc(strlen(glob)*2+ 3 + (append_realm ? 3 : 0));
834
update_princ_encryption_1(void *cb, krb5_db_entry *ent)
836
struct update_enc_mkvno *p = cb;
838
krb5_error_code retval;
845
retval = krb5_unparse_name(util_context, ent->princ, &pname);
847
com_err(progname, retval,
848
"getting string representation of principal name");
852
if (krb5_principal_compare(util_context, ent->princ, master_princ)) {
856
#ifdef SOLARIS_REGEXPS
857
match = (step(pname, p->expbuf) != 0);
860
match = (regexec(&p->preg, pname, 0, NULL, 0) == 0);
863
match = (re_exec(pname) != 0);
869
retval = krb5_dbe_lookup_mkvno(util_context, ent, &old_mkvno);
871
com_err(progname, retval,
872
"determining master key used for principal '%s'",
876
/* Line up "skip" and "update" messages for viewing. */
877
if (old_mkvno == new_mkvno) {
878
if (p->dry_run && p->verbose)
879
printf("would skip: %s\n", pname);
881
printf("skipping: %s\n", pname);
882
p->already_current++;
887
printf("would update: %s\n", pname);
890
} else if (p->verbose)
891
printf("updating: %s\n", pname);
892
retval = master_key_convert (util_context, ent);
894
com_err(progname, retval,
895
"error re-encrypting key for principal '%s'", pname);
898
if ((retval = krb5_timeofday(util_context, &now))) {
899
com_err(progname, retval, "while getting current time");
903
if ((retval = krb5_dbe_update_mod_princ_data(util_context, ent,
904
now, master_princ))) {
905
com_err(progname, retval,
906
"while updating principal '%s' modification time", pname);
910
ent->mask |= KADM5_KEY_DATA;
912
if ((retval = krb5_db_put_principal(util_context, ent, &nentries))) {
913
com_err(progname, retval,
914
"while updating principal '%s' key data in the database",
927
krb5_free_unparsed_name(util_context, pname);
931
extern int are_you_sure (const char *, ...)
932
#if !defined(__cplusplus) && (__GNUC__ > 2)
933
__attribute__((__format__(__printf__, 1, 2)))
938
are_you_sure (const char *format, ...)
943
va_start(va, format);
946
printf("\n(type 'yes' to confirm)? ");
948
if (fgets(ansbuf, sizeof(ansbuf), stdin) == NULL)
950
if (strcmp(ansbuf, "yes\n"))
956
kdb5_update_princ_encryption(int argc, char *argv[])
958
struct update_enc_mkvno data = { 0 };
959
char *name_pattern = NULL;
962
krb5_error_code retval;
963
krb5_actkvno_node *actkvno_list = 0;
964
krb5_db_entry master_entry;
966
krb5_boolean more = FALSE;
967
char *mkey_fullname = 0;
972
krb5_keyblock *tmp_keyblock = NULL;
974
while ((optchar = getopt(argc, argv, "fnv")) != -1) {
991
if (argv[optind] != NULL) {
992
name_pattern = argv[optind];
993
if (argv[optind+1] != NULL)
997
retval = krb5_unparse_name(util_context, master_princ, &mkey_fullname);
999
com_err(progname, retval, "while formatting master principal name");
1004
if (master_keylist == NULL) {
1005
com_err(progname, retval, "master keylist not initialized");
1010
/* The glob_to_regexp code only cares if the "realm" parameter is
1011
NULL or not; the string data is irrelevant. */
1012
if (name_pattern == NULL)
1014
if (glob_to_regexp(name_pattern, "hi", ®exp) != 0) {
1015
com_err(progname, ENOMEM,
1016
"converting glob pattern '%s' to regular expression",
1023
#ifdef SOLARIS_REGEXPS
1024
((data.expbuf = compile(regexp, NULL, NULL)) == NULL)
1026
#ifdef POSIX_REGEXPS
1027
((regcomp(&data.preg, regexp, REG_NOSUB)) != 0)
1030
((msg = (char *) re_comp(regexp)) != NULL)
1033
/* XXX syslog msg or regerr(regerrno) */
1034
com_err(progname, 0, "error compiling converted regexp '%s'", regexp);
1039
retval = krb5_db_get_principal(util_context, master_princ, &master_entry,
1042
com_err(progname, retval, "while getting master key principal %s",
1047
if (nentries != 1) {
1048
com_err(progname, 0,
1049
"cannot find master key principal %s in database!",
1055
retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
1057
com_err(progname, retval, "while looking up active kvno list");
1062
/* Master key is always stored encrypted in the latest version of
1064
new_mkvno = krb5_db_get_key_data_kvno(util_context,
1065
master_entry.n_key_data,
1066
master_entry.key_data);
1068
retval = krb5_dbe_find_mkey(util_context, master_keylist,
1069
&master_entry, &tmp_keyblock);
1071
com_err(progname, retval, "retrieving the most recent master key");
1075
new_master_keyblock = *tmp_keyblock;
1079
!are_you_sure("Re-encrypt all keys not using master key vno %u?",
1081
printf("OK, doing nothing.\n");
1087
printf("Principals whose keys WOULD BE re-encrypted to master key vno %u:\n",
1090
printf("Principals whose keys are being re-encrypted to master key vno %u if necessary:\n",
1094
retval = krb5_db_iterate(util_context, name_pattern,
1095
update_princ_encryption_1, &data);
1096
/* If exit_status is set, then update_princ_encryption_1 already
1097
printed a message. */
1098
if (retval != 0 && exit_status == 0) {
1099
com_err(progname, retval, "trying to process principal database");
1102
(void) krb5_db_fini(util_context);
1104
printf("%u principals processed: %u would be updated, %u already current\n",
1105
data.re_match_count, data.updated, data.already_current);
1107
printf("%u principals processed: %u updated, %u already current\n",
1108
data.re_match_count, data.updated, data.already_current);
1112
memset(&new_master_keyblock, 0, sizeof(new_master_keyblock));
1113
krb5_free_keyblock(util_context, tmp_keyblock);
1114
krb5_free_unparsed_name(util_context, mkey_fullname);
1115
krb5_dbe_free_actkvno_list(util_context, actkvno_list);
1118
struct kvnos_in_use {
1120
unsigned int use_count;
1124
krb5_context kcontext;
1125
struct kvnos_in_use *kvnos;
1126
unsigned int num_kvnos;
1129
static krb5_error_code
1130
find_mkvnos_in_use(krb5_pointer ptr,
1131
krb5_db_entry *entry)
1133
krb5_error_code retval;
1134
struct purge_args * args;
1138
args = (struct purge_args *) ptr;
1140
retval = krb5_dbe_lookup_mkvno(args->kcontext, entry, &mkvno);
1144
for (i = 0; i < args->num_kvnos; i++) {
1145
if (args->kvnos[i].kvno == mkvno) {
1146
/* XXX do I need to worry about use_count wrapping? */
1147
args->kvnos[i].use_count++;
1155
kdb5_purge_mkeys(int argc, char *argv[])
1158
krb5_error_code retval;
1159
char *mkey_fullname = NULL;
1161
krb5_db_entry master_entry;
1163
krb5_boolean more = FALSE;
1164
krb5_boolean force = FALSE, dry_run = FALSE, verbose = FALSE;
1165
struct purge_args args;
1167
unsigned int i, j, k, num_kvnos_inuse, num_kvnos_purged;
1168
unsigned int old_key_data_count;
1169
krb5_actkvno_node *actkvno_list = NULL, *actkvno_entry, *prev_actkvno_entry;
1170
krb5_mkey_aux_node *mkey_aux_list = NULL, *mkey_aux_entry, *prev_mkey_aux_entry;
1171
krb5_key_data *old_key_data;
1173
memset(&master_princ, 0, sizeof(master_princ));
1174
memset(&args, 0, sizeof(args));
1177
while ((optchar = getopt(argc, argv, "fnv")) != -1) {
1183
dry_run = TRUE; /* mkey princ will not be modified */
1184
force = TRUE; /* implied */
1196
/* assemble & parse the master key name */
1197
if ((retval = krb5_db_setup_mkey_name(util_context,
1198
global_params.mkey_name,
1199
global_params.realm,
1200
&mkey_fullname, &master_princ))) {
1201
com_err(progname, retval, "while setting up master key name");
1206
retval = krb5_db_get_principal(util_context, master_princ, &master_entry,
1209
com_err(progname, retval,
1210
"while getting master key principal %s",
1213
goto cleanup_return;
1214
} else if (nentries == 0) {
1215
com_err(progname, KRB5_KDB_NOENTRY,
1216
"principal %s not found in Kerberos database",
1219
goto cleanup_return;
1220
} else if (nentries > 1) {
1221
com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
1222
"principal %s has multiple entries in Kerberos database",
1225
goto cleanup_return;
1229
printf("Will purge all unused master keys stored in the '%s' principal, are you sure?\n",
1231
printf("(type 'yes' to confirm)? ");
1232
if (fgets(buf, sizeof(buf), stdin) == NULL) {
1234
goto cleanup_return;
1236
if (strcmp(buf, "yes\n")) {
1238
goto cleanup_return;
1240
printf("OK, purging unused master keys from '%s'...\n", mkey_fullname);
1243
/* save the old keydata */
1244
old_key_data_count = master_entry.n_key_data;
1245
if (old_key_data_count == 1) {
1247
printf("There is only one master key which can not be purged.\n");
1248
goto cleanup_return;
1250
old_key_data = master_entry.key_data;
1252
args.kvnos = (struct kvnos_in_use *) malloc(sizeof(struct kvnos_in_use) * old_key_data_count);
1253
if (args.kvnos == NULL) {
1255
com_err(progname, ENOMEM, "while allocating args.kvnos");
1257
goto cleanup_return;
1259
memset(args.kvnos, 0, sizeof(struct kvnos_in_use) * old_key_data_count);
1260
args.num_kvnos = old_key_data_count;
1261
args.kcontext = util_context;
1263
/* populate the kvnos array with all the current mkvnos */
1264
for (i = 0; i < old_key_data_count; i++)
1265
args.kvnos[i].kvno = master_entry.key_data[i].key_data_kvno;
1267
if ((retval = krb5_db_iterate(util_context,
1270
(krb5_pointer) &args))) {
1271
com_err(progname, retval, "while finding master keys in use");
1273
goto cleanup_return;
1276
* args.kvnos has been marked with the mkvno's that are currently protecting
1280
printf("Would purge the follwing master key(s) from %s:\n", mkey_fullname);
1282
printf("Purging the follwing master key(s) from %s:\n", mkey_fullname);
1284
/* find # of keys still in use or print out verbose info */
1285
for (i = num_kvnos_inuse = num_kvnos_purged = 0; i < args.num_kvnos; i++) {
1286
if (args.kvnos[i].use_count > 0) {
1289
/* this key would be deleted */
1290
if (args.kvnos[i].kvno == master_kvno) {
1291
com_err(progname, KRB5_KDB_STORED_MKEY_NOTCURRENT,
1292
"master key stash file needs updating, command aborting");
1294
goto cleanup_return;
1297
printf("KNVO: %d\n", args.kvnos[i].kvno);
1300
/* didn't find any keys to purge */
1301
if (num_kvnos_inuse == args.num_kvnos) {
1302
printf("All keys in use, nothing purged.\n");
1303
goto cleanup_return;
1306
/* bail before doing anything else */
1307
printf("%d key(s) would be purged.\n", num_kvnos_purged);
1308
goto cleanup_return;
1311
retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
1313
com_err(progname, retval, "while looking up active kvno list");
1315
goto cleanup_return;
1318
retval = krb5_dbe_lookup_mkey_aux(util_context, &master_entry, &mkey_aux_list);
1320
com_err(progname, retval, "while looking up mkey aux data list");
1322
goto cleanup_return;
1325
master_entry.key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * num_kvnos_inuse);
1326
if (master_entry.key_data == NULL) {
1328
com_err(progname, ENOMEM, "while allocating key_data");
1330
goto cleanup_return;
1332
memset((char *) master_entry.key_data, 0, sizeof(krb5_key_data) * num_kvnos_inuse);
1333
master_entry.n_key_data = num_kvnos_inuse; /* there's only 1 mkey per kvno */
1336
* Assuming that the latest mkey will not be purged because it will always
1337
* be "in use" so this code will not bother with encrypting keys again.
1339
for (i = k = 0; i < old_key_data_count; i++) {
1340
for (j = 0; j < args.num_kvnos; j++) {
1341
if (args.kvnos[j].kvno == (krb5_kvno) old_key_data[i].key_data_kvno) {
1342
if (args.kvnos[j].use_count != 0) {
1343
master_entry.key_data[k++] = old_key_data[i];
1346
/* remove unused mkey */
1347
/* adjust the actkno data */
1348
for (prev_actkvno_entry = actkvno_entry = actkvno_list;
1349
actkvno_entry != NULL;
1350
actkvno_entry = actkvno_entry->next) {
1352
if (actkvno_entry->act_kvno == args.kvnos[j].kvno) {
1353
if (actkvno_entry == actkvno_list) {
1354
/* remove from head */
1355
actkvno_list = actkvno_entry->next;
1356
prev_actkvno_entry = actkvno_list;
1357
} else if (actkvno_entry->next == NULL) {
1358
/* remove from tail */
1359
prev_actkvno_entry->next = NULL;
1361
/* remove in between */
1362
prev_actkvno_entry->next = actkvno_entry->next;
1364
actkvno_entry->next = NULL;
1365
krb5_dbe_free_actkvno_list(util_context, actkvno_entry);
1366
break; /* deleted entry, no need to loop further */
1368
prev_actkvno_entry = actkvno_entry;
1371
/* adjust the mkey aux data */
1372
for (prev_mkey_aux_entry = mkey_aux_entry = mkey_aux_list;
1373
mkey_aux_entry != NULL;
1374
mkey_aux_entry = mkey_aux_entry->next) {
1376
if (mkey_aux_entry->mkey_kvno == args.kvnos[j].kvno) {
1377
if (mkey_aux_entry == mkey_aux_list) {
1378
mkey_aux_list = mkey_aux_entry->next;
1379
prev_mkey_aux_entry = mkey_aux_list;
1380
} else if (mkey_aux_entry->next == NULL) {
1381
prev_mkey_aux_entry->next = NULL;
1383
prev_mkey_aux_entry->next = mkey_aux_entry->next;
1385
mkey_aux_entry->next = NULL;
1386
krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_entry);
1387
break; /* deleted entry, no need to loop further */
1389
prev_mkey_aux_entry = mkey_aux_entry;
1396
assert(k == num_kvnos_inuse);
1398
if ((retval = krb5_dbe_update_actkvno(util_context, &master_entry,
1400
com_err(progname, retval,
1401
"while updating actkvno data for master principal entry");
1403
goto cleanup_return;
1406
if ((retval = krb5_dbe_update_mkey_aux(util_context, &master_entry,
1408
com_err(progname, retval,
1409
"while updating mkey_aux data for master principal entry");
1414
if ((retval = krb5_timeofday(util_context, &now))) {
1415
com_err(progname, retval, "while getting current time");
1417
goto cleanup_return;
1420
if ((retval = krb5_dbe_update_mod_princ_data(util_context, &master_entry,
1421
now, master_princ))) {
1422
com_err(progname, retval,
1423
"while updating the master key principal modification time");
1425
goto cleanup_return;
1428
master_entry.mask |= KADM5_KEY_DATA;
1430
if ((retval = krb5_db_put_principal(util_context, &master_entry, &nentries))) {
1431
(void) krb5_db_fini(util_context);
1432
com_err(progname, retval, "while adding master key entry to the database");
1434
goto cleanup_return;
1436
printf("%d key(s) purged.\n", num_kvnos_purged);
1440
(void) krb5_db_fini(util_context);
1441
krb5_free_principal(util_context, master_princ);
1443
krb5_free_unparsed_name(util_context, mkey_fullname);
1444
krb5_dbe_free_actkvno_list(util_context, actkvno_list);
1445
krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_list);