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