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