1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kadmin/dbutil/dump.c - Dump a KDC database */
3 /*
4  * Copyright 1990,1991,2001,2006,2008,2009,2013 by the Massachusetts Institute
5  * of Technology.  All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 /*
27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #include <k5-int.h>
32 #include <kadm5/admin.h>
33 #include <kadm5/server_internal.h>
34 #include <kdb.h>
35 #include <com_err.h>
36 #include "kdb5_util.h"
37 #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
38 #include <regex.h>
39 #endif  /* HAVE_REGEX_H */
40 
41 /* Needed for master key conversion. */
42 static krb5_boolean mkey_convert;
43 krb5_keyblock new_master_keyblock;
44 krb5_kvno new_mkvno;
45 
46 #define K5Q1(x) #x
47 #define K5Q(x) K5Q1(x)
48 #define K5CONST_WIDTH_SCANF_STR(x) "%" K5Q(x) "s"
49 
50 /* Use compile(3) if no regcomp present. */
51 #if !defined(HAVE_REGCOMP) && defined(HAVE_REGEXP_H)
52 #define INIT char *sp = instring;
53 #define GETC() (*sp++)
54 #define PEEKC() (*sp)
55 #define UNGETC(c) (--sp)
56 #define RETURN(c) return(c)
57 #define ERROR(c)
58 #define RE_BUF_SIZE 1024
59 #include <regexp.h>
60 #endif /* !HAVE_REGCOMP && HAVE_REGEXP_H */
61 
62 typedef krb5_error_code (*dump_func)(krb5_context context,
63                                      krb5_db_entry *entry, const char *name,
64                                      FILE *fp, krb5_boolean verbose,
65                                      krb5_boolean omit_nra);
66 typedef int (*load_func)(krb5_context context, const char *dumpfile, FILE *fp,
67                          krb5_boolean verbose, int *linenop);
68 
69 typedef struct _dump_version {
70     char *name;
71     char *header;
72     int updateonly;
73     int iprop;
74     int ipropx;
75     dump_func dump_princ;
76     osa_adb_iter_policy_func dump_policy;
77     load_func load_record;
78 } dump_version;
79 
80 struct dump_args {
81     FILE *ofile;
82     krb5_context context;
83     char **names;
84     int nnames;
85     krb5_boolean verbose;
86     krb5_boolean omit_nra;      /* omit non-replicated attributes */
87     dump_version *dump;
88 };
89 
90 /* External data */
91 extern krb5_db_entry *master_entry;
92 
93 /*
94  * Re-encrypt the key_data with the new master key...
95  */
96 krb5_error_code
master_key_convert(krb5_context context,krb5_db_entry * db_entry)97 master_key_convert(krb5_context context, krb5_db_entry *db_entry)
98 {
99     krb5_error_code retval;
100     krb5_keyblock v5plainkey, *key_ptr, *tmp_mkey;
101     krb5_keysalt keysalt;
102     krb5_key_data new_key_data, *key_data;
103     krb5_boolean is_mkey;
104     krb5_kvno kvno;
105     int i, j;
106 
107     is_mkey = krb5_principal_compare(context, master_princ, db_entry->princ);
108 
109     if (is_mkey) {
110         return add_new_mkey(context, db_entry, &new_master_keyblock,
111                             new_mkvno);
112     }
113 
114     for (i = 0; i < db_entry->n_key_data; i++) {
115         key_data = &db_entry->key_data[i];
116         retval = krb5_dbe_find_mkey(context, db_entry, &tmp_mkey);
117         if (retval)
118             return retval;
119         retval = krb5_dbe_decrypt_key_data(context, tmp_mkey, key_data,
120                                            &v5plainkey, &keysalt);
121         if (retval)
122             return retval;
123 
124         memset(&new_key_data, 0, sizeof(new_key_data));
125 
126         key_ptr = &v5plainkey;
127         kvno = key_data->key_data_kvno;
128 
129         retval = krb5_dbe_encrypt_key_data(context, &new_master_keyblock,
130                                            key_ptr, &keysalt, kvno,
131                                            &new_key_data);
132         if (retval)
133             return retval;
134         krb5_free_keyblock_contents(context, &v5plainkey);
135         for (j = 0; j < key_data->key_data_ver; j++) {
136             if (key_data->key_data_length[j])
137                 free(key_data->key_data_contents[j]);
138         }
139         *key_data = new_key_data;
140     }
141     assert(new_mkvno > 0);
142     return krb5_dbe_update_mkvno(context, db_entry, new_mkvno);
143 }
144 
145 /* Create temp file for new dump to be named ofile. */
146 static FILE *
create_ofile(char * ofile,char ** tmpname)147 create_ofile(char *ofile, char **tmpname)
148 {
149     int fd = -1;
150     FILE *f;
151 
152     *tmpname = NULL;
153     if (asprintf(tmpname, "%s-XXXXXX", ofile) < 0)
154         goto error;
155 
156     fd = mkstemp(*tmpname);
157     if (fd == -1)
158         goto error;
159 
160     f = fdopen(fd, "w+");
161     if (f != NULL)
162         return f;
163 
164 error:
165     com_err(progname, errno, _("while allocating temporary filename dump"));
166     if (fd >= 0)
167         unlink(*tmpname);
168     exit(1);
169 }
170 
171 /* Rename new dump file into place. */
172 static void
finish_ofile(char * ofile,char ** tmpname)173 finish_ofile(char *ofile, char **tmpname)
174 {
175     if (rename(*tmpname, ofile) == -1) {
176         com_err(progname, errno, _("while renaming dump file into place"));
177         exit(1);
178     }
179     free(*tmpname);
180     *tmpname = NULL;
181 }
182 
183 /* Create the .dump_ok file. */
184 static krb5_boolean
prep_ok_file(krb5_context context,char * file_name,int * fd_out)185 prep_ok_file(krb5_context context, char *file_name, int *fd_out)
186 {
187     static char ok[] = ".dump_ok";
188     krb5_error_code retval;
189     char *file_ok = NULL;
190     int fd = -1;
191     krb5_boolean success = FALSE;
192 
193     *fd_out = -1;
194 
195     if (asprintf(&file_ok, "%s%s", file_name, ok) < 0) {
196         com_err(progname, ENOMEM, _("while allocating dump_ok filename"));
197         goto cleanup;
198     }
199 
200     fd = open(file_ok, O_WRONLY | O_CREAT | O_TRUNC, 0600);
201     if (fd == -1) {
202         com_err(progname, errno, _("while creating 'ok' file, '%s'"), file_ok);
203         goto cleanup;
204     }
205     retval = krb5_lock_file(context, fd, KRB5_LOCKMODE_EXCLUSIVE);
206     if (retval) {
207         com_err(progname, retval, _("while locking 'ok' file, '%s'"), file_ok);
208         goto cleanup;
209     }
210 
211     *fd_out = fd;
212     fd = -1;
213     success = TRUE;
214 
215 cleanup:
216     free(file_ok);
217     if (fd != -1)
218         close(fd);
219     if (!success)
220         exit_status++;
221     return success;
222 }
223 
224 /*
225  * Update the "ok" file.
226  */
227 static void
update_ok_file(krb5_context context,int fd)228 update_ok_file(krb5_context context, int fd)
229 {
230     write(fd, "", 1);
231     krb5_lock_file(context, fd, KRB5_LOCKMODE_UNLOCK);
232     close(fd);
233 }
234 
235 /* Return true if a principal name matches a regular expression or string. */
236 static int
name_matches(char * name,struct dump_args * args)237 name_matches(char *name, struct dump_args *args)
238 {
239 #if HAVE_REGCOMP
240     regex_t reg;
241     regmatch_t rmatch;
242     int st;
243     char errmsg[BUFSIZ];
244 #elif   HAVE_REGEXP_H
245     char regexp_buffer[RE_BUF_SIZE];
246 #elif   HAVE_RE_COMP
247     extern char *re_comp();
248     char *re_result;
249 #endif  /* HAVE_RE_COMP */
250     int i, match;
251 
252     /* Check each regular expression in args. */
253     match = args->nnames ? 0 : 1;
254     for (i = 0; i < args->nnames && !match; i++) {
255 #if HAVE_REGCOMP
256         /* Compile the regular expression. */
257         st = regcomp(&reg, args->names[i], REG_EXTENDED);
258         if (st) {
259             regerror(st, &reg, errmsg, sizeof(errmsg));
260             fprintf(stderr, _("%s: regular expression error: %s\n"), progname,
261                     errmsg);
262             break;
263         }
264         /* See if we have a match. */
265         st = regexec(&reg, name, 1, &rmatch, 0);
266         if (st == 0) {
267             /* See if it matches the whole name. */
268             if (rmatch.rm_so == 0 && (size_t)rmatch.rm_eo == strlen(name))
269                 match = 1;
270         } else if (st != REG_NOMATCH) {
271             regerror(st, &reg, errmsg, sizeof(errmsg));
272             fprintf(stderr, _("%s: regular expression match error: %s\n"),
273                     progname, errmsg);
274             break;
275         }
276         regfree(&reg);
277 #elif HAVE_REGEXP_H
278         /* Compile the regular expression. */
279         compile(args->names[i], regexp_buffer, &regexp_buffer[RE_BUF_SIZE],
280                 '\0');
281         if (step(name, regexp_buffer)) {
282             if (loc1 == name && loc2 == &name[strlen(name)])
283                 match = 1;
284         }
285 #elif HAVE_RE_COMP
286         /* Compile the regular expression. */
287         re_result = re_comp(args->names[i]);
288         if (re_result) {
289             fprintf(stderr, _("%s: regular expression error: %s\n"), progname,
290                     re_result);
291             break;
292         }
293         if (re_exec(name))
294             match = 1;
295 #else /* HAVE_RE_COMP */
296         /* If no regular expression support, then just compare the strings. */
297         if (!strcmp(args->names[i], name))
298             match = 1;
299 #endif /* HAVE_REGCOMP */
300     }
301     return match;
302 }
303 
304 /* Output "-1" if len is 0; otherwise output len bytes of data in hex. */
305 static void
dump_octets_or_minus1(FILE * fp,unsigned char * data,size_t len)306 dump_octets_or_minus1(FILE *fp, unsigned char *data, size_t len)
307 {
308     if (len > 0) {
309         for (; len > 0; len--)
310             fprintf(fp, "%02x", *data++);
311     } else {
312         fprintf(fp, "-1");
313     }
314 }
315 
316 /*
317  * Dump TL data; common to principals and policies.
318  *
319  * If filter_kadm then the KRB5_TL_KADM_DATA (where a principal's policy
320  * name is stored) is filtered out.  This is for dump formats that don't
321  * support policies.
322  */
323 static void
dump_tl_data(FILE * ofile,krb5_tl_data * tlp,krb5_boolean filter_kadm)324 dump_tl_data(FILE *ofile, krb5_tl_data *tlp, krb5_boolean filter_kadm)
325 {
326     for (; tlp != NULL; tlp = tlp->tl_data_next) {
327         if (tlp->tl_data_type == KRB5_TL_KADM_DATA && filter_kadm)
328             continue;
329         fprintf(ofile, "\t%d\t%d\t", (int)tlp->tl_data_type,
330                 (int)tlp->tl_data_length);
331         dump_octets_or_minus1(ofile, tlp->tl_data_contents,
332                               tlp->tl_data_length);
333     }
334 }
335 
336 /* Dump a principal entry in krb5 beta 7 format.  Omit kadmin tl-data if kadm
337  * is false. */
338 static krb5_error_code
k5beta7_common(krb5_context context,krb5_db_entry * entry,const char * name,FILE * fp,krb5_boolean verbose,krb5_boolean omit_nra,krb5_boolean kadm)339 k5beta7_common(krb5_context context, krb5_db_entry *entry,
340                const char *name, FILE *fp, krb5_boolean verbose,
341                krb5_boolean omit_nra, krb5_boolean kadm)
342 {
343     krb5_tl_data *tlp;
344     krb5_key_data *kdata;
345     int counter, skip, i;
346 
347     /*
348      * The dump format is as follows:
349      *      len strlen(name) n_tl_data n_key_data e_length
350      *      name
351      *      attributes max_life max_renewable_life expiration
352      *      pw_expiration last_success last_failed fail_auth_count
353      *      n_tl_data*[type length <contents>]
354      *      n_key_data*[ver kvno ver*(type length <contents>)]
355      *      <e_data>
356      * Fields which are not encapsulated by angle-brackets are to appear
357      * verbatim.  A bracketed field's absence is indicated by a -1 in its
358      * place.
359      */
360 
361     /* Make sure that the tagged list is reasonably correct. */
362     counter = skip = 0;
363     for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) {
364         /* Don't dump tl data types we know aren't understood by earlier
365          * versions. */
366         if (tlp->tl_data_type == KRB5_TL_KADM_DATA && !kadm)
367             skip++;
368         else
369             counter++;
370     }
371 
372     if (counter + skip != entry->n_tl_data) {
373         fprintf(stderr, _("%s: tagged data list inconsistency for %s "
374                           "(counted %d, stored %d)\n"), progname, name,
375                 counter + skip, (int)entry->n_tl_data);
376         return EINVAL;
377     }
378 
379     /* Write out header. */
380     fprintf(fp, "princ\t%d\t%lu\t%d\t%d\t%d\t%s\t", (int)entry->len,
381             (unsigned long)strlen(name), counter, (int)entry->n_key_data,
382             (int)entry->e_length, name);
383     fprintf(fp, "%d\t%d\t%d\t%u\t%u\t%u\t%u\t%d", entry->attributes,
384             entry->max_life, entry->max_renewable_life,
385             (unsigned int)entry->expiration,
386             (unsigned int)entry->pw_expiration,
387             (unsigned int)(omit_nra ? 0 : entry->last_success),
388             (unsigned int)(omit_nra ? 0 : entry->last_failed),
389             omit_nra ? 0 : entry->fail_auth_count);
390 
391     /* Write out tagged data. */
392     dump_tl_data(fp, entry->tl_data, !kadm);
393     fprintf(fp, "\t");
394 
395     /* Write out key data. */
396     for (counter = 0; counter < entry->n_key_data; counter++) {
397         kdata = &entry->key_data[counter];
398         fprintf(fp, "%d\t%d\t", (int)kdata->key_data_ver,
399                 (int)kdata->key_data_kvno);
400         for (i = 0; i < kdata->key_data_ver; i++) {
401             fprintf(fp, "%d\t%d\t", kdata->key_data_type[i],
402                     kdata->key_data_length[i]);
403             dump_octets_or_minus1(fp, kdata->key_data_contents[i],
404                                   kdata->key_data_length[i]);
405             fprintf(fp, "\t");
406         }
407     }
408 
409     /* Write out extra data. */
410     dump_octets_or_minus1(fp, entry->e_data, entry->e_length);
411 
412     /* Write trailer. */
413     fprintf(fp, ";\n");
414 
415     if (verbose)
416         fprintf(stderr, "%s\n", name);
417 
418     return 0;
419 }
420 
421 /* Output a dump record in krb5b7 format. */
422 static krb5_error_code
dump_k5beta7_princ(krb5_context context,krb5_db_entry * entry,const char * name,FILE * fp,krb5_boolean verbose,krb5_boolean omit_nra)423 dump_k5beta7_princ(krb5_context context, krb5_db_entry *entry,
424                    const char *name, FILE *fp, krb5_boolean verbose,
425                    krb5_boolean omit_nra)
426 {
427     return k5beta7_common(context, entry, name, fp, verbose, omit_nra, FALSE);
428 }
429 
430 static krb5_error_code
dump_k5beta7_princ_withpolicy(krb5_context context,krb5_db_entry * entry,const char * name,FILE * fp,krb5_boolean verbose,krb5_boolean omit_nra)431 dump_k5beta7_princ_withpolicy(krb5_context context, krb5_db_entry *entry,
432                               const char *name, FILE *fp, krb5_boolean verbose,
433                               krb5_boolean omit_nra)
434 {
435     return k5beta7_common(context, entry, name, fp, verbose, omit_nra, TRUE);
436 }
437 
438 static void
dump_k5beta7_policy(void * data,osa_policy_ent_t entry)439 dump_k5beta7_policy(void *data, osa_policy_ent_t entry)
440 {
441     struct dump_args *arg = data;
442 
443     fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\n", entry->name,
444             entry->pw_min_life, entry->pw_max_life, entry->pw_min_length,
445             entry->pw_min_classes, entry->pw_history_num, 0);
446 }
447 
448 static void
dump_r1_8_policy(void * data,osa_policy_ent_t entry)449 dump_r1_8_policy(void *data, osa_policy_ent_t entry)
450 {
451     struct dump_args *arg = data;
452 
453     fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
454             entry->name, entry->pw_min_life, entry->pw_max_life,
455             entry->pw_min_length, entry->pw_min_classes, entry->pw_history_num,
456             0, entry->pw_max_fail, entry->pw_failcnt_interval,
457             entry->pw_lockout_duration);
458 }
459 
460 static void
dump_r1_11_policy(void * data,osa_policy_ent_t entry)461 dump_r1_11_policy(void *data, osa_policy_ent_t entry)
462 {
463     struct dump_args *arg = data;
464 
465     fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t"
466             "%d\t%d\t%d\t%s\t%d", entry->name, entry->pw_min_life,
467             entry->pw_max_life, entry->pw_min_length, entry->pw_min_classes,
468             entry->pw_history_num, 0, entry->pw_max_fail,
469             entry->pw_failcnt_interval, entry->pw_lockout_duration,
470             entry->attributes, entry->max_life, entry->max_renewable_life,
471             entry->allowed_keysalts ? entry->allowed_keysalts : "-",
472             entry->n_tl_data);
473 
474     dump_tl_data(arg->ofile, entry->tl_data, FALSE);
475     fprintf(arg->ofile, "\n");
476 }
477 
478 static krb5_error_code
dump_iterator(void * ptr,krb5_db_entry * entry)479 dump_iterator(void *ptr, krb5_db_entry *entry)
480 {
481     krb5_error_code ret;
482     struct dump_args *args = ptr;
483     char *name;
484 
485     ret = krb5_unparse_name(args->context, entry->princ, &name);
486     if (ret) {
487         com_err(progname, ret, _("while unparsing principal name"));
488         return ret;
489     }
490 
491     /* Re-encode the keys in the new master key, if necessary. */
492     if (mkey_convert) {
493         ret = master_key_convert(args->context, entry);
494         if (ret) {
495             com_err(progname, ret, _("while converting %s to new master key"),
496                     name);
497             goto cleanup;
498         }
499     }
500 
501     /* Don't dump this entry if we have match strings and it doesn't match. */
502     if (args->nnames > 0 && !name_matches(name, args))
503         goto cleanup;
504 
505     ret = args->dump->dump_princ(args->context, entry, name, args->ofile,
506                                  args->verbose, args->omit_nra);
507 
508 cleanup:
509     free(name);
510     return ret;
511 }
512 
513 static inline void
load_err(const char * fname,int lineno,const char * msg)514 load_err(const char *fname, int lineno, const char *msg)
515 {
516     fprintf(stderr, _("%s(%d): %s\n"), fname, lineno, msg);
517 }
518 
519 /* Read a string of bytes.  Increment *lp for each newline.  Return 0 on
520  * success, 1 on failure. */
521 static int
read_string(FILE * f,char * buf,int len,int * lp)522 read_string(FILE *f, char *buf, int len, int *lp)
523 {
524     int c, i;
525 
526     for (i = 0; i < len; i++) {
527         c = fgetc(f);
528         if (c < 0)
529             return 1;
530         if (c == '\n')
531             (*lp)++;
532         buf[i] = c;
533     }
534     buf[len] = '\0';
535     return 0;
536 }
537 
538 /* Read a string of two-character representations of bytes. */
539 static int
read_octet_string(FILE * f,unsigned char * buf,int len)540 read_octet_string(FILE *f, unsigned char *buf, int len)
541 {
542     int c, i;
543 
544     for (i = 0; i < len; i++) {
545         if (fscanf(f, "%02x", &c) != 1)
546             return 1;
547         buf[i] = c;
548     }
549     return 0;
550 }
551 
552 /* Read the end of a dumpfile record. */
553 static void
read_record_end(FILE * f,const char * fn,int lineno)554 read_record_end(FILE *f, const char *fn, int lineno)
555 {
556     int ch;
557 
558     if ((ch = fgetc(f)) != ';' || (ch = fgetc(f)) != '\n') {
559         fprintf(stderr, _("%s(%d): ignoring trash at end of line: "), fn,
560                 lineno);
561         while (ch != '\n') {
562             putc(ch, stderr);
563             ch = fgetc(f);
564         }
565         putc(ch, stderr);
566     }
567 }
568 
569 /* Allocate and form a TL data list of a desired size. */
570 static int
alloc_tl_data(krb5_int16 n_tl_data,krb5_tl_data ** tldp)571 alloc_tl_data(krb5_int16 n_tl_data, krb5_tl_data **tldp)
572 {
573     krb5_tl_data **tlp = tldp;
574     int i;
575 
576     for (i = 0; i < n_tl_data; i++) {
577         *tlp = calloc(1, sizeof(krb5_tl_data));
578         if (*tlp == NULL)
579             return ENOMEM; /* caller cleans up */
580         tlp = &((*tlp)->tl_data_next);
581     }
582 
583     return 0;
584 }
585 
586 /* If len is zero, read the string "-1" from fp.  Otherwise allocate space and
587  * read len octets.  Return 0 on success, 1 on failure. */
588 static int
read_octets_or_minus1(FILE * fp,size_t len,unsigned char ** out)589 read_octets_or_minus1(FILE *fp, size_t len, unsigned char **out)
590 {
591     int ival;
592     unsigned char *buf;
593 
594     *out = NULL;
595     if (len == 0)
596         return fscanf(fp, "%d", &ival) != 1 || ival != -1;
597     buf = malloc(len);
598     if (buf == NULL)
599         return 1;
600     if (read_octet_string(fp, buf, len)) {
601         free(buf);
602         return 1;
603     }
604     *out = buf;
605     return 0;
606 }
607 
608 /* Read TL data for a principal or policy.  Print an error and return -1 on
609  * failure. */
610 static int
process_tl_data(const char * fname,FILE * filep,int lineno,krb5_tl_data * tl_data)611 process_tl_data(const char *fname, FILE *filep, int lineno,
612                 krb5_tl_data *tl_data)
613 {
614     krb5_tl_data *tl;
615     int nread, i1;
616     unsigned int u1;
617 
618     for (tl = tl_data; tl; tl = tl->tl_data_next) {
619         nread = fscanf(filep, "%d\t%u\t", &i1, &u1);
620         if (nread != 2) {
621             load_err(fname, lineno,
622                      _("cannot read tagged data type and length"));
623             return EINVAL;
624         }
625         if (i1 < INT16_MIN || i1 > INT16_MAX || u1 > UINT16_MAX) {
626             load_err(fname, lineno, _("data type or length overflowed"));
627             return EINVAL;
628         }
629         tl->tl_data_type = i1;
630         tl->tl_data_length = u1;
631         if (read_octets_or_minus1(filep, tl->tl_data_length,
632                                   &tl->tl_data_contents)) {
633             load_err(fname, lineno, _("cannot read tagged data contents"));
634             return EINVAL;
635         }
636     }
637 
638     return 0;
639 }
640 
641 /* Read a beta 7 entry and add it to the database.  Return -1 for end of file,
642  * 0 for success and 1 for failure. */
643 static int
process_k5beta7_princ(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)644 process_k5beta7_princ(krb5_context context, const char *fname, FILE *filep,
645                       krb5_boolean verbose, int *linenop)
646 {
647     int retval, nread, i, j;
648     krb5_db_entry *dbentry;
649     int t1, t2, t3, t4;
650     unsigned int u1, u2, u3, u4, u5;
651     char *name = NULL;
652     krb5_key_data *kp = NULL, *kd;
653     krb5_tl_data *tl;
654     krb5_error_code ret;
655 
656     dbentry = calloc(1, sizeof(*dbentry));
657     if (dbentry == NULL)
658         return 1;
659     (*linenop)++;
660     nread = fscanf(filep, "%u\t%u\t%u\t%u\t%u\t", &u1, &u2, &u3, &u4, &u5);
661     if (nread == EOF) {
662         retval = -1;
663         goto cleanup;
664     }
665     if (nread != 5) {
666         load_err(fname, *linenop, _("cannot match size tokens"));
667         goto fail;
668     }
669 
670     /* Get memory for flattened principal name */
671     name = malloc(u2 + 1);
672     if (name == NULL)
673         goto fail;
674 
675     /* Get memory for and form tagged data linked list */
676     if (u3 > UINT16_MAX) {
677         load_err(fname, *linenop, _("cannot allocate tl_data (too large)"));
678         goto fail;
679     }
680     if (alloc_tl_data(u3, &dbentry->tl_data))
681         goto fail;
682     dbentry->n_tl_data = u3;
683 
684     /* Get memory for key list */
685     if (u4 && (kp = calloc(u4, sizeof(krb5_key_data))) == NULL)
686         goto fail;
687 
688     dbentry->len = u1;
689     dbentry->n_key_data = u4;
690     dbentry->e_length = u5;
691 
692     if (kp != NULL) {
693         dbentry->key_data = kp;
694         kp = NULL;
695     }
696 
697     /* Read in and parse the principal name */
698     if (read_string(filep, name, u2, linenop)) {
699         load_err(fname, *linenop, _("cannot read name string"));
700         goto fail;
701     }
702     ret = krb5_parse_name(context, name, &dbentry->princ);
703     if (ret) {
704         com_err(progname, ret, _("while parsing name %s"), name);
705         goto fail;
706     }
707 
708     /* Get the fixed principal attributes */
709     nread = fscanf(filep, "%d\t%d\t%d\t%u\t%u\t%d\t%d\t%d\t",
710                    &t1, &t2, &t3, &u1, &u2, &u3, &u4, &u5);
711     if (nread != 8) {
712         load_err(fname, *linenop, _("cannot read principal attributes"));
713         goto fail;
714     }
715     dbentry->attributes = t1;
716     dbentry->max_life = t2;
717     dbentry->max_renewable_life = t3;
718     dbentry->expiration = u1;
719     dbentry->pw_expiration = u2;
720     dbentry->last_success = u3;
721     dbentry->last_failed = u4;
722     dbentry->fail_auth_count = u5;
723     dbentry->mask = KADM5_LOAD | KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
724         KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
725         KADM5_PRINC_EXPIRE_TIME | KADM5_PW_EXPIRATION | KADM5_LAST_SUCCESS |
726         KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT;
727 
728     /* Read tagged data. */
729     if (dbentry->n_tl_data) {
730         if (process_tl_data(fname, filep, *linenop, dbentry->tl_data))
731             goto fail;
732         for (tl = dbentry->tl_data; tl; tl = tl->tl_data_next) {
733             /* test to set mask fields */
734             if (tl->tl_data_type == KRB5_TL_KADM_DATA) {
735                 XDR xdrs;
736                 osa_princ_ent_rec osa_princ_ent;
737 
738                 /*
739                  * Assuming aux_attributes will always be
740                  * there
741                  */
742                 dbentry->mask |= KADM5_AUX_ATTRIBUTES;
743 
744                 /* test for an actual policy reference */
745                 memset(&osa_princ_ent, 0, sizeof(osa_princ_ent));
746                 xdrmem_create(&xdrs, (char *)tl->tl_data_contents,
747                               tl->tl_data_length, XDR_DECODE);
748                 if (xdr_osa_princ_ent_rec(&xdrs, &osa_princ_ent)) {
749                     if ((osa_princ_ent.aux_attributes & KADM5_POLICY) &&
750                         osa_princ_ent.policy != NULL)
751                         dbentry->mask |= KADM5_POLICY;
752                     kdb_free_entry(NULL, NULL, &osa_princ_ent);
753                 }
754                 xdr_destroy(&xdrs);
755             }
756         }
757         dbentry->mask |= KADM5_TL_DATA;
758     }
759 
760     /* Get the key data. */
761     for (i = 0; i < dbentry->n_key_data; i++) {
762         kd = &dbentry->key_data[i];
763         nread = fscanf(filep, "%d\t%d\t", &t1, &t2);
764         if (nread != 2) {
765             load_err(fname, *linenop, _("cannot read key size and version"));
766             goto fail;
767         }
768         if (t1 > KRB5_KDB_V1_KEY_DATA_ARRAY) {
769             load_err(fname, *linenop, _("unsupported key_data_ver version"));
770             goto fail;
771         }
772 
773         kd->key_data_ver = t1;
774         kd->key_data_kvno = t2;
775 
776         for (j = 0; j < t1; j++) {
777             nread = fscanf(filep, "%d\t%d\t", &t3, &t4);
778             if (nread != 2 || t4 < 0) {
779                 load_err(fname, *linenop,
780                          _("cannot read key type and length"));
781                 goto fail;
782             }
783             kd->key_data_type[j] = t3;
784             kd->key_data_length[j] = t4;
785             if (read_octets_or_minus1(filep, t4, &kd->key_data_contents[j])) {
786                 load_err(fname, *linenop, _("cannot read key data"));
787                 goto fail;
788             }
789         }
790     }
791     if (dbentry->n_key_data)
792         dbentry->mask |= KADM5_KEY_DATA;
793 
794     /* Get the extra data */
795     if (read_octets_or_minus1(filep, dbentry->e_length, &dbentry->e_data)) {
796         load_err(fname, *linenop, _("cannot read extra data"));
797         goto fail;
798     }
799 
800     /* Finally, find the end of the record. */
801     read_record_end(filep, fname, *linenop);
802 
803     ret = krb5_db_put_principal(context, dbentry);
804     if (ret) {
805         com_err(progname, ret, _("while storing %s"), name);
806         goto fail;
807     }
808 
809     if (verbose)
810         fprintf(stderr, "%s\n", name);
811     retval = 0;
812 
813 cleanup:
814     free(kp);
815     free(name);
816     krb5_db_free_principal(context, dbentry);
817     return retval;
818 
819 fail:
820     retval = 1;
821     goto cleanup;
822 }
823 
824 static int
process_k5beta7_policy(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)825 process_k5beta7_policy(krb5_context context, const char *fname, FILE *filep,
826                        krb5_boolean verbose, int *linenop)
827 {
828     osa_policy_ent_rec rec;
829     char namebuf[1024];
830     unsigned int refcnt;
831     int nread, ret;
832 
833     memset(&rec, 0, sizeof(rec));
834 
835     (*linenop)++;
836     rec.name = namebuf;
837 
838     nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u", rec.name,
839                    &rec.pw_min_life, &rec.pw_max_life, &rec.pw_min_length,
840                    &rec.pw_min_classes, &rec.pw_history_num, &refcnt);
841     if (nread == EOF)
842         return -1;
843     if (nread != 7) {
844         fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);
845         return 1;
846     }
847 
848     ret = krb5_db_create_policy(context, &rec);
849     if (ret)
850         ret = krb5_db_put_policy(context, &rec);
851     if (ret) {
852         com_err(progname, ret, _("while creating policy"));
853         return 1;
854     }
855     if (verbose)
856         fprintf(stderr, _("created policy %s\n"), rec.name);
857 
858     return 0;
859 }
860 
861 static int
process_r1_8_policy(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)862 process_r1_8_policy(krb5_context context, const char *fname, FILE *filep,
863                     krb5_boolean verbose, int *linenop)
864 {
865     osa_policy_ent_rec rec;
866     char namebuf[1024];
867     unsigned int refcnt;
868     int nread, ret;
869 
870     memset(&rec, 0, sizeof(rec));
871 
872     (*linenop)++;
873     rec.name = namebuf;
874 
875     nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u",
876                    rec.name, &rec.pw_min_life, &rec.pw_max_life,
877                    &rec.pw_min_length, &rec.pw_min_classes,
878                    &rec.pw_history_num, &refcnt, &rec.pw_max_fail,
879                    &rec.pw_failcnt_interval, &rec.pw_lockout_duration);
880     if (nread == EOF)
881         return -1;
882     if (nread != 10) {
883         fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);
884         return 1;
885     }
886 
887     ret = krb5_db_create_policy(context, &rec);
888     if (ret)
889         ret = krb5_db_put_policy(context, &rec);
890     if (ret) {
891         com_err(progname, ret, _("while creating policy"));
892         return 1;
893     }
894     if (verbose)
895         fprintf(stderr, "created policy %s\n", rec.name);
896 
897     return 0;
898 }
899 
900 static int
process_r1_11_policy(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)901 process_r1_11_policy(krb5_context context, const char *fname, FILE *filep,
902                      krb5_boolean verbose, int *linenop)
903 {
904     osa_policy_ent_rec rec;
905     krb5_tl_data *tl, *tl_next;
906     char namebuf[1024];
907     char keysaltbuf[KRB5_KDB_MAX_ALLOWED_KS_LEN + 1];
908     unsigned int refcnt;
909     int nread, c, ret = 0;
910 
911     memset(&rec, 0, sizeof(rec));
912 
913     (*linenop)++;
914     rec.name = namebuf;
915 
916     /*
917      * Due to a historical error, iprop dumps use the same version before and
918      * after the 1.11 policy extensions.  So we need to accept both 1.8-format
919      * and 1.11-format policy entries.  Begin by reading the 1.8 fields.
920      */
921     nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u",
922                    rec.name, &rec.pw_min_life, &rec.pw_max_life,
923                    &rec.pw_min_length, &rec.pw_min_classes,
924                    &rec.pw_history_num, &refcnt, &rec.pw_max_fail,
925                    &rec.pw_failcnt_interval, &rec.pw_lockout_duration);
926     if (nread == EOF)
927         return -1;
928     if (nread != 10) {
929         fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);
930         return 1;
931     }
932 
933     /* The next character should be a newline (1.8) or a tab (1.11). */
934     c = getc(filep);
935     if (c == EOF)
936         return -1;
937     if (c != '\n') {
938         /* Read the additional 1.11-format fields. */
939         rec.allowed_keysalts = keysaltbuf;
940         nread = fscanf(filep, "%u\t%u\t%u\t"
941                        K5CONST_WIDTH_SCANF_STR(KRB5_KDB_MAX_ALLOWED_KS_LEN)
942                        "\t%hd", &rec.attributes, &rec.max_life,
943                        &rec.max_renewable_life, rec.allowed_keysalts,
944                        &rec.n_tl_data);
945         if (nread == EOF)
946             return -1;
947         if (nread != 5) {
948             fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);
949             return 1;
950         }
951 
952         if (rec.allowed_keysalts && !strcmp(rec.allowed_keysalts, "-"))
953             rec.allowed_keysalts = NULL;
954 
955         /* Get TL data */
956         ret = alloc_tl_data(rec.n_tl_data, &rec.tl_data);
957         if (ret)
958             goto cleanup;
959 
960         ret = process_tl_data(fname, filep, *linenop, rec.tl_data);
961         if (ret)
962             goto cleanup;
963     }
964 
965     ret = krb5_db_create_policy(context, &rec);
966     if (ret)
967         ret = krb5_db_put_policy(context, &rec);
968     if (ret) {
969         com_err(progname, ret, _("while creating policy"));
970         goto cleanup;
971     }
972     if (verbose)
973         fprintf(stderr, "created policy %s\n", rec.name);
974 
975 cleanup:
976     for (tl = rec.tl_data; tl; tl = tl_next) {
977         tl_next = tl->tl_data_next;
978         free(tl->tl_data_contents);
979         free(tl);
980     }
981     return ret ? 1 : 0;
982 }
983 
984 /* Read a record which is tagged with "princ" or "policy", calling princfn
985  * or policyfn as appropriate. */
986 static int
process_tagged(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop,load_func princfn,load_func policyfn)987 process_tagged(krb5_context context, const char *fname, FILE *filep,
988                krb5_boolean verbose, int *linenop, load_func princfn,
989                load_func policyfn)
990 {
991     int nread;
992     char rectype[100];
993 
994     nread = fscanf(filep, "%99s\t", rectype);
995     if (nread == EOF)
996         return -1;
997     if (nread != 1)
998         return 1;
999     if (strcmp(rectype, "princ") == 0)
1000         return (*princfn)(context, fname, filep, verbose, linenop);
1001     if (strcmp(rectype, "policy") == 0)
1002         return (*policyfn)(context, fname, filep, verbose, linenop);
1003     if (strcmp(rectype, "End") == 0)  /* Only expected for OV format */
1004         return -1;
1005 
1006     fprintf(stderr, _("unknown record type \"%s\"\n"), rectype);
1007     return 1;
1008 }
1009 
1010 static int
process_k5beta7_record(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)1011 process_k5beta7_record(krb5_context context, const char *fname, FILE *filep,
1012                        krb5_boolean verbose, int *linenop)
1013 {
1014     return process_tagged(context, fname, filep, verbose, linenop,
1015                           process_k5beta7_princ, process_k5beta7_policy);
1016 }
1017 
1018 static int
process_r1_8_record(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)1019 process_r1_8_record(krb5_context context, const char *fname, FILE *filep,
1020                     krb5_boolean verbose, int *linenop)
1021 {
1022     return process_tagged(context, fname, filep, verbose, linenop,
1023                           process_k5beta7_princ, process_r1_8_policy);
1024 }
1025 
1026 static int
process_r1_11_record(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)1027 process_r1_11_record(krb5_context context, const char *fname, FILE *filep,
1028                      krb5_boolean verbose, int *linenop)
1029 {
1030     return process_tagged(context, fname, filep, verbose, linenop,
1031                           process_k5beta7_princ, process_r1_11_policy);
1032 }
1033 
1034 dump_version beta7_version = {
1035     "Kerberos version 5",
1036     "kdb5_util load_dump version 4\n",
1037     0,
1038     0,
1039     0,
1040     dump_k5beta7_princ,
1041     dump_k5beta7_policy,
1042     process_k5beta7_record,
1043 };
1044 dump_version r1_3_version = {
1045     "Kerberos version 5 release 1.3",
1046     "kdb5_util load_dump version 5\n",
1047     0,
1048     0,
1049     0,
1050     dump_k5beta7_princ_withpolicy,
1051     dump_k5beta7_policy,
1052     process_k5beta7_record,
1053 };
1054 dump_version r1_8_version = {
1055     "Kerberos version 5 release 1.8",
1056     "kdb5_util load_dump version 6\n",
1057     0,
1058     0,
1059     0,
1060     dump_k5beta7_princ_withpolicy,
1061     dump_r1_8_policy,
1062     process_r1_8_record,
1063 };
1064 dump_version r1_11_version = {
1065     "Kerberos version 5 release 1.11",
1066     "kdb5_util load_dump version 7\n",
1067     0,
1068     0,
1069     0,
1070     dump_k5beta7_princ_withpolicy,
1071     dump_r1_11_policy,
1072     process_r1_11_record,
1073 };
1074 dump_version iprop_version = {
1075     "Kerberos iprop version",
1076     "iprop",
1077     0,
1078     1,
1079     0,
1080     dump_k5beta7_princ_withpolicy,
1081     dump_k5beta7_policy,
1082     process_k5beta7_record,
1083 };
1084 dump_version ipropx_1_version = {
1085     "Kerberos iprop extensible version",
1086     "ipropx",
1087     0,
1088     1,
1089     1,
1090     dump_k5beta7_princ_withpolicy,
1091     dump_r1_11_policy,
1092     process_r1_11_record,
1093 };
1094 
1095 /* Read the dump header.  Return 1 on success, 0 if the file is not a
1096  * recognized iprop dump format. */
1097 static int
parse_iprop_header(char * buf,dump_version ** dv,kdb_last_t * last)1098 parse_iprop_header(char *buf, dump_version **dv, kdb_last_t *last)
1099 {
1100     char head[128];
1101     int nread;
1102     uint32_t u[4];
1103     uint32_t *up = &u[0];
1104 
1105     nread = sscanf(buf, "%127s %u %u %u %u", head, &u[0], &u[1], &u[2], &u[3]);
1106     if (nread < 1)
1107         return 0;
1108 
1109     if (!strcmp(head, ipropx_1_version.header)) {
1110         if (nread != 5)
1111             return 0;
1112         if (u[0] == IPROPX_VERSION_0) {
1113             *dv = &iprop_version;
1114         } else if (u[0] == IPROPX_VERSION_1) {
1115             *dv = &ipropx_1_version;
1116         } else {
1117             fprintf(stderr, _("%s: Unknown iprop dump version %d\n"), progname,
1118                     u[0]);
1119             return 0;
1120         }
1121         up = &u[1];
1122     } else if (!strcmp(head, iprop_version.header)) {
1123         if (nread != 4)
1124             return 0;
1125         *dv = &iprop_version;
1126     } else {
1127         fprintf(stderr, "Invalid iprop header\n");
1128         return 0;
1129     }
1130 
1131     last->last_sno = *up++;
1132     last->last_time.seconds = *up++;
1133     last->last_time.useconds = *up++;
1134     return 1;
1135 }
1136 
1137 /* Return true if the serial number and timestamp in an existing dump file is
1138  * in the ulog. */
1139 static krb5_boolean
current_dump_sno_in_ulog(krb5_context context,const char * ifile)1140 current_dump_sno_in_ulog(krb5_context context, const char *ifile)
1141 {
1142     update_status_t status;
1143     dump_version *junk;
1144     kdb_last_t last;
1145     char buf[BUFSIZ], *r;
1146     FILE *f;
1147 
1148     f = fopen(ifile, "r");
1149     if (f == NULL)
1150         return 0;              /* aliasing other errors to ENOENT here is OK */
1151 
1152     r = fgets(buf, sizeof(buf), f);
1153     fclose(f);
1154     if (r == NULL)
1155         return errno ? -1 : 0;
1156 
1157     if (!parse_iprop_header(buf, &junk, &last))
1158         return 0;
1159 
1160     status = ulog_get_sno_status(context, &last);
1161     return status == UPDATE_OK || status == UPDATE_NIL;
1162 }
1163 
1164 void
dump_db(int argc,char ** argv)1165 dump_db(int argc, char **argv)
1166 {
1167     FILE *f;
1168     struct dump_args args;
1169     char *ofile = NULL, *tmpofile = NULL, *new_mkey_file = NULL;
1170     krb5_error_code ret, retval;
1171     dump_version *dump;
1172     int aindex, ok_fd = -1;
1173     bool_t dump_sno = FALSE;
1174     kdb_log_context *log_ctx;
1175     unsigned int ipropx_version = IPROPX_VERSION_0;
1176     krb5_kvno kt_kvno;
1177     krb5_boolean conditional = FALSE;
1178     kdb_last_t last;
1179     krb5_flags iterflags = 0;
1180 
1181     /* Parse the arguments. */
1182     dump = &r1_11_version;
1183     args.verbose = FALSE;
1184     args.omit_nra = FALSE;
1185     mkey_convert = FALSE;
1186     log_ctx = util_context->kdblog_context;
1187 
1188     /*
1189      * Parse the qualifiers.
1190      */
1191     for (aindex = 1; aindex < argc; aindex++) {
1192         if (!strcmp(argv[aindex], "-b7")) {
1193             dump = &beta7_version;
1194         } else if (!strcmp(argv[aindex], "-ov")) {
1195             fprintf(stderr, _("OV dump format not supported\n"));
1196             goto error;
1197         } else if (!strcmp(argv[aindex], "-r13")) {
1198             dump = &r1_3_version;
1199         } else if (!strcmp(argv[aindex], "-r18")) {
1200             dump = &r1_8_version;
1201         } else if (!strncmp(argv[aindex], "-i", 2)) {
1202             /* Intentionally undocumented - only used by kadmin. */
1203             if (log_ctx && log_ctx->iproprole) {
1204                 /* ipropx_version is the maximum version acceptable. */
1205                 ipropx_version = atoi(argv[aindex] + 2);
1206                 dump = ipropx_version ? &ipropx_1_version : &iprop_version;
1207                 /*
1208                  * dump_sno is used to indicate if the serial number should be
1209                  * populated in the output file to be used later by iprop for
1210                  * updating the replica's update log when loading.
1211                  */
1212                 dump_sno = TRUE;
1213                 /* FLAG_OMIT_NRA is set to indicate that non-replicated
1214                  * attributes should be omitted. */
1215                 args.omit_nra = TRUE;
1216             } else {
1217                 fprintf(stderr, _("Iprop not enabled\n"));
1218                 goto error;
1219             }
1220         } else if (!strcmp(argv[aindex], "-c")) {
1221             conditional = 1;
1222         } else if (!strcmp(argv[aindex], "-verbose")) {
1223             args.verbose = TRUE;
1224         } else if (!strcmp(argv[aindex], "-mkey_convert")) {
1225             mkey_convert = 1;
1226         } else if (!strcmp(argv[aindex], "-new_mkey_file")) {
1227             new_mkey_file = argv[++aindex];
1228             mkey_convert = 1;
1229         } else if (!strcmp(argv[aindex], "-rev")) {
1230             iterflags |= KRB5_DB_ITER_REV;
1231         } else if (!strcmp(argv[aindex], "-recurse")) {
1232             iterflags |= KRB5_DB_ITER_RECURSE;
1233         } else {
1234             break;
1235         }
1236     }
1237 
1238     args.names = NULL;
1239     args.nnames = 0;
1240     if (aindex < argc) {
1241         ofile = argv[aindex];
1242         aindex++;
1243         if (aindex < argc) {
1244             args.names = &argv[aindex];
1245             args.nnames = argc - aindex;
1246         }
1247     }
1248 
1249     /* If a conditional ipropx dump we check if the existing dump is
1250      * good enough. */
1251     if (ofile != NULL && conditional) {
1252         if (!dump->iprop) {
1253             com_err(progname, 0,
1254                     _("Conditional dump is an undocumented option for "
1255                       "use only for iprop dumps"));
1256             goto error;
1257         }
1258         if (current_dump_sno_in_ulog(util_context, ofile))
1259             return;
1260     }
1261 
1262     /*
1263      * Make sure the database is open.  The policy database only has
1264      * to be opened if we try a dump that uses it.
1265      */
1266     if (!dbactive) {
1267         com_err(progname, 0, _("Database not currently opened!"));
1268         goto error;
1269     }
1270 
1271     /*
1272      * If we're doing a master key conversion, set up for it.
1273      */
1274     if (mkey_convert) {
1275         if (!valid_master_key) {
1276             /* TRUE here means read the keyboard, but only once */
1277             retval = krb5_db_fetch_mkey(util_context, master_princ,
1278                                         master_keyblock.enctype, TRUE, FALSE,
1279                                         NULL, NULL, NULL, &master_keyblock);
1280             if (retval) {
1281                 com_err(progname, retval, _("while reading master key"));
1282                 exit(1);
1283             }
1284             retval = krb5_db_fetch_mkey_list(util_context, master_princ,
1285                                              &master_keyblock);
1286             if (retval) {
1287                 com_err(progname, retval, _("while verifying master key"));
1288                 exit(1);
1289             }
1290         }
1291         new_master_keyblock.enctype = global_params.enctype;
1292         if (new_master_keyblock.enctype == ENCTYPE_UNKNOWN)
1293             new_master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
1294 
1295         if (new_mkey_file) {
1296             if (global_params.mask & KADM5_CONFIG_KVNO)
1297                 kt_kvno = global_params.kvno;
1298             else
1299                 kt_kvno = IGNORE_VNO;
1300 
1301             retval = krb5_db_fetch_mkey(util_context, master_princ,
1302                                         new_master_keyblock.enctype, FALSE,
1303                                         FALSE, new_mkey_file, &kt_kvno, NULL,
1304                                         &new_master_keyblock);
1305             if (retval) {
1306                 com_err(progname, retval, _("while reading new master key"));
1307                 exit(1);
1308             }
1309         } else {
1310             printf(_("Please enter new master key....\n"));
1311             retval = krb5_db_fetch_mkey(util_context, master_princ,
1312                                         new_master_keyblock.enctype, TRUE,
1313                                         TRUE, NULL, NULL, NULL,
1314                                         &new_master_keyblock);
1315             if (retval) {
1316                 com_err(progname, retval, _("while reading new master key"));
1317                 exit(1);
1318             }
1319         }
1320         /* Get new master key vno that will be used to protect princs. */
1321         new_mkvno = get_next_kvno(util_context, master_entry);
1322     }
1323 
1324     ret = 0;
1325 
1326     if (ofile != NULL && strcmp(ofile, "-")) {
1327         /* Discourage accidental dumping to filenames beginning with '-'. */
1328         if (ofile[0] == '-')
1329             usage();
1330         if (!prep_ok_file(util_context, ofile, &ok_fd))
1331             return;             /* prep_ok_file() bumps exit_status */
1332         f = create_ofile(ofile, &tmpofile);
1333         if (f == NULL) {
1334             com_err(progname, errno, _("while opening %s for writing"), ofile);
1335             goto error;
1336         }
1337     } else {
1338         f = stdout;
1339     }
1340 
1341     args.ofile = f;
1342     args.context = util_context;
1343     args.dump = dump;
1344     fprintf(args.ofile, "%s", dump->header);
1345 
1346     if (dump_sno) {
1347         ret = ulog_get_last(util_context, &last);
1348         if (ret) {
1349             com_err(progname, ret, _("while reading update log header"));
1350             goto error;
1351         }
1352         if (ipropx_version)
1353             fprintf(f, " %u", IPROPX_VERSION);
1354         fprintf(f, " %u", last.last_sno);
1355         fprintf(f, " %u", last.last_time.seconds);
1356         fprintf(f, " %u", last.last_time.useconds);
1357     }
1358 
1359     if (dump->header[strlen(dump->header)-1] != '\n')
1360         fputc('\n', args.ofile);
1361 
1362     ret = krb5_db_iterate(util_context, NULL, dump_iterator, &args, iterflags);
1363     if (ret) {
1364         com_err(progname, ret, _("performing %s dump"), dump->name);
1365         goto error;
1366     }
1367 
1368     /* Don't dump policies if specific principal entries were requested. */
1369     if (dump->dump_policy != NULL && args.nnames == 0) {
1370         ret = krb5_db_iter_policy(util_context, "*", dump->dump_policy, &args);
1371         if (ret) {
1372             com_err(progname, ret, _("performing %s dump"), dump->name);
1373             goto error;
1374         }
1375     }
1376 
1377     if (f != stdout) {
1378         fclose(f);
1379         finish_ofile(ofile, &tmpofile);
1380         update_ok_file(util_context, ok_fd);
1381     }
1382     return;
1383 
1384 error:
1385     if (tmpofile != NULL)
1386         unlink(tmpofile);
1387     free(tmpofile);
1388     exit_status++;
1389 }
1390 
1391 /* Restore the database from any version dump file. */
1392 static int
restore_dump(krb5_context context,char * dumpfile,FILE * f,krb5_boolean verbose,dump_version * dump)1393 restore_dump(krb5_context context, char *dumpfile, FILE *f,
1394              krb5_boolean verbose, dump_version *dump)
1395 {
1396     int err = 0;
1397     int lineno = 1;
1398 
1399     /* Process the records. */
1400     while (!(err = dump->load_record(context, dumpfile, f, verbose, &lineno)));
1401     if (err != -1) {
1402         fprintf(stderr, _("%s: error processing line %d of %s\n"), progname,
1403                 lineno, dumpfile);
1404         return err;
1405     }
1406     return 0;
1407 }
1408 
1409 void
load_db(int argc,char ** argv)1410 load_db(int argc, char **argv)
1411 {
1412     krb5_error_code ret;
1413     FILE *f = NULL;
1414     char *dumpfile = NULL, *dbname, buf[BUFSIZ];
1415     dump_version *load = NULL;
1416     int aindex;
1417     kdb_log_context *log_ctx;
1418     kdb_last_t last;
1419     krb5_boolean db_locked = FALSE, temp_db_created = FALSE;
1420     krb5_boolean verbose = FALSE, update = FALSE, iprop_load = FALSE;
1421 
1422     /* Parse the arguments. */
1423     dbname = global_params.dbname;
1424     exit_status = 0;
1425     log_ctx = util_context->kdblog_context;
1426 
1427     for (aindex = 1; aindex < argc; aindex++) {
1428         if (!strcmp(argv[aindex], "-b7")){
1429             load = &beta7_version;
1430         } else if (!strcmp(argv[aindex], "-ov")) {
1431             fprintf(stderr, _("OV dump format not supported\n"));
1432             goto error;
1433         } else if (!strcmp(argv[aindex], "-r13")) {
1434             load = &r1_3_version;
1435         } else if (!strcmp(argv[aindex], "-r18")){
1436             load = &r1_8_version;
1437         } else if (!strcmp(argv[aindex], "-i")) {
1438             /* Intentionally undocumented - only used by kadmin. */
1439             if (log_ctx && log_ctx->iproprole) {
1440                 load = &iprop_version;
1441                 iprop_load = TRUE;
1442             } else {
1443                 fprintf(stderr, _("Iprop not enabled\n"));
1444                 goto error;
1445             }
1446         } else if (!strcmp(argv[aindex], "-verbose")) {
1447             verbose = TRUE;
1448         } else if (!strcmp(argv[aindex], "-update")){
1449             update = TRUE;
1450         } else if (!strcmp(argv[aindex], "-hash")) {
1451             if (!add_db_arg("hash=true")) {
1452                 com_err(progname, ENOMEM, _("while parsing options"));
1453                 goto error;
1454             }
1455         } else {
1456             break;
1457         }
1458     }
1459     if (argc - aindex != 1)
1460         usage();
1461     dumpfile = argv[aindex];
1462 
1463     /* Open the dumpfile. */
1464     if (dumpfile != NULL) {
1465         f = fopen(dumpfile, "r");
1466         if (f == NULL) {
1467             com_err(progname, errno, _("while opening %s"), dumpfile);
1468             goto error;
1469         }
1470     } else {
1471         f = stdin;
1472         dumpfile = _("standard input");
1473     }
1474 
1475     /* Auto-detect dump version if we weren't told, or verify if we were. */
1476     if (fgets(buf, sizeof(buf), f) == NULL) {
1477         fprintf(stderr, _("%s: can't read dump header in %s\n"), progname,
1478                 dumpfile);
1479         goto error;
1480     }
1481     if (load) {
1482         /* Only check what we know; some headers only contain a prefix.
1483          * NB: this should work for ipropx even though load is iprop */
1484         if (strncmp(buf, load->header, strlen(load->header)) != 0) {
1485             fprintf(stderr, _("%s: dump header bad in %s\n"), progname,
1486                     dumpfile);
1487             goto error;
1488         }
1489     } else {
1490         if (strcmp(buf, beta7_version.header) == 0) {
1491             load = &beta7_version;
1492         } else if (strcmp(buf, r1_3_version.header) == 0) {
1493             load = &r1_3_version;
1494         } else if (strcmp(buf, r1_8_version.header) == 0) {
1495             load = &r1_8_version;
1496         } else if (strcmp(buf, r1_11_version.header) == 0) {
1497             load = &r1_11_version;
1498         } else {
1499             fprintf(stderr, _("%s: dump header bad in %s\n"), progname,
1500                     dumpfile);
1501             goto error;
1502         }
1503     }
1504 
1505     if (global_params.iprop_enabled &&
1506         ulog_map(util_context, global_params.iprop_logfile,
1507                  global_params.iprop_ulogsize)) {
1508         fprintf(stderr, _("Could not open iprop ulog\n"));
1509         goto error;
1510     }
1511 
1512     if (load->updateonly && !update) {
1513         fprintf(stderr, _("%s: dump version %s can only be loaded with the "
1514                           "-update flag\n"), progname, load->name);
1515         goto error;
1516     }
1517 
1518     /* If we are not in update mode, we create an alternate database and then
1519      * promote it to be the live db. */
1520     if (!update) {
1521         if (!add_db_arg("temporary")) {
1522             com_err(progname, ENOMEM, _("computing parameters for database"));
1523             goto error;
1524         }
1525 
1526         if (iprop_load && !add_db_arg("merge_nra")) {
1527             com_err(progname, ENOMEM, _("computing parameters for database"));
1528             goto error;
1529         }
1530 
1531         ret = krb5_db_create(util_context, db5util_db_args);
1532         if (ret) {
1533             com_err(progname, ret, _("while creating database"));
1534             goto error;
1535         }
1536         temp_db_created = TRUE;
1537     } else {
1538         /* Initialize the database. */
1539         ret = krb5_db_open(util_context, db5util_db_args,
1540                            KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
1541         if (ret) {
1542             com_err(progname, ret, _("while opening database"));
1543             goto error;
1544         }
1545 
1546         /* Make sure the db is left unusable if the update fails, if the db
1547          * supports locking. */
1548         ret = krb5_db_lock(util_context, KRB5_DB_LOCKMODE_PERMANENT);
1549         if (ret == 0) {
1550             db_locked = TRUE;
1551         } else if (ret != KRB5_PLUGIN_OP_NOTSUPP) {
1552             com_err(progname, ret, _("while permanently locking database"));
1553             goto error;
1554         }
1555     }
1556 
1557     if (log_ctx != NULL && log_ctx->iproprole && !update) {
1558         /* Don't record updates we are making to the temporary DB.  We will
1559          * reinitialize or update the ulog header after promoting it. */
1560         log_ctx->iproprole = IPROP_REPLICA;
1561         if (iprop_load) {
1562             /* Parse the iprop header information. */
1563             if (!parse_iprop_header(buf, &load, &last))
1564                 goto error;
1565         }
1566     }
1567 
1568     if (restore_dump(util_context, dumpfile ? dumpfile : _("standard input"),
1569                      f, verbose, load)) {
1570         fprintf(stderr, _("%s: %s restore failed\n"), progname, load->name);
1571         goto error;
1572     }
1573 
1574     if (db_locked && (ret = krb5_db_unlock(util_context))) {
1575         com_err(progname, ret, _("while unlocking database"));
1576         goto error;
1577     }
1578 
1579     if (!update) {
1580         /* Initialize the ulog header before promoting so we can't leave behind
1581          * the pre-load ulog state if we are killed just after promoting. */
1582         if (log_ctx != NULL && log_ctx->iproprole) {
1583             ret = ulog_init_header(util_context);
1584             if (ret) {
1585                 com_err(progname, ret, _("while reinitializing update log"));
1586                 goto error;
1587             }
1588         }
1589 
1590         ret = krb5_db_promote(util_context, db5util_db_args);
1591         /* Ignore a not supported error since there is nothing to do about it
1592          * anyway. */
1593         if (ret != 0 && ret != KRB5_PLUGIN_OP_NOTSUPP) {
1594             com_err(progname, ret,
1595                     _("while making newly loaded database live"));
1596             goto error;
1597         }
1598 
1599         if (log_ctx != NULL && log_ctx->iproprole) {
1600             /* Reinitialize the ulog header since we replaced the DB, and
1601              * record the iprop state if we received it. */
1602             ret = ulog_init_header(util_context);
1603             if (ret) {
1604                 com_err(progname, ret, _("while reinitializing update log"));
1605                 goto error;
1606             }
1607             if (iprop_load) {
1608                 ret = ulog_set_last(util_context, &last);
1609                 if (ret) {
1610                     com_err(progname, ret,
1611                             _("while writing update log header"));
1612                     goto error;
1613                 }
1614             }
1615         }
1616     }
1617 
1618 cleanup:
1619     /* If we created a temporary DB but didn't succeed, destroy it. */
1620     if (exit_status && temp_db_created) {
1621         ret = krb5_db_destroy(util_context, db5util_db_args);
1622         /* Ignore a not supported error since there is nothing to do about
1623          * it anyway. */
1624         if (ret != 0 && ret != KRB5_PLUGIN_OP_NOTSUPP) {
1625             com_err(progname, ret, _("while deleting bad database %s"),
1626                     dbname);
1627         }
1628     }
1629 
1630     if (f != NULL && f != stdin)
1631         fclose(f);
1632 
1633     return;
1634 
1635 error:
1636     exit_status++;
1637     goto cleanup;
1638 }
1639