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