~ubuntu-branches/ubuntu/maverick/krb5/maverick

« back to all changes in this revision

Viewing changes to src/kadmin/dbutil/kdb5_mkey.c

  • Committer: Bazaar Package Importer
  • Author(s): Sam Hartman
  • Date: 2009-05-07 16:16:34 UTC
  • mfrom: (13.1.7 sid)
  • Revision ID: james.westby@ubuntu.com-20090507161634-xqyk0s9na0le4flj
Tags: 1.7dfsg~beta1-4
When  decrypting the TGS response fails with the subkey, try with the
session key to work around Heimdal bug, Closes: #527353 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: c; 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 <stdio.h>
 
8
#include <time.h>
 
9
#include <k5-int.h>
 
10
#include <kdb.h>
 
11
#include <kadm5/server_internal.h>
 
12
#include <kadm5/admin.h>
 
13
#include <adm_proto.h>
 
14
#include "kdb5_util.h"
 
15
 
 
16
#if defined(HAVE_COMPILE) && defined(HAVE_STEP)
 
17
#define SOLARIS_REGEXPS
 
18
#elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXEC)
 
19
#define POSIX_REGEXPS
 
20
#elif defined(HAVE_RE_COMP) && defined(HAVE_RE_EXEC)
 
21
#define BSD_REGEXPS
 
22
#else
 
23
#error I cannot find any regexp functions
 
24
#endif
 
25
#ifdef SOLARIS_REGEXPS
 
26
#include        <regexpr.h>
 
27
#endif
 
28
#ifdef POSIX_REGEXPS
 
29
#include        <regex.h>
 
30
#endif
 
31
 
 
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 *);
 
43
 
 
44
static char *strdate(krb5_timestamp when)
 
45
{
 
46
    struct tm *tm;
 
47
    static char out[40];
 
48
 
 
49
    time_t lcltim = when;
 
50
    tm = localtime(&lcltim);
 
51
    strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm);
 
52
    return out;
 
53
}
 
54
 
 
55
krb5_kvno
 
56
get_next_kvno(krb5_context context, krb5_db_entry *entry)
 
57
{
 
58
    krb5_kvno new_kvno;
 
59
 
 
60
    new_kvno = krb5_db_get_key_data_kvno(context, entry->n_key_data,
 
61
                                         entry->key_data);
 
62
    new_kvno++;
 
63
    /* deal with wrapping */
 
64
    if (new_kvno == 0)
 
65
        new_kvno = 1; /* knvo must not be 0 as this is special value (IGNORE_VNO) */
 
66
 
 
67
    return (new_kvno);
 
68
}
 
69
 
 
70
krb5_error_code
 
71
add_new_mkey(krb5_context context, krb5_db_entry *master_entry,
 
72
             krb5_keyblock *new_mkey, krb5_kvno use_mkvno)
 
73
{
 
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;
 
80
 
 
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);
 
86
 
 
87
    /* save the old keydata */
 
88
    old_key_data_count = master_entry->n_key_data;
 
89
    old_key_data = master_entry->key_data;
 
90
 
 
91
    /* alloc enough space to hold new and existing key_data */
 
92
    /*
 
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().
 
96
     */
 
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)
 
100
        return (ENOMEM);
 
101
 
 
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;
 
105
 
 
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,
 
109
                                              new_mkey, NULL, 
 
110
                                              (int) new_mkey_kvno,
 
111
                                              &master_entry->key_data[0]))) {
 
112
        return (retval);
 
113
    }
 
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]);
 
117
        return (retval);
 
118
    }
 
119
    /*
 
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
 
124
     * the older mkeys.
 
125
     *
 
126
     * The new mkey is followed by existing keys.
 
127
     *
 
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.
 
130
     */
 
131
    mkey_aux_data_head = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
 
132
    if (mkey_aux_data_head == NULL) {
 
133
        retval = ENOMEM;
 
134
        goto clean_n_exit;
 
135
    }
 
136
    memset(mkey_aux_data_head, 0, sizeof(krb5_mkey_aux_node));
 
137
    mkey_aux_data = &mkey_aux_data_head;
 
138
 
 
139
    for (keylist_node = master_keylist, i = 1; keylist_node != NULL;
 
140
         keylist_node = keylist_node->next, i++) {
 
141
 
 
142
        /*
 
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
 
145
         * old mkey).
 
146
         */
 
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) {
 
151
                retval = ENOMEM;
 
152
                goto clean_n_exit;
 
153
            }
 
154
            memset(*mkey_aux_data, 0, sizeof(krb5_mkey_aux_node));
 
155
        }
 
156
 
 
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,
 
160
                                             new_mkey,
 
161
                                             NULL, /* no keysalt */
 
162
                                             (int) new_mkey_kvno,
 
163
                                             &tmp_key_data);
 
164
        if (retval)
 
165
            goto clean_n_exit;
 
166
 
 
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);
 
170
 
 
171
        /*
 
172
         * Store old key in master_entry keydata past the new mkey
 
173
         */
 
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]);
 
179
        if (retval)
 
180
            goto clean_n_exit;
 
181
    }
 
182
    assert(i == old_key_data_count + 1);
 
183
 
 
184
    if ((retval = krb5_dbe_update_mkey_aux(context, master_entry,
 
185
                                           mkey_aux_data_head))) {
 
186
        goto clean_n_exit;
 
187
    }
 
188
    master_entry->mask |= KADM5_KEY_DATA;
 
189
 
 
190
clean_n_exit:
 
191
    krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_head);
 
192
    return (retval);
 
193
}
 
194
 
 
195
void
 
196
kdb5_add_mkey(int argc, char *argv[])
 
197
{
 
198
    int optchar;
 
199
    krb5_error_code retval;
 
200
    char *mkey_fullname;
 
201
    char *pw_str = 0;
 
202
    unsigned int pw_size = 0;
 
203
    int do_stash = 0, nentries = 0;
 
204
    krb5_boolean more = 0;
 
205
    krb5_data pwd;
 
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;
 
211
    krb5_timestamp now;
 
212
 
 
213
    /*
 
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.
 
216
     */
 
217
 
 
218
    memset(&new_mkeyblock, 0, sizeof(new_mkeyblock));
 
219
    memset(&master_princ, 0, sizeof(master_princ));
 
220
    master_salt.data = NULL;
 
221
 
 
222
    while ((optchar = getopt(argc, argv, "e:s")) != -1) {
 
223
        switch(optchar) {
 
224
        case 'e':
 
225
            if (krb5_string_to_enctype(optarg, &new_master_enctype)) {
 
226
                com_err(progname, EINVAL, "%s is an invalid enctype", optarg);
 
227
                exit_status++;
 
228
                return;
 
229
            }
 
230
            break;
 
231
        case 's':
 
232
            do_stash++;
 
233
            break;
 
234
        case '?':
 
235
        default:
 
236
            usage();
 
237
            return;
 
238
        }
 
239
    }
 
240
 
 
241
    if (new_master_enctype == ENCTYPE_UNKNOWN)
 
242
        new_master_enctype = global_params.enctype;
 
243
 
 
244
    /* assemble & parse the master key name */
 
245
    if ((retval = krb5_db_setup_mkey_name(util_context,
 
246
                                          global_params.mkey_name,
 
247
                                          global_params.realm,  
 
248
                                          &mkey_fullname, &master_princ))) {
 
249
        com_err(progname, retval, "while setting up master key name");
 
250
        exit_status++;
 
251
        return;
 
252
    }
 
253
 
 
254
    retval = krb5_db_get_principal(util_context, master_princ, &master_entry,
 
255
                                   &nentries, &more);
 
256
    if (retval != 0) {
 
257
        com_err(progname, retval,
 
258
                "while getting master key principal %s",
 
259
                mkey_fullname);
 
260
        exit_status++;
 
261
        goto cleanup_return;
 
262
    } else if (nentries == 0) {
 
263
        com_err(progname, KRB5_KDB_NOENTRY,
 
264
                "principal %s not found in Kerberos database",
 
265
                mkey_fullname);
 
266
        exit_status++;
 
267
        goto cleanup_return;
 
268
    } else if (nentries > 1) {
 
269
        com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
 
270
                "principal %s has multiple entries in Kerberos database",
 
271
                mkey_fullname);
 
272
        exit_status++;
 
273
        goto cleanup_return;
 
274
    }
 
275
 
 
276
    printf("Creating new master key for master key principal '%s'\n",
 
277
        mkey_fullname);
 
278
 
 
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");
 
281
    fflush(stdout);
 
282
 
 
283
    pw_size = 1024;
 
284
    pw_str = malloc(pw_size);
 
285
    if (pw_str == NULL) {
 
286
        com_err(progname, ENOMEM, "while creating new master key");
 
287
        exit_status++;
 
288
        goto cleanup_return;
 
289
    }
 
290
 
 
291
    retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
 
292
                                pw_str, &pw_size);
 
293
    if (retval) {
 
294
        com_err(progname, retval, "while reading new master key from keyboard");
 
295
        exit_status++;
 
296
        goto cleanup_return;
 
297
    }
 
298
    new_mkey_password = pw_str;
 
299
 
 
300
    pwd.data = new_mkey_password;
 
301
    pwd.length = strlen(new_mkey_password);
 
302
    retval = krb5_principal2salt(util_context, master_princ, &master_salt);
 
303
    if (retval) {
 
304
        com_err(progname, retval, "while calculating master key salt");
 
305
        exit_status++;
 
306
        goto cleanup_return;
 
307
    }
 
308
 
 
309
    retval = krb5_c_string_to_key(util_context, new_master_enctype, 
 
310
                                  &pwd, &master_salt, &new_mkeyblock);
 
311
    if (retval) {
 
312
        com_err(progname, retval, "while transforming master key from password");
 
313
        exit_status++;
 
314
        goto cleanup_return;
 
315
    }
 
316
 
 
317
    retval = add_new_mkey(util_context, &master_entry, &new_mkeyblock, 0);
 
318
    if (retval) {
 
319
        com_err(progname, retval, "adding new master key to master principal");
 
320
        exit_status++;
 
321
        goto cleanup_return;
 
322
    }
 
323
 
 
324
    if ((retval = krb5_timeofday(util_context, &now))) {
 
325
        com_err(progname, retval, "while getting current time");
 
326
        exit_status++;
 
327
        goto cleanup_return;
 
328
    }
 
329
 
 
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");
 
333
        exit_status++;
 
334
        goto cleanup_return;
 
335
    }
 
336
 
 
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");
 
340
        exit_status++;
 
341
        goto cleanup_return;
 
342
    }
 
343
 
 
344
    if (do_stash) {
 
345
        retval = krb5_db_store_master_key(util_context,
 
346
                                          global_params.stash_file,
 
347
                                          master_princ,
 
348
                                          new_mkey_kvno,
 
349
                                          &new_mkeyblock,
 
350
                                          mkey_password);
 
351
        if (retval) {
 
352
            com_err(progname, errno, "while storing key");
 
353
            printf("Warning: couldn't stash master key.\n");
 
354
        }
 
355
    }
 
356
 
 
357
cleanup_return:
 
358
    /* clean up */
 
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);
 
364
    if (pw_str) {
 
365
        zap(pw_str, pw_size);
 
366
        free(pw_str);
 
367
    }
 
368
    free(master_salt.data);
 
369
    krb5_free_unparsed_name(util_context, mkey_fullname);
 
370
    return;
 
371
}
 
372
 
 
373
void
 
374
kdb5_use_mkey(int argc, char *argv[])
 
375
{
 
376
    krb5_error_code retval;
 
377
    char  *mkey_fullname = NULL;
 
378
    krb5_kvno  use_kvno;
 
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;
 
383
    int nentries = 0;
 
384
    krb5_boolean more = FALSE;
 
385
    krb5_keylist_node *keylist_node;
 
386
    krb5_boolean inserted = FALSE;
 
387
 
 
388
    memset(&master_princ, 0, sizeof(master_princ));
 
389
 
 
390
    if (argc < 2 || argc > 3) {
 
391
        /* usage calls exit */
 
392
        usage();
 
393
    }
 
394
 
 
395
    use_kvno = atoi(argv[1]);
 
396
    if (use_kvno == 0) {
 
397
        com_err(progname, EINVAL, "0 is an invalid KVNO value");
 
398
        exit_status++;
 
399
        return;
 
400
    } else {
 
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)
 
405
                break;
 
406
        }
 
407
        if (!keylist_node) {
 
408
            com_err(progname, EINVAL, "%d is an invalid KVNO value", use_kvno);
 
409
            exit_status++;
 
410
            return;
 
411
        }
 
412
    }
 
413
 
 
414
    if ((retval = krb5_timeofday(util_context, &now))) {
 
415
        com_err(progname, retval, "while getting current time");
 
416
        exit_status++;
 
417
        return;
 
418
    }
 
419
 
 
420
    if (argc == 3) {
 
421
        time_t t = get_date(argv[2]);
 
422
        if (t == -1) {
 
423
            com_err(progname, 0, "could not parse date-time string '%s'",
 
424
                    argv[2]);
 
425
            exit_status++;
 
426
            return;
 
427
        } else
 
428
            start_time = (krb5_timestamp) t;
 
429
    } else {
 
430
        start_time = now;
 
431
    }
 
432
 
 
433
    /*
 
434
     * Need to:
 
435
     *
 
436
     * 1. get mkey princ
 
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
 
440
     * 5. put mkey princ.
 
441
     */
 
442
 
 
443
    /* assemble & parse the master key name */
 
444
    if ((retval = krb5_db_setup_mkey_name(util_context,
 
445
                                          global_params.mkey_name,
 
446
                                          global_params.realm,  
 
447
                                          &mkey_fullname, &master_princ))) {
 
448
        com_err(progname, retval, "while setting up master key name");
 
449
        exit_status++;
 
450
        goto cleanup_return;
 
451
    }
 
452
 
 
453
    retval = krb5_db_get_principal(util_context, master_princ, &master_entry,
 
454
                                   &nentries, &more);
 
455
    if (retval != 0) {
 
456
        com_err(progname, retval,
 
457
                "while getting master key principal %s",
 
458
                mkey_fullname);
 
459
        exit_status++;
 
460
        goto cleanup_return;
 
461
    } else if (nentries == 0) {
 
462
        com_err(progname, KRB5_KDB_NOENTRY,
 
463
                "principal %s not found in Kerberos database",
 
464
                mkey_fullname);
 
465
        exit_status++;
 
466
        goto cleanup_return;
 
467
    } else if (nentries > 1) {
 
468
        com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
 
469
                "principal %s has multiple entries in Kerberos database",
 
470
                mkey_fullname);
 
471
        exit_status++;
 
472
        goto cleanup_return;
 
473
    }
 
474
 
 
475
    retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
 
476
    if (retval != 0) {
 
477
        com_err(progname, retval,
 
478
                "while looking up active version of master key");
 
479
        exit_status++;
 
480
        goto cleanup_return;
 
481
    }
 
482
 
 
483
    /*
 
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.
 
486
     */
 
487
    for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
 
488
         cur_actkvno != NULL;
 
489
         prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
 
490
 
 
491
        if (cur_actkvno->act_kvno == use_kvno) {
 
492
            /* delete it */
 
493
            if (prev_actkvno) {
 
494
                prev_actkvno->next = cur_actkvno->next;
 
495
                cur_actkvno->next = NULL;
 
496
                krb5_dbe_free_actkvno_list(util_context, cur_actkvno);
 
497
            } else {
 
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);
 
503
                } else {
 
504
                    /* There's only one entry, go ahead and change the time */
 
505
                    cur_actkvno->act_time = start_time;
 
506
                    inserted = TRUE;
 
507
                }
 
508
            }
 
509
            break;
 
510
        }
 
511
    }
 
512
 
 
513
    if (!inserted) {
 
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");
 
518
            exit_status++;
 
519
            goto cleanup_return;
 
520
        }
 
521
        memset(new_actkvno, 0, sizeof(krb5_actkvno_node));
 
522
        new_actkvno->act_kvno = use_kvno;
 
523
        new_actkvno->act_time = start_time;
 
524
 
 
525
        /* insert new act kvno node */
 
526
 
 
527
        if (actkvno_list == NULL) {
 
528
            /* new actkvno is the list */
 
529
            actkvno_list = new_actkvno;
 
530
        } else {
 
531
            for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
 
532
                 cur_actkvno != NULL;
 
533
                 prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
 
534
 
 
535
                if (new_actkvno->act_time < cur_actkvno->act_time) {
 
536
                    if (prev_actkvno) {
 
537
                        prev_actkvno->next = new_actkvno;
 
538
                        new_actkvno->next = cur_actkvno;
 
539
                    } else {
 
540
                        new_actkvno->next = actkvno_list;
 
541
                        actkvno_list = new_actkvno;
 
542
                    }
 
543
                    break;
 
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;
 
547
                    break;
 
548
                }
 
549
            }
 
550
        }
 
551
    }
 
552
 
 
553
    if (actkvno_list->act_time > now) {
 
554
        com_err(progname, EINVAL, "there must be one master key currently active");
 
555
        exit_status++;
 
556
        goto cleanup_return;
 
557
    }
 
558
 
 
559
    if ((retval = krb5_dbe_update_actkvno(util_context, &master_entry,
 
560
                                          actkvno_list))) {
 
561
        com_err(progname, retval, "while updating actkvno data for master principal entry");
 
562
        exit_status++;
 
563
        goto cleanup_return;
 
564
    }
 
565
 
 
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");
 
569
        exit_status++;
 
570
        goto cleanup_return;
 
571
    }
 
572
 
 
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");
 
576
        exit_status++;
 
577
        goto cleanup_return;
 
578
    }
 
579
 
 
580
cleanup_return:
 
581
    /* clean up */
 
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);
 
586
    return;
 
587
}
 
588
 
 
589
void
 
590
kdb5_list_mkeys(int argc, char *argv[])
 
591
{
 
592
    krb5_error_code retval;
 
593
    char  *mkey_fullname = NULL, *output_str = NULL, enctype[BUFSIZ];
 
594
    krb5_kvno  act_kvno;
 
595
    krb5_timestamp act_time;
 
596
    krb5_actkvno_node *actkvno_list = NULL, *cur_actkvno;
 
597
    krb5_db_entry master_entry;
 
598
    int   nentries = 0;
 
599
    krb5_boolean more = FALSE;
 
600
    krb5_keylist_node  *cur_kb_node;
 
601
    krb5_keyblock *act_mkey;
 
602
 
 
603
    if (master_keylist == NULL) {
 
604
        com_err(progname, 0, "master keylist not initialized");
 
605
        exit_status++;
 
606
        return;
 
607
    }
 
608
 
 
609
    /* assemble & parse the master key name */
 
610
    if ((retval = krb5_db_setup_mkey_name(util_context,
 
611
                                          global_params.mkey_name,
 
612
                                          global_params.realm,  
 
613
                                          &mkey_fullname, &master_princ))) {
 
614
        com_err(progname, retval, "while setting up master key name");
 
615
        exit_status++;
 
616
        return;
 
617
    }
 
618
 
 
619
    retval = krb5_db_get_principal(util_context, master_princ, &master_entry,
 
620
                                   &nentries, &more);
 
621
    if (retval != 0) {
 
622
        com_err(progname, retval,
 
623
                "while getting master key principal %s",
 
624
                mkey_fullname);
 
625
        exit_status++;
 
626
        goto cleanup_return;
 
627
    } else if (nentries == 0) {
 
628
        com_err(progname, KRB5_KDB_NOENTRY,
 
629
                "principal %s not found in Kerberos database",
 
630
                mkey_fullname);
 
631
        exit_status++;
 
632
        goto cleanup_return;
 
633
    } else if (nentries > 1) {
 
634
        com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
 
635
                "principal %s has multiple entries in Kerberos database",
 
636
                mkey_fullname);
 
637
        exit_status++;
 
638
        goto cleanup_return;
 
639
    }
 
640
 
 
641
    retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
 
642
    if (retval != 0) {
 
643
        com_err(progname, retval, "while looking up active kvno list");
 
644
        exit_status++;
 
645
        goto cleanup_return;
 
646
    }
 
647
 
 
648
    if (actkvno_list == NULL) {
 
649
        act_kvno = master_entry.key_data[0].key_data_kvno;
 
650
    } else {
 
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, "");
 
657
            /* Keep going.  */
 
658
            act_kvno = -1;
 
659
        } else if (retval != 0) {
 
660
            com_err(progname, retval, "while looking up active master key");
 
661
            exit_status++;
 
662
            goto cleanup_return;
 
663
        }
 
664
    }
 
665
 
 
666
    printf("Master keys for Principal: %s\n", mkey_fullname);
 
667
 
 
668
    for (cur_kb_node = master_keylist; cur_kb_node != NULL;
 
669
         cur_kb_node = cur_kb_node->next) {
 
670
 
 
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");
 
674
            exit_status++;
 
675
            goto cleanup_return;
 
676
        }
 
677
 
 
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;
 
684
                    break;
 
685
                }
 
686
            }
 
687
        } else {
 
688
            /*
 
689
             * mkey princ doesn't have an active knvo list so assume the current
 
690
             * key is active now
 
691
             */
 
692
            if ((retval = krb5_timeofday(util_context, &act_time))) {
 
693
                com_err(progname, retval, "while getting current time");
 
694
                exit_status++;
 
695
                goto cleanup_return;
 
696
            }
 
697
        }
 
698
 
 
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));
 
703
        } else {
 
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));
 
707
            } else {
 
708
                retval = asprintf(&output_str, "KNVO: %d, Enctype: %s, No activate time set\n",
 
709
                                  cur_kb_node->kvno, enctype);
 
710
            }
 
711
        }
 
712
        if (retval == -1) {
 
713
            com_err(progname, ENOMEM, "asprintf could not allocate enough memory to hold output");
 
714
            exit_status++;
 
715
            goto cleanup_return;
 
716
        }
 
717
        printf("%s", output_str);
 
718
        free(output_str);
 
719
        output_str = NULL;
 
720
    }
 
721
 
 
722
cleanup_return:
 
723
    /* clean up */
 
724
    (void) krb5_db_fini(util_context);
 
725
    krb5_free_unparsed_name(util_context, mkey_fullname);
 
726
    free(output_str);
 
727
    krb5_free_principal(util_context, master_princ);
 
728
    krb5_dbe_free_actkvno_list(util_context, actkvno_list);
 
729
    return;
 
730
}
 
731
 
 
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
 
739
    char *expbuf;
 
740
#endif
 
741
#ifdef POSIX_REGEXPS
 
742
    regex_t preg;
 
743
#endif
 
744
#if !defined(SOLARIS_REGEXPS) && !defined(POSIX_REGEXPS)
 
745
    unsigned char placeholder;
 
746
#endif
 
747
};
 
748
 
 
749
/* XXX Duplicated in libkadm5srv! */
 
750
/*
 
751
 * Function: glob_to_regexp
 
752
 *
 
753
 * Arguments:
 
754
 *
 
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
 
758
 *
 
759
 * Effects:
 
760
 *
 
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.
 
765
 *
 
766
 * Conversion algorithm:
 
767
 *
 
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
 
773
 *              they are copied
 
774
 *      other characters are copied
 
775
 *      regexp is anchored with ^ and $
 
776
 */
 
777
static int glob_to_regexp(char *glob, char *realm, char **regexp)
 
778
{
 
779
     int append_realm;
 
780
     char *p;
 
781
 
 
782
     /* validate the glob */
 
783
     if (glob[strlen(glob)-1] == '\\')
 
784
          return EINVAL;
 
785
 
 
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 */
 
788
     /* the realm. */
 
789
     append_realm = (realm != NULL) && (strchr(glob, '@') == NULL);
 
790
     p = (char *) malloc(strlen(glob)*2+ 3 + (append_realm ? 3 : 0));
 
791
     if (p == NULL)
 
792
          return ENOMEM;
 
793
     *regexp = p;
 
794
 
 
795
     *p++ = '^';
 
796
     while (*glob) {
 
797
          switch (*glob) {
 
798
          case '?':
 
799
               *p++ = '.';
 
800
               break;
 
801
          case '*':
 
802
               *p++ = '.';
 
803
               *p++ = '*';
 
804
               break;
 
805
          case '.':
 
806
          case '^':
 
807
          case '$':
 
808
               *p++ = '\\';
 
809
               *p++ = *glob;
 
810
               break;
 
811
          case '\\':
 
812
               *p++ = '\\';
 
813
               *p++ = *++glob;
 
814
               break;
 
815
          default:
 
816
               *p++ = *glob;
 
817
               break;
 
818
          }
 
819
          glob++;
 
820
     }
 
821
 
 
822
     if (append_realm) {
 
823
          *p++ = '@';
 
824
          *p++ = '.';
 
825
          *p++ = '*';
 
826
     }
 
827
 
 
828
     *p++ = '$';
 
829
     *p++ = '\0';
 
830
     return 0;
 
831
}
 
832
 
 
833
static int
 
834
update_princ_encryption_1(void *cb, krb5_db_entry *ent)
 
835
{
 
836
    struct update_enc_mkvno *p = cb;
 
837
    char *pname = 0;
 
838
    krb5_error_code retval;
 
839
    int match;
 
840
    krb5_timestamp now;
 
841
    int nentries = 1;
 
842
    int result;
 
843
    krb5_kvno old_mkvno;
 
844
 
 
845
    retval = krb5_unparse_name(util_context, ent->princ, &pname);
 
846
    if (retval) {
 
847
        com_err(progname, retval,
 
848
                "getting string representation of principal name");
 
849
        goto fail;
 
850
    }
 
851
 
 
852
    if (krb5_principal_compare(util_context, ent->princ, master_princ)) {
 
853
        goto skip;
 
854
    }
 
855
 
 
856
#ifdef SOLARIS_REGEXPS
 
857
    match = (step(pname, p->expbuf) != 0);
 
858
#endif
 
859
#ifdef POSIX_REGEXPS
 
860
    match = (regexec(&p->preg, pname, 0, NULL, 0) == 0);
 
861
#endif
 
862
#ifdef BSD_REGEXPS
 
863
    match = (re_exec(pname) != 0);
 
864
#endif
 
865
    if (!match) {
 
866
        goto skip;
 
867
    }
 
868
    p->re_match_count++;
 
869
    retval = krb5_dbe_lookup_mkvno(util_context, ent, &old_mkvno);
 
870
    if (retval) {
 
871
        com_err(progname, retval,
 
872
                "determining master key used for principal '%s'",
 
873
                pname);
 
874
        goto fail;
 
875
    }
 
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);
 
880
        else if (p->verbose)
 
881
            printf("skipping: %s\n", pname);
 
882
        p->already_current++;
 
883
        goto skip;
 
884
    }
 
885
    if (p->dry_run) {
 
886
        if (p->verbose)
 
887
            printf("would update: %s\n", pname);
 
888
        p->updated++;
 
889
        goto skip;
 
890
    } else if (p->verbose)
 
891
        printf("updating: %s\n", pname);
 
892
    retval = master_key_convert (util_context, ent);
 
893
    if (retval) {
 
894
        com_err(progname, retval,
 
895
                "error re-encrypting key for principal '%s'", pname);
 
896
        goto fail;
 
897
    }
 
898
    if ((retval = krb5_timeofday(util_context, &now))) {
 
899
        com_err(progname, retval, "while getting current time");
 
900
        goto fail;
 
901
    }
 
902
 
 
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);
 
907
        goto fail;
 
908
    }
 
909
 
 
910
    ent->mask |= KADM5_KEY_DATA;
 
911
 
 
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",
 
915
                pname);
 
916
        goto fail;
 
917
    }
 
918
    p->updated++;
 
919
skip:
 
920
    result = 0;
 
921
    goto egress;
 
922
fail:
 
923
    exit_status++;
 
924
    result = 1;
 
925
egress:
 
926
    if (pname)
 
927
        krb5_free_unparsed_name(util_context, pname);
 
928
    return result;
 
929
}
 
930
 
 
931
extern int are_you_sure (const char *, ...)
 
932
#if !defined(__cplusplus) && (__GNUC__ > 2)
 
933
    __attribute__((__format__(__printf__, 1, 2)))
 
934
#endif
 
935
    ;
 
936
 
 
937
int
 
938
are_you_sure (const char *format, ...)
 
939
{
 
940
    va_list va;
 
941
    char ansbuf[100];
 
942
 
 
943
    va_start(va, format);
 
944
    vprintf(format, va);
 
945
    va_end(va);
 
946
    printf("\n(type 'yes' to confirm)? ");
 
947
    fflush(stdout);
 
948
    if (fgets(ansbuf, sizeof(ansbuf), stdin) == NULL)
 
949
        return 0;
 
950
    if (strcmp(ansbuf, "yes\n"))
 
951
        return 0;
 
952
    return 1;
 
953
}
 
954
 
 
955
void
 
956
kdb5_update_princ_encryption(int argc, char *argv[])
 
957
{
 
958
    struct update_enc_mkvno data = { 0 };
 
959
    char *name_pattern = NULL;
 
960
    int force = 0;
 
961
    int optchar;
 
962
    krb5_error_code retval;
 
963
    krb5_actkvno_node *actkvno_list = 0;
 
964
    krb5_db_entry master_entry;
 
965
    int nentries = 1;
 
966
    krb5_boolean more = FALSE;
 
967
    char *mkey_fullname = 0;
 
968
#ifdef BSD_REGEXPS
 
969
    char *msg;
 
970
#endif
 
971
    char *regexp = NULL;
 
972
    krb5_keyblock *tmp_keyblock = NULL;
 
973
 
 
974
    while ((optchar = getopt(argc, argv, "fnv")) != -1) {
 
975
        switch (optchar) {
 
976
        case 'f':
 
977
            force = 1;
 
978
            break;
 
979
        case 'n':
 
980
            data.dry_run = 1;
 
981
            break;
 
982
        case 'v':
 
983
            data.verbose = 1;
 
984
            break;
 
985
        case '?':
 
986
        case ':':
 
987
        default:
 
988
            usage();
 
989
        }
 
990
    }
 
991
    if (argv[optind] != NULL) {
 
992
        name_pattern = argv[optind];
 
993
        if (argv[optind+1] != NULL)
 
994
            usage();
 
995
    }
 
996
 
 
997
    retval = krb5_unparse_name(util_context, master_princ, &mkey_fullname);
 
998
    if (retval) {
 
999
        com_err(progname, retval, "while formatting master principal name");
 
1000
        exit_status++;
 
1001
        goto cleanup;
 
1002
    }
 
1003
 
 
1004
    if (master_keylist == NULL) {
 
1005
        com_err(progname, retval, "master keylist not initialized");
 
1006
        exit_status++;
 
1007
        goto cleanup;
 
1008
    }
 
1009
 
 
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)
 
1013
        name_pattern = "*";
 
1014
    if (glob_to_regexp(name_pattern, "hi", &regexp) != 0) {
 
1015
        com_err(progname, ENOMEM,
 
1016
                "converting glob pattern '%s' to regular expression",
 
1017
                name_pattern);
 
1018
        exit_status++;
 
1019
        goto cleanup;
 
1020
    }
 
1021
 
 
1022
    if (
 
1023
#ifdef SOLARIS_REGEXPS
 
1024
        ((data.expbuf = compile(regexp, NULL, NULL)) == NULL)
 
1025
#endif
 
1026
#ifdef POSIX_REGEXPS
 
1027
        ((regcomp(&data.preg, regexp, REG_NOSUB)) != 0)
 
1028
#endif
 
1029
#ifdef BSD_REGEXPS
 
1030
        ((msg = (char *) re_comp(regexp)) != NULL)
 
1031
#endif
 
1032
        ) {
 
1033
        /* XXX syslog msg or regerr(regerrno) */
 
1034
        com_err(progname, 0, "error compiling converted regexp '%s'", regexp);
 
1035
        exit_status++;
 
1036
        goto cleanup;
 
1037
    }
 
1038
 
 
1039
    retval = krb5_db_get_principal(util_context, master_princ, &master_entry,
 
1040
                                   &nentries, &more);
 
1041
    if (retval != 0) {
 
1042
        com_err(progname, retval, "while getting master key principal %s",
 
1043
                mkey_fullname);
 
1044
        exit_status++;
 
1045
        goto cleanup;
 
1046
    }
 
1047
    if (nentries != 1) {
 
1048
        com_err(progname, 0,
 
1049
                "cannot find master key principal %s in database!",
 
1050
                mkey_fullname);
 
1051
        exit_status++;
 
1052
        goto cleanup;
 
1053
    }
 
1054
 
 
1055
    retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
 
1056
    if (retval != 0) {
 
1057
        com_err(progname, retval, "while looking up active kvno list");
 
1058
        exit_status++;
 
1059
        goto cleanup;
 
1060
    }
 
1061
 
 
1062
    /* Master key is always stored encrypted in the latest version of
 
1063
       itself.  */
 
1064
    new_mkvno = krb5_db_get_key_data_kvno(util_context,
 
1065
                                          master_entry.n_key_data,
 
1066
                                          master_entry.key_data);
 
1067
 
 
1068
    retval = krb5_dbe_find_mkey(util_context, master_keylist,
 
1069
                                &master_entry, &tmp_keyblock);
 
1070
    if (retval) {
 
1071
        com_err(progname, retval, "retrieving the most recent master key");
 
1072
        exit_status++;
 
1073
        goto cleanup;
 
1074
    }
 
1075
    new_master_keyblock = *tmp_keyblock;
 
1076
 
 
1077
    if (!force &&
 
1078
        !data.dry_run &&
 
1079
        !are_you_sure("Re-encrypt all keys not using master key vno %u?",
 
1080
                      new_mkvno)) {
 
1081
        printf("OK, doing nothing.\n");
 
1082
        exit_status++;
 
1083
        goto cleanup;
 
1084
    }
 
1085
    if (data.verbose) {
 
1086
        if (data.dry_run)
 
1087
            printf("Principals whose keys WOULD BE re-encrypted to master key vno %u:\n",
 
1088
                   new_mkvno);
 
1089
        else
 
1090
            printf("Principals whose keys are being re-encrypted to master key vno %u if necessary:\n",
 
1091
                   new_mkvno);
 
1092
    }
 
1093
 
 
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");
 
1100
        exit_status++;
 
1101
    }
 
1102
    (void) krb5_db_fini(util_context);
 
1103
    if (data.dry_run)
 
1104
        printf("%u principals processed: %u would be updated, %u already current\n",
 
1105
               data.re_match_count, data.updated, data.already_current);
 
1106
    else
 
1107
        printf("%u principals processed: %u updated, %u already current\n",
 
1108
               data.re_match_count, data.updated, data.already_current);
 
1109
 
 
1110
cleanup:
 
1111
    free(regexp);
 
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);
 
1116
}
 
1117
 
 
1118
struct kvnos_in_use {
 
1119
    krb5_kvno               kvno;
 
1120
    unsigned int            use_count;
 
1121
};
 
1122
 
 
1123
struct purge_args {
 
1124
    krb5_context         kcontext;
 
1125
    struct kvnos_in_use  *kvnos;
 
1126
    unsigned int         num_kvnos;
 
1127
};
 
1128
 
 
1129
static krb5_error_code
 
1130
find_mkvnos_in_use(krb5_pointer   ptr,
 
1131
                   krb5_db_entry *entry)
 
1132
{
 
1133
    krb5_error_code retval;
 
1134
    struct purge_args * args;
 
1135
    unsigned int i;
 
1136
    krb5_kvno mkvno;
 
1137
 
 
1138
    args = (struct purge_args *) ptr;
 
1139
 
 
1140
    retval = krb5_dbe_lookup_mkvno(args->kcontext, entry, &mkvno);
 
1141
    if (retval)
 
1142
        return (retval);
 
1143
 
 
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++;
 
1148
            break;
 
1149
        }
 
1150
    }
 
1151
    return 0;
 
1152
}
 
1153
 
 
1154
void
 
1155
kdb5_purge_mkeys(int argc, char *argv[])
 
1156
{
 
1157
    int optchar;
 
1158
    krb5_error_code retval;
 
1159
    char  *mkey_fullname = NULL;
 
1160
    krb5_timestamp now;
 
1161
    krb5_db_entry master_entry;
 
1162
    int   nentries = 0;
 
1163
    krb5_boolean more = FALSE;
 
1164
    krb5_boolean force = FALSE, dry_run = FALSE, verbose = FALSE;
 
1165
    struct purge_args args;
 
1166
    char buf[5];
 
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;
 
1172
 
 
1173
    memset(&master_princ, 0, sizeof(master_princ));
 
1174
    memset(&args, 0, sizeof(args));
 
1175
 
 
1176
    optind = 1;
 
1177
    while ((optchar = getopt(argc, argv, "fnv")) != -1) {
 
1178
        switch(optchar) {
 
1179
        case 'f':
 
1180
            force = TRUE;
 
1181
            break;
 
1182
        case 'n':
 
1183
            dry_run = TRUE; /* mkey princ will not be modified */
 
1184
            force = TRUE; /* implied */
 
1185
            break;
 
1186
        case 'v':
 
1187
            verbose = TRUE;
 
1188
            break;
 
1189
        case '?':
 
1190
        default:
 
1191
            usage();
 
1192
            return;
 
1193
        }
 
1194
    }
 
1195
 
 
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");
 
1202
        exit_status++;
 
1203
        return;
 
1204
    }
 
1205
 
 
1206
    retval = krb5_db_get_principal(util_context, master_princ, &master_entry,
 
1207
                                   &nentries, &more);
 
1208
    if (retval != 0) {
 
1209
        com_err(progname, retval,
 
1210
                "while getting master key principal %s",
 
1211
                mkey_fullname);
 
1212
        exit_status++;
 
1213
        goto cleanup_return;
 
1214
    } else if (nentries == 0) {
 
1215
        com_err(progname, KRB5_KDB_NOENTRY,
 
1216
                "principal %s not found in Kerberos database",
 
1217
                mkey_fullname);
 
1218
        exit_status++;
 
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",
 
1223
                mkey_fullname);
 
1224
        exit_status++;
 
1225
        goto cleanup_return;
 
1226
    }
 
1227
 
 
1228
    if (!force) {
 
1229
        printf("Will purge all unused master keys stored in the '%s' principal, are you sure?\n",
 
1230
               mkey_fullname);
 
1231
        printf("(type 'yes' to confirm)? ");
 
1232
        if (fgets(buf, sizeof(buf), stdin) == NULL) {
 
1233
            exit_status++;
 
1234
            goto cleanup_return;
 
1235
        }
 
1236
        if (strcmp(buf, "yes\n")) {
 
1237
            exit_status++;
 
1238
            goto cleanup_return;
 
1239
        }
 
1240
        printf("OK, purging unused master keys from '%s'...\n", mkey_fullname);
 
1241
    }
 
1242
 
 
1243
    /* save the old keydata */
 
1244
    old_key_data_count = master_entry.n_key_data;
 
1245
    if (old_key_data_count == 1) {
 
1246
        if (verbose)
 
1247
            printf("There is only one master key which can not be purged.\n");
 
1248
        goto cleanup_return;
 
1249
    }
 
1250
    old_key_data = master_entry.key_data;
 
1251
 
 
1252
    args.kvnos = (struct kvnos_in_use *) malloc(sizeof(struct kvnos_in_use) * old_key_data_count);
 
1253
    if (args.kvnos == NULL) {
 
1254
        retval = ENOMEM;
 
1255
        com_err(progname, ENOMEM, "while allocating args.kvnos");
 
1256
        exit_status++;
 
1257
        goto cleanup_return;
 
1258
    }
 
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;
 
1262
 
 
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;
 
1266
 
 
1267
    if ((retval = krb5_db_iterate(util_context,
 
1268
                                  NULL,
 
1269
                                  find_mkvnos_in_use,
 
1270
                                  (krb5_pointer) &args))) {
 
1271
        com_err(progname, retval, "while finding master keys in use");
 
1272
        exit_status++;
 
1273
        goto cleanup_return;
 
1274
    }
 
1275
    /*
 
1276
     * args.kvnos has been marked with the mkvno's that are currently protecting
 
1277
     * princ entries
 
1278
     */
 
1279
    if (dry_run)
 
1280
        printf("Would purge the follwing master key(s) from %s:\n", mkey_fullname);
 
1281
    else
 
1282
        printf("Purging the follwing master key(s) from %s:\n", mkey_fullname);
 
1283
 
 
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) {
 
1287
            num_kvnos_inuse++;
 
1288
        } else {
 
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");
 
1293
                exit_status++;
 
1294
                goto cleanup_return;
 
1295
            }
 
1296
            num_kvnos_purged++;
 
1297
            printf("KNVO: %d\n", args.kvnos[i].kvno);
 
1298
        }
 
1299
    }
 
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;
 
1304
    }
 
1305
    if (dry_run) {
 
1306
        /* bail before doing anything else */
 
1307
        printf("%d key(s) would be purged.\n", num_kvnos_purged);
 
1308
        goto cleanup_return;
 
1309
    }
 
1310
 
 
1311
    retval = krb5_dbe_lookup_actkvno(util_context, &master_entry, &actkvno_list);
 
1312
    if (retval != 0) {
 
1313
        com_err(progname, retval, "while looking up active kvno list");
 
1314
        exit_status++;
 
1315
        goto cleanup_return;
 
1316
    }
 
1317
 
 
1318
    retval = krb5_dbe_lookup_mkey_aux(util_context, &master_entry, &mkey_aux_list);
 
1319
    if (retval != 0) {
 
1320
        com_err(progname, retval, "while looking up mkey aux data list");
 
1321
        exit_status++;
 
1322
        goto cleanup_return;
 
1323
    }
 
1324
 
 
1325
    master_entry.key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * num_kvnos_inuse);
 
1326
    if (master_entry.key_data == NULL) {
 
1327
        retval = ENOMEM;
 
1328
        com_err(progname, ENOMEM, "while allocating key_data");
 
1329
        exit_status++;
 
1330
        goto cleanup_return;
 
1331
    }
 
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 */
 
1334
 
 
1335
    /*
 
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.
 
1338
     */
 
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];
 
1344
                    break;
 
1345
                } else {
 
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) {
 
1351
 
 
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;
 
1360
                            } else {
 
1361
                                /* remove in between */
 
1362
                                prev_actkvno_entry->next = actkvno_entry->next;
 
1363
                            }
 
1364
                            actkvno_entry->next = NULL;
 
1365
                            krb5_dbe_free_actkvno_list(util_context, actkvno_entry);
 
1366
                            break; /* deleted entry, no need to loop further */
 
1367
                        } else {
 
1368
                            prev_actkvno_entry = actkvno_entry;
 
1369
                        }
 
1370
                    }
 
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) {
 
1375
 
 
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;
 
1382
                            } else {
 
1383
                                prev_mkey_aux_entry->next = mkey_aux_entry->next;
 
1384
                            }
 
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 */
 
1388
                        } else {
 
1389
                            prev_mkey_aux_entry = mkey_aux_entry;
 
1390
                        }
 
1391
                    }
 
1392
                }
 
1393
            }
 
1394
        }
 
1395
    }
 
1396
    assert(k == num_kvnos_inuse);
 
1397
 
 
1398
    if ((retval = krb5_dbe_update_actkvno(util_context, &master_entry,
 
1399
                                          actkvno_list))) {
 
1400
        com_err(progname, retval,
 
1401
                "while updating actkvno data for master principal entry");
 
1402
        exit_status++;
 
1403
        goto cleanup_return;
 
1404
    }
 
1405
 
 
1406
    if ((retval = krb5_dbe_update_mkey_aux(util_context, &master_entry,
 
1407
                                           mkey_aux_list))) {
 
1408
        com_err(progname, retval,
 
1409
                "while updating mkey_aux data for master principal entry");
 
1410
        exit_status++;
 
1411
        return;
 
1412
    }
 
1413
 
 
1414
    if ((retval = krb5_timeofday(util_context, &now))) {
 
1415
        com_err(progname, retval, "while getting current time");
 
1416
        exit_status++;
 
1417
        goto cleanup_return;
 
1418
    }
 
1419
 
 
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");
 
1424
        exit_status++;
 
1425
        goto cleanup_return;
 
1426
    }
 
1427
 
 
1428
    master_entry.mask |= KADM5_KEY_DATA;
 
1429
 
 
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");
 
1433
        exit_status++;
 
1434
        goto cleanup_return;
 
1435
    }
 
1436
    printf("%d key(s) purged.\n", num_kvnos_purged);
 
1437
 
 
1438
cleanup_return:
 
1439
    /* clean up */
 
1440
    (void) krb5_db_fini(util_context);
 
1441
    krb5_free_principal(util_context, master_princ);
 
1442
    free(args.kvnos);
 
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);
 
1446
    return;
 
1447
}