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