1/*	$NetBSD: iprop-log.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
2
3/*
4 * Copyright (c) 1997 - 2005 Kungliga Tekniska H��gskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "iprop.h"
37#include <krb5/sl.h>
38#include <krb5/parse_time.h>
39#include "iprop-commands.h"
40
41__RCSID("$NetBSD: iprop-log.c,v 1.2 2017/01/28 21:31:49 christos Exp $");
42
43static krb5_context context;
44
45static kadm5_server_context *
46get_kadmin_context(const char *config_file, char *realm)
47{
48    kadm5_config_params conf;
49    krb5_error_code ret;
50    void *kadm_handle;
51    char *file = NULL;
52    char **files;
53    int aret;
54
55    if (config_file == NULL) {
56	aret = asprintf(&file, "%s/kdc.conf", hdb_db_dir(context));
57	if (aret == -1 || file == NULL)
58	    errx(1, "out of memory");
59	config_file = file;
60    }
61
62    ret = krb5_prepend_config_files_default(config_file, &files);
63    free(file);
64    if (ret)
65	krb5_err(context, 1, ret, "getting configuration files");
66
67    ret = krb5_set_config_files(context, files);
68    krb5_free_config_files(files);
69    if (ret)
70	krb5_err(context, 1, ret, "reading configuration files");
71
72    memset(&conf, 0, sizeof(conf));
73    if(realm) {
74	conf.mask |= KADM5_CONFIG_REALM;
75	conf.realm = realm;
76    }
77
78    ret = kadm5_init_with_password_ctx (context,
79					KADM5_ADMIN_SERVICE,
80					NULL,
81					KADM5_ADMIN_SERVICE,
82					&conf, 0, 0,
83					&kadm_handle);
84    if (ret)
85	krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");
86
87    return (kadm5_server_context *)kadm_handle;
88}
89
90/*
91 * dump log
92 */
93
94static const char *op_names[] = {
95    "get",
96    "delete",
97    "create",
98    "rename",
99    "chpass",
100    "modify",
101    "randkey",
102    "get_privs",
103    "get_princs",
104    "chpass_with_key",
105    "nop"
106};
107
108static kadm5_ret_t
109print_entry(kadm5_server_context *server_context,
110	    uint32_t ver,
111	    time_t timestamp,
112	    enum kadm_ops op,
113	    uint32_t len,
114	    krb5_storage *sp,
115	    void *ctx)
116{
117    char t[256];
118    const char *entry_kind = ctx;
119    int32_t mask;
120    int32_t nop_time;
121    uint32_t nop_ver;
122    hdb_entry ent;
123    krb5_principal source;
124    char *name1, *name2;
125    krb5_data data;
126    krb5_context scontext = server_context->context;
127    krb5_error_code ret;
128
129    krb5_data_zero(&data);
130
131    strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S", localtime(&timestamp));
132
133    if((int)op < (int)kadm_get || (int)op > (int)kadm_nop) {
134	printf("unknown op: %d\n", op);
135	return 0;
136    }
137
138    printf ("%s%s: ver = %u, timestamp = %s, len = %u\n",
139	    entry_kind, op_names[op], ver, t, len);
140    switch(op) {
141    case kadm_delete:
142	krb5_ret_principal(sp, &source);
143	krb5_unparse_name(scontext, source, &name1);
144	printf("    %s\n", name1);
145	free(name1);
146	krb5_free_principal(scontext, source);
147	break;
148    case kadm_rename:
149	ret = krb5_data_alloc(&data, len);
150	if (ret)
151	    krb5_err (scontext, 1, ret, "kadm_rename: data alloc: %d", len);
152	krb5_ret_principal(sp, &source);
153	krb5_storage_read(sp, data.data, data.length);
154	hdb_value2entry(scontext, &data, &ent);
155	krb5_unparse_name(scontext, source, &name1);
156	krb5_unparse_name(scontext, ent.principal, &name2);
157	printf("    %s -> %s\n", name1, name2);
158	free(name1);
159	free(name2);
160	krb5_free_principal(scontext, source);
161	free_hdb_entry(&ent);
162	break;
163    case kadm_create:
164	ret = krb5_data_alloc(&data, len);
165	if (ret)
166	    krb5_err (scontext, 1, ret, "kadm_create: data alloc: %d", len);
167	krb5_storage_read(sp, data.data, data.length);
168	ret = hdb_value2entry(scontext, &data, &ent);
169	if(ret)
170	    abort();
171	mask = ~0;
172	goto foo;
173    case kadm_modify:
174	ret = krb5_data_alloc(&data, len);
175	if (ret)
176	    krb5_err (scontext, 1, ret, "kadm_modify: data alloc: %d", len);
177	krb5_ret_int32(sp, &mask);
178	krb5_storage_read(sp, data.data, data.length);
179	ret = hdb_value2entry(scontext, &data, &ent);
180	if(ret)
181	    abort();
182    foo:
183	if(ent.principal /* mask & KADM5_PRINCIPAL */) {
184	    krb5_unparse_name(scontext, ent.principal, &name1);
185	    printf("    principal = %s\n", name1);
186	    free(name1);
187	}
188	if(mask & KADM5_PRINC_EXPIRE_TIME) {
189	    if(ent.valid_end == NULL) {
190		strlcpy(t, "never", sizeof(t));
191	    } else {
192		strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S",
193			 localtime(ent.valid_end));
194	    }
195	    printf("    expires = %s\n", t);
196	}
197	if(mask & KADM5_PW_EXPIRATION) {
198	    if(ent.pw_end == NULL) {
199		strlcpy(t, "never", sizeof(t));
200	    } else {
201		strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S",
202			 localtime(ent.pw_end));
203	    }
204	    printf("    password exp = %s\n", t);
205	}
206	if(mask & KADM5_LAST_PWD_CHANGE) {
207	}
208	if(mask & KADM5_ATTRIBUTES) {
209	    unparse_flags(HDBFlags2int(ent.flags),
210			  asn1_HDBFlags_units(), t, sizeof(t));
211	    printf("    attributes = %s\n", t);
212	}
213	if(mask & KADM5_MAX_LIFE) {
214	    if(ent.max_life == NULL)
215		strlcpy(t, "for ever", sizeof(t));
216	    else
217		unparse_time(*ent.max_life, t, sizeof(t));
218	    printf("    max life = %s\n", t);
219	}
220	if(mask & KADM5_MAX_RLIFE) {
221	    if(ent.max_renew == NULL)
222		strlcpy(t, "for ever", sizeof(t));
223	    else
224		unparse_time(*ent.max_renew, t, sizeof(t));
225	    printf("    max rlife = %s\n", t);
226	}
227	if(mask & KADM5_MOD_TIME) {
228	    printf("    mod time\n");
229	}
230	if(mask & KADM5_MOD_NAME) {
231	    printf("    mod name\n");
232	}
233	if(mask & KADM5_KVNO) {
234	    printf("    kvno = %d\n", ent.kvno);
235	}
236	if(mask & KADM5_MKVNO) {
237	    printf("    mkvno\n");
238	}
239	if(mask & KADM5_AUX_ATTRIBUTES) {
240	    printf("    aux attributes\n");
241	}
242	if(mask & KADM5_POLICY) {
243	    printf("    policy\n");
244	}
245	if(mask & KADM5_POLICY_CLR) {
246	    printf("    mod time\n");
247	}
248	if(mask & KADM5_LAST_SUCCESS) {
249	    printf("    last success\n");
250	}
251	if(mask & KADM5_LAST_FAILED) {
252	    printf("    last failed\n");
253	}
254	if(mask & KADM5_FAIL_AUTH_COUNT) {
255	    printf("    fail auth count\n");
256	}
257	if(mask & KADM5_KEY_DATA) {
258	    printf("    key data\n");
259	}
260	if(mask & KADM5_TL_DATA) {
261	    printf("    tl data\n");
262	}
263	free_hdb_entry(&ent);
264	break;
265    case kadm_nop :
266        if (len == 16) {
267            uint64_t off;
268            krb5_ret_uint64(sp, &off);
269            printf("uberblock offset %llu ", (unsigned long long)off);
270        } else {
271            printf("nop");
272        }
273        if (len == 16 || len == 8) {
274            krb5_ret_int32(sp, &nop_time);
275            krb5_ret_uint32(sp, &nop_ver);
276
277            timestamp = nop_time;
278            strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S", localtime(&timestamp));
279            printf("timestamp %s version %u", t, nop_ver);
280        }
281        printf("\n");
282	break;
283    default:
284	abort();
285    }
286    krb5_data_free(&data);
287
288    return 0;
289}
290
291int
292iprop_dump(struct dump_options *opt, int argc, char **argv)
293{
294    kadm5_server_context *server_context;
295    krb5_error_code ret;
296    enum kadm_iter_opts iter_opts_1st = 0;
297    enum kadm_iter_opts iter_opts_2nd = 0;
298    char *desc_1st = "";
299    char *desc_2nd = "";
300
301    server_context = get_kadmin_context(opt->config_file_string,
302					opt->realm_string);
303
304    if (argc > 0) {
305        free(server_context->log_context.log_file);
306        server_context->log_context.log_file = strdup(argv[0]);
307        if (server_context->log_context.log_file == NULL)
308            krb5_err(context, 1, errno, "strdup");
309    }
310
311    if (opt->reverse_flag) {
312        iter_opts_1st = kadm_backward | kadm_unconfirmed;
313        iter_opts_2nd = kadm_backward | kadm_confirmed;
314        desc_1st = "unconfirmed ";
315    } else {
316        iter_opts_1st = kadm_forward | kadm_confirmed;
317        iter_opts_2nd = kadm_forward | kadm_unconfirmed;
318        desc_2nd = "unconfirmed";
319    }
320
321    if (opt->no_lock_flag) {
322        ret = kadm5_log_init_sharedlock(server_context, LOCK_NB);
323        if (ret == EAGAIN || ret == EWOULDBLOCK) {
324            warnx("Not locking the iprop log");
325            ret = kadm5_log_init_nolock(server_context);
326            if (ret)
327                krb5_err(context, 1, ret, "kadm5_log_init_nolock");
328        }
329    } else {
330        warnx("If this command appears to block, try the --no-lock option");
331        ret = kadm5_log_init_sharedlock(server_context, 0);
332        if (ret)
333            krb5_err(context, 1, ret, "kadm5_log_init_sharedlock");
334    }
335
336    ret = kadm5_log_foreach(server_context, iter_opts_1st,
337                            NULL, print_entry, desc_1st);
338    if (ret)
339	krb5_warn(context, ret, "kadm5_log_foreach");
340
341    ret = kadm5_log_foreach(server_context, iter_opts_2nd,
342                            NULL, print_entry, desc_2nd);
343    if (ret)
344	krb5_warn(context, ret, "kadm5_log_foreach");
345
346    ret = kadm5_log_end (server_context);
347    if (ret)
348	krb5_warn(context, ret, "kadm5_log_end");
349
350    kadm5_destroy(server_context);
351    return 0;
352}
353
354int
355iprop_truncate(struct truncate_options *opt, int argc, char **argv)
356{
357    kadm5_server_context *server_context;
358    krb5_error_code ret;
359
360    server_context = get_kadmin_context(opt->config_file_string,
361					opt->realm_string);
362
363    if (argc > 0) {
364        free(server_context->log_context.log_file);
365        server_context->log_context.log_file = strdup(argv[0]);
366        if (server_context->log_context.log_file == NULL)
367            krb5_err(context, 1, errno, "strdup");
368    }
369
370    if (opt->keep_entries_integer < 0 &&
371        opt->max_bytes_integer < 0) {
372        opt->keep_entries_integer = 100;
373        opt->max_bytes_integer = 0;
374    }
375    if (opt->keep_entries_integer < 0)
376        opt->keep_entries_integer = 0;
377    if (opt->max_bytes_integer < 0)
378        opt->max_bytes_integer = 0;
379
380    if (opt->reset_flag) {
381        /* First recover unconfirmed records */
382        ret = kadm5_log_init(server_context);
383        if (ret == 0)
384            ret = kadm5_log_reinit(server_context, 0);
385    } else {
386        ret = kadm5_log_init(server_context);
387        if (ret)
388            krb5_err(context, 1, ret, "kadm5_log_init");
389        ret = kadm5_log_truncate(server_context, opt->keep_entries_integer,
390                                 opt->max_bytes_integer);
391    }
392    if (ret)
393	krb5_err(context, 1, ret, "kadm5_log_truncate");
394
395    kadm5_log_signal_master(server_context);
396
397    kadm5_destroy(server_context);
398    return 0;
399}
400
401int
402last_version(struct last_version_options *opt, int argc, char **argv)
403{
404    kadm5_server_context *server_context;
405    char *alt_argv[2] = { NULL, NULL };
406    krb5_error_code ret;
407    uint32_t version;
408    size_t i;
409
410    server_context = get_kadmin_context(opt->config_file_string,
411					opt->realm_string);
412
413    if (argc == 0) {
414        alt_argv[0] = strdup(server_context->log_context.log_file);
415        if (alt_argv[0] == NULL)
416            krb5_err(context, 1, errno, "strdup");
417        argv = alt_argv;
418        argc = 1;
419    }
420
421    for (i = 0; i < argc; i++) {
422        free(server_context->log_context.log_file);
423        server_context->log_context.log_file = strdup(argv[i]);
424        if (server_context->log_context.log_file == NULL)
425            krb5_err(context, 1, errno, "strdup");
426
427        if (opt->no_lock_flag) {
428            ret = kadm5_log_init_sharedlock(server_context, LOCK_NB);
429            if (ret == EAGAIN || ret == EWOULDBLOCK) {
430                warnx("Not locking the iprop log");
431                ret = kadm5_log_init_nolock(server_context);
432                if (ret)
433                    krb5_err(context, 1, ret, "kadm5_log_init_nolock");
434            }
435        } else {
436            warnx("If this command appears to block, try the "
437                  "--no-lock option");
438            ret = kadm5_log_init_sharedlock(server_context, 0);
439            if (ret)
440                krb5_err(context, 1, ret, "kadm5_log_init_sharedlock");
441        }
442
443        ret = kadm5_log_get_version (server_context, &version);
444        if (ret)
445            krb5_err (context, 1, ret, "kadm5_log_get_version");
446
447        ret = kadm5_log_end (server_context);
448        if (ret)
449            krb5_warn(context, ret, "kadm5_log_end");
450
451        printf("version: %lu\n", (unsigned long)version);
452    }
453
454    kadm5_destroy(server_context);
455    free(alt_argv[0]);
456    return 0;
457}
458
459int
460signal_master(struct signal_options *opt, int argc, char **argv)
461{
462    kadm5_server_context *server_context;
463
464    server_context = get_kadmin_context(opt->config_file_string,
465					opt->realm_string);
466
467    kadm5_log_signal_master(server_context);
468
469    kadm5_destroy(server_context);
470    return 0;
471}
472
473/*
474 * Replay log
475 */
476
477int start_version = -1;
478int end_version = -1;
479
480static kadm5_ret_t
481apply_entry(kadm5_server_context *server_context,
482	    uint32_t ver,
483	    time_t timestamp,
484	    enum kadm_ops op,
485	    uint32_t len,
486	    krb5_storage *sp,
487	    void *ctx)
488{
489    struct replay_options *opt = ctx;
490    krb5_error_code ret;
491
492    if((opt->start_version_integer != -1 && ver < (uint32_t)opt->start_version_integer) ||
493       (opt->end_version_integer != -1 && ver > (uint32_t)opt->end_version_integer)) {
494	/* XXX skip this entry */
495	return 0;
496    }
497    printf ("ver %u... ", ver);
498    fflush (stdout);
499
500    ret = kadm5_log_replay(server_context, op, ver, len, sp);
501    if (ret)
502	krb5_warn (server_context->context, ret, "kadm5_log_replay");
503
504    printf ("done\n");
505
506    return 0;
507}
508
509int
510iprop_replay(struct replay_options *opt, int argc, char **argv)
511{
512    kadm5_server_context *server_context;
513    krb5_error_code ret;
514
515    server_context = get_kadmin_context(opt->config_file_string,
516					opt->realm_string);
517
518    if (argc > 0) {
519        free(server_context->log_context.log_file);
520        server_context->log_context.log_file = strdup(argv[0]);
521        if (server_context->log_context.log_file == NULL)
522            krb5_err(context, 1, errno, "strdup");
523    }
524
525    ret = server_context->db->hdb_open(context,
526				       server_context->db,
527				       O_RDWR | O_CREAT, 0600);
528    if (ret)
529	krb5_err (context, 1, ret, "db->open");
530
531    ret = kadm5_log_init (server_context);
532    if (ret)
533	krb5_err (context, 1, ret, "kadm5_log_init");
534
535    ret = kadm5_log_foreach(server_context,
536                            kadm_forward | kadm_confirmed | kadm_unconfirmed,
537                            NULL, apply_entry, opt);
538    if(ret)
539	krb5_warn(context, ret, "kadm5_log_foreach");
540    ret = kadm5_log_end (server_context);
541    if (ret)
542	krb5_warn(context, ret, "kadm5_log_end");
543    ret = server_context->db->hdb_close (context, server_context->db);
544    if (ret)
545	krb5_err (context, 1, ret, "db->close");
546
547    kadm5_destroy(server_context);
548    return 0;
549}
550
551static int help_flag;
552static int version_flag;
553
554static struct getargs args[] = {
555    { "version", 	0,	arg_flag, 	&version_flag,
556      NULL,		NULL
557    },
558    { "help", 	'h', 	arg_flag, 	&help_flag,
559      NULL, NULL
560    }
561};
562
563static int num_args = sizeof(args) / sizeof(args[0]);
564
565int
566help(void *opt, int argc, char **argv)
567{
568    if(argc == 0) {
569	sl_help(commands, 1, argv - 1 /* XXX */);
570    } else {
571	SL_cmd *c = sl_match (commands, argv[0], 0);
572 	if(c == NULL) {
573	    fprintf (stderr, "No such command: %s. "
574		     "Try \"help\" for a list of commands\n",
575		     argv[0]);
576	} else {
577	    if(c->func) {
578		static char shelp[] = "--help";
579		char *fake[3];
580		fake[0] = argv[0];
581		fake[1] = shelp;
582		fake[2] = NULL;
583		(*c->func)(2, fake);
584		fprintf(stderr, "\n");
585	    }
586	    if(c->help && *c->help)
587		fprintf (stderr, "%s\n", c->help);
588	    if((++c)->name && c->func == NULL) {
589		int f = 0;
590		fprintf (stderr, "Synonyms:");
591		while (c->name && c->func == NULL) {
592		    fprintf (stderr, "%s%s", f ? ", " : " ", (c++)->name);
593		    f = 1;
594		}
595		fprintf (stderr, "\n");
596	    }
597	}
598    }
599    return 0;
600}
601
602static void
603usage(int status)
604{
605    arg_printusage(args, num_args, NULL, "command");
606    exit(status);
607}
608
609int
610main(int argc, char **argv)
611{
612    int optidx = 0;
613    krb5_error_code ret;
614
615    setprogname(argv[0]);
616
617    if(getarg(args, num_args, argc, argv, &optidx))
618	usage(1);
619    if(help_flag)
620	usage(0);
621    if(version_flag) {
622	print_version(NULL);
623	exit(0);
624    }
625    argc -= optidx;
626    argv += optidx;
627    if(argc == 0)
628	usage(1);
629
630    ret = krb5_init_context(&context);
631    if (ret)
632	errx(1, "krb5_init_context failed with: %d\n", ret);
633
634    ret = sl_command(commands, argc, argv);
635    if(ret == -1)
636	warnx ("unrecognized command: %s", argv[0]);
637    return ret;
638}
639