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(®, args->names[i], REG_EXTENDED);
236 if (st) {
237 regerror(st, ®, 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(®, 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, ®, errmsg, sizeof(errmsg));
250 fprintf(stderr, _("%s: regular expression match error: %s\n"),
251 progname, errmsg);
252 break;
253 }
254 regfree(®);
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