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(®, args->names[i], REG_EXTENDED);
258 if (st) {
259 regerror(st, ®, 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(®, 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, ®, errmsg, sizeof(errmsg));
272 fprintf(stderr, _("%s: regular expression match error: %s\n"),
273 progname, errmsg);
274 break;
275 }
276 regfree(®);
277 #elif HAVE_REGEXP_H
278 /* Compile the regular expression. */
279 compile(args->names[i], regexp_buffer, ®exp_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 if (u2 > UINT_MAX / 2) {
672 load_err(fname, *linenop, _("cannot allocate principal (too large)"));
673 goto fail;
674 }
675 name = malloc(u2 + 1);
676 if (name == NULL)
677 goto fail;
678
679 /* Get memory for and form tagged data linked list */
680 if (u3 > UINT16_MAX) {
681 load_err(fname, *linenop, _("cannot allocate tl_data (too large)"));
682 goto fail;
683 }
684 if (alloc_tl_data(u3, &dbentry->tl_data))
685 goto fail;
686 dbentry->n_tl_data = u3;
687
688 /* Get memory for key list */
689 if (u4 > INT16_MAX) {
690 load_err(fname, *linenop, _("invalid key_data size"));
691 goto fail;
692 }
693 if (u4 && (kp = calloc(u4, sizeof(krb5_key_data))) == NULL)
694 goto fail;
695
696 dbentry->len = u1;
697 dbentry->n_key_data = u4;
698 dbentry->e_length = u5;
699
700 if (kp != NULL) {
701 dbentry->key_data = kp;
702 kp = NULL;
703 }
704
705 /* Read in and parse the principal name */
706 if (read_string(filep, name, u2, linenop)) {
707 load_err(fname, *linenop, _("cannot read name string"));
708 goto fail;
709 }
710 ret = krb5_parse_name(context, name, &dbentry->princ);
711 if (ret) {
712 com_err(progname, ret, _("while parsing name %s"), name);
713 goto fail;
714 }
715
716 /* Get the fixed principal attributes */
717 nread = fscanf(filep, "%d\t%d\t%d\t%u\t%u\t%d\t%d\t%d\t",
718 &t1, &t2, &t3, &u1, &u2, &u3, &u4, &u5);
719 if (nread != 8) {
720 load_err(fname, *linenop, _("cannot read principal attributes"));
721 goto fail;
722 }
723 dbentry->attributes = t1;
724 dbentry->max_life = t2;
725 dbentry->max_renewable_life = t3;
726 dbentry->expiration = u1;
727 dbentry->pw_expiration = u2;
728 dbentry->last_success = u3;
729 dbentry->last_failed = u4;
730 dbentry->fail_auth_count = u5;
731 dbentry->mask = KADM5_LOAD | KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
732 KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
733 KADM5_PRINC_EXPIRE_TIME | KADM5_PW_EXPIRATION | KADM5_LAST_SUCCESS |
734 KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT;
735
736 /* Read tagged data. */
737 if (dbentry->n_tl_data) {
738 if (process_tl_data(fname, filep, *linenop, dbentry->tl_data))
739 goto fail;
740 for (tl = dbentry->tl_data; tl; tl = tl->tl_data_next) {
741 /* test to set mask fields */
742 if (tl->tl_data_type == KRB5_TL_KADM_DATA) {
743 XDR xdrs;
744 osa_princ_ent_rec osa_princ_ent;
745
746 /*
747 * Assuming aux_attributes will always be
748 * there
749 */
750 dbentry->mask |= KADM5_AUX_ATTRIBUTES;
751
752 /* test for an actual policy reference */
753 memset(&osa_princ_ent, 0, sizeof(osa_princ_ent));
754 xdrmem_create(&xdrs, (char *)tl->tl_data_contents,
755 tl->tl_data_length, XDR_DECODE);
756 if (xdr_osa_princ_ent_rec(&xdrs, &osa_princ_ent)) {
757 if ((osa_princ_ent.aux_attributes & KADM5_POLICY) &&
758 osa_princ_ent.policy != NULL)
759 dbentry->mask |= KADM5_POLICY;
760 kdb_free_entry(NULL, NULL, &osa_princ_ent);
761 }
762 xdr_destroy(&xdrs);
763 }
764 }
765 dbentry->mask |= KADM5_TL_DATA;
766 }
767
768 /* Get the key data. */
769 for (i = 0; i < dbentry->n_key_data; i++) {
770 kd = &dbentry->key_data[i];
771 nread = fscanf(filep, "%d\t%d\t", &t1, &t2);
772 if (nread != 2) {
773 load_err(fname, *linenop, _("cannot read key size and version"));
774 goto fail;
775 }
776 if (t1 > KRB5_KDB_V1_KEY_DATA_ARRAY) {
777 load_err(fname, *linenop, _("unsupported key_data_ver version"));
778 goto fail;
779 }
780 if (t2 < 0 || t2 > UINT16_MAX) {
781 load_err(fname, *linenop, _("invalid kvno"));
782 goto fail;
783 }
784
785 kd->key_data_ver = t1;
786 kd->key_data_kvno = t2;
787
788 for (j = 0; j < t1; j++) {
789 nread = fscanf(filep, "%d\t%d\t", &t3, &t4);
790 if (nread != 2 || t4 < 0 || t4 > UINT16_MAX) {
791 load_err(fname, *linenop,
792 _("cannot read key type and length"));
793 goto fail;
794 }
795 kd->key_data_type[j] = t3;
796 kd->key_data_length[j] = t4;
797 if (read_octets_or_minus1(filep, t4, &kd->key_data_contents[j])) {
798 load_err(fname, *linenop, _("cannot read key data"));
799 goto fail;
800 }
801 }
802 }
803 if (dbentry->n_key_data)
804 dbentry->mask |= KADM5_KEY_DATA;
805
806 /* Get the extra data */
807 if (read_octets_or_minus1(filep, dbentry->e_length, &dbentry->e_data)) {
808 load_err(fname, *linenop, _("cannot read extra data"));
809 goto fail;
810 }
811
812 /* Finally, find the end of the record. */
813 read_record_end(filep, fname, *linenop);
814
815 ret = krb5_db_put_principal(context, dbentry);
816 if (ret) {
817 com_err(progname, ret, _("while storing %s"), name);
818 goto fail;
819 }
820
821 if (verbose)
822 fprintf(stderr, "%s\n", name);
823 retval = 0;
824
825 cleanup:
826 free(kp);
827 free(name);
828 krb5_db_free_principal(context, dbentry);
829 return retval;
830
831 fail:
832 retval = 1;
833 goto cleanup;
834 }
835
836 static int
process_k5beta7_policy(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)837 process_k5beta7_policy(krb5_context context, const char *fname, FILE *filep,
838 krb5_boolean verbose, int *linenop)
839 {
840 osa_policy_ent_rec rec;
841 char namebuf[1024];
842 unsigned int refcnt;
843 int nread, ret;
844
845 memset(&rec, 0, sizeof(rec));
846
847 (*linenop)++;
848 rec.name = namebuf;
849
850 nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u", rec.name,
851 &rec.pw_min_life, &rec.pw_max_life, &rec.pw_min_length,
852 &rec.pw_min_classes, &rec.pw_history_num, &refcnt);
853 if (nread == EOF)
854 return -1;
855 if (nread != 7) {
856 fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);
857 return 1;
858 }
859
860 ret = krb5_db_create_policy(context, &rec);
861 if (ret)
862 ret = krb5_db_put_policy(context, &rec);
863 if (ret) {
864 com_err(progname, ret, _("while creating policy"));
865 return 1;
866 }
867 if (verbose)
868 fprintf(stderr, _("created policy %s\n"), rec.name);
869
870 return 0;
871 }
872
873 static int
process_r1_8_policy(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)874 process_r1_8_policy(krb5_context context, const char *fname, FILE *filep,
875 krb5_boolean verbose, int *linenop)
876 {
877 osa_policy_ent_rec rec;
878 char namebuf[1024];
879 unsigned int refcnt;
880 int nread, ret;
881
882 memset(&rec, 0, sizeof(rec));
883
884 (*linenop)++;
885 rec.name = namebuf;
886
887 nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u",
888 rec.name, &rec.pw_min_life, &rec.pw_max_life,
889 &rec.pw_min_length, &rec.pw_min_classes,
890 &rec.pw_history_num, &refcnt, &rec.pw_max_fail,
891 &rec.pw_failcnt_interval, &rec.pw_lockout_duration);
892 if (nread == EOF)
893 return -1;
894 if (nread != 10) {
895 fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);
896 return 1;
897 }
898
899 ret = krb5_db_create_policy(context, &rec);
900 if (ret)
901 ret = krb5_db_put_policy(context, &rec);
902 if (ret) {
903 com_err(progname, ret, _("while creating policy"));
904 return 1;
905 }
906 if (verbose)
907 fprintf(stderr, "created policy %s\n", rec.name);
908
909 return 0;
910 }
911
912 static int
process_r1_11_policy(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)913 process_r1_11_policy(krb5_context context, const char *fname, FILE *filep,
914 krb5_boolean verbose, int *linenop)
915 {
916 osa_policy_ent_rec rec;
917 krb5_tl_data *tl, *tl_next;
918 char namebuf[1024];
919 char keysaltbuf[KRB5_KDB_MAX_ALLOWED_KS_LEN + 1];
920 unsigned int refcnt;
921 int nread, c, ret = 0;
922
923 memset(&rec, 0, sizeof(rec));
924
925 (*linenop)++;
926 rec.name = namebuf;
927
928 /*
929 * Due to a historical error, iprop dumps use the same version before and
930 * after the 1.11 policy extensions. So we need to accept both 1.8-format
931 * and 1.11-format policy entries. Begin by reading the 1.8 fields.
932 */
933 nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u",
934 rec.name, &rec.pw_min_life, &rec.pw_max_life,
935 &rec.pw_min_length, &rec.pw_min_classes,
936 &rec.pw_history_num, &refcnt, &rec.pw_max_fail,
937 &rec.pw_failcnt_interval, &rec.pw_lockout_duration);
938 if (nread == EOF)
939 return -1;
940 if (nread != 10) {
941 fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);
942 return 1;
943 }
944
945 /* The next character should be a newline (1.8) or a tab (1.11). */
946 c = getc(filep);
947 if (c == EOF)
948 return -1;
949 if (c != '\n') {
950 /* Read the additional 1.11-format fields. */
951 rec.allowed_keysalts = keysaltbuf;
952 nread = fscanf(filep, "%u\t%u\t%u\t"
953 K5CONST_WIDTH_SCANF_STR(KRB5_KDB_MAX_ALLOWED_KS_LEN)
954 "\t%hd", &rec.attributes, &rec.max_life,
955 &rec.max_renewable_life, rec.allowed_keysalts,
956 &rec.n_tl_data);
957 if (nread == EOF)
958 return -1;
959 if (nread != 5) {
960 fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);
961 return 1;
962 }
963
964 if (rec.allowed_keysalts && !strcmp(rec.allowed_keysalts, "-"))
965 rec.allowed_keysalts = NULL;
966
967 /* Get TL data */
968 ret = alloc_tl_data(rec.n_tl_data, &rec.tl_data);
969 if (ret)
970 goto cleanup;
971
972 ret = process_tl_data(fname, filep, *linenop, rec.tl_data);
973 if (ret)
974 goto cleanup;
975 }
976
977 ret = krb5_db_create_policy(context, &rec);
978 if (ret)
979 ret = krb5_db_put_policy(context, &rec);
980 if (ret) {
981 com_err(progname, ret, _("while creating policy"));
982 goto cleanup;
983 }
984 if (verbose)
985 fprintf(stderr, "created policy %s\n", rec.name);
986
987 cleanup:
988 for (tl = rec.tl_data; tl; tl = tl_next) {
989 tl_next = tl->tl_data_next;
990 free(tl->tl_data_contents);
991 free(tl);
992 }
993 return ret ? 1 : 0;
994 }
995
996 /* Read a record which is tagged with "princ" or "policy", calling princfn
997 * or policyfn as appropriate. */
998 static int
process_tagged(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop,load_func princfn,load_func policyfn)999 process_tagged(krb5_context context, const char *fname, FILE *filep,
1000 krb5_boolean verbose, int *linenop, load_func princfn,
1001 load_func policyfn)
1002 {
1003 int nread;
1004 char rectype[100];
1005
1006 nread = fscanf(filep, "%99s\t", rectype);
1007 if (nread == EOF)
1008 return -1;
1009 if (nread != 1)
1010 return 1;
1011 if (strcmp(rectype, "princ") == 0)
1012 return (*princfn)(context, fname, filep, verbose, linenop);
1013 if (strcmp(rectype, "policy") == 0)
1014 return (*policyfn)(context, fname, filep, verbose, linenop);
1015 if (strcmp(rectype, "End") == 0) /* Only expected for OV format */
1016 return -1;
1017
1018 fprintf(stderr, _("unknown record type \"%s\"\n"), rectype);
1019 return 1;
1020 }
1021
1022 static int
process_k5beta7_record(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)1023 process_k5beta7_record(krb5_context context, const char *fname, FILE *filep,
1024 krb5_boolean verbose, int *linenop)
1025 {
1026 return process_tagged(context, fname, filep, verbose, linenop,
1027 process_k5beta7_princ, process_k5beta7_policy);
1028 }
1029
1030 static int
process_r1_8_record(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)1031 process_r1_8_record(krb5_context context, const char *fname, FILE *filep,
1032 krb5_boolean verbose, int *linenop)
1033 {
1034 return process_tagged(context, fname, filep, verbose, linenop,
1035 process_k5beta7_princ, process_r1_8_policy);
1036 }
1037
1038 static int
process_r1_11_record(krb5_context context,const char * fname,FILE * filep,krb5_boolean verbose,int * linenop)1039 process_r1_11_record(krb5_context context, const char *fname, FILE *filep,
1040 krb5_boolean verbose, int *linenop)
1041 {
1042 return process_tagged(context, fname, filep, verbose, linenop,
1043 process_k5beta7_princ, process_r1_11_policy);
1044 }
1045
1046 dump_version beta7_version = {
1047 "Kerberos version 5",
1048 "kdb5_util load_dump version 4\n",
1049 0,
1050 0,
1051 0,
1052 dump_k5beta7_princ,
1053 dump_k5beta7_policy,
1054 process_k5beta7_record,
1055 };
1056 dump_version r1_3_version = {
1057 "Kerberos version 5 release 1.3",
1058 "kdb5_util load_dump version 5\n",
1059 0,
1060 0,
1061 0,
1062 dump_k5beta7_princ_withpolicy,
1063 dump_k5beta7_policy,
1064 process_k5beta7_record,
1065 };
1066 dump_version r1_8_version = {
1067 "Kerberos version 5 release 1.8",
1068 "kdb5_util load_dump version 6\n",
1069 0,
1070 0,
1071 0,
1072 dump_k5beta7_princ_withpolicy,
1073 dump_r1_8_policy,
1074 process_r1_8_record,
1075 };
1076 dump_version r1_11_version = {
1077 "Kerberos version 5 release 1.11",
1078 "kdb5_util load_dump version 7\n",
1079 0,
1080 0,
1081 0,
1082 dump_k5beta7_princ_withpolicy,
1083 dump_r1_11_policy,
1084 process_r1_11_record,
1085 };
1086 dump_version iprop_version = {
1087 "Kerberos iprop version",
1088 "iprop",
1089 0,
1090 1,
1091 0,
1092 dump_k5beta7_princ_withpolicy,
1093 dump_k5beta7_policy,
1094 process_k5beta7_record,
1095 };
1096 dump_version ipropx_1_version = {
1097 "Kerberos iprop extensible version",
1098 "ipropx",
1099 0,
1100 1,
1101 1,
1102 dump_k5beta7_princ_withpolicy,
1103 dump_r1_11_policy,
1104 process_r1_11_record,
1105 };
1106
1107 /* Read the dump header. Return 1 on success, 0 if the file is not a
1108 * recognized iprop dump format. */
1109 static int
parse_iprop_header(char * buf,dump_version ** dv,kdb_last_t * last)1110 parse_iprop_header(char *buf, dump_version **dv, kdb_last_t *last)
1111 {
1112 char head[128];
1113 int nread;
1114 uint32_t u[4];
1115 uint32_t *up = &u[0];
1116
1117 nread = sscanf(buf, "%127s %u %u %u %u", head, &u[0], &u[1], &u[2], &u[3]);
1118 if (nread < 1)
1119 return 0;
1120
1121 if (!strcmp(head, ipropx_1_version.header)) {
1122 if (nread != 5)
1123 return 0;
1124 if (u[0] == IPROPX_VERSION_0) {
1125 *dv = &iprop_version;
1126 } else if (u[0] == IPROPX_VERSION_1) {
1127 *dv = &ipropx_1_version;
1128 } else {
1129 fprintf(stderr, _("%s: Unknown iprop dump version %d\n"), progname,
1130 u[0]);
1131 return 0;
1132 }
1133 up = &u[1];
1134 } else if (!strcmp(head, iprop_version.header)) {
1135 if (nread != 4)
1136 return 0;
1137 *dv = &iprop_version;
1138 } else {
1139 fprintf(stderr, "Invalid iprop header\n");
1140 return 0;
1141 }
1142
1143 last->last_sno = *up++;
1144 last->last_time.seconds = *up++;
1145 last->last_time.useconds = *up++;
1146 return 1;
1147 }
1148
1149 /* Return true if the serial number and timestamp in an existing dump file is
1150 * in the ulog. */
1151 static krb5_boolean
current_dump_sno_in_ulog(krb5_context context,const char * ifile)1152 current_dump_sno_in_ulog(krb5_context context, const char *ifile)
1153 {
1154 update_status_t status;
1155 dump_version *junk;
1156 kdb_last_t last;
1157 char buf[BUFSIZ], *r;
1158 FILE *f;
1159
1160 f = fopen(ifile, "r");
1161 if (f == NULL)
1162 return 0; /* aliasing other errors to ENOENT here is OK */
1163
1164 r = fgets(buf, sizeof(buf), f);
1165 fclose(f);
1166 if (r == NULL)
1167 return errno ? -1 : 0;
1168
1169 if (!parse_iprop_header(buf, &junk, &last))
1170 return 0;
1171
1172 status = ulog_get_sno_status(context, &last);
1173 return status == UPDATE_OK || status == UPDATE_NIL;
1174 }
1175
1176 void
dump_db(int argc,char ** argv)1177 dump_db(int argc, char **argv)
1178 {
1179 FILE *f;
1180 struct dump_args args;
1181 char *ofile = NULL, *tmpofile = NULL, *new_mkey_file = NULL;
1182 krb5_error_code ret, retval;
1183 dump_version *dump;
1184 int aindex, ok_fd = -1;
1185 bool_t dump_sno = FALSE;
1186 kdb_log_context *log_ctx;
1187 unsigned int ipropx_version = IPROPX_VERSION_0;
1188 krb5_kvno kt_kvno;
1189 krb5_boolean conditional = FALSE;
1190 kdb_last_t last;
1191 krb5_flags iterflags = 0;
1192
1193 /* Parse the arguments. */
1194 dump = &r1_11_version;
1195 args.verbose = FALSE;
1196 args.omit_nra = FALSE;
1197 mkey_convert = FALSE;
1198 log_ctx = util_context->kdblog_context;
1199
1200 /*
1201 * Parse the qualifiers.
1202 */
1203 for (aindex = 1; aindex < argc; aindex++) {
1204 if (!strcmp(argv[aindex], "-b7")) {
1205 dump = &beta7_version;
1206 } else if (!strcmp(argv[aindex], "-ov")) {
1207 fprintf(stderr, _("OV dump format not supported\n"));
1208 goto error;
1209 } else if (!strcmp(argv[aindex], "-r13")) {
1210 dump = &r1_3_version;
1211 } else if (!strcmp(argv[aindex], "-r18")) {
1212 dump = &r1_8_version;
1213 } else if (!strncmp(argv[aindex], "-i", 2)) {
1214 /* Intentionally undocumented - only used by kadmin. */
1215 if (log_ctx && log_ctx->iproprole) {
1216 /* ipropx_version is the maximum version acceptable. */
1217 ipropx_version = atoi(argv[aindex] + 2);
1218 dump = ipropx_version ? &ipropx_1_version : &iprop_version;
1219 /*
1220 * dump_sno is used to indicate if the serial number should be
1221 * populated in the output file to be used later by iprop for
1222 * updating the replica's update log when loading.
1223 */
1224 dump_sno = TRUE;
1225 /* FLAG_OMIT_NRA is set to indicate that non-replicated
1226 * attributes should be omitted. */
1227 args.omit_nra = TRUE;
1228 } else {
1229 fprintf(stderr, _("Iprop not enabled\n"));
1230 goto error;
1231 }
1232 } else if (!strcmp(argv[aindex], "-c")) {
1233 conditional = 1;
1234 } else if (!strcmp(argv[aindex], "-verbose")) {
1235 args.verbose = TRUE;
1236 } else if (!strcmp(argv[aindex], "-mkey_convert")) {
1237 mkey_convert = 1;
1238 } else if (!strcmp(argv[aindex], "-new_mkey_file")) {
1239 new_mkey_file = argv[++aindex];
1240 mkey_convert = 1;
1241 } else if (!strcmp(argv[aindex], "-rev")) {
1242 iterflags |= KRB5_DB_ITER_REV;
1243 } else if (!strcmp(argv[aindex], "-recurse")) {
1244 iterflags |= KRB5_DB_ITER_RECURSE;
1245 } else {
1246 break;
1247 }
1248 }
1249
1250 args.names = NULL;
1251 args.nnames = 0;
1252 if (aindex < argc) {
1253 ofile = argv[aindex];
1254 aindex++;
1255 if (aindex < argc) {
1256 args.names = &argv[aindex];
1257 args.nnames = argc - aindex;
1258 }
1259 }
1260
1261 /* If a conditional ipropx dump we check if the existing dump is
1262 * good enough. */
1263 if (ofile != NULL && conditional) {
1264 if (!dump->iprop) {
1265 com_err(progname, 0,
1266 _("Conditional dump is an undocumented option for "
1267 "use only for iprop dumps"));
1268 goto error;
1269 }
1270 if (current_dump_sno_in_ulog(util_context, ofile))
1271 return;
1272 }
1273
1274 /*
1275 * Make sure the database is open. The policy database only has
1276 * to be opened if we try a dump that uses it.
1277 */
1278 if (!dbactive) {
1279 com_err(progname, 0, _("Database not currently opened!"));
1280 goto error;
1281 }
1282
1283 /*
1284 * If we're doing a master key conversion, set up for it.
1285 */
1286 if (mkey_convert) {
1287 if (!valid_master_key) {
1288 /* TRUE here means read the keyboard, but only once */
1289 retval = krb5_db_fetch_mkey(util_context, master_princ,
1290 master_keyblock.enctype, TRUE, FALSE,
1291 NULL, NULL, NULL, &master_keyblock);
1292 if (retval) {
1293 com_err(progname, retval, _("while reading master key"));
1294 exit(1);
1295 }
1296 retval = krb5_db_fetch_mkey_list(util_context, master_princ,
1297 &master_keyblock);
1298 if (retval) {
1299 com_err(progname, retval, _("while verifying master key"));
1300 exit(1);
1301 }
1302 }
1303 new_master_keyblock.enctype = global_params.enctype;
1304 if (new_master_keyblock.enctype == ENCTYPE_UNKNOWN)
1305 new_master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
1306
1307 if (new_mkey_file) {
1308 if (global_params.mask & KADM5_CONFIG_KVNO)
1309 kt_kvno = global_params.kvno;
1310 else
1311 kt_kvno = IGNORE_VNO;
1312
1313 retval = krb5_db_fetch_mkey(util_context, master_princ,
1314 new_master_keyblock.enctype, FALSE,
1315 FALSE, new_mkey_file, &kt_kvno, NULL,
1316 &new_master_keyblock);
1317 if (retval) {
1318 com_err(progname, retval, _("while reading new master key"));
1319 exit(1);
1320 }
1321 } else {
1322 printf(_("Please enter new master key....\n"));
1323 retval = krb5_db_fetch_mkey(util_context, master_princ,
1324 new_master_keyblock.enctype, TRUE,
1325 TRUE, NULL, NULL, NULL,
1326 &new_master_keyblock);
1327 if (retval) {
1328 com_err(progname, retval, _("while reading new master key"));
1329 exit(1);
1330 }
1331 }
1332 /* Get new master key vno that will be used to protect princs. */
1333 new_mkvno = get_next_kvno(util_context, master_entry);
1334 }
1335
1336 ret = 0;
1337
1338 if (ofile != NULL && strcmp(ofile, "-")) {
1339 /* Discourage accidental dumping to filenames beginning with '-'. */
1340 if (ofile[0] == '-')
1341 usage();
1342 if (!prep_ok_file(util_context, ofile, &ok_fd))
1343 return; /* prep_ok_file() bumps exit_status */
1344 f = create_ofile(ofile, &tmpofile);
1345 if (f == NULL) {
1346 com_err(progname, errno, _("while opening %s for writing"), ofile);
1347 goto error;
1348 }
1349 } else {
1350 f = stdout;
1351 }
1352
1353 args.ofile = f;
1354 args.context = util_context;
1355 args.dump = dump;
1356 fprintf(args.ofile, "%s", dump->header);
1357
1358 if (dump_sno) {
1359 ret = ulog_get_last(util_context, &last);
1360 if (ret) {
1361 com_err(progname, ret, _("while reading update log header"));
1362 goto error;
1363 }
1364 if (ipropx_version)
1365 fprintf(f, " %u", IPROPX_VERSION);
1366 fprintf(f, " %u", last.last_sno);
1367 fprintf(f, " %u", last.last_time.seconds);
1368 fprintf(f, " %u", last.last_time.useconds);
1369 }
1370
1371 if (dump->header[strlen(dump->header)-1] != '\n')
1372 fputc('\n', args.ofile);
1373
1374 ret = krb5_db_iterate(util_context, NULL, dump_iterator, &args, iterflags);
1375 if (ret) {
1376 com_err(progname, ret, _("performing %s dump"), dump->name);
1377 goto error;
1378 }
1379
1380 /* Don't dump policies if specific principal entries were requested. */
1381 if (dump->dump_policy != NULL && args.nnames == 0) {
1382 ret = krb5_db_iter_policy(util_context, "*", dump->dump_policy, &args);
1383 if (ret) {
1384 com_err(progname, ret, _("performing %s dump"), dump->name);
1385 goto error;
1386 }
1387 }
1388
1389 if (f != stdout) {
1390 fclose(f);
1391 finish_ofile(ofile, &tmpofile);
1392 update_ok_file(util_context, ok_fd);
1393 }
1394 return;
1395
1396 error:
1397 if (tmpofile != NULL)
1398 unlink(tmpofile);
1399 free(tmpofile);
1400 exit_status++;
1401 }
1402
1403 /* Restore the database from any version dump file. */
1404 static int
restore_dump(krb5_context context,char * dumpfile,FILE * f,krb5_boolean verbose,dump_version * dump)1405 restore_dump(krb5_context context, char *dumpfile, FILE *f,
1406 krb5_boolean verbose, dump_version *dump)
1407 {
1408 int err = 0;
1409 int lineno = 1;
1410
1411 /* Process the records. */
1412 while (!(err = dump->load_record(context, dumpfile, f, verbose, &lineno)));
1413 if (err != -1) {
1414 fprintf(stderr, _("%s: error processing line %d of %s\n"), progname,
1415 lineno, dumpfile);
1416 return err;
1417 }
1418 return 0;
1419 }
1420
1421 void
load_db(int argc,char ** argv)1422 load_db(int argc, char **argv)
1423 {
1424 krb5_error_code ret;
1425 FILE *f = NULL;
1426 char *dumpfile = NULL, *dbname, buf[BUFSIZ];
1427 dump_version *load = NULL;
1428 int aindex;
1429 kdb_log_context *log_ctx;
1430 kdb_last_t last;
1431 krb5_boolean db_locked = FALSE, temp_db_created = FALSE;
1432 krb5_boolean verbose = FALSE, update = FALSE, iprop_load = FALSE;
1433
1434 /* Parse the arguments. */
1435 dbname = global_params.dbname;
1436 exit_status = 0;
1437 log_ctx = util_context->kdblog_context;
1438
1439 for (aindex = 1; aindex < argc; aindex++) {
1440 if (!strcmp(argv[aindex], "-b7")){
1441 load = &beta7_version;
1442 } else if (!strcmp(argv[aindex], "-ov")) {
1443 fprintf(stderr, _("OV dump format not supported\n"));
1444 goto error;
1445 } else if (!strcmp(argv[aindex], "-r13")) {
1446 load = &r1_3_version;
1447 } else if (!strcmp(argv[aindex], "-r18")){
1448 load = &r1_8_version;
1449 } else if (!strcmp(argv[aindex], "-i")) {
1450 /* Intentionally undocumented - only used by kadmin. */
1451 if (log_ctx && log_ctx->iproprole) {
1452 load = &iprop_version;
1453 iprop_load = TRUE;
1454 } else {
1455 fprintf(stderr, _("Iprop not enabled\n"));
1456 goto error;
1457 }
1458 } else if (!strcmp(argv[aindex], "-verbose")) {
1459 verbose = TRUE;
1460 } else if (!strcmp(argv[aindex], "-update")){
1461 update = TRUE;
1462 } else if (!strcmp(argv[aindex], "-hash")) {
1463 if (!add_db_arg("hash=true")) {
1464 com_err(progname, ENOMEM, _("while parsing options"));
1465 goto error;
1466 }
1467 } else {
1468 break;
1469 }
1470 }
1471 if (argc - aindex != 1)
1472 usage();
1473 dumpfile = argv[aindex];
1474
1475 /* Open the dumpfile. */
1476 if (dumpfile != NULL) {
1477 f = fopen(dumpfile, "r");
1478 if (f == NULL) {
1479 com_err(progname, errno, _("while opening %s"), dumpfile);
1480 goto error;
1481 }
1482 } else {
1483 f = stdin;
1484 dumpfile = _("standard input");
1485 }
1486
1487 /* Auto-detect dump version if we weren't told, or verify if we were. */
1488 if (fgets(buf, sizeof(buf), f) == NULL) {
1489 fprintf(stderr, _("%s: can't read dump header in %s\n"), progname,
1490 dumpfile);
1491 goto error;
1492 }
1493 if (load) {
1494 /* Only check what we know; some headers only contain a prefix.
1495 * NB: this should work for ipropx even though load is iprop */
1496 if (strncmp(buf, load->header, strlen(load->header)) != 0) {
1497 fprintf(stderr, _("%s: dump header bad in %s\n"), progname,
1498 dumpfile);
1499 goto error;
1500 }
1501 } else {
1502 if (strcmp(buf, beta7_version.header) == 0) {
1503 load = &beta7_version;
1504 } else if (strcmp(buf, r1_3_version.header) == 0) {
1505 load = &r1_3_version;
1506 } else if (strcmp(buf, r1_8_version.header) == 0) {
1507 load = &r1_8_version;
1508 } else if (strcmp(buf, r1_11_version.header) == 0) {
1509 load = &r1_11_version;
1510 } else {
1511 fprintf(stderr, _("%s: dump header bad in %s\n"), progname,
1512 dumpfile);
1513 goto error;
1514 }
1515 }
1516
1517 if (global_params.iprop_enabled &&
1518 ulog_map(util_context, global_params.iprop_logfile,
1519 global_params.iprop_ulogsize)) {
1520 fprintf(stderr, _("Could not open iprop ulog\n"));
1521 goto error;
1522 }
1523
1524 if (load->updateonly && !update) {
1525 fprintf(stderr, _("%s: dump version %s can only be loaded with the "
1526 "-update flag\n"), progname, load->name);
1527 goto error;
1528 }
1529
1530 /* If we are not in update mode, we create an alternate database and then
1531 * promote it to be the live db. */
1532 if (!update) {
1533 if (!add_db_arg("temporary")) {
1534 com_err(progname, ENOMEM, _("computing parameters for database"));
1535 goto error;
1536 }
1537
1538 if (iprop_load && !add_db_arg("merge_nra")) {
1539 com_err(progname, ENOMEM, _("computing parameters for database"));
1540 goto error;
1541 }
1542
1543 ret = krb5_db_create(util_context, db5util_db_args);
1544 if (ret) {
1545 com_err(progname, ret, _("while creating database"));
1546 goto error;
1547 }
1548 temp_db_created = TRUE;
1549 } else {
1550 /* Initialize the database. */
1551 ret = krb5_db_open(util_context, db5util_db_args,
1552 KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
1553 if (ret) {
1554 com_err(progname, ret, _("while opening database"));
1555 goto error;
1556 }
1557
1558 /* Make sure the db is left unusable if the update fails, if the db
1559 * supports locking. */
1560 ret = krb5_db_lock(util_context, KRB5_DB_LOCKMODE_PERMANENT);
1561 if (ret == 0) {
1562 db_locked = TRUE;
1563 } else if (ret != KRB5_PLUGIN_OP_NOTSUPP) {
1564 com_err(progname, ret, _("while permanently locking database"));
1565 goto error;
1566 }
1567 }
1568
1569 if (log_ctx != NULL && log_ctx->iproprole && !update) {
1570 /* Don't record updates we are making to the temporary DB. We will
1571 * reinitialize or update the ulog header after promoting it. */
1572 log_ctx->iproprole = IPROP_REPLICA;
1573 if (iprop_load) {
1574 /* Parse the iprop header information. */
1575 if (!parse_iprop_header(buf, &load, &last))
1576 goto error;
1577 }
1578 }
1579
1580 if (restore_dump(util_context, dumpfile ? dumpfile : _("standard input"),
1581 f, verbose, load)) {
1582 fprintf(stderr, _("%s: %s restore failed\n"), progname, load->name);
1583 goto error;
1584 }
1585
1586 if (db_locked && (ret = krb5_db_unlock(util_context))) {
1587 com_err(progname, ret, _("while unlocking database"));
1588 goto error;
1589 }
1590
1591 if (!update) {
1592 /* Initialize the ulog header before promoting so we can't leave behind
1593 * the pre-load ulog state if we are killed just after promoting. */
1594 if (log_ctx != NULL && log_ctx->iproprole) {
1595 ret = ulog_init_header(util_context);
1596 if (ret) {
1597 com_err(progname, ret, _("while reinitializing update log"));
1598 goto error;
1599 }
1600 }
1601
1602 ret = krb5_db_promote(util_context, db5util_db_args);
1603 /* Ignore a not supported error since there is nothing to do about it
1604 * anyway. */
1605 if (ret != 0 && ret != KRB5_PLUGIN_OP_NOTSUPP) {
1606 com_err(progname, ret,
1607 _("while making newly loaded database live"));
1608 goto error;
1609 }
1610
1611 if (log_ctx != NULL && log_ctx->iproprole) {
1612 /* Reinitialize the ulog header since we replaced the DB, and
1613 * record the iprop state if we received it. */
1614 ret = ulog_init_header(util_context);
1615 if (ret) {
1616 com_err(progname, ret, _("while reinitializing update log"));
1617 goto error;
1618 }
1619 if (iprop_load) {
1620 ret = ulog_set_last(util_context, &last);
1621 if (ret) {
1622 com_err(progname, ret,
1623 _("while writing update log header"));
1624 goto error;
1625 }
1626 }
1627 }
1628 }
1629
1630 cleanup:
1631 /* If we created a temporary DB but didn't succeed, destroy it. */
1632 if (exit_status && temp_db_created) {
1633 ret = krb5_db_destroy(util_context, db5util_db_args);
1634 /* Ignore a not supported error since there is nothing to do about
1635 * it anyway. */
1636 if (ret != 0 && ret != KRB5_PLUGIN_OP_NOTSUPP) {
1637 com_err(progname, ret, _("while deleting bad database %s"),
1638 dbname);
1639 }
1640 }
1641
1642 if (f != NULL && f != stdin)
1643 fclose(f);
1644
1645 return;
1646
1647 error:
1648 exit_status++;
1649 goto cleanup;
1650 }
1651