Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/kadmin/dbutil/kdb5_mkey.c
34889 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/*
3
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
4
* Use is subject to license terms.
5
*/
6
7
#include <k5-int.h>
8
#include <kdb.h>
9
#include <kadm5/server_internal.h>
10
#include <kadm5/admin.h>
11
#include <adm_proto.h>
12
#include "kdb5_util.h"
13
#include <time.h>
14
#include "k5-regex.h"
15
16
extern krb5_keyblock master_keyblock; /* current mkey */
17
extern krb5_kvno master_kvno;
18
extern krb5_principal master_princ;
19
extern krb5_data master_salt;
20
extern char *mkey_fullname;
21
extern char *mkey_password;
22
extern char *progname;
23
extern int exit_status;
24
extern kadm5_config_params global_params;
25
extern krb5_context util_context;
26
extern time_t get_date(char *);
27
28
static const char *
29
strdate(krb5_timestamp when)
30
{
31
struct tm *tm;
32
static char out[40];
33
time_t lcltim = ts2tt(when);
34
35
tm = localtime(&lcltim);
36
if (tm == NULL ||
37
strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm) == 0)
38
strlcpy(out, "(error)", sizeof(out));
39
return out;
40
}
41
42
krb5_kvno
43
get_next_kvno(krb5_context context, krb5_db_entry *entry)
44
{
45
krb5_kvno new_kvno;
46
47
new_kvno = krb5_db_get_key_data_kvno(context, entry->n_key_data,
48
entry->key_data);
49
new_kvno++;
50
/* deal with wrapping */
51
if (new_kvno == 0)
52
new_kvno = 1; /* knvo must not be 0 as this is special value (IGNORE_VNO) */
53
54
return (new_kvno);
55
}
56
57
krb5_error_code
58
add_new_mkey(krb5_context context, krb5_db_entry *master_entry,
59
krb5_keyblock *new_mkey, krb5_kvno use_mkvno)
60
{
61
krb5_error_code retval = 0;
62
int old_key_data_count, i;
63
krb5_kvno new_mkey_kvno;
64
krb5_key_data tmp_key_data;
65
krb5_mkey_aux_node *mkey_aux_data_head = NULL, **mkey_aux_data;
66
krb5_keylist_node *keylist_node;
67
krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(context);
68
69
/* do this before modifying master_entry key_data */
70
new_mkey_kvno = get_next_kvno(context, master_entry);
71
/* verify the requested mkvno if not 0 is the one that would be used here. */
72
if (use_mkvno != 0 && new_mkey_kvno != use_mkvno)
73
return (KRB5_KDB_KVNONOMATCH);
74
75
old_key_data_count = master_entry->n_key_data;
76
77
/* alloc enough space to hold new and existing key_data */
78
/*
79
* The encrypted key is malloc'ed by krb5_dbe_encrypt_key_data and
80
* krb5_key_data key_data_contents is a pointer to this key. Using some
81
* logic from master_key_convert().
82
*/
83
for (i = 0; i < master_entry->n_key_data; i++)
84
krb5_free_key_data_contents(context, &master_entry->key_data[i]);
85
free(master_entry->key_data);
86
master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) *
87
(old_key_data_count + 1));
88
if (master_entry->key_data == NULL)
89
return (ENOMEM);
90
91
memset(master_entry->key_data, 0,
92
sizeof(krb5_key_data) * (old_key_data_count + 1));
93
master_entry->n_key_data = old_key_data_count + 1;
94
95
/* Note, mkey does not have salt */
96
/* add new mkey encrypted with itself to mkey princ entry */
97
if ((retval = krb5_dbe_encrypt_key_data(context, new_mkey, new_mkey, NULL,
98
(int) new_mkey_kvno,
99
&master_entry->key_data[0]))) {
100
return (retval);
101
}
102
/* the mvkno should be that of the newest mkey */
103
if ((retval = krb5_dbe_update_mkvno(context, master_entry, new_mkey_kvno))) {
104
krb5_free_key_data_contents(context, &master_entry->key_data[0]);
105
return (retval);
106
}
107
/*
108
* Need to decrypt old keys with the current mkey which is in the global
109
* master_keyblock and encrypt those keys with the latest mkey. And while
110
* the old keys are being decrypted, use those to create the
111
* KRB5_TL_MKEY_AUX entries which store the latest mkey encrypted by one of
112
* the older mkeys.
113
*
114
* The new mkey is followed by existing keys.
115
*
116
* First, set up for creating a krb5_mkey_aux_node list which will be used
117
* to update the mkey aux data for the mkey princ entry.
118
*/
119
mkey_aux_data_head = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
120
if (mkey_aux_data_head == NULL) {
121
retval = ENOMEM;
122
goto clean_n_exit;
123
}
124
memset(mkey_aux_data_head, 0, sizeof(krb5_mkey_aux_node));
125
mkey_aux_data = &mkey_aux_data_head;
126
127
for (keylist_node = master_keylist, i = 1; keylist_node != NULL;
128
keylist_node = keylist_node->next, i++) {
129
130
/*
131
* Create a list of krb5_mkey_aux_node nodes. One node contains the new
132
* mkey encrypted by an old mkey and the old mkey's kvno (one node per
133
* old mkey).
134
*/
135
if (*mkey_aux_data == NULL) {
136
/* *mkey_aux_data points to next field of previous node */
137
*mkey_aux_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
138
if (*mkey_aux_data == NULL) {
139
retval = ENOMEM;
140
goto clean_n_exit;
141
}
142
memset(*mkey_aux_data, 0, sizeof(krb5_mkey_aux_node));
143
}
144
145
memset(&tmp_key_data, 0, sizeof(tmp_key_data));
146
/* encrypt the new mkey with the older mkey */
147
retval = krb5_dbe_encrypt_key_data(context, &keylist_node->keyblock,
148
new_mkey, NULL, (int) new_mkey_kvno,
149
&tmp_key_data);
150
if (retval)
151
goto clean_n_exit;
152
153
(*mkey_aux_data)->latest_mkey = tmp_key_data;
154
(*mkey_aux_data)->mkey_kvno = keylist_node->kvno;
155
mkey_aux_data = &((*mkey_aux_data)->next);
156
157
/*
158
* Store old key in master_entry keydata past the new mkey
159
*/
160
retval = krb5_dbe_encrypt_key_data(context, new_mkey,
161
&keylist_node->keyblock,
162
NULL, (int) keylist_node->kvno,
163
&master_entry->key_data[i]);
164
if (retval)
165
goto clean_n_exit;
166
}
167
assert(i == old_key_data_count + 1);
168
169
if ((retval = krb5_dbe_update_mkey_aux(context, master_entry,
170
mkey_aux_data_head))) {
171
goto clean_n_exit;
172
}
173
master_entry->mask |= KADM5_KEY_DATA | KADM5_TL_DATA;
174
175
clean_n_exit:
176
krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_head);
177
return (retval);
178
}
179
180
void
181
kdb5_add_mkey(int argc, char *argv[])
182
{
183
int optchar;
184
krb5_error_code retval;
185
char *pw_str = 0;
186
unsigned int pw_size = 0;
187
int do_stash = 0;
188
krb5_data pwd;
189
krb5_kvno new_mkey_kvno;
190
krb5_keyblock new_mkeyblock;
191
krb5_enctype new_master_enctype = ENCTYPE_UNKNOWN;
192
char *new_mkey_password;
193
krb5_db_entry *master_entry = NULL;
194
krb5_timestamp now;
195
196
/*
197
* The command table entry for this command causes open_db_and_mkey() to be
198
* called first to open the KDB and get the current mkey.
199
*/
200
201
memset(&new_mkeyblock, 0, sizeof(new_mkeyblock));
202
master_salt.data = NULL;
203
204
while ((optchar = getopt(argc, argv, "e:s")) != -1) {
205
switch(optchar) {
206
case 'e':
207
if (krb5_string_to_enctype(optarg, &new_master_enctype)) {
208
com_err(progname, EINVAL, _("%s is an invalid enctype"),
209
optarg);
210
exit_status++;
211
return;
212
}
213
break;
214
case 's':
215
do_stash++;
216
break;
217
case '?':
218
default:
219
usage();
220
return;
221
}
222
}
223
224
if (new_master_enctype == ENCTYPE_UNKNOWN)
225
new_master_enctype = global_params.enctype;
226
227
retval = krb5_db_get_principal(util_context, master_princ, 0,
228
&master_entry);
229
if (retval != 0) {
230
com_err(progname, retval, _("while getting master key principal %s"),
231
mkey_fullname);
232
exit_status++;
233
goto cleanup_return;
234
}
235
236
printf(_("Creating new master key for master key principal '%s'\n"),
237
mkey_fullname);
238
239
printf(_("You will be prompted for a new database Master Password.\n"));
240
printf(_("It is important that you NOT FORGET this password.\n"));
241
fflush(stdout);
242
243
pw_size = 1024;
244
pw_str = malloc(pw_size);
245
if (pw_str == NULL) {
246
com_err(progname, ENOMEM, _("while creating new master key"));
247
exit_status++;
248
goto cleanup_return;
249
}
250
251
retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
252
pw_str, &pw_size);
253
if (retval) {
254
com_err(progname, retval,
255
_("while reading new master key from keyboard"));
256
exit_status++;
257
goto cleanup_return;
258
}
259
new_mkey_password = pw_str;
260
261
pwd.data = new_mkey_password;
262
pwd.length = strlen(new_mkey_password);
263
retval = krb5_principal2salt(util_context, master_princ, &master_salt);
264
if (retval) {
265
com_err(progname, retval, _("while calculating master key salt"));
266
exit_status++;
267
goto cleanup_return;
268
}
269
270
retval = krb5_c_string_to_key(util_context, new_master_enctype,
271
&pwd, &master_salt, &new_mkeyblock);
272
if (retval) {
273
com_err(progname, retval,
274
_("while transforming master key from password"));
275
exit_status++;
276
goto cleanup_return;
277
}
278
279
new_mkey_kvno = get_next_kvno(util_context, master_entry);
280
retval = add_new_mkey(util_context, master_entry, &new_mkeyblock,
281
new_mkey_kvno);
282
if (retval) {
283
com_err(progname, retval,
284
_("adding new master key to master principal"));
285
exit_status++;
286
goto cleanup_return;
287
}
288
289
if ((retval = krb5_timeofday(util_context, &now))) {
290
com_err(progname, retval, _("while getting current time"));
291
exit_status++;
292
goto cleanup_return;
293
}
294
295
if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry,
296
now, master_princ))) {
297
com_err(progname, retval, _("while updating the master key principal "
298
"modification time"));
299
exit_status++;
300
goto cleanup_return;
301
}
302
303
if ((retval = krb5_db_put_principal(util_context, master_entry))) {
304
com_err(progname, retval, _("while adding master key entry to the "
305
"database"));
306
exit_status++;
307
goto cleanup_return;
308
}
309
310
if (do_stash) {
311
retval = krb5_db_store_master_key(util_context,
312
global_params.stash_file,
313
master_princ,
314
new_mkey_kvno,
315
&new_mkeyblock,
316
mkey_password);
317
if (retval) {
318
com_err(progname, retval, _("while storing key"));
319
printf(_("Warning: couldn't stash master key.\n"));
320
}
321
}
322
323
cleanup_return:
324
/* clean up */
325
krb5_db_free_principal(util_context, master_entry);
326
zap((char *)new_mkeyblock.contents, new_mkeyblock.length);
327
free(new_mkeyblock.contents);
328
if (pw_str) {
329
zap(pw_str, pw_size);
330
free(pw_str);
331
}
332
free(master_salt.data);
333
return;
334
}
335
336
void
337
kdb5_use_mkey(int argc, char *argv[])
338
{
339
krb5_error_code retval;
340
krb5_kvno use_kvno;
341
krb5_timestamp now, start_time;
342
krb5_actkvno_node *actkvno_list = NULL, *new_actkvno = NULL,
343
*prev_actkvno, *cur_actkvno;
344
krb5_db_entry *master_entry = NULL;
345
krb5_keylist_node *keylist_node;
346
krb5_boolean inserted = FALSE;
347
krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context);
348
349
if (argc < 2 || argc > 3) {
350
/* usage calls exit */
351
usage();
352
}
353
354
use_kvno = atoi(argv[1]);
355
if (use_kvno == 0) {
356
com_err(progname, EINVAL, _("0 is an invalid KVNO value"));
357
exit_status++;
358
return;
359
} else {
360
/* verify use_kvno is valid */
361
for (keylist_node = master_keylist; keylist_node != NULL;
362
keylist_node = keylist_node->next) {
363
if (use_kvno == keylist_node->kvno)
364
break;
365
}
366
if (!keylist_node) {
367
com_err(progname, EINVAL, _("%d is an invalid KVNO value"),
368
use_kvno);
369
exit_status++;
370
return;
371
}
372
}
373
374
if ((retval = krb5_timeofday(util_context, &now))) {
375
com_err(progname, retval, _("while getting current time"));
376
exit_status++;
377
return;
378
}
379
380
if (argc == 3) {
381
time_t t = get_date(argv[2]);
382
if (t == -1) {
383
com_err(progname, 0, _("could not parse date-time string '%s'"),
384
argv[2]);
385
exit_status++;
386
return;
387
} else
388
start_time = (krb5_timestamp) t;
389
} else {
390
start_time = now;
391
}
392
393
/*
394
* Need to:
395
*
396
* 1. get mkey princ
397
* 2. get krb5_actkvno_node list
398
* 3. add use_kvno to actkvno list (sorted in right spot)
399
* 4. update mkey princ's tl data
400
* 5. put mkey princ.
401
*/
402
403
retval = krb5_db_get_principal(util_context, master_princ, 0,
404
&master_entry);
405
if (retval != 0) {
406
com_err(progname, retval, _("while getting master key principal %s"),
407
mkey_fullname);
408
exit_status++;
409
goto cleanup_return;
410
}
411
412
retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
413
if (retval != 0) {
414
com_err(progname, retval,
415
_("while looking up active version of master key"));
416
exit_status++;
417
goto cleanup_return;
418
}
419
420
/*
421
* If an entry already exists with the same kvno either delete it or if it's
422
* the only entry, just set its active time.
423
*/
424
for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
425
cur_actkvno != NULL;
426
prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
427
428
if (cur_actkvno->act_kvno == use_kvno) {
429
/* delete it */
430
if (prev_actkvno) {
431
prev_actkvno->next = cur_actkvno->next;
432
cur_actkvno->next = NULL;
433
krb5_dbe_free_actkvno_list(util_context, cur_actkvno);
434
} else {
435
if (cur_actkvno->next) {
436
/* delete it from front of list */
437
actkvno_list = cur_actkvno->next;
438
cur_actkvno->next = NULL;
439
krb5_dbe_free_actkvno_list(util_context, cur_actkvno);
440
} else {
441
/* There's only one entry, go ahead and change the time */
442
cur_actkvno->act_time = start_time;
443
inserted = TRUE;
444
}
445
}
446
break;
447
}
448
}
449
450
if (!inserted) {
451
/* alloc enough space to hold new and existing key_data */
452
new_actkvno = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node));
453
if (new_actkvno == NULL) {
454
com_err(progname, ENOMEM, _("while adding new master key"));
455
exit_status++;
456
goto cleanup_return;
457
}
458
memset(new_actkvno, 0, sizeof(krb5_actkvno_node));
459
new_actkvno->act_kvno = use_kvno;
460
new_actkvno->act_time = start_time;
461
462
/* insert new act kvno node */
463
464
if (actkvno_list == NULL) {
465
/* new actkvno is the list */
466
actkvno_list = new_actkvno;
467
} else {
468
for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
469
cur_actkvno != NULL;
470
prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
471
472
if (ts_after(cur_actkvno->act_time, new_actkvno->act_time)) {
473
if (prev_actkvno) {
474
prev_actkvno->next = new_actkvno;
475
new_actkvno->next = cur_actkvno;
476
} else {
477
new_actkvno->next = actkvno_list;
478
actkvno_list = new_actkvno;
479
}
480
break;
481
} else if (cur_actkvno->next == NULL) {
482
/* end of line, just add new node to end of list */
483
cur_actkvno->next = new_actkvno;
484
break;
485
}
486
}
487
}
488
}
489
490
if (ts_after(actkvno_list->act_time, now)) {
491
com_err(progname, EINVAL,
492
_("there must be one master key currently active"));
493
exit_status++;
494
goto cleanup_return;
495
}
496
497
if ((retval = krb5_dbe_update_actkvno(util_context, master_entry,
498
actkvno_list))) {
499
com_err(progname, retval,
500
_("while updating actkvno data for master principal entry"));
501
exit_status++;
502
goto cleanup_return;
503
}
504
505
if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry,
506
now, master_princ))) {
507
com_err(progname, retval, _("while updating the master key principal "
508
"modification time"));
509
exit_status++;
510
goto cleanup_return;
511
}
512
513
master_entry->mask |= KADM5_TL_DATA;
514
515
if ((retval = krb5_db_put_principal(util_context, master_entry))) {
516
com_err(progname, retval,
517
_("while adding master key entry to the database"));
518
exit_status++;
519
goto cleanup_return;
520
}
521
522
cleanup_return:
523
/* clean up */
524
krb5_db_free_principal(util_context, master_entry);
525
krb5_dbe_free_actkvno_list(util_context, actkvno_list);
526
return;
527
}
528
529
void
530
kdb5_list_mkeys(int argc, char *argv[])
531
{
532
krb5_error_code retval;
533
char *output_str = NULL, enctype[BUFSIZ];
534
krb5_kvno act_kvno;
535
krb5_timestamp act_time;
536
krb5_actkvno_node *actkvno_list = NULL, *cur_actkvno;
537
krb5_db_entry *master_entry = NULL;
538
krb5_keylist_node *cur_kb_node;
539
krb5_keyblock *act_mkey;
540
krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context);
541
542
if (master_keylist == NULL) {
543
com_err(progname, 0, _("master keylist not initialized"));
544
exit_status++;
545
return;
546
}
547
548
retval = krb5_db_get_principal(util_context, master_princ, 0,
549
&master_entry);
550
if (retval != 0) {
551
com_err(progname, retval, _("while getting master key principal %s"),
552
mkey_fullname);
553
exit_status++;
554
goto cleanup_return;
555
}
556
557
retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
558
if (retval != 0) {
559
com_err(progname, retval, _("while looking up active kvno list"));
560
exit_status++;
561
goto cleanup_return;
562
}
563
564
retval = krb5_dbe_find_act_mkey(util_context, actkvno_list, &act_kvno,
565
&act_mkey);
566
if (retval != 0) {
567
com_err(progname, retval, _("while looking up active master key"));
568
exit_status++;
569
goto cleanup_return;
570
}
571
572
printf("Master keys for Principal: %s\n", mkey_fullname);
573
574
for (cur_kb_node = master_keylist; cur_kb_node != NULL;
575
cur_kb_node = cur_kb_node->next) {
576
577
if ((retval = krb5_enctype_to_name(cur_kb_node->keyblock.enctype,
578
FALSE, enctype, sizeof(enctype)))) {
579
com_err(progname, retval, _("while getting enctype description"));
580
exit_status++;
581
goto cleanup_return;
582
}
583
584
act_time = -1; /* assume actkvno entry not found */
585
for (cur_actkvno = actkvno_list; cur_actkvno != NULL;
586
cur_actkvno = cur_actkvno->next) {
587
if (cur_actkvno->act_kvno == cur_kb_node->kvno) {
588
act_time = cur_actkvno->act_time;
589
break;
590
}
591
}
592
593
if (cur_kb_node->kvno == act_kvno) {
594
/* * indicates kvno is currently active */
595
retval = asprintf(&output_str,
596
_("KVNO: %d, Enctype: %s, Active on: %s *\n"),
597
cur_kb_node->kvno, enctype, strdate(act_time));
598
} else {
599
if (act_time != -1) {
600
retval = asprintf(&output_str,
601
_("KVNO: %d, Enctype: %s, Active on: %s\n"),
602
cur_kb_node->kvno, enctype, strdate(act_time));
603
} else {
604
retval = asprintf(&output_str,
605
_("KVNO: %d, Enctype: %s, No activate time "
606
"set\n"), cur_kb_node->kvno, enctype);
607
}
608
}
609
if (retval == -1) {
610
com_err(progname, ENOMEM, _("asprintf could not allocate enough "
611
"memory to hold output"));
612
exit_status++;
613
goto cleanup_return;
614
}
615
printf("%s", output_str);
616
free(output_str);
617
output_str = NULL;
618
}
619
620
cleanup_return:
621
/* clean up */
622
krb5_db_free_principal(util_context, master_entry);
623
free(output_str);
624
krb5_dbe_free_actkvno_list(util_context, actkvno_list);
625
return;
626
}
627
628
struct update_enc_mkvno {
629
unsigned int re_match_count;
630
unsigned int already_current;
631
unsigned int updated;
632
unsigned int dry_run : 1;
633
unsigned int verbose : 1;
634
regex_t preg;
635
};
636
637
/* XXX Duplicated in libkadm5srv! */
638
/*
639
* Function: glob_to_regexp
640
*
641
* Arguments:
642
*
643
* glob (r) the shell-style glob (?*[]) to convert
644
* realm (r) the default realm to append, or NULL
645
* regexp (w) the ed-style regexp created from glob
646
*
647
* Effects:
648
*
649
* regexp is filled in with allocated memory containing a regular
650
* expression that matches what the shell-style glob would match.
651
* If glob does not contain an "@" character and realm is not
652
* NULL, "@*" is appended to the regexp.
653
*
654
* Conversion algorithm:
655
*
656
* quoted characters are copied quoted
657
* ? is converted to .
658
* * is converted to .*
659
* active characters are quoted: ^, $, .
660
* [ and ] are active but supported and have the same meaning, so
661
* they are copied
662
* other characters are copied
663
* regexp is anchored with ^ and $
664
*/
665
static int glob_to_regexp(char *glob, char *realm, char **regexp)
666
{
667
int append_realm;
668
char *p;
669
670
/* validate the glob */
671
if (glob[strlen(glob)-1] == '\\')
672
return EINVAL;
673
674
/* A character of glob can turn into two in regexp, plus ^ and $ */
675
/* and trailing null. If glob has no @, also allocate space for */
676
/* the realm. */
677
append_realm = (realm != NULL) && (strchr(glob, '@') == NULL);
678
p = (char *) malloc(strlen(glob)*2+ 3 + (append_realm ? 3 : 0));
679
if (p == NULL)
680
return ENOMEM;
681
*regexp = p;
682
683
*p++ = '^';
684
while (*glob) {
685
switch (*glob) {
686
case '?':
687
*p++ = '.';
688
break;
689
case '*':
690
*p++ = '.';
691
*p++ = '*';
692
break;
693
case '.':
694
case '^':
695
case '$':
696
*p++ = '\\';
697
*p++ = *glob;
698
break;
699
case '\\':
700
*p++ = '\\';
701
*p++ = *++glob;
702
break;
703
default:
704
*p++ = *glob;
705
break;
706
}
707
glob++;
708
}
709
710
if (append_realm) {
711
*p++ = '@';
712
*p++ = '.';
713
*p++ = '*';
714
}
715
716
*p++ = '$';
717
*p++ = '\0';
718
return 0;
719
}
720
721
static int
722
update_princ_encryption_1(void *cb, krb5_db_entry *ent)
723
{
724
struct update_enc_mkvno *p = cb;
725
char *pname = 0;
726
krb5_error_code retval;
727
krb5_timestamp now;
728
int result;
729
krb5_kvno old_mkvno;
730
731
retval = krb5_unparse_name(util_context, ent->princ, &pname);
732
if (retval) {
733
com_err(progname, retval,
734
_("getting string representation of principal name"));
735
goto fail;
736
}
737
738
if (krb5_principal_compare(util_context, ent->princ, master_princ)) {
739
goto skip;
740
}
741
742
if (regexec(&p->preg, pname, 0, NULL, 0) != 0)
743
goto skip;
744
p->re_match_count++;
745
retval = krb5_dbe_get_mkvno(util_context, ent, &old_mkvno);
746
if (retval) {
747
com_err(progname, retval,
748
_("determining master key used for principal '%s'"), pname);
749
goto fail;
750
}
751
/* Line up "skip" and "update" messages for viewing. */
752
if (old_mkvno == new_mkvno) {
753
if (p->dry_run && p->verbose)
754
printf(_("would skip: %s\n"), pname);
755
else if (p->verbose)
756
printf(_("skipping: %s\n"), pname);
757
p->already_current++;
758
goto skip;
759
}
760
if (p->dry_run) {
761
if (p->verbose)
762
printf(_("would update: %s\n"), pname);
763
p->updated++;
764
goto skip;
765
} else if (p->verbose)
766
printf(_("updating: %s\n"), pname);
767
retval = master_key_convert (util_context, ent);
768
if (retval) {
769
com_err(progname, retval,
770
_("error re-encrypting key for principal '%s'"), pname);
771
goto fail;
772
}
773
if ((retval = krb5_timeofday(util_context, &now))) {
774
com_err(progname, retval, _("while getting current time"));
775
goto fail;
776
}
777
778
if ((retval = krb5_dbe_update_mod_princ_data(util_context, ent,
779
now, master_princ))) {
780
com_err(progname, retval,
781
_("while updating principal '%s' modification time"), pname);
782
goto fail;
783
}
784
785
ent->mask |= KADM5_KEY_DATA | KADM5_TL_DATA;
786
787
if ((retval = krb5_db_put_principal(util_context, ent))) {
788
com_err(progname, retval, _("while updating principal '%s' key data "
789
"in the database"), pname);
790
goto fail;
791
}
792
p->updated++;
793
skip:
794
result = 0;
795
goto egress;
796
fail:
797
exit_status++;
798
result = 1;
799
egress:
800
if (pname)
801
krb5_free_unparsed_name(util_context, pname);
802
return result;
803
}
804
805
extern int are_you_sure (const char *, ...)
806
#if !defined(__cplusplus) && (__GNUC__ > 2)
807
__attribute__((__format__(__printf__, 1, 2)))
808
#endif
809
;
810
811
int
812
are_you_sure (const char *format, ...)
813
{
814
va_list va;
815
char ansbuf[100];
816
817
va_start(va, format);
818
vprintf(format, va);
819
va_end(va);
820
printf(_("\n(type 'yes' to confirm)? "));
821
fflush(stdout);
822
if (fgets(ansbuf, sizeof(ansbuf), stdin) == NULL)
823
return 0;
824
if (strcmp(ansbuf, "yes\n"))
825
return 0;
826
return 1;
827
}
828
829
void
830
kdb5_update_princ_encryption(int argc, char *argv[])
831
{
832
struct update_enc_mkvno data = { 0 };
833
char *name_pattern = NULL;
834
int force = 0;
835
int optchar;
836
krb5_error_code retval;
837
krb5_actkvno_node *actkvno_list = 0;
838
krb5_db_entry *master_entry = NULL;
839
char *regexp = NULL;
840
krb5_keyblock *act_mkey;
841
krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context);
842
krb5_flags iterflags = 0;
843
844
while ((optchar = getopt(argc, argv, "fnv")) != -1) {
845
switch (optchar) {
846
case 'f':
847
force = 1;
848
break;
849
case 'n':
850
data.dry_run = 1;
851
break;
852
case 'v':
853
data.verbose = 1;
854
break;
855
case '?':
856
case ':':
857
default:
858
usage();
859
}
860
}
861
if (argv[optind] != NULL) {
862
name_pattern = argv[optind];
863
if (argv[optind+1] != NULL)
864
usage();
865
}
866
867
if (master_keylist == NULL) {
868
com_err(progname, 0, _("master keylist not initialized"));
869
exit_status++;
870
goto cleanup;
871
}
872
873
/* The glob_to_regexp code only cares if the "realm" parameter is
874
NULL or not; the string data is irrelevant. */
875
if (name_pattern == NULL)
876
name_pattern = "*";
877
if (glob_to_regexp(name_pattern, "hi", &regexp) != 0) {
878
com_err(progname, ENOMEM,
879
_("converting glob pattern '%s' to regular expression"),
880
name_pattern);
881
exit_status++;
882
goto cleanup;
883
}
884
885
if (regcomp(&data.preg, regexp, REG_NOSUB) != 0) {
886
/* XXX syslog msg or regerr(regerrno) */
887
com_err(progname, 0, _("error compiling converted regexp '%s'"),
888
regexp);
889
exit_status++;
890
goto cleanup;
891
}
892
893
retval = krb5_db_get_principal(util_context, master_princ, 0,
894
&master_entry);
895
if (retval != 0) {
896
com_err(progname, retval, _("while getting master key principal %s"),
897
mkey_fullname);
898
exit_status++;
899
goto cleanup;
900
}
901
902
retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
903
if (retval != 0) {
904
com_err(progname, retval, _("while looking up active kvno list"));
905
exit_status++;
906
goto cleanup;
907
}
908
909
retval = krb5_dbe_find_act_mkey(util_context, actkvno_list, &new_mkvno,
910
&act_mkey);
911
if (retval) {
912
com_err(progname, retval, _("while looking up active master key"));
913
exit_status++;
914
goto cleanup;
915
}
916
new_master_keyblock = *act_mkey;
917
918
if (!force &&
919
!data.dry_run &&
920
!are_you_sure(_("Re-encrypt all keys not using master key vno %u?"),
921
new_mkvno)) {
922
printf(_("OK, doing nothing.\n"));
923
exit_status++;
924
goto cleanup;
925
}
926
if (data.verbose) {
927
if (data.dry_run) {
928
printf(_("Principals whose keys WOULD BE re-encrypted to master "
929
"key vno %u:\n"), new_mkvno);
930
} else {
931
printf(_("Principals whose keys are being re-encrypted to master "
932
"key vno %u if necessary:\n"), new_mkvno);
933
}
934
}
935
936
if (!data.dry_run) {
937
/* Grab a write lock so we don't have to upgrade to a write lock and
938
* reopen the DB while iterating. */
939
iterflags = KRB5_DB_ITER_WRITE;
940
}
941
942
retval = krb5_db_iterate(util_context, name_pattern,
943
update_princ_encryption_1, &data, iterflags);
944
/* If exit_status is set, then update_princ_encryption_1 already
945
printed a message. */
946
if (retval != 0 && exit_status == 0) {
947
com_err(progname, retval, _("trying to process principal database"));
948
exit_status++;
949
}
950
if (data.dry_run) {
951
printf(_("%u principals processed: %u would be updated, %u already "
952
"current\n"),
953
data.re_match_count, data.updated, data.already_current);
954
} else {
955
printf(_("%u principals processed: %u updated, %u already current\n"),
956
data.re_match_count, data.updated, data.already_current);
957
}
958
959
cleanup:
960
krb5_db_free_principal(util_context, master_entry);
961
free(regexp);
962
regfree(&data.preg);
963
memset(&new_master_keyblock, 0, sizeof(new_master_keyblock));
964
krb5_dbe_free_actkvno_list(util_context, actkvno_list);
965
}
966
967
struct kvnos_in_use {
968
krb5_kvno kvno;
969
unsigned int use_count;
970
};
971
972
struct purge_args {
973
krb5_context kcontext;
974
struct kvnos_in_use *kvnos;
975
unsigned int num_kvnos;
976
};
977
978
static krb5_error_code
979
find_mkvnos_in_use(krb5_pointer ptr,
980
krb5_db_entry *entry)
981
{
982
krb5_error_code retval;
983
struct purge_args * args;
984
unsigned int i;
985
krb5_kvno mkvno;
986
987
args = (struct purge_args *) ptr;
988
989
retval = krb5_dbe_get_mkvno(args->kcontext, entry, &mkvno);
990
if (retval)
991
return (retval);
992
993
for (i = 0; i < args->num_kvnos; i++) {
994
if (args->kvnos[i].kvno == mkvno) {
995
/* XXX do I need to worry about use_count wrapping? */
996
args->kvnos[i].use_count++;
997
break;
998
}
999
}
1000
return 0;
1001
}
1002
1003
void
1004
kdb5_purge_mkeys(int argc, char *argv[])
1005
{
1006
int optchar;
1007
krb5_error_code retval;
1008
krb5_timestamp now;
1009
krb5_db_entry *master_entry = NULL;
1010
krb5_boolean force = FALSE, dry_run = FALSE, verbose = FALSE;
1011
struct purge_args args;
1012
char buf[5];
1013
unsigned int i, j, k, num_kvnos_inuse, num_kvnos_purged;
1014
unsigned int old_key_data_count;
1015
krb5_actkvno_node *actkvno_list = NULL, *actkvno_entry, *prev_actkvno_entry;
1016
krb5_mkey_aux_node *mkey_aux_list = NULL, *mkey_aux_entry, *prev_mkey_aux_entry;
1017
krb5_key_data *old_key_data;
1018
1019
/*
1020
* Verify that the master key list has been initialized before doing
1021
* anything else.
1022
*/
1023
if (krb5_db_mkey_list_alias(util_context) == NULL) {
1024
com_err(progname, KRB5_KDB_DBNOTINITED,
1025
_("master keylist not initialized"));
1026
exit_status++;
1027
return;
1028
}
1029
1030
memset(&args, 0, sizeof(args));
1031
1032
optind = 1;
1033
while ((optchar = getopt(argc, argv, "fnv")) != -1) {
1034
switch(optchar) {
1035
case 'f':
1036
force = TRUE;
1037
break;
1038
case 'n':
1039
dry_run = TRUE; /* mkey princ will not be modified */
1040
force = TRUE; /* implied */
1041
break;
1042
case 'v':
1043
verbose = TRUE;
1044
break;
1045
case '?':
1046
default:
1047
usage();
1048
return;
1049
}
1050
}
1051
1052
retval = krb5_db_get_principal(util_context, master_princ, 0,
1053
&master_entry);
1054
if (retval != 0) {
1055
com_err(progname, retval, _("while getting master key principal %s"),
1056
mkey_fullname);
1057
exit_status++;
1058
goto cleanup_return;
1059
}
1060
1061
if (!force) {
1062
printf(_("Will purge all unused master keys stored in the '%s' "
1063
"principal, are you sure?\n"), mkey_fullname);
1064
printf(_("(type 'yes' to confirm)? "));
1065
if (fgets(buf, sizeof(buf), stdin) == NULL) {
1066
exit_status++;
1067
goto cleanup_return;
1068
}
1069
if (strcmp(buf, "yes\n")) {
1070
exit_status++;
1071
goto cleanup_return;
1072
}
1073
printf(_("OK, purging unused master keys from '%s'...\n"),
1074
mkey_fullname);
1075
}
1076
1077
/* save the old keydata */
1078
old_key_data_count = master_entry->n_key_data;
1079
if (old_key_data_count == 1) {
1080
if (verbose)
1081
printf(_("There is only one master key which can not be "
1082
"purged.\n"));
1083
goto cleanup_return;
1084
}
1085
old_key_data = master_entry->key_data;
1086
1087
args.kvnos = (struct kvnos_in_use *) malloc(sizeof(struct kvnos_in_use) * old_key_data_count);
1088
if (args.kvnos == NULL) {
1089
retval = ENOMEM;
1090
com_err(progname, ENOMEM, _("while allocating args.kvnos"));
1091
exit_status++;
1092
goto cleanup_return;
1093
}
1094
memset(args.kvnos, 0, sizeof(struct kvnos_in_use) * old_key_data_count);
1095
args.num_kvnos = old_key_data_count;
1096
args.kcontext = util_context;
1097
1098
/* populate the kvnos array with all the current mkvnos */
1099
for (i = 0; i < old_key_data_count; i++)
1100
args.kvnos[i].kvno = master_entry->key_data[i].key_data_kvno;
1101
1102
if ((retval = krb5_db_iterate(util_context,
1103
NULL,
1104
find_mkvnos_in_use,
1105
(krb5_pointer) &args, 0))) {
1106
com_err(progname, retval, _("while finding master keys in use"));
1107
exit_status++;
1108
goto cleanup_return;
1109
}
1110
/*
1111
* args.kvnos has been marked with the mkvno's that are currently protecting
1112
* princ entries
1113
*/
1114
if (dry_run) {
1115
printf(_("Would purge the following master key(s) from %s:\n"),
1116
mkey_fullname);
1117
} else {
1118
printf(_("Purging the following master key(s) from %s:\n"),
1119
mkey_fullname);
1120
}
1121
1122
/* find # of keys still in use or print out verbose info */
1123
for (i = num_kvnos_inuse = num_kvnos_purged = 0; i < args.num_kvnos; i++) {
1124
if (args.kvnos[i].use_count > 0) {
1125
num_kvnos_inuse++;
1126
} else {
1127
/* this key would be deleted */
1128
if (args.kvnos[i].kvno == master_kvno) {
1129
com_err(progname, KRB5_KDB_STORED_MKEY_NOTCURRENT,
1130
_("master key stash file needs updating, command "
1131
"aborting"));
1132
exit_status++;
1133
goto cleanup_return;
1134
}
1135
num_kvnos_purged++;
1136
printf(_("KVNO: %d\n"), args.kvnos[i].kvno);
1137
}
1138
}
1139
/* didn't find any keys to purge */
1140
if (num_kvnos_inuse == args.num_kvnos) {
1141
printf(_("All keys in use, nothing purged.\n"));
1142
goto cleanup_return;
1143
}
1144
if (dry_run) {
1145
/* bail before doing anything else */
1146
printf(_("%d key(s) would be purged.\n"), num_kvnos_purged);
1147
goto cleanup_return;
1148
}
1149
1150
retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
1151
if (retval != 0) {
1152
com_err(progname, retval, _("while looking up active kvno list"));
1153
exit_status++;
1154
goto cleanup_return;
1155
}
1156
1157
retval = krb5_dbe_lookup_mkey_aux(util_context, master_entry, &mkey_aux_list);
1158
if (retval != 0) {
1159
com_err(progname, retval, _("while looking up mkey aux data list"));
1160
exit_status++;
1161
goto cleanup_return;
1162
}
1163
1164
master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * num_kvnos_inuse);
1165
if (master_entry->key_data == NULL) {
1166
retval = ENOMEM;
1167
com_err(progname, ENOMEM, _("while allocating key_data"));
1168
exit_status++;
1169
goto cleanup_return;
1170
}
1171
memset(master_entry->key_data, 0, sizeof(krb5_key_data) * num_kvnos_inuse);
1172
master_entry->n_key_data = num_kvnos_inuse; /* there's only 1 mkey per kvno */
1173
1174
/*
1175
* Assuming that the latest mkey will not be purged because it will always
1176
* be "in use" so this code will not bother with encrypting keys again.
1177
*/
1178
for (i = k = 0; i < old_key_data_count; i++) {
1179
for (j = 0; j < args.num_kvnos; j++) {
1180
if (args.kvnos[j].kvno == (krb5_kvno) old_key_data[i].key_data_kvno) {
1181
if (args.kvnos[j].use_count != 0) {
1182
master_entry->key_data[k++] = old_key_data[i];
1183
memset(&old_key_data[i], 0, sizeof(old_key_data[i]));
1184
break;
1185
} else {
1186
/* remove unused mkey */
1187
/* adjust the actkno data */
1188
for (prev_actkvno_entry = actkvno_entry = actkvno_list;
1189
actkvno_entry != NULL;
1190
actkvno_entry = actkvno_entry->next) {
1191
1192
if (actkvno_entry->act_kvno == args.kvnos[j].kvno) {
1193
if (actkvno_entry == actkvno_list) {
1194
/* remove from head */
1195
actkvno_list = actkvno_entry->next;
1196
} else if (actkvno_entry->next == NULL) {
1197
/* remove from tail */
1198
prev_actkvno_entry->next = NULL;
1199
} else {
1200
/* remove in between */
1201
prev_actkvno_entry->next = actkvno_entry->next;
1202
}
1203
actkvno_entry->next = NULL;
1204
krb5_dbe_free_actkvno_list(util_context, actkvno_entry);
1205
break; /* deleted entry, no need to loop further */
1206
} else {
1207
prev_actkvno_entry = actkvno_entry;
1208
}
1209
}
1210
/* adjust the mkey aux data */
1211
for (prev_mkey_aux_entry = mkey_aux_entry = mkey_aux_list;
1212
mkey_aux_entry != NULL;
1213
mkey_aux_entry = mkey_aux_entry->next) {
1214
1215
if (mkey_aux_entry->mkey_kvno == args.kvnos[j].kvno) {
1216
if (mkey_aux_entry == mkey_aux_list) {
1217
mkey_aux_list = mkey_aux_entry->next;
1218
} else if (mkey_aux_entry->next == NULL) {
1219
prev_mkey_aux_entry->next = NULL;
1220
} else {
1221
prev_mkey_aux_entry->next = mkey_aux_entry->next;
1222
}
1223
mkey_aux_entry->next = NULL;
1224
krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_entry);
1225
break; /* deleted entry, no need to loop further */
1226
} else {
1227
prev_mkey_aux_entry = mkey_aux_entry;
1228
}
1229
}
1230
}
1231
}
1232
}
1233
}
1234
assert(k == num_kvnos_inuse);
1235
1236
/* Free any key data entries we did not consume in the loop above. */
1237
for (i = 0; i < old_key_data_count; i++)
1238
krb5_dbe_free_key_data_contents(util_context, &old_key_data[i]);
1239
free(old_key_data);
1240
1241
if ((retval = krb5_dbe_update_actkvno(util_context, master_entry,
1242
actkvno_list))) {
1243
com_err(progname, retval,
1244
_("while updating actkvno data for master principal entry"));
1245
exit_status++;
1246
goto cleanup_return;
1247
}
1248
1249
if ((retval = krb5_dbe_update_mkey_aux(util_context, master_entry,
1250
mkey_aux_list))) {
1251
com_err(progname, retval,
1252
_("while updating mkey_aux data for master principal entry"));
1253
exit_status++;
1254
goto cleanup_return;
1255
}
1256
1257
if ((retval = krb5_timeofday(util_context, &now))) {
1258
com_err(progname, retval, _("while getting current time"));
1259
exit_status++;
1260
goto cleanup_return;
1261
}
1262
1263
if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry,
1264
now, master_princ))) {
1265
com_err(progname, retval, _("while updating the master key principal "
1266
"modification time"));
1267
exit_status++;
1268
goto cleanup_return;
1269
}
1270
1271
master_entry->mask |= KADM5_KEY_DATA | KADM5_TL_DATA;
1272
1273
if ((retval = krb5_db_put_principal(util_context, master_entry))) {
1274
com_err(progname, retval,
1275
_("while adding master key entry to the database"));
1276
exit_status++;
1277
goto cleanup_return;
1278
}
1279
printf(_("%d key(s) purged.\n"), num_kvnos_purged);
1280
1281
cleanup_return:
1282
krb5_db_free_principal(util_context, master_entry);
1283
free(args.kvnos);
1284
krb5_dbe_free_actkvno_list(util_context, actkvno_list);
1285
krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_list);
1286
return;
1287
}
1288
1289